通過使用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'\x22\x00\xf0\xc3\x00\x00\x00\x02' \
b'\x01\x00\x00\x02\xe1\x00\x00\x33\x0a\x01\x01\x05\x0a\x01\x01\x05' \
b'\x01\x00\x00\x02\xe1\x00\x00\x33\x0a\x01\x01\x05\x0a\x01\x01\x05'
igmp = IGMP(test)
for i in igmp.group_list : print(i)