Необработанные сокеты и sendto в python

Я работаю над интеграцией Scapy с Twisted, но столкнулся с этой очень странной ошибкой в ​​​​OSX, которую, похоже, не могу понять.

По сути, я не могу отправить действительный пакет TCP (включая заголовки IP) через необработанный сокет. Вот что я делаю:

import socket
from scapy.all import IP, TCP
pkt = IP(src='0.0.0.0', dst='127.0.0.1')/TCP()
spkt1 = str(pkt)
outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
outs.sendto(spkt1, ('127.0.0.1', 0))

Когда я запускаю это, я получаю следующую ошибку:

outs.sendto(spkt1, ('127.0.0.1', 0)) socket.error: [Errno 22] Invalid argument

Если у вас нет scapy, вы не хотите его использовать, это пакет, закодированный base64:

import base64
spkt1 = base64.b64decode("RQAAKAABAABABvvOAAAAAH8AAAEAFABQAAAAAAAAAABQAiAAEH4AAA==")

Очень странно то, что почти идентичный пакет отправляется правильно:

spkt2 = base64.b64decode("RQBAAAWwAAACBgAAAAAAAH8AAAEAyAOEAAAAAAAAAACwAgDIAHsAAAIEBbQBAwMBAQEICk3PUjMAAAAABAIAAA==")

Вот как выглядят два пакета:

SPKT1
0000   45 00 00 28 00 01 00 00  40 06 FB CE 00 00 00 00   E..(....@.......
0010   7F 00 00 01 00 14 00 50  00 00 00 00 00 00 00 00   .......P........
0020   50 02 20 00 10 7E 00 00                            P. ..~..
SPKT2
0000   45 00 40 00 05 B0 00 00  02 06 00 00 00 00 00 00   E.@.............
0010   7F 00 00 01 00 C8 03 84  00 00 00 00 00 00 00 00   ................
0020   B0 02 00 C8 00 7B 00 00  02 04 05 B4 01 03 03 01   .....{..........
0030   01 01 08 0A 4D CF 52 33  00 00 00 00 04 02 00 00   ....M.R3........

Проверяя их в wireshark, они отличаются только частью TCP.

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

Кто-нибудь знает, почему это может происходить?

РЕДАКТИРОВАТЬ:

Этот пакет работает:

pkt = IP(len=16384, src='0.0.0.0', dst='127.0.0.1',
     id=RandShort(), ttl=2)/TCP(sport=255,
      dport=900, flags="S", window=200,
      options=[('MSS', 1460), ('WScale', 2)])
spkt = bytes(pkt)
spkt += '\x00'*20

Если вы не добавите нули, это не сработает.


person Arturo Filastò - hellais    schedule 12.06.2012    source источник
comment
Не могли бы вы исправить import в своем первом фрагменте кода? (также забавный факт, когда я читал запрос на ваш вопрос: вы можете использовать "…".decode("base64") и "…".encode("base64") вместо import base64). Хорошо, извините, с этим помочь не могу. Но у вас есть мой голос.   -  person David Wolever    schedule 13.06.2012
comment
FWIW, я получаю ту же ошибку в вашем коде.   -  person Yuval Adam    schedule 13.06.2012


Ответы (6)


В конце концов я решил, что Raw Sockets просто недоработаны, чтобы их можно было использовать. Тем более, что это программное обеспечение должно быть кроссплатформенным, особенности OSX могут быть неприменимы к другим ОС.

Пока что я просто обернул «сокеты», предоставленные scapy. В будущем я напишу что-то, что зависит только от libdnet (как это делает scapy для записи необработанных кадров).

Вы можете найти это реализованным здесь:

https://github.com/hellais/txscapy

person Arturo Filastò - hellais    schedule 13.06.2012

Заголовок IP должен быть кратен 32 битам, чтобы быть действительным. И есть также область заполнения на конце.

Таким образом, в зависимости от параметров IP, установленных в заголовке, — «который занимает переменное количество битов», — необходимо подсчитывать биты и заполнение.

Похоже, разные ОС по-разному справляются с этим. Можно подумать, что умная ОС сделает это дополнение за вас.

person cox    schedule 16.09.2015

0.0.0.0 не выглядит для меня действительным исходным IP-адресом. Имеет ли значение изменение этого значения на любое другое значение?

person Jean-Paul Calderone    schedule 12.06.2012
comment
Похоже, без разницы. - person Yuval Adam; 13.06.2012
comment
нет, это не имеет никакого значения. Довольно любопытно, например, что этот пакет работает: pkt = IP(len=16384, src='0.0.0.0', dst='127.0.0.1', id=RandShort(), ttl=2)/TCP( sport=255, dport=900, flags=S, window=200, options=[('MSS', 1460), ('WScale', 2)]) spkt = bytes(pkt) spkt += '\x00'* 20. Если вы не добавите 20 нулей, он потерпит неудачу, как и предыдущий. (добавление пакета в правильно отформатированное сообщение) - person Arturo Filastò - hellais; 13.06.2012
comment
Странный. Не знаю. Моя следующая мысль состоит в том, что ядро ​​OS X полно непонятных ошибок, подобных этой. ;) - person Jean-Paul Calderone; 13.06.2012
comment
Да, это, вероятно, ошибка OSX. Я просто собираюсь отказаться от использования необработанных сокетов, поскольку они ужасны для кроссплатформенной поддержки, и просто использую libdnet. - person Arturo Filastò - hellais; 13.06.2012

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> from scapy.all import IP, TCP
WARNING: No route found for IPv6 destination :: (no default route?)
>>> pkt = IP(src='0.0.0.0', dst='127.0.0.1')/TCP()
>>> spkt1 = str(pkt)
>>> 
>>> outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
>>> outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
>>> outs.sendto(spkt1, ('127.0.0.1', 0))
40

Кажется, у меня нет ошибок при записи этого конкретного набора пакетов - я использую x86_64 с ядром 2.6.38-* Gnu/Linux.

Возможно, ваша проблема связана с некоторым повреждением мозга Mac OS X с сырыми сокетами?

person cypherpunks    schedule 12.06.2012

Еще одна связанная проблема. Модуль Python impacket имеет ping.py скрипт для проверки связи с хостами. В Mac OS X Lion я получил ошибку при использовании этого скрипта:

Traceback (most recent call last):
  File "/private/var/www/env/bin/ping.py", line 73, in <module>
    s.sendto(ip.get_packet(), (dst, 0))
socket.error: [Errno 22] Invalid argument

Но в Ubuntu все работает нормально, и я получаю ответы от хостов.

person Alex Emelin    schedule 21.08.2012

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

Из википедии:

Минимальная полезная нагрузка составляет 42 октета при наличии тега 802.1Q и 46 октетов при его отсутствии.

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

Если это так, поведение имеет смысл; ОС отклоняет пакет, потому что вы не предоставляете ей достаточно данных для создания действительного пакета.

person Aleksi Torhamo    schedule 21.08.2012