Gnirehtet
30 Mar 2017 (also available in English)Durant ces dernières semaines chez Genymobile, j’ai développé un outil de reverse tethering pour Android, permettant aux téléphones (et aux tablettes) d’utiliser la connexion internet de l’ordinateur sur lequel ils sont branchés en USB, sans accès root (ni sur le téléphone, ni sur le PC). Il fonctionne sur GNU/Linux, Windows et Mac OS.
Nous avons décidé de le publier en open source, sous le nom de gnirehtet.
Oui, c’est un nom bizarre, jusqu’à ce qu’on réalise qu’il s’agit du résultat de la commande bash :
rev <<< tethering
Utilisation
Il suffit de télécharger la dernière release, de l’extraire, et d’exécuter la commande suivante sur le PC :
./gnirehtet rt
Une fois activé, un logo en forme de clé apparaît dans la barre de statut du téléphone :
Lisez le fichier README pour plus de détails.
Fonctionnement
Le projet est composé de deux parties :
- une application Android (le client) ;
- une application Java pour le PC (le serveur relais).
Depuis, je l’ai réécrit en Rust.
Le client s’enregistre en tant que VPN, de manière à intercepter tout le trafic
réseau du téléphone, sous la forme de byte[]
de paquets IPv4 bruts, qu’il
transmet alors vers le serveur relais sur une connexion TCP (établie
par-dessus adb).
Le serveur relais analyse les en-têtes des paquets, ouvre des connexions à partir du PC vers les adresses de destinations demandées, et relaie le contenu dans les deux sens en suivant les protocoles UDP et TCP. Il crée et renvoie des paquets de réponse vers le client Android, qui les écrit sur l’interface VPN.
D’une certaine manière, le serveur relais se comporte comme un NAT, en cela qu’il ouvre des connexions pour le compte d’autres machines qui n’ont pas accès au réseau. Cependant, il diffère des NAT standards dans la manière dont il communique avec les clients, en utilisant un protocole spécifique (très simple) sur une connexion TCP.
Pour plus de détails, lisez la page développeurs.
Conception
Une fois que l’application est capable d’intercepter l’intégralité du traffic réseau du téléphone, différentes approches sont possibles. Voici celles que j’ai considérées.
TL;DR: J’ai d’abord étudié l’utilisation d’un “TUN device” sur le PC, mais ça ne répondait pas à nos besoins. J’ai ensuite voulu utiliser SOCKS pour bénéficier des serveurs existants, mais des contraintes nous empêchaient de relayer le trafic UDP. Alors j’ai implémenté gnirehtet.
TUN device
Lors de mes recherches pour savoir comment implémenter le reverse tethering,
j’ai d’abord trouvé des projets créant un TUN device sur l’ordinateur
(vpn-reverse-tether
and SimpleRT
).
Cette conception fonctionne très bien, et a plusieurs avantages :
- le traitement est effectué directement au niveau réseau, donc il n’y a pas besoin de traduction entre le niveau 3 et le niveau 5 du modèle OSI ;
- tous les paquets sont retransmis, indépendamment de leur protocole de transport (ils sont donc tous supportés, là où gnirehtet ne supporte “que” TCP et UDP).
Cependant :
- elle nécessite un accès root sur l’ordinateur ;
- elle ne fonctionne pas sur autre chose que Linux.
Il se peut néanmoins que ces applications répondent davantage à vos besoins.
SOCKS
Afin d’éviter d’avoir à développer un serveur relais spécifique, ma première
idée était d’écrire un client qui parlait le protocole SOCKS (suivant le RFC
1928). Ainsi, il serait possible d’utiliser n’importe quel serveur SOCKS
existant, par exemple celui fourni par ssh -D
.
Vous l’avez probablement déjà utilisé pour éviter le filtrage des pare-feux en entreprise. Pour cela, démarrez le tunnel :
ssh mon_serveur -ND1080
Puis configurez votre navigateur pour utiliser le proxy SOCKS localhost:1080
.
N’oubliez pas d’activer la résolution DNS distante pour résoudre les noms de
domaine à partir de mon_serveur
(dans Firefox, activez
network.proxy.socks_remote_dns
dans about:config
).
Malheureusement, l’implémentation d’OpenSSH ne supporte pas UDP, même si le protocole SOCKS5 lui-même le supporte. Et nous avons besoin d’UDP, au moins pour les requêtes DNS (ainsi que pour NTP).
Si vous avez lu attentivement les deux paragraphes précédents, vous devriez vous demander :
Comment Firefox peut-il résoudre les noms de domaine à distance alors que le proxy SOCKS d’OpenSSH ne supporte même pas UDP ?
La réponse se trouve dans la section 4 du RFC : l’adresse de destination demandée peut être une IPv4, une IPv6 ou un nom de domaine. Par contre, pour utiliser cette fonctionnalité, le client (par exemple Firefox) doit savoir qu’il passe par un proxy (puisqu’il doit explicitement passer le nom de domaine au lieu de le résoudre localement), alors que notre reverse tethering doit être transparent.
Mais tout n’est pas perdu. Certes, OpenSSH ne supporte pas UDP, mais ce n’est
qu’une implémentation spécifique, nous pourrions en utiliser une autre.
Malheureusement, SOCKS5 relaie UDP sur UDP, et les téléphones
et l’ordinateur communiquent sur adb (grâce à adb reverse
), qui ne supporte
pas non plus la redirection de ports UDP.
Peut-être que nous pourrions au moins relayer les requêtes DNS en les forçant à utiliser TCP, comme le fait tsocks :
tsocks will normally not be able to send DNS queries through a SOCKS server since SOCKS V4 works on TCP and DNS normally uses UDP. Version 1.5 and up do however provide a method to force DNS lookups to use TCP, which then makes them proxyable.
Mais finalement, SOCKS n’est plus une solution aussi attirante pour implémenter le reverse tethering.
Gnirehtet
Par conséquent, j’ai développé à la fois le client et le serveur relais manuellement.
Ce billet de blog et différents projets open source (SimpleRT
,
vpn-reverse-tether
, LocalVPN
et ToyVpn
) m’ont beaucoup aidé à comprendre
comment implémenter cette solution de reverse tethering.
Conclusion
Gnirehtet permet aux téléphones et tablettes Android d’utiliser facilement la connection internet d’un ordinateur par USB, sans accès root. C’est très utile quand vous ne pouvez pas accéder au réseau par un point d’accès WiFi.
J’espère qu’il pourra être utile à certains d’entre vous.
Discussions sur reddit et Hacker News.
Hello,
Chouette article, super projet. Bravo ! 👍
J’ai testé. Bravo !
Quelle performance peut on espérer ? J’obtient environ 13Mb/s en débit et 50ms de ping. La limite est elle du à adb ? à l’usb ?
J’ai créé un fichier aléatoire de 500Mio :
J’ai chronométré le temps nécessaire pour le copier par
adb
directement :Il a fallu 1mn28, soit 5,68Mio/s.
J’ai ensuite hébergé ce fichier sur le PC avec Apache, puis j’ai chronométré le temps nécessaire pour le télécharger depuis Firefox sur Android (tip:
10.0.2.2
est l’adresse du PC depuis le device lorsque gnirehtet est activé).Ça a pris 1mn35, soit 5,26Mio/s.
Les résultats sont donc très proches d’
adb
(légèrement inférieurs sans doute parce que les paquets TCP sont découpés en plus petits bouts). Je pense donc que la limite vient d’adb
(l’USB 2 et 3 sont plus rapides).Pour info, j’ai fait le même test en wifi (le device et le laptop tous deux connectés à ma box) : 7mn20, soit 1,14Mio/s.
Merci Romain,
Gnirehtet est exactement ce qu’il me fallait …
Salut Merci infiniment pour cet utilitaire, c’est exactement ce que je cherchais ! Par contre, quand le lance ./gnirehtet, j’obtiens un horrible “Failure [INSTALL_FAILED_OLDER_SDK]” De ce que j’ai compris, tu as spécifié un numéro de version minimum dans ton APK, et j’aurais une version trop vieille avec mon Android 4.4.2, cest bien ça ?
Y aurait-il une solution autre que de faire la mise à jour d’Android ? (ce qui ne m’enchante pas du tout)
Oui, c’est bien ça.
Je te conseille de suivre l’issue #2.
En gros, il serait possible de supporter des versions plus anciennes :
En attendant, tu peux utiliser un autre outil de reverse tethering qui fonctionne depuis l’API 14 (mais qui nécessite d’être root sur le pc).
Dans tous les cas, et indépendamment du reverse tethering, vu le nombre de failles de sécurité découvertes depuis les versions 4, il serait raisonnable de mettre à jour ;-)
Malheureusement, impossible d’obtenir une mise à jour au-delà de la 4.4.2 sans rooter l’appareil, le constructeur ne fournissant pas de version supérieure. C’est pour ça que je ne suis pas chaud pour l’upgrade.
Merci pour ta réponse, en tout cas.
Très utile. Merci !
Salut !
Merci, ton outil est franchement génial. Il tourne nickel sous Lineage OS (OnePlus 2, 7.1 Nougat) :)
J’ignore si tu comptes le maintenir plus longtemps que ça, mais as-tu envisagé d’y ajouter quelques fonctions ? Personnellement, j’utilise NetGuard sur mon téléphone (pare-feu open-source qui s’installe en tant que VPN et qui permet de filtrer le trafic des applications), mais je suis obligé de le désactiver pour faire tourner ton application. Je ne sais pas si c’est possible sans root, mais un module permettant de faire passer NetGuard à travers gnirethet serait vraiment cool !
Je me demandais aussi s’il est obligatoire de dépendre d’un serveur DNS (comme celui de Google) pour résoudre les adresses. Je ne m’y connais pas dans ce domaine, mais n’est-il pas possible de résoudre les adresses de la même manière que l’hôte (ou n’importe quel appareil connecté à Internet) le ferait ? J’avoue que je ne suis pas fan de l’idée de dépendre d’un système tiers.
Dernièrement, ça n’a aucun rapport avec cette page mais je me permets de te conseiller d’ajouter un certificat TLS à ton site web ; des services comme Let’s Encrypt (open source) peuvent t’en générer un gratuitement.
Bonne continuation !
Pour paraphraser NetGuard : Android ne permet pas de chaîner des services VPN, donc il n’est pas possible d’utiliser gnirehtet en même temps que d’autres applications utilisant un service VPN.
Tu peux préciser l’adresse du ou des serveurs DNS à utiliser :
Par exemple, pour un serveur DNS de Free :
Si tu n’en précises pas, il utilise celui de Google (8.8.8.8).
Vu que c’est le device Android qui initie les connexions, c’est lui qui forge les requêtes DNS (qu’il envoie dans des paquets UDP qui sont relayés par gnirehtet). Pour cela, il doit connaître l’adresse du serveur DNS à utiliser.
Si tu veux le même serveur que sur le PC, utilise une adresse listée dans
/etc/resolv.conf
.Oui, j’ai ça dans la section “à faire un jour” de ma todo-list (virtuelle)… :)
Hello, on Android 7.1.2 I have the problem that certain apps (Playstore for example) does not recognice the network connection. Other Apps are working fine.
Is there a solution for that?
I have no solution (except fixing the apps).
https://github.com/Genymobile/gnirehtet/issues/37
@Wyles
Done ;-)
Merci beaucoup pour ce merveilleux utilitaire. Il fonctionne très bien avec Galaxy S5 Neo.
Voici quelques lignes de gnirehtet.cmd que j’ai modifié
Merci encore
Content que ce logiciel soit utile :)
Merci pour le code. Cependant, l’interface en ligne de commande n’est plus écrite dans un script séparé (donc le contenu de ce fichier n’existe plus sur la branche de développement).
Par ailleurs, les contributions de code se font par pull request.
Encore merci, c’est vraiment super.
Très joli, merci !
Pour faire passer du UDP en natif dans un cable USB, tu peux utiliser https://www.raspberrypi.org/forums/viewtopic.php?f=36&t=18916 : Settings / Wireless & Networks / More / Tethering & Portable Hotspot… click “USB”. Puis, si on est root sur sur l’ordi, taper
histoire de virer la route (non prioritaire) de tethering. 192.168.42.129 est l’IP du téléphone, affichée par
/sbin/route -n
.Bonjour, pourquoi diable dire à la fin de l’article en anglais qu’il existe en français ? Au début c’est plus utile. Ici on dit bien au début qu’il existe en anglais.
@Bibi, merci, je viens de corriger.
Les commentaires sont fermés.