web-dev-qa-db-ja.com

スコープのないローカル(fe80::)アドレスをリンクすると/etc/resolv.confに移動して問題が発生する

断続的な接続の問題があります。 ipv6リンクのローカルアドレスが/etc/resolv.confに自動的に追加されます。これにより、libcのリゾルバーが解決に失敗するようです。そのアドレスがそこに挿入されるのを防ぐか、適切な回避策を見つける方法を知りたいです。

私のセットアップ: ipv4とipv6を使用したUbuntu 14.04デスクトップ展開セットアップがあります。 OpenWrtを実行しているホームルーターのLANポートへの有線接続(Wi-Fiなし)は1つだけです。デスクトップのネットワークは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では、fe80 :: beefの再帰DNSサーバーオプションを使用して、ルーターアドバタイズメントメッセージ(icmpv6.type == 134)を定期的に送信します。ルーターの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」セクションヘッダーの下に情報が表示されませんが、ip addr showで表示できるipv6アドレス(slaacおよびステートフルdhcpv6アドレス)を受信します。

問題:ネットワークマネージャーでネットワークをリセットし(「ネットワークを有効にする」を切り替え)、待機(つまり、次のルーターアドバタイズメントメッセージまで待機、疑わしい)後、新しいエントリが/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

しかし、もちろん、この変更は次回に削除されます。

以下の方法はありますか?

  • DNSのクエリのみをipv4に強制します(ipv6のみのセットアップはすぐに実行されるとは思わない)
  • リゾルバでデフォルトのインターフェイススコープを指定します(つまり、「%eth0」)
  • ルーターのdnsmasq(またはradvd、またはrdnss)をnotに変更し、DNSのipv6アドレスを通知します(ipv4のみ)

編集:修正に失敗しました

/etc/resolv.confシンボリックリンクを/etc/resolv.conf.oldに移動し、ローカルdnsmasqサーバーIP(ネームサーバー127.0.1.1)のみを含む独自の静的/etc/resolv.confを書き込むと、ファイルがまだ変更中であることがわかります。 「検索」行を追加する他の何かによって。

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には何もありません。

注:一部の個人情報を非表示にしようとして、上記のアドレスサフィックスとホスト名を編集しました

3
init_js

私は自分の質問に答えて、参考のために説明とパッチを提供しています。

問題は、パッケージwide-dhcpv6-clientで提供されるdhcp6cスクリプトにあります。 dhcp6cはシステム上でデーモンとして実行され(/etc/init.d/wide-dhcpv6-clientで開始)、数分ごとにDHCPv6要求を送信します。 wide-dhcpv6-clientはデフォルトでubuntuデスクトップにインストールされるとは思わない。その構成(/etc/wide-dhcpv6/dhcp6c.conf)は、更新が必要なときにスクリプト/etc/wide-dhcpv6/dhcp6c-scriptを呼び出すように設定されています。このスクリプトにはバグがあります。

  1. リンクローカルアドレスの場合、スコープを無視します

  2. /etc/resolv.confがresolvconfによって管理されていない(つまり、シンボリックリンクではない)場合、行が既に存在するかどうかを確認せずにsearch X行が追加されます(ファイルは拡大し続けます)。

Dhcp6c **で監視されているインターフェイスが1つしかない場合、ファイル/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/default/wide -dhcpv6-client] &&。 /etc/default/wide-dhcpv6-client

+# scope_idとして使用するインターフェイスを推測します。 dhcp6cは、DHCPv6応答が受信されたインターフェイスの
 +#名を渡すことが理想的です。
 + scope = "" 
 + IFACE for $ INTERFACES; do 
 + scope = "$ IFACE" 
 + break; 
 + done 
 + 
 +#完全に一致する行。 grepは、ドメイン期間を特別な
 +#文字として解釈します。単純なループは、他の非coreutils 
 +#コマンド(sed、awkなど)に依存することを避けます。
 + has_line(){
 + local line 
 +行の読み取り中。 do 
 + if ["$ line" = "$ 1"]; then 
 + return 0 
 + fi 
 + done 
 + return 1 
 +} 
 + 
 + 
 if [-n "$ new_domain_name" -o -n "$ new_domain_name_servers"]; then 
 old_resolv_conf =/etc/resolv.conf 
 new_resolv_conf =/etc/resolv.conf.dhcp6c-new 
 rm -f $ new_resolv_conf 
 if [-n 「$ new_domain_name」]; then 
