I'm having intermittent connectivity issues. An ipv6 link local address gets automatically added to my /etc/resolv.conf, and that seems to cause libc's resolver to fail resolving. I would like to know how to either prevent that address to be inserted there or find a suitable workaround.
My setup: I have a Ubuntu 14.04 desktop deployment setup with ipv4 and ipv6. It has only one wired connection (no wifi) to a lan port of a home router running OpenWrt. The desktop's networking is taken in charge by NetworkManager, which runs its own local copy of dnsmasq. All the network manager files in /etc are "stock", I haven't touched them.
When I reset my networking via network manager, everything works fine (but only for a few minutes). My working configuration of resolv.conf looks like so:
user@foo:/$ cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by
resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.1.1
search lan
My router (192.168.1.1 or fe80::beef) also runs a copy of dnsmasq, and is configured to advertise 192.168.1.1 (itself) to its dhcp clients on v4. On v6, it periodically sends router advertisement messages (icmpv6.type == 134) with a recursive DNS Server option for fe80::beef. The router's dnsmasq DNS service is listening on both addresses: .1.1 and ::beef (the router's lan bridge ipv6 link address).
# working dns server. ran from the desktop.
user@foo:/$ dig google.com +time=1 @fe80::beef > /dev/null ; echo $?
0
At any time, if I go to "Connection Information" in NetworkManager, my primary DNS and router in Ipv4 is set to 182.168.1.1. The NetworkManager GUI shows no information under the "ipv6" section header -- but my nic receives ipv6 addresses (slaac and stateful dhcpv6 ones) which i can view with ip addr show
.
The problem: After resetting my network via network manager (toggle "Enable Networking") and waiting (i.e. waiting until the next router advertisement message, i suspect), a new entry makes its way to /etc/resolv.conf
:
user@foo:~$ cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver fe80::beef
nameserver 127.0.1.1
search lan
The problem is that once this happens, some userland tools (including firefox and google-chrome) will fail to resolve (non-cached) domain names.
As far as I understand, working with link local addresses requires a link scope to be mentioned explicitly. The following trace shows how connect
fails without the link scope (default scope_id of 0).
user@foo:~$ strace ping google.com
execve("/bin/ping", ["ping", "google.com"], [/* 73 vars */]) = 0
...
stat("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=220, ...}) = 0
socket(PF_INET6, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET6, sin6_port=htons(53), inet_pton(AF_INET6, "fe80::beef", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 EINVAL (Invalid argument)
close(3) = 0
socket(PF_INET6, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET6, sin6_port=htons(53), inet_pton(AF_INET6, "fe80::beef", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 EINVAL (Invalid argument)
close(3) = 0
write(2, "ping: unknown host google.com\n", 29ping: unknown host google.com) = 29
exit_group(2) = ?
+++ exited with 2 +++
DNS fails, but I have connectivity otherwise:
user@foo:~$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=60 time=7.77 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=60 time=7.81 ms
....
user@foo:~$ ping6 2607:f8b0:400a:808::200e # google.com AAAA
PING 2607:f8b0:400a:808::200e(2607:f8b0:400a:808::200e) 56 data bytes
64 bytes from 2607:f8b0:400a:808::200e: icmp_seq=1 ttl=57 time=7.94 ms
64 bytes from 2607:f8b0:400a:808::200e: icmp_seq=2 ttl=57 time=7.86 ms
...
Adding the scope (%eth0) to the end of the address in resolv.conf fixes the issue:
nameserver fe80::beef%eth0
nameserver 127.0.1.1
search lan
But of course, this change gets wiped the next time around.
Is there any way to:
- Force ipv4 only to query DNS (I don't think I'll run an ipv6-only setup any time soon)
- Specify a default interface scope in the resolver (i.e. "%eth0")
- Change the router's dnsmasq (or radvd, or rdnss) to not advertise its ipv6 address for DNS (only its ipv4)
Edit: failed fix attempt
If I move the /etc/resolv.conf
symlink to /etc/resolv.conf.old
and write my own static /etc/resolv.conf
containing only the local dnsmasq server ip (nameserver 127.0.1.1), I find out that the file is still being modified by something else which adds a "search" line.
user@foo:~$ cat /etc/resolv.conf # my new file, not the symlink
# Edited by hand to avoid using the ipv6 link local scopeless address
# check resolv.conf.old to see normal file
#nameserver fe80::beef
nameserver 127.0.1.1
search lan
After a bit of time:
user@foo:~$ cat /etc/resolv.conf # my new file prefixed by something
search lan.
# Edited by hand to avoid using the ipv6 link local scopeless address
# check resolv.conf.old to see normal file
#nameserver fe80::beef
nameserver 127.0.1.1
search lan
After a reboot:
user@foo:~$ cat /etc/resolv.conf # eh. same line added again.
search lan.
search lan.
# Edited by hand to avoid using the ipv6 link local scopeless address
# check resolv.conf.old to see normal file
#nameserver fe80::beef
nameserver 127.0.1.1
search lan
So. unless I start playing with chattr +i and other tricks to prevent that script (or whatever it is) from touching /etc/resolv.conf, I feel like this semi-static option isn't really clean. Accountability when these files are changed or logging about it would be a plus. syslog has nothing.
Note: In an attempt to hide some private info, I've redacted address suffixes and hostnames above