使用Scapy解析PPPoE标签
我正在尝试用Scapy正确解析PPPoE发现数据包。下面是Scapy显示的一个示例PADI数据包:
>>> 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'
我想要解析这个原始负载。这个负载其实就是一系列PPPoE标签。每个标签包含两个字节的代码字段、两个字节的长度字段和一个值(这个值的长度是由前面的字段给出的,当然)。
这是我尝试表示这些内容的方法:
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)
我不太确定这样做是否正确,也不确定这是否是最好的方法。有没有什么建议可以改进一下?
2 个回答
1
我会这样做,就像Scapy的Dot11Elt实现那样(而且它能正确理解End-Of-List
标签后面的字节是填充数据):
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)
0
在我自己的代码中,我处理了一个类似的低级问题(解析串口协议的原始数据流,使用像SOT、EOT、NULL、BELL等ASCII控制代码作为信息分隔符)。我使用了一组正则表达式和标准比较器。这样写的代码结构清晰,别人也能容易理解,而且使用预编译的正则表达式速度也很快。
虽然我没有坐下来写出确切的Python代码,但如果我想在不添加任何额外依赖的情况下提取字段,我会用类似下面的伪代码。
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