From 02874cf12e4312bca93b0fca027b5b878d3ed376 Mon Sep 17 00:00:00 2001 From: Symphorien Gibol <symphorien.gibol@gmail.com> Date: Mon, 6 Jun 2016 21:13:05 +0200 Subject: [PATCH] documentation --- .gitignore | 4 ++ README | 6 --- doc/doc.tex | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 6 deletions(-) delete mode 100644 README create mode 100644 doc/doc.tex diff --git a/.gitignore b/.gitignore index 14ed09e..3a75095 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ *.class *.swp *.swo +**/__pycache__ +tunmgr/venv +doc/* +!doc/doc.tex diff --git a/README b/README deleted file mode 100644 index 6aebc53..0000000 --- a/README +++ /dev/null @@ -1,6 +0,0 @@ -le patch ajoute une option sysctl net.ipv4.tcp_initial_rto au noyau linux. -Il provient de https://www.spinics.net/lists/netdev/msg164645.html et a été adapté. -Testé sur linux hardened 4.4.8-r1. -Il y a un problème d'unités; aussi il vaut mieux mettre dans /etc/sysctl.conf : -net.ipv4.tcp_initial_rto = 1 -30 est une bonne valeur pour faire du sms. diff --git a/doc/doc.tex b/doc/doc.tex new file mode 100644 index 0000000..3d22791 --- /dev/null +++ b/doc/doc.tex @@ -0,0 +1,131 @@ +\documentclass{article} +\usepackage[frenchb]{babel} +\usepackage{amsmath} +\usepackage{fontspec} +\usepackage{amssymb} +\usepackage{rotating} +\usepackage{hyperref} + +\author{Guillaume Vizier, Guillaume Girol} +\title{IP over SMS} + +\begin{document} +\maketitle + +\section*{Structure} +L'objectif est de fournir une paire de device \texttt{tun} permettant à deux ordinateurs Linux munis d'un téléphone Android chacun d'échanger du traffic IP grâce à deux abonnements SMS illimités. + + +\begin{sidewaysfigure} +\footnotesize{ + \fbox{\parbox{0.47\textwidth}{Alice \\ + \fbox{\parbox{0.29\textwidth}{Ordinateur \\ + \fbox{Userland} $\leftrightarrow$ + \fbox{\texttt{/dev/tun?}} $\leftrightarrow$ + \fbox{\texttt{tunmgr.py}} + }} + $\underleftrightarrow{\text{UDP}}$ + \fbox{\parbox{0.1\textwidth}{Téléphone \\ + \fbox{Application} + }} + }} + $\underleftrightarrow{\text{SMS}}$ + \fbox{\parbox{0.47\textwidth}{Bob \\ + \fbox{\parbox{0.1\textwidth}{Téléphone \\ + \fbox{Application} + }} + $\underleftrightarrow{\text{UDP}}$ + \fbox{\parbox{0.29\textwidth}{Ordinateur \\ + \fbox{\texttt{tunmgr.py}} $\leftrightarrow$ + \fbox{\texttt{/dev/tun?}} $\leftrightarrow$ + \fbox{Userland} + }} + }} +} +\caption[Architecture du dispositif]{Architecture du dispositif: chacun des ordinateurs est connecté au hotspot wifi du téléphone respectif. Les paquets traversent successivement le réseau wifi, le réseau cellulaire puis à nouveau le réseau wifi.} +\label{archi} +\end{sidewaysfigure} + +L'architecture choisie est décrite par la figure \ref{archi}. Elle a l'avantage, malgré une certaine complexité, de ne pas nécessiter de matériel dédié (comme un lecteur de carte SIM), mais seulement : +\begin{itemize} + \item Un ordinateur Linux disposant d'une carte wifi (ou d'un port USB) + \item Un téléphone Android capable de créer un hostpot wifi (ou de partager sa connection en USB) + \item Un abonnement SMS illimité +\end{itemize} + +\section*{Mise en place} +\begin{enumerate} + \item Se procurer le code source + \item \texttt{pip install -r tunmgr/requirements.txt} + \item Compiler l'application Android contenue dans le dossier \texttt{guillaume} et l'installer sur votre terminal + \item Activer le hostpot wifi de votre téléphone, noter son adresse ip $A$. Le plus souvent $A = 192.168.43.1$. + \item Connecter l'ordinateur au hostpot, noter son adresse ip B. Le plus souvent $B \in 192.168.43.0/24$. + \item Lancer l'application (elle s'appelle \emph{SendSMS}), y enter $B$ et le numéro de téléphone du correspondant. Appuyer sur le bouton (une seule fois !). Votre correspondant doit reçevoir un SMS disant \og{}serveur udp lancé\fg{}. + \item En tant que root, lancer \texttt{python3 tunmgr/tunmgr.py $A$}. +\end{enumerate} + +Recommencer chez votre correspondant, à la différence près que la dernière commande à lancer devient \texttt{python3 tunmgr/tunmgr.py $A$ reversed}. + +Vous obtiendrez un tunnel (normalement appelé \texttt{/dev/tun0}) d'extrémités 10.8.0.1 et 10.8.0.2. Pinguez votre correspondant et appréciez la latence. + +\section*{Caveat} +\paragraph{SMS multiparts} Par défaut (c'est hardcodé, mais bien visible dans le code de \texttt{tunmgr.py}) nous utilisons des SMS multiparts allant jusqu'à 14 SMS de façon à obtenir un MTU suffisament grand (1800 octets environ) pour faire passer de l'IPV6 par exemple. Cependant, certains terminaux ne supportent pas des SMS aussi longs, et réduire 14 à 6 par exemple sera peut-être nécessaire. + +\paragraph{Pare-feu} Il vous faut ouvrir le port 51117 en UDP sur le téléphone et l'ordinateur, en entrée et en sortie. + +\paragraph{Injection de paquets} L'aspect sécurité du tout a été totalement négligé : quiconque vous envoie un sms pendant la durée d'utilisation de ces programmes peut injecter des paquets arbitraires (bien formés ou non) sur votre réseau. Lors de l'écriture de règles de pare-feu, considérez que le device tun ainsi créé est exposé au \emph{wild internet}. + +\paragraph{TCP over SMS} TCP fonctionne en renvoyant les paquets dont il n'a pas encore reçu les ACK. Le délai à attendre avant de retransmettre un tel paquet est appelé RTO (Retransmission TimeOut). Cette quantité est calculée en fonction du RTT estimé (Round Trip Time, temps d'aller retour du couple paquet -- ACK) selon les prescriptions de la RFC 6298. Le RTO par défaut est de 1 seconde, ce signifie qu'à l'établissement d'une connexion TCP, tant que l'ACK du SYN n'est pas reçu, il y aura une retransmission au bout de 1, 2, 4 etc. secondes. Or le RTT typique d'un échange over SMS est plutôt de 12 secondes les jours fastes. L'établissement d'une connexion TCP over SMS résulte donc en une cacophonie de retransmissions jusqu'à ce que le RTO devienne plus raisonnable (de l'ordre de la dizaine de secondes au moins) et que la congestion SMS se résorbe. Bref, ce n'est pas rose. + +Une solution consisterait à modifier la valeur initiale du RTO (pour la régler à 30 secondes par exemple) ce qui, malgré nos efforts, s'est révélé impossible sur un kernel vanilla. Nous fournissons donc un patch \texttt{sysctl\_tcp\_initial\_rto.patch} qui ajoute une variable sysctl appelée \texttt{net.ipv4.tcp\_initial\_rto} qui est le RTO par défaut en secondes. Ce patch est inspiré de \url{https://www.spinics.net/lists/netdev/msg164645.html} et a été adapté pour linux hardened 4.4.8-r1. Il contient une erreur d'unités qui fait que la valeur par défaut de \texttt{net.ipv4.tcp\_initial\_rto} est de 1000 au lieu de 1. Vous êtes donc vivement invités à ajouter à \texttt{/etc/sysctl.conf} la ligne +\begin{verbatim} +net.ipv4.tcp_initial_rto = 1 +\end{verbatim} + +Ensuite, au moment d'utiliser le tunnel IP over SMS, nous vous conseillons d'augmenter cette valeur à 30. Vous verrez que la congestion disparaîtra. + +\section*{Aspects techniques} + +Nous allons détailler symboliquement comment un paquet IP $A$ est transporté d'un bout à l'autre du tunnel. + +\subsection*{Conversions} +Nous aurons besoin d'une injection $gsmencode$ qui convertit une suite d'octets en un message n'utilisant que les caractères de l'alphabet GSM, et d'un inverse $gsmdecode$. Une des contraintes est que la longueur du message encodé en GSM soit connue à l'avance de sorte que le MTU du device tun puisse être adapté pour faire rentrer tous les messages dans le nombre de SMS voulu. + +L'alphabet GSM est composé de 128 caractère donc un caractère d'échappement qui ne peut être utilisé que suivi d'une poignée d'autres lettres pour former des caractères comme \texttt{\{}. Nous avons renoncé à utiliser ce caractère. Nous obtenons un alphabet à 127 caractères. + +Étant donné une suite d'octets $b_i$, nous calculons $N = \sum b_i 256^i$. Puis nous décomposons $N$ en base 127 : $N = \sum g_i 127^i$ sans 0 préfixes. D'autre part, nous écrivons la longueur de $(b_i)$ en base 127 sur deux chiffres : $l_0 + 127l_1$. Alors, $gsmencode((b_i)) = (l_0, l_1, g_0, g_1, \dots)$. + +L'opération inverse est simple, et on voit facilement comment la longueur de $gsmencode((b_i))$ est bornée par la longueur de $(b_i)$. + +Ces opérations sont définies dans \texttt{tunmgr/base.py} pour les changements de base, et dans \texttt{tunmgr/gsm.py} pour \texttt{gsmencode} et \texttt{gsmdecode}. + +\subsection*{Protocoles} +Quand un paquet $A$ est envoyé par Alice sur le device tun, il est lu par \texttt{tunmgr/tunmgr.py} avec un header de 4 octets : on obtient le paquet $A'$. +Un paquet UDP de payload $gsmencode(A')$\texttt{+"\~\ "} encodée en UTF-8 est alors envoyé au téléphone d'Alice sur le port 51117\footnote{Des ports moins élevés sont parfois filtrés par le pare-feu des téléphones.}. + Le caractère \texttt{\~\ } sert juste à délimiter la fin du message ; il ne fait pas partie de l'alphabet GSM sans \texttt{ESC}. + + L'application Android d'Alice reçoit le paquet, enlève le \texttt{\~\ } et envoie la payload $gsmencode(A')$ ainsi obtenue par SMS multipart à Bob. + + L'application Android de Bob lit le SMS, et envoie la payload $gsmencode(A')$ encodée en UTF-8 à l'ordinateur de Bob sur le port 51117. + + \texttt{tunmgr.py} reçoit ce paquet de payload $gsmencode(A')$ et écrit sur le device tun de Bob $A'=gsmdecode(gsmencode(A'))$. + Bob reçoit ainsi $A$. + +\subsection*{Threading} +\subsubsection*{tunmgr} +Ce programme est single-threaded ; il utilise \texttt{select (2)} ou un équivalent. + +\subsubsection*{SendSMS} +L'API Android cache certains threads, mais il semble que l'application utilise au moins 2 fils d'exécution parallèle : +\begin{itemize} + \item La GUI + \item Le serveur UDP, qui n'est pas threadé. +\end{itemize} +Threader le serveur n'est pas réellement utile, puisque le goulot d'étranglement en terme de performance est la partie SMS. + +D'autre part, un nouveau thread est dédié à chaque envoi de paquet UDP. + +Enfin, la réception de SMS est certainement dans un thread dédié aussi, mais si c'est le cas, l'API le cache. + +\end{document} + -- GitLab