Linux抽象网络设备简介
和磁盘设备类似,Linux用户想要使用网络功能,不能通过直接操作硬件完成,而需要直接或间接的操作一个Linux为我们抽象出来的设备,既通用的Linux网络设备来完成。一个常见的情况是,系统里装有一个硬件网卡,Linux会在系统里为其生成一个网络设备实例,如eth0,用户需要对eth0发出命令以配置或使用它了。更多的硬件会带来更多的设备实例,虚拟的硬件也会带来更多的设备实例。随着网络技术,虚拟化技术的发展,更多的高级网络设备被加入了到了Linux中,使得情况变得更加复杂。在以下章节中,将一一分析在虚拟化技术中经常使用的几种Linux网络设备抽象类型:Bridge、802.1.qVLANdevice、VETH、TAP,详细解释如何用它们配合Linux中的Routetable、IPtable简单的创建出本地虚拟网络。
相关网络设备工作原理
Bridge
Bridge(桥)是Linux上用来做TCP/IP二层协议交换的设备,与现实世界中的交换机功能相似。Bridge设备实例可以和Linux上其他网络设备实例连接,既attach一个从设备,类似于在现实世界中的交换机和一个用户终端之间连接一根网线。当有数据到达时,Bridge会根据报文中的MAC信息进行广播、转发、丢弃处理。
图1.Bridge设备工作过程
如图所示,Bridge的功能主要在内核里实现。当一个从设备被attach到Bridge上时,相当于现实世界里交换机的端口被插入了一根连有终端的网线。这时在内核程序里,netdev_rx_handler_register()被调用,一个用于接受数据的回调函数被注册。以后每当这个从设备收到数据时都会调用这个函数可以把数据转发到Bridge上。当Bridge接收到此数据时,br_handle_frame()被调用,进行一个和现实世界中的交换机类似的处理过程:判断包的类别(广播/单点),查找内部MAC端口映射表,定位目标端口号,将数据转发到目标端口或丢弃,自动更新内部MAC端口映射表以自我学习。
Bridge和现实世界中的二层交换机有一个区别,图中左侧画出了这种情况:数据被直接发到Bridge上,而不是从一个端口接受。这种情况可以看做Bridge自己有一个MAC可以主动发送报文,或者说Bridge自带了一个隐藏端口和寄主Linux系统自动连接,Linux上的程序可以直接从这个端口向Bridge上的其他端口发数据。所以当一个Bridge拥有一个网络设备时,如bridge0加入了eth0时,实际上bridge0拥有两个有效MAC地址,一个是bridge0的,一个是eth0的,他们之间可以通讯。由此带来一个有意思的事情是,Bridge可以设置IP地址。通常来说IP地址是三层协议的内容,不应该出现在二层设备Bridge上。但是Linux里Bridge是通用网络设备抽象的一种,只要是网络设备就能够设定IP地址。当一个bridge0拥有IP后,Linux便可以通过路由表或者IP表规则在三层定位bridge0,此时相当于Linux拥有了另外一个隐藏的虚拟网卡和Bridge的隐藏端口相连,这个网卡就是名为bridge0的通用网络设备,IP可以看成是这个网卡的。当有符合此IP的数据到达bridge0时,内核协议栈认为收到了一包目标为本机的数据,此时应用程序可以通过Socket接收到它。一个更好的对比例子是现实世界中的带路由的交换机设备,它也拥有一个隐藏的MAC地址,供设备中的三层协议处理程序和管理程序使用。设备里的三层协议处理程序,对应名为bridge0的通用网络设备的三层协议处理程序,即寄主Linux系统内核协议栈程序。设备里的管理程序,对应bridge0寄主Linux系统里的应用程序。
Bridge的实现当前有一个限制:当一个设备被attach到Bridge上时,那个设备的IP会变的无效,Linux不再使用那个IP在三层接受数据。举例如下:如果eth0本来的IP是192.168.1.2,此时如果收到一个目标地址是192.168.1.2的数据,Linux的应用程序能通过Socket操作接受到它。而当eth0被attach到一个bridge0时,尽管eth0的IP还在,但应用程序是无法接受到上述数据的。此时应该把IP192.168.1.2赋予bridge0。
另外需要注意的是数据流的方向。对于一个被attach到Bridge上的设备来说,只有它收到数据时,此包数据才会被转发到Bridge上,进而完成查表广播等后续操作。当请求是发送类型时,数据是不会被转发到Bridge上的,它会寻找下一个发送出口。用户在配置网络时经常忽略这一点从而造成网络故障。
VLANdevicefor802.1.q
VLAN又称虚拟网络,是一个被广泛使用的概念,有些应用程序把自己的内部网络也称为VLAN。此处主要说的是在物理世界中存在的,需要协议支持的VLAN。它的种类很多,按照协议原理一般分为:MACVLAN、802.1.qVLAN、802.1.qbgVLAN、802.1.qbhVLAN。其中出现较早,应用广泛并且比较成熟的是802.1.qVLAN,其基本原理是在二层协议里插入额外的VLAN协议数据(称为802.1.qVLANTag),同时保持和传统二层设备的兼容性。Linux里的VLAN设备是对802.1.q协议的一种内部软件实现,模拟现实世界中的802.1.q交换机。
图2.VLAN设备工作过程
如图所示,Linux里802.1.qVLAN设备是以母子关系成对出现的,母设备相当于现实世界中的交换机TRUNK口,用于连接上级网络,子设备相当于普通接口用于连接下级网络。当数据在母子设备间传递时,内核将会根据802.1.qVLANTag进行对应操作。母子设备之间是一对多的关系,一个母设备可以有多个子设备,一个子设备只有一个母设备。当一个子设备有一包数据需要发送时,数据将被加入VLANTag然后从母设备发送出去。当母设备收到一包数据时,它将会分析其中的VLANTag,如果有对应的子设备存在,则把数据转发到那个子设备上并根据设置移除VLANTag,否则丢弃该数据。在某些设置下,VLANTag可以不被移除以满足某些监听程序的需要,如DHCP服务程序。举例说明如下:eth0作为母设备创建一个ID为100的子设备eth0.100。此时如果有程序要求从eth0.100发送一包数据,数据将被打上VLAN100的Tag从eth0发送出去。如果eth0收到一包数据,VLANTag是100,数据将被转发到eth0.100上,并根据设置决定是否移除VLANTag。如果eth0收到一包包含VLANTag101的数据,其将被丢弃。上述过程隐含以下事实:对于寄主Linux系统来说,母设备只能用来收数据,子设备只能用来发送数据。和Bridge一样,母子设备的数据也是有方向的,子设备收到的数据不会进入母设备,同样母设备上请求发送的数据不会被转到子设备上。可以把VLAN母子设备作为一个整体想象为现实世界中的802.1.q交换机,下级接口通过子设备连接到寄主Linux系统网络里,上级接口同过主设备连接到上级网络,当母设备是物理网卡时上级网络是外界真实网络,当母设备是另外一个Linux虚拟网络设备时上级网络仍然是寄主Linux系统网络。
需要注意的是母子VLAN设备拥有相同的MAC地址,可以把它当成现实世界中802.1.q交换机的MAC,因此多个VLAN设备会共享一个MAC。当一个母设备拥有多个VLAN子设备时,子设备之间是隔离的,不存在Bridge那样的交换转发关系,原因如下:802.1.qVLAN协议的主要目的是从逻辑上隔离子网。现实世界中的802.1.q交换机存在多个VLAN,每个VLAN拥有多个端口,同一VLAN端口之间可以交换转发,不同VLAN端口之间隔离,所以其包含两层功能:交换与隔离。LinuxVLANdevice实现的是隔离功能,没有交换功能。一个VLAN母设备不可能拥有两个相同ID的VLAN子设备,因此也就不可能出现数据交换情况。如果想让一个VLAN里接多个设备,就需要交换功能。在Linux里Bridge专门实现交换功能,因此将VLAN子设备attach到一个Bridge上就能完成后续的交换功能。总结起来,Bridge加VLANdevice能在功能层面完整模拟现实世界里的802.1.q交换机。
Linux支持VLAN硬件加速,在安装有特定硬件情况下,图中所述内核处理过程可以被放到物理设备上完成。
TAP设备与VETH设备
TUN/TAP设备是一种让用户态程序向内核协议栈注入数据的设备,一个工作在三层,一个工作在二层,使用较多的是TAP设备。VETH设备出现较早,它的作用是反转通讯数据的方向,需要发送的数据会被转换成需要收到的数据重新送入内核网络层进行处理,从而间接的完成数据的注入。
图3.TAP设备和VETH设备工作过程
如图所示,当一个TAP设备被创建时,在Linux设备文件目录下将会生成一个对应char设备,用户程序可以像打开普通文件一样打开这个文件进行读写。当执行write()操作时,数据进入TAP设备,此时对于Linux网络层来说,相当于TAP设备收到了一包数据,请求内核接受它,如同普通的物理网卡从外界收到一包数据一样,不同的是其实数据来自Linux上的一个用户程序。Linux收到此数据后将根据网络配置进行后续处理,从而完成了用户程序向Linux内核网络层注入数据的功能。当用户程序执行read()请求时,相当于向内核查询TAP设备上是否有需要被发送出去的数据,有的话取出到用户程序里,完成TAP设备的发送数据功能。针对TAP设备的一个形象的比喻是:使用TAP设备的应用程序相当于另外一台计算机,TAP设备是本机的一个网卡,他们之间相互连接。应用程序通过read()/write()操作,和本机网络核心进行通讯。
VETH设备总是成对出现,送到一端请求发送的数据总是从另一端以请求接受的形式出现。该设备不能被用户程序直接操作,但使用起来比较简单。创建并配置正确后,向其一端输入数据,VETH会改变数据的方向并将其送入内核网络核心,完成数据的注入。在另一端能读到此数据。
网络设置举例说明
为了更好的说明Linux网络设备的用法,下面将用一系列的例子,说明在一个复杂的Linux网络元素组合出的虚拟网络里,数据的流向。网络设置简介如下:一个中心Bridge:bridge0下attach了4个网络设备,包括2个VETH设备,1个TAP设备tap0,1个物理网卡eth0。在VETH的另外一端又创建了VLAN子设备。Linux上共存在2个VLAN网络,既vlan100与vlan200。物理网卡和外部网络相连,并且在它之下创建了一个VLANID为200的VLAN子设备。
从vlan100子设备发送ARP报文
图4.ARP from vlan100 child device
如图所示,当用户尝试ping192.168.100.3时,Linux将会根据路由表,从vlan100子设备发出ARP报文,具体过程如下:
1)用户ping192.168.100.3
2)Linux向vlan100子设备发送ARP信息。
3)ARP报文被打上VLANID100的Tag成为ARP@vlan100,转发到母设备上。
4)VETH设备将这一发送请求转变方向,成为一个需要接受处理的报文送入内核网络模块。
5)由于对端的VETH设备被加入到了bridge0上,并且内核发现它收到一个报文,于是报文被转发到bridge0上。
6)bridge0处理此ARP@vlan100信息,根据TCP/IP二层协议发现是一个广播请求,于是向它所知道的所有端口广播此报文,其中一路进入另一对VETH设备的一端,一路进入TAP设备tap0,一路进入物理网卡设备eth0。此时在tap0上,用户程序可以通过read()操作读到ARP@vlan100,eth0将会向外界发送ARP@vlan100,但eth0的VLAN子设备不会收到它,因为此数据方向为请求发送而不是请求接收。
7)VETH将请求方向转换,此时在另一端得到请求接受的ARP@vlan100报文。
8)对端VETH设备发现有数据需要接受,并且自己有两个VLAN子设备,于是执行VLAN处理逻辑。其中一个子设备是vlan100,与ARP@vlan100吻合,于是去除VLANID100的Tag转发到这个子设备上,重新成为标准的以太网ARP报文。另一个子设备由于ID不吻合,不会得到此报文。
9)此VLAN子设备又被attach到另一个桥bridge1上,于是转发自己收到的ARP报文。
10)bridge1广播ARP报文。
11)最终另外一个TAP设备tap1收到此请求发送报文,用户程序通过read()可以得到它。
从vlan200子设备发送ARP报文
图5.ARP from vlan200 child device
和前面情况类似,区别是VLANID是200,对端的vlan200子设备设置为reorder_hdr=0,表示此设备被要求保留收到的报文中的VLANTag。此时子设备会收到ARP报文,但是带了VLANID200的Tag,既ARP@vlan200。
从中心bridge发送ARP报文
图6.ARP from central bridge
当bridge0拥有IP时,通过Linux路由表用户程序可以直接将ARP报文发向bridge0。这时tap0和外部网络都能收到ARP,但VLAN子设备由于VLANID过滤的原因,将收不到ARP信息。
从外部网络向物理网卡发送ARP@vlan200报文
图7.ARP from external network
当外部网络连接在一个支持VLAN并且对应端口为vlan200时,此情况会发生。此时所有的VLANID为200的VLAN子设备都将接受到报文,如果设置reorder_hdr=0则会收到带Tag的ARP@vlan200。
从TAP设备以ping方式发送ARP
图8.ping from TAP device
给tap0赋予IP并加入路由,此时再Ping其对应网段的未知IP会产生ARP发送请求。需要注意的是此时由于tap0上存在的是发送而不是接收请求,因此ARP报文不会被转发到桥上,从而什么也不会发生。图中右边画了一个类似情况:从vlan200子设备发送ARP请求。由于缺少VETH设备反转请求方向,因此报文也不会被转发到桥上,而是直接通过物理网卡发往外部网络。
以文件操作方式从TAP设备发送报文
图9.file operation on TAP device
用户程序指定tap0设备发送报文有两种方式:socket和fileoperation。当用socket_raw标志新建socket并指定设备编号时,可以要求内核将报文从tap0发送。但和前面的pingfromtap0情况类似,由于报文方向问题,消息并不会被转发到bridge0上。当用open()方式打开tap设备文件时,情况有所不同。当执行write()操作时,内核认为tap0收到了报文,从而会触发转发动作,bridge0将收到它。如果发送的报文如图所示,是一个以A为目的地的携带VLANID100Tag的单点报文,bridge0将会找到对应的设备进行转发,对应的VLAN子设备将收到没有VLANID100Tag的报文。
Linux上配置网络设备命令举例
以Redhat6.2红帽Linux发行版为例,如果已安装VLAN内核模块和管理工具vconfig,TAP/TUN设备管理工具tunctl,那么可以用以下命令设置前述网络设备:
创建Bridge:brctladdbr[BRIDGENAME]
删除Bridge:brctldelbr[BRIDGENAME]
attach设备到Bridge:brctladdif[BRIDGENAME][DEVICENAME]
从Bridgedetach设备:brctldelif[BRIDGENAME][DEVICENAME]
查询Bridge情况:brctlshow
创建VLAN设备:vconfigadd[PARENTDEVICENAME][VLANID]
删除VLAN设备:vconfigrem[VLANDEVICENAME]
设置VLAN设备flag:vconfigset_flag[VLANDEVICENAME][FLAG][VALUE]
设置VLAN设备qos:
vconfigset_egress_map[VLANDEVICENAME][SKB_PRIORITY][VLAN_QOS]
vconfigset_ingress_map[VLANDEVICENAME][SKB_PRIORITY][VLAN_QOS]
查询VLAN设备情况:cat/proc/net/vlan/[VLANDEVICENAME]
创建VETH设备:iplinkaddlink[DEVICENAME]typeveth
创建TAP设备:tunctl-p[TAPDEVICENAME]
删除TAP设备:tunctl-d[TAPDEVICENAME]
查询系统里所有二层设备,包括VETH/TAP设备:iplinkshow
删除普通二层设备:iplinkdelete[DEVICENAME]type[TYPE]
小结
综上所述,Linux已经提供一套基本工具供用户创建出各种内部网络,利用这些工具可以方便的创建出特定网络给应用程序使用,包括云计算中的初级内部虚拟网络。