Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 47 additions & 5 deletions conntrack_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -413,6 +437,8 @@ func (s *ConntrackFlow) toNlData() ([]*nl.RtAttr, error) {
// <BEuint16>
// <len, CTA_PROTO_DST_PORT>
// <BEuint16>
// <len, CTA_ZONE>
// <BEuint16>
// <len, CTA_STATUS>
// <uint64>
// <len, CTA_MARK>
Expand All @@ -421,6 +447,8 @@ func (s *ConntrackFlow) toNlData() ([]*nl.RtAttr, error) {
// <BEuint64>
// <len, CTA_LABELS>
// <binary data>
// <len, CTA_LABELS_MASK>
// <binary data>
// <len, NLA_F_NESTED|CTA_PROTOINFO>

// CTA_TUPLE_ORIG
Expand All @@ -447,13 +475,27 @@ 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.
// 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 {
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 {
Expand Down