-エコー検索$ new_domain_name >> $ new_resolv_conf 
 + has_line "search $ new_domain_name" <$ old_resolv_conf || {
 + echo "search $ new_domain_name" >> $ new_resolv_conf 
 +} 
 fi 
 if [-n "$ new_domain_name_servers"]; then 
 $ new_domain_name_serversのネームサーバー。 do 
 + if [-n "$ scope" -a "$ {nameserver ## fe80 ::}"!= "$ nameserver"]; then 
 + nameserver = "$ nameserver%$ scope" 
 + fi 
 + 
#既存のネームサーバーを追加する必要はありません
-res = $(grep "nameserver $ nameserver" $ old_resolv_conf)
-if [-z "$ res"]; then 
-echo nameserver $ nameserver >> $ new_resolv_conf 
-fi 
 + has_line "nameserver $ nameserver" <$ old_resolv_conf || {
 + echo "nameserver $ nameserver" >> $ new_resolv_conf 
 +} 
 done 
 fi 

**上記のコードは、/etc/default/wide-dhcpv6-clientのインターフェイスのリストから最初のインターフェイスを選択し、fe80::で始まるネームサーバーに接尾辞を付けます。これは、/etc/dhcp/dhclient-enter-hooks.d/resolvconfで行われていることに似ています。特に複数のインターフェイスがある場合は理想的ではありません。 dhcp6cは環境内のインターフェイスの名前を渡さないため、推測することしかできません。 (提出されたバグ: https://bugs.launchpad.net/ubuntu/+source/wide-dhcpv6/+bug/1620221

複数のNICがあり、これをより堅牢にする必要がある場合は、代わりに/etc/wide-dhcpv6/dhcp6c.confを構成して、インターフェイス名に応じてスクリプトの異なるコピーを開始し、スクリプトでインターフェイスの名前をハードコードできます。

問題を理解する前に、問題を解決せずに症状を回避する2つの回避策を発見しました。

回避策1:dnsmasqローカルリゾルバーを無効にします。

NetworkManagerのdnsmasqサービスが無効になっている場合、resolv.confのエントリは正しく読み込まれます。だから私はその特典を失います。完璧ではありません。

Network Managerが使用する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:OpenDNSを構成して、追加のdhcpv6サーバーをRDNSSの一部としてアドバタイズします。このサーバーは、/ etc/resolv.conf内のリンクローカルサーバーよりも優先されます。ローカルリゾルバ(少なくともipv4の場合)が保持されるため、これは回避策#1よりもわずかに優れたソリューションであると感じますが、より複雑です。

  1. fd00:cafe::/48と言う、IPv6 ULAプレフィックスを使用してOpenWrtをセットアップします。 (Menu -> Network -> Interfaces -> bottom of page)。ルーターは、LAN上でfd00::cafe::1のローカルipv6を取得します。 (OpenWRT 15.05.1 chaos落ち着きでこれを試しました)。

  2. Odhcpd(openwrtのそのバージョンでdhcpv6サービスを提供する)に、IPを(追加の)DNSサーバーとしてもアドバタイズするように指示します。 odhcpd(バージョン2015-11-19-01d3f ...)には、dhcp.lan.dns( flag description )と呼ばれる設定フラグがあります。 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の1つしか含まれないため、dhcp6cスクリプトのバグは回避されます。

ネットワーク上にローカルホストリゾルバーを実行していないnotがある場合(たとえば、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サーバーだけでなくルーター全体がダウンしていることを意味しているので、問題ではありません。ただし、走行距離は異なる場合があります。

extra次のスクリプトを使用して、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
0
init_js