1. Decode an IPv4 packet¶
The chat relay tutorial designed a wire format from scratch. This second
sequence applies the same Struct/pack/unpack workflow to a handful of
formats rustruct already models for you – formats you did not design and
cannot change, which is the more common case once code has to talk to the
outside world. Complete 1. Install rustruct first if
rustruct.Struct.pack() and rustruct.Struct.unpack() are
unfamiliar.
Start with a complete packet containing an IPv4 header and a TCP header:
import ipaddress
from rustruct.protocols import IPProtocol, IPv4
from rustruct.protocols.tcp import TCP
wire = bytes.fromhex(
"45 00 00 28 1c 46 40 00 40 06 b1 e6 c0 a8 00 01 c0 a8 00 02"
"00 50 c3 50 00 00 00 01 00 00 00 00 50 02 72 10 e6 f2 00 00"
)
packet = IPv4.unpack(wire)
The first 20 bytes are the IPv4 header itself:
Offset |
Bytes |
Field |
Value |
|---|---|---|---|
0 |
1 |
version + IHL |
|
1 |
1 |
DSCP / ECN |
|
2-3 |
2 |
total length |
|
4-5 |
2 |
identification |
|
6-7 |
2 |
flags + fragment offset |
|
8 |
1 |
TTL |
|
9 |
1 |
protocol |
|
10-11 |
2 |
header checksum |
|
12-15 |
4 |
source address |
|
16-19 |
4 |
destination address |
|
Bytes 20-39 are the TCP header packet.body.payload decodes into. The
model converts addresses to IPv4Address and dispatches the payload from
the IP protocol number:
assert packet.source == ipaddress.IPv4Address("192.168.0.1")
assert packet.destination == ipaddress.IPv4Address("192.168.0.2")
assert packet.protocol == IPProtocol.TCP
assert isinstance(packet.body.payload, TCP)
assert packet.body.payload.source_port == 80
assert packet.body.payload.dest_port == 50000
Packing the typed packet preserves the original bytes:
assert packet.pack() == wire