The iptables raw table isn’t about bypassing connection tracking; it’s about preventing connection tracking from happening in the first place, and it’s the only place where you can do that.
Let’s see it in action. Imagine you’ve got a high-throughput application, say, a load balancer or a firewall itself, that’s generating a massive amount of short-lived connections. The kernel’s Netfilter framework, which iptables hooks into, normally tracks every single TCP connection (and some UDP flows) using the conntrack module. This tracking allows for stateful firewalling – only allowing packets that are part of an established connection, for example. But when you have millions of tiny packets, conntrack can become a bottleneck. It has to allocate memory for each new connection, update its state, and then eventually garbage collect it.
The raw table is a special table in iptables that is processed before the conntrack module. This means any rules you put in the raw table are evaluated before the kernel even considers whether a packet belongs to an existing connection or is starting a new one.
Here’s a typical scenario: a high-speed network device that needs to forward packets as fast as possible, without the overhead of connection tracking. We can use the raw table to mark specific traffic that we don’t want conntrack to touch.
Let’s say we have a service running on port 8080 that generates a lot of UDP traffic, and we’ve identified it as a performance issue due to conntrack overhead. We want to tell Netfilter to ignore connection tracking for this specific UDP port.
# First, we'll mark packets destined for UDP port 8080
# The target 'NOTRACK' tells Netfilter not to track this connection.
iptables -t raw -A PREROUTING -p udp --dport 8080 -j NOTRACK
# To verify, we can check the iptables rules
iptables -t raw -L PREROUTING -v -n
The output might look something like this:
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 NOTRACK udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:8080
Here, pkts and bytes show the packet and byte counts that have hit this rule. The NOTRACK target is the key. It explicitly instructs the kernel’s connection tracking system to not create or update an entry for these packets.
Why does this work? Netfilter processes tables in a specific order: raw, mangle, nat, filter. The conntrack module is typically invoked during the mangle or nat table processing (depending on the hook point). By placing a rule with the NOTRACK target in the raw table’s PREROUTING chain (which is processed very early for incoming packets), you intercept the packet before conntrack gets a chance to see it and assign it to a connection. The packet then continues through the rest of the iptables processing and out to its destination, but without any connection tracking state associated with it.
This is particularly useful for UDP traffic, as UDP is connectionless. conntrack still tries to track UDP flows if it sees a pattern of packets between two IPs and ports, but it’s less robust than TCP tracking. For UDP services that are purely transactional and don’t rely on ongoing session state, disabling conntrack can significantly reduce CPU load and improve throughput.
The raw table also has a OUTPUT chain, which applies to locally generated traffic. You can use this similarly:
# For locally generated UDP traffic on port 8080
iptables -t raw -A OUTPUT -p udp --dport 8080 -j NOTRACK
This ensures that even if your own server is sending UDP packets to port 8080, they won’t be tracked.
It’s critical to understand that NOTRACK is a blunt instrument. You lose all stateful firewalling capabilities for traffic matching this rule. You cannot, for example, use iptables rules in the filter table (like --ctstate ESTABLISHED,RELATED) to allow or deny these packets based on connection state because there is no connection state to check. If you need to filter this traffic, you must do so based on other packet attributes (source/destination IP, port, protocol, etc.) before it hits the raw table, or use other mechanisms.
A common mistake is to try and use NOTRACK for TCP traffic. While it will prevent connection tracking, it breaks TCP’s handshake and subsequent communication. TCP relies heavily on its connection state, and by bypassing conntrack, you’re effectively telling the kernel to ignore TCP’s state machine. This will lead to connection failures, retransmissions, and generally broken TCP sessions. The NOTRACK target is almost exclusively useful for UDP traffic or specific, high-volume, stateless protocols where you’ve profiled the system and confirmed conntrack is a bottleneck.
If you’ve applied NOTRACK to a port and are now seeing other, unrelated iptables rules failing for packets that should be tracked, you’ve likely applied NOTRACK too broadly. The next step is to carefully review your iptables rules, especially in the raw table, and ensure NOTRACK is only applied to the specific traffic that genuinely benefits from it.
The next problem you might encounter after successfully bypassing connection tracking for a specific UDP port is dealing with potential denial-of-service attacks against that port, as you can no longer rely on stateful filtering to mitigate them.