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

将代码块移至函数后出现OSError: [Errno 22]无效参数错误

问题

将BPF过滤器的创建逻辑移到独立函数create_filters_struct()后,调用sock.setsockopt()时出现OSError: [Errno 22] Invalid argument错误,但所有代码写在主逻辑里时运行正常。

原代码如下:

from ctypes import create_string_buffer, addressof
from struct import pack, unpack

import socket


SHOW_ERROR: bool = True


def bpf_jump(code, k, jt, jf):
    return pack('HBBI', code, jt, jf, k)

def bpf_stmt(code, k):
    return bpf_jump(code, k, 0, 0)

# 指令类别
BPF_LD = 0x00
BPF_JMP = 0x05
BPF_RET = 0x06

# ld/ldx 字段
BPF_H = 0x08
BPF_B = 0x10
BPF_ABS = 0x20

# alu/jmp 字段
BPF_JEQ = 0x10
BPF_K = 0x00

# 定义于asm/socket.h
SO_ATTACH_FILTER = 26

# 出于性能考虑,过滤器的顺序与直觉相反:最可能失败的检查放在最前面。
filters_list = [
    # 必须是UDP(检查字节偏移23处的协议字段)
    bpf_stmt(BPF_LD | BPF_B | BPF_ABS, 23),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 3),
    # 必须是IPv4(检查字节偏移12处的以太网类型字段)
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 12),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 1),
    bpf_stmt(BPF_RET | BPF_K, 0x0FFFFFFF), # 通过
    bpf_stmt(BPF_RET | BPF_K, 0), # 拒绝
]

str_buf = None
def create_filters_struct() -> bytes:
    filters = b''.join(filters_list)
    str_buf = create_string_buffer(filters)
    mem_addr_of_filters = addressof(str_buf)
    fprog = pack('HL', len(filters_list), mem_addr_of_filters)
    return fprog

if SHOW_ERROR is False:
    filters = b''.join(filters_list)
    b = create_string_buffer(filters)
    mem_addr_of_filters = addressof(b)
    fprog = pack('HL', len(filters_list), mem_addr_of_filters)
else:
    fprog = create_filters_struct()

net_if = "eth0"

sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
sock.setsockopt(socket.SOL_SOCKET,
                SO_ATTACH_FILTER,
                fprog)

错误回溯信息:

Traceback (most recent call last):
  File "error_demo.py", line 68, in <module>
    fprog)
OSError: [Errno 22] Invalid argument

解决方案

问题根源

函数create_filters_struct()内的str_buf是局部变量,函数执行完毕后,这个c_char_Array对象会被Python垃圾回收机制销毁,对应的内存地址变为无效地址。而你打包进fprog的正是这个内存地址,当setsockopt尝试访问该地址时,内存已被释放,从而触发参数无效错误。

主逻辑中运行正常的原因是变量b处于全局作用域,直到setsockopt调用完成后才会被回收,内存地址始终有效。

修复方法

需要保证str_buf的生命周期覆盖到setsockopt调用完成,以下两种方式均可:

方式1:将str_buf提升为全局变量

修改create_filters_struct()函数,声明使用全局str_buf,避免局部变量被提前回收:

str_buf = None
def create_filters_struct() -> bytes:
    global str_buf  # 声明使用全局变量
    filters = b''.join(filters_list)
    str_buf = create_string_buffer(filters)
    mem_addr_of_filters = addressof(str_buf)
    fprog = pack('HL', len(filters_list), mem_addr_of_filters)
    return fprog

方式2:返回缓冲区对象和打包结构

让函数同时返回fprog和str_buf,在主逻辑中持有str_buf的引用,直到setsockopt执行完成:

def create_filters_struct() -> tuple[bytes, object]:
    filters = b''.join(filters_list)
    str_buf = create_string_buffer(filters)
    mem_addr_of_filters = addressof(str_buf)
    fprog = pack('HL', len(filters_list), mem_addr_of_filters)
    return fprog, str_buf

# 主逻辑部分修改
if SHOW_ERROR is False:
    filters = b''.join(filters_list)
    b = create_string_buffer(filters)
    mem_addr_of_filters = addressof(b)
    fprog = pack('HL', len(filters_list), mem_addr_of_filters)
else:
    fprog, str_buf = create_filters_struct()  # 持有缓冲区引用,避免被回收

两种方式都能确保str_buf对应的内存地址在setsockopt调用时仍有效,解决参数无效的问题。


内容的提问来源于stack exchange,提问作者Jim Fell

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:35:32