Le VPN Android fonctionne en IPv6, mais le navigateur choisit encore IPv4
Suivez les quatre étapes où une adresse IPv6 privée disparaît : le résolveur, la déclaration VpnService, la vérification des candidats par le navigateur et la livraison de la route.
Le VPN est actif, l'interface web privée répond en IPv6 depuis un ordinateur portable, et le téléphone Android ouvre toujours la même page en IPv4 ou refuse de la charger. L'enregistrement AAAA existe. Le tunnel annonce une adresse IPv6. Rien dans le client ne semble anormal. Quatre décisions distinctes déterminent le résultat : quel résolveur a répondu, ce que le VpnService a déclaré à Android, quelle famille d'adresses le navigateur était prêt à essayer et si un paquet peut faire l'aller-retour. Traitez-les dans cet ordre plutôt que d'ajouter une route par défaut en espérant que le symptôme se déplace.
Étape 1 : prouvez quel résolveur a répondu à la requête AAAA
Android n'a pas un seul chemin DNS. Un VPN peut pousser un serveur DNS pour son réseau, le système peut utiliser Private DNS pour les noms d'hôte qu'il juge éligibles, et Chrome peut exécuter son propre résolveur chiffré depuis chrome://settings/security. Sur un téléphone où le tunnel pousse 10.61.0.1 mais où Private DNS pointe vers un fournisseur public, le nom de votre zone privée n'est pas résolu par la machine qui possède la zone. Le navigateur reçoit une réponse d'un résolveur sans autorité sur ce nom et la traite comme absente, et le texte de l'erreur ne mentionne jamais DNS.
Interrogez directement le résolveur du tunnel avant de changer quoi que ce soit d'autre. Les deux premières commandes montrent quels serveurs DNS Android a attachés au réseau actif et si Private DNS est en mode automatique ou strict ; la troisième s'exécute depuis n'importe quel client à l'intérieur du tunnel, pas depuis le téléphone.
adb shell dumpsys connectivity | grep -A3 "DnsAddresses"
adb shell settings get global private_dns_mode
nslookup -type=AAAA nas.lan 10.61.0.1
Étape 2 : lisez ce que le VpnService a réellement déclaré
Le côté Android est explicite et non déduit. Un VpnService.Builder ajoute des adresses avec addAddress, des routes avec addRoute, des serveurs DNS avec addDnsServer, et peut restreindre tout le tunnel à une seule famille d'adresses avec allowFamily. Un constructeur qui a appelé allowFamily(AF_INET) puis ajouté une route IPv6 a dit au système deux choses contradictoires, et c'est la restriction de famille que le système conserve. Une route couvrant fd00:61::/64 combinée à une adresse d'interface dans un autre préfixe échoue pour une raison similaire : l'adresse existe, mais elle n'est pas on-link pour cette route.
Lisez la déclaration au lieu de faire confiance à l'interrupteur de l'application. dumpsys netd affiche les adresses et les routes attachées à l'interface tun, et le LinkProperties par réseau dans dumpsys connectivity montre la liste DNS à côté des familles que le réseau revendique.
adb shell ip -6 addr show tun0
adb shell ip -6 route show table all | grep tun0
adb shell dumpsys connectivity | grep -E "LinkAddresses|Routes"
Si l'interface tun possède une adresse IPv6 mais qu'aucune route ne correspond au préfixe du service, la livraison IPv6 est déjà cassée avant qu'un navigateur n'entre en jeu. C'est un bogue différent de celui d'un navigateur qui refuse d'essayer, et il demande un correctif différent.
Étape 3 : le navigateur décide s'il tente IPv6
Chromium n'utilise pas tous les enregistrements AAAA qu'il reçoit. Son résolveur d'hôtes embarque des vérifications d'accessibilité IPv6 et un filtrage des candidats qui peuvent écarter ou rétrograder IPv6 lorsque le réseau est jugé incapable de le porter, et le code source du résolveur montre cette sonde avec les conditions qui l'entourent. La logique dépend de la version et des indicateurs, et c'est précisément pourquoi deux téléphones sur le même tunnel peuvent se comporter différemment. Une fois les candidats présents, Happy Eyeballs fait courir les familles par connexion, si bien qu'un chemin IPv6 qui répond lentement peut encore perdre contre IPv4 qui répond vite.
Observez la décision au lieu de la déduire. Une session enregistrée via chrome://net-export et rejouée dans la visionneuse netlog montre chaque résolution et la famille choisie ; chrome://net-internals/#dns montre les résultats en cache. Une résolution qui ne renvoie que des enregistrements A pour un nom qui a AAAA sur votre propre résolveur désigne directement cette étape.
Étape 4 : vérifiez qu'un paquet peut faire l'aller-retour
La sélection d'adresse n'est pas la livraison. Android conserve des tables de routage et des règles de stratégie par réseau qui choisissent une table par fwmark, donc une route visible dans la table principale n'est pas automatiquement celle qu'un socket utilise. Lire les règles et demander au noyau de résoudre une destination pour une adresse source sépare les deux.
adb shell ip -6 rule show
adb shell ip -6 route get fd00:61::20 from fd00:61::10
C'est la sortie de route get qui répond à la question. Si elle nomme tun0 et que les paquets meurent encore, passez au MTU du tunnel et au transfert IPv6 de la passerelle plutôt que de revenir au téléphone.
Pourquoi ajouter ::/0 aggrave généralement les choses
Ajouter ::/0 fait passer la prétention du tunnel d'un préfixe à toutes les destinations IPv6. Sur le téléphone, cela peut satisfaire une sonde d'accessibilité, car une adresse de test hors de votre préfixe paraît désormais routable. Cela ne crée pas de capacité IPv6 amont sur la passerelle, et beaucoup de passerelles domestiques n'ont aucune liaison montante IPv6 fonctionnelle. Le résultat est un trou noir pour l'IPv6 public : des noms qui retombaient sur IPv4 restent maintenant suspendus, et la page privée peut sembler meilleure uniquement parce que la sonde mesure autre chose.
Il y a aussi un coût de stratégie. Avec ::/0 dans le tunnel, tout le trafic IPv6 sort par le VPN, y compris le trafic que le propriétaire du réseau n'a jamais accepté de porter. Traitez cela comme une décision de déploiement avec un responsable nommé, pas comme une étape de débogage prise à 1 heure du matin.
L'ordre de travail qui met fin à cela
Confirmez que la réponse AAAA vient du résolveur qui détient la zone, lisez l'état de VpnService et de netd pour l'interface tun, capturez une trace net-export pour voir si Chromium a écarté le candidat, puis testez l'aller-retour avec ip -6 route get. Corrigez la couche qui a réellement échoué et laissez la stratégie de séparation tranquille. Si les noms privés ont besoin de réponses différentes des noms publics, c'est exactement à cela que sert le split tunneling.
Notez la version du téléphone, la version du navigateur et le résolveur interrogé. Ces trois faits transforment « IPv6 is broken » en quelque chose que la personne suivante peut reproduire.