From 6bcd6d135f110d89772f503c888c72ef10022945 Mon Sep 17 00:00:00 2001 From: Hongliang Liu Date: Sun, 7 Apr 2024 00:00:53 +0800 Subject: [PATCH] Add zone filter to conntrack --- conntrack_linux.go | 71 +++++++++----- conntrack_test.go | 18 +++- nl/conntrack_linux.go | 209 ++++++++++++++++++++++-------------------- 3 files changed, 176 insertions(+), 122 deletions(-) diff --git a/conntrack_linux.go b/conntrack_linux.go index 67b92e22..7bf40cdf 100644 --- a/conntrack_linux.go +++ b/conntrack_linux.go @@ -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 diff --git a/conntrack_test.go b/conntrack_test.go index 29450935..3c8f5685 100644 --- a/conntrack_test.go +++ b/conntrack_test.go @@ -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)", }, } diff --git a/nl/conntrack_linux.go b/nl/conntrack_linux.go index 8659cd13..256a861a 100644 --- a/nl/conntrack_linux.go +++ b/nl/conntrack_linux.go @@ -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,19 +126,20 @@ 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 @@ -141,27 +147,29 @@ const ( 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