Packet Basics¶
In this section we cover the basics of how to create a packet and manipulate it contents.
Creating a Packet¶
Creating a custom packet requires inheriting the Packet
class and then defining the Fields within the order
they are expected to be seen
>>> from calpack import models
>>> class UDP_Header(models.Packet):
... source_port = models.IntField16()
... dest_port = models.IntField16()
... length = models.IntField16()
... checksum = models.IntField16()
Note
The order in which the fields are defined is also the order in which the fields are set within the internal c structure.
If you desired to have a default value for a particular field, simply use the default_val
param for the Field
>>> class UDP_Header(models.Packet):
... source_port = models.IntField16(default_val=8888)
... dest_port = models.IntField16(default_val=8000)
... length = models.IntField16()
... checksum = models.IntField16()
Upon creation of the Packet instance, any fields that haven’t been set but have a default value will be automatically set to that default value.
Accessing and Manipulating the Fields¶
Once a packet is defined, creating an instance of that packet allows you to manipulate it.
>>> my_pkt = UDP_Header()
>>> my_pkt.source_port = 8080
>>> my_pkt.dest_port = 8080
>>> my_pkt.length = 0x2
>>> my_pkt.checksum = 0x0
>>> print(my_pkt.source_port)
8080
An instance of a packet can also be created with fields already populated
>>> my_pkt = UDP_Header(
... source_port=8080,
... dest_port=8080,
... length=0x2,
... checksum=0x0
... )
>>> print(my_pkt.source_port, my_pkt.dest_port, my_pkt.length, my_pkt.checksum)
8080 8080 2 0
Note
This is different than the default_val
param. This value will overwrite that default value.
Packet fields can be easily copied from and/or compared to other packets of the same Packet subclass
>>> my_pkt2 = UDP_Header()
>>> my_pkt2.source_port = my_pkt.source_port
>>> my_pkt2.dest_port = 8888
>>> my_pkt.source_port == my_pkt2.source_port
True
>>> my_pkt.dest_port == my_pkt2.dest_port
False
Packets themselves can also be compared
>>> my_pkt = UDP_Header()
>>> my_pkt.source_port = 123
>>> my_pkt.dest_port = 456
>>> my_pkt.length = 789
>>> my_pkt2 = UDP_Header()
>>> my_pkt2.source_port = 123
>>> my_pkt2.dest_port = 456
>>> my_pkt2.length = 123
>>> my_pkt == my_pkt2
False
>>> my_pkt2.length = 789
>>> my_pkt == my_pkt2
True
Note
Comparing two packets that are different classes but may have the same byte output will result in False
Packets and Byte Strings¶
A packet instance can then be converted into a byte string
>>> my_pkt.to_bytes()
b'\x90\x1f\x90\x1f\x02\x00\x00\x00'
In reverse, a packet can be created from a byte string array
>>> my_parsed_pkt = UDP_Header.from_bytes(b'\x90\x1f\x90\x1f\x02\x00\x00\x00')
>>> print(my_parsed_pkt.source_port)
8080
>>> print(my_parsed_pkt.dest_port)
8080
>>> my_parsed_pkt == my_pkt
True
>>> # Show that the packets are two different objects
>>> my_parsed_pkt is my_pkt
False
Packet Endianess¶
By default, Packets will parse and generate byte data based on the system endianess. If a specific endianess is
desired, then PacketBigEndian
or PacketLittleEndian
can be used to force that endianess.
Defining a Packet for a particular Endianness is the same as defining a typical Packet, with the exception of using the desired Endian Packet. For example:
>>> class BigUDP_Header(models.PacketBigEndian):
... source_port = models.IntField16()
... dest_port = models.IntField16()
... length = models.IntField16()
... checksum = models.IntField16()
>>> class LittleUDP_Header(models.PacketLittleEndian):
... source_port = models.IntField16()
... dest_port = models.IntField16()
... length = models.IntField16()
... checksum = models.IntField16()
Using the from_bytes
and to_bytes
can be used as well. However, they are now tied to the specific
endianess defined and NOT the system default.
>>> my_big_pkt = BigUDP_Header(
... source_port = 8080,
... dest_port = 8080,
... length = 0x2,
... checksum = 0x0
... )
>>> my_big_pkt.to_bytes()
b'\x1f\x90\x1f\x90\x00\x02\x00\x00'
>>> my_little_pkt = LittleUDP_Header.from_bytes(b'\x90\x1f\x90\x1f\x02\x00\x00\x00')
>>> my_little_pkt.source_port == 8080
True
>>> my_little_pkt.dest_port == 8080
True
>>> my_little_pkt.length
2
>>> my_little_pkt.to_bytes()
b'\x90\x1f\x90\x1f\x02\x00\x00\x00'