Python发送原始IP数据包失败,求助排查原因
嘿,我来帮你排查无法发送原始IP数据包的问题!结合你的描述和代码片段,先从几个最常见的原因说起,再给你补全代码的思路:
1. 权限不足(最常见的坑)
原始套接字操作属于系统底层网络操作,必须以root/管理员权限运行脚本:
- Linux/macOS:运行时加上
sudo,比如sudo python ip.py 192.168.1.100 8.8.8.8 eth0 - Windows:右键命令提示符/终端,选择「以管理员身份运行」后再执行脚本
2. 操作系统限制
有些系统默认会限制普通用户创建原始套接字:
- Linux:内核3.17之后普通用户可创建部分原始套接字,但
IPPROTO_RAW仍需root权限,无需额外调整内核参数 - Windows:需要确保系统允许使用原始套接字,若遇到报错,可检查防火墙是否放行原始数据包,一般管理员权限就能解决多数问题
3. 不完整的IP头构造
你的IP_HEADER函数只写了开头,而用IPPROTO_RAW创建的套接字,系统不会自动帮你生成IP头——你必须手动构造完整的IP头,包括版本、IHL、总长度、ID、TTL、协议、校验和、源/目标IP等所有字段。如果头构造不完整或校验和错误,数据包会被系统直接丢弃。
4. 未绑定指定网卡
你的代码里没有绑定到指定网卡的逻辑,系统可能会选择默认路由的网卡发送数据包,导致不符合预期。需要在创建套接字后添加绑定网卡的代码:
# Linux/macOS下绑定网卡 ST.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, sys.argv[3].encode())
补全后的完整示例代码
这里给你补全了IP头构造、校验和计算、网卡绑定和权限检查的完整代码:
#!/usr/bin/python import socket import struct import sys import os def Create_SOCKET(interface): global ST try: ST = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) # 绑定到指定网卡 ST.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, interface.encode()) # 明确告知系统我们自己处理IP头 ST.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) except socket.error as MSG: print "[ERROR] Socket creation error:\n", MSG sys.exit() def calculate_checksum(data): # 计算IP头校验和的标准逻辑 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 >> 16) + (checksum & 0xffff) checksum = ~checksum & 0xffff return checksum def IP_HEADER(Source_IP, Destination_IP): global IP_PACKET IP_VER = 4 IP_IHL = 5 IP_TOS = 0 IP_TOTAL_LENGTH = 20 # 仅IP头,无数据时为20字节 IP_ID = 54321 IP_FLAGS = 0 IP_FRAG_OFFSET = 0 IP_TTL = 64 IP_PROTOCOL = socket.IPPROTO_TCP # 可替换为ICMP/UDP等你需要的协议 IP_CHECKSUM = 0 IP_SRC = socket.inet_aton(Source_IP) IP_DST = socket.inet_aton(Destination_IP) # 先构造不带校验和的临时IP头 temp_header = struct.pack('!BBHHHBBH4s4s', (IP_VER << 4) | IP_IHL, IP_TOS, IP_TOTAL_LENGTH, IP_ID, (IP_FLAGS << 13) | IP_FRAG_OFFSET, IP_TTL, IP_PROTOCOL, IP_CHECKSUM, IP_SRC, IP_DST) # 计算并替换校验和 IP_CHECKSUM = calculate_checksum(temp_header) IP_PACKET = struct.pack('!BBHHHBBH4s4s', (IP_VER << 4) | IP_IHL, IP_TOS, IP_TOTAL_LENGTH, IP_ID, (IP_FLAGS << 13) | IP_FRAG_OFFSET, IP_TTL, IP_PROTOCOL, socket.htons(IP_CHECKSUM), IP_SRC, IP_DST) def send_packet(dest_ip): try: # 原始套接字sendto的端口参数无意义,填0即可 ST.sendto(IP_PACKET, (dest_ip, 0)) print "[INFO] Packet sent successfully!" except socket.error as MSG: print "[ERROR] Failed to send packet:\n", MSG if __name__ == "__main__": if len(sys.argv) != 4: print "Usage: sudo python ip.py <Source_IP> <Destination_IP> <Interface>" sys.exit(1) # Linux/macOS下检查root权限 if os.geteuid() != 0: print "[ERROR] Need root privileges to run this script!" sys.exit(1) src_ip = sys.argv[1] dest_ip = sys.argv[2] interface = sys.argv[3] Create_SOCKET(interface) IP_HEADER(src_ip, dest_ip) send_packet(dest_ip)
额外排查技巧
如果还是无法发送,可以用tcpdump(Linux/macOS)或Wireshark抓包,查看:
- 指定网卡是否有数据包发出
- 发出的IP头格式是否正确(比如校验和、源/目标IP是否符合预期)
内容的提问来源于stack exchange,提问作者D.H
相关产品推荐
相关产品推荐

