Question

I am using scapy to send data over ICMP. I need to send image and other files over ICMP using scapy. I am able to send simple strings in ICMP. How could I send image and other files ?

Was it helpful?

Solution

I don't know scapy, so my answer will be algorithm and straight python.

In order to send image files, you'll need to break up the image into chunks, keeping in mind you'll need to have some mechanism to track the order of what chunk was sent.

To start, read the image file as a binary file. Then, decide by what amount the binary data should be divided. Given an MTU of 1500, I would suggest 1500 - IP header - ICMP header, or 1472, to avoid IP fragmentation. Further, I would suggest using either a 16-bit or 32-bit number to keep track of order of sent chunks, and subtract that from 1500 as well. Example: 1500 - IP - ICMP - 4 = 1468.

Using the above example, I would break the image like this:

image = file("/path/to/file","rb").read()
chunk = []
interval = 1500 - 20 - 8 - 4
for n in range(0, len(image), interval):
    chunk.append(image[n:n + interval])

With the image now in chunks, I'd add the tracking bytes to each chunk. It can either be prefixed or suffixed, your choice. I chose prefix:

for n in range(len(chunk)):
    chunk[n] = struct.pack(">I", n) + chunk[n]

Now that we have the chunks marked for tracking, we need to build the ICMP header, with each chunk appended. But which type/code do we use? In this use case, we'll build an ECHO REQUEST packet (other types such as 0 or 3 are unsolicited and may cause the receiving kernel not to forward the packet to your ICMP socket handlers). Also, an important thing to note: you'll need checksum code to calculate the ICMP checksum:

icmp_chunks = []
for img_piece in chunk:
    icmp = struct.pack(">BBHHH%ds" % len(img_piece), 8, 0, 0, 0, 0, img_piece)
    icmp = struct.pack(">BBHHH%ds" % len(img_piece), 8, 0, cksum(icmp), 0, 0, img_piece)
    icmp_chunks.append(icmp)

To reiterate, note the cksum(icmp) parameter in the second icmp header packet. This checksum is needed, otherwise the receiving kernel might not forward the packet to any of the ICMP socket handlers if incorrect. Also note the 4th and 5th parameters being zero: these are the "identifier" and "sequence number" fields. Typically these are used to store the process ID and an incremental number (respectively). Socket objects do this as a way to keep track which socket sent what and by how many. In this use case, since we are sending an unsolicited ECHO REPLY, you can code your receiving end to look for type 0 and further look for your tracking bytes in the sent chunk. Thus for now I leave identifier and sequence fields as zero. If you're wondering why I constructed two icmp headers, it's because the kernel bases a correctly computed checksum on the initial checksum field being zero.

At this point, we now have the icmp headers and are ready to send:

for i in icmp_chunks:
    [socket].sendto(i, ("hostname-or-ip", 0))

I presume the [socket] object to be either scapy or your own socket object.

On the receiving end, you read back the data and look for your image payload, reconstructing each payload back into its original order. However, you'd still need some way to figure out if all the received chunks are indeed all of them. Maybe think about how to structure an initial byte sequence indicating total expected size, before sending image chunks.

Hope this helps.

UPDATE: I tested sending unsolicited type 0 packets to Ubuntu Linux and noted the receiving kernel did not pass the packet to my handler. So I've amended my explanation to account for this.

OTHER TIPS

I think you've discovered why the inventors of the Internet developed TCP. There's a limit on the payload size in an ICMP packet. Most networks have a 1500-byte total limit on the size of Ethernet packets, so with a 20-byte IP header and an 8-byte ICMP header, your maximum payload size in a single packet will be 1472 octets. That's not enough for many image files.

You need a way of breaking up your image data stream into chunks of about that size, sending them in multiple ICMP packets, and reassembling them into a data stream on the receiver.

Considering that there's no guarantee ICMP packets are received in order, and indeed no guarantee that they'll all be received in order, you will need to put some kind of sequence number into the the individual packets, so you can reassembly them in order. You also may need some timeout logic so your receiving machine can figure out that a packet was lost and so the image will never be completed.

RTP and TCP are two protocols layered on IP that do that. They are documented in precise detail, and you can learn from those documents how to do what you're trying to do and some of the pitfalls and performance implications.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top