Skip to content

Instantly share code, notes, and snippets.

@jschwinger233
Last active May 15, 2020 09:53
Show Gist options
  • Save jschwinger233/a5cd21a5adf0ac0079e17fcc1179b37a to your computer and use it in GitHub Desktop.
Save jschwinger233/a5cd21a5adf0ac0079e17fcc1179b37a to your computer and use it in GitHub Desktop.

手动实现 bridge 网络和 calico 网络

本质上要解决的问题是连通 host 和 netns, 来体会一下两者的不同思路.

Bridge

用变量方便大家阅读, 否则区分不出语法和变量

NS=dock-ns3
VETH=vd3
BRIDGE=dock3

先创建 network namespace, 以下简称 netns

ip netns add $NS

创建 veth pair:

ip l add $VETH type veth peer name $VETH-peer

veth 拉起来, 一端塞到 netns 里, 在 netns 里拉起来, 设置 ip

ip l set $VETH-peer up
ip l set $VETH netns $NS
ip netns exec $NS ip l set $VETH up
ip netns exec $NS ip a add 10.5.0.2/16 dev $VETH

创建 bridge, 拉起来

ip l add name $BRIDGE type bridge
ip l set $BRIDGE up

把 veth 另一端加入 bridge

ip l set $VETH-peer master $BRIDGE

最后把 bridge 也设置上 ip:

ip a add 10.5.0.1/16 dev $BRIDGE

宿主机上的路由也会自动加上:

# ip r | grep dock3
10.5.0.0/16 dev dock3  proto kernel  scope link  src 10.5.0.1

这时候就可以在宿主机 ping netns 里的 ip 了.

如果按照上面的流程再创建另一个 netns 和 veth pair 并加入 bridge, 那么两个 netns 已经可以互 ping 了.

不过还有问题, netns 里 ping 不了外面的世界, 这是因为 netns 里的路由没有默认 gateway:

ip netns exec $NS ip r add default via 10.5.0.1

不过依然不可以, 这是因为没有 SNAT

iptables -t nat -A POSTROUTING -s 10.5.0.0/16 -j MASQUERADE

完工.

这里的几个细节几乎和大家熟悉的 docker0 if 一模一样, 我枚举一下:

  1. docker0 bridge 等价于 dock3 bridge
  2. netns 里的 route 设置一模一样, default gw 指向 bridge, 相同网络的指向 veth
  3. iptables nat 表 POSTROUTING chain 里把所在网络全部 SNAT

Calico

calico 的做法完全没有 bridge 这种妖艳贱货, 而是 veth 直通, 路由指路.

先设置变量:

NS=cali
VETH=v-cali

创建 netns 和 veth, veth 一端塞进去, 射 ip:

ip netns add $NS

ip l add $VETH type veth peer name $VETH-peer

ip l set $VETH-peer up
ip l set $VETH netns $NS
ip netns exec $NS ip l set $VETH up

ip netns exec $NS ip a add 10.2.0.1/32 dev $VETH

然后在宿主机直接路由 ip 到 veth:

ip r add 10.2.0.1/32 dev $VETH-peer

netns 里设置 default gw 到 veth:

ip netns exec $NS ip r add default dev $VETH

这时候可以从 host ping netns, 但是反过来就不可达, 抓包发现是因为 arp 不知道 mac 地址, 加上 arp proxy

echo 1 > /proc/sys/net/ipv4/conf/$VETH-peer/proxy_arp
sysctl -p

发现还是不行, 抓包发现 arp 没问题了但是 ICMP 没有回复, 这是因为 SNAT:

iptables -t nat -A POSTROUTING -s 10.6.0.1/32 -j MASQUERADE

这下可以和 host 互 ping 了, google.com 也没问题了, 功能上没问题.

不过有个优化的问题, arp proxy 会有一些问题, 比如这里会导致 netns 里的 arp cache 无限扩张, 所有的 outbound ip 都会产生一条 arp entry.

为了解决这个问题, 我们用一个假的 ip 169.254.1.1 作为 link-local address, 绕一下:

ip netns exec $NS ip r del default dev $VETH
ip netns exec $NS ip r add 169.254.1.1 dev $VETH  scope link
ip netns exec $NS ip r add default via 169.254.1.1 dev $VETH

这下所有的 outbound 都会变成对 169.254.1.1 arp, 也不会产生无数的 entry.

和 calico 比较一下, 要点有这么几个:

  1. arp proxy 代替了 bridge 打通二层
  2. 169.254.1.1 这个 ip 也是 calico 使用的 ip, 在 calico 创建的 netns 路由里这个迷一样的 ip 让无数人神魂颠倒

当然剩下的事情我就不说了, bird 监听到宿主机的新路由后用 BGP 广播 Update 消息, 从而在完成跨宿主机的路由建立.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment