Sockets sin formato y sendto en python

Estoy trabajando en la integración de scapy con twisted, pero me encontré con este error muy extraño en OSX que parece que no puedo entender.

Básicamente, no puedo enviar un paquete TCP válido (incluidos los encabezados de IP) a través de un socket sin formato. Esto es lo que estoy haciendo:

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))

Cuando ejecuto esto, aparece el siguiente error:

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

En caso de que no tenga Scapy activado, no quiera usarlo, este es el paquete codificado en base64:

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

Lo muy extraño es que un paquete que es casi idéntico parece enviarse correctamente:

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

Así es como se ven los dos paquetes:

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

Al revisarlos en wireshark solo se diferencian en la parte TCP.

Hice muchos experimentos diferentes y al final pude configurar ciertas opciones específicas de TCP para enviar el paquete, pero no tiene sentido que dicho paquete no funcione.

¿Alguien tiene una idea de por qué esto puede estar sucediendo?

EDIT:

Este paquete parece funcionar:

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

Si no agrega los ceros, no funciona.

preguntado el 12 de junio de 12 a las 21:06

¿Podrías arreglar el import en su primer fragmento de código? (también, un hecho divertido mientras leo la solicitud de su pregunta: puede usar "…".decode("base64") y "…".encode("base64") en lugar de import base64). Ok, lo siento, no puedo ayudar con esto. Pero tienes mi voto a favor. -

FWIW, recibo el mismo error en tu código. -

6 Respuestas

Terminé decidiendo que los Raw Sockets están demasiado molestos para ser utilizables. Especialmente dado que este software debe ser multiplataforma, las peculiaridades de OSX pueden no ser aplicables a otros sistemas operativos.

Por el momento, simplemente envolví los "sockets" proporcionados por scapy. En el futuro, escribiré algo que solo dependa de libdnet (ya que eso es lo que hace scapy para escribir marcos sin formato).

Puede encontrar esto implementado aquí:

https://github.com/hellais/txscapy

Respondido el 13 de junio de 12 a las 15:06

Se requiere que un encabezado IP tenga un múltiplo de 32 bits para ser válido. Y también hay un área de relleno en el extremo.

Entonces, dependiendo de las opciones de IP establecidas en el encabezado, que ocupa una cantidad variable de bits, es necesario contar los bits. y relleno.

Parece que los diferentes sistemas operativos manejan esto de manera diferente. Uno podría considerar que un sistema operativo inteligente haría este relleno por usted.

Respondido el 17 de Septiembre de 15 a las 00:09

0.0.0.0 no me parece una dirección IP de origen válida. ¿Cambiar esto a cualquier otro valor hace alguna diferencia?

Respondido el 12 de junio de 12 a las 22:06

Parece que no hace ninguna diferencia. - adán yuval

no, no hace ninguna diferencia. Curiosamente, este paquete, por ejemplo, parece funcionar: 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 Si no sumas los 20 ceros falla como el otro. (agregando el paquete a la publicación correctamente formateado) - Arturo Filasto - hellais

Extraño. No sé. Mi siguiente pensamiento es que el kernel de OS X está lleno de errores oscuros como este. ;) - Jean-Paul Calderone

Sí, esto probablemente sea un error de OSX. Voy a dejar de usar sockets sin procesar, ya que son terribles para el soporte multiplataforma y solo usaré libdnet. - Arturo Filasto - hellais

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

Parece que no tengo ningún error al escribir ese conjunto específico de paquetes: estoy usando x86_64 con un kernel 2.6.38-* Gnu/Linux.

¿Quizás su problema está relacionado con algún daño cerebral de Mac OS X con sockets sin procesar?

Respondido el 12 de junio de 12 a las 22:06

Otro tema relacionado. Pitón impacket el módulo tiene ping.py secuencia de comandos para hacer ping a los hosts. En Mac OS X Lion, recibí un error al usar este script:

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

Pero en Ubuntu todo funciona bien y recibo respuestas de los anfitriones.

Respondido 21 ago 12, 21:08

No tengo pruebas sólidas, pero creo que esto podría estar relacionado con el tamaño mínimo de la carga útil de Ethernet.

Desde Wikipedia:

La carga útil mínima es de 42 octetos cuando la etiqueta 802.1Q está presente y de 46 octetos cuando está ausente.

Su primer paquete de ejemplo tenía solo 40 bytes, por lo que estaría por debajo del límite en cualquier caso. Puede experimentar cambiando el relleno de 20 bytes a esos valores para verificar que deja de funcionar en uno de los límites.

Si es así, el comportamiento tiene mucho sentido; el sistema operativo está rechazando el paquete porque no le está dando suficientes datos para construir un paquete válido.

Respondido 21 ago 12, 23:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.