®om's blog { un blog libre }

Gnirehtet

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, 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 :

key

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

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.

archi

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.

Cet article est également disponible en anglais sur Medium.

Commentaires

Hello,

Chouette article, super projet. Bravo ! 👍

Philippe G.

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 ?

®om

J’ai créé un fichier aléatoire de 500Mio :

dd if=/dev/urandom of=test bs=1M count=500

J’ai chronométré le temps nécessaire pour le copier par adb directement :

time adb push test /sdcard/

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.

ronan

Merci Romain,

Gnirehtet est exactement ce qu’il me fallait …

Jacques

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)

®om

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 ?

Oui, c’est bien ça.

Y aurait-il une solution autre que de faire la mise à jour d’Android ?

Je te conseille de suivre l’issue #2.

En gros, il serait possible de supporter des versions plus anciennes :

  • soit en ajoutant une dépendance vers le NDK (ce que je voudrais éviter juste pour ça) ;
  • soit en fournissant un mécanisme dégradé pour les anciennes versions (polling).

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 ;-)

Jacques

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.

Poster un commentaire

Nom : (requis)
E-mail : (optionnel, non publié)
Site web : (optionnel)
Quelle est la 3e lettre du mot blog ? (antispam)

Les commentaires sont formatés en markdown.