From 951b05bc05bc05a35ff149542dd007861f6b490b Mon Sep 17 00:00:00 2001 From: eustrain Date: Tue, 16 Dec 2025 06:01:48 +0000 Subject: [PATCH 1/3] feat: implement ct labels mask in toNlData --- conntrack_linux.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/conntrack_linux.go b/conntrack_linux.go index f4cdf081..8c708964 100644 --- a/conntrack_linux.go +++ b/conntrack_linux.go @@ -421,6 +421,8 @@ func (s *ConntrackFlow) toNlData() ([]*nl.RtAttr, error) { // // // + // + // // // CTA_TUPLE_ORIG @@ -447,13 +449,21 @@ func (s *ConntrackFlow) toNlData() ([]*nl.RtAttr, error) { ctTimeout := nl.NewRtAttr(nl.CTA_TIMEOUT, nl.BEUint32Attr(s.TimeOut)) payload = append(payload, ctTupleOrig, ctTupleReply, ctMark, ctTimeout) - // Labels: nil => do not send; 16 zero bytes => clear all labels. + // Labels: nil => do not send; 16 zero bytes => update conntrack labels. if s.Labels != nil { if len(s.Labels) != 16 { return nil, fmt.Errorf("conntrack CTA_LABELS must be 16 bytes, got %d", len(s.Labels)) } ctLabels := nl.NewRtAttr(nl.CTA_LABELS, s.Labels) payload = append(payload, ctLabels) + // Labels Mask: nil => do not send; 16 zero bytes => update conntrack labels with mask. + if s.LabelsMask != nil { + if len(s.LabelsMask) != 16 { + return nil, fmt.Errorf("conntrack CTA_LABELS_MASK must be 16 bytes, got %d", len(s.LabelsMask)) + } + ctLabelsMask := nl.NewRtAttr(nl.CTA_LABELS_MASK, s.LabelsMask) + payload = append(payload, ctLabelsMask) + } } if s.ProtoInfo != nil { From 293b8781a26730fc750650737e2feb5c8ad624ee Mon Sep 17 00:00:00 2001 From: eustrain Date: Tue, 16 Dec 2025 07:26:23 +0000 Subject: [PATCH 2/3] feat: support icmp proto in toNlData --- conntrack_linux.go | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/conntrack_linux.go b/conntrack_linux.go index 8c708964..74da681e 100644 --- a/conntrack_linux.go +++ b/conntrack_linux.go @@ -318,10 +318,34 @@ func (t *IPTuple) toNlData(family uint8) ([]*nl.RtAttr, error) { ctTupleProto := nl.NewRtAttr(unix.NLA_F_NESTED|nl.CTA_TUPLE_PROTO, nil) ctTupleProtoNum := nl.NewRtAttr(nl.CTA_PROTO_NUM, []byte{t.Protocol}) ctTupleProto.AddChild(ctTupleProtoNum) - ctTupleProtoSrcPort := nl.NewRtAttr(nl.CTA_PROTO_SRC_PORT, nl.BEUint16Attr(t.SrcPort)) - ctTupleProto.AddChild(ctTupleProtoSrcPort) - ctTupleProtoDstPort := nl.NewRtAttr(nl.CTA_PROTO_DST_PORT, nl.BEUint16Attr(t.DstPort)) - ctTupleProto.AddChild(ctTupleProtoDstPort) + + // For ICMP/ICMPv6, use ICMP-specific attributes instead of ports + switch t.Protocol { + case unix.IPPROTO_ICMP: + ctTupleProtoICMPID := nl.NewRtAttr(nl.CTA_PROTO_ICMP_ID, nl.BEUint16Attr(t.ICMPID)) + ctTupleProto.AddChild(ctTupleProtoICMPID) + ctTupleProtoICMPType := nl.NewRtAttr(nl.CTA_PROTO_ICMP_TYPE, []byte{t.ICMPType}) + ctTupleProto.AddChild(ctTupleProtoICMPType) + ctTupleProtoICMPCode := nl.NewRtAttr(nl.CTA_PROTO_ICMP_CODE, []byte{t.ICMPCode}) + ctTupleProto.AddChild(ctTupleProtoICMPCode) + case unix.IPPROTO_ICMPV6: + ctTupleProtoICMPV6ID := nl.NewRtAttr(nl.CTA_PROTO_ICMPV6_ID, nl.BEUint16Attr(t.ICMPID)) + ctTupleProto.AddChild(ctTupleProtoICMPV6ID) + ctTupleProtoICMPV6Type := nl.NewRtAttr(nl.CTA_PROTO_ICMPV6_TYPE, []byte{t.ICMPType}) + ctTupleProto.AddChild(ctTupleProtoICMPV6Type) + ctTupleProtoICMPV6Code := nl.NewRtAttr(nl.CTA_PROTO_ICMPV6_CODE, []byte{t.ICMPCode}) + ctTupleProto.AddChild(ctTupleProtoICMPV6Code) + case unix.IPPROTO_TCP, unix.IPPROTO_UDP, unix.IPPROTO_DCCP, unix.IPPROTO_SCTP, unix.IPPROTO_UDPLITE: + // For other protocols (TCP, UDP, etc.), use port attributes + ctTupleProtoSrcPort := nl.NewRtAttr(nl.CTA_PROTO_SRC_PORT, nl.BEUint16Attr(t.SrcPort)) + ctTupleProto.AddChild(ctTupleProtoSrcPort) + ctTupleProtoDstPort := nl.NewRtAttr(nl.CTA_PROTO_DST_PORT, nl.BEUint16Attr(t.DstPort)) + ctTupleProto.AddChild(ctTupleProtoDstPort) + case unix.IPPROTO_IPIP: + fallthrough + default: + // do nothing + } return []*nl.RtAttr{ctTupleIP, ctTupleProto}, nil } From 34c6da1fbb13e9c27fafd12bb24b1074dcd55eca Mon Sep 17 00:00:00 2001 From: eustrain Date: Wed, 17 Dec 2025 03:04:28 +0000 Subject: [PATCH 3/3] feat: support ct zone --- conntrack_linux.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/conntrack_linux.go b/conntrack_linux.go index 74da681e..130f629e 100644 --- a/conntrack_linux.go +++ b/conntrack_linux.go @@ -437,6 +437,8 @@ func (s *ConntrackFlow) toNlData() ([]*nl.RtAttr, error) { // // // + // + // // // // @@ -473,6 +475,12 @@ func (s *ConntrackFlow) toNlData() ([]*nl.RtAttr, error) { ctTimeout := nl.NewRtAttr(nl.CTA_TIMEOUT, nl.BEUint32Attr(s.TimeOut)) payload = append(payload, ctTupleOrig, ctTupleReply, ctMark, ctTimeout) + // Zone is required for matching conntrack entries in the kernel + // The kernel uses zone when looking up conntrack entries: nf_conntrack_find_get(net, &zone, &otuple) + if s.Zone != 0 { + ctZone := nl.NewRtAttr(nl.CTA_ZONE, nl.BEUint16Attr(s.Zone)) + payload = append(payload, ctZone) + } // Labels: nil => do not send; 16 zero bytes => update conntrack labels. if s.Labels != nil { if len(s.Labels) != 16 {