web-dev-qa-db-fra.com

link local (fe80: :) les adresses sans portée se frayent un chemin dans /etc/resolv.conf et causent des problèmes

J'ai des problèmes de connectivité intermittents. Une adresse locale de lien ipv6 est automatiquement ajoutée à mon fichier /etc/resolv.conf, ce qui semble empêcher la résolution du résolveur de libc. Je voudrais savoir comment empêcher cette adresse d'y être insérée ou trouver une solution de contournement appropriée.

Ma configuration: J'ai une configuration de déploiement de bureau Ubuntu 14.04 avec ipv4 et ipv6. Il ne dispose que d’une connexion filaire (pas de wifi) à un port LAN d’un routeur domestique exécutant OpenWrt. La gestion de réseau du bureau est prise en charge par NetworkManager, qui exécute sa propre copie locale de Dnsmasq. Tous les fichiers de gestionnaire de réseau dans/etc sont "stockés", je ne les ai pas touchés.

Lorsque je réinitialise mon réseau via le gestionnaire de réseau, tout fonctionne correctement (mais seulement pendant quelques minutes). Ma configuration de travail de resolv.conf ressemble à ceci:

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

Mon routeur (192.168.1.1 ou fe80 :: beef) exécute également une copie de Dnsmasq et est configuré pour annoncer 192.168.1.1 (lui-même) à ses clients DHCP à partir de la v4. Sur la v6, il envoie périodiquement des messages de publication de routeur (icmpv6.type == 134) avec une option de serveur DNS récursive pour fe80 :: beef. Le service DNS dnsmasq du routeur est à l'écoute des deux adresses: .1.1 et :: beef (l'adresse de liaison ipv6 du pont de réseau local du routeur).

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

À tout moment, si j'accède à "Informations de connexion" dans NetworkManager, mon DNS principal et mon routeur dans Ipv4 sont définis sur 182.168.1.1. L’interface graphique de NetworkManager ne montre aucune information dans l’en-tête de la section "ipv6" - mais mon nic reçoit des adresses ipv6 (adresses slaac et dhcpv6 avec état) que je peux voir avec _ip addr show_.

Le problème: Après la réinitialisation de mon réseau via le gestionnaire de réseau (basculer sur "Activer le réseau") et attendre (c'est-à-dire attendre le prochain message de publication du routeur, je soupçonne), une nouvelle entrée se rend à _/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
_

Le problème est que, une fois que cela se produit, certains outils utilisateur (y compris Firefox et Google Chrome) ne parviendront pas à résoudre les noms de domaine (non mis en cache).

Pour autant que je sache, travailler avec des adresses locales de lien nécessite que la portée d'un lien soit explicitement mentionnée. La trace suivante montre comment connect échoue sans la portée du lien (valeur par défaut scope_id de 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 +++
_

Le DNS échoue, mais j'ai la connectivité sinon:

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

L'ajout de la portée (% eth0) à la fin de l'adresse dans le fichier resolv.conf résout le problème suivant:

_nameserver fe80::beef%eth0
nameserver 127.0.1.1
search lan
_

Mais bien sûr, ce changement est effacé la prochaine fois.

Est-il possible de:

  • Forcer ipv4 uniquement à interroger DNS (je ne pense pas que je lancerai une configuration ipv6 uniquement)
  • Spécifiez une portée d'interface par défaut dans le résolveur ("% eth0")
  • Remplacez par dnsmasq (ou radvd ou rdnss) le routeur par et non par la publicité de son adresse ipv6 pour DNS (uniquement son ipv4).

Edit: échec de la tentative de réparation

Si je déplace le lien symbolique _/etc/resolv.conf_ vers _/etc/resolv.conf.old_ et écris mon propre statique _/etc/resolv.conf_ contenant uniquement l'adresse IP du serveur Dnsmasq local (serveur de noms 127.0.1.1), le fichier est toujours en cours de modification par quelque chose d'autre qui ajoute une ligne "recherche".

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

Après un peu de temps:

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

Après un redémarrage:

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

Alors. À moins que je ne commence à jouer avec chattr + i et d'autres astuces pour empêcher ce script (ou quoi que ce soit d'autre) de toucher /etc/resolv.conf, j'ai l'impression que cette option semi-statique n'est pas vraiment propre. La responsabilité lorsque ces fichiers sont modifiés ou la journalisation à ce sujet serait un plus. syslog n'a rien.

