没有范围的链接本地(fe80::)地址进入 /etc/resolv.conf 并导致问题

没有范围的链接本地(fe80::)地址进入 /etc/resolv.conf 并导致问题

我遇到了间歇性连接问题。一个 ipv6 链接本地地址会自动添加到我的 /etc/resolv.conf 中,这似乎导致 libc 的解析器无法解析。我想知道如何阻止该地址插入那里或找到合适的解决方法。

我的设置:我有一个 Ubuntu 14.04 桌面部署设置,其中有 ipv4 和 ipv6。它只有一个有线连接(没有 wifi),连接到运行 OpenWrt 的家用路由器的 LAN 端口。桌面的网络由 NetworkManager 负责,它运行自己的 dnsmasq 本地副本。/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 127.0.1.1
search lan

我的路由器(192.168.1.1 或 fe80::beef)也运行 dnsmasq 的一个副本,并配置为在 v4 上向其 dhcp 客户端通告 192.168.1.1(本身)。在 v6 上,它会定期发送路由器通告消息(icmpv6.type == 134),其中包含 fe80::beef 的递归 DNS 服务器选项。路由器的 dnsmasq DNS 服务正在监听两个地址:.1.1 和 ::beef(路由器的 LAN 桥接 IPv6 链接地址)。

# working dns server. ran from the desktop.
user@foo:/$ dig google.com +time=1 @fe80::beef > /dev/null ; echo $?
0

任何时候,如果我转到 NetworkManager 中的“连接信息”,我的 Ipv4 中的主 DNS 和路由器都会设置为 182.168.1.1。NetworkManager GUI 在“ipv6”部分标题下不显示任何信息 - 但我的网卡会收到 ipv6 地址(slaac 和有状态 dhcpv6 地址),我可以使用 查看ip addr show

问题:通过网络管理器重置我的网络(切换“启用网络”)并等待(即等待直到下一个路由器广告消息,我怀疑)后,一个新的条目进入/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

问题是,一旦发生这种情况,一些用户空间工具(包括 Firefox 和 google-chrome)将无法解析(非缓存)域名。

