Как использовать Raw-сокет в Python?

Я пишу приложение для тестирования сетевого драйвера для обработки поврежденных данных. И я подумал о отправке этих данных с помощью raw-сокета, поэтому он не будет исправлен стеком TCP-IP отправляющей машины.

Я пишу это приложение исключительно на Linux. У меня есть примеры кода использования необработанных сокетов в системных вызовах, но я бы очень хотел сохранить свой тест как можно более динамичным и написать большинство, если не все, на Python.

Я немного погуглил в интернете для объяснения и примеры использования необработанных сокетов в python, но не нашли ничего действительно поучительного. Просто очень старый пример кода, который демонстрирует идею, но никоим образом не работает.

из того, что я собрал, использование Raw Socket в Python почти идентично семантике raw socket UNIX, но без structs, которые определяют структуру пакетов.

мне было интересно, было бы даже лучше не писать сырую часть сокета теста в Python, но в C с системными вызовами, и вызвать его из основного кода Python?

7 ответов


вы делаете это так:

сначала вы отключаете автоматическую контрольную сумму вашей сетевой карты:

sudo ethtool -K eth1 tx off

а затем отправьте свой изворотливый кадр с python:

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

сделано.


системные вызовы сокетов (или Winsocks, в Windows) уже завернуты в стандартный модуль socket: интро, ссылка.

Я никогда не использовал необработанные сокеты, но похоже, что их можно использовать с этим модулем:

в последнем примере показано, как написать очень простой сетевой сниффер с сырьем розетки на окнах. Образец требуются права администратора для изменить интерфейс:

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)

Is этой старый код, который вы упомянули найти? Это выглядит разумным для меня, но я не тестировал его сам (или использовал сырые сокеты много). из документации показано, как использовать необработанные сокеты для обнюхивания пакетов, и это выглядит достаточно похоже.


В конце концов, лучшим решением для этого случая было написать все это на C, потому что это не большое приложение, поэтому оно понесло бы большее наказание, чтобы написать такую маленькую вещь на более чем 1 языке.

после многих игр с сокетами C и python RAW я в конечном итоге предпочел сокеты C RAW. Необработанные сокеты требуют изменений на уровне битов менее 8-битных групп для записи заголовков пакетов. Иногда пишу только 4 бита или меньше. python определяет no помощь в этом, тогда как Linux C имеет полный API для этого.

но я определенно считаю, что если бы только эта небольшая инициализация заголовка была обработана удобно в python, я бы никогда не использовал C здесь.


s = socket(AF_PACKET, SOCK_RAW)
s = socket(PF_PACKET, SOCK_RAW)

результате:

[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                        ]]]....

класс сокета должен помочь. Если нет, вам нужно будет либо написать модуль Python в C, либо просто использовать C. см.http://mail.python.org/pipermail/python-list/2001-April/077454.html.

Basic Googling нашел это.

Я действительно попробовал пример кода, который" размотать " указал. AF_PACKET работал для меня в python 2.7.4


Вы можете использовать эту библиотеку Python: rawsocketpy Он позволяет использовать необработанные сокеты на уровне 2 = > нет заголовков 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")

или сервер, вида:

#!/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 __name__ == '__main__':
    main()