Homebridge, Docker, and Wake-on-Lan

Continuing on from the previous post in this series, I have been doing a bit more investigation into solving the issues I was having with the homebridge-samsung-tizen plugin. All the features of the plugin were functioning as expected, with the exception of powering on the TV. This depends on Wake-on-Lan, which by default only works in the same network subnet, something which is not true unless the docker container is ran in host network mode. I have now found how to configure this to work.

Listening for Broadcast Traffic

For the purposes of investigating this I did not want to be turning my TV on and off repeatedly, so I used tcpdump to inspect the network traffic that used the ports that can be used for Wake-on-Lan packets.

In this example I had attached tcpdump to the interface the docker container was in directly, just to verify the approach worked.

dhutchison@procent:~$ sudo tcpdump -i br-b0695d90ec6a '(udp and port 7) or (udp and port 9)'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-b0695d90ec6a, link-type EN10MB (Ethernet), capture size 262144 bytes
14:34:21.033509 IP 172.29.0.6.35290 > 172.29.255.255.discard: UDP, length 102
14:34:21.134265 IP 172.29.0.6.35290 > 172.29.255.255.discard: UDP, length 102
14:34:21.234811 IP 172.29.0.6.35290 > 172.29.255.255.discard: UDP, length 102

So broadcasting to the docker network subnet works fine, we just need to establish how to cross the subnet boundary.

For more isolated testing, I created a basic Docker container which used the python awake library for sending Wake-on-Lan packets.

FROM python:3-alpine  
RUN pip3 install awake

ENTRYPOINT ["awake"]

Once built (I called it “wol”), this takes in a few arguments when ran:

  • the broadcast IP address of the network to send the packet to
  • the mac address of the device to be woken
pi@devpi1:~ $ docker run --rm --network=core_traefik-backend wol -b 192.168.0.255 aa:bb:cc:dd:ee:ff
Sending magic packet to 192.168.0.255 with broadcast 192.168.0.255 MAC aa:bb:cc:dd:ee:ff port 9

Allowing Broadcast Traffic to be Re-Broadcast

The controls which prevent a broadcast packet received on one network interface being sent to another are part of the linux kernel. In our scenario one of these network interfaces is a docker bridge network as opposed to an external network, but the same controls apply.

These settings require to be set:

  • net.ipv4.icmp_echo_ignore_broadcasts=0
  • net.ipv4.conf.all.bc_forwarding=1
  • net.ipv4.conf.${interface}.bc_forwarding=1

This last setting requires “${interface}” to be replaced with the name of the network interface which relates to the docker network. Even though the setting before it is for “all”, it does not seem to work on its own and the specific interface setting also isn’t enough on its own.

I have put together a script configure_docker_networks_for_wol.sh, which for a specific docker network, configures these settings. These are configured using both “sysctl -w” and as a configuration file in “/etc/sysctl.d/” so they will be applied instantly and survive a reboot.

With this combination of settings configured, I can now use the test containing using awake and see the packet received by another device on the target network when using tcpdump.

pi@devpi1:~ $ docker run --rm --network=core_traefik-backend wol -b 192.168.0.255 aa:bb:cc:dd:ee:ff
Sending magic packet to 192.168.0.255 with broadcast 192.168.0.255 MAC aa:bb:cc:dd:ee:ff port 9


dhutchison@MacBook:~ $ sudo tcpdump -i en0 '(udp and port 9)'
listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:36:25.537328 IP devpi1.lan.46235 > 192.168.0.255.discard: UDP, length 102

Homebridge Plugin Configuration

The configuration for the homebridge-samsung-tizen plugin allows for an extra wol element to define the source IP for the Wake-on-Lan packet and the destination.

"platforms": [
    {
        "platform": "SamsungTizen",
        "devices": [
            {
                "name": "TV",
                "ip": "192.168.0.30",
                "mac": "AB:CD:EF:12:34:56",
                "wol": {
                    "from": "172.32.0.200",
                    "address": "192.168.0.255"
                }
            }
        ]
    }
]

This is passed directly to the wakeonlan library. If the from attribute is not set then the address will be ignored and the library will perform its default behaviour of calculating the broadcast address for each network interface. We need to change this to the broadcast address of the subnet the destination device for the Wake-on-Lan packet is in.

This from address has to be a valid IP address for the interface that is connected to our docker container, unfortunately this means the container needs to have a fixed IP address assigned to it.

Setting a Fixed IP Address for a Docker Container

In my use case, I have a number of containers linked to a docker network which traefik acts as a reverse proxy in front of. This is created as an external network that different docker-compose files reference. Step one in assigning a container a fixed IP address is to set a fixed subnet on this network.

docker network create --driver bridge --subnet=172.32.0.0/16 traefik-backend

As part of the docker-compose configuration for the container we can then set an IP address in the subnet defined for the network.

homebridge:
    image: oznu/homebridge:no-avahi
    restart: unless-stopped

    networks:
      traefik-backend:
        # Need a fixed IP here so we can use WOL from the Samsung Tizen plugin
        # without using host networking
        ipv4_address: 172.32.0.200

Conclusion

So we have now managed to successfully send Wake-on-Lan packets from within a docker container without needing to set the container in host network mode. However, this did require the container to be assigned a fixed IP address which does negate some of the benefits of being able to run software in a container like this.

As I am going through this journey of adding more smart devices, I do seem to hit more issues with running Homebridge in a container. The next issue revolves around getting a bluetooth LE device working.