Bridge and ipv6 autoconfiguration

“There’s no sound”, said my missus.

Few minutes before, as usual, I picked up my 17-inch laptop and placed it on the second desk, where it can face the room. I don’t really like dragging the armchair every time I want to watch a video together, but the flat isn’t big enough to arrange a place with a permanent setup. (I also don’t own a TV and I’m proud of it.)

This time there was indeed no sound.

“Wait a minute”, I said and opened Gnome Terminal. At my place, there is only one set of speakers connected to one computer, and shared over the local area network. After a while I figured out that mplayer had trouble contacting PulseAudio server. The server wouldn’t respond.

I tried playing sound from a different machine – it worked. So it’s not the server. Next test:

maciej@quince ~ $ ping -c 1 leon
PING ( 56(84) bytes of data.
64 bytes from ( icmp_seq=1 ttl=64 time=0.165 ms

— ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.165/0.165/0.165/0.000 ms

Next test…

maciej@quince ~ $ telnet leon 4713
Trying 2001:

Timeout. And… ah. I forgot I was running native IPv6 in my LAN. I tried connecting over IPv4, it worked. I’ve set ${PULSE_SERVER} to the IPv4 address of the PulseAudio server and happily watched the movie together with my missus.

But… what’s going on? Running ping6 gives good results – there are answers. But when I ran ping6 leon, I got no answer. How can ping work for an external host but not for a host in my LAN?

After more poking at the machine, I’ve found out that the response packets (ACK) over IPv6 are in fact coming to the slave eth0 interface, but never to the bridge interface, br0.

Then I’ve noticed that IPv6 routing is wrong! There are two routes:

2001:660:952::/64 dev eth0
2001:660:952::/64 dev br0

Packets should leave through br0, not eth0. First idea, restart the bridge. However, the wrong route stayed there even after interface restart. It made me think what happens when I stop the bridge.

Well, eth0 goes back from slave state into a normal state, and then… it’s autoconfigured. That means, it gets a route assigned to it. When the bridge is being started, eth0 becomes a slave of br0, but there probably is no mechanism to remove the route which was assigned during eth0’s autoconfiguration.

A quick ad-hoc fix is:

ip -6 route del 2001:660:952::/64 dev eth0

A permanent solution would be better, but I happen to be lazy.

UPDATE 2009-02-01: Jeroen Massar from SixXS offered a better workaround:
put in /etc/sysctl.conf:
net.ipv6.conf.eth0.accept_ra = 0

When talking about this problem, the conversation usually goes like this:
– I have this problem with IPv6 autoconfiguration… (yada yada)
– What distribution are you using?
– Gentoo.
– (laughter)

Well, turns out, they were right. I tried this config on Ubuntu:

maciej@ubuntu-vbox:~$ cat /etc/network/interfaces
auto lo
iface lo inet loopback
iface br0 inet dhcp
bridge_ports eth0
auto eth0
auto br0

After “/etc/init.d/network restart”, the surplus route was there. But after a reboot, it wasn’t there any more. Ubuntu/Debian somehow handles this case.

IPv6-enabled OpenCSW mirror

My home network has native IPv6 with radvd running on the router. Any host connected to it automatically gets an IPv6 address. (Unless it’s a Solaris zone, but that’s another story.) For a reason I haven’t worked out yet, all IPv4 traffic on my downlink seems to be throttled to 60kB/s. However, when downloading over IPv6, I’m easily getting 150kB/s or 200kB/s.

When setting up a Solaris 10 system (this time using kvm), I’m getting an IPv6 address straight away:

bash-3.00# ifconfig -a6
lo0: flags=2002000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL> mtu 8252 index 1
inet6 ::1/128
rtls0: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2
inet6 fe80::5053:ff:fe13:9457/10
ether 52:53:0:13:94:57
rtls0:1: flags=2080841<UP,RUNNING,MULTICAST,ADDRCONF,IPv6> mtu 1500 index 2
inet6 2001:770:18d:0:5053:ff:fe13:9457/64

(the ‘rtls0’ interface is a name for a rtl8139 NIC)

I want to use an IPv6-enabled OpenCSW mirror! Such as

You may want that too. Edit /etc/wgetrc (or /opt/csw/etc/wgetrc if you’re using OpenCSW wget) in your Solaris system and add a line:

prefer-family = IPv6


bash-3.00# pkg-get -U
WARNING: gpg not found
Getting catalog...
=> `catalog'
Resolving 2001:770:18:aa40::c101:c140,
Connecting to|2001:770:18:aa40::c101:c140|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 357,696 (349K) [text/plain]

100%[==============>] 357,696      452.07K/s

20:58:53 (451.18 KB/s) - `catalog' saved [357696/357696]

wget will now try to use IPv6 where available. When IPv6-enabled site is not there (no AAAA record, for instance), it will fall back to IPv4.

UPDATE: Putting network interface in a (VirtualBox 1.6.4) virtual machine into promiscuous mode does in fact make the interface accept icmp6 traffic.

UPDATE (2009-03-11): Added information about OpenCSW wget configuration path.

DHCP vs IPv6 address autoconfiguration

I kept on having this irritating problem. I would create a simple Gentoo network configuration in /etc/conf.d/net using DHCP for IPv4 and intending to use IPv6 address autoconfiguration.

config_eth0=( "dhcp" )

Simple and nice. The DHCP part usually worked, but IPv6 autoconfiguration didn’t. Guys on freenode’s #ipv6 IRC channel said: it should Just Work™. But it doesn’t! Well, sometimes it did. When I tried to debug it. But when I didn’t try to run tcpdump, it could just sit there for hours and not get an address. Just as if my debugging influenced it.

Interestingly, when I switched to a static IPv4 configuration, IPv6 autoconfiguration would magically start working. I haven’t worked out the root cause of this, but I’ve came up with a workaround. Well, the workaround ran into me so hard, it would be difficult not to notice. I wanted to set up VirtualBox bridged networking. When I configured it, my new bridge interface got an IPv6 address straight away, just as guys from #ipv6 would expect.

I’ve reproduced it on two machines. My working setup is the one from Gentoo’s Virtual Box howto page.

ipv6 in a Solaris zone

This is a continuation of my saga of Solaris zones. In this episode, you’ll be presented with a ipv6 routing problem on Solaris.

Looking for information about ipv6 in Solaris zones, you’ll be likely to get across this page. It will tell you, how to assign an ipv6 address to your zone: get into the global zone and use zonecfg.

netra / $ zonecfg -z wibble
Sorry, I don't know anything about your "screen" terminal.
netra / $ export TERM=vt100
netra / $ zonecfg -z wibble
zonecfg:wibble> add net
zonecfg:wibble:net> set address=2001:0:0:1::4/64
zonecfg:wibble:net> set physical=eri0
zonecfg:wibble:net> end
zonecfg:wibble> verify
zonecfg:wibble> commit
zonecfg:wibble> exit
netra / $ zoneadm -z wibble reboot

After zone reboot:

bash-3.00# ifconfig -a
lo0:1: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
inet netmask ff000000
eri0:1: flags=1000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
inet netmask ffffff00 broadcast
lo0:2: flags=2002000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL> mtu 8252 index 1
inet6 ::1/128
eri0:4: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2
inet6 2001:0:0:1::4/64

Nice, we have an ipv6 address.

bash-3.00# ping
ICMPv6 No Route to Destination from gateway 2001:0:0:1::4
for icmp6 from 2001:0:0:1::4 to 2001:4860:0:1001::68

The address resolves, but there’s no routing. The aforementioned page does actually say something about it:

Starting in the Solaris 10 8/07 release, the /etc/hosts and /etc/inet/ipnodes files are unified and are symbolic links to each other. Routing must be done in the global zone as is discussed in the Trusted Extensions and Zones forums.

The question is, what’s the URL to the relevant thread? I couldn’t find it. Maybe my Google seach-fu isn’t good enough. My friend and me found a way to make this work, although we have no idea whether this solution is correct. All we know is that is somehow… works.

If you look at the ipv6 routing table in the global zone, you’ll see something like this:

Routing Table: IPv6
Destination/Mask            Gateway                   Flags Ref   Use    If
--------------------------- --------------------------- ----- --- ------- -----
2001:0:0:1::/64        U       1       6 eri0:1
fe80::/10                   fe80::203:baff:fe0b:fd4b    U       1       1 eri0
ff00::/8                    fe80::203:baff:fe0b:fd4b    U       1       0 eri0
default                     fe80::213:a9ff:fe80:43be    UG      1       1 eri0
localhost                   localhost                   UH      1       0 lo0

It seems like routing is done via a link-local type address. If you look at the correspoding table in your non-global zone, you’ll see:

Routing Table: IPv6
Destination/Mask            Gateway                   Flags Ref   Use    If
--------------------------- --------------------------- ----- --- ------- -----
2001:0:0:1::/64             2001:0:0:1::4               U       1       0 eri0:4
ff00::/8                    2001:0:0:1::4               U       1       0 eri0:4
localhost                   localhost                   UH      1       0 lo0:2

Apparently, there is no link-local address here. Normally, link-local addresses are derived from the MAC addresses of network cards. Here, we have just one network card, and one MAC address – hence one link-local address, and it’s already assigned to the global zone. We need one more link-local address here… why not make one up?

netra / $ ifconfig -a | ggrep -B1 fe80
eri0: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2
inet6 fe80::203:baff:fe0b:fd4b/10

We’ll just pick the next address.

netra / $ zonecfg -z wibble
zonecfg:wibble> add net
zonecfg:wibble:net> set address=fe80::213:a9ff:fe80:43c0/10
zonecfg:wibble:net> set physical=eri0
zonecfg:wibble:net> end
zonecfg:wibble> verify
zonecfg:wibble> commit
zonecfg:wibble> exit
netra / $ zoneadm -z wibble reboot

After logging (using ‘zlogin wibble’) to the zone:

netra / $ zlogin wibble
[Connected to zone 'wibble' pts/3]
Last login: Sat Aug 30 11:57:01 on pts/3
Sun Microsystems Inc.   SunOS 5.10      Generic January 2005
# bash -l
bash-3.00# ifconfig -a | /opt/csw/bin/ggrep -B1 fe80 # GNU grep FTW!
eri0:5: flags=2000841<UP,RUNNING,MULTICAST,IPv6> mtu 1500 index 2
inet6 fe80::213:a9ff:fe80:43c0/10

We’ve got our address! It’s completely arbitrary and probably makes no sense. But the probability of collision is low and…

bash-3.00# ping is alive

…it works!

UPDATE 2008-09-05: Another way of doing it is creating an IPv6 route on the global zone, using a global IPv6 address (as opposed to link-local).