[Dnsmasq-discuss] DHCPv6: Problems w/ multiple interfaces that have identical MACs

Cory Benfield Cory.Benfield at metaswitch.com
Mon Jan 12 16:47:19 GMT 2015


All,

I'm trying to use dnsmasq to provide DHCPv6 service on a number of tap interfaces. For some slightly boring technical reasons, each of these tap interfaces needs to have the same MAC address (for the purposes of the remainder of this email, that MAC is de:ad:be:ef:00:01). Using this topology I believe I've revealed a bug in the way dnsmasq handles multiple interfaces with the same IPv6 link-local address.

I observe three different behaviours depending on what I do. First I bring up a number of tap interfaces ahead of time. I then start dnsmasq. dnsmasq correctly locates all the interfaces, binds listening sockets for them, and joins the multicast group for DHCPv6 (as verified by running netstat -g and showing such a bind on the relevant interface).

The second behaviour is observed when I add a new tap interface. Nothing exciting happens here, but dnsmasq does not join the multicast group for that interface (as verified by running netstat -g), so DHCPv6 does not function for that interface.  Running strace reveals that dnsmasq never binds a listening socket for the interface, despite finding it in an enumeration of the interfaces. You can see this with the configuration below (where interface 22 existed before dnsmasq started, and interface 28 was created after):


ubuntu at hostname:~$ ip link show
22: tap9c17245e-ef: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 500
    link/ether de:ad:be:ef:00:01 brd ff:ff:ff:ff:ff:ff
28: tap6dea49ab-27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 500
    link/ether de:ad:be:ef:00:01 brd ff:ff:ff:ff:ff:ff


strace reveals that dnsmasq does know the interface exists, because it makes the three ioctl calls I'd expect at the top of the iface_allowed function:


ioctl(18, SIOCGIFNAME, {ifr_index=22, ifr_name="tap9c17245e-ef"}) = 0
ioctl(18, SIOCGIFFLAGS, {ifr_name="tap9c17245e-ef", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(18, SIOCGIFMTU, {ifr_name="tap9c17245e-ef", ifr_mtu=1500}) = 0
ioctl(18, SIOCGIFNAME, {ifr_index=28, ifr_name="tap6dea49ab-27"}) = 0
ioctl(18, SIOCGIFFLAGS, {ifr_name="tap6dea49ab-27", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(18, SIOCGIFMTU, {ifr_name="tap6dea49ab-27", ifr_mtu=1500}) = 0


Note that dnsmasq has also correctly got the two interface IDs. Checking netstat reveals no DHCPv6 join multicast join for interface 28 (tap6dea49ab-27), which should have joined group ff02::1:2:


ubuntu at hostname:~$ netstat -g
IPv6/IPv4 Group Memberships
Interface       RefCnt Group
--------------- ------ ---------------------
tap9c17245e-ef  1      ff05::1:3
tap9c17245e-ef  1      ff02::1:2
tap9c17245e-ef  1      ff02::fb
tap9c17245e-ef  1      ff02::1:ff00:0
tap9c17245e-ef  1      ff02::1:ffef:1
tap9c17245e-ef  2      ip6-allrouters
tap9c17245e-ef  1      ip6-allnodes
tap9c17245e-ef  1      ff01::1
tap6dea49ab-27  1      ff02::fb
tap6dea49ab-27  1      ff02::1:ff00:0
tap6dea49ab-27  1      ff02::1:ffef:1
tap6dea49ab-27  1      ip6-allrouters
tap6dea49ab-27  1      ip6-allnodes
tap6dea49ab-27  1      ff01::1


This gets worse if I remove all the extant tap interfaces and create a new one. In this case, dnsmasq prints the following to syslog:


Jan 12 16:26:09 hostname dnsmasq[31994]: failed to create listening socket for fe80::dcad:beff:feef:1: No such device


Again, strace reveals that dnsmasq has spotted the new interface, but this time also reveals that it fails in its attempt to bind the interface:


ioctl(9, SIOCGIFNAME, {ifr_index=29, ifr_name="tap375ee24b-5a"}) = 0
ioctl(9, SIOCGIFFLAGS, {ifr_name="tap375ee24b-5a", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(9, SIOCGIFMTU, {ifr_name="tap375ee24b-5a", ifr_mtu=1500}) = 0
...
bind(9, {sa_family=AF_INET6, sin6_port=htons(53), inet_pton(AF_INET6, "fe80::dcad:beff:feef:1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=22}, 28) = -1 ENODEV (No such device)


Note the sin6_scope_id of this call, which is set to 22. 22 was the only interface on which dnsmasq correctly joined the multicast group in this run: all subsequent interface creations failed to bind. This strongly suggests to me that dnsmasq is incorrectly caching information about interfaces and re-using address structures when their information does not match. This is almost certainly caused by assuming that IPv6 link-local addresses are unique, which they needn't be.

Unfortunately, I've begin to run low on ideas for where in the code this is going wrong. The closest I've gotten so far is to look suspiciously at the iface_check function, which looks like it could possibly be getting this wrong. If someone can point me in the correct direction that would be extremely helpful for formulating a patch for this problem.

Thanks in advance!

Cory 



More information about the Dnsmasq-discuss mailing list