2010-04-25

Duplicate IPv6 Address Detection in Karmic

I've finally gotten my little empire of virtual machines somewhat stable, but a little wrench in the works has been the extra flag I keep needing for ssh: -4.

Sure, I can log in without it, but the systems will try using IPv6 first, since they all have IPv6 addresses in DNS, and then fall back to IPv4 when that times out. I could use my .ssh/config file to force IPv4, but that gets rid of the nagging reminder that I need to fix it.

Turns out IPv6 is broken by default in Karmic (and hopefully fixed in Lucid, but I'm not holding my breath). One of the "features" of IPv6 is Duplicate Address Detection; you should never have the problem of having the same IPv6 address allocated to 2 computers unless you do it intentionally. Of course, EUI-64 addressing should take care of that anyway...

Unfortunately, Karmic's implementation of DAD is horribly broken. Even with unique entries in DNS and a properly configured rtadvd, I kept getting logs filled with:

Apr 25 13:49:59 vm1 kernel: [ 6851.010394] eth0: IPv6 duplicate address detected!
Apr 25 13:50:01 vm2 kernel: [147906.360484] eth0: IPv6 duplicate address detected!
Apr 25 13:50:00 vm3 kernel: [315943.970527] eth0: IPv6 duplicate address detected!

So, I tried to configure IPv6 manually. But, Ubuntu is smarter than me (or so it thinks). Once it's detected that the address is a duplicate, you're not allowed to actually assign it to an interface. All the packets were attempting to be sent out with a source ip of ::1, so they all went out the lo interface which effectively firewalls the whole IPv6 stack from the outside world.

The only solution is to turn off Duplicate Address Detection.

sysctl net.ipv6.conf.all.dad_transmits=0
sysctl net.ipv6.conf.all.accept_dad=0

Perfect!

Well, not quite. See, "all" in sysctl doesn't actually mean "all." I'm not sure what it means, but it seems to be the exact opposite of "all," i.e. "none."

What I actually had to do was explicitely disable DAD on both lo and eth0. lo shouldn't need it, since all IPs on loopback are the same box anyway. If I see an IPv6 loopback packet storm, maybe I'll think about turning DAD back on for lo.

The final sysctl.conf:

net.ipv6.conf.all.dad_transmits = 0
net.ipv6.conf.all.accept_dad = 0
net.ipv6.conf.lo.dad_transmits = 0
net.ipv6.conf.lo.accept_dad = 0
net.ipv6.conf.eth0.dad_transmits = 0
net.ipv6.conf.eth0.accept_dad = 0

Phew! Good thing for me I have this all in Puppet, so it's easy to replicate across all my hosts:

exec { "sysctl -p":
  cwd         => '/',
  path        => '/bin:/sbin:/usr/bin:/usr/sbin',
  refreshonly => true,
  subscribe   => File[sysctl_config]
}

:wq

No comments:

Post a Comment