Zócalos crudos y sendto en python.

Estoy trabajando en la integración de scapy con twisted, pero me encontré con un 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 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 me sale el siguiente error:

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

En caso de que no tengas scapy activado, no quieres usarlo, este es el paquete base64 codificado:

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

Lo muy extraño es que un paquete que es casi idéntico parece que se envía 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 Wirehark solo difieren en la parte TCP.

He realizado muchos experimentos diferentes y al final pude establecer ciertas opciones TCP específicas para que se enviara el paquete, pero no tiene sentido que un paquete así no funcione.

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

EDITAR:

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 añades los ceros no funciona.

Acabé decidiendo que Raw Sockets es solo un error para poder usarlo. Especialmente debido a que este software debe ser multiplataforma, las características de OSX pueden no ser aplicables a otros sistemas operativos.

Por el momento, simplemente envolví los “sockets” que proporciona scapy. En el futuro escribiré algo que solo depende de libdnet (ya que eso es lo que scapy hace para escribir marcos en bruto).

Puedes encontrar esto implementado aquí:

https://github.com/hellais/txscapy

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

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

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

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

 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 en Mac OS X con sockets crudos?

Otro tema relacionado. impacket módulo de impacket Python tiene ping.py secuencia de comandos ping.py 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  s.sendto(ip.get_packet(), (dst, 0)) socket.error: [Errno 22] Invalid argument 

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

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

De 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 solo tenía 40 bytes, por lo que estaría por debajo del límite en cualquier caso. Podría 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 perfecto sentido; el sistema operativo está rechazando el paquete porque no le está dando suficientes datos para construir un paquete válido.