Python使用原始套接字发送IP数据包的两类问题咨询
嘿,我来帮你搞定这两个RAW套接字的问题!咱们一个个来:
问题1:无法发送IP_PACKET变量
首先,RAW套接字发送数据包有几个容易踩坑的点,我帮你梳理常见原因和解决办法:
核心问题排查
缺少IP_HDRINCL选项设置
当你使用IPPROTO_RAW创建套接字时,内核默认会自动替你生成IP头,覆盖你自定义的IP_PACKET里的IP头。必须通过套接字选项告诉内核:“我自己已经构造好完整IP头了,别改!”
修正你的Create_SOCKET函数:def Create_SOCKET(): global ST try: ST = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) # 关键:开启IP_HDRINCL,让内核使用自定义IP头 ST.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) except socket.error as MSG: print "[ERROR] Socket creation error:\n", MSG sys.exit()IP_PACKET不完整
你构造的IP_PACKET必须是完整的IP头 + 载荷数据,不能只有IP头。比如在IP_HEADER函数最后,要把你的业务数据(payload)拼接到IP头后面:# 示例:添加测试载荷 payload = b"Test RAW socket data" IP_PACKET = ip_header + payload权限不足
RAW套接字需要特权权限才能使用,在Linux下必须用sudo运行脚本:sudo python your_script.py发送参数错误
使用sendto时,目标地址格式要正确,端口参数对RAW套接字无效,填0即可:# 发送示例 ST.sendto(IP_PACKET, (Destination_IP, 0))
问题2:设置IP数据包的标志位
IP头里的标志位占3个比特,位于**IP标识字段后的2字节(标志+分片偏移)**中,高3位是标志位:
- 位15(最高位):保留位,必须为0
- 位14:DF(Don't Fragment,不分片),设为1表示禁止分片
- 位13:MF(More Fragments,更多分片),设为1表示后面还有分片
具体实现步骤
定义标志位数值
按网络字节序(大端)计算,常见的标志组合:- 无标志:
0x0000 - 开启DF(不分片):
0x4000(因为2^14 = 16384 = 0x4000) - 开启MF(更多分片):
0x2000(2^13 = 8192 = 0x2000) - 同时开启DF+MF:
0x6000
- 无标志:
打包到IP头中
在构造IP头时,把标志位和分片偏移(一般设为0,除非你要分片)一起打包:def IP_HEADER(Source_IP, Destination_IP): global IP_PACKET IP_VER = 4 IP_IHL = 5 # 版本+IHL合并为1字节(4位版本+4位IHL) IP_VER_IHL = (IP_VER << 4) | IP_IHL IP_TOS = 0 IP_ID = 0 # 开启DF标志,分片偏移为0 IP_FLAGS_OFFSET = 0x4000 IP_TTL = 64 # 协议类型,比如TCP(IPPROTO_TCP)或UDP(IPPROTO_UDP) IP_PROTOCOL = socket.IPPROTO_TCP IP_CHECKSUM = 0 # 先打包IP头(总长度暂时填0,后面再修正) ip_header = struct.pack('!BBHHHBBH4s4s', IP_VER_IHL, IP_TOS, 0, # 先占位总长度 IP_ID, IP_FLAGS_OFFSET, IP_TTL, IP_PROTOCOL, IP_CHECKSUM, socket.inet_aton(Source_IP), socket.inet_aton(Destination_IP)) # (可选但推荐)计算IP头校验和 def calculate_checksum(data): checksum = 0 data_len = len(data) if data_len % 2 != 0: data += b'\x00' data_len += 1 for i in range(0, data_len, 2): word = (data[i] << 8) + data[i+1] checksum += word checksum = (checksum & 0xffff) + (checksum >> 16) return ~checksum & 0xffff # 替换校验和 ip_header = ip_header[:10] + struct.pack('!H', calculate_checksum(ip_header)) + ip_header[12:] # 添加载荷并修正总长度 payload = b"Test payload for RAW IP packet" total_length = len(ip_header) + len(payload) ip_header = ip_header[:2] + struct.pack('!H', total_length) + ip_header[4:] # 组合成完整数据包 IP_PACKET = ip_header + payload
小提示
- 如果不需要分片,建议开启DF标志,避免数据包在传输中被分片导致问题
- 校验和可以让内核自动计算,但手动计算能避免某些内核环境下的兼容性问题
内容的提问来源于stack exchange,提问作者D.H
相关产品推荐
相关产品推荐

