For the sake of learning I am currently trying to create a simple python porgram to send a ICMP ping packet to some device. To get started I looked through the source code of the python module Pyping: https://github.com/Akhavi/pyping/blob/master/pyping/core.py
I am trying to understand all that is going on when sending and constructing the packet however i have managed to get stuck on one part of the code and can't seem to figure out exactly what its fucntion and use is. I have been looking into ICMP packets and i understand that they contain Type code checksum and data now the piece of code that puzzles me is:
self.own_id = os.getpid() & 0xFFFF
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, checksum, self.own_id, self.seq_number
)
padBytes = []
startVal = 0x42
for i in range(startVal, startVal + (self.packet_size)):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
data = bytes(padBytes)
My questions would be:
I am new to networking and any help would be really appreciated.
ICMP works like the communication between the carpenter and the store. It relays messages from the receiver to the sender about the data that was supposed to arrive. If the data either does not reach the receiver or is received in the wrong order, ICMP lets the sender know so the data can be resent.
All ICMP packets have an 8-byte header and variable-sized data section. The first 4 bytes of the header have fixed format, while the last 4 bytes depend on the type/code of that ICMP packet.
ICMP creates and sends messages to the source IP address indicating that a gateway to the internet, such as a router, service or host, cannot be reached for packet delivery. Any IP network device has the capability to send, receive or process ICMP messages.
If the type field is 8, then the packet is an ICMP echo (ping) request, while if the type field is 0, then the packet is an ICMP echo (ping) reply. That type of field is a one-byte field at the very beginning of the ICMP protocol header. We might consider the ICMP Type field the packet’s classification and the Code field its subclass.
ICMP will take the source IP from the discarded packet and informs to the source by sending a parameter problem message.
The ICMP protocol has a field called type, which indicates what type the ICMP packet is. If the type field is 8, then the packet is an ICMP echo (ping) request, while if the type field is 0, then the packet is an ICMP echo (ping) reply.
We might consider the ICMP Type field the packet’s classification and the Code field its subclass. For example, a Type field value of 3 indicates “destination unreachable.”
The ICMP Echo Request PDU looks like this:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type(8) | Code(0) | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
And here's a description of the various fields from the wiki link above:
The Identifier and Sequence Number can be used by the client to match the reply with the request that caused the reply.
In practice, most Linux systems use a unique identifier for every ping process, and sequence number is an increasing number within that process. Windows uses a fixed identifier, which varies between Windows versions, and a sequence number that is only reset at boot time.
pyping
CodeLook at the full function body of send_one_ping
, which is where your code is from. I will annotate it with some information:
def send_one_ping(self, current_socket):
"""
Send one ICMP ECHO_REQUEST
"""
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
# Annotation: the Type is 8 bits, the code is 8 bits, the
# header checksum is 16 bits
# Additional Header Information is 32-bits (identifier and sequence number)
# After that is Payload, which is of arbitrary length.
So this line
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, checksum, self.own_id, self.seq_number
)
This line creates the packet header using struct
with layout !BBHHH
, which means:
B
- Unsigned Char (8 bits)B
- Unsigned Char (8 bits)H
- Unsigned Short (16 bits)H
- Unsigned Short (16 bits)H
- Unsigned Short (16 bits)And so the header will look like this:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ICMP_ECHO | 0 | checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| self.own_id | self.seq_number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Note this:
self.own_id
sets the identifier of the application sending this data. For this code, it just uses the program's Program Identifier number.self.seq_number
sets the sequence number. This helps you identify which ICMP request packet this is if you were to send multiple in a row. It would help you do things like calculate ICMP packet loss.Both the Identifier and Sequence Number fields combined can be used by a client to match up echo replies with echo requests.
Now let's move on to the Payload portion. Payloads are of arbitrary length, but the Ping
class this code is taken from defaults to a total packet payload size of 55 bytes.
So the portion below just creates a bunch of arbitrary bytes to stuff into the payload section.
padBytes = []
startVal = 0x42
# Annotation: 0x42 = 66 decimal
# This loop would go from [66, 66 + packet_size],
# which in default pyping means [66, 121)
for i in range(startVal, startVal + (self.packet_size)):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
data = bytes(padBytes)
At the end of it, byte(padBytes)
actually looks like this:
>> bytes(padBytes)
b'BCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwx'
0x42
was chosen?As far as I know, 0x42
has no actual significance as a Payload identifier, so this seems rather arbitrary. The payload here is actually pretty meaningless. As you can see from the Payload Generation section, it just generates a contiguous sequence that doesn't really mean anything. They could have just decided to fill the entire packet payload with 0x42
bytes if they wanted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With