macvlan host guest connectivity

There are a number of sites out there mentioning that macvlan has a limitation when used with docker or lxc containers. The host machine will not be able to communicate with the guest machine and vice versa. While this is true by default, there is a tweak that you can perform to make host guest connectivity possible. I found out about this through a 2012 blog post which is no longer around. It has worked for me all the way up till Ubuntu 20.04 where some further steps are needed.

# cat /etc/network/interfaces
auto lo
iface lo inet loopback


auto eth0
iface eth0 inet static
        address 192.168.0.123           # static IP of your host machine
        netmask 255.255.255.0
        gateway 192.168.0.1             # IP of gateway
        dns-nameservers 8.8.8.8 8.8.4.4

auto macvlan0
iface macvlan0 inet dhcp
    # as eth0 and macvlan0 are on the same LAN, we must drop default route and LAN route
    # from eth0 configuration to avoid conflicts (this just slooooow down things).
    pre-up route del default            # pre-up executes these commands before bringing up the macvlan0 interface 
    # NOTE: adapt this line to your LAN address and netmask         
    pre-up route del -net 192.168.0.0 netmask 255.255.255.0
    pre-up ip link add link eth0 name macvlan0 type macvlan mode bridge

What this achieves is to give a static IP address to eth0 on the host machine, so that it is easily accessible by other machines. As eth0 is unable to communicate with the guest machine, we get pass this known limitation by deleting all routes through eth0 then setup macvlan0 interface and route all egress traffic through macvlan0. Since your host machine traffic is now routed through macvlan0, we can now communicate with the guest machine. Do note that all egress traffic from the host machine will now appear to originate from the DHCP IP, 192.168.0.55 in the example, and not the static IP. This does not stop me from hosting services on the host machine, I can SSH to the static IP just fine.

# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.123  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::0  prefixlen 64  scopeid 0x20<link>
        ether de:ad:be:ef:ca:fe  txqueuelen 1000  (Ethernet)

macvlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.55  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::0  prefixlen 64  scopeid 0x20<link>
        ether de:ad:be:ef:ca:ff  txqueuelen 1000  (Ethernet)

# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.0.1     0.0.0.0         UG    203    0        0 macvlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     203    0        0 macvlan0

For Ubuntu 20.04, I have found that the eth0 route will still be present. This is because dhcpcd will automatically add the eth0 route at regular intervals. To get around this, I added an additional line to set the eth0 route metric to 400 for the 2 files listed below. This means that the macvlan0 route will be prioritised over the eth0 route and connectivity is restored. It would also be prudent to try disabling/enabling dhcpcd to see the effect. I have found that allowing dhcpcd to run may result in multiple DHCP IPs to be requested by the macvlan0 interface.

# cat /etc/dhcpcd.conf
interface eth0
        static ip_address=192.168.0.123/24
        static routers=192.168.0.1
        static domain_name_servers=8.8.8.8 8.8.4.4
        metric 400                                  # Add this line for Ubuntu 20.04

# cat /etc/network/interfaces
...
iface eth0 inet static
        address 192.168.0.123           # static IP of your host machine
        netmask 255.255.255.0
        gateway 192.168.0.1             # IP of gateway
        dns-nameservers 8.8.8.8 8.8.4.4
        metric 400                      # May have to add this for Ubuntu 20.04
...

# service dhcpcd restart
# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.0.1     0.0.0.0         UG    203    0        0 macvlan0
default         192.168.0.1     0.0.0.0         UG    400    0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     203    0        0 macvlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     400    0        0 eth0

If you are using LXC containers, the following profile should work. You can configure the eth0 interface on the guest to either use a static IP or DHCP as per normal.

# lxc profile show default
config: {}
description: Default LXD profile
devices:
  eth0:
    nictype: macvlan
    parent: eth0
    type: nic
...