Linux网络虚拟化
文章目录
- Linux网络虚拟化
- 前言
- 一、network namespace
- 1.1 初识network namespace
- 1.2 配置network namespace
- 二、veth pair
- 三、容器与host veth pair的关系
- 3.1 方法一
- 3.2 方法二
- 3.2 方法三
- 四、 Linux bridge
- 4.1 Linux bridge初体验
- 4.2 把IP让给Linux bridge
- 4.3 与外部网关的情况
- 五、Linux bridge在网络虚拟化中的应用
- 5.1 虚拟机
- 5.2 容器
- 六、 网络接口的混杂模式
前言
一、network namespace
和其他namespace一样,network namespace可以通过系统调用来创 建,我们可以调用Linux的clone()(其实是UNIX系统调用fork()的 延伸)API创建一个通用的namespace,然后传入CLONE_NEWNET参数 表面创建一个network namespace
network namespace的增删改查功能已经集成 到Linux的ip工具的netns子命令中,因此大大降低了初学者的体验门槛。
1.1 初识network namespace
# 创建一个名为netns1的network namespace
ip netns add netns1
# ip netns exec命令进入
ip netns exec netns1 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
#查看系统中有哪些network namespace
ip netns list
netns1
# 删除network namespace,
ip netns delete netns1
1.2 配置network namespace
# 自带的lo设备状态还是DOWN
ip netns exec netns1 ping 127.0.0.1
# 设备状态设置成UP
ip netns exec netns1 ip link set dev lo up
#本地回环设备是没法与外界通信的。如果我们想与 外界(比如主机上的网卡)进行通信,
#就需要在namespace里再创建一 对虚拟的以太网卡,即所谓的veth pair。
# 创建一对虚拟以太网卡,在默认情况下,它们都在主机的根network namespce中
ip link add veth0 type veth peer name veth1
# veth1 放到 netns1 network namespace
ip link set veth1 netns netns1
# 为veth1绑定 IP地址10.1.1.1/24,设置up
ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
ifconfig veth0 10.1.1.2/24 up
# 确认通信
ping 10.1.1.1
ip netns exec netns1 ping 10.1.1.2
# 不同network namespace之间的路由表和防火墙规则相关隔离
ip netns exec netns1 route
ip netns exec netns1 iptables
需要注意的是,用户可以随意将虚拟网络设备分配到自定义的 network namespace里,而连接真实硬件的物理设备则只能放在系统的根 network namesapce中。并且,任何一个网络设备最多只能存在于一个 network namespace中。
从主机的 根network namespace到用户自定义network namespace,反之亦可。
ip netns exec netns1 ip link set veth1 netns 1
# netns1 network 下的veth1网卡挪到PID为1的进程(即init进程)所在的network namespace
# 把veth1从netns1 network namespace移动到系统根 network namespace。
# 有两种途径索引network namespace:名字(例如 netns1)或者属于该namespace的进程PID,上文中用的就是后者。
二、veth pair
veth是虚拟以太网卡(Virtual Ethernet)的缩写。veth设备总是成对 的,因此我们称之为veth pair。veth pair一端发送的数据会在另外一端接 收,非常像Linux的双向管道。
仅有veth pair设备,容器是无法访问外部网络的。 为什么呢?因为从容器发出的数据包,实际上是直接进了veth pair设备 的协议栈。如果容器需要访问网络,则需要使用网桥等技术将veth pair设备接收的数据包通过某种方式转发出去
三、容器与host veth pair的关系
经典容器组网模型就是veth pair+bridge的模式。容器 中的eth0实际上和外面host上的某个veth是成对的(pair)关系,那么, 有没有办法知道host上的vethxxx和哪个container eth0是成对的关系呢?
3.1 方法一
# 目标容器里查看
cat /sys/class/net/eth0/iflink
6
# 在主机上遍历/sys/claas/net下面的全部目录 或者 用 ip addr 查看6
cat /sys/class/net/vethb0fbeff/ifindex
6
# 例子中主机上vethb0fbeff的ifindex刚好是6,意味着是目标容器veth pair的另一端
3.2 方法二
# 目标容器里查看,其中116是eth0接口的 index,117是和它成对的veth的index
ip link show eth0
2: 116:eth0@if117: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:4d:77:d3 brd ff:ff:ff:ff:ff:ff
# 当host执行,可以看到对应117的veth网卡是哪一个
ip link show | grep 117
3.2 方法三
可以通过ethtool-S命令列出veth pair对端的网卡index
# 目标容器里查看,其中116是eth0接口的 index,117是和它成对的veth的index
ethtool -S eth0
NIC statistics:
peer_ifindex:6
# 当host执行,可以看到对应117的veth网卡是哪一个
ip addr
6: vethb0fbeff@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ea:43:af:c8:f7:aa brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::e843:afff:fec8:f7aa/64 scope link
valid_lft forever preferred_lft forever
四、 Linux bridge
两个network namespace可以通过veth pair连接,但要做到两个以上 network namespace相互连接,veth pair就显得捉襟见肘了。这就轮到本节的主角Linux bridge出场了。
网桥 是二层网络设备,两个端口分别有一条独立的交换信道,不共享一条背 板总线,可隔离冲突域。网桥比集线器(hub)性能更好,集线器上各 端口都是共享同一条背板总线的。后来,网桥被具有更多端口、可隔离 冲突域的交换机(switch)所取代。
4.1 Linux bridge初体验
为了充分发挥Linux bridge的作用,我们特将它和前文介绍的veth pair配合起来使用。我们将创建一对veth设备,并配置IP地址
# iproute2软件包里的ip命令创建一个bridge
ip link add name br0 type bridge
ip link set br0 up
# 删除命令
ip link delete br0 type bridge
# 创建veth0, 结合 eth0 地址设置
ip link add veth0 type veth peer name veth1
ip addr add 10.10.2.101/24 dev veth0
ip addr add 10.10.2.102/24 dev veth1
ip link set veth0 up
ip link set veth1 up
# 通过下面的命令将veth0连接到br0上
ip link set dev veth0 master br0
# 查看添加成功
bridge link
14: veth0 state UP @veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state disabled priority 32 cost 2
连接veth pair的bridge设备的网络拓扑如图:
br0和veth0相连之后发生了如下变化:
- br0和veth0之间连接起来了,并且是双向的通道;
- 协议栈和veth0之间变成了单通道,协议栈能发数据给veth0,但 veth0从外面收到的数据不会转发给协议栈;
- br0的MAC地址变成了veth0的MAC地址。
需要打开eth0网卡的混杂模式(后续会 详细介绍Linux bridge的混杂模式),不然veth1的网络会不通。
不通:这是由于我使用的 Ubuntu 系统内核中一些 ARP 相关的默认配置限制所导致的,需要修改一下配置项
#
echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/veth0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter
# 从veth0 ping veth1, veth0 ping veth1失败。为什么veth0加入bridge之后失败呢
ping -c 1 -I veth0 10.10.2.102
# 由于veth0的ARP缓存里没有veth1的MAC地址,所以 ping之前先发ARP请求。
# veth1抓取的报文显示,veth1收到了ARP请求, 并且返回了应答
tcpdump -n -i veth1
# veth0上的数据包都发出去了,而且也收到了响应
tcpdump -n -i veth0
# 下报文可以看出,包的去和回的流程都没有问题,
# 问题 就出在veth0收到应答包后没有给协议栈,而是给了br0,于是协议栈 不到veth1的MAC地址,导致通信失败
tcpdump -n -i br0
安装 tcpdump 命令, 可到“http://www.tcpdump.org”下载最新的Tcpdump源码包。
# 首先安装相关依赖包
yum -y install gcc
yum -y install gcc-c++
yum -y install cgdb
yum -y install git
yum -y install flex
yum -y install flex-devel
yum -y install bison
#
4.2 把IP让给Linux bridge
通过上面的分析可以看出,给veth0配置IP没有意义,因为就算协议栈传数据包给veth0,回程报文也回不来。这里我们就把veth0的IP地 址“让给”Linux bridge。
ip addr del 10.10.2.101/24 dev veth0
ip addr add 10.10.2.101/24 dev br0
# 此时 应该能够正常ping
ping -c 3 -I br0 10.10.2.102
将协议栈和veth0之间的联系去掉了,veth0相当于一根网线。 实际上,veth0和协议栈之间是有联系的,但由于veth0没有配置IP,所 以协议栈在路由的时候不会将数据包发给veth0。就算强制要求数据包 通过veth0发送出去,由于veth0从另一端收到的数据包只会给br0,协议 栈还是没法收到相应的ARP应答包,同样会导致通信失败。
4.3 与外部网关的情况
Linux bridge不会区分接入进来的到底是物理设备还是虚拟设备,对 它来说没有区别。因此,eth0加入br0后,落得和上面veth0一样的“下 场”,从外面网络收到的数据包将无条件地转发给br0,自己变成了一根网线。
当前现况: 虚拟机对外网关地址为 192.168.33.1
# 成功的
ping -c 1 -I eth0 192.168.33.1
# 主机上的物理网卡eth0添加到Linux bridge
ip link set dev eth0 master br0
# 过eth0 ping网关失败, br0 ping网关成功, 通过veth1 ping网关成功, 通过eth0 ping网关失败
ping -c 1 -I eth0 192.168.33.1
ping -c 1 -I br0 192.168.33.1
# 如果ping的时候不指定网卡, 则协议栈有可能优先选择eth0,导致ping不通。
# 因此,需要将eth0上的IP 去掉。在以上测试过程中,由于eth0上有IP,在访问1.2.3.0/24网段时, 会优先选择eth0。
# 查看主机路由表,
route -v
# eth0接入了br0,因此它收到的数据包都会转发给br0,于是协议栈 收不到ARP应答包,导致ping失败
# 将eth0上的IP删除, 路由表里就没有它
ip addr del 10.0.2.101/24 dev eth0
# 通过观察以上路由表信息可以看出:原来的默认路由进过eth0, eth0的IP被删除后,默认路由不见了,
# 想要连接1.2.3.0/24以外的网段, 需要手动将默认网关加回来
ip route add default via 192.168.3.1
ping www.baidu.com
五、Linux bridge在网络虚拟化中的应用
5.1 虚拟机
虚拟机通过tun/tap或者其他类似的虚拟网络设备,将虚拟机内的网 卡同br0连接起来;
在虚拟机场景下,我们给主机物理网卡eth0分配了IP地 址
5.2 容器
- 虚拟机使用的 是tun/tap设备, 容器使用的是veth pair设备,
- 在虚拟机场景下,我们给主机物理网卡eth0分配了IP地 址;而在容器场景下,我们一般不会对宿主机eth0进行配置。
- 在虚拟机场景下,虚拟器一般会和主机在同一个网段; 而在容器场景下,容器和
物理网络不在同一个网段内。
因此,从容器发出去的数据包先到达br0,然 后交给host机器的协议栈。由于目的IP是外网IP,且host机器开启了IP forward功能,数据包会通过eth0发送出去。因为容器所分配的网段一般 都不在物理网络网段内,所以一般发出去之前会先做NAT转换(NAT转换需要自己配置,可以使用iptables,1.5节会介绍iptables)
六、 网络接口的混杂模式
ifconfig eth0,查看eth0的配置,包括混杂模式。当输出包含 PROMISC时,表明该网络接口处于混杂模式。 在混杂模式下,网卡会接收经过它的所有 帧!
ifconfig eth0
# 启用网卡的混杂模式
ifconfig veth0 promisc
# 退出网卡的混杂模式
ifconfig eth0 -promisc