Conntrack tales – one thousand and one flows
blog.cloudflare.com
blog.cloudflare.com
In this one I tried to do a quick "what happens when table is full", and ended up with a rather rich and complex blog post. In it I discuss:
- EPERM
- implicitly dropped packets
- weird kernel behaviour on loose=0 and ACK being dropped
- iptable policy counters having their gotchas
- problem of syn floods and conntrac
and more.
It's not that this is super breathtaking, but modern deployments depend on conntrack (I'm looking at you k8s). Even though conntrack is notoriously under-documented and misunderstood. I hope that the linked scripts would allow folks to try to reproduce the weird conntrack behaviour and spread the knowledge of underdocumented corner cases.
This blog post aims to narrow the gap and explain in clear language what you can and what you can not expect from the conntrack layer. Pros and cons.
Also have a look into the conntrack hashsize. The conntrack connection table is a hashtable and the hashsize is how many buckets it has. This affect collisions, memory consumption and cache hits.
Then conntrack timeouts settings, that decide when connections can be released from the tables. Connections can be hold by default for minutes even after close. The most trivial performance test can cycle a million connections in a minute and put down any system using conntrack.
I wouldn't have guessed that cloudflare relied heavily on conntrack. It's not that scalable or easy to use IMO.
The rationale is that iptables need to do indirect jumps, which with spetre mitigations means retpoline which means slow. So... conntrack avoids the indirect jumps and is faster than a couple of indirect jumps that would be taken on the iptables exception path.
But I haven't done the benchmarks so I didn't want to put that in the post.
Anyhow, barring the SYN flood aspect, I think conntrack is fast enough for everybody. The only exceptions might be... dunno... HFT.
What is slower between memory cache miss and indirect jump? I do not know, haven't done a benchmark either.
Calico and other controllers resolve this by calling conntrack(1) in a loop until success or a limit (e.g. 3, 5, or w'ever magic number) is reached. But even then you regularly get one more error than the limit, triggering alarms.
I submitted a patch last year that added a command-line switch for suppressing certain errors, but the netdev mailing-list is high traffic and I never got any feedback. I never expected my patch to be accepted on the first round; not even the approach. I can think of several other alternative ways to address the issue, such as not exiting on error until working through the queue, utilizing exit status codes for reporting the reason, etc. But it's unfortunate I got no feedback whatsoever considering it's a substantial pain point in the wild and a clear (IMO) defect in the implementation--because the error is inevitable and to be expected given the TOCTTOU race.
Of course, if I were writing a controller like Calico I probably wouldn't be shelling out to conntrack(1), but instead making the netlink calls in-process. But I'm not most people, especially in this particular case where everybody else manifestly treats conntrack(1) as the de facto programmatic interface.