Index Blogs

Gentoo Hotplug Bridge

Published on 18 Sep 2019.

I've been playing a bunch with the Raspberry Pi Zero (abbreviated as rpi0 from here onwards) lately and have been accessing the devices mainly via their USB gadget ethernet (and a serial UART cable to debug when the network devices don't come up).

The problem I had was that each rpi0 was creating a separate network interface on the host PC each time it booted. Initially I had a script which looked a bit like this:

#!/bin/sh -x

route_zero()
{
    DEVICE=$1
    SUBNET=$2

    GATEWAY="$SUBNET.1"
    ip link set "$DEVICE" up &&

    ip addr flush dev "$DEVICE" &&
    ip route flush dev "$DEVICE" &&

    ip addr add "$GATEWAY" dev "$DEVICE" &&
    ip route add "$SUBNET.0/24" via "$GATEWAY" dev "$DEVICE"
}

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -o enp3s0 -j MASQUERADE

route_zero enp4s0u2 192.168.2
route_zero enp4s0u1 192.168.1

This script worked fine but I had to run it every time I rebooted the rpi0s or the host PC. I ended up setting up each of these enp4s0u1 and enp4s0u2 devices as their own /etc/rc.lo symlinks like:

ln -s /etc/net.{lo,enp4s0u1}
ln -s /etc/net.{lo,enp4s0u2}

I then added them to the rc_hotplug="enp4s0u1 enp4s0u2" in /etc/rc.conf. To access at least two of my rpi0s via their respective IPs on via each interface (though they were still on their separate subnets).

After thinking about it further I figured that actually a bridge interface was exactly what I needed:

            Host PC
+-----------------------------+
|                             |
|     +--------------------+  |
|     |                    |  |
|  +--+--+             +---v-->
|  | br0 <-----+       | eth0 <
|  +-----+     |       +------>
|              |              |
|     +------->-<-------+     |
|     |        |        |     |
|  +--+--+  +--+--+  +--+--+  |
|  |enps0|  |enps1|  |enps2|  |
+-----^-----------------------+
      |
      |     Pi Zero
      |    +-------------+
      +----+usb0|        |
           +----+        |
           |             |
           +-------------+

Note I renamed a couple of the interfaces in the diagram but it should still make sense. All the USB network interfaces are added as slaves to the br0 bridge and the routing table is updated to allow proper routing between the bridge and the external network interface.

The master bridge interface must be configured without any slaves so that it does not stop the net OpenRC dependency whenever (inevitably) the slaves don't exist (because they're just not plugged in). Each slave interface must have an entry in rc_hotplug in the /etc/rc.conf configuration file and the /etc/conf.d/net below:

config_enp4s0u1="null"
rc_net_enp4s0u1_provide="!net"

bridge_br0=""
config_br0="192.168.1.1/24"
routes_br0="192.168.1.0/24 via 192.168.1.1"

bridge_forward_delay_br0=0
bridge_hello_time_br0=1000

I did not find a way to actually register the slaves to bridge on hotplug without a bit of a hack. So each slave device must also have a postup() up script like the /etc/conf.d/net/enp4s0u1 below:

postup() {
    ip link set enp4s0u1 master br0
}

Maybe I should write a patch for adding this functionality to OpenRC itself in the future, but for now this setup works for me.