ti-mo / conntrack Goto Github PK
View Code? Open in Web Editor NEWPure-Go Conntrack implementation; for humans.
License: MIT License
Pure-Go Conntrack implementation; for humans.
License: MIT License
The Conntrack (per-CPU) stats constants are already in the package, but requesting stats and parsing the messages are not implemented yet.
Hello,
Not sure if it breaks backwards compatibility or not, but I believe that eventWorker
function should close error channel with defer
.
The same applies to events channel. It a bit harder to close correctly when all workers are done, but it's correct way to deal with channels. Writer closes channel.
Hi @ti-mo
In the current implementation updates/gets with a flow that has only the tupleReply defined are blocked by marshal and return an error. This is not required by the netlink API though. Even though for create both are needed, one can update a flow if only the tupleReply information is needed. This is useful when one only cares about the tupleReply state and not the original state.
I have a PR where this seems to work, but I was wondering if there is some other reason for
that choice that I am missing.
Do you see any issues with that? If not, I can create PR.
zoneID can be a netfilter attribute along with mark and mask. This will help in dumping connections in a specific zoneID of conntrack table.
The netip
package was introduced in Go 1.18. Compared to the existing net.IP
type, the netip.Addr
type takes less memory, is immutable, and is comparable so it supports == and can be used as a map key.
// Sizes: (64-bit)
// net.IP: 24 byte slice header + {4, 16} = 28 to 40 bytes
// net.IPAddr: 40 byte slice header + {4, 16} = 44 to 56 bytes + zone length
// netip.Addr: 24 bytes (zone is per-name singleton, shared across all users)
The Flow
type for this library contains 3 fields of type Tuple
, each with 2 IP addresses, that's 6 IP addresses in total. For IPv6 connections, memory usage (when considering IP addresses only, not other fields), could go down from 40*6 = 240B
to 24*6 = 144B
. And in practice, for IPv4 addresses, we would see the same benefit, due to a bug in the unmarshalling code:
Lines 117 to 128 in 7b6dc48
Calling net.IPv4(...)
creates a 16B slice (+ 24B slice header): https://cs.opensource.google/go/go/+/refs/tags/go1.21.1:src/net/ip.go;l=50-53
One thing to keep in mind is that it would be a non-backward compatible change, but:
This is something I am happy to contribute. It will enable programs consuming this library to reduce their memory footprint, as well as start using the netip package more easily.
Hello 👋🏻
When I use this library on kernel 5.10.178-162.673.amzn2.x86_64
I don't get any issues but when I deploy on 5.15.0-1031-aws
library works but sometimes throws the below error and stops capturing the events.
ProtoInfoTCP unmarshal: need (at least) 2 child attributes
I logged packages with this library and conntrack at the same time and I noticed close is shown as an update on conntrack and may be this breaks lib.
[UPDATE] tcp 6 60 SYN_RECV src=XXXX dst=172.17.0.2 sport=54558 dport=8080 src=172.17.0.2 dst=XXXX sport=8080 dport=54558
[UPDATE] tcp 6 10 CLOSE src=XXXX dst=172.17.0.2 sport=54556 dport=8080 src=172.17.0.2 dst=XXXX sport=8080 dport=54556 [ASSURED]
other close events
[DESTROY] tcp 6 CLOSE src=XXXX dst=172.17.0.2 sport=53256 dport=8080 src=172.17.0.2 dst=XXXX sport=8080 dport=53256 [ASSURED]
Conn.Listen() is not yet taken into account in the integration test suite. Write a test suite that subscribes to Create, Update, Destroy, and simulate those events taking place.
It seems that the netlink library slightly changed it's APIs and the master cannot build any more. PR to fix this here: #9
Marshaling Label and LabelMask attributes are needed when Conn.Update
try to change the Label of Conntrack
Here are my changes:
diff --git a/flow.go b/flow.go
index 07617f1..a1b3c35 100644
--- a/flow.go
+++ b/flow.go
@@ -267,6 +267,18 @@ func (f Flow) marshal() ([]netfilter.Attribute, error) {
attrs = append(attrs, f.SynProxy.marshal())
}
+ if len(f.Labels) > 0 {
+ a := netfilter.Attribute{Type: uint16(ctaLabels)}
+ a.Data = f.Labels
+ attrs = append(attrs, a)
+ }
+
+ if len(f.LabelsMask) > 0 {
+ a := netfilter.Attribute{Type: uint16(ctaLabelsMask)}
+ a.Data = f.LabelsMask
+ attrs = append(attrs, a)
+ }
+
I encountered an error in the high concurrency scenario . when use watch method : No buffer space available
Hi,
I recently used this module on a high-bandwidth server and got regular crashes because the netlink socket buffer was filled up. To counter this issue I would like to increase the buffer size of the underlying netlink socket, but conntrack does not provide the necessary methods yet.
I'm willing to implement this feature and send a PR if I get some guidance from the author.
The used netlink package already has a method to change the buffer size: netlink.Conn.SetReadBuffer, so I see two possible solutions:
conntrack.Conn
could itself has a method SetReadBuffer
which in turn calls a new SetReadBuffer
method on netfilter.Conn
, which in turn calls this method on netlink.Conn
.conntrack.Conn
somehow exposes the underlying netlink.Conn
connection so that users can change this (and possibly other) parameters on the netlink connection.What would be the easiest and cleanest possibility to change a netlink socket's buffer size?
When I upgraded my system from Ubuntu 20.04 to 22.04 I got the error "ProtoInfoTCP unmarshal: need (at least) 2 child attributes".
The received netlink msg is below
{{212 unknown(258) 0 0 0} [2 0 0 0 52 0 1 128 20 0 1 128 8 0 1 0 10 0 100 52 8 0 2 0 10 208 240 84 28 0 2 128 5 0 1 0 6 0 0 0 6 0 2 0 237 238 0 0 6 0 3 0 1 187 0 0 52 0 2 128 20 0 1 128 8 0 1 0 10 208 240 84 8 0 2 0 10 208 229 115 28 0 2 128 5 0 1 0 6 0 0 0 6 0 2 0 1 187 0 0 6 0 3 0 237 238 0 0 8 0 12 0 247 95 136 190 8 0 3 0 0 0 3 158 28 0 9 128 12 0 1 0 0 0 0 0 0 0 0 16 12 0 2 0 0 0 0 0 0 0 5 119 28 0 10 128 12 0 1 0 0 0 0 0 0 0 0 14 12 0 2 0 0 0 0 0 0 0 18 39 16 0 4 128 12 0 1 128 5 0 1 0 8 0 0 0]}
netlink msg header: <Subsystem: NFSubsysCTNetlink, Message Type: 2, Family: ProtoIPv4, Version: 0, ResourceID: 0>
Seems this check (https://github.com/ti-mo/conntrack/blob/master/attribute_types.go#L153) failed because the received data has TCP_STATE only.
Is it good to remove the check?
I'm using conntrack (thanks!) for doing some fairly simple monitoring of dropped connections and was looking for ways to reduce garbage / CPU spent in GC for flows where only a few attributes were being called. I wanted to specifically defer full unmarshal in the event that a few basic criteria weren't met (protocol, port, flow status, etc). Would you consider an API addition to conntrack/netfilter that allows walking the message without fully decoding?
As an example of the structure I was using in a fork:
ok, err := netfilter.WalkMessage(
recv[0],
func(h netfilter.Header) (bool, error) {
if h.SubsystemID != netfilter.NFSubsysCTNetlink {
return false, nil
}
if err := event.Type.Unmarshal(h); err != nil {
return false, err
}
switch event.Type {
case conntrack.EventDestroy, conntrack.EventUpdate:
return true, nil
default:
return false, nil
}
},
func(attr netfilter.Attribute) (bool, error) {
switch conntrack.AttributeType(attr.Type) {
case conntrack.CTAStatus:
if event.Type != conntrack.EventDestroy {
return true, nil
}
if err := flow.Unmarshal([]netfilter.Attribute{attr}); err != nil {
return false, err
}
if flow.Status.SeenReply() {
return false, nil
}
case conntrack.CTATupleOrig:
if err := attr.UnmarshalNested(); err != nil {
return false, err
}
if err := flow.Unmarshal([]netfilter.Attribute{attr}); err != nil {
return false, err
}
if flow.TupleOrig.Proto.Protocol != unix.IPPROTO_TCP {
return false, nil
}
}
return true, nil
},
)
and the implementations might look like:
// WalkMessage unmarshals a netlink.Message into a Netfilter Header and Attributes.
func WalkMessage(msg netlink.Message, hdrFn func(Header) (bool, error), attrFn func(Attribute) (bool, error)) (bool, error) {
var h Header
err := h.unmarshal(msg)
if err != nil {
return false, err
}
ok, err := hdrFn(h)
if err != nil {
return false, err
}
if !ok {
return false, nil
}
return WalkAttributes(msg.Data[nfHeaderLen:], attrFn)
}
// WalkAttributes steps through a net link message, invoking fn
// once per Attribute structure.
func WalkAttributes(b []byte, fn func(Attribute) (bool, error)) (bool, error) {
// Obtain a list of parsed netlink attributes possibly holding
// nested Netfilter attributes in their binary Data field.
attrs, err := netlink.UnmarshalAttributes(b)
if err != nil {
return false, errors.Wrap(err, errWrapNetlinkUnmarshalAttrs)
}
for _, nla := range attrs {
// Copy the netlink attribute's fields into the netfilter attribute.
nfa := Attribute{
// Only consider the rightmost 14 bits for Type
Type: nla.Type & ^(uint16(unix.NLA_F_NESTED) | uint16(unix.NLA_F_NET_BYTEORDER)),
Data: nla.Data,
}
// Boolean flags extracted from the two leftmost bits of Type
nfa.Nested = (nla.Type & uint16(unix.NLA_F_NESTED)) != 0
nfa.NetByteOrder = (nla.Type & uint16(unix.NLA_F_NET_BYTEORDER)) != 0
if nfa.NetByteOrder && nfa.Nested {
return false, errInvalidAttributeFlags
}
ok, err := fn(nfa)
if err != nil {
return false, err
}
if !ok {
return false, nil
}
}
return true, nil
}
This does require public access to some of the constants and unmarshal methods to make practical - is that something you'd be willing to accept a PR for? I had planned on raising a similar ask to mdlayher/netlink to allow the attributes to be walked instead of allocated, but even without that I was able to cut allocations by 90% for the core loop.
Per-CPU stats of main conntrack table are now implemented (#3), but Global and Expect aren't yet.
Compiling on MIPS for my OpenWRT router discovered that conntrack flows were not being decoded properly. Attempting to print counters for flows returned by Dump()
resulted in the following error:
need exactly 2 child attributes for attribute type ctaCountersOrig/ctaCountersReply
The issue is that my router has nf_conntrack_helper
set to 'N' rather than default 0. (Why OpenWRT is setting this value rather than '1' I'm not sure):
root@router:~# cat /sys/module/nf_conntrack/parameters/nf_conntrack_helper
N
When enabled, this embeds 2 additional attributes into the Children slice, causing the check of number of children in (*Counter) unmarshal()
to fail. A small change to the function to allow more children and to ignore any of type ctaHelp
allows unmarshaling to complete without error.
I expect I can see the tcp state changes (SYN_SENT/SYN_RECV/ESTABLISHEDFIN_WAIT….etc) when I have a tcp connection. However, when I use iperf3 (iperf3 -c 10.244.2.9 -t 1) to have a tcp connection, I have following results:
In conntrack -E (or event listener), there is only one tcp state (SYN_SENT).
TIME_WAIT/CLOSE/CLOSE_WAIT is supposed to appear in events when a connection ends, but I can’t see any of them when using iperf.
[1685571135.590407] [NEW] tcp 6 120 SYN_SENT src=10.244.1.6 dst=10.244.2.9 sport=49400 dport=5201 [UNREPLIED] src=10.244.2.9 dst=10.244.1.6 sport=5201 dport=49400 zone=65520
[1685571135.590491] [UPDATE] tcp 6 120 src=10.244.1.6 dst=10.244.2.9 sport=49400 dport=5201 [UNREPLIED] src=10.244.2.9 dst=10.244.1.6 sport=5201 dport=49400 mark=3 zone=65520
[1685571135.592658] [NEW] tcp 6 120 SYN_SENT src=10.244.1.6 dst=10.244.2.9 sport=49402 dport=5201 [UNREPLIED] src=10.244.2.9 dst=10.244.1.6 sport=5201 dport=49402 zone=65520
[1685571135.592697] [UPDATE] tcp 6 120 src=10.244.1.6 dst=10.244.2.9 sport=49402 dport=5201 [UNREPLIED] src=10.244.2.9 dst=10.244.1.6 sport=5201 dport=49402 mark=3 zone=65520
In conntrack -L (or DumpFilter), I can see the tcp state is changed to TIME_WAIT, which is supposed to be in conntrack events, but it isn’t.
tcp 6 104 TIME_WAIT src=10.244.1.6 dst=10.244.2.9 sport=52730 dport=5201 packets=18 bytes=1375 src=10.244.2.9 dst=10.244.1.6 sport=5201 dport=52730 packets=18 bytes=1247 [ASSURED] mark=3 zone=65520 delta-time=17 use=1
tcp 6 104 TIME_WAIT src=10.244.1.6 dst=10.244.2.9 sport=52732 dport=5201 packets=39669 bytes=1448346109 src=10.244.2.9 dst=10.244.1.6 sport=5201 dport=52732 packets=24397 bytes=1269164 [ASSURED] mark=3 zone=65520 delta-time=17 use=1
But if I use wget, I can see all the tcp states in events:
[1685572749.172775] [NEW] tcp 6 120 SYN_SENT src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 [UNREPLIED] src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606
[1685572749.212094] [UPDATE] tcp 6 60 SYN_RECV src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606
[1685572749.212144] [UPDATE] tcp 6 432000 ESTABLISHED src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606 [ASSURED]
[1685572749.350889] [UPDATE] tcp 6 120 FIN_WAIT src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606 [ASSURED]
[1685572749.351111] [UPDATE] tcp 6 3600 CLOSE_WAIT src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606 [ASSURED]
[1685572749.385305] [UPDATE] tcp 6 30 LAST_ACK src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606 [ASSURED]
[1685572749.385362] [UPDATE] tcp 6 120 TIME_WAIT src=10.244.1.6 dst=142.250.191.68 sport=44606 dport=80 src=142.250.191.68 dst=172.18.0.3 sport=80 dport=44606 [ASSURED]
I got same results when using Conn.Listen()
I'm wondering the reason why I can’t see the TIME_WAIT event when using iperf?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.