经过上一篇tun/tap的介绍,大家应该对虚拟网络设备有了一定的了解。本文将继续介绍另一个虚拟网络设备veth。veth设备的特点veth和其他网络设备一样,一端连接到内核协议栈。veth设备成对出现,另一端的两个设备相互连接。一台设备从协议栈接收到数据传输请求后,会将数据发送给另一台设备。下面的关系图很清楚的说明了veth设备的特点:+-------------------------------------------------------------+|||+------------------------------------------------+|||网络协议栈|||+----------------------------------------------------+||↑↑↑||...................|................|...............|..................||↓↓↓||+---------++---------++-----------+|||eth0||veth0||veth1|||+----------++------------++----------+||192.168.1.11↑↑↑|||+----------------+|||192.168.2.11192.168.2.1|+--------------|-----------------------------------------------+↓物理网络上图中我们配置物理网卡eth0的IP为192.168.1.11,veth0和veth1的IP分别为192.168.2.11和192.168.2.1。下面一步步举例看看veth设备的特点。只为一个veth设备配置IP,首先通过iplink命令添加veth0和veth1,然后配置veth0的IP,同时启动两个设备dev@debian:~$sudoiplinkaddveth0typevethpeernameveth1dev@debian:~$sudoipaddradd192.168.2.11/24devveth0dev@debian:~$sudoiplinksetveth0updev@debian:~$sudoiplinksetveth1up这里之所以不给veth1配置IP,可以看看veth1里面是什么如果没有IP,veth0会不会把从协议栈收到的数据转发给veth1呢?ping192.168.2.1,因为veth1没有配置IP,所以肯定不行dev@debian:~$ping-c4192.168.2.1PING192.168.2.1(192.168.2.1)56(84)bytesofdata.From192.168.2.11icmp_seq=1DestinationHostUnreachableFrom192.168.2.11icmp_seq=2DestinationHostUnreachableFrom192.168.2.11icmp_seq=3DestinationHostUnreachableFrom192.168.2.11icmp_seq=4DestinationHostUnreachable---192.168.2.4packets,pingstatistics--0received,+4errors,100%packetloss,time3015mspipe3但是为什么ping不通?哪里失败了?先看抓包。从下面的输出中,我们可以看到veth0和veth1收到了相同的ARP请求包,但是没有看到ARP响应包:dev@debian:~$sudotcpdump-n-iveth0tcpdump:verboseoutputsuppressed,使用-v或者-vvforfullprotocoldecodelisteningonveth0,link-typeEN10MB(Ethernet),capturesize262144bytes20:20:18.285230ARP,Requestwho-has192.168.2.1tell192.168.2.11,20length20:19.282018ARP,Requestwho-has192.168.2.1tell192.168.2.11,length2820:20:20.282038ARP,Requestwho-has192.168.2.1tell192.168.2.11,lengthh2820:20:21.300320ARP,请求谁有192.168.2.1告诉192.168.2.11,长度2820:20:22.298783ARP,请求谁有192.168.2.1告诉192.168.2.11,长度298wRP29230:-1116告诉192.1168.2.19.2.11,长度28dev@debian:~$sudotcpdump-n-iveth1tcpdump:抑制详细输出,使用-v或-vv在veth1上进行完整协议解码监听,链路类型EN10MB(以太网),捕获大小262144字节20:20:48.570459ARP,请求谁有192.168.2.1告诉192.168.2.11,长度2820:20:49.570012ARP,请求谁有192.168.2.1告诉192.168.2.11,长度2820:20:50.570023ARP,请求谁有-16tell192.168.2.11,length2820:20:51.570023ARP,Requestwho-has192.168.2.1tell192.168.2.11,length2820:20:52.569988ARP,Requestwho-has192.168.2.1tell192.168.2.11,length2820:20:53.570833ARP,Requestwho-has192.168.2.1tell192.168.2.11,length28为什么会这样?了解了ping背后发生了什么,你就会明白:ping进程构造一个ICMP回显请求包,通过socket发送给协议栈。协议栈使用目的IP地址和系统路由表,知道去往192.168.2.1的数据包应该从192.168.2.11端口出去,因为是第一次访问192.168.2.1,目的IP和本地IP在同一网段,所以协议栈会先发出ARP出去,让192.168.2.1的mac地址协议栈把ARP包发给veth0,让它发出去。由于veth0的另一端连接到veth1,所以ARP请求包被转发到veth1。veth1收到ARP包后,转发给另一端。协议栈查看自己的设备列表,发现本地没有IP192.168.2.1,于是丢弃ARP请求包,这就是为什么只能看到ARP请求包,看不到响应包的原因。所有veth设备都配置了veth1和IPdev@debian的IP:~$sudoipaddradd192.168.2.1/24devveth1然后ping192.168.2.1成功(因为192.168.2.1是本地IP,所以lo设备会是默认使用,为了避免这种情况,这里使用带-I参数的ping命令指定数据包去指定的设备)dev@debian:~$ping-c4192.168.2.1-Iveth0PING192.168.2.1(192.168.2.1)来自192.168。2.11veth0:56(84)字节的数据。来自192.168.2.1的64字节:icmp_seq=1ttl=64time=0.032ms64来自192.168.2.1的字节:icmp_seq=2ttl=64time=0.048ms来自192.168.2的64字节。icmp_seq=3ttl=64time=0.055ms64bytesfrom192.168.2.1:icmp_seq=4ttl=64time=0.050ms---192.168.2.1pingstatistics---4packetstransmitted,4received,0%packetloss,time3002msrttmin/avg/max/mdev=0.032/0.046/0.055/0.009ms注意:对于非debian系统,可能会有ping失败,主要是内核中一些ARP相关的配置导致veth1没有返回ARP响应包,比如在ubuntu上出现,解决方法如下:root@ubuntu:~#echo1>/proc/sys/net/ipv4/conf/veth1/accept_localroot@ubuntu:~#echo1>/proc/sys/net/ipv4/conf/veth0/accept_localroot@ubuntu:~#echo0>/proc/sys/net/ipv4/conf/all/rp_filterroot@ubuntu:~#echo0>/proc/sys/net/ipv4/conf/veth0/rp_filterroot@ubuntu:~#echo0>/proc/sys/net/ipv4/conf/veth1/rp_filter我们来看看再包情况,我们在veth0和veth1上都看到了ICMPechorequest包,但是为什么没有响应包呢?以上不就说明ping进程已经成功收到响应包了吗?dev@debian:~$sudotcpdump-n-iveth0tcpdump:抑制详细输出,使用-v或-vv在veth0上进行完整协议解码监听,链路类型EN10MB(以太网),捕获大小262144字节20:23:43.113062IP192.168。2.11>192.168.2.1:ICMP回显请求,id24169,seq1,长度6420:23:44.112078IP192.168.2.11>192.168.2.1:ICMPecho请求,id24169,seq2,长度1952.18IP1920.18.16>192.1629.2.18.2.1:ICMPechorequest,id24169,seq3,length6420:23:46.110082IP192.168.2.11>192.168.2.1:ICMPechorequest,id24169,seq4,??length64dev@debian:~$sudotcpdump-n-iveth1tcpdump:详细输出被抑制,使用-v或-vv进行完整的协议解码监听veth1,链接类型EN10MB(以太网),捕获大小262144字节20:24:12.221372IP192.168.2.11>192.168.2.1:ICMP回显请求,id24174,seq1,长度6420:24:13.222016IP>192.2.2:ICMPecho请求,id24174,seq2,长度6420:24:14.224836IP192.168.2.11>192.168.2.1:ICMPecho请求,id24174,seq3,长度6420:24:15.223826IP812.12.12:ICMPecho请求,id24174,seq4,??length64看数据包的流程就明白了:ping进程构造ICMP回显请求包,通过socket发送到协议栈,因为ping程序指定veth0,本地ARP缓存中已经有相关记录了,所以不用再发出ARP了,协议栈直接把数据包交给veth0,因为veth0的另一端连接的是veth1,所以ICMPechorequest包被转发到veth1,veth1收到ICMPechorequest包后,转发给对端的协议栈,协议栈查看自己的设备列表,发现有192.168的IP。192.168.2.11的数据包要走lo口,所以响应包交给lo设备。收到协议栈的响应包后,什么?什么都不做,转手把数据包还给协议栈(相当于协议栈通过发送进程把数据包发给lo,然后lo把数据包交给协议栈的接收进程).协议栈收到响应包后,发现有socket需要包,于是交给对应的socket处理。这个socket刚好是ping进程创建的socket,所以ping进程收到响应包,抓取lo设备上的数据,发现响应包确实是从lo端口返回的:dev@debian:~$sudotcpdump-n-ilotcpdump:抑制详细输出,使用-v或-vv在lo上进行完整协议解码侦听,链路类型EN10MB(以太网),捕获大小262144字节20:25:49.590273IP192.168。2.1>192.168.2.11:ICMP回显回复,id24177,seq1,长度6420:25:50.590018IP192.168.2.1>192.168.2.11:ICMP回显回复,id24177,seq2,长度19027IP6420.18:2892.16>.2.11:ICMPechoreply,id24177,seq3,length6420:25:52.590030IP192.168.2.1>192.168.2.11:ICMPechoreply,id24177,seq4,??length64IPping尝试ping其他IP在192.168.2.0/24网段失败,ping一个公网IP也失败:dev@debian:~$ping-c1-Iveth0192.168.2.2PING192.168.2.2(192.168.2.2)from192.168.2.11veth0:56(84)个字节的数据。来自192.168.2.11icmp_seq=1目标主机无法访问---192.168.2.2ping统计---1个数据包传输,0个接收,+1个错误,100%丢包,时间0msdev@debian:~$ping-c1-Iveth0baidu.comPINGbaidu.com(111.13.101.208)from192.168.2.11veth0:56(84)bytesofdata.From192.168.2.11icmp_seq=1DestinationHostUnreachable---baidu.compingstatistics---1packetstransmitted,0received,+1errors,100%packetloss,从抓包的角度来看,时间0ms和上面第一种情况一样,veth1没有配置IP,没有人处理ARP请求。dev@debian:~$sudotcpdump-iveth1tcpdump:详细输出被抑制,使用-v或-vv在veth1上进行完整的协议解码监听,链路类型EN10MB(以太网),捕获大小262144bytes02:25:23.223947ARP,请求who-有192.168.2.2告诉192.168.2.11,长度2802:25ques:24.224358Response-2AhoRP,.2.2告诉192.168.2.11,长度2802:25:25.223471ARP,请求谁有192.168.2.2告诉192.1162,8.225:27.946539ARP,请求谁拥有123.125.114.144告诉192.168.2.11,长度2802:25:28.946633ARP,请求谁拥有123.125。114.144tell192.168.2.11,length2802:25:29.948055ARP,Requestwho-has123.125.114.144tell192.168.2.11,length28结论从上面的介绍可以看出,veth0设备的数据包会被转发到veth1上面如果目的地址是veth1的IP,可以被协议栈处理。否则连ARP层都过不去,IP转发也用不上。所以,如果没有其他虚拟设备的帮助,这样的数据包只能在本地的协议栈里转来转去,我是走不到eth0的,也就是发不到外网的。下一篇介绍linux下的网桥,到时候veth这个设备就派上用场了。参考LinuxSwitching–InterconnectingNamespaces
