Python3.6用sendmsg设置UDP包TTL时遇[Errno22]无效参数问题
我之前在处理类似的UDP数据包发送问题时碰到过一模一样的错误,帮你拆解下原因和可行的解决办法:
核心原因
在Linux系统中,普通的UDP(SOCK_DGRAM)套接字无法通过sendmsg()的辅助数据(ancdata)设置IP_TTL——这个方式仅对两种套接字生效:
SOCK_RAW类型的套接字- 开启了
IP_HDRINCL选项的套接字
而你用setsockopt()设置IP_TTL能正常工作,是因为这个接口是全局配置套接字的TTL值,对所有UDP套接字都适用,不受上述限制。
另外提一句,你代码里有个小语法错误:Ancdata = [(socket.SOL_IP, socket.IP_TTL, bytes([1])] 少了一个右括号,应该改成Ancdata = [(socket.SOL_IP, socket.IP_TTL, bytes([1]))],不过这应该是你输入时的笔误,毕竟你说空ancdata时sendmsg能正常运行。
解决方案
根据你的需求(尽可能用sendmsg()),分两种场景给出方案:
场景1:需要为每个数据包设置不同的TTL
这种情况只能用SOCK_RAW套接字或者开启IP_HDRINCL选项,这里以SOCK_RAW为例(需要root权限),你需要手动构造IP和UDP头部:
import socket import struct # 创建RAW套接字,必须以root身份运行 s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # 构造IP头部 def build_ip_header(src_ip, dst_ip, ttl): ip_version = 4 ip_ihl = 5 # 头部长度,单位是32位字 ip_ver_ihl = (ip_version << 4) + ip_ihl ip_tos = 0 ip_total_length = 0 # 由内核自动填充 ip_id = 12345 ip_frag_offset = 0 ip_ttl = ttl ip_proto = socket.IPPROTO_UDP ip_checksum = 0 # 由内核自动填充 ip_src_addr = socket.inet_aton(src_ip) ip_dst_addr = socket.inet_aton(dst_ip) return struct.pack( '!BBHHHBBH4s4s', ip_ver_ihl, ip_tos, ip_total_length, ip_id, ip_frag_offset, ip_ttl, ip_proto, ip_checksum, ip_src_addr, ip_dst_addr ) # 构造UDP头部 def build_udp_header(src_port, dst_port, payload): udp_src_port = src_port udp_dst_port = dst_port udp_length = 8 + len(payload) # UDP头部8字节 + 负载长度 udp_checksum = 0 # 可选,这里不计算校验和 return struct.pack('!HHHH', udp_src_port, udp_dst_port, udp_length, udp_checksum) # 准备发送参数 local_ip = "你的本地IP地址" target_ip = "10.0.0.0" local_port = 12345 target_port = 2222 payload = b'' # 组装数据包 ip_header = build_ip_header(local_ip, target_ip, ttl=1) udp_header = build_udp_header(local_port, target_port, payload) full_packet = ip_header + udp_header + payload # 使用sendmsg发送 s.sendmsg([full_packet], [], 0, (target_ip, target_port))
场景2:不需要每个包单独设置TTL,只是想用sendmsg发送数据
这种情况可以先通过setsockopt()全局设置TTL,再调用sendmsg()发送,简单直接:
import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 全局设置TTL值 s.setsockopt(socket.SOL_IP, socket.IP_TTL, 1) msg = [b''] address = ('10.0.0.0', 2222) # 直接用sendmsg发送,无需带ancdata s.sendmsg(msg, [], 0, address)
内容的提问来源于stack exchange,提问作者Tomasz Jadczak
相关产品推荐
相关产品推荐

