### Install via pip Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Standard installation method using PyPI. ```bash pip install NetfilterQueue ``` -------------------------------- ### Install from source Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Build and install the module directly from the repository. ```bash pip install cython git clone https://github.com/oremanj/python-netfilterqueue cd python-netfilterqueue pip install . ``` -------------------------------- ### Install dependencies on Debian/Ubuntu Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Install required development headers and build tools before compiling the extension. ```bash apt-get install build-essential python3-dev libnetfilter-queue-dev ``` -------------------------------- ### NetfilterQueue.run() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Starts the packet processing loop to receive and dispatch packets to the callback function. ```APIDOC ## run(block=True) ### Description Starts the processing loop. By default, it blocks the execution until interrupted. Can be set to non-blocking mode for integration with event loops. ### Parameters #### Parameters - **block** (bool) - Optional - If True, blocks until interrupted. If False, processes available packets and returns immediately. ``` -------------------------------- ### Initialize NetfilterQueue Background Processor Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Starts a background thread to process packets from a Netfilter queue. ```python # Start background processor processor = threading.Thread(target=process_packets, daemon=True) processor.start() nfqueue = NetfilterQueue() nfqueue.bind(1, capture_callback) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Marking Network Traffic Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt This example demonstrates how to mark network packets based on protocol and port numbers. It categorizes SSH traffic as high priority, bulk transfer ports (FTP, HTTP alternate) as low priority, and logs other traffic. ```APIDOC ## POST /mark_traffic ### Description Marks network packets based on protocol and destination port. SSH (port 22) is marked as HIGH_PRIORITY, ports 20, 21, and 8080 are marked as LOW_PRIORITY, and other TCP traffic is marked as LOGGED. The marks are combined using bitwise OR. ### Method POST ### Endpoint /mark_traffic ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **pkt** (object) - Required - The packet object to be marked. - **payload** (bytes) - The packet's payload. - **mark** (int) - The current mark of the packet. - **get_payload()** (method) - Returns the packet payload. - **get_mark()** (method) - Returns the current mark of the packet. - **set_mark(mark)** (method) - Sets the mark for the packet. - **accept()** (method) - Accepts the packet. ### Request Example ```python # Example usage within the NetfilterQueue callback def mark_traffic(pkt): payload = pkt.get_payload() original_mark = pkt.mark current_mark = pkt.get_mark() if len(payload) >= 24: protocol = payload[9] ihl = (payload[0] & 0x0F) * 4 if protocol == 6 and len(payload) >= ihl + 4: # TCP import struct dst_port = struct.unpack('!H', payload[ihl+2:ihl+4])[0] if dst_port == 22: pkt.set_mark(MARK_HIGH_PRIORITY) print(f"Packet {pkt.id}: SSH traffic marked HIGH_PRIORITY") elif dst_port in (20, 21, 8080): pkt.set_mark(MARK_LOW_PRIORITY) print(f"Packet {pkt.id}: Bulk traffic marked LOW_PRIORITY") else: pkt.set_mark(current_mark | MARK_LOGGED) pkt.accept() ``` ### Response #### Success Response (200) No explicit response body, packet is accepted or modified in place. #### Response Example None ``` -------------------------------- ### Manage Kernel Packet Marks with set_mark() and get_mark() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Utilize packet marks (32-bit values) for routing or other iptables-based decisions. Marks persist through the network stack. This example shows how to set and check marks. ```python from netfilterqueue import NetfilterQueue ``` -------------------------------- ### Set Packet Verdicts with accept(), drop(), repeat() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Control packet flow by setting verdicts. accept() allows the packet, drop() discards it, and repeat() restarts processing. This example blocks TCP traffic to port 80 and allows HTTPS, while using repeat() for packets needing reprocessing. ```python from netfilterqueue import NetfilterQueue import struct # Track packets for potential reordering pending_packets = [] def firewall_callback(pkt): payload = pkt.get_payload() # Parse destination port from TCP header (if TCP) if len(payload) >= 24: protocol = payload[9] ihl = (payload[0] & 0x0F) * 4 if protocol == 6 and len(payload) >= ihl + 4: # TCP dst_port = struct.unpack('!H', payload[ihl+2:ihl+4])[0] # Block traffic to port 80 (HTTP) if dst_port == 80: print(f"BLOCKED: TCP packet to port 80") pkt.drop() return # Allow HTTPS if dst_port == 443: print(f"ALLOWED: TCP packet to port 443") pkt.accept() return # Example: Use repeat() to reprocess with a mark # This could trigger different iptables rules if pkt.get_mark() == 0: pkt.set_mark(100) pkt.repeat() # Reprocess from beginning of chain print("Packet marked and repeated") return # Default: accept the packet pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, firewall_callback) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Packet.get_timestamp() - Getting Packet Arrival Time Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Explains how to obtain the kernel-captured arrival time of a packet using `get_timestamp()`, which returns a Unix timestamp with microsecond precision. It notes that timestamps are 0.0 for outgoing packets. ```APIDOC ## GET /measure_latency ### Description Measures and prints the latency of a packet since it was captured by the kernel. This is achieved by comparing the kernel's timestamp with the current time. ### Method GET ### Endpoint /measure_latency ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python from netfilterqueue import NetfilterQueue import time def measure_latency(pkt): kernel_time = pkt.get_timestamp() now = time.time() if kernel_time > 0: latency_ms = (now - kernel_time) * 1000 print(f"Packet {pkt.id}: Queue latency = {latency_ms:.3f}ms") print(f" Kernel timestamp: {kernel_time}") print(f" Current time: {now}") else: print(f"Packet {pkt.id}: No kernel timestamp (outgoing packet)") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, measure_latency) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` ### Response #### Success Response (200) Prints the calculated queue latency in milliseconds, the kernel timestamp, and the current time to standard output. For outgoing packets, it indicates that no kernel timestamp is available. #### Response Example ``` Packet 54321: Queue latency = 1.234ms Kernel timestamp: 1678886400.123456 Current time: 1678886400.124690 ``` OR ``` Packet 54321: No kernel timestamp (outgoing packet) ``` ``` -------------------------------- ### Packet.get_payload() - Accessing Raw Packet Data Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Demonstrates how to retrieve the raw payload of a network packet as bytes, starting from the IP header, for inspection and analysis. ```APIDOC ## Packet.get_payload() ### Description Retrieves the packet's payload as bytes, beginning with the IP header. This enables detailed inspection and analysis of packet contents. If you need to access the payload after the callback returns, ensure you call `retain()`. ### Method `get_payload()` ### Parameters None ### Request Example ```python from netfilterqueue import NetfilterQueue import struct import socket def inspect_packet(pkt): payload = pkt.get_payload() payload_len = pkt.get_payload_len() # Parse IPv4 header (first 20 bytes minimum) if len(payload) >= 20: version_ihl = payload[0] version = version_ihl >> 4 ihl = (version_ihl & 0x0F) * 4 # Header length in bytes protocol = payload[9] # Extract source and destination IPs src_ip = socket.inet_ntoa(payload[12:16]) dst_ip = socket.inet_ntoa(payload[16:20]) print(f"IPv{version} {src_ip} -> {dst_ip}") print(f"Protocol: {protocol}, Length: {payload_len} bytes") # For TCP (protocol 6), extract ports if protocol == 6 and len(payload) >= ihl + 4: src_port, dst_port = struct.unpack('!HH', payload[ihl:ihl+4]) print(f"TCP Port: {src_port} -> {dst_port}") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, inspect_packet) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` ### Response #### Success Response (bytes) - The raw payload of the packet as a bytes object. #### Response Example ``` b'\x45\x00\x00\x3c\x00\x01\x00\x00\x40\x06\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x50\x00\x35\x00\x00\x00\x00\x00\x00\x00\x00\x50\x02\xff\xff\x00\x00\x00\x00' ``` ``` -------------------------------- ### Modify Packet Payload with set_payload() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Modify packet contents using set_payload(). Remember to recompute checksums and lengths after modification. This example modifies the IPv4 TTL and recomputes the header checksum. ```python from netfilterqueue import NetfilterQueue import struct def compute_ip_checksum(header): """Compute IPv4 header checksum.""" if len(header) % 2: header += b'\x00' words = struct.unpack('!%dH' % (len(header) // 2), header) checksum = sum(words) while checksum >> 16: checksum = (checksum & 0xFFFF) + (checksum >> 16) return ~checksum & 0xFFFF def modify_packet(pkt): payload = pkt.get_payload() # Example: Modify TTL in IPv4 header if len(payload) >= 20: # Get header length ihl = (payload[0] & 0x0F) * 4 header = bytearray(payload[:ihl]) # Modify TTL (byte 8) original_ttl = header[8] header[8] = 64 # Set TTL to 64 # Clear checksum and recompute header[10:12] = b'\x00\x00' new_checksum = compute_ip_checksum(bytes(header)) header[10:12] = struct.pack('!H', new_checksum) # Reconstruct packet new_payload = bytes(header) + payload[ihl:] pkt.set_payload(new_payload) print(f"Modified TTL: {original_ttl} -> 64") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, modify_packet) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Initialize and Bind a NetfilterQueue Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Create a queue handler and bind it to a specific queue number with a callback function. The queue number must correspond to the --queue-num defined in the iptables rule. ```python from netfilterqueue import NetfilterQueue def packet_callback(pkt): """Process each packet received from the queue.""" print(f"Received: {pkt}") pkt.accept() # Allow the packet to continue # Create queue handler nfqueue = NetfilterQueue() # Bind to queue number 1 with the callback # max_len: max packets in queue before dropping (default: 1024) # mode: COPY_PACKET (full packet), COPY_META (metadata only), COPY_NONE # range: how many bytes of packet to copy (default: 65535) nfqueue.bind( queue_num=1, user_callback=packet_callback, max_len=1024, mode=NetfilterQueue.COPY_PACKET, range=65535 ) try: # Start processing packets (blocks until interrupted) nfqueue.run() except KeyboardInterrupt: print("Stopping...") # Clean up nfqueue.unbind() # Required iptables rule to send packets to this queue: # sudo iptables -I INPUT -j NFQUEUE --queue-num 1 ``` -------------------------------- ### Configure iptables for NFQUEUE Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Use these commands to route packets to a specific Netfilter queue number. ```bash iptables -I -j NFQUEUE --queue-num ``` ```bash iptables -I INPUT -d 192.168.0.0/24 -j NFQUEUE --queue-num 1 ``` -------------------------------- ### Use custom socket with NetfilterQueue Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Integrate with event loops like gevent by providing a custom socket. Ensure the socket is created using the file descriptor from the queue. ```python from netfilterqueue import NetfilterQueue import socket def print_and_accept(pkt): print(pkt) pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, print_and_accept) s = socket.fromfd(nfqueue.get_fd(), socket.AF_UNIX, socket.SOCK_STREAM) try: nfqueue.run_socket(s) except KeyboardInterrupt: print('') s.close() nfqueue.unbind() ``` -------------------------------- ### Configure iptables for NFQUEUE Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Command to route traffic to the queue number used in the Python script. ```bash iptables -I INPUT -d 192.168.0.0/24 -j NFQUEUE --queue-num 1 ``` -------------------------------- ### View Netfilter queue statistics Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Read the contents of the proc filesystem to inspect current queue status. ```bash cat /proc/net/netfilter/nfnetlink_queue 1 31621 0 2 4016 0 0 2 1 ``` -------------------------------- ### Packet.get_hw() - Accessing Source MAC Address Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Demonstrates how to retrieve the source MAC address of a network packet using the `get_hw()` method. It also explains when MAC addresses are not available. ```APIDOC ## GET /log_mac_addresses ### Description Retrieves and logs the source MAC address of a network packet if available. MAC addresses are typically available for INPUT and FORWARD chains but not for OUTPUT or PREROUTING chains. ### Method GET ### Endpoint /log_mac_addresses ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python from netfilterqueue import NetfilterQueue def log_mac_addresses(pkt): hw = pkt.get_hw() if hw: mac_bytes = hw[0:6] mac_addr = ":".join(f"{b:02x}" for b in mac_bytes) print(f"Packet {pkt.id}: Source MAC = {mac_addr}") else: print(f"Packet {pkt.id}: Source MAC not available (hook={pkt.hook})") pkt.accept() # Example iptables rule: # sudo iptables -I INPUT -j NFQUEUE --queue-num 1 nfqueue = NetfilterQueue() nfqueue.bind(1, log_mac_addresses) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` ### Response #### Success Response (200) Logs the source MAC address to standard output if available, or a message indicating it's not available. #### Response Example ``` Packet 12345: Source MAC = 00:11:22:33:44:55 ``` OR ``` Packet 12345: Source MAC not available (hook=0) ``` ``` -------------------------------- ### Integrate with Gevent using run_socket Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Use run_socket() with a Python socket object to enable compatibility with gevent or other libraries that monkeypatch socket operations. ```python from netfilterqueue import NetfilterQueue import socket def process_packet(pkt): print(f"Packet from hook {pkt.hook}: {pkt}") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, process_packet) # Create a Python socket from the queue's file descriptor # This allows gevent or other libraries to monkeypatch it sock = socket.fromfd( nfqueue.get_fd(), socket.AF_UNIX, socket.SOCK_STREAM ) # Optionally make it non-blocking for async usage sock.setblocking(False) try: nfqueue.run_socket(sock) except KeyboardInterrupt: pass finally: sock.close() nfqueue.unbind() ``` -------------------------------- ### Packet Verdicts: accept(), drop(), repeat() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Defines the possible verdicts for a packet: accept to allow it through, drop to discard it, or repeat to reprocess it. ```APIDOC ## Packet.accept(), Packet.drop(), Packet.repeat() ### Description Assigns a verdict to a packet. `accept()` allows the packet to continue, `drop()` discards it, and `repeat()` restarts its processing from the beginning of the netfilter hook. This allows for packet reordering. ### Methods - `accept()`: Allows the packet to proceed. - `drop()`: Discards the packet. - `repeat()`: Resets packet processing to the beginning of the hook. ### Parameters None for any of these methods. ### Request Example ```python from netfilterqueue import NetfilterQueue import struct # Track packets for potential reordering pending_packets = [] def firewall_callback(pkt): payload = pkt.get_payload() # Parse destination port from TCP header (if TCP) if len(payload) >= 24: protocol = payload[9] ihl = (payload[0] & 0x0F) * 4 if protocol == 6 and len(payload) >= ihl + 4: # TCP dst_port = struct.unpack('!H', payload[ihl+2:ihl+4])[0] # Block traffic to port 80 (HTTP) if dst_port == 80: print(f"BLOCKED: TCP packet to port 80") pkt.drop() return # Allow HTTPS if dst_port == 443: print(f"ALLOWED: TCP packet to port 443") pkt.accept() return # Example: Use repeat() to reprocess with a mark # This could trigger different iptables rules if pkt.get_mark() == 0: pkt.set_mark(100) pkt.repeat() # Reprocess from beginning of chain print("Packet marked and repeated") return # Default: accept the packet pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, firewall_callback) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` ### Response #### Success Response (None) - These methods do not return a value; they set the packet's verdict. ``` -------------------------------- ### Packet Marks: set_mark(), get_mark() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Explains how to set and retrieve 32-bit marks on packets, which can be used by subsequent iptables rules. ```APIDOC ## Packet.set_mark() and Packet.get_mark() ### Description Allows setting and retrieving a 32-bit mark on a packet. This mark can be utilized by subsequent `iptables` rules for various purposes like routing or rate limiting. The mark persists throughout the networking stack. ### Methods - `set_mark(mark)`: Sets a 32-bit mark on the packet. - `get_mark()`: Retrieves the current mark set on the packet. ### Parameters - **mark** (int) - Required - The 32-bit integer value to set as the packet mark. ### Request Example ```python from netfilterqueue import NetfilterQueue # Example usage within a callback function: def process_packet(pkt): current_mark = pkt.get_mark() print(f"Current packet mark: {current_mark}") # Set a new mark if it's not already set if current_mark == 0: new_mark = 12345 pkt.set_mark(new_mark) print(f"Set packet mark to: {new_mark}") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, process_packet) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` ### Response #### Success Response (get_mark()) - **mark** (int) - The 32-bit integer mark associated with the packet. #### Response Example (get_mark()) ``` 12345 ``` ``` -------------------------------- ### Measure Packet Latency with Packet.get_timestamp() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Calculate the latency of packets in the queue by comparing the kernel's arrival timestamp with the current time. Returns 0.0 for outgoing packets (OUTPUT/POSTROUTING hooks). ```python from netfilterqueue import NetfilterQueue import time def measure_latency(pkt): kernel_time = pkt.get_timestamp() now = time.time() if kernel_time > 0: latency_ms = (now - kernel_time) * 1000 print(f"Packet {pkt.id}: Queue latency = {latency_ms:.3f}ms") print(f" Kernel timestamp: {kernel_time}") print(f" Current time: {now}") else: # No timestamp for OUTPUT/POSTROUTING packets print(f"Packet {pkt.id}: No kernel timestamp (outgoing packet)") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, measure_latency) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Monitor Network Namespaces Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Configures NetfilterQueue to monitor traffic in a specific network namespace using a file descriptor. ```python from netfilterqueue import NetfilterQueue import socket def namespace_callback(pkt): print(f"Packet from other namespace: {pkt}") pkt.accept() # To monitor a different network namespace: # 1. A process in the target namespace creates a Netlink socket: # sock = socket(AF_NETLINK, SOCK_RAW, 12) # 12 = NETLINK_NETFILTER # 2. It sends the fd to your process via Unix domain socket (socket.send_fds) # 3. You receive it and pass to NetfilterQueue: # Example assuming you received fd from another namespace # received_fd = receive_fd_from_namespace() # Your IPC mechanism # Create NetfilterQueue using the namespace's socket # nfqueue = NetfilterQueue(sockfd=received_fd) # nfqueue.bind(1, namespace_callback) # nfqueue.run() # For regular usage in current namespace: nfqueue = NetfilterQueue() # Uses default: af=PF_INET nfqueue.bind(1, namespace_callback) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### NetfilterQueue Class Methods Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Methods for managing the lifecycle of a packet queue, including binding, unbinding, and processing packets. ```APIDOC ## NetfilterQueue.bind ### Description Create and bind to a specific kernel queue. ### Parameters - **queue_num** (int) - Required - Uniquely identifies the queue for the kernel. - **callback** (callable) - Required - Function that takes a Packet object as an argument. - **max_len** (int) - Optional - Largest number of packets in the queue. - **mode** (int) - Optional - Amount of packet data provided (COPY_NONE, COPY_META, COPY_PACKET). - **range** (int) - Optional - Number of bytes of the packet to retrieve. - **sock_len** (int) - Optional - Receive socket buffer size. ## NetfilterQueue.unbind ### Description Remove the queue. Packets matched by the iptables rule will be dropped. ## NetfilterQueue.get_fd ### Description Get the file descriptor of the socket used to receive queued packets and send verdicts. ## NetfilterQueue.run ### Description Send packets to the callback. By default, this method blocks until an exception is raised. ### Parameters - **block** (bool) - Optional - If False, processes pending messages without waiting. ``` -------------------------------- ### Access Source MAC Address with Packet.get_hw() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Retrieve the source MAC address of a packet using the get_hw() method. MAC addresses are only available on INPUT or FORWARD chains, not OUTPUT or PREROUTING. The method returns bytes or None. ```python from netfilterqueue import NetfilterQueue def log_mac_addresses(pkt): hw = pkt.get_hw() if hw: # Extract 6-byte MAC address from the 8-byte hw field mac_bytes = hw[0:6] mac_addr = ":".join(f"{b:02x}" for b in mac_bytes) print(f"Packet {pkt.id}: Source MAC = {mac_addr}") else: # MAC not available (OUTPUT/PREROUTING chain or not captured) print(f"Packet {pkt.id}: Source MAC not available (hook={pkt.hook})") pkt.accept() # Note: To capture MAC addresses, use INPUT or FORWARD chain: # sudo iptables -I INPUT -j NFQUEUE --queue-num 1 nfqueue = NetfilterQueue() nfqueue.bind(1, log_mac_addresses) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Process packets with NetfilterQueue Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst Basic implementation to intercept packets and accept them. Requires an active iptables rule targeting the specified queue number. ```python from netfilterqueue import NetfilterQueue def print_and_accept(pkt): print(pkt) pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, print_and_accept) try: nfqueue.run() except KeyboardInterrupt: print('') nfqueue.unbind() ``` -------------------------------- ### Control Packet Copy Modes Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Configures how much packet data is copied to userspace using different copy modes and ranges. ```python from netfilterqueue import NetfilterQueue, COPY_NONE, COPY_META, COPY_PACKET def metadata_only_callback(pkt): """Callback for COPY_META mode - no payload access.""" print(f"Packet {pkt.id}: hook={pkt.hook}, mark={pkt.mark}") # pkt.get_payload() would raise an error in COPY_META mode pkt.accept() def full_packet_callback(pkt): """Callback for COPY_PACKET mode - full access.""" payload = pkt.get_payload() print(f"Packet {pkt.id}: {len(payload)} bytes") pkt.accept() # COPY_META: Fast, only metadata (use when you don't need payload) # Good for simple accept/drop decisions based on marks nfqueue_meta = NetfilterQueue() nfqueue_meta.bind(1, metadata_only_callback, mode=COPY_META) # COPY_PACKET with limited range: Only copy first N bytes # Useful when you only need headers (e.g., first 40 bytes for IP+TCP headers) nfqueue_headers = NetfilterQueue() nfqueue_headers.bind(2, full_packet_callback, mode=COPY_PACKET, range=40) # COPY_PACKET with full range (default): Complete packet data nfqueue_full = NetfilterQueue() nfqueue_full.bind(3, full_packet_callback, mode=COPY_PACKET, range=65535) # Remember to set up corresponding iptables rules: # sudo iptables -I INPUT -j NFQUEUE --queue-num 1 # For queue 1 # sudo iptables -I INPUT -j NFQUEUE --queue-num 2 # For queue 2 # etc. ``` -------------------------------- ### Packet Object Methods Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst This section details the methods available on the Packet object for manipulating and inspecting network packets. ```APIDOC ## Packet Object Methods ### Description Methods for interacting with and modifying network packet data. ### Methods - **`get_payload()`** - **Description**: Returns the packet's payload as a bytes object. The payload starts with the IP header. Call `retain()` if you need to access the payload after the callback returns. - **Return Type**: `bytes` - **`set_payload(payload)`** - **Description**: Sets the packet payload. This should be called before `accept()` if modifications are intended. Ensure transport-layer checksums and IP lengths are updated accordingly. - **Parameters**: - `payload` (bytes) - The new payload for the packet. - **`get_payload_len()`** - **Description**: Returns the size of the packet's payload. - **Return Type**: `int` - **`set_mark(mark)`** - **Description**: Assigns a 32-bit kernel mark to the packet, usable in future iptables rules. - **Parameters**: - `mark` (int) - The 32-bit mark to set. - **`get_mark()`** - **Description**: Retrieves the mark on the packet. This can be a previously set mark or the original mark if `set_mark()` has not been called. - **Return Type**: `int` - **`get_hw()`** - **Description**: Returns the source hardware address (e.g., MAC address) of the packet as a bytestring. Returns `None` if not available (e.g., for OUTPUT or PREROUTING hooks). - **Return Type**: `bytes` or `None` - **`get_timestamp()`** - **Description**: Returns the kernel reception time of the packet as a Unix timestamp (float). Returns 0.0 for packets from OUTPUT or POSTROUTING hooks. - **Return Type**: `float` - **`retain()`** - **Description**: Allocates a copy of the packet payload for use after the callback returns. Essential if `get_payload()` will be called post-callback. - **`accept()`** - **Description**: Accepts the packet, allowing it to proceed. Packets can be reordered by accepting them in a different sequence. - **`drop()`** - **Description**: Drops the packet, preventing it from proceeding. - **`repeat()`** - **Description**: Restarts processing of the packet from the beginning of its Netfilter hook. Preserves changes made by `set_payload()` or `set_mark()`. ``` -------------------------------- ### NetfilterQueue.run_socket() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Processes packets using a provided socket object, enabling compatibility with libraries like gevent. ```APIDOC ## run_socket(socket) ### Description Allows the use of a Python socket object for packet processing, which is useful for libraries that monkeypatch socket operations for cooperative multitasking. ### Parameters #### Parameters - **socket** (socket.socket) - Required - A Python socket object created from the queue's file descriptor. ``` -------------------------------- ### Access Detailed Packet Attributes Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Retrieves and prints packet metadata including hooks, interface names, and IP protocols. ```python from netfilterqueue import NetfilterQueue, PROTOCOLS import socket # Hook constants for reference HOOKS = {0: 'PREROUTING', 1: 'INPUT', 2: 'FORWARD', 3: 'OUTPUT', 4: 'POSTROUTING'} def detailed_packet_info(pkt): print(f"=== Packet {pkt.id} ===") print(f"Hook: {HOOKS.get(pkt.hook, 'UNKNOWN')} ({pkt.hook})") print(f"HW Protocol: 0x{pkt.hw_protocol:04x}") # e.g., 0x0800 = IPv4 print(f"Original Mark: {pkt.mark}") # Interface information if pkt.indev: try: in_name = socket.if_indextoname(pkt.indev) except OSError: in_name = f"index:{pkt.indev}" print(f"Input Interface: {in_name}") if pkt.outdev: try: out_name = socket.if_indextoname(pkt.outdev) except OSError: out_name = f"index:{pkt.outdev}" print(f"Output Interface: {out_name}") # Physical interfaces (for bridged traffic) if pkt.physindev: print(f"Physical In: index {pkt.physindev}") if pkt.physoutdev: print(f"Physical Out: index {pkt.physoutdev}") # IP Protocol from payload payload = pkt.get_payload() if len(payload) >= 10: ip_proto = payload[9] proto_name = PROTOCOLS.get(ip_proto, f"Unknown({ip_proto})") print(f"IP Protocol: {proto_name}") print() pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, detailed_packet_info) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Process Packets in Non-blocking Mode Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Use select() to poll the queue's file descriptor, allowing for non-blocking packet processing within an event loop. ```python from netfilterqueue import NetfilterQueue import select def handle_packet(pkt): print(f"Processing packet ID: {pkt.id}") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, handle_packet) # Option 1: Blocking mode (simple usage) # nfqueue.run() # Blocks forever until exception # Option 2: Non-blocking mode with select() for integration with event loops fd = nfqueue.get_fd() try: while True: # Wait for data with 1 second timeout readable, _, _ = select.select([fd], [], [], 1.0) if readable: # Process available packets without blocking nfqueue.run(block=False) # Can do other work here between packet processing except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Access Raw Packet Payload with get_payload() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Use get_payload() to retrieve packet data as bytes. Call retain() if the payload is needed after the callback returns. Ensure the payload is long enough before parsing headers. ```python from netfilterqueue import NetfilterQueue import struct import socket def inspect_packet(pkt): payload = pkt.get_payload() payload_len = pkt.get_payload_len() # Parse IPv4 header (first 20 bytes minimum) if len(payload) >= 20: version_ihl = payload[0] version = version_ihl >> 4 ihl = (version_ihl & 0x0F) * 4 # Header length in bytes protocol = payload[9] # Extract source and destination IPs src_ip = socket.inet_ntoa(payload[12:16]) dst_ip = socket.inet_ntoa(payload[16:20]) print(f"IPv{version} {src_ip} -> {dst_ip}") print(f"Protocol: {protocol}, Length: {payload_len} bytes") # For TCP (protocol 6), extract ports if protocol == 6 and len(payload) >= ihl + 4: src_port, dst_port = struct.unpack('!HH', payload[ihl:ihl+4]) print(f"TCP Port: {src_port} -> {dst_port}") pkt.accept() nfqueue = NetfilterQueue() nfqueue.bind(1, inspect_packet) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### NetfilterQueue.bind() Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Binds the NetfilterQueue instance to a specific queue number and defines the callback function for packet processing. ```APIDOC ## bind(queue_num, user_callback, max_len=1024, mode=COPY_PACKET, range=65535) ### Description Binds the queue handler to a specific queue number defined in iptables. Once bound, the specified callback function is invoked for every packet received. ### Parameters #### Parameters - **queue_num** (int) - Required - The queue number matching the --queue-num in the iptables rule. - **user_callback** (callable) - Required - Function to process each packet (receives a Packet object). - **max_len** (int) - Optional - Maximum number of packets in the queue before dropping (default: 1024). - **mode** (int) - Optional - Copy mode: COPY_PACKET, COPY_META, or COPY_NONE. - **range** (int) - Optional - Number of bytes of the packet to copy (default: 65535). ``` -------------------------------- ### Mark Network Traffic with NetfilterQueue Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Mark packets based on protocol and port to classify traffic. Requires iptables rules to utilize the marks. Ensure the NetfilterQueue is bound to a queue number. ```python MARK_HIGH_PRIORITY = 0x10 MARK_LOW_PRIORITY = 0x20 MARK_LOGGED = 0x100 def mark_traffic(pkt): payload = pkt.get_payload() original_mark = pkt.mark # Mark when packet was enqueued current_mark = pkt.get_mark() # Current mark (may have been modified) if len(payload) >= 24: protocol = payload[9] ihl = (payload[0] & 0x0F) * 4 if protocol == 6 and len(payload) >= ihl + 4: # TCP import struct dst_port = struct.unpack('!H', payload[ihl+2:ihl+4])[0] # Mark SSH traffic as high priority if dst_port == 22: pkt.set_mark(MARK_HIGH_PRIORITY) print(f"Packet {pkt.id}: SSH traffic marked HIGH_PRIORITY") # Mark bulk transfer ports as low priority elif dst_port in (20, 21, 8080): pkt.set_mark(MARK_LOW_PRIORITY) print(f"Packet {pkt.id}: Bulk traffic marked LOW_PRIORITY") # Combine marks using bitwise OR else: pkt.set_mark(current_mark | MARK_LOGGED) pkt.accept() # iptables rules to use marks: # sudo iptables -t mangle -A POSTROUTING -m mark --mark 0x10 -j CLASSIFY --set-class 1:10 # sudo iptables -t mangle -A POSTROUTING -m mark --mark 0x20 -j CLASSIFY --set-class 1:20 nfqueue = NetfilterQueue() nfqueue.bind(1, mark_traffic) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` -------------------------------- ### Packet Object Properties Source: https://github.com/oremanj/python-netfilterqueue/blob/master/README.rst This section details the properties available on the Packet object for inspecting packet metadata. ```APIDOC ## Packet Object Properties ### Description Properties providing metadata about the network packet. ### Properties - **`id`** - **Description**: The identifier assigned to this packet by the kernel. Typically starts at 1 for the first packet. - **Type**: `int` - **`hw_protocol`** - **Description**: The link-layer protocol for this packet (e.g., EtherType for IPv4). - **Type**: `int` - **`mark`** - **Description**: The mark assigned to the packet when it was enqueued. This value is not updated if `set_mark()` is called later. - **Type**: `int` - **`hook`** - **Description**: The Netfilter hook (iptables chain) that diverted the packet. Values 0-4 correspond to PREROUTING, INPUT, FORWARD, OUTPUT, and POSTROUTING. - **Type**: `int` - **`indev`** - **Description**: The interface index on which the packet arrived. 0 indicates no interface (locally generated/received). - **Type**: `int` - **`outdev`** - **Description**: The interface index on which the packet is slated to depart. 0 indicates no interface. - **Type**: `int` - **`physindev`** - **Description**: For bridged interfaces, the physical member interface index on which the packet arrived. 0 otherwise. - **Type**: `int` - **`physoutdev`** - **Description**: For bridged interfaces, the physical member interface index on which the packet is slated to depart. 0 otherwise. - **Type**: `int` ``` -------------------------------- ### Packet.retain() - Keeping Packet Data After Callback Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Illustrates the use of `retain()` to preserve packet payload data after the callback function has finished executing. This is crucial for asynchronous processing or when data needs to be accessed later. ```APIDOC ## POST /capture_and_process ### Description Captures packet data using a fast callback that calls `retain()` to keep the payload accessible. Packets are then placed in a queue for background processing, allowing for non-blocking analysis. ### Method POST ### Endpoint /capture_and_process ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **pkt** (object) - Required - The packet object to be processed. - **retain()** (method) - Retains the packet payload for later use. - **get_timestamp()** (method) - Returns the kernel timestamp. - **get_payload()** (method) - Returns the packet payload (safe after retain()). - **mark** (int) - The packet's mark. - **hook** (int) - The packet's hook type. - **accept()** (method) - Accepts the packet. ### Request Example ```python from netfilterqueue import NetfilterQueue import threading import queue packet_queue = queue.Queue() def capture_callback(pkt): pkt.retain() packet_info = { 'id': pkt.id, 'timestamp': pkt.get_timestamp(), 'payload': pkt.get_payload(), 'mark': pkt.mark, 'hook': pkt.hook, } packet_queue.put(packet_info) pkt.accept() def process_packets(): while True: try: info = packet_queue.get(timeout=1.0) payload = info['payload'] if len(payload) >= 20: src_ip = ".".join(str(b) for b in payload[12:16]) dst_ip = ".".join(str(b) for b in payload[16:20]) print(f"Analyzed packet {info['id']}: {src_ip} -> {dst_ip}") except queue.Empty: continue # Start the background processing thread processor_thread = threading.Thread(target=process_packets, daemon=True) processor_thread.start() # Bind the callback and run the queue nfqueue = NetfilterQueue() nfqueue.bind(1, capture_callback) try: nfqueue.run() except KeyboardInterrupt: pass nfqueue.unbind() ``` ### Response #### Success Response (200) Packets are accepted immediately, and their data is queued for background analysis. The analysis results (e.g., source and destination IPs) are printed to standard output. #### Response Example ``` Analyzed packet 98765: 192.168.1.100 -> 8.8.8.8 ``` ``` -------------------------------- ### Retain Packet Data for Asynchronous Processing Source: https://context7.com/oremanj/python-netfilterqueue/llms.txt Use pkt.retain() to keep packet payload accessible after the callback returns, enabling asynchronous processing. This is crucial for complex analysis without blocking the NetfilterQueue. ```python from netfilterqueue import NetfilterQueue import threading import queue # Queue for async packet processing packet_queue = queue.Queue() def capture_callback(pkt): """Fast callback that just captures and accepts packets.""" # Must call retain() to keep payload accessible after callback returns pkt.retain() # Store packet info for later processing packet_info = { 'id': pkt.id, 'timestamp': pkt.get_timestamp(), 'payload': pkt.get_payload(), # Safe to call after retain() 'mark': pkt.mark, 'hook': pkt.hook, } packet_queue.put(packet_info) # Accept immediately to minimize queue blocking pkt.accept() def process_packets(): """Background thread to analyze captured packets.""" while True: try: info = packet_queue.get(timeout=1.0) payload = info['payload'] # Detailed analysis can happen here without blocking the queue if len(payload) >= 20: src_ip = ".".join(str(b) for b in payload[12:16]) dst_ip = ".".join(str(b) for b in payload[16:20]) print(f"Analyzed packet {info['id']}: {src_ip} -> {dst_ip}") except queue.Empty: continue # Example of how to start the processing thread and bind the queue: # processor_thread = threading.Thread(target=process_packets, daemon=True) # processor_thread.start() # # nfqueue = NetfilterQueue() # nfqueue.bind(1, capture_callback) # try: # nfqueue.run() # except KeyboardInterrupt: # pass # nfqueue.unbind() ```