























Hello! Yesterday I spent a lot of time trying to understand what a bridge is so here are my notes. I’m only going to talk about bridges as applied to container / VM networking because that’s what I’m trying to do and also I’ve also never seen a bridge used for anything else.
Some things in this post are almost certainly wrong.
I’ve been setting up some VMs using AWS Firecracker (which is amazing and lets you start up VMs in like ONE SECOND, it’s extremely fast, and I will write about it in the future because I really love it). Yesterday morning I was trying to configure the VM’s networking so that I could connect to the outside internet from inside the VM. I was googling how to do it and there were all these references to “make a bridge”, so I figured I needed to use a bridge.
I’ve been avoiding understanding what a bridge is for years because it seemed confusing, but I copied and pasted some things from Github issues / various blog posts and none of them did what I wanted.
So I figured the “blindly copy and paste” approach wasn’t really going to work and decided OKAY TODAY IS THE DAY I WILL LEARN WHAT A BRIDGE IS.
I’m going to use a bunch of Docker examples in this post even though I’m not planning to use Docker because it’s already set up on my computer, it basically does what I want to do (but using a veth instead of a tap device), and I’m more familiar with it.
I asked on Twitter for help understanding Docker container networking and people linked me to 2 really good blog posts that helped me:
perf trace an eBPF. The perf trace example in
particular is super easy to use and it helped me feel more confident about
what was going onOne thing I learned is that a bridge is a layer 2 (ethernet) device that can have an arbitrary number of network interfaces attached to it.
My understanding of how it works is:
Docker uses veth pairs and the VM setup I’m using right now is using taps. These are also both layer 2 network interfaces. So they just sort of send packets on blindly and something else is responsible for setting the correct MAC addresses on packets.
One question I had was – if you’re sending a packet to a container and it needs to have the right MAC address on it to make it through the bridge, how does it know what the right MAC address is? I think the answer is the same as in a physical network: ARP!
So your computer sends an ARP request like “hey who’s 172.17.0.8” and the container sends an ARP reply back like “it’s me! here’s my MAC address”. The bridge gets involved here because it needs to send the ARP request to the container and the ARP reply back to the host.
I’m not 100% sure about this but I saw some ARP requests/replies being sent back and forth and it makes sense. I think this is really funny because – all of this information is on the same computer and it doesn’t feel like it should need to use ARP to look up MAC addresses, like it has all the information already! But I guess it just literally pretends like it’s a physical network.
I think the most important thing with bridges is to set up the route tables correctly. So far my understanding is that there are 2 route table entries you need to set:
route entry 1: on the host
The first route entry that needs to be set is on the host, to make sure that everything on the bridge subnet gets sent to the bridge. Here’s what that looks like in Docker’s default networking setup.
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
route entry 2: inside the container/VM
In the container/VM, the system’s gateway needs to be set to the bridge
For example, in a Docker container, you’ll see this:
$ ip route list
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
This default via 172.17.0.1 dev eth0 line means that all packets going
outside the container subnet should be first sent to 172.17.0.1, which is the
docker0 bridge (actually it’s a veth pair which leads to the docker0 bridge)
The third thing I need if I want the containers to be able to talk to the wider internet is an SNAT iptables rule on the host. Here’s what that looks like in Docker’s default setup.
$ sudo iptables-save
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
I’m still not 100% on bridges but I did manage to configure a bridge so that when I ssh into my Firecracker VM I can access the internet! I am extremely delighted by this.
Right now I’m reusing the docker0 bridge which I’ll probably stop doing, but
it does work.
My understanding of all the steps to get a Docker-like setup where you can access a VM and it can access the outside internet are:
tap device or a veth) (ip tuntap add dev "$TAP_DEV" mode tap)sudo brctl addif docker0 $TAP_DEV)ip link set dev "$TAP_DEV" up)iptables)/etc/resolv.conf to say nameserver 8.8.8.8 because I don’t have a working local resolver inside the VMHere’s a snippet from my script that starts a firecracker VM. I modified it from the firecracker-demo code.
I might post the whole thing later after I clean it up and stop using the docker bridge.
CONTAINER_IP=172.17.0.33
GATEWAY_IP=172.17.0.1
DOCKER_MASK_LONG=255.255.255.0
ip tuntap add dev "$TAP_DEV" mode tap
sudo brctl addif docker0 $TAP_DEV
ip link set dev "$TAP_DEV" up
# I'm not sure what these two are for exactly
sysctl -w net.ipv4.conf.${TAP_DEV}.proxy_arp=1 > /dev/null
sysctl -w net.ipv6.conf.${TAP_DEV}.disable_ipv6=1 > /dev/null
I also had to set ip=${CONTAINER_IP}::${GATEWAY_IP}:${DOCKER_MASK_LONG}::eth0:off in the kernel boot args. to set the VM’s gateway.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。