Question

I am trying to correctly dissect PPPoE Discovery packets with Scapy. Here's how Scapy displays example PADI packet:

>>> p = Ether("\xff\xff\xff\xff\xff\xff\x08\x00'\xf3<5\x88c\x11\t\x00\x00\x00\x0c\x01\x01\x00\x00\x01\x03\x00\x04\xe0\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
>>> p.show()
 ###[ Ethernet ]###
  dst= ff:ff:ff:ff:ff:ff
  src= 08:00:27:f3:3c:35
  type= 0x8863
###[ PPP over Ethernet Discovery ]###
     version= 1L
     type= 1L
     code= PADI
     sessionid= 0x0
     len= 12
###[ Raw ]###
        load= '\x01\x01\x00\x00\x01\x03\x00\x04\xe0\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

I want to parse that Raw payload. This payload is just a list PPPoE tags. Each tags consists of two byte code field, two byte length field and a value (it's length given by previous field, of course).

This is my attempt at representing all this:

from scapy.all import *

class PPPoETag(Packet):
    name = "PPPoE Tag"
    fields_desc = [ ShortEnumField('tag_type', None,
                                   {0x0000: 'End-Of-List',
                                    0x0101: 'Service-Name',
                                    0x0102: 'AC-Name',
                                    0x0103: 'Host-Uniq',
                                    0x0104: 'AC-Cookie',
                                    0x0105: 'Vendor-Specific',
                                    0x0110: 'Relay-Session-Id',
                                    0x0201: 'Service-Name-Error',
                                    0x0202: 'AC-System-Error',
                                    0x0203: 'Generic-Error'}),
                    FieldLenField('tag_len', None, length_of='tag_value', fmt='H'),
                    StrLenField('tag_value', '', length_from=lambda pkt:pkt.tag_len)]
    def extract_padding(self, s):
        return '', s

class PPPoED_Tags(Packet):
    name = "PPPoE Tag List"
    fields_desc = [ PacketListField('tag_list', None, PPPoETag) ]

bind_layers(PPPoED, PPPoED_Tags, type=1)

Not quite sure if it's the right and best way. Any advice on improving?

Was it helpful?

Solution

In my own code for a similarly low level problem (parsing the raw stream of serial port protocol using ASCII control code based information delimiters such as SOT, EOT, NULL, BELL, etc) I used a set of regexs and standard comparators. It was easy to structure in the code for comprehension by others, and also quite quick using the precompiled regexs.

Without sitting down & coding out the exact python for it, If i wanted to get the fields out without adding any non system dependencies, Id use something roughly like this pseudocode.

    Start Loop over packet content.
        Match any Tag
            Match specific tag type
                set array index based on tag type
            extract length of value
            extract tag value
            store value in array at the index set above
            slice off all the entire now matched & extracted tag.
        Loop until end no more tags match.
    End of loop

OTHER TIPS

I would do this instead, just like with Scapy's Dot11Elt implementation (plus it correctly understands bytes after End-Of-List tag as Padding):

class PPPoE_Tag(Packet):
    name = "PPPoE Tag"
    fields_desc = [ ShortEnumField('tag_type', None,
                                   {0x0000: 'End-Of-List',
                                    0x0101: 'Service-Name',
                                    0x0102: 'AC-Name',
                                    0x0103: 'Host-Uniq',
                                    0x0104: 'AC-Cookie',
                                    0x0105: 'Vendor-Specific',
                                    0x0110: 'Relay-Session-Id',
                                    0x0201: 'Service-Name-Error',
                                    0x0202: 'AC-System-Error',
                                    0x0203: 'Generic-Error'}),
                    FieldLenField('tag_len', None, length_of='tag_value', fmt='H'),
                    StrLenField('tag_value', '', length_from=lambda pkt:pkt.tag_len)]

bind_layers(PPPoED, PPPoE_Tag, type=1)
bind_layers(PPPoE_Tag, Padding, tag_type=0)
bind_layers(PPPoE_Tag, PPPoE_Tag)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top