Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add zone filter to conntrack
Browse files Browse the repository at this point in the history
hongliangl committed Apr 7, 2024
1 parent 19057e8 commit 6bcd6d1
Showing 3 changed files with 176 additions and 122 deletions.
71 changes: 49 additions & 22 deletions conntrack_linux.go
Original file line number Diff line number Diff line change
@@ -146,6 +146,7 @@ type ConntrackFlow struct {
Forward ipTuple
Reverse ipTuple
Mark uint32
Zone uint16
TimeStart uint64
TimeStop uint64
TimeOut uint32
@@ -154,7 +155,7 @@ type ConntrackFlow struct {

func (s *ConntrackFlow) String() string {
// conntrack cmd output:
// udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0 labels=0x00000000050012ac4202010000000000
// udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0 labels=0x00000000050012ac4202010000000000 zone=0
// start=2019-07-26 01:26:21.557800506 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=30(sec)
start := time.Unix(0, int64(s.TimeStart))
stop := time.Unix(0, int64(s.TimeStop))
@@ -167,6 +168,9 @@ func (s *ConntrackFlow) String() string {
if len(s.Labels) > 0 {
res += fmt.Sprintf("labels=0x%x ", s.Labels)
}
if s.Zone != 0 {
res += fmt.Sprintf("zone=%d ", s.Zone)
}
res += fmt.Sprintf("start=%v stop=%v timeout=%d(sec)", start, stop, timeout)
return res
}
@@ -318,6 +322,12 @@ func parseConnectionLabels(r *bytes.Reader) (label []byte) {
return
}

func parseConnectionZone(r *bytes.Reader) (zone uint16) {
parseBERaw16(r, &zone)
r.Seek(2, seekCurrent)
return
}

func parseRawData(data []byte) *ConntrackFlow {
s := &ConntrackFlow{}
// First there is the Nfgenmsg header
@@ -369,6 +379,8 @@ func parseRawData(data []byte) *ConntrackFlow {
s.TimeOut = parseTimeOut(reader)
case nl.CTA_STATUS, nl.CTA_USE, nl.CTA_ID:
skipNfAttrValue(reader, l)
case nl.CTA_ZONE:
s.Zone = parseConnectionZone(reader)
default:
skipNfAttrValue(reader, l)
}
@@ -413,18 +425,18 @@ func parseRawData(data []byte) *ConntrackFlow {
type ConntrackFilterType uint8

const (
ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
ConntrackReplySrcIP // --reply-src ip Reply Source IP
ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
ConntrackReplyAnyIP // Match source or destination reply IP
ConntrackOrigSrcPort // --orig-port-src port Source port in original direction
ConntrackOrigDstPort // --orig-port-dst port Destination port in original direction
ConntrackMatchLabels // --label label1,label2 Labels used in entry
ConntrackUnmatchLabels // --label label1,label2 Labels not used in entry
ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instead ConntrackReplyAnyIP
ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
ConntrackReplySrcIP // --reply-src ip Reply Source IP
ConntrackReplyDstIP // --reply-dst ip Reply Destination IP
ConntrackReplyAnyIP // Match source or destination reply IP
ConntrackOrigSrcPort // --orig-port-src port Source port in original direction
ConntrackOrigDstPort // --orig-port-dst port Destination port in original direction
ConntrackMatchLabels // --label label1,label2 Labels used in entry
ConntrackUnmatchLabels // --label label1,label2 Labels not used in entry
ConntrackNatSrcIP = ConntrackReplySrcIP // deprecated use instead ConntrackReplySrcIP
ConntrackNatDstIP = ConntrackReplyDstIP // deprecated use instead ConntrackReplyDstIP
ConntrackNatAnyIP = ConntrackReplyAnyIP // deprecated use instead ConntrackReplyAnyIP
)

type CustomConntrackFilter interface {
@@ -438,6 +450,7 @@ type ConntrackFilter struct {
portFilter map[ConntrackFilterType]uint16
protoFilter uint8
labelFilter map[ConntrackFilterType][][]byte
zoneFilter *uint16
}

// AddIPNet adds a IP subnet to the conntrack filter
@@ -493,14 +506,14 @@ func (f *ConntrackFilter) AddProtocol(proto uint8) error {

// AddLabels adds the provided list (zero or more) of labels to the conntrack filter
// ConntrackFilterType here can be either:
// 1) ConntrackMatchLabels: This matches every flow that has a label value (len(flow.Labels) > 0)
// against the list of provided labels. If `flow.Labels` contains ALL the provided labels
// it is considered a match. This can be used when you want to match flows that contain
// one or more labels.
// 2) ConntrackUnmatchLabels: This matches every flow that has a label value (len(flow.Labels) > 0)
// against the list of provided labels. If `flow.Labels` does NOT contain ALL the provided labels
// it is considered a match. This can be used when you want to match flows that don't contain
// one or more labels.
// 1. ConntrackMatchLabels: This matches every flow that has a label value (len(flow.Labels) > 0)
// against the list of provided labels. If `flow.Labels` contains ALL the provided labels
// it is considered a match. This can be used when you want to match flows that contain
// one or more labels.
// 2. ConntrackUnmatchLabels: This matches every flow that has a label value (len(flow.Labels) > 0)
// against the list of provided labels. If `flow.Labels` does NOT contain ALL the provided labels
// it is considered a match. This can be used when you want to match flows that don't contain
// one or more labels.
func (f *ConntrackFilter) AddLabels(tp ConntrackFilterType, labels [][]byte) error {
if len(labels) == 0 {
return errors.New("Invalid length for provided labels")
@@ -515,10 +528,19 @@ func (f *ConntrackFilter) AddLabels(tp ConntrackFilterType, labels [][]byte) err
return nil
}

// AddZone adds a zone to the conntrack filter
func (f *ConntrackFilter) AddZone(zone uint16) error {
if f.zoneFilter != nil {
return errors.New("Filter attribute already present")
}
f.zoneFilter = &zone
return nil
}

// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
// false otherwise
func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
if len(f.ipNetFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 && len(f.labelFilter) == 0 {
if len(f.ipNetFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 && len(f.labelFilter) == 0 && f.zoneFilter == nil {
// empty filter always not match
return false
}
@@ -529,6 +551,11 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
return false
}

// Conntrack zone filter
if f.zoneFilter != nil && *f.zoneFilter != flow.Zone {
return false
}

match := true

// IP conntrack filter
18 changes: 17 additions & 1 deletion conntrack_test.go
Original file line number Diff line number Diff line change
@@ -381,6 +381,7 @@ func TestConntrackFilter(t *testing.T) {
Protocol: 6,
},
Labels: []byte{0, 0, 0, 0, 3, 4, 61, 141, 207, 170, 2, 0, 0, 0, 0, 0},
Zone: 200,
},
ConntrackFlow{
FamilyType: unix.AF_INET6,
@@ -398,6 +399,7 @@ func TestConntrackFilter(t *testing.T) {
DstPort: 1000,
Protocol: 132,
},
Zone: 200,
})

// Empty filter
@@ -755,6 +757,17 @@ func TestConntrackFilter(t *testing.T) {
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
}

filterV4 = &ConntrackFilter{}
err = filterV4.AddZone(200)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
filterV6 = &ConntrackFilter{}
v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
if v4Match != 2 || v6Match != 0 {
t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
}

}

func TestParseRawData(t *testing.T) {
@@ -943,13 +956,16 @@ func TestParseRawData(t *testing.T) {
/* >>>> CTA_COUNTERS_BYTES */
12, 0, 2, 0,
0, 0, 0, 0, 0, 0, 7, 66,
/* >> CTA_ZONE */
8, 0, 18, 0,
0, 100, 0, 0,
/* >> nested CTA_TIMESTAMP */
16, 0, 20, 128,
/* >>>> CTA_TIMESTAMP_START */
12, 0, 1, 0,
22, 134, 80, 175, 134, 10, 182, 221},
expConntrackFlow: "tcp\t6 src=192.168.0.10 dst=192.168.77.73 sport=42625 dport=3333 packets=11 bytes=1914\t" +
"src=192.168.77.73 dst=192.168.0.10 sport=3333 dport=42625 packets=10 bytes=1858 mark=0x5 " +
"src=192.168.77.73 dst=192.168.0.10 sport=3333 dport=42625 packets=10 bytes=1858 mark=0x5 zone=100 " +
"start=2021-06-07 13:43:50.511990493 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=152(sec)",
},
}
209 changes: 110 additions & 99 deletions nl/conntrack_linux.go
Original file line number Diff line number Diff line change
@@ -18,18 +18,18 @@ var L4ProtoMap = map[uint8]string{
// All the following constants are coming from:
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink_conntrack.h

// enum cntl_msg_types {
// IPCTNL_MSG_CT_NEW,
// IPCTNL_MSG_CT_GET,
// IPCTNL_MSG_CT_DELETE,
// IPCTNL_MSG_CT_GET_CTRZERO,
// IPCTNL_MSG_CT_GET_STATS_CPU,
// IPCTNL_MSG_CT_GET_STATS,
// IPCTNL_MSG_CT_GET_DYING,
// IPCTNL_MSG_CT_GET_UNCONFIRMED,
// enum cntl_msg_types {
// IPCTNL_MSG_CT_NEW,
// IPCTNL_MSG_CT_GET,
// IPCTNL_MSG_CT_DELETE,
// IPCTNL_MSG_CT_GET_CTRZERO,
// IPCTNL_MSG_CT_GET_STATS_CPU,
// IPCTNL_MSG_CT_GET_STATS,
// IPCTNL_MSG_CT_GET_DYING,
// IPCTNL_MSG_CT_GET_UNCONFIRMED,
//
// IPCTNL_MSG_MAX
// };
// IPCTNL_MSG_MAX
// };
const (
IPCTNL_MSG_CT_GET = 1
IPCTNL_MSG_CT_DELETE = 2
@@ -47,36 +47,38 @@ const (
NLA_ALIGNTO uint16 = 4 // #define NLA_ALIGNTO 4
)

// enum ctattr_type {
// CTA_UNSPEC,
// CTA_TUPLE_ORIG,
// CTA_TUPLE_REPLY,
// CTA_STATUS,
// CTA_PROTOINFO,
// CTA_HELP,
// CTA_NAT_SRC,
// enum ctattr_type {
// CTA_UNSPEC,
// CTA_TUPLE_ORIG,
// CTA_TUPLE_REPLY,
// CTA_STATUS,
// CTA_PROTOINFO,
// CTA_HELP,
// CTA_NAT_SRC,
//
// #define CTA_NAT CTA_NAT_SRC /* backwards compatibility */
// CTA_TIMEOUT,
// CTA_MARK,
// CTA_COUNTERS_ORIG,
// CTA_COUNTERS_REPLY,
// CTA_USE,
// CTA_ID,
// CTA_NAT_DST,
// CTA_TUPLE_MASTER,
// CTA_SEQ_ADJ_ORIG,
// CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG,
// CTA_SEQ_ADJ_REPLY,
// CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY,
// CTA_SECMARK, /* obsolete */
// CTA_ZONE,
// CTA_SECCTX,
// CTA_TIMESTAMP,
// CTA_MARK_MASK,
// CTA_LABELS,
// CTA_LABELS_MASK,
// __CTA_MAX
// };
//
// CTA_TIMEOUT,
// CTA_MARK,
// CTA_COUNTERS_ORIG,
// CTA_COUNTERS_REPLY,
// CTA_USE,
// CTA_ID,
// CTA_NAT_DST,
// CTA_TUPLE_MASTER,
// CTA_SEQ_ADJ_ORIG,
// CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG,
// CTA_SEQ_ADJ_REPLY,
// CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY,
// CTA_SECMARK, /* obsolete */
// CTA_ZONE,
// CTA_SECCTX,
// CTA_TIMESTAMP,
// CTA_MARK_MASK,
// CTA_LABELS,
// CTA_LABELS_MASK,
// __CTA_MAX
// };
const (
CTA_TUPLE_ORIG = 1
CTA_TUPLE_REPLY = 2
@@ -88,31 +90,34 @@ const (
CTA_COUNTERS_REPLY = 10
CTA_USE = 11
CTA_ID = 12
CTA_ZONE = 18
CTA_TIMESTAMP = 20
CTA_LABELS = 22
)

// enum ctattr_tuple {
// CTA_TUPLE_UNSPEC,
// CTA_TUPLE_IP,
// CTA_TUPLE_PROTO,
// CTA_TUPLE_ZONE,
// __CTA_TUPLE_MAX
// };
// enum ctattr_tuple {
// CTA_TUPLE_UNSPEC,
// CTA_TUPLE_IP,
// CTA_TUPLE_PROTO,
// CTA_TUPLE_ZONE,
// __CTA_TUPLE_MAX
// };
//
// #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
const (
CTA_TUPLE_IP = 1
CTA_TUPLE_PROTO = 2
)

// enum ctattr_ip {
// CTA_IP_UNSPEC,
// CTA_IP_V4_SRC,
// CTA_IP_V4_DST,
// CTA_IP_V6_SRC,
// CTA_IP_V6_DST,
// __CTA_IP_MAX
// };
// enum ctattr_ip {
// CTA_IP_UNSPEC,
// CTA_IP_V4_SRC,
// CTA_IP_V4_DST,
// CTA_IP_V6_SRC,
// CTA_IP_V6_DST,
// __CTA_IP_MAX
// };
//
// #define CTA_IP_MAX (__CTA_IP_MAX - 1)
const (
CTA_IP_V4_SRC = 1
@@ -121,47 +126,50 @@ const (
CTA_IP_V6_DST = 4
)

// enum ctattr_l4proto {
// CTA_PROTO_UNSPEC,
// CTA_PROTO_NUM,
// CTA_PROTO_SRC_PORT,
// CTA_PROTO_DST_PORT,
// CTA_PROTO_ICMP_ID,
// CTA_PROTO_ICMP_TYPE,
// CTA_PROTO_ICMP_CODE,
// CTA_PROTO_ICMPV6_ID,
// CTA_PROTO_ICMPV6_TYPE,
// CTA_PROTO_ICMPV6_CODE,
// __CTA_PROTO_MAX
// };
// enum ctattr_l4proto {
// CTA_PROTO_UNSPEC,
// CTA_PROTO_NUM,
// CTA_PROTO_SRC_PORT,
// CTA_PROTO_DST_PORT,
// CTA_PROTO_ICMP_ID,
// CTA_PROTO_ICMP_TYPE,
// CTA_PROTO_ICMP_CODE,
// CTA_PROTO_ICMPV6_ID,
// CTA_PROTO_ICMPV6_TYPE,
// CTA_PROTO_ICMPV6_CODE,
// __CTA_PROTO_MAX
// };
//
// #define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
const (
CTA_PROTO_NUM = 1
CTA_PROTO_SRC_PORT = 2
CTA_PROTO_DST_PORT = 3
)

// enum ctattr_protoinfo {
// CTA_PROTOINFO_UNSPEC,
// CTA_PROTOINFO_TCP,
// CTA_PROTOINFO_DCCP,
// CTA_PROTOINFO_SCTP,
// __CTA_PROTOINFO_MAX
// };
// enum ctattr_protoinfo {
// CTA_PROTOINFO_UNSPEC,
// CTA_PROTOINFO_TCP,
// CTA_PROTOINFO_DCCP,
// CTA_PROTOINFO_SCTP,
// __CTA_PROTOINFO_MAX
// };
//
// #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
const (
CTA_PROTOINFO_TCP = 1
)

// enum ctattr_protoinfo_tcp {
// CTA_PROTOINFO_TCP_UNSPEC,
// CTA_PROTOINFO_TCP_STATE,
// CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
// CTA_PROTOINFO_TCP_WSCALE_REPLY,
// CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
// CTA_PROTOINFO_TCP_FLAGS_REPLY,
// __CTA_PROTOINFO_TCP_MAX
// };
// enum ctattr_protoinfo_tcp {
// CTA_PROTOINFO_TCP_UNSPEC,
// CTA_PROTOINFO_TCP_STATE,
// CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
// CTA_PROTOINFO_TCP_WSCALE_REPLY,
// CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
// CTA_PROTOINFO_TCP_FLAGS_REPLY,
// __CTA_PROTOINFO_TCP_MAX
// };
//
// #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
const (
CTA_PROTOINFO_TCP_STATE = 1
@@ -171,15 +179,16 @@ const (
CTA_PROTOINFO_TCP_FLAGS_REPLY = 5
)

// enum ctattr_counters {
// CTA_COUNTERS_UNSPEC,
// CTA_COUNTERS_PACKETS, /* 64bit counters */
// CTA_COUNTERS_BYTES, /* 64bit counters */
// CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */
// CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */
// CTA_COUNTERS_PAD,
// __CTA_COUNTERS_M
// };
// enum ctattr_counters {
// CTA_COUNTERS_UNSPEC,
// CTA_COUNTERS_PACKETS, /* 64bit counters */
// CTA_COUNTERS_BYTES, /* 64bit counters */
// CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */
// CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */
// CTA_COUNTERS_PAD,
// __CTA_COUNTERS_M
// };
//
// #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
const (
CTA_COUNTERS_PACKETS = 1
@@ -195,12 +204,14 @@ const (
)

// /* General form of address family dependent message.
// */
// struct nfgenmsg {
// __u8 nfgen_family; /* AF_xxx */
// __u8 version; /* nfnetlink version */
// __be16 res_id; /* resource id */
// };
//
// */
//
// struct nfgenmsg {
// __u8 nfgen_family; /* AF_xxx */
// __u8 version; /* nfnetlink version */
// __be16 res_id; /* resource id */
// };
type Nfgenmsg struct {
NfgenFamily uint8
Version uint8

0 comments on commit 6bcd6d1

Please sign in to comment.