据我所知,使用链接本地地址需要明确提及链接范围。以下跟踪显示了在connect没有链接范围的情况下如何失败(默认 scope_id 为 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 失败,但是其他方面我可以连接:

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
...

在 resolv.conf 中的地址末尾添加范围(%eth0)可以解决此问题:

nameserver fe80::beef%eth0
nameserver 127.0.1.1
search lan

但当然,下一次这个变化就会被消除。

有什么办法可以:

  • 强制仅使用 ipv4 来查询 DNS(我认为我不会很快运行仅使用 ipv6 的设置)
  • 在解析器中指定默认接口范围(即“%eth0”)
  • 将路由器的 dnsmasq(或 radvd 或 rdnss)更改为不是向 DNS 公布其 ipv6 地址(仅限其 ipv4)

编辑:修复尝试失败

如果我将/etc/resolv.conf符号链接移动到/etc/resolv.conf.old并编写我自己的静态文件/etc/resolv.conf,其中仅包含本地 dnsmasq 服务器 ip(名称服务器 127.0.1.1),我会发现该文件仍被其他添加了“搜索”行的东西修改。

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

过了一会儿:

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

重启后:

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

所以,除非我开始使用 chattr +i 和其他技巧来阻止该脚本(或任何其他脚本)接触 /etc/resolv.conf,否则我觉得这个半静态选项并不干净。当这些文件发生更改或记录更改时,可追究责任,这将是一个优点。syslog 什么都没有。

注意:为了隐藏一些私人信息,我删除了上面的地址后缀和主机名

答案1

我正在回答我自己的问题,并提供解释和补丁以供参考。

问题出在软件包 提供的 dhcp6c 脚本中wide-dhcpv6-clientdhcp6c它在我的系统上作为守护进程运行(由 启动/etc/init.d/wide-dhcpv6-client),每隔几分钟发送一次 DHCPv6 请求。我认为wide-dhcpv6-clientubuntu 桌面中默认没有安装。它的配置 ( /etc/wide-dhcpv6/dhcp6c.conf) 设置为/etc/wide-dhcpv6/dhcp6c-script在需要进行更新时调用脚本。此脚本存在错误:

  1. 对于链路本地地址,它会忽略范围

  2. 如果/etc/resolv.conf不受 resolvconf 管理(即不是符号链接),则会导致search X附加行而不检查行是否已经存在(然后文件会不断增长)。

假设您有一个由 dhcp6c** 监控的单个接口,/etc/wide-dhcpv6/dhcp6c-script使用以下补丁修补该文件将解决问题:

--- /etc/wide-dhcpv6/dhcp6c-script.orig 2016-09-04 17:12:35.405042056 -0700
+++/etc/wide-dhcpv6/dhcp6c-script.new 2016-09-04 22:57:05.213169351 -0700
@@ -6,20 +6,48 @@

 [ -f /etc/默认/宽-dhcpv6-客户端 ] && . /etc/默认/宽-dhcpv6-客户端

+# 猜测用作 scope_id 的接口。理想情况下,dhcp6c 将传递
+# 接收 DHCPv6 回复的接口的名称。
+范围=“”
+对于 $INTERFACES 中的 IFACE;执行
+ 范围=“$IFACE”
+休息;
+完成
+
+# 精确行匹配。grep 会将域名句点解释为特殊
+# 字符。简单循环避免依赖其他非核心工具
+# 命令(sed、awk 等)。
+有线(){
+ 本地电话
+ 当读取行时;执行
+ 如果 [ “$line” = “$1” ]; 然后
+ 返回 0
+ 菲
+ 完成
+ 返回 1
+}
+    
+
 如果 [ -n “$new_domain_name” -o -n “$new_domain_name_servers” ]; 那么
     old_resolv_conf=/etc/resolv.conf
     new_resolv_conf=/etc/resolv.conf.dhcp6c-new
     rm -f $new_resolv_conf
     如果 [ -n "$new_domain_name" ]; 那么
- 回显搜索 $new_domain_name >> $new_resolv_conf
+ has_line "搜索 $new_domain_name" < $old_resolv_conf || {
+ 回显“搜索 $new_domain_name”>> $new_resolv_conf
+ }
     如果 [ -n "$new_domain_name_servers" ]; 那么
         对于 $new_domain_name_servers 中的名称服务器;执行
+ 如果 [ -n "$scope" -a "${nameserver##fe80::}" != "$nameserver" ]; 然后
+ 名称服务器="$名称服务器%$范围"
+ 菲
+
             # 无需添加已经存在的名称服务器
— res = $(grep“名称服务器$名称服务器”$old_resolv_conf)
- 如果 [ -z "$res" ]; 那么
- 回显名称服务器 $nameserver >> $new_resolv_conf
- 菲
+ has_line "名称服务器 $nameserver" < $old_resolv_conf || {
+ 回显“名称服务器 $nameserver”>> $new_resolv_conf
+ }
         完毕

**上述代码将从 中的接口列表中选择第一个接口/etc/default/wide-dhcpv6-client,并将其后缀为以 开头的任何名称服务器fe80::。这与 中所做的类似/etc/dhcp/dhclient-enter-hooks.d/resolvconf。它并不理想,特别是如果您有多个接口。dhcp6c 不会在环境中传递接口的名称,因此我们只能猜测。(错误记录:https://bugs.launchpad.net/ubuntu/+source/wide-dhcpv6/+bug/1620221

如果您有多个网卡并且需要它更加强大,您可以配置/etc/wide-dhcpv6/dhcp6c.conf根据接口名称启动不同的脚本副本,并在脚本中对接口名称进行硬编码。

在我弄清楚问题之前,我发现了两种解决方法,它们虽然不能解决问题,但却可以避免症状。

解决方法 1:禁用 dnsmasq 本地解析器。

当 NetworkManager 的 dnsmasq 服务被禁用时,resolv.conf 中的条目会正确填充。所以我失去了它的好处。不完美。

按照步骤如何禁用网络管理器使用的 DNS?.另请参阅安全和性能影响列表禁用本地解析器。

重新启动 NM 后,链接本地地址已正确添加范围:

# 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 192.168.1.1
nameserver fe80::beef%eth0
search lan

我不太清楚为什么这样做有效,但我怀疑这是因为 NetworkManager 和 dhcp6c 都写入了 resolvconv,并且更新脚本倾向于 fe80::beef%eth0(由 NetworkManager 传递)而不是没有范围的相同地址(由 dhcp6c 传递)。

解决方法 2:配置 OpenWrt 以将附加的 dhcpv6 服务器作为 RDNSS 的一部分进行宣传。此服务器将优先于 /etc/resolv.conf 中的链接本地服务器。我觉得这个解决方案比解决方法 #1 略好一些,因为它保留了本地解析器(至少对于 ipv4 而言),但它更复杂。

  1. 使用 IPv6 ULA-Prefix 设置 OpenWrt,例如fd00:cafe::/48。(Menu -> Network -> Interfaces -> bottom of page)。路由器将在fd00::cafe::1局域网上获取本地 ipv6。(我在 OpenWRT 15.05.1 chaos calmer 上尝试过这个)。

  2. 指示 odhcpd(在该版本的 openwrt 上提供 dhcpv6 服务)也将 IP 作为(附加的)DNS 服务器进行宣传。odhcpd(版本 2015-11-19-01d3f...)提供了一个名为 dhcp.lan.dns 的配置标志(标志描述)。在 luci UI 中,它位于 下Menu -> Network -> Interfaces -> LAN (click edit) -> DHCP Server section -> IPv6 Settings tab -> Announced DNS Servers。将路由器的 ULA 地址添加到字段中。为了对称,我还将路由器的 ipv4 子网地址添加到 dns 服务器列表 (192.168.1.1)。

有了这个,odhcpd 会将这两个地址作为 RA 的一部分进行宣传,但对我们有利的是,NM 只会将 ULA 地址写入 resolv.conf。链路本地地址永远不会添加到 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 fd00:cafe::1
nameserver 127.0.1.1
search lan

查看 wireshark 跟踪,当以这种方式配置时,来自 openwrt 的 RA 消息将列出两个 dns 服务器地址,但其 DHCPv6 回复只有一个fd00:cafe::1,因此避免了 dhcp6c 脚本中的错误。

如果你在网络上有一台 unix 主机不是运行本地解析器(例如如果你在 ubuntu 上应用了解决方法 1 和 2),resolv.conf 将拥有所有 IP:

# 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 192.168.1.1
nameserver fd00:cafe::1
nameserver fe80::beef%eth0
search lan

libc 解析器中的默认算法是依次尝试 resolv.conf 中的每个条目,每个条目都超时。因此,如果出现问题,同一个 DNS 服务器的多个别名可能会导致额外的延迟。就我而言,这很可能意味着我的整个路由器都宕机了,而不仅仅是它的 DNS 服务器,所以这不是问题。但您的情况可能会有所不同。

额外的我使用以下脚本调试对 resolvconf 所做的更改。这使我能够找出哪个守护进程发送了错误的更改:

#!/bin/bash

# logs all interactions to /sbin/resolvconf into syslog
# this script is named /sbin/resolvconf, temporarily.
# moved resolvconv binary to resolvconf.real, temporarily.

BIN=/sbin/resolvconf.real
LOGGER=/usr/bin/logger

ppid () { ps -p ${1:-$$} -o ppid= 2>/dev/null | sed 's/ //g'; }

PROC=$$

"$LOGGER" "resolvconf.wrapper args: $@"

for ((i=0; i<4; i++)); do
    PROC=$(ppid $PROC)
    if [[ "$PROC" == 0 || "$PROC" == "" ]]; then
    break;
    fi
    if [[ "$i" -eq 0 ]]; then
    "$LOGGER" "resolvconf.wrapper invoked by: pid=$PROC $(/bin/ps -p $PROC -o command=)"
    else
    "$LOGGER" "resolvconf.wrapper child of: pid=$PROC $(/bin/ps -p $PROC -o command=)"
    fi
done

# peek at stdin (which contains the config)
if [[ "$1" == "-a" ]] && [[ -n "$2" ]]; then
    tmp=$(/bin/mktemp)
    while read REST; do
    logger "resolvconf.wrapper feeding: $REST"
    echo $REST >> "$tmp"
    done
    exec < "$tmp"
    rm "$tmp"
fi

exec -a /sbin/resolvconf "$BIN" "$@"

有了这个,syslog 包含如下块:

Sep  3 02:32:29 calm logger: resolvconf.wrapper invoked by: pid=12610 sh -c /sbin/resolvconf -a NetworkManager
Sep  3 02:32:29 calm logger: resolvconf.wrapper child of: pid=11910 NetworkManager
Sep  3 02:32:29 calm logger: resolvconf.wrapper child of: pid=1 /sbin/init
Sep  3 02:32:29 calm logger: resolvconf.wrapper feeding: # Generated by NetworkManager
Sep  3 02:32:29 calm logger: resolvconf.wrapper feeding: domain base
Sep  3 02:32:29 calm logger: resolvconf.wrapper feeding: search base base.
Sep  3 02:32:29 calm logger: resolvconf.wrapper feeding: nameserver 127.0.1.1
...
Sep  3 02:14:23 calm logger: resolvconf.wrapper gparent: pid=1553 /usr/sbin/dhcp6c -Pdefault eth0
Sep  3 02:14:23 calm logger: resolvconf.wrapper invoked by: pid=15926 /bin/sh /etc/wide-dhcpv6/dhcp6c-script
Sep  3 02:14:23 calm logger: resolvconf.wrapper args: -a eth0
Sep  3 02:14:23 calm logger: resolvconf.wrapper feeding: search base.
Sep  3 02:14:23 calm logger: resolvconf.wrapper feeding: nameserver fd00:cafe::1

相关内容