How to print field offsets in scapy?

Question:

I have a scapy protocol and a packet.

Like "First steps" say, it’s easy to print a packet with its fields, and the packet’s binary dump:

>>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 nn"
>>> a
<Ether  type=IPv4 |<IP  frag=0 proto=6 dst=Net("www.slashdot.org/32") |<TCP  
|<Raw  load='GET /index.html HTTP/1.0 nn' |>
>>>
>>> hexdump(a)
0000  FF FF FF FF FF FF 02 42 AC 1F 80 3C 08 00 45 00  .......B...<..E.
0010  00 43 00 01 00 00 40 06 C9 F0 AC 1F 80 3C 68 12  .C....@......<h.
0020  1C 56 00 14 00 50 00 00 00 00 00 00 00 00 50 02  .V...P........P.
0030  20 00 0C EE 00 00 47 45 54 20 2F 69 6E 64 65 78   .....GET /index
0040  2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A  .html HTTP/1.0 .
0050  0A                                               .
>>>

Now, I want to know, for example, what offset does dport field in TCP have. Or all of the fields in all the layers.

Can I print them with scapy? Is there a single way to do it for all protocols, including custom ones?

Asked By: Victor Sergienko

||

Answers:

interesting question. I don’t believe that there’s currently any builtin way of doing that, and I can’t really think of a use case.

The things that gets the closest, for purely informative purpose, are probably either:

>>> rfc(TCP)
 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             SPORT             |             DPORT             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              SEQ                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              ACK                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|DATAOFS|RESER|      FLAGS      |             WINDOW            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             CHKSUM            |             URGPTR            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            OPTIONS            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                             Fig. TCP

But if you want really is a numerical value, matching the offset of a field from the first byte, It’s not super hard to do it yourself, with something like:

def calc_offset(packet, layer, field):
    """Calculate the offset of a field, in a packet"""
    offset = 0
    while packet:  # for each payload
        for fld in packet.fields_desc:  # for each field
            if fld.name == field and isinstance(packet, layer):
                return int(offset)
            offset += fld.i2len(packet, packet.getfieldval(fld.name))  # add length
        packet = packet.payload
    return -1

In your example:

>>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 nn"
>>> calc_offset(a, TCP, "dport")
36
>>> struct.unpack("!H", bytes(a)[36:36+2])[0]
80
>>> a.dport
80

Hope this helped

Answered By: Cukic0d

For completeness sake, here is a function to get/print all fields’ offsets.

def field_offsets(packet):
    offsets = []
    offset = 0
    while packet:
        for field in packet.fields_desc:
            offsets.append((f'{packet.name}.{field.name}', offset))
            offset += field.i2len(packet, packet.getfieldval(field.name))
        packet = packet.payload
    return offsets
for (f, o) in field_offsets(a):
    print(f'{f}: {o}')

Ethernet.dst: 0
Ethernet.src: 6
Ethernet.type: 12
IP.version: 14
IP.ihl: 14.5
IP.tos: 15.0
IP.len: 16.0
IP.id: 18.0
IP.flags: 20.0
IP.frag: 20.375
IP.ttl: 22.0
IP.proto: 23.0
IP.chksum: 24.0
IP.src: 26.0
IP.dst: 30.0
IP.options: 34.0
TCP.sport: 34.0
TCP.dport: 36.0
TCP.seq: 38.0
TCP.ack: 42.0
TCP.dataofs: 46.0
TCP.reserved: 46.5
TCP.flags: 46.875
TCP.window: 48.0
TCP.chksum: 50.0
TCP.urgptr: 52.0
TCP.options: 54.0
Raw.load: 54.0
Answered By: Victor Sergienko
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.