web-dev-qa-db-fra.com

Comment utiliser Raw Socket en Python?

Je suis en train d'écrire une application pour tester un pilote réseau pour la gestion de données corrompues. Et j'ai pensé envoyer ces données en utilisant une socket, donc elles ne seront pas corrigées par la pile TCP-IP de la machine d'envoi.

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

J'ai un peu cherché sur le Web des explications et des exemples d'utilisation de sockets bruts en python, mais je n'ai rien trouvé de vraiment instructif. Juste un très vieux exemple de code qui illustre l’idée, mais ne fonctionne en aucun cas.

D'après ce que j'ai compris, l'utilisation de Raw Socket en Python est presque identique en sémantique au socket brut d'UNIX, mais sans les structs qui définissent la structure des paquets.

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

32
Avihu Turzion

Vous le faites comme ça:

D'abord, vous désactivez le contrôle automatique de votre carte réseau:

Sudo ethtool -K eth1 tx off

Et envoyez ensuite votre cadre louche de python 2 (vous devrez vous convertir vous-même en Python 3):

#!/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)

Terminé.

42
brice

Les appels système Sockets (ou Winsocks, sous Windows) sont déjà intégrés au module standard socket: intro , reference .

Je n'ai jamais utilisé de sockets bruts mais il semble qu'ils puissent être utilisés avec ce module:

Le dernier exemple montre comment écrire un renifleur de réseau très simple avec raw sockets 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)
10
Bastien Léonard

Est-ce que this est l'ancien code que vous avez mentionné? Cela me semble judicieux, mais je ne l’ai pas testé moi-même (ni utilisé beaucoup de douilles brutes). Cet exemple de la documentation montre comment utiliser des sockets bruts pour détecter les paquets, et il semble assez similaire.

2
unwind

Finalement, la meilleure solution pour ce cas était d'écrire le tout en C, parce que ce n'était pas une grosse application. Il aurait donc été plus pénalisant d'écrire une chose aussi petite dans plus d'une langue.

Après avoir beaucoup joué avec les sockets C et Python RAW, j'ai finalement préféré les sockets C RAW. Les sockets RAW nécessitent des modifications au niveau des bits de moins de 8 groupes pour écrire les en-têtes de paquets. Parfois, n'écrivant que 4 bits ou moins. python ne définit aucune assistance à cet égard, alors que Linux C dispose d’une API complète à cet effet.

Mais je crois vraiment que si seulement cette petite initialisation d’en-tête était gérée de manière pratique en python, je n’aurais jamais utilisé C ici.

2
Avihu Turzion
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                        ]]]....
1
gteng

La classe socket devrait aider. Sinon, vous devrez écrire un module Python en C ou simplement utiliser C. Voir http://mail.python.org/pipermail/python-list/2001-April/077454.html .

Google a trouvé cela de base.

J'ai effectivement essayé l'exemple de code que "décompresser" a souligné. AF_PACKET a fonctionné pour moi en python 2.7.4

0

Vous pouvez utiliser cette bibliothèque Python: rawsocketpy il permet d’utiliser des sockets bruts sur la couche 2 => Aucun en-tête IP/TCP/UDP.

#!/usr/bin/env python
from rawsocketpy import RawSocket

sock = RawSocket("wlp2s0", 0xEEFA)
sock.send("some data")
sock.send("personal data", dest="\xAA\xBB\xCC\xDD\xEE\xFF")

ou la forme du serveur:

#!/usr/bin/env python
from rawsocketpy import RawRequestHandler, RawAsyncServerCallback
import time

def callback(handler, server):
    print("Testing")
    handler.setup()
    handler.handle()
    handler.finish()

class LongTaskTest(RawRequestHandler):
    def handle(self):
        time.sleep(1)
        print(self.packet)

    def finish(self):
        print("End")

    def setup(self):
        print("Begin") 

def main():
    rs = RawAsyncServerCallback("wlp2s0", 0xEEFA, LongTaskTest, callback)
    rs.spin()

if __== '__main__':
    main()
0
Alexis Paques