Linux kernel/netfilter incompatibility with ip_queue and IMQ

I use an IMQ device in Linux to shape received traffic. Normally you can only bandwidth-limit or shape traffic you transmit. Looping all traffic through an IMQ device is a great workaround to let you shape traffic you receive as well as transmit. In this case we are limiting bandwidth on a 3G cellular modem connection. The iptables rule to loop received 3G traffic through the imq0 network interface looks like this:

iptables -t mangle -A PREROUTING -i 3g -j IMQ --todev 0

I found today that if the linux kernel module ip_queue was loaded then the IMQ device acted like a roach motel for packets. They check in, but they don't check out. Because tcpdump won't capture packets on an imq interface (at least, it never works for me), I was at quite a loss to troubleshoot this or to figure out where the missing packets were going.

Outbound packets were getting transmitted out the 3G interface just fine, and the replies were generated by the remote system, and they would even show up on the PPP interface (I used ppp's record option to write all PPP traffic to a file and then could open it in Wireshark). For example the TCP 3-way handshake looked like this:

  • SYN ->
  • SYN,ACK <-
  • SYN,ACK <- (retransmit)

Obviously my application was never receiving the SYN,ACK so it didn't transmit an ACK to complete the TCP 3-way handshake. Instead the application would restart the TCP 3-way handshake, and basically all communications were broken. This behavior was true of any network interface for which received traffic was looped through imq0 by the mangle/PREROUTING iptables rule.

The imq0 txqueuelen was long enough. I already know to check that because a too-short txqueuelen can cause dropped packets under heavy system load. You should watch for this especially on PPP interfaces such as PPPoE (PPP over ethernet).  If the default txqueuelen (transmit queue length) is 3 and you have megabit download speeds, the packets will get dropped inside the kernel as it tries to queue them up for transmission. I also checked and the MTU was not a problem.

The solution to my dropped packets on the imq0 interface was to remove/unload the ip_queue kernel modules, and also blacklist it so that it would never load again.

I'm not sure I understand exactly why IMQ and ip_queue conflict with each other but they both provide means of queueing and traffic management/shaping so I conclude that you can use one or the other but not both.