48 votes

Comment utiliser Raw Socket en Python ?

J'écris une application pour tester un pilote de réseau pour la gestion des données corrompues. Et j'ai pensé à envoyer ces données en utilisant une socket brute, de sorte qu'elles ne seront pas corrigées par la pile TCP-IP de la machine émettrice.

J'écris cette application uniquement sous Linux. J'ai des exemples de code pour l'utilisation de sockets bruts dans les appels système, mais j'aimerais vraiment garder mon test aussi dynamique que possible, et l'écrire en grande partie, sinon en totalité, en Python.

J'ai cherché sur le web des explications et des exemples d'utilisation de raw sockets en python, mais je n'ai rien trouvé de vraiment éclairant. Juste un très vieil exemple de code qui démontre l'idée, mais qui ne fonctionne en aucun cas.

D'après ce que j'ai compris, l'utilisation de Raw Socket en Python est presque identique en termes de sémantique à celle de raw socket d'UNIX, mais sans l'option struct qui définissent la structure des paquets.

Je me demandais s'il ne serait pas préférable de ne pas écrire la partie "raw socket" du test en Python, mais en C avec des appels système, et de l'appeler à partir du code Python principal ?

61voto

brice Points 5740

Voici comment procéder :

Vous devez d'abord désactiver la somme de contrôle automatique de votre carte réseau :

sudo ethtool -K eth1 tx off

Puis envoyez votre cadre douteux à partir de Python 2 (vous devrez le convertir à Python 3 vous-même) :

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))

# We're putting together an ethernet frame here, 
# but you could have anything you want instead
# Have a look at the 'struct' module for more 
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"

s.send(dst_addr+src_addr+ethertype+payload+checksum)

Fait.

2 votes

Je pense que c'est faux ; que l'interface AF_PACKET n'expose pas la partie checksum à l'espace utilisateur, de sorte qu'elle est automatiquement générée et vérifiée par le pilote/matériel, et qu'il n'y a rien que nous puissions faire à ce sujet. Wireshark indique que le "checksum" que j'ai envoyé fait partie des "données".

1 votes

