一 前言
本来这几天研究eBPF的xdp的,虽然阅读了不少资料,但是测试代码仍然有问题,所以耽误了,在测试过程中发现了虚拟网络环境,又是自己没有接触过的,所以研究下,挺有意思的,写了篇文章分享下。
简单来说,构建一套虚拟的网络环境。
二 交叉线和veth
如果玩计算机比较早的朋友,如果没有路由器,集线器这些通讯设备,我们两个计算机想连接打个CS啥的,也是有一种办法的,就是采用双绞线直连,我记得买这种线子的时候,一定要选择交叉线(只是线的顺序不一样做网线的时候做下),才可以计算机对计算机,即同类设备连接,如果是计算机连集线器啥的,用的是直连线,现在用直连线计算机据说也可以适配了,目前没用过。
交叉线我们在一个计算机中,也可以虚拟出来类似交叉线的东东,veth即表示两个类似通过交叉线互联的虚拟网卡。
2.1 veth使用
简单实用ip命令创建veth,这个如同上面的交叉线,是两个虚拟网卡做成一对,可以配置同一个网段的ip,也可以互联通信。
一般用于不同的网络空间,通过veth连着两个协议栈,更好的理解可以认为是bash中的管道,从一端输入另一端输出。
ip link add veth0 type veth peer name veth1
把参数用中括号括起来方便查看如下:
ip link add [veth0] type veth peer name [veth1]
veth0和veth1是虚拟两个网卡名字,结果通过命令查看:
ip link
...
6: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 42:7b:d8:e5:47:82 brd ff:ff:ff:ff:ff:ff
7: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 0a:21:f4:08:f9:92 brd ff:ff:ff:ff:ff:ff
可以看到没设置ip,也没有启动,设置ip,启动下看看效果,如下命令:
#sudo ip addr add 192.168.4.3/24 dev veth0
#sudo ip addr add 192.168.4.2/24 dev veth1
#sudo ip link set veth0 up
#sudo ip link set veth1 up
# ip addr show
6: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 42:7b:d8:e5:47:82 brd ff:ff:ff:ff:ff:ff
inet 192.168.4.2/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::407b:d8ff:fee5:4782/64 scope link
valid_lft forever preferred_lft forever
7: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 0a:21:f4:08:f9:92 brd ff:ff:ff:ff:ff:ff
inet 192.168.4.3/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::821:f4ff:fe08:f992/64 scope link
valid_lft forever preferred_lft forever
可以看到ip已经启动,ip也设置好了。
现在的网络情况是这样的,如下图:
网络互通情况
命令验证如下:
root@ubuntu-lab:~# ping 192.168.4.2 -I lo
ping: Warning: source address might be selected on device other than: lo
PING 192.168.4.2 (192.168.4.2) from 192.168.31.198 lo: 56(84) bytes of data.
64 bytes from 192.168.4.2: icmp_seq=1 ttl=64 time=0.051 ms
^C
--- 192.168.4.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.051/0.051/0.051/0.000 ms
root@ubuntu-lab:~# ping 192.168.4.3 -I lo
ping: Warning: source address might be selected on device other than: lo
PING 192.168.4.3 (192.168.4.3) from 192.168.31.198 lo: 56(84) bytes of data.
64 bytes from 192.168.4.3: icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from 192.168.4.3: icmp_seq=2 ttl=64 time=0.155 ms
^C
--- 192.168.4.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1017ms
rtt min/avg/max/mdev = 0.076/0.115/0.155/0.039 ms
root@ubuntu-lab:~# ping 192.168.4.3 -I eth1
ping: SO_BINDTODEVICE eth1: No such device
root@ubuntu-lab:~# ping 192.168.4.3 -I veth1
PING 192.168.4.3 (192.168.4.3) from 192.168.4.2 veth1: 56(84) bytes of data.
^C
--- 192.168.4.3 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2033ms
root@ubuntu-lab:~# ping 192.168.4.2 -I veth0
PING 192.168.4.2 (192.168.4.2) from 192.168.4.3 veth0: 56(84) bytes of data.
^C
在用ping 192.168.4.3 -I lo
命令我们对veth0 抓包也没有抓到ping包,说明是走的lo回环。
root@ubuntu-lab:/home/miao/xdp-test# echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
root@ubuntu-lab:/home/miao/xdp-test# echo 0 > /proc/sys/net/ipv4/conf/veth0/rp_filter
root@ubuntu-lab:/home/miao/xdp-test# echo 0 > proc/sys/net/ipv4/conf/veth1/rp_filter
root@ubuntu-lab:/home/miao/xdp-test# echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
root@ubuntu-lab:/home/miao/xdp-test# echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
rp_filter (Reverse Path Filtering)参数定义了网卡对接收到的数据包进行反向路由验证的规则。
1(默认值)表示源IP必须是路由可达的,同时入口接口恰好是路由表查到的接口;
2 表示源IP必须是路由可达的,但放松为任意接口可达即可;
0 表示不检查源IP
所谓反向路由校验,就是在一个网卡收到数据包后,把源地址和目标地址对调后查找路由出口,从而得到反身后路由出口。然后根据反向路由出口进行过滤。
当rp_filter的值为1时,要求反向路由的出口必须与数据包的入口网卡是同一块,否则就会丢弃数据包。
当rp_filter的值为2时,要求反向路由必须是可达的,如果反路由不可达,则会丢弃数据包。
这是因为Linux还有一个检测源IP地址的机制,若收到的IP报文源IP与本机上某一个IP相同,那么内核默认丢弃这个报文。这其实也很好理解,正常情况下,如果收到一个IP报文的源地址属于自己,那么可能这个IP报文是伪造的。但也有一些特殊的场景可能用到,例如LVS机器的DR模式或者隧道模式,恰好LVS机器又处在响应的路径里,此时响应源IP是一个VIP,恰好与LVS机器上环回接口配置的一致。
accept_local可以改变默认的行为,把它设为1则接受源IP是本机中的IP的报文通过。
其实ping下不返回,则抓下包:
root@ubuntu-lab:/home/miao/xdp-test# ping 192.168.4.2 -I veth0
PING 192.168.4.2 (192.168.4.2) from 192.168.4.3 veth0: 56(84) bytes of data.
root@ubuntu-lab:/home/miao/ebpfkit-master# tcpdump -i veth1 -vv -n
tcpdump: listening on veth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
06:41:20.542005 IP (tos 0x0, ttl 64, id 11454, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.4.3 > 192.168.4.2: ICMP echo request, id 31, seq 941, length 64
06:41:21.565565 IP (tos 0x0, ttl 64, id 11710, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.4.3 > 192.168.4.2: ICMP echo request, id 31, seq 942, length 64
# 返回的报文是从lo过来的,所以ping没有返回
root@ubuntu-lab:/home/miao/ebpfkit-master# tcpdump -i lo -vv -n
tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
11:35:42.893121 IP (tos 0x0, ttl 64, id 3593, offset 0, flags [none], proto ICMP (1), length 84)
192.168.4.2 > 192.168.4.3: ICMP echo reply, id 50, seq 325, length 64
11:35:43.917182 IP (tos 0x0, ttl 64, id 3726, offset 0, flags [none], proto ICMP (1), length 84)
192.168.4.2 > 192.168.4.3: ICMP echo reply, id 50, seq 326, length 64
11:35:44.942339 IP (tos 0x0, ttl 64, id 3738, offset 0, flags [none], proto ICMP (1), length 84)
192.168.4.2 > 192.168.4.3: ICMP echo reply, id 50, seq 327, length 64
其实看到上面的抓包,veth1 网卡收到包了但是返回从lo返回,返回的时候没办法指定网卡。
2.2 加路由让ping返回走特定网卡
添加路由信息:
root@ubuntu-lab:/home/miao/ebpfkit-master# ip rule add pref 1000 tab local
root@ubuntu-lab:/home/miao/ebpfkit-master# ip rule add fwmark 100 pref 100 tab 100
# 目的是为了路由信息提到local之前
root@ubuntu-lab:/home/miao/ebpfkit-master# ip rule add pref 1000 tab local
root@ubuntu-lab:/home/miao/ebpfkit-master# ip rule del pref 0 tab local
root@ubuntu-lab:/home/miao/ebpfkit-master# ip route add 192.168.4.2/32 dev veth0 src 192.168.4.3 tab 100
root@ubuntu-lab:/home/miao/ebpfkit-master# ip route add 192.168.4.3/32 dev veth1 src 192.168.4.2 tab 100
# 给出去报文加标识
root@ubuntu-lab:/home/miao/xdp-test# iptables -t mangle -A OUTPUT -d 192.168.4.2/32 -j MARK --set-mark 100
然后还是不行,检查了很多配置,检查一天还是没ping通,查看路由:
root@ubuntu-lab:~# ip route get 192.168.4.2
local 192.168.4.2 dev lo table local src 192.168.4.2 uid 0
cache <local>
root@ubuntu-lab:~# ip route get 192.168.4.3
local 192.168.4.3 dev lo table local src 192.168.4.3 uid 0
cache <local>
通过配置来看,走的仍然是lo,用的是缓存,但是没有办法清理lo路由,因为:
root@ubuntu-lab:~# ip route ls tab 100
192.168.4.2 dev veth0 scope link src 192.168.4.3
192.168.4.3 dev veth1 scope link src 192.168.4.2
root@ubuntu-lab:~# ip route ls tab 1000
Error: ipv4: FIB table does not exist.
Dump terminated
目前没有找到办法清理。但是veth更多的是用来转发,比如我们发到veth0的数据可以自动转发到veth1,反之亦然。
三 交换机“bridge”
上面聊的veth,每队通信都要通过路由配置来做,非常麻烦,这个在实际使用如果虚拟网络环境太多,路由都搞的很复杂不值得。
现实中,上大学宿舍多人组网,要么是集线器,要么是交换机,同样在网络环境中,我们也可以通过一个软件实现brige,然后多个虚拟网卡连在一起,借助纯软件实现通信。
docker 有种网络模式就是这样通信的,如下图:
网络环境
清理下环境,重新试验:
#删除veth,删除veth0,veth1自动也删除了。
root@ubuntu-lab:/home/miao/ebpfkit-master# ip link del veth0
#清理路由配置
#ip route del 192.168.4.2/32 dev veth0 src 192.168.4.3 tab 100
#ip route del 192.168.4.3/32 dev veth1 src 192.168.4.2 tab 100
root@ubuntu-lab:/home/miao/ebpfkit-master# ip rule del fwmark 100
3.1 新建虚拟网络环境
Network namespace是linux内核提供的进行网络隔离的功能,每个网络空间都有自己的网络协议栈,路由表,lo等。Docker的网络环境就是通过Network namespace进行网络隔离的。准备搭建的环境如下:
准备试验网络
我们新建了两个不同的网络空间,设置不同的网络段,目标是通过bridge来相互ping通,达到网络互联的目的,再多容器都可以通过连接到bridge来达到相互通信的目的。
建立虚拟网络空间
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns add ns1
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns add ns2
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns
ns2
ns1
创建veth对
root@ubuntu-lab:/home/miao/ebpfkit-master# ip link add veth1 type veth peer name veth1_p
root@ubuntu-lab:/home/miao/ebpfkit-master# ip link set veth1 netns ns1
root@ubuntu-lab:/home/miao/ebpfkit-master# ip link add veth2 type veth peer name veth2_p
root@ubuntu-lab:/home/miao/ebpfkit-master# ip link set veth2 netns ns2
配置ip和启动
ip netns exec ns1 ip addr add 192.168.5.101/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip addr add 192.168.5.102/24 dev veth2
ip netns exec ns2 ip link set veth2 up
查看配置结果:
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns exec ns1 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
9: veth1@if8: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
link/ether 42:7b:d8:e5:47:82 brd ff:ff:ff:ff:ff:ff link-netnsid 0
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns exec ns2 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
11: veth2@if10: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
link/ether ca:18:9d:4c:8b:bc brd ff:ff:ff:ff:ff:ff link-netnsid 0
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns exec ns1 ifconfig
veth1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.5.101 netmask 255.255.255.0 broadcast 0.0.0.0
ether 42:7b:d8:e5:47:82 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@ubuntu-lab:/home/miao/ebpfkit-master# ip netns exec ns2 ifconfig
veth2: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.5.102 netmask 255.255.255.0 broadcast 0.0.0.0
ether ca:18:9d:4c:8b:bc txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
3.2 创建虚拟bridge,虚拟网卡连接bridge
命令如下:
#创建bridge
#brctl addbr br0
#将veth一端连接到bridge
ip link set dev veth1_p master br0
ip link set dev veth2_p master br0
#设置bridge的ip
ip addr add 192.168.5.1/24 dev br0
bridge和veth的启动:
ip link set veth1_p up
ip link set veth2_p up
ip link set br0 up
查看网络信息:
root@ubuntu-lab:/home/miao/ebpfkit-master# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.d246bc47bc05 no veth1_p
配置好后的ip示意图:
ip配置好的网络环境
搞定之后,利用ping测试下:
root@ubuntu-lab:/home/miao# ip netns exec ns1 ping 192.168.5.102
PING 192.168.5.102 (192.168.5.102) 56(84) bytes of data.
64 bytes from 192.168.5.102: icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from 192.168.5.102: icmp_seq=2 ttl=64 time=0.085 ms
64 bytes from 192.168.5.102: icmp_seq=3 ttl=64 time=0.085 ms
root@ubuntu-lab:/home/miao# ip netns exec ns2 ping 192.168.5.101
PING 192.168.5.101 (192.168.5.101) 56(84) bytes of data.
64 bytes from 192.168.5.101: icmp_seq=1 ttl=64 time=0.106 ms
64 bytes from 192.168.5.101: icmp_seq=2 ttl=64 time=0.093 ms
64 bytes from 192.168.5.101: icmp_seq=3 ttl=64 time=0.058 ms
刚开始也是不通的,后来发现原因,bridge 开启了iptables功能,导致所有经过br0的网络包都受到iptables的限制,docker为了安全性,将iptables的filter 表的FORWARD链默认设置成drop了,所以导致了ping的包被drop导致的。
关键是这条路由啊:
root@ubuntu-lab:/home/miao# iptables -A FORWARD -i br0 -j ACCEPT
3.3 换换网段测试看看
虽然测试的时候用的ip是属于同一个网段,但是 br0的ip设置在ping的时候是没有用的,更改下试试
root@ubuntu-lab:/home/miao# ip addr del 192.168.5.1/24 dev br0
root@ubuntu-lab:/home/miao# ip addr add 192.168.6.1/24 dev br0
ping测试的效果不受到影响:
root@ubuntu-lab:/home/miao# ip netns exec ns1 ping 192.168.5.102
PING 192.168.5.102 (192.168.5.102) 56(84) bytes of data.
64 bytes from 192.168.5.102: icmp_seq=1 ttl=64 time=0.092 ms
64 bytes from 192.168.5.102: icmp_seq=2 ttl=64 time=0.095 ms
^C
--- 192.168.5.102 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1038ms
rtt min/avg/max/mdev = 0.092/0.093/0.095/0.001 ms
root@ubuntu-lab:/home/miao# ip netns exec ns2 ping 192.168.5.101
PING 192.168.5.101 (192.168.5.101) 56(84) bytes of data.
64 bytes from 192.168.5.101: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 192.168.5.101: icmp_seq=2 ttl=64 time=0.063 ms
^C
--- 192.168.5.101 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1012ms
rtt min/avg/max/mdev = 0.063/0.076/0.090/0.013 ms
root@ubuntu-lab:/home/miao#
虽然每个网络空间的里面的br0的ip不对了,但是仍然不受到影响:
root@ubuntu-lab:/home/miao# ip -n ns1 route
default via 192.168.5.1 dev veth1
192.168.5.0/24 dev veth1 scope link
root@ubuntu-lab:/home/miao# ip -n ns2 route
default via 192.168.5.1 dev veth2
192.168.5.0/24 dev veth2 scope link
继续换下,让两个网络的ip属于不同网段玩玩。
root@ubuntu-lab:/home/miao# ip netns exec ns2 ip addr del 192.168.5.102/24 dev veth2
root@ubuntu-lab:/home/miao# ip netns exec ns2 ip addr add 10.25.5.102/24 dev veth2
root@ubuntu-lab:/home/miao# ip netns exec ns2 ip route
10.25.5.0/24 dev veth2 proto kernel scope link src 10.25.5.102
root@ubuntu-lab:/home/miao# ip -n ns2 route add 192.168.5.0/24 dev veth2
root@ubuntu-lab:/home/miao# ip -n ns1 route add 10.25.5.0/24 dev veth1
root@ubuntu-lab:/home/miao# ip netns exec ns1 ip route
10.25.5.0/24 dev veth1 scope link
192.168.5.0/24 dev veth1 scope link
root@ubuntu-lab:/home/miao# ip netns exec ns2 ip route
10.25.5.0/24 dev veth2 proto kernel scope link src 10.25.5.102
192.168.5.0/24 dev veth2 scope link
root@ubuntu-lab:/home/miao# ip addr
12: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether d2:46:bc:47:bc:05 brd ff:ff:ff:ff:ff:ff
inet 192.168.6.1/24 scope global br0
root@ubuntu-lab:/home/miao# ip netns exec ns1 ping 10.25.5.102
PING 10.25.5.102 (10.25.5.102) 56(84) bytes of data.
64 bytes from 10.25.5.102: icmp_seq=1 ttl=64 time=0.089 ms
64 bytes from 10.25.5.102: icmp_seq=2 ttl=64 time=0.103 ms
64 bytes from 10.25.5.102: icmp_seq=3 ttl=64 time=0.089 ms
哇竟然真的还是通的,br0,veth0,veth1分别设置了三个网段的地址,配置好路由后仍然不影响他们之间的通讯的。
虚拟网络空间的命令执行有些类似于docker,看看下面的命令 :
root@ubuntu-lab:/home/miao# ip netns exec ns1 /bin/bash
root@ubuntu-lab:/home/miao# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
9: veth1@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 42:7b:d8:e5:47:82 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.5.101/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::407b:d8ff:fee5:4782/64 scope link
valid_lft forever preferred_lft forever
root@ubuntu-lab:/home/miao# exit
exit
可以进入网络空间,也可以像上面那样,直接在网络空间执行命令。
为了区分不同的网络空间提示符,可以在执行exec命令的时候修改下:
oot@ubuntu-lab:/home/miao# ip netns exec ns1 /bin/bash --rcfile <(echo "PS1=\"namespace ns1> \"")
namespace ns1> ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
9: veth1@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 42:7b:d8:e5:47:82 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.5.101/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::407b:d8ff:fee5:4782/64 scope link
valid_lft forever preferred_lft forever
namespace ns1> exit
exit
网友评论