Remarque: pour tenter de masquer certaines informations privées, j'ai rédigé les suffixes d'adresse et les noms d'hôte ci-dessus .

3
init_js

Je réponds à ma propre question, en fournissant une explication et un correctif pour référence.

Le problème réside dans les scripts dhcp6c, fournis avec le package wide-dhcpv6-client. dhcp6c s'exécute en tant que démon sur mon système (démarré par /etc/init.d/wide-dhcpv6-client) et envoie des requêtes DHCPv6 toutes les quelques minutes. Je ne pense pas que wide-dhcpv6-client soit installé par défaut dans ubuntu desktop. Sa configuration (/etc/wide-dhcpv6/dhcp6c.conf) est configurée pour appeler le script /etc/wide-dhcpv6/dhcp6c-script lorsqu'une mise à jour doit être effectuée. Ce script est buggy:

  1. Il ignore la portée dans le cas des adresses de liens locaux

  2. Dans les cas où /etc/resolv.conf n'est pas géré par resolvconf (c'est-à-dire qu'il ne s'agit pas d'un lien symbolique), il en résulte l'ajout de lignes search X sans vérifier si elles sont déjà présentes (le fichier s'agrandit sans cesse).

En supposant que dhcp6c ** surveille une seule interface, appliquer un correctif au fichier /etc/wide-dhcpv6/dhcp6c-script avec le correctif ci-dessous résoudra le problème:

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

+# Devinez l'interface à utiliser comme scope_id. Idéalement, dhcp6c transmettrait le nom 
 + # De l'interface sur laquelle la réponse DHCPv6 a été reçue. 
 + Scope = "" 
 + Pour IFACE dans $ INTERFACES; do 
 + scope = "$ IFACE" 
 + pause; 
 + terminé 
 + 
 + # correspondance exacte de la ligne. grep interpréterait les périodes de domaine comme des caractères spéciaux 
 + # caractères. La boucle naïve évite de s’appuyer sur d’autres commandes 
 + # Non coreutils (sed, awk, etc.). 
 + Has_line () {
 + Ligne locale 
 + pendant la lecture de la ligne; do 
 + if ["$ line" = "$ 1"]; alors 
 + retour 0 
 + fi 
 + terminé 
 + retour 1 
 +} 
 + 
 + 
 if [-n "$ nouveau_nom_domaine" -o -n "$ nouveau_nom_domaine_serveurs"]; 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 
 - echo search $ 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"]; puis 
 pour le serveur de noms dans $ new_domain_name_servers; do 
 + if [-n "$ scope" -a "$ {nameserver ## fe80 ::}"! = "$ nameserver"]; then 
 + serveur de noms = "$ serveur de noms% $ scope" 
 + fi 
 + 
 # Il n'est pas nécessaire d'ajouter un serveur de noms déjà existant 
 - res = $ (grep "serveur de noms $ serveur de noms" $ 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 

** Le code ci-dessus choisit la première interface dans la liste des interfaces de /etc/default/wide-dhcpv6-client et le suffixe à tout serveur de noms commençant par fe80::. Ceci est similaire à ce qui est fait dans /etc/dhcp/dhclient-enter-hooks.d/resolvconf. Ce n'est pas idéal, surtout si vous avez plusieurs interfaces. dhcp6c ne transmet pas le nom de l'interface dans l'environnement, nous ne pouvons donc que le deviner. (bug déposé: https://bugs.launchpad.net/ubuntu/+source/wide-dhcpv6/+bug/1620221 )

Si vous avez plusieurs nics et que vous avez besoin que cela soit plus robuste, vous pouvez plutôt configurer /etc/wide-dhcpv6/dhcp6c.conf pour démarrer une copie différente du script en fonction du nom de l'interface et coder en dur le nom de l'interface dans le script.

Avant de comprendre le problème, j'avais découvert deux solutions de contournement, qui ne résolvaient pas le problème, mais évitaient les symptômes.

Solution de contournement 1: désactivez le résolveur local Dnsmasq.

Les entrées du fichier resolv.conf sont renseignées correctement lorsque le service Dnsmasq de NetworkManager est désactivé. Alors je perds ses avantages. Pas parfait.

Étapes suivies dans Comment puis-je désactiver le DNS utilisé par Network Manager? . Voir aussi liste des implications pour la sécurité et les performances de la désactivation du résolveur local.

Après avoir redémarré NM, l'adresse locale du lien avait la portée ajoutée correctement:

# 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

Je ne sais pas exactement pourquoi cela fonctionne, mais je suppose que c'est parce que NetworkManager et dhcp6c écrivent dans resolvconv, et que le script de mise à jour favorise fe80 :: beef% eth0 (transmis par NetworkManager) par rapport à la même adresse sans la portée (passée par dhcp6c).

Solution de contournement 2: Configurez OpenWrt pour publier un serveur dhcpv6 supplémentaire dans le cadre de RDNSS. Ce serveur aura la priorité sur le lien local dans /etc/resolv.conf. Je pense que cette solution est légèrement meilleure que la première solution, car elle préserve le résolveur local (au moins pour ipv4), mais elle est plus compliquée.

  1. Configurez OpenWrt avec un préfixe IPv6 ULA, dites fd00:cafe::/48. (Menu -> Network -> Interfaces -> bottom of page). Le routeur obtiendra un ipv6 local de fd00::cafe::1 sur le réseau local. (J'ai essayé cela sur OpenWRT 15.05.1 chaos calmer).

  2. Demandez à odhcpd (qui fournit le service dhcpv6 sur cette version d’openwrt) d’annoncer également l’adresse IP en tant que serveur DNS (supplémentaire). odhcpd (version 2015-11-19-01d3f ...) propose un indicateur de configuration appelé dhcp.lan.dns ( description de l'indicateur ). Dans luci UI, cela se trouve sous Menu -> Network -> Interfaces -> LAN (click edit) -> DHCP Server section -> IPv6 Settings tab -> Announced DNS Servers. Ajoutez l'adresse ULA du routeur au champ. J'ai également ajouté l'adresse de sous-réseau ipv4 du routeur à la liste des serveurs DNS (192.168.1.1), par souci de symétrie.

Ceci mis en place, odhcpd affichera les deux adresses dans RA, mais NM, à notre avantage, écrit uniquement l'adresse ULA dans resolv.conf. L'adresse lien-local n'est jamais ajoutée à 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

En examinant les traces wirehark, le message RA de openwrt, lorsqu'il est configuré de cette manière, répertorie les deux adresses de serveur DNS, mais ses réponses DHCPv6 ne comportent que le nom fd00:cafe::1. Le bogue dans les scripts dhcp6c est donc évité.

Si vous avez un hôte Unix sur le réseau et non exécutant un résolveur local (par exemple, si vous avez appliqué les solutions de contournement 1 et 2 sur ubuntu), resolv.conf aurait toutes les adresses 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

L'algorithme par défaut dans le résolveur de libc consiste à essayer chaque entrée de resolv.conf à son tour, en respectant un délai d'attente écoulé. Ainsi, avoir plusieurs alias sur le même serveur DNS peut entraîner des délais supplémentaires en cas de problèmes. Dans mon cas, cela signifie très probablement que tout mon routeur est en panne, et pas seulement son serveur DNS, ce n'est donc pas un problème. mais votre kilométrage peut varier.

extra J'ai utilisé le script suivant pour déboguer les modifications apportées à resolvconf. Cela m'a permis de déterminer quel démon envoyait les modifications incorrectes:

#!/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" "$@"

Avec ceci en place, syslog contient des blocs tels que:

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