在上一节 搭建基础框架
后本节我们真正实现对IP报文的解析,首先通过下面这张图先了解一下数据报文组装的结构顺序,IP首部处于IP数据包的头部,每一行32位,共计5行,总长度为20字节。IP协议处于OSI七层模型的网络层,网络层的作用就是负责解决数据报在虚拟网络中传输路径的问题。
![请添加图片描述](https://img-blog.csdnimg.cn/45aa234cd34a437eb172ad4376ef7a85.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5pe65pe65bCP5bCP6LaF,size_20,color_FFFFFF,t_70,g_se,x_16)
一、前期准备
1.1 IP首部组成
IP首部位于IP数据报的前20个字节,每一行有32比特位,共计5行。
第一行包括 4位IP-version 4位IP头长度 8位服务类型 16位报文总长度
第二行包括 16位标识符 3位标记位 13位片偏移 (解析时不关注此行)
第三行包括 8位TTL 8位协议 16位头部校验和
第四行包括 32位源IP地址
第五行包括 32位目的IP地址
1.2 struct处理二进制报文数据
通过设置下面的格式可以将二进制数据按照不同字节大小进行解析
FORMAT | C TYPE | PYTHON TYPE | STANDARD SIZE |
---|---|---|---|
B | unsigned char | integer | 1字节 |
H | unsigned short | integer | 2字节 |
L | unsigned long | integer | 4字节 |
s | char[] | string | ~ |
大端字节序表示高位在前,低位在后,是网络通信中常用的顺序;小段字节序则相反,低位在前,高位在后,因为计算机电路在处理低位字节效率较高,常用于主机存储中。
下面是struct包解析demo,其中>表示按照大端顺序进行解析。
import struct
bin_str = b'ABCDEFGH'
print(bin_str)
print(bin_str.decode())
res = struct.unpack('>8B', bin_str)
print(res)
res2 = struct.unpack('>4H', bin_str)
print(res2)
res3 = struct.unpack('>2L', bin_str)
print(res3)
res4 = struct.unpack('>8s', bin_str)
print(res4)
测试结果:
二、IP首部解析器的实现
创建net包 新建parser.py文件
在IPParser中分别对头部报文的每一行进行解析。
class IPParser:
IP_HEADER_LENGTH = 20 # 报文前二十字节为ip头部
@classmethod
def parse_ip_header(cls, ip_header):
"""
IP报文格式
1. 4位IP-version 4位IP头长度 8位服务类型 16位报文总长度
2. 16位标识符 3位标记位 13位片偏移 暂时不关注此行
3. 8位TTL 8位协议 16位头部校验和
4. 32位源IP地址
5. 32位目的IP地址
:param ip_header:
:return:
"""
line1 = struct.unpack('>BBH', ip_header[:4]) # 先按照8位、8位、16位解析
ip_version = line1[0] >> 4 # 通过右移4位获取高四位
# 报文头部长度的单位是32位 即四个字节
iph_length = (line1[0] & 15) * 4 # 与1111与运算获取低四位
packet_length = line1[2]
line3 = struct.unpack('>BBH', ip_header[8: 12])
TTL = line3[0]
protocol = line3[1]
iph_checksum = line3[2]
line4 = struct.unpack('>4s', ip_header[12: 16])
src_ip = socket.inet_ntoa(line4[0])
line5 = struct.unpack('>4s', ip_header[16: 20])
dst_ip = socket.inet_ntoa(line5[0])
# 返回结果
# ip_version ip版本
# iph_length ip头部长度
# packet_length 报文长度
# TTL 报文寿命
# protocol 协议号 1 ICMP协议 6 TCP协议 17 UDP协议
# iph_checksum ip头部的校验和
# src_ip 源ip
# dst_ip 目的ip
return {
'ip_version': ip_version,
'iph_length': iph_length,
'packet_length': packet_length,
'TTL': TTL,
'protocol': protocol,
'iph_checksum': iph_checksum,
'src_ip': src_ip,
'dst_ip': dst_ip
}
@classmethod
def parse(cls, packet):
ip_header = packet[:cls.IP_HEADER_LENGTH]
return cls.parse_ip_header(ip_header)
三、测试逻辑
在ServerProcessTask中调用IPParser
def process(self):
"""
异步处理方法
:return:
"""
headers = {
'network_header': None,
'transport_header': None
}
ip_header = IPParser.parse(self.packet)
headers['network_header'] = ip_header
return headers
测试结果: