通过使用Python struct库来解析IGMPv3报文
struct模块中的函数
函数 | return | explain |
---|---|---|
pack(fmt,v1,v2…) | string | 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回. |
pack_into(fmt,buffer,offset,v1,v2…) | None | 按照给定的格式(fmt),将数据转换成字符串(字节流),并将字节流写入以offset开始的buffer中.(buffer为可写的缓冲区,可用array模块) |
unpack(fmt,v1,v2…..) | tuple | 按照给定的格式(fmt)解析字节流,并返回解析结果 |
pack_from(fmt,buffer,offset) | tuple | 按照给定的格式(fmt)解析以offset开始的缓冲区,并返回解析结果 |
calcsize(fmt) | size of fmt | 计算给定的格式(fmt)占用多少字节的内存,注意对齐方式 |
格式化字符串
当打包或者解包的时,需要按照特定的方式来打包或者解包.该方式就是格式化字符串,它指定了数据类型,除此之外,还有用于控制字节顺序、大小和对齐方式的特殊字符.
对齐方式
为了同c中的结构体交换数据,还要考虑c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.定义如下
Character | Byte order | Size | Alignment |
---|---|---|---|
@(默认) | 本机 | 本机 | 本机,凑够4字节 |
= | 本机 | 标准 | none,按原字节数 |
< | 小端 | 标准 | none,按原字节数 |
> | 大端 | 标准 | none,按原字节数 |
! | network(大端) | 标准 | none,按原字节数 |
如果不懂大小端,见大小端参考网址.
格式符
格式符 | C语言类型 | Python类型 | Standard size |
---|---|---|---|
x | pad byte(填充字节) | no value | |
c | char | string of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I(大写的i) | unsigned int | integer | 4 |
l(小写的L) | long | integer | 4 |
L | unsigned long | long | 4 |
q | long long | long | 8 |
Q | unsigned long long | long | 8 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | string | |
p | char[] | string | |
P | void * | long |
注- -!
- _Bool在C99中定义,如果没有这个类型,则将这个类型视为char,一个字节;
- q和Q只适用于64位机器;
- 每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int;
- P用来转换一个指针,其长度和计算机相关;
- f和d的长度和计算机相关;
报文结构
Group Record格式:
来源:https://tools.ietf.org/html/rfc3376
Code
import struct
class IGMP:
def __init__(self, raw):
raw_head = raw[:8]
self.type, self.reserved, self.checknum, self.reserved2, self.number_group_records = struct.unpack(">BBHHH", raw_head)
raw_group = raw[8:]
self.group_list = []
self.deal_group_record(self.number_group_records, raw_group)
def deal_group_record(self, number_group, raw_group):
group_pointer = 0
for _ in range(number_group):
record_type, aux_data_len, number_source, multicast_address = struct.unpack(">BBHI",raw_group[group_pointer : group_pointer + 8])
source_address_list = []
for k in range(number_source):
tmp_pointer = group_pointer + 8 + k * 4
source_address = struct.unpack(">I",raw_group[tmp_pointer : tmp_pointer + 4])
source_address_list.append(source_address)
tmp_pointer = group_pointer + 8 + number_source * 4
if aux_data_len>0:
auxiliary_data = struct.unpack(">%sI"%aux_data_len,raw_group[tmp_pointer : tmp_pointer + 4 * aux_data_len])
else:
auxiliary_data = ""
self.group_list.append({"record_type": record_type,"aux_data_len": aux_data_len,"number_source": number_source,"multicast_address": multicast_address,"source_address_list": source_address_list,"auxiliary_data": auxiliary_data})
group_pointer = group_pointer + 8 + number_source * 4 + aux_data_len * 4
if __name__ == '__main__':
test = b'x22x00xf0xc3x00x00x00x02'
b'x01x00x00x02xe1x00x00x33x0ax01x01x05x0ax01x01x05'
b'x01x00x00x02xe1x00x00x33x0ax01x01x05x0ax01x01x05'
igmp = IGMP(test)
for i in igmp.group_list : print(i)