Routage VPN Android : les règles de politique avant la table main
Android choisit une table de routage via des règles d'uid et de marque avant toute recherche de destination, si bien qu'une route ajoutée à main peut n'être jamais consultée.
Une route ajoutée à la table main sous Android paraît correcte dans toutes les listes et ne transporte pourtant pas le trafic. La raison est que la table main n'est qu'une option parmi plusieurs, et elle n'est pas consultée en premier. Le service réseau d'Android construit des règles de politique qui choisissent une table de routage à partir d'informations que l'adresse de destination ne contient pas — l'uid de l'application, une marque sur le paquet, l'interface par laquelle il est arrivé — et la recherche de destination a lieu ensuite, dans la table que ces règles ont choisie.
Trois décisions dans un ordre fixe
Un paquet sortant prend trois décisions distinctes. D'abord, une règle de politique détermine quelle table de routage s'applique. Ensuite, une route dans cette table détermine le prochain saut et l'interface de sortie. Enfin, les hooks netfilter décident si le paquet survit, est modifié ou est redirigé.
L'ordre compte car chaque étape ne peut répondre qu'à sa propre question. Une règle de pare-feu qui accepte un paquet ne choisit pas de table, et une route ajoutée à une table qu'aucune règle ne choisit est inerte. Une bonne part de la confusion autour des VPN Android vient du fait qu'on corrige l'une des trois et qu'on n'observe aucun changement, parce que la panne était dans une autre.
| Stage | Selector | What it cannot decide |
|---|---|---|
| Policy rule | uid range, mark, incoming interface | Which route exists inside the table |
| Route lookup | Destination prefix, longest match | Whether the packet is allowed out |
| Netfilter hook | Chain position, match conditions | Which table was selected |
Les marques sont un champ compacté, pas une étiquette qui vous appartient
Android compacte plusieurs faits dans la même valeur de marque : quel réseau a été choisi, si une application a explicitement choisi un réseau, si une socket est protégée du VPN et des bits liés aux permissions. L'en-tête fwmark d'AOSP définit cette disposition pour l'arbre actuel, et la disposition est un détail interne de la plateforme qui change d'une version à l'autre.
C'est pourquoi écrire une valeur de marque entière avec iptables -j MARK --set-mark est dangereux sous Android. Cela n'ajoute pas une étiquette ; cela remplace la sélection de réseau que le système a déjà faite. Le paquet correspond alors à d'autres règles, ce qui peut donner l'impression que le VPN s'est mis à fonctionner, ou qu'une application sans rapport a perdu la connectivité, selon les bits écrasés.
Si vous devez toucher aux marques, lisez d'abord la valeur actuelle et utilisez un masque pour que seuls les bits que vous comprenez soient modifiés :
ip rule show
ip route show table all | head -40
ip route get 10.20.4.11Une marque de connexion est un objet distinct d'une marque de paquet, et aucune des deux n'est une route. Restaurer la marque sur les paquets de réponse est une étape distincte du choix d'une table à la sortie, et c'est pourquoi les corrections unidirectionnelles produisent des connexions qui s'ouvrent puis se bloquent.
Qui possède les règles et les tables
Le service réseau d'Android, netd, crée les règles et les tables par réseau. Le AOSP RouteController est l'endroit où ces règles sont assemblées pour les VPN, les réseaux physiques et les plages d'uid, et son cycle de vie est lié à des événements réseau plutôt qu'à un fichier de configuration.
Deux conséquences en découlent. D'abord, les priorités et les numéros de table que vous trouvez sur un appareil sont des détails internes de compilation, pas une interface : des numéros d'apparence identique sur une autre version d'Android peuvent signifier tout autre chose. Ensuite, l'état est reconstruit lorsque les réseaux changent, si bien qu'une règle insérée aujourd'hui depuis un shell root peut avoir disparu après la transition Wi-Fi suivante.
Cette disparition est souvent décrite comme un système qui ignore une route. Ce qui s'est réellement passé, c'est que la règle qui choisissait la table n'existe plus, et la route dans la table est désormais inatteignable du point de vue du paquet. Vérifiez la liste des règles, pas celle des routes, lorsqu'une configuration qui fonctionnait cesse de fonctionner après une reconnexion.
Les questions d'ordre des hooks n'ont pas de réponse universelle
La question de savoir si iptables s'exécute avant le routage VPN n'a pas de réponse sans nommer le chemin du paquet. Un paquet généré localement traverse le hook output, où un changement de marque déclenche une vérification de reroutage — une seconde recherche, pas une modification rétroactive de la première. Un paquet venant d'un client en tethering traverse le chemin prerouting et est réexpédié à la place, et les chaînes qui le voient sont différentes.
La même règle peut donc être efficace pour le trafic d'une application et invisible pour le trafic en tethering, et un compteur qui reste à zéro vous indique quel chemin le paquet a pris, non si la règle est fausse. Utilisez les compteurs pour établir le chemin avant de changer une condition de correspondance.
C'est aussi l'étape où une erreur de routage par politique devient difficile à voir. Router vers une table qui existe mais ne contient pas la destination produit un réseau inaccessible qui ressemble à un rejet du pare-feu, tandis qu'un rejet du pare-feu produit un délai d'attente qui ressemble à un trou noir de routage.
Diagnostiquer dans le système, ou le laisser tranquille
Pour un examen autorisé, établissez quatre faits avant de toucher à quoi que ce soit : l'uid et le profil de l'application, le réseau qu'Android a choisi pour elle, si le VPN inclut cette application, et si le problème existe pour les deux familles d'adresses. Une exclusion par application explique un grand nombre de rapports VPN ne fonctionne pas sans aucune panne de routage.
Lisez ensuite les règles, masques et tables réellement en vigueur, et gardez un changement de réseau dans la fenêtre d'observation. Si le comportement change au fil d'une reconnexion, vous regardez un cycle de vie géré, pas une route manquante.
Pour les besoins ordinaires, utilisez les contrôles pris en charge : inclusion et exclusion par application dans l'application VPN, et la sélection de réseau propre à la plateforme pour tout ce qui exige un chemin précis. Si une intégration privilégiée doit réellement coopérer avec la machinerie de routage, ciblez l'implémentation de la version exacte d'Android que vous prenez en charge et maintenez son cycle de vie via le composant qui en est propriétaire. Vider les chaînes de la plateforme ou insérer une règle de haute priorité transforme un diagnostic en second gestionnaire de réseau, et le téléphone se comportera en conséquence.