Cela dépend de votre matériel/pilote. Certains pilotes ignorent la somme de contrôle si elle est déjà présente, tandis que d'autres la considèrent comme faisant partie des données. C'est en fait une tâche difficile à réaliser de manière fiable avec différents pilotes. Ce problème existera également dans une implémentation en C. Ne pas mettre la somme de contrôle semble toujours aboutir au comportement correct (c'est-à-dire que la somme de contrôle du paquet est calculée par le pilote/matériel).

2 votes

Cela n'a pas beaucoup de sens. Dans les pilotes qui pourraient gérer un checksum explicite, comment est-il supposé savoir que ces 4 octets nuls à la fin (ou n'importe quels 4 octets, d'ailleurs) doivent être utilisés comme checksum ? Même si les octets correspondent à la somme de contrôle correcte, cela impliquerait que le pilote doit la calculer, mais ne l'ajoute à la trame que si les 4 derniers octets ne correspondent pas déjà à ce qu'il a calculé : un comportement hautement improbable et propice aux erreurs.

11voto

Bastien Léonard Points 18404

Les appels système des sockets (ou Winsocks, sous Windows) sont déjà intégrés dans le module standard socket : intro , référence .

Je n'ai jamais utilisé de prises brutes mais il semble qu'elles puissent être utilisées avec ce module :

Le dernier exemple montre renifleur de réseau très simple avec des sous Windows. L'exemple nécessite des privilèges d'administrateur pour modifier l'interface :

import socket

# the public network interface
HOST = socket.gethostbyname(socket.gethostname())

# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))

# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# receive a package
print s.recvfrom(65565)

# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

0 votes

C'est exactement l'exemple de code auquel unwind renvoie dans sa réponse. C'est un bon exemple, mais j'ai besoin de savoir comment j'envoie des données brutes. Dans toutes mes tentatives basées sur ces exemples, les données brutes que j'ai construites n'ont tout simplement pas été envoyées. Même lorsque j'ai essayé d'envoyer des paquets échantillonnés bruts, cela a fonctionné au lieu de construire mes propres paquets.

0 votes

Avec les sockets brutes, vous devriez également construire vous-même l'en-tête IP et TCP ou UDP. Peut-être essayez-vous simplement d'envoyer les données de votre application en espérant que le système d'exploitation fera le reste pour vous. Une fois que vous utilisez les sockets brutes, les jeux sont faits. Tout doit être fait par vous.

0 votes

Et lorsque j'ai parlé de TCP ou d'UDP, je voulais dire n'importe quel protocole au-dessus de l'IP que vous voulez construire vous-même.

2voto

unwind Points 181987

Es este l'ancien code que vous avez mentionné avoir trouvé ? Il me semble judicieux, mais je ne l'ai pas testé moi-même (ou je n'ai pas beaucoup utilisé les raw sockets). Cet exemple de la documentation montre comment utiliser des sockets brutes pour renifler des paquets, et cela semble assez similaire.

1 votes

C'est exactement l'exemple de code dont je parlais ! . Son problème est qu'il utilise des initialisations de la socket, et que les protocoles sont complètement dépréciés. De plus, je pense qu'il s'agit d'une sorte de mélange des côtés serveur et client dans une application conceptuelle.

0 votes

Il semble que la famille d'adresses AF_PACKET n'existe plus. Le deuxième exemple (reproduit dans la réponse de Bastien) utilise AF_INET, je suppose que c'est logique. Si tout ce que vous voulez tester est l'envoi de données (pour vérifier par exemple une corruption basée sur un motif ou autre), l'adressage ne devrait pas avoir d'importance. Je pense que le premier exemple "mélange les concepts" en faisant un envoi totalement synchrone suivi d'un appel de réception. Ce n'est pas très courant ou agréable, mais cela devrait être légal.

1voto

Avihu Turzion Points 763

Finalement la meilleure solution pour ce cas était d'écrire la totalité de la chose en C, car ce n'est pas une grosse application, de sorte qu'il aurait encourus plus de peine à écrire une si petite chose en plus que 1 langue.

Après beaucoup de jongle avec à la fois le C et le python RAW sockets, j'ai finalement préféré le C les sockets RAW. Les sockets RAW nécessitent peu au niveau des modifications de moins de 8 groupes de bits pour écrire les en-têtes des paquets. Parfois, l'écriture n'a que 4 bits ou moins. python définit pas d'aide de la ce, alors que Linux C est une API complète pour cette.

Mais je crois vraiment que si seulement ce petit bout d'en-tête de l'initialisation a été manipulé facilement en python, je n'ai jamais utilisé C ici.

10 votes

Cela ne répond pas à la question.

0 votes

Je suis d'accord qu'en tant que point de vue global sur la question de savoir si les sockets RAW Python sont préférables aux sockets RAW C, cela ne répond pas à la question. Mais en fin de compte, cela se rapportait au problème que je traitais à l'époque, et cela a résolu le problème, et donc c'était la bonne réponse en ce qui me concerne.

0 votes

Non, jetez un coup d'œil à cette question

1voto

gteng Points 11
s = socket(AF_PACKET, SOCK_RAW)
s = socket(PF_PACKET, SOCK_RAW)

résultat:

[root@localhost python]# tcpdump -i eth0

capture size 96 bytes
11:01:46.850438 

01:02:03:04:05:06 (oui Unknown) > 01:02:03:04:05:06 (oui Unknown), ethertype Unknown (0x0801), length 85:

        0x0000:  5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b  [[[[[[[[[[[[[[[[
        0x0010:  5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5041  [[[[[[[[[[[[[[PA
        0x0020:  594c 4f41 445d 5d5d 5d5d 5d5d 5d5d 5d5d  YLOAD]]]]]]]]]]]
        0x0030:  5d5d 5d5d 5d5d 5d5d 5d5d 5d5d 5d5d 5d5d  ]]]]]]]]]]]]]]]]
        0x0040:  5d5d 5d00 0000 00                        ]]]....

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X