PyTCP est une pile TCP/IP entièrement fonctionnelle écrite en Python. Il prend en charge le transport basé sur le flux TCP avec une livraison fiable des paquets basée sur un mécanisme de fenêtre glissante et un contrôle de congestion de base. Il prend également en charge les protocoles IPv6/ICMPv6 avec configuration d'adresse SLAAC. Il fonctionne comme un programme d'espace utilisateur attaché à l'interface Linux TAP. Il a mis en œuvre un routage simple et peut envoyer et recevoir du trafic sur un réseau local et Internet.
La version 2.7, contrairement à ses prédécesseurs, contient le code de la pile PyTCP sous la forme d'une bibliothèque afin qu'il puisse être facilement importé et utilisé par du code externe. Cela devrait rendre l'expérience utilisateur plus fluide et éventuellement offrir la possibilité complète de remplacer les appels de pile Linux standard (par exemple, la bibliothèque de sockets) par les appels PyTCP dans n'importe quelle application tierce.
Ce projet a initialement commencé comme un effort purement éducatif visant à améliorer mes compétences Python et à rafraîchir mes connaissances en réseau dans le cadre de la préparation au rôle d'ingénieur réseau chez Facebook. Depuis, c'est devenu plutôt un « projet favori » auquel je consacre une partie de mon temps de manière quelque peu irrégulière. Cependant, quelques mises à jour y sont généralement ajoutées tous les mois ou tous les deux mois.
J'accepte toute contribution et aide de toute personne intéressée par la programmation réseau. Toute contribution est appréciée. N'oubliez pas non plus que certaines fonctionnalités de la pile peuvent n'être implémentées que partiellement (si nécessaire pour le fonctionnement de la pile). Ils peuvent être implémentés de manière sous-optimale ou non 100 % conforme à la RFC (en raison du manque de temps), ou ils peuvent contenir des bugs que je dois encore corriger.
N'hésitez pas à consulter mes deux autres projets connexes :
RusTCP - Tentative de réécriture de certaines fonctionnalités de PyTCP dans Rust et utilisation pour créer un routeur de laboratoire IPv6/SRv6.
SeaTCP - Tentative de création d'une pile à faible latence à l'aide des langages C et Assembly.
Principe de fonctionnement et configuration de test
La pile PyTCP dépend de l'interface Linux TAP. L'interface TAP est une interface virtuelle qui, côté réseau, peut être « branchée » à l'infrastructure de réseau virtuel existante via un pont Linux ou Open vSwitch. Du côté interne, l'interface TAP peut être utilisée comme n'importe quelle autre carte réseau en envoyant et en recevant par programmation des paquets vers/depuis celle-ci.
Si vous souhaitez tester la pile PyTCP sur votre réseau local, je vous suggère de créer la configuration réseau suivante qui vous permettra de connecter à la fois le noyau Linux (essentiellement votre système d'exploitation Linux) et la pile PyTCP à votre réseau local. .
Une fois que l'exemple de programme (client ou service) a démarré la pile, il peut communiquer avec elle via des sockets BSD simplifiés comme l'interface API. Il existe également la possibilité d'envoyer des paquets directement en appelant l'une des méthodes _*_phtx() de la classe PacketHandler .
Cloner PyTCP à partir du référentiel GitHub
Dans la plupart des cas, PyTCP doit être cloné directement à partir du référentiel GitHub, car ce type d'installation fournit un environnement de développement et de test complet.
git clone http://github.com/ccie18643/PyTCP
Après le clonage, nous pouvons exécuter l'un des exemples inclus :
Accédez au répertoire racine de la pile (il s'appelle « PyTCP »).
Exécutez la commande sudo make bridge pour créer le pont 'br0' si nécessaire.
Exécutez la commande sudo make tap pour créer l'interface tap7 et attribuez-la au pont 'br0'.
Exécutez la commande make pour créer l'environnement virtuel approprié pour le développement et les tests.
Courir . venv/bin/activate Commande . venv/bin/activate pour activer l'environnement virtuel.
Exécutez n'importe quel exemple, par exemple, example/run_stack.py .
Appuyez sur Ctrl-C pour l'arrêter.
Pour affiner divers paramètres opérationnels de la pile, veuillez modifier le fichier pytcp/config.py en conséquence.
Installer PyTCP à partir du référentiel PyPi
PyTCP peut également être installé en tant que module standard à partir du référentiel PyPi.
python -m pip install PyTCP
Après l'installation, veuillez vous assurer que l'interface TAP est opérationnelle et ajoutée au pont.
sudo ip tuntap add name tap7 mode tapsudo ip link set dev tap7 upsudo brctl addbr br0sudo brctl addif br0 tap7
La pile PyTCP peut être importée et démarrée à l'aide du code suivant. Il démarre les sous-systèmes de la pile et configure automatiquement les adresses de protocole IPv4 et IPv6 à l'aide respectivement de DHCPv4 et IPv6 SLAAC.
Les sous-systèmes de pile s'exécutent dans leurs propres threads. Après le démarrage, la pile redonne le contrôle au code utilisateur et peut être arrêtée à l'aide de l'appel suivant.
stack . stop ()
Caractéristiques
Déjà mis en œuvre :
Stack - Analyseur de paquets rapide utilisant l'approche « zéro copie ».
Stack - Bibliothèque de manipulation d'adresses MAC - Compatible avec le protocole tampon (Memoryview).
Stack - Bibliothèque de manipulation d'adresses IPv4 - Compatible avec le protocole tampon (Memoryview) et ne dépend pas de la bibliothèque standard Python.
Stack - Bibliothèque de manipulation d'adresses IPv6 - Compatible avec le protocole tampon (Memoryview) et ne dépend pas de la bibliothèque standard Python.
Code - Tests unitaires pour certaines bibliothèques et modules (basés sur le framework Testslide de Facebook)
Protocole Ethernet - Prise en charge des trames standard Ethernet II.
Protocole Ethernet - Unicast, multicast IPv4, multicast IPv6 et adressage de diffusion.
Protocole ARP - Réponses, requêtes, mécanisme de cache ARP.
Protocole ARP - Mécanisme de détection de conflits IP (ACD) de sonde/annonce ARP.
Protocole IPv4 - Routage par défaut, la pile peut communiquer avec les hôtes via Internet à l'aide du protocole IPv4.
Protocole IPv4 - Configuration automatique de l'adresse IPv4 à l'aide du protocole DHCPv4.
Protocole IPv4 - Défragmentation des paquets entrants, mécanisme robuste capable de gérer des fragments de données dans le désordre et qui se chevauchent.
Protocole IPv4 - Fragmentation des paquets sortants.
Protocole IPv4 : options IPv4 acceptées mais non prises en charge.
Protocole IPv4 - Prise en charge de plusieurs adresses IPv4 de pile, chacune d'elles agit comme si elle était attribuée à un VRF distinct
Protocole ICMPv4 : les messages de demande d'écho, de réponse d'écho et de port inaccessible.
Protocole IPv6 - Routage par défaut, la pile peut communiquer avec les hôtes via Internet à l'aide du protocole IPv6.
Protocole IPv6 - Configuration automatique des adresses lien-local à l'aide d'EUI64 et de la détection d'adresses en double.
Protocole IPv6 - Configuration automatique de l'adresse GUA à l'aide de Router Advertisement / EUI64.
Protocole IPv6 - Attribution automatique des adresses de multidiffusion des nœuds sollicités.
Protocole IPv6 - Attribution automatique des adresses MAC de multidiffusion IPv6.
Protocole IPv6 - Défragmentation des paquets entrants, mécanisme robuste capable de gérer des fragments de données dans le désordre et qui se chevauchent.
Protocole IPv6 - Fragmentation des paquets sortants.
Protocole ICMPv6 : les messages de demande d'écho, de réponse d'écho et de port inaccessible.
Protocole ICMPv6 - Découverte de voisin, détection d'adresse en double.
Protocole ICMPv6 - Mécanisme de cache Neighbor Discovery.
Protocole ICMPv6 - Implémentation du protocole Multicast Listener Discovery v2 (MLDv2) (uniquement les messages nécessaires à la pile).
Protocole UDP - Prise en charge complète. La pile peut échanger des données avec d'autres hôtes en utilisant le protocole UDP.
Sockets UDP - Prise en charge complète, API « utilisateur final » de la pile similaire aux sockets Berkeley.
Services UDP - Les services Echo, Discard et Daytime implémentés à des fins de test (dans les « exemples »).
Protocole TCP - Implémentation complète de TCP Finite State Machine. À ce stade, la pile peut échanger des données en masse avec d'autres hôtes via le protocole TCP.
Protocole TCP - Prise en charge des options TCP pour : MSS, WSCALE, SACKPERM, TIMESTAMP.
Protocole TCP - Mécanisme de fenêtre coulissante TCP avec retransmission de données (retransmission rapide et scénarios temporels).
Protocole TCP - Mécanisme d'attente TCP / contrôle de congestion de base.
Protocole TCP - Retransmission des paquets TCP SYN/FIN.
Sockets TCP - Prise en charge complète, API « utilisateur final » de la pile similaire aux sockets Berkeley
A mettre en œuvre :
ICMPv6 - La prise en charge de MLDv2 est désormais un vrai désastre. Il faut le finir.
Tests - Nécessité de refactoriser les tests de flux de paquets (tests/packet_flow_ .py) pour utiliser le même format et le même répertoire que les tests FPA basés sur test_frames.*
Tests - Créez des tests unitaires FPA pour le rapport MLDv2 (len, str, assemble).
IPv4 - Réimplémentez la défragmentation des paquets pour stocker des paquets entiers dans la base de données de flux au lieu de faire des copies de l'en-tête IP et des données.
Stack - Implémente la prise en charge des sockets RAW - à utiliser par exemple, le client ICMP-Echo.
Code - Tests unitaires pour les bibliothèques et modules restants (basés sur la bibliothèque Testslide de Facebook).
Code - Réécrivez la prise en charge du protocole DHCPv4 pour utiliser l'approche FPA/FPP standard au lieu du code existant.
Pile - Revenez à l'implémentation de la console de débogage de la pile afin que certaines informations sur les composants de la pile puissent être affichées à la demande en envoyant des commandes. par exemple, 'show icmpv6 nd cache', 'show ipv6 route', etc... cela devrait également vous permettre d'exécuter des commandes interactives comme ping ou les clients d'écho UDP/TCP de la pile.
Protocole QUIC - Recherche et plan de mise en œuvre. Cela dépend de la capacité à créer un environnement de laboratoire pour cela.
Protocole IPv6 - Repensez la gestion des options RA PI et la configuration automatique du préfixe ND pour utiliser correctement les indicateurs A et L. Des recherches sont également nécessaires lorsqu'un préfixe différent de /64 est annoncé.
Protocole IPv6 : implémentez les en-têtes d'extension restants.
Protocole IPv6 - Valider et éventuellement réimplémenter certains mécanismes/processus IPv6 selon les règles RFC.
Protocole IPv6 : Enquêtez sur l'en-tête des options saut par saut et sa relation avec le message de rapport MLD2, implémentez-le si nécessaire pour que MLD2 fonctionne correctement.
Protocole ICMPv6 - Implémentation du message de redirection ND.
Protocole ICMPv6 - Implémentation complète de Multicast Listener Discovery v2 (MLDv2) <-- cela peut être requis par la pile pour répondre aux requêtes MLD.
Protocole TCP - Gestion correcte des paquets RST dans différents états. Il faut faire des recherches à ce sujet. Un rapport de bug a été soumis à ce sujet.
Protocole TCP - Nécessité de retravailler le mécanisme d'appel système CLOSE afin que l'indicateur FIN puisse être défini sur le dernier paquet de données au lieu d'être transporté dans un paquet séparé.
Protocole TCP - Retransmission du paquet ACK au cas où nous obtiendrions une retransmission FIN dans l'état TIME_WAIT. Il faut enquêter là-dessus.
Protocole TCP - implémente une réponse appropriée aux paquets contenant d'anciens numéros SEQ et/ou ACK. Il faut enquêter là-dessus.
Protocoles IPv6/IPv4 - mécanisme de routage approprié, tables de routage, etc...
Protocoles IPv6/IPv4 - capacité de la pile à agir comme un routeur
Cache ARP - implémenter le FSM approprié
Cache ICMPv6 ND - implémenter le FSM approprié
Journalisation - Remplacez Loguru par un enregistreur local pour améliorer les performances et la flexibilité.
Pile - Convertissez la pile PyTCP en bibliothèque afin que les applications externes puissent l'importer facilement.
Stack - Compteurs de flux de paquets pour aider à collecter des statistiques sur les paquets et permettre le suivi du flux de paquets pour les tests unitaires.
Pile - Implémentez un mécanisme de retour pour le chemin TX afin que les échecs d'envoi de paquets puissent être communiqués aux sockets.
Protocole IPv6 - Possibilité d'acheminer le trafic vers des destinations externes via la passerelle par défaut.
Protocole TCP - Assurez-vous que la communication des événements de la session TCP au socket fonctionne correctement (par exemple, réinitialisation de la connexion par un homologue).
Protocole IPv4 - Une amélioration du mécanisme de défragmentation IP est nécessaire, une gestion des fragments dans le désordre et une purge des fragments orphelins.
Protocole UDP - Nécessite un client UDP Echo et un mécanisme pour signaler la réception d'un message ICMP Port Unreachable vers le socket UDP.
Sockets UDP - Une refonte est nécessaire pour que l'interface « utilisateur final » corresponde plus étroitement aux sockets Berkeley afin que les applications tierces puissent l'utiliser sans portage.
Sockets TCP - Une refonte est nécessaire pour que l'interface « utilisateur final » corresponde plus étroitement aux sockets Berkeley afin que les applications tierces puissent l'utiliser sans portage.
Exemples
Plusieurs paquets ping et deux singes ont été livrés via TCP via le protocole IPv6.
Découverte de voisin IPv6/détection d'adresse en double/configuration automatique d'adresse.
Stack essaie de configurer automatiquement son adresse lien-local. Il le génère sous la forme d'une adresse EUI64. Dans le cadre du processus DAD, il rejoint le groupe de multidiffusion de nœuds sollicités approprié et envoie une sollicitation au voisin pour son adresse générée.
Stack ne reçoit aucune annonce de voisin pour l'adresse qu'il a générée, il l'attribue donc à son interface.
Stack essaie d'attribuer une adresse statique préconfigurée. Dans le cadre du processus DAD, il rejoint le groupe de multidiffusion de nœuds sollicités approprié et envoie une sollicitation au voisin pour l'adresse statique.
Un autre hôte avec la même adresse déjà attribuée répond avec un message d'annonce de voisin. Cela indique à la pile qu'un autre hôte a déjà attribué l'adresse qu'il tente d'attribuer, la pile ne peut donc pas l'utiliser.
Stack envoie un message de sollicitation de routeur pour vérifier s'il existe des préfixes globaux à utiliser.
Le routeur répond avec une annonce de routeur contenant un préfixe supplémentaire.
Stack essaie d'attribuer une adresse générée en fonction du préfixe reçu et de la partie hôte EUI64. Dans le cadre du processus DAD, il rejoint le groupe de multidiffusion de nœuds sollicités approprié et envoie une sollicitation au voisin pour l'adresse statique.
Stack ne reçoit aucune annonce de voisin pour l'adresse qu'il a générée, il l'attribue donc à son interface.
Une fois toutes les adresses attribuées, la pile envoie un autre rapport d'écoute multidiffusion répertoriant toutes les adresses multidiffusion qu'elle souhaite écouter.
TCP Fast Retransmit en action après la perte d'un paquet TX.
Le paquet sortant est « perdu » en raison d’un mécanisme de perte de paquets simulé.
L'homologue remarque l'incohérence dans les numéros SEQ des paquets et envoie une « demande de retransmission rapide ».
Stack reçoit la demande et retransmet le paquet perdu.
File d'attente dans le désordre en action lors d'un événement de perte de paquets RX
Le paquet entrant est « perdu » en raison d’un mécanisme de perte de paquet simulé.
Stack remarque une incohérence dans le numéro SEQ du paquet entrant et envoie une demande de « retransmission rapide ».
Avant que l'homologue ne reçoive la demande, il envoie plusieurs paquets avec un SEQ supérieur à celui attendu par la pile. Stack met en file d’attente tous ces paquets.
Le homologue retransmet le paquet perdu.
Stack reçoit le paquet perdu, extrait tous les paquets stockés dans la file d'attente dans le désordre et les traite.
Stacks envoie un paquet ACK pour accuser réception des derniers paquets extraits de la file d'attente.
TCP Finite State Machine - la pile exécute le service TCP Echo.
Le pair ouvre la connexion.
Le homologue envoie des données.
Stack renvoie les données.
Le pair ferme la connexion.
TCP Finite State Machine - la pile exécute le client TCP Echo.
Stack ouvre la connexion.
Stack envoie des données.
Le homologue renvoie les données.
Stack ferme la connexion.
Pré-analyser les contrôles d’intégrité des paquets en action.
La première capture d'écran montre la pile avec le contrôle d'intégrité désactivé. Un paquet ICMPv6 mal formé peut le faire planter.
La deuxième capture d'écran montre la pile avec le contrôle d'intégrité activé. Un paquet ICMPv6 mal formé est rejeté avant d'être transmis à l'analyseur du protocole ICMPv6.
La troisième capture d'écran montre le paquet mal formé. Le champ du nombre d'enregistrements MA a été défini sur 777 même si le paquet ne contient qu'un seul enregistrement.
Mécanisme de sonde/annonce ARP.
Stack utilise des sondes ARP pour rechercher d'éventuels conflits pour chaque adresse IP configurée.
L'une des adresses IP (192.168.9.102) est déjà utilisée, donc la pile en est informée et l'ignore.
Le reste des adresses IP est gratuit, donc stack les revendique en envoyant une annonce ARP pour chacune d'elles.
Résolution ARP et gestion des paquets ping.
L'hôte 192.168.9.20 tente d'envoyer une requête ping à la pile. Pour pouvoir le faire, il envoie d'abord un paquet de requête ARP pour connaître l'adresse MAC de la pile.
Stack répond en envoyant un paquet de réponse ARP (la pile n'a pas besoin d'envoyer sa demande car elle a déjà noté le MAC de l'hôte à partir de la demande de l'hôte).
L'hôte envoie des paquets ping et la pile y répond.
Fragmentation de la propriété intellectuelle.
L'hôte envoie un datagramme UDP de 4 Ko à l'aide de trois paquets IP fragmentés (trois fragments).
Stack reçoit les paquets et les assemble en un seul morceau, puis les transmet (via le gestionnaire de protocole UDP et le socket UDP) au service UDO Echo.
Le service UDP Echo récupère les données et les remet dans UDP Socket.
Le datagramme UDP est transmis au gestionnaire de protocole IP, qui crée un paquet IP et, après avoir vérifié qu'il dépasse le lien, MTU le fragmente en trois paquets IP distincts.
Les paquets IP sont encapsulés dans des trames Ethernet et placés sur un anneau TX.