linux ipv6 bridge address does not work when mac address is forced

While playing with linux bridging and ipv6 on ubuntu focal, I met an issue that I would like to understand.

When I manually set the bridge mac address, bridge ipv6 address is not working anymore, why ?

Bridge with system allocated mac address [Works fine]

root@node:~# ip link del dev brv6
root@node:~# ip link add name brv6 type bridge
root@node:~# ip -6 address add dev brv6 scope global fdfe::401/118
root@node:~# ip link set dev brv6 up
root@node:~# ping -c2 fdfe::401
PING fdfe::401(fdfe::401) 56 data bytes
64 bytes from fdfe::401: icmp_seq=1 ttl=64 time=0.035 ms
64 bytes from fdfe::401: icmp_seq=2 ttl=64 time=0.029 ms

--- fdfe::401 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1007ms
rtt min/avg/max/mdev = 0.029/0.032/0.035/0.003 ms
root@node:~# ip link show dev brv6
13: brv6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether b2:75:42:d1:de:a9 brd ff:ff:ff:ff:ff:ff

Bridge with manually allocated mac address [Does not work]

root@node:~# ip link del dev brv6
root@node:~# ip link add name brv6 type bridge
root@node:~# ip link set dev brv6 address 6a:58:ea:de:65:79
root@node:~# ip -6 address add dev brv6 scope global fdfe::401/118
root@node:~# ip link set dev brv6 up
root@node:~# ping -c2 fdfe::401
PING fdfe::401(fdfe::401) 56 data bytes
From 2001:321:4321:ab:acf4::1 icmp_seq=1 Destination unreachable: Address unreachable
From 2001:321:4321:ab:acf4::1 icmp_seq=2 Destination unreachable: Address unreachable

--- fdfe::401 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1021ms
root@node:~# ip link show dev brv6
14: brv6: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
    link/ether 6a:58:ea:de:65:79 brd ff:ff:ff:ff:ff:ff

Thanks for your help.


This doesn't have much to do with IPv6 (layer 3) but with the interface state (layer 1?). This problem doesn't happen in real use cases because then a bridge always has at least one bridge port.

When you leave the bridge in "automatic MAC assignment mode" the bridge:

  • is operatively UP (but the driver just tells UNKNOWN here) when set administratively UP. This is conditioned by the link's carrier state:

    # ip link show dev brv6
    7: brv6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
        link/ether c2:15:e6:2f:0e:37 brd ff:ff:ff:ff:ff:ff
    
  • will inherit a MAC address from an interface later set as bridge port (precise behavior might depend on kernel version).

When the MAC address is forced:

  • is operatively DOWN even if set administratively UP:

    # ip link show dev brv6
    8: brv6: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
        link/ether 6a:58:ea:de:65:79 brd ff:ff:ff:ff:ff:ff
    

    because the NO-CARRIER flag forces the operative state to stay DOWN.

  • won't inherit a MAC address from any interface set as bridge port.

I don't know the precise rationale about this behavior, but it looks as if the bridge in "automatic" mode is left the benefit of the doubt for its future use and is granted a free carrier UP state on initial setup.

In either cases, but this might depend on the kernel version (tested with 5.13.x here), once an interface has been set as bridge port once, the behavior will become the same: the bridge needs at least one port itself in operative state up to be up, so for example:

  • adding a veth interface as bridge port requires both sides of the veth pair to be up to get a carrier up and get this state on the bridge too
  • adding a dummy interface only requires the dummy interface to be up since there's no other side.

As long as the operative state is DOWN, not all automatic routes created from the IPv6 address are added in routing tables (either in main table ip -6 route or in local table ip -6 route show table local) and/or these routes are ignored when having the linkdown property. In the end this IPv6 address is not reachable.

Behavior here might differ with IPv4 where adding an address on a down interface still gets it reachable from other interfaces. Linux' IPv6 doesn't behave identically to Linux' IPv4.


The easiest way to solve this problem is to add a dummy interface to the bridge. For example initiating the bridge like this (with "compressed" commands):

ip link add name brv6 address 6a:58:ea:de:65:79 up type bridge
ip link add name brv6-dummy up master brv6 type dummy

which gives (note the actual UP rather than UNKNOWN):

# ip link show dev brv6
9: brv6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 6a:58:ea:de:65:79 brd ff:ff:ff:ff:ff:ff