You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:54:24