Linux Bridge 剖析
文章目录
最近看了一些 Linux Bridge 的资料. 以及看看了 Linux kernel 的代码实现.
the very begining
因为最近使用 k8s CNI 中的 main plugin 和自制的 IPAM 插件来实现 CNI, 了解了许多 main plugin 下的知识. 其中又比较仔细地看了下 bridge 的实现.
Linux Bridge 的组成
- 一些网络端口 ( a set of network ports)
- 一个控制配置逻辑 (a control plane)
- 一个路由逻辑 (a forwarding plane)
- 一个 MAC 端口映射表 (a MAC learning database)
Linux Bridge 数据结构
以下代码来自 torvalds/linux/net/bridge
net_bridge
1 | struct net_bridge { |
- 是最主要的数据结构, 包含了该 Bridge device 的所有配置.
- 一个
net_bridge_port
组成的双向链表port_list
*dev
指向当前设备hash
指向 MAC 端口映射表
net_bridge_port
1 | struct net_bridge_port { |
net_bridge_fdb_entry
1 | struct net_bridge_fdb_entry { |
Bridge 的创建和添加 dev
用到了 ioctl 的 SIOCBRADDBR 和 SIOCBRADDIF. 添加 dev 到 Bridge 的过程, 会将目标 dev 添加到 MAC 端口映射表中去.
package 处理
主要是分为两块, 首先是 get 或者 update MAC 端口印射表, 然后根据是否找到对应的 MAC-port 以及 dest-MAC 的类型来决定是如何将进入 Bridge 的包转发出去. 分别由 br_forward
/br_multicast_forward
/br_flood_forward
来发起单播/组播/广播.
VEB and VEPA
- VEB 即
Virtual Ethernet Bridges
- VEPA 即
Virtual Ethernet Port Aggregator
也即发卡弯模式. brctl hairpin <bridge> <port> {on|off}
turn hairpin on/off
该部分对 bridge 上端口的设置, 影响的主要是同一个 bridge 下 device 包的走向. 具体差距可以参考以下.
Docker 下的 bridge
docker network create -d bridge br-0
会创建一个 subnet: 172.23.0.0/16
, gateway: 172.23.0.1
的 Bridge 网络. 然后使用 docker run -p PORT IMAGE
运行的容器会被分配一个该子网下的 IP, 并在主机上通过 iptables DNAT 主机的 port 到该 IP.
k8s 下的 Bridge (CNI main plugin)
会根据 CNI 配置文件 /etc/cni/net.d/*.conf[list]
来对本地进行配置 Linux Bridge. 而 IPAM 一般可以使用 host-local. 但仅仅使用 bridge + host-local 的 CNI plugin 甚至不能满足 k8s 下对 CNI 的基本要求–即 pod 和 pod 之间可以不经过 NAT 直接进行连通. 因为该 bridge 创建并分配的子网网络并没有一个路由的出口(如主机在 192.168.0.0/16 网段, 而 host-local 在 10.0.0.0/8 网段), 外部也没有一个可以访问 pod 的手段(192.168.0.0/16 的主机无法访问某台主机上的 10.x.y.z 的 pod).
Trick in bridge
- k8s 下的 CNI plugin 都是单独的代码进行打包, 实现的非常简单(甚至例如 bridge/macvlan/ipvlan 等简单的网络虚拟手段, 都可以用 brctl/ip 等命令来实现, 除了解析 json 需要用到 jq 略麻烦). 而 bridge 在实现的时候, 有一个步骤会确认主机上所需的 br0 是存在的. 也就是说如果提前创建好了的 br0, bridge 插件并不会重新去创建.
- 前面没有提到的是, 任何设备使用
brctl addif <bridge> <device>
之后, 则流经该设备的流量都会由该<bridge>
来处理. - 所以, 在容器之前, 那个虚拟机管理下的
桥接模式
是将本机的 eth0 add 到了一个网桥上, 并通过和 eth0 所在的网络直接进行网络 IP 的分配, 也即虚拟机可以获取和主机同网段的 IP 地址, 或者是自己手动指定 static 的 IP 地址. - 同样的, 该方式在 k8s 的 bridge CNI plugin 也能实现, 甚至还能进一步地实现指定 pod 指定 IP 的功能. 即给 192.168.0.0/16 网段主机上的容器分配同网段的 IP 给 pod.
- 该方式的利弊端都很明显, 弊端即需要预先配置一个网桥, 还有会占用掉主机端的 IP, 以及网络隔离可能会引入复杂 vlan 划分方式, 利端则是该方式无需外部路由配置即可实现 k8s 对 CNI 的基本要求(需要上游交换机打开混杂模式), IP 管理的问题在一个提前规划的生产环境内可以降低到可接受的范围.
Reference
torvalds/linux
Anatomy of a Linux bridge
Edge Virtual Bridging with VEB and VEPA
A Hacker’s Guide to Kubernetes Networking
原文作者: Pike.SZ.fish
原文链接: https://page.pikeszfish.me/2018/01/20/anatomy-of-a-linux-bridge/
许可协议: 本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可