-2

I have this code that works fine when it's defined altogether, but if I move a portion of the code to its own function, I get an 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)

# Instruction classes
BPF_LD = 0x00
BPF_JMP = 0x05
BPF_RET = 0x06

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

# alu/jmp fields
BPF_JEQ = 0x10
BPF_K = 0x00

# As defined in asm/socket.h
SO_ATTACH_FILTER = 26

# Ordering of the filters is backwards of what would be intuitive for
# performance reasons: the check that is most likely to fail is first.
filters_list = [
    # Must be UDP (check protocol field at byte offset 23)
    bpf_stmt(BPF_LD | BPF_B | BPF_ABS, 23),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 3),
    # Must be IPv4 (check ethertype field at byte offset 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), # pass
    bpf_stmt(BPF_RET | BPF_K, 0), # reject
]

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)

Here is the error traceback:

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

How can I fix this?

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
Jim Fell
  • 13,750
  • 36
  • 127
  • 202

2 Answers2

0

The function appears to work just fine and only other thing that changed was tuple(self.__command.interfaces.items())[0][1] which is probably where the invalid argument comes from.

PS: People don't like the question because adding it to a function isn't the "only" thing that changed. It's also part of a class now with no info on that. We also didn't get the whole error.

ajgrinds
  • 186
  • 11
0

In this code:

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

As jasonharper points out, you are getting the address from a local variable. Local variables are dropped when the function ends, and their contents become eligible for garbage collection. So trying to make use of that address outside of the function will be unsuccessful.

Storing the string buffer in a global variable instead seems to solve the problem. That could be as simple as adding global str_buf into your function definition.

khelwood
  • 55,782
  • 14
  • 81
  • 108