为一张物理网卡设置多个 mac 地址,相当于物理网卡施展了影分身之术,由一个变多个,同时要求物理网卡打开混杂模式。针对每个 mac 地址,都可以设置 IP 地址,本来是一块物理网卡连接到交换机,现在是多块虚拟网卡连接到交换机

Macvlan 允许单个物理接口使用 macvlan 子接口拥有多个 mac 和 ip 地址。这与使用 vlan 在物理接口上创建子接口不同。对于 vlan 子接口,每个子接口使用 vlan 属于不同的 L2 域,并且所有子接口具有相同的 MAC 地址。使用 macvlan,每个子接口都会获得唯一的 mac 和 ip 地址,并直接暴露在底层网络中。Macvlan 接口通常用于虚拟化应用程序,每个 macvlan 接口都连接到一个 Container 或 VM。每个容器或 VM 都可以像主机一样直接从公共服务器获取 dhcp 地址。这将有助于已经拥有 IP 寻址方案的 Containers 成为其传统网络的一部分的客户。

四种模式

  • private mode:过滤掉所有来自其他 macvlan 接口的报文,因此不同 macvlan 接口之间无法互相通信
  • vepa (Virtual Ethernet Port Aggregator) mode: 需要主接口连接的交换机支持 VEPA/802.1Qbg 特性。所有发送出去的报文都会经过交换机,交换机作为再发送到对应的目标地址(即使目标地址就是主机上的其他 macvlan 接口),也就是 hairpin mode 模式,这个模式用在交互机上需要做过滤、统计等功能的场景。
  • bridge mode:通过虚拟的交换机讲主接口的所有 macvlan 接口连接在一起,这样的话,不同 macvlan 接口之间能够直接通信,不需要将报文发送到主机之外。这个模式下,主机外是看不到主机上 macvlan interface 之间通信的报文的。

【bridge 模式是在不同的命名空间下才可以通信,数据包的目标 IP 地址就是自身的 IP 地址后,会直接将其送回给网络层,进行 ICMP Echo 回复数据包的处理,因此这个数据包并不会传递到数据链路层,也就不会被捕获

  • passthru mode:这种模式,只允许单个子接口连接主接口,且必须设置成混杂模式,一般用于子接口桥接和创建 VLAN 子接口的场景

VEPA 和 passthru 模式下,两个 macvlan 接口之间的通信会经过主接口两次:第一次是发出的时候,第二次是返回的时候。这样会影响物理接口的宽带,也限制了不同 macvlan 接口之间通信的速度。如果多个 macvlan 接口之间通信比较频繁,对于性能的影响会比较明显。

private 模式下,所有的 macvlan 接口都不能互相通信,对性能影响最小。

bridge 模式下,数据报文是通过内存直接转发的,因此效率会高一些,但是会造成 CPU 额外的计算量。

macvlan 的四种通信模式,常用模式是 bridge。

modprobe macvlan
lsmod | grep macvlan
macvlan                28672  0
ip link set eth0 promisc on
ip link add mv2 link eth0 type macvlan mode bridge
ip addr add 192.168.1.2/24 dev mv2
ip link set mv2 up
ip link add mv3 link eth0 type macvlan mode bridge
ip addr add 192.168.1.3/24 dev mv3
ip link set mv3 up

macvlan  原理及实测试_IP

ip -d link show

macvlan  原理及实测试_docker_02

macvlan  原理及实测试_docker_03

可以看到,macvlan的brigde 模式下是无法直接通信的,而且是抓不到通信数据包的(除了arp发出的arp广播,但是,只能在发出的网卡上抓到,没有发送到对端,ping自己是抓包任何包的,因为没有经过二层,在 mv1 上执行 ping 操作,系统会创建一个 ICMP Echo 请求数据包,然后将其发送到网络堆栈。网络堆栈在看到这个数据包的目标 IP 地址就是自身的 IP 地址后,会直接将其送回给网络层,进行 ICMP Echo 回复数据包的处理,因此这个数据包并不会传递到数据链路层,也就不会被 mv1 捕获),

root@vm-132:~# tcpdump -i eth0 icmp and dst 192.168.2.2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
root@vm-132:~# tcpdump -i mv0 icmp and dst 192.168.2.2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on mv0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel

加入不同的命名空间,找vm-133测试(docker 每个容器都有自己的命名空间,使用没问题)

# 创建两个新的网络命名空间
ip netns add ns0
ip netns add ns1

# 创建两个macvlan接口,并将它们分别移动到新的网络命名空间
ip link add mv0 link eth0 type macvlan mode bridge
ip link set mv0 netns ns0
ip link add mv1 link eth0 type macvlan mode bridge
ip link set mv1 netns ns1

# 在每个网络命名空间中为macvlan接口分配IP地址并启动接口
ip netns exec ns0 ip addr add 192.168.2.2/24 dev mv0
ip netns exec ns0 ip link set mv0 up
ip netns exec ns1 ip addr add 192.168.2.3/24 dev mv1
ip netns exec ns1 ip link set mv1 up

macvlan  原理及实测试_macvlan_04

macvlan  原理及实测试_命名空间_05

macvlan  原理及实测试_docker_06

docker 网络使用macvlan

docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=eth0 mac1
docker run -itd --name c1 --ip=172.16.10.2 --network mac1 busybox
docker run -itd --name c2 --ip=172.16.10.3 --network mac1 busybox

root@Copy-of-VM-U2204:~#  docker exec c1 ip r
default via 172.16.10.1 dev eth0 
172.16.10.0/24 dev eth0 scope link  src 172.16.10.2 
root@Copy-of-VM-U2204:~# docker exec c2 ip r
default via 172.16.10.1 dev eth0 
172.16.10.0/24 dev eth0 scope link  src 172.16.10.3 
root@Copy-of-VM-U2204:~# docker exec c1 ping -c 2 172.16.10.3
PING 172.16.10.3 (172.16.10.3): 56 data bytes
64 bytes from 172.16.10.3: seq=0 ttl=64 time=0.031 ms
64 bytes from 172.16.10.3: seq=1 ttl=64 time=0.030 ms

--- 172.16.10.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.030/0.030/0.031 ms

由于macvlan的特性,在同一个网络或者vlan内,只要能发arp广播对目标收到,都可以通信(网络策略没有限制的情况下)

因为每个虚拟的地址都有自己的mac和ip,物理mac帮他们直接通信,就是局域网ip互相通,加上网关并设置转发,就可以不同网络通信

server1  server2 server3(网关),ubuntu2204

#server1 与 server2 都执行
ip link set eth0.100 up
ip link set eth0.200 up
ip link add link eth0 name eth0.100 type vlan id 100
ip link add link eth0 name eth0.200 type vlan id 200

docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=eth0.100 macvlan10
docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=eth0.200 macvlan20

#server1

docker run -itd --name d1 --ip=172.16.10.10 --network macvlan10 busybox
docker run -itd --name d2 --ip=172.16.20.10 --network macvlan20 busybox

#server2

docker run -itd --name d3 --ip=172.16.10.11 --network macvlan10 busybox
docker run -itd --name d4 --ip=172.16.20.11 --network macvlan20 busybox

#server3

一定要关闭防火墙及开启转发,否则可以ping网关而不能转发
ufw disable
sysctl -w net.ipv4.ip_forward=1

ip link set dev eth0.100 type vlan reorder_hdr on
ip link set dev eth0.200 type vlan reorder_hdr on
设置 VLAN 接口 eth0.100 和 eth0.200的 REORDER_HDR 标志为 1,这使得内核在发送和接收 VLAN 数据包时重新排序 VLAN 标签和 IP 数据包头
ifconfig eth0.100 172.16.10.1 netmask 255.255.255.0 up
ifconfig eth0.200 172.16.20.1 netmask 255.255.255.0 up

Macvlan的 vepa 测试

docker network create -d macvlan --subnet=192.168.122.0/24 --gateway=192.168.122.1 -o parent=eth0 -o macvlan_mode=vepa vepamv
docker network inspect vepamv

docker run -itd --net=vepamv --ip=192.168.122.22 --name=macvlan-vepa-1 busybox /bin/sh
docker run -itd --net=vepamv --ip=192.168.122.23 --name=macvlan-vepa-2 busybox /bin/sh

macvlan  原理及实测试_docker_07

macvlan-vepa-2 中 ping macvlan-vepa-1 发现无法 ping 通,因为本地环境上并没有开启 hairpin 模式的交换机或路由器

创建 L3 模式的 IPVLAN 接口:
ip link add ipvlan0 link eth0 type ipvlan mode l3
ip link add ipvlan1 link eth0 type ipvlan mode l3
直接物理接口docker只能创建一个ipvlan网络
ip lin set ipvlan0 up
ip lin set ipvlan1 up
docker network  create  -d ipvlan \
    --subnet=192.168.30.0/24 \
    -o parent=ipvlan0 \
    -o ipvlan_mode=l3 ipvlan30

docker network  create  -d ipvlan \
    --subnet=192.168.110.0/24 \
    -o parent=ipvlan1 \
    -o ipvlan_mode=l3 ipvlan110    

docker run --net=ipvlan30 -it --name ivlan_test3 --rm alpine /bin/sh
docker run --net=ipvlan30 -it --name ivlan_test4 --rm alpine /bin/sh
docker run --net=ipvlan110 -it --name ivlan_test5 --rm alpine /bin/sh


macvlan  原理及实测试_IP_08