From ce927990187cbcae2eb6f8766cb53f281aafafed Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Thu, 5 Dec 2024 13:10:23 +0000 Subject: [PATCH 01/14] feat(ct-metrics): BPF implementation --- pkg/plugin/conntrack/_cprog/conntrack.c | 78 ++++++++++++++++-- pkg/plugin/conntrack/conntrack_bpfel_x86.go | 18 ++-- pkg/plugin/conntrack/conntrack_bpfel_x86.o | Bin 0 -> 1856 bytes pkg/plugin/packetparser/_cprog/packetparser.c | 4 + .../packetparser/packetparser_bpfel_x86.go | 38 ++++++--- .../packetparser/packetparser_bpfel_x86.o | Bin 0 -> 59240 bytes 6 files changed, 111 insertions(+), 27 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index c078ff78ab..dc05f1c64e 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -15,6 +15,21 @@ struct tcpmetadata { __u32 tsecr; // TCP timestamp echo reply }; +struct conntrackmetadata { + __u8 traffic_direction; // This is the inital direction of the connection. It is set to egress if the connection is initiated from the host and ingress otherwise. + /* + bytes_*_count indicates the number of bytes sent and received in the forward and reply direction. + These will be reset to 0 every time an event is reported. + */ + __u64 bytes_forward_count; + __u64 bytes_reply_count; + /* + packets_*_count indicates the number of packets sent and received in the forward and reply direction. + These will be reset to 0 every time an event is reported. + */ + __u64 packets_forward_count; + __u64 packets_reply_count; +}; struct packet { @@ -30,6 +45,7 @@ struct packet __u8 proto; __u8 flags; // For TCP packets, this is the TCP flags. For UDP packets, this is will always be 1 for conntrack purposes. bool is_reply; + struct conntrackmetadata conntrack_metadata; }; @@ -68,6 +84,18 @@ struct ct_entry { * before retina deployment and the SYN packet was not captured. */ bool is_direction_unknown; + /* + bytes_*_count indicates the number of bytes sent and received in the forward and reply direction. + These will be reset to 0 every time an event is reported. + */ + __u64 bytes_forward_count; + __u64 bytes_reply_count; + /* + packets_*_count indicates the number of packets sent and received in the forward and reply direction. + These will be reset to 0 every time an event is reported. + */ + __u64 packets_forward_count; + __u64 packets_reply_count; }; struct { @@ -110,11 +138,11 @@ static __always_inline __u8 _ct_get_traffic_direction(__u8 observation_point) { /** * Create a new TCP connection. + * @arg *p pointer to the packet to be processed. * @arg key The key to be used to create the new connection. - * @arg flags The flags of the packet. * @arg observation_point The point in the network stack where the packet is observed. */ -static __always_inline bool _ct_create_new_tcp_connection(struct ct_v4_key key, __u8 flags, __u8 observation_point) { +static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, struct ct_v4_key key, __u8 observation_point) { struct ct_entry new_value; __builtin_memset(&new_value, 0, sizeof(struct ct_entry)); __u64 now = bpf_mono_now(); @@ -123,10 +151,22 @@ static __always_inline bool _ct_create_new_tcp_connection(struct ct_v4_key key, return false; } new_value.eviction_time = now + CT_SYN_TIMEOUT; - new_value.flags_seen_tx_dir = flags; + new_value.flags_seen_tx_dir = p->flags; new_value.is_direction_unknown = false; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); + new_value.packets_forward_count = 1; + new_value.bytes_forward_count = p->bytes; bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); + // Update packet + p->is_reply = false; + p->traffic_direction = new_value.traffic_direction; + // Update initial conntrack metadata for the connection. + p->conntrack_metadata.bytes_forward_count = new_value.packets_forward_count; + p->conntrack_metadata.packets_forward_count = new_value.bytes_forward_count; + // The initial SYN is captured. Set the traffic direction of the connection. + // This is important for the case where the SYN packet is not captured + // and the connection is created with unknown direction. + p->conntrack_metadata.traffic_direction = new_value.traffic_direction; return true; } @@ -148,10 +188,16 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); + new_value.packets_forward_count = 1; + new_value.bytes_forward_count = p->bytes; bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); // Update packet p->is_reply = false; p->traffic_direction = new_value.traffic_direction; + // Update packet's conntrack metadata. + p->conntrack_metadata.bytes_forward_count = new_value.bytes_forward_count; + p->conntrack_metadata.packets_forward_count = new_value.packets_forward_count; + p->conntrack_metadata.traffic_direction = new_value.traffic_direction; return true; } @@ -165,11 +211,8 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct ct_v4_key key, struct ct_v4_key reverse_key, __u8 observation_point) { // Check if the packet is a SYN packet. if (p->flags & TCP_SYN) { - // Update packet accordingly. - p->is_reply = false; - p->traffic_direction = _ct_get_traffic_direction(observation_point); // Create a new connection with a timeout of CT_SYN_TIMEOUT. - return _ct_create_new_tcp_connection(key, p->flags, observation_point); + return _ct_create_new_tcp_connection(p, key, observation_point); } // The packet is not a SYN packet and the connection corresponding to this packet is not found. @@ -193,13 +236,22 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c p->is_reply = true; new_value.flags_seen_rx_dir = p->flags; new_value.last_report_rx_dir = now; + new_value.bytes_reply_count = p->bytes; + new_value.packets_reply_count = 1; bpf_map_update_elem(&retina_conntrack, &reverse_key, &new_value, BPF_ANY); } else { // Otherwise, the packet is considered as a packet in the send direction. p->is_reply = false; new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; + new_value.bytes_forward_count = p->bytes; + new_value.packets_forward_count = 1; bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); } + // Update packet's conntrack metadata. + p->conntrack_metadata.bytes_forward_count = new_value.bytes_forward_count; + p->conntrack_metadata.bytes_reply_count = new_value.bytes_reply_count; + p->conntrack_metadata.packets_forward_count = new_value.packets_forward_count; + p->conntrack_metadata.packets_reply_count = new_value.packets_reply_count; return true; } @@ -318,6 +370,12 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac // Update the packet accordingly. p->is_reply = false; p->traffic_direction = entry->traffic_direction; + // Update packet count and bytes count on conntrack entry. + WRITE_ONCE(entry->packets_forward_count, READ_ONCE(entry->packets_forward_count) + 1); + WRITE_ONCE(entry->bytes_forward_count, READ_ONCE(entry->bytes_forward_count) + p->bytes); + // Update packet's conntract metadata. + p->conntrack_metadata.bytes_forward_count = entry->bytes_forward_count; + p->conntrack_metadata.packets_forward_count = entry->packets_forward_count; return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_TX, &key); } @@ -333,6 +391,12 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac // Update the packet accordingly. p->is_reply = true; p->traffic_direction = entry->traffic_direction; + // Update packet count and bytes count on conntrack entry. + WRITE_ONCE(entry->packets_reply_count, READ_ONCE(entry->packets_reply_count) + 1); + WRITE_ONCE(entry->bytes_reply_count, READ_ONCE(entry->bytes_reply_count) + p->bytes); + // Update packet's conntract metadata. + p->conntrack_metadata.bytes_reply_count = entry->bytes_reply_count; + p->conntrack_metadata.packets_reply_count = entry->packets_reply_count; return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_RX, &reverse_key); } diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.go b/pkg/plugin/conntrack/conntrack_bpfel_x86.go index 9dede98e2c..7fe4957c18 100644 --- a/pkg/plugin/conntrack/conntrack_bpfel_x86.go +++ b/pkg/plugin/conntrack/conntrack_bpfel_x86.go @@ -13,13 +13,17 @@ import ( ) type conntrackCtEntry struct { - EvictionTime uint32 - LastReportTxDir uint32 - LastReportRxDir uint32 - TrafficDirection uint8 - FlagsSeenTxDir uint8 - FlagsSeenRxDir uint8 - IsDirectionUnknown bool + EvictionTime uint32 + LastReportTxDir uint32 + LastReportRxDir uint32 + TrafficDirection uint8 + FlagsSeenTxDir uint8 + FlagsSeenRxDir uint8 + IsDirectionUnknown bool + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 } type conntrackCtV4Key struct { diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.o b/pkg/plugin/conntrack/conntrack_bpfel_x86.o index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4a2afcd828401cec9c84b820f22099fa65e3f4fe 100644 GIT binary patch literal 1856 zcmbtVy>1gh5FR@Q41}NXPlGJcAcU+WL=i$1mmo(3MIs7mf+8(vdu^X`zB}&Lj$M%g zBq$IMkb;7qiULuEXW$u-ka&PV1>fxLo}J^TVU%y@o7tJ2x!v{VX>DbxSSXN{0`1et zvR)$R&=}X$q4FOiM{(oq&9?$wGLAu!i1Be$s5pT+V-`x_mCPibB7_tBj{>F@8wZb; z;$f)E0Om`lp|1i*0G8_j^9nEn@H!4~+wfKByTEbaF|Z6k%4G}>G^MQGW@?v@LD$V3 z_s2D#q;`1{x}S2ryS$E(;z^Ka0BV({p<e;E&t1bS(C-X)pg#e&x6g)mpuZR%LGJ^n zfB`Ux&76YX0V;;?fzKLF(Dw{?pf>?XnZLnJx$V2uHgjh%*EMteJq}=`c+N69cwXtL z>B-`Szr>_VrssAIF*|Tr-*XT?ZwGh@euC$L*WhQN_kdmS^U$317J!_*n40t+HvV{9 zvirw1$O{w+v9Pwbupu5UKdOoKjn$eE)KH>7C${8}lDHwfh?<FF#a0x?O52)5ai~J9 z(YrZIy&&=0foxXNWqUL8BUjSlG{oH+`5GNJ=29|H@sMQSYbY-agz~zQd>0jC$u)!; zh^7}GaK))h#cr$RHF#R;WNP_tI}wSLSSO7c^O!6z86}8buoZ;c0o8{}CZZL_+iu(x zjj)HUNqy+T9~w{OHnyb7$64=qWIYUh5{r5iz4^J(XZvB$uJAx=7)jz??z?_ZQr8{m zz3^m0krxCWLSm`Bz(w@{n}p(2?YdFIXZH```!SVXK^>*f8teuCKq35w@D(XjRmp)e zYyMu!_xoMpHk&cdm8yw~m0O1%cD0VBUtL^Z;syUxZXjPjaJhAUaHEh~s<JV?-kGVt zg?;??jf*pGjB&GU=HYjT^YcA8Cc^HYF|lt4#etdci6c5ZK<?)X=1jVS82c9M1T%a! zf&3n^aG&h5^{xL3d=o1_L#Q1flkX-!-&@|ELx#B;SuT?gCV$#^IgerM+y7<103Dg- A$^ZZW literal 0 HcmV?d00001 diff --git a/pkg/plugin/packetparser/_cprog/packetparser.c b/pkg/plugin/packetparser/_cprog/packetparser.c index 0243ffadcb..b607e24165 100644 --- a/pkg/plugin/packetparser/_cprog/packetparser.c +++ b/pkg/plugin/packetparser/_cprog/packetparser.c @@ -201,6 +201,10 @@ static void parse(struct __sk_buff *skb, __u8 obs) return; } + // Initialize the conntrack metadata. + struct conntrackmetadata conntrack_metadata; + __builtin_memset(&conntrack_metadata, 0, sizeof(conntrack_metadata)); + p.conntrack_metadata = conntrack_metadata; // Process the packet in ct bool report __attribute__((unused)); diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.go b/pkg/plugin/packetparser/packetparser_bpfel_x86.go index 6d3cb0bfe0..92cbd5cd4d 100644 --- a/pkg/plugin/packetparser/packetparser_bpfel_x86.go +++ b/pkg/plugin/packetparser/packetparser_bpfel_x86.go @@ -13,13 +13,17 @@ import ( ) type packetparserCtEntry struct { - EvictionTime uint32 - LastReportTxDir uint32 - LastReportRxDir uint32 - TrafficDirection uint8 - FlagsSeenTxDir uint8 - FlagsSeenRxDir uint8 - IsDirectionUnknown bool + EvictionTime uint32 + LastReportTxDir uint32 + LastReportRxDir uint32 + TrafficDirection uint8 + FlagsSeenTxDir uint8 + FlagsSeenRxDir uint8 + IsDirectionUnknown bool + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 } type packetparserCtV4Key struct { @@ -49,12 +53,20 @@ type packetparserPacket struct { Tsval uint32 Tsecr uint32 } - ObservationPoint uint8 - TrafficDirection uint8 - Proto uint8 - Flags uint8 - IsReply bool - _ [3]byte + ObservationPoint uint8 + TrafficDirection uint8 + Proto uint8 + Flags uint8 + IsReply bool + _ [3]byte + ConntrackMetadata struct { + TrafficDirection uint8 + _ [7]byte + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 + } } // loadPacketparser returns the embedded CollectionSpec for packetparser. diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.o b/pkg/plugin/packetparser/packetparser_bpfel_x86.o index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2002b5e4f3eb79925e5018887f1a13b0e4c3f313 100644 GIT binary patch literal 59240 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 literal 0 HcmV?d00001 From 9642697d5f66f4f1553b9cc0720c8aeed68d3cc3 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Fri, 6 Dec 2024 13:53:23 +0000 Subject: [PATCH 02/14] feat(ct-metrics): group counters in ct meta and use memcpy --- pkg/plugin/conntrack/_cprog/conntrack.c | 74 +++++++----------- pkg/plugin/conntrack/conntrack_bpfel_x86.go | 26 +++--- pkg/plugin/conntrack/conntrack_bpfel_x86.o | Bin 1856 -> 1928 bytes pkg/plugin/dropreason/kprobe_bpfel_x86.o | Bin 0 -> 24384 bytes pkg/plugin/filter/filter_bpfel_x86.o | Bin 0 -> 2144 bytes pkg/plugin/mock/plugin.go | 29 ++++--- .../packetforward/packetforward_bpfel_x86.o | Bin 0 -> 4504 bytes .../packetparser/packetparser_bpfel_x86.go | 30 ++++--- .../packetparser/packetparser_bpfel_x86.o | Bin 59240 -> 57968 bytes 9 files changed, 76 insertions(+), 83 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index dc05f1c64e..ad17ebb90b 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -7,6 +7,7 @@ #include "compiler.h" #include "bpf_helpers.h" #include "conntrack.h" +#include "string.h" struct tcpmetadata { __u32 seq; // TCP sequence number @@ -16,19 +17,23 @@ struct tcpmetadata { }; struct conntrackmetadata { - __u8 traffic_direction; // This is the inital direction of the connection. It is set to egress if the connection is initiated from the host and ingress otherwise. /* bytes_*_count indicates the number of bytes sent and received in the forward and reply direction. - These will be reset to 0 every time an event is reported. + These values will be based on the conntrack entry. */ __u64 bytes_forward_count; __u64 bytes_reply_count; /* packets_*_count indicates the number of packets sent and received in the forward and reply direction. - These will be reset to 0 every time an event is reported. + These values will be based on the conntrack entry. */ __u64 packets_forward_count; __u64 packets_reply_count; + /* + This is the inital direction of the connection. + It is set to egress if the connection is initiated from the host and ingress otherwise. + */ + __u8 traffic_direction; }; struct packet @@ -84,18 +89,7 @@ struct ct_entry { * before retina deployment and the SYN packet was not captured. */ bool is_direction_unknown; - /* - bytes_*_count indicates the number of bytes sent and received in the forward and reply direction. - These will be reset to 0 every time an event is reported. - */ - __u64 bytes_forward_count; - __u64 bytes_reply_count; - /* - packets_*_count indicates the number of packets sent and received in the forward and reply direction. - These will be reset to 0 every time an event is reported. - */ - __u64 packets_forward_count; - __u64 packets_reply_count; + struct conntrackmetadata conntrack_metadata; }; struct { @@ -154,19 +148,18 @@ static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, stru new_value.flags_seen_tx_dir = p->flags; new_value.is_direction_unknown = false; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); - new_value.packets_forward_count = 1; - new_value.bytes_forward_count = p->bytes; + new_value.conntrack_metadata.packets_forward_count = 1; + new_value.conntrack_metadata.bytes_forward_count = p->bytes; + // The initial SYN is captured. Set the traffic direction of the connection. + // This is important for the case where the SYN packet is not captured + // and the connection is created with unknown direction. + new_value.conntrack_metadata.traffic_direction = new_value.traffic_direction; bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); // Update packet p->is_reply = false; p->traffic_direction = new_value.traffic_direction; // Update initial conntrack metadata for the connection. - p->conntrack_metadata.bytes_forward_count = new_value.packets_forward_count; - p->conntrack_metadata.packets_forward_count = new_value.bytes_forward_count; - // The initial SYN is captured. Set the traffic direction of the connection. - // This is important for the case where the SYN packet is not captured - // and the connection is created with unknown direction. - p->conntrack_metadata.traffic_direction = new_value.traffic_direction; + memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); return true; } @@ -188,16 +181,14 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); - new_value.packets_forward_count = 1; - new_value.bytes_forward_count = p->bytes; + new_value.conntrack_metadata.packets_forward_count = 1; + new_value.conntrack_metadata.bytes_forward_count = p->bytes; bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); // Update packet p->is_reply = false; p->traffic_direction = new_value.traffic_direction; // Update packet's conntrack metadata. - p->conntrack_metadata.bytes_forward_count = new_value.bytes_forward_count; - p->conntrack_metadata.packets_forward_count = new_value.packets_forward_count; - p->conntrack_metadata.traffic_direction = new_value.traffic_direction; + memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; return true; } @@ -236,22 +227,19 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c p->is_reply = true; new_value.flags_seen_rx_dir = p->flags; new_value.last_report_rx_dir = now; - new_value.bytes_reply_count = p->bytes; - new_value.packets_reply_count = 1; + new_value.conntrack_metadata.bytes_reply_count = p->bytes; + new_value.conntrack_metadata.packets_reply_count = 1; bpf_map_update_elem(&retina_conntrack, &reverse_key, &new_value, BPF_ANY); } else { // Otherwise, the packet is considered as a packet in the send direction. p->is_reply = false; new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; - new_value.bytes_forward_count = p->bytes; - new_value.packets_forward_count = 1; + new_value.conntrack_metadata.bytes_forward_count = p->bytes; + new_value.conntrack_metadata.packets_forward_count = 1; bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); } // Update packet's conntrack metadata. - p->conntrack_metadata.bytes_forward_count = new_value.bytes_forward_count; - p->conntrack_metadata.bytes_reply_count = new_value.bytes_reply_count; - p->conntrack_metadata.packets_forward_count = new_value.packets_forward_count; - p->conntrack_metadata.packets_reply_count = new_value.packets_reply_count; + memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); return true; } @@ -371,11 +359,10 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac p->is_reply = false; p->traffic_direction = entry->traffic_direction; // Update packet count and bytes count on conntrack entry. - WRITE_ONCE(entry->packets_forward_count, READ_ONCE(entry->packets_forward_count) + 1); - WRITE_ONCE(entry->bytes_forward_count, READ_ONCE(entry->bytes_forward_count) + p->bytes); + WRITE_ONCE(entry->conntrack_metadata.packets_forward_count, READ_ONCE(entry->conntrack_metadata.packets_forward_count) + 1); + WRITE_ONCE(entry->conntrack_metadata.bytes_forward_count, READ_ONCE(entry->conntrack_metadata.bytes_forward_count) + p->bytes); // Update packet's conntract metadata. - p->conntrack_metadata.bytes_forward_count = entry->bytes_forward_count; - p->conntrack_metadata.packets_forward_count = entry->packets_forward_count; + memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_TX, &key); } @@ -392,11 +379,10 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac p->is_reply = true; p->traffic_direction = entry->traffic_direction; // Update packet count and bytes count on conntrack entry. - WRITE_ONCE(entry->packets_reply_count, READ_ONCE(entry->packets_reply_count) + 1); - WRITE_ONCE(entry->bytes_reply_count, READ_ONCE(entry->bytes_reply_count) + p->bytes); + WRITE_ONCE(entry->conntrack_metadata.packets_reply_count, READ_ONCE(entry->conntrack_metadata.packets_reply_count) + 1); + WRITE_ONCE(entry->conntrack_metadata.bytes_reply_count, READ_ONCE(entry->conntrack_metadata.bytes_reply_count) + p->bytes); // Update packet's conntract metadata. - p->conntrack_metadata.bytes_reply_count = entry->bytes_reply_count; - p->conntrack_metadata.packets_reply_count = entry->packets_reply_count; + memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_RX, &reverse_key); } diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.go b/pkg/plugin/conntrack/conntrack_bpfel_x86.go index 7fe4957c18..a3a3a07951 100644 --- a/pkg/plugin/conntrack/conntrack_bpfel_x86.go +++ b/pkg/plugin/conntrack/conntrack_bpfel_x86.go @@ -13,17 +13,21 @@ import ( ) type conntrackCtEntry struct { - EvictionTime uint32 - LastReportTxDir uint32 - LastReportRxDir uint32 - TrafficDirection uint8 - FlagsSeenTxDir uint8 - FlagsSeenRxDir uint8 - IsDirectionUnknown bool - BytesForwardCount uint64 - BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + EvictionTime uint32 + LastReportTxDir uint32 + LastReportRxDir uint32 + TrafficDirection uint8 + FlagsSeenTxDir uint8 + FlagsSeenRxDir uint8 + IsDirectionUnknown bool + ConntrackMetadata struct { + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 + TrafficDirection uint8 + _ [7]byte + } } type conntrackCtV4Key struct { diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.o b/pkg/plugin/conntrack/conntrack_bpfel_x86.o index 4a2afcd828401cec9c84b820f22099fa65e3f4fe..ebe86751b01c3579f40e19a3906f1010af4680d3 100644 GIT binary patch delta 384 zcmX|*zb^zq6vy9;owXO-T^#mkG{Vx*Q3!il4W(+$EfsewvV}&R?usj%*NIjmLct%f z8c}LhYK6pq!1raBm-)W$=lz(;WaVz<B2eA_{ATG|6Nx_hr$5Fca#r|U#b?usB)lrp zOwLM7HWmN|9-v+YkHRIk3J$@|f;-@of-~?lGz1ZPJqY2|kS=ppc%MQdUe_fx&Mocp zt{zrCXl~~QJNX*XEdYOzUAhO4z&p?rcnqG2z!Pw}^a>%$^}M8a)HoGsPCkBVUoFN- zyPY1}_#kTSr*_Y#mYPX&NYRGm|LNb52b^t=oFjg7>gtX^od?}8d}Uq~vvrsC5(;<C Tgz`&nxHbNE15Jjf24{Z(Yxh7t delta 316 zcmeC+KfpIZgVAB4rZ;B=69WSX@0wWqpOu?|fyH2QBV%%^0wV*1AOi!#0U$PlvKfH1 z2bApqq+@^>2si|QL?VRE2%<QErldpVgn;~9s2ot7!2yV&fJtQXL?&@wpkla)7*Iq# zvH_?bqJ@E>706%($~ypYCy)(P&d>nF{Xn)bkUjv!vw>_;APv&A7>I#@PaMK!*aGAO z5kE*!VzVH#IOF69mH@7#{QMk-cqbq^c{xkrWN}tW#*WE`tm~P6vP^!&dW_L)@=7*y ZrWZgCn;p{+mdQ%&c1$~1CMU700RT^TEHnTB diff --git a/pkg/plugin/dropreason/kprobe_bpfel_x86.o b/pkg/plugin/dropreason/kprobe_bpfel_x86.o index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a0098ede38482561f4961ac10fc98c79ff9e3792 100644 GIT binary patch literal 24384 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 literal 0 HcmV?d00001 diff --git a/pkg/plugin/filter/filter_bpfel_x86.o b/pkg/plugin/filter/filter_bpfel_x86.o index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bf6c879c816f34c58c39471bdebc664f91e53879 100644 GIT binary patch literal 2144 zcmbtVJ8KkC6h6C&W;I4lREUwtfFM!ACL{>45H<^mh$-T$(89@NcQzR^nH^?lL*fI2 zAXwUnji6X*Wf{9ziG@GFA7CL^T4*7x-*@NU$?g~s@gv_ok9*F!=RS7wU}kPMpUa6s zPJYOaWxOIU_w48;rIw@@k(HreEt*?MvLGu%Kb5`xie8bPyvWa1t2LM@`UrD$ZEOFQ zwr}WOXcJEp@z3LnZ#fy%S?{n0c}lMZcfJdQHgnz3t<)4w3ECF=dqKT*69<n%elNsv z5V73_NDJ%%iL)Se3mgN10%Oi8RsN{8AO3POSKyi#bsT$So<_dUXwumy<23SzjFyQ} z7lS$U-8~L@12lQJpmU!k@HV&)+ytxO0q{K-fCoV)-GQLL1pOg>m^zHX)Pw9h0{aQb zb9e><g`*grD^=7@V{hHBU?cv(0OT+f{HZPUCvn1F#S3Dw?dhvmr*GQVF5Q~3uiu=X zv29s(!prW4gpphG)_pgSsuMeiY)(!{GiZ2Afm^kb<mS|lr?Tus5^sdAtRQ>M@tZD7 z*)`u;YCv1J-5`!Uw;`by1YWQtksEt~W7j-Cb|djZr&^6<v0nGZE_3wj^_6BQ#n@d( zr<E|OFO7BN6f0tBG~%dPiLIn}3msS&th?!~SF=VI!<x;0lB%}ryQ?FoJIak(r;&K( zjNF$ZA{!!n^Oim{o-;WIE`S8z(Hjuwt8fe>?~Fdab>c1f0UTZyRg1R42o&01z`lD) zUD_v5F!vbBU(ls~Rh>b6cl-q{D7iC?LLF#}M>`59G4`k3O7b?~V{Zia#=U&ho1{*% z9zmT`C6F-od}5~dLdFg=cApd@*H126Y31v)Mc#aJ7acd1J`LYrTebOa@GPZ>M-V%U zkQ=K?vL33)T)8}MPw4gjS4^XT`k-YBo!1{VC*#-0mU<aRAHQ|&C~3poi<v7JMN8)L zTT;@t5g&Q*0N)?M?_VA?`Nq$0IBR70@ESg1JQ3+=@{ObY8)tl!pSAIwi?%_xm3po~ z+pYi4yY$bqGV7cE55bo`Qx^ZEH?&Ek4cYbizhJlZzv{qwoy+s%cQ(8JBz#?StvbXe gm|{xbf3~-*eF+(vBT_Zy_4@0&qPZipzWLw#8+^aPQ~&?~ literal 0 HcmV?d00001 diff --git a/pkg/plugin/mock/plugin.go b/pkg/plugin/mock/plugin.go index 888d9267dd..403a472c3c 100644 --- a/pkg/plugin/mock/plugin.go +++ b/pkg/plugin/mock/plugin.go @@ -5,11 +5,11 @@ // // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/microsoft/retina/pkg/plugin/ (interfaces: Plugin) +// Source: github.com/microsoft/retina/pkg/plugin (interfaces: Plugin) // // Generated by this command: // -// mockgen -destination=mock/plugin.go -copyright_file=../lib/ignore_headers.txt -package=plugin github.com/microsoft/retina/pkg/plugin/ Plugin +// mockgen -destination=mock/plugin.go -copyright_file=../lib/ignore_headers.txt -package=plugin github.com/microsoft/retina/pkg/plugin Plugin // // Package plugin is a generated GoMock package. @@ -27,7 +27,6 @@ import ( type MockPlugin struct { ctrl *gomock.Controller recorder *MockPluginMockRecorder - isgomock struct{} } // MockPluginMockRecorder is the mock recorder for MockPlugin. @@ -48,31 +47,31 @@ func (m *MockPlugin) EXPECT() *MockPluginMockRecorder { } // Compile mocks base method. -func (m *MockPlugin) Compile(ctx context.Context) error { +func (m *MockPlugin) Compile(arg0 context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Compile", ctx) + ret := m.ctrl.Call(m, "Compile", arg0) ret0, _ := ret[0].(error) return ret0 } // Compile indicates an expected call of Compile. -func (mr *MockPluginMockRecorder) Compile(ctx any) *gomock.Call { +func (mr *MockPluginMockRecorder) Compile(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compile", reflect.TypeOf((*MockPlugin)(nil).Compile), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compile", reflect.TypeOf((*MockPlugin)(nil).Compile), arg0) } // Generate mocks base method. -func (m *MockPlugin) Generate(ctx context.Context) error { +func (m *MockPlugin) Generate(arg0 context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Generate", ctx) + ret := m.ctrl.Call(m, "Generate", arg0) ret0, _ := ret[0].(error) return ret0 } // Generate indicates an expected call of Generate. -func (mr *MockPluginMockRecorder) Generate(ctx any) *gomock.Call { +func (mr *MockPluginMockRecorder) Generate(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Generate", reflect.TypeOf((*MockPlugin)(nil).Generate), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Generate", reflect.TypeOf((*MockPlugin)(nil).Generate), arg0) } // Init mocks base method. @@ -118,17 +117,17 @@ func (mr *MockPluginMockRecorder) SetupChannel(arg0 any) *gomock.Call { } // Start mocks base method. -func (m *MockPlugin) Start(ctx context.Context) error { +func (m *MockPlugin) Start(arg0 context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Start", ctx) + ret := m.ctrl.Call(m, "Start", arg0) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. -func (mr *MockPluginMockRecorder) Start(ctx any) *gomock.Call { +func (mr *MockPluginMockRecorder) Start(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPlugin)(nil).Start), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPlugin)(nil).Start), arg0) } // Stop mocks base method. diff --git a/pkg/plugin/packetforward/packetforward_bpfel_x86.o b/pkg/plugin/packetforward/packetforward_bpfel_x86.o index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5f175500269f66b9fa5956441e65ccf97025395a 100644 GIT binary patch literal 4504 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 EKhp6_`2YX_ literal 0 HcmV?d00001 diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.go b/pkg/plugin/packetparser/packetparser_bpfel_x86.go index 92cbd5cd4d..440a4ccc59 100644 --- a/pkg/plugin/packetparser/packetparser_bpfel_x86.go +++ b/pkg/plugin/packetparser/packetparser_bpfel_x86.go @@ -13,17 +13,21 @@ import ( ) type packetparserCtEntry struct { - EvictionTime uint32 - LastReportTxDir uint32 - LastReportRxDir uint32 - TrafficDirection uint8 - FlagsSeenTxDir uint8 - FlagsSeenRxDir uint8 - IsDirectionUnknown bool - BytesForwardCount uint64 - BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + EvictionTime uint32 + LastReportTxDir uint32 + LastReportRxDir uint32 + TrafficDirection uint8 + FlagsSeenTxDir uint8 + FlagsSeenRxDir uint8 + IsDirectionUnknown bool + ConntrackMetadata struct { + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 + TrafficDirection uint8 + _ [7]byte + } } type packetparserCtV4Key struct { @@ -60,12 +64,12 @@ type packetparserPacket struct { IsReply bool _ [3]byte ConntrackMetadata struct { - TrafficDirection uint8 - _ [7]byte BytesForwardCount uint64 BytesReplyCount uint64 PacketsForwardCount uint64 PacketsReplyCount uint64 + TrafficDirection uint8 + _ [7]byte } } diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.o b/pkg/plugin/packetparser/packetparser_bpfel_x86.o index 2002b5e4f3eb79925e5018887f1a13b0e4c3f313..d57a7d4a51f4f6e9a5e390dd25324dccdabe35fb 100644 GIT binary patch literal 57968 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 J^J|da{{<rJ-t+(f literal 59240 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 From 15d85f6f0709c7c60bc824edff71c8a48f44e7ae Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Mon, 9 Dec 2024 09:11:19 +0000 Subject: [PATCH 03/14] feat(ct-metrics): use __builtin_memcpy --- pkg/plugin/conntrack/_cprog/conntrack.c | 11 +++++------ pkg/plugin/conntrack/conntrack_bpfel_x86.o | Bin 1928 -> 0 bytes .../packetparser/packetparser_bpfel_x86.o | Bin 57968 -> 0 bytes 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index ad17ebb90b..ef2005d9fa 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -7,7 +7,6 @@ #include "compiler.h" #include "bpf_helpers.h" #include "conntrack.h" -#include "string.h" struct tcpmetadata { __u32 seq; // TCP sequence number @@ -159,7 +158,7 @@ static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, stru p->is_reply = false; p->traffic_direction = new_value.traffic_direction; // Update initial conntrack metadata for the connection. - memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); + __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); return true; } @@ -188,7 +187,7 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c p->is_reply = false; p->traffic_direction = new_value.traffic_direction; // Update packet's conntrack metadata. - memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; + __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; return true; } @@ -239,7 +238,7 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); } // Update packet's conntrack metadata. - memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); + __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); return true; } @@ -362,7 +361,7 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac WRITE_ONCE(entry->conntrack_metadata.packets_forward_count, READ_ONCE(entry->conntrack_metadata.packets_forward_count) + 1); WRITE_ONCE(entry->conntrack_metadata.bytes_forward_count, READ_ONCE(entry->conntrack_metadata.bytes_forward_count) + p->bytes); // Update packet's conntract metadata. - memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); + __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_TX, &key); } @@ -382,7 +381,7 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac WRITE_ONCE(entry->conntrack_metadata.packets_reply_count, READ_ONCE(entry->conntrack_metadata.packets_reply_count) + 1); WRITE_ONCE(entry->conntrack_metadata.bytes_reply_count, READ_ONCE(entry->conntrack_metadata.bytes_reply_count) + p->bytes); // Update packet's conntract metadata. - memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); + __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_RX, &reverse_key); } diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.o b/pkg/plugin/conntrack/conntrack_bpfel_x86.o index ebe86751b01c3579f40e19a3906f1010af4680d3..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 1928 zcmbtVJ#Q015FI-P4hVtpO#_yoKoPPMh@uFkxCA*;q)0>|O%T#@KCk0b&UfZ+?bsDA zC@7$T5+o`bgi`SXl8&B=ii(OKkP_bP?fIO8RE+X=-ptPI%<Zl>&#SA;#X^BnDbN9B zR_Y~sc`W0$rZ!psjLhOj*@s^Rx@sKni$shcS)t+>=8Rb=ftQCS@f0DP*nbi*t)y}A zXel0tx&~mrbPoC^Z~|bN0+^S98GzTZf%}H9K|cUa15be!0Ag7f9%@R%dQTGDIt5)b zbKD=-e3sbOICMARdUtspBgF}j7XW;vdFa<b+UGmN%h0=q+tB+!+S@n7o6tWEZ$lpd zXMr9tj?J8e9soDMAr|*{E8%HRr776FU$-&00Dhh$A!~-);5EYs;620l!1n>}i4X1- z0I@D$c$4q~f3po3DPBq$9X`|KWc6h6$`LWil3A}G;YfX-K=@ABz%KY1=mGEr{37%o z1N<^HCw&DV6MP?;e#d}6-a37MSo4067bp^9aeaMpQ#@LETooIeYgHl0RiZm9wq>8< z$Q53j>ak+QRy&N8wl!%-p$f4^=iXiF1hKaj$a*<hwlg!Ax#A|LA#OhNxlJeHQZi6c zpJdl_l@|s=c`Zr4gNl*l8bb9%-HQ&nqQs>lr_u0So|Zb98osj?i&#pmlf;a8OqLgC z38E8h2jNaYZWy2q$K4h!shql_9IAz(PhyG3;cPb9S27ljFxqjVx^Tk|wz8&u=-Tfa zPa6wLm5)okqmkH}x!G)Q{4m%m^FXTKmc&QVb^MN`mebQ4=E<1aUJ!T)iKOxZ2PMrX zP^IOxV?N-ch40X0a!qxRz7JrZ`G*eSw}|hKMHMA`$}IVJCExG1gj26aID4wZDpF1j zy5G_|l73}rW0@EHPq~A9{ZQuCS>zuEq?)MV$i0sW+YEj~N5vjBMmT@}sPOy6`S~7f z6O`_sG5PH~BsQ#kPaM$X0rECiFlW*NV$wHVCzxT{d_TW^EZisOru9?*b@;|se}_;5 gU?{f9&-a$MXOUs9#xPeClRs^|oX1G(r~ecG0I1CM-v9sr diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.o b/pkg/plugin/packetparser/packetparser_bpfel_x86.o index d57a7d4a51f4f6e9a5e390dd25324dccdabe35fb..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 57968 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 J^J|da{{<rJ-t+(f From 87848a5d83572e500b6d87989f26406f9036362d Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Mon, 9 Dec 2024 16:06:28 +0000 Subject: [PATCH 04/14] feat(ct-metrics): process metrics within IFDEF --- .../retina/templates/agent/configmap.yaml | 1 + .../helm/retina/templates/configmap.yaml | 1 + .../controller/helm/retina/values.yaml | 1 + pkg/config/config.go | 1 + pkg/plugin/conntrack/_cprog/conntrack.c | 92 +++++++++++++------ pkg/plugin/conntrack/_cprog/dynamic.h | 1 + pkg/plugin/conntrack/conntrack_linux.go | 21 +++++ pkg/plugin/conntrack/types_linux.go | 5 +- pkg/plugin/packetparser/_cprog/dynamic.h | 1 + pkg/plugin/packetparser/_cprog/packetparser.c | 12 ++- pkg/plugin/packetparser/packetparser_linux.go | 15 ++- 11 files changed, 115 insertions(+), 36 deletions(-) create mode 100644 pkg/plugin/conntrack/_cprog/dynamic.h diff --git a/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml b/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml index 0e80cefed2..704cf5a9c5 100644 --- a/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml +++ b/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml @@ -108,6 +108,7 @@ data: metricsIntervalDuration: {{ .Values.metricsIntervalDuration }} enableTelemetry: {{ .Values.enableTelemetry }} enablePodLevel: {{ .Values.enablePodLevel }} + enableConntrackMetrics: {{ .Values.enableConntrackMetrics }} remoteContext: {{ .Values.remoteContext }} enableAnnotations: {{ .Values.enableAnnotations }} bypassLookupIPOfInterest: {{ .Values.bypassLookupIPOfInterest }} diff --git a/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml b/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml index 6218bc62b3..a53b8537a6 100644 --- a/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml +++ b/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml @@ -19,6 +19,7 @@ data: metricsIntervalDuration: {{ .Values.metricsIntervalDuration }} enableTelemetry: {{ .Values.enableTelemetry }} enablePodLevel: {{ .Values.enablePodLevel }} + enableConntrackMetrics: {{ .Values.enableConntrackMetrics }} remoteContext: {{ .Values.remoteContext }} enableAnnotations: {{ .Values.enableAnnotations }} bypassLookupIPOfInterest: {{ .Values.bypassLookupIPOfInterest }} diff --git a/deploy/legacy/manifests/controller/helm/retina/values.yaml b/deploy/legacy/manifests/controller/helm/retina/values.yaml index f95da03629..7dee8c519e 100644 --- a/deploy/legacy/manifests/controller/helm/retina/values.yaml +++ b/deploy/legacy/manifests/controller/helm/retina/values.yaml @@ -35,6 +35,7 @@ image: # Overrides the image tag whose default is the chart appVersion. tag: "v0.0.2" +enableConntrackMetrics: false enablePodLevel: false remoteContext: false enableAnnotations: false diff --git a/pkg/config/config.go b/pkg/config/config.go index 8ee4798ea2..5a5098456e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -61,6 +61,7 @@ type Config struct { EnableTelemetry bool `yaml:"enableTelemetry"` EnableRetinaEndpoint bool `yaml:"enableRetinaEndpoint"` EnablePodLevel bool `yaml:"enablePodLevel"` + EnableConntrackMetrics bool `yaml:"enableConntrackMetrics"` RemoteContext bool `yaml:"remoteContext"` EnableAnnotations bool `yaml:"enableAnnotations"` BypassLookupIPOfInterest bool `yaml:"bypassLookupIPOfInterest"` diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index ef2005d9fa..7e24d2fa3a 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -7,6 +7,7 @@ #include "compiler.h" #include "bpf_helpers.h" #include "conntrack.h" +#include "dynamic.h" struct tcpmetadata { __u32 seq; // TCP sequence number @@ -147,18 +148,24 @@ static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, stru new_value.flags_seen_tx_dir = p->flags; new_value.is_direction_unknown = false; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); - new_value.conntrack_metadata.packets_forward_count = 1; - new_value.conntrack_metadata.bytes_forward_count = p->bytes; - // The initial SYN is captured. Set the traffic direction of the connection. - // This is important for the case where the SYN packet is not captured - // and the connection is created with unknown direction. - new_value.conntrack_metadata.traffic_direction = new_value.traffic_direction; - bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); + + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + new_value.conntrack_metadata.packets_forward_count = 1; + new_value.conntrack_metadata.bytes_forward_count = p->bytes; + // The initial SYN is captured. Set the traffic direction of the connection. + // This is important for the case where the SYN packet is not captured + // and the connection is created with unknown direction. + new_value.conntrack_metadata.traffic_direction = new_value.traffic_direction; + // Update initial conntrack metadata for the connection. + __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); + #endif + #endif // CONNTRACK_METRICS + // Update packet p->is_reply = false; p->traffic_direction = new_value.traffic_direction; - // Update initial conntrack metadata for the connection. - __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); + bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); return true; } @@ -180,14 +187,19 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); - new_value.conntrack_metadata.packets_forward_count = 1; - new_value.conntrack_metadata.bytes_forward_count = p->bytes; - bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + new_value.conntrack_metadata.packets_forward_count = 1; + new_value.conntrack_metadata.bytes_forward_count = p->bytes; + // Update packet's conntrack metadata. + __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; + #endif + #endif // CONNTRACK_METRICS + // Update packet p->is_reply = false; p->traffic_direction = new_value.traffic_direction; - // Update packet's conntrack metadata. - __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; + bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); return true; } @@ -226,19 +238,31 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c p->is_reply = true; new_value.flags_seen_rx_dir = p->flags; new_value.last_report_rx_dir = now; - new_value.conntrack_metadata.bytes_reply_count = p->bytes; - new_value.conntrack_metadata.packets_reply_count = 1; + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + new_value.conntrack_metadata.bytes_reply_count = p->bytes; + new_value.conntrack_metadata.packets_reply_count = 1; + #endif + #endif // CONNTRACK_METRICS bpf_map_update_elem(&retina_conntrack, &reverse_key, &new_value, BPF_ANY); } else { // Otherwise, the packet is considered as a packet in the send direction. p->is_reply = false; new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; - new_value.conntrack_metadata.bytes_forward_count = p->bytes; - new_value.conntrack_metadata.packets_forward_count = 1; + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + new_value.conntrack_metadata.bytes_forward_count = p->bytes; + new_value.conntrack_metadata.packets_forward_count = 1; + #endif + #endif // CONNTRACK_METRICS bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); } - // Update packet's conntrack metadata. - __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + // Update packet's conntrack metadata. + __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); + #endif + #endif // CONNTRACK_METRICS return true; } @@ -357,11 +381,15 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac // Update the packet accordingly. p->is_reply = false; p->traffic_direction = entry->traffic_direction; - // Update packet count and bytes count on conntrack entry. - WRITE_ONCE(entry->conntrack_metadata.packets_forward_count, READ_ONCE(entry->conntrack_metadata.packets_forward_count) + 1); - WRITE_ONCE(entry->conntrack_metadata.bytes_forward_count, READ_ONCE(entry->conntrack_metadata.bytes_forward_count) + p->bytes); - // Update packet's conntract metadata. - __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + // Update packet count and bytes count on conntrack entry. + WRITE_ONCE(entry->conntrack_metadata.packets_forward_count, READ_ONCE(entry->conntrack_metadata.packets_forward_count) + 1); + WRITE_ONCE(entry->conntrack_metadata.bytes_forward_count, READ_ONCE(entry->conntrack_metadata.bytes_forward_count) + p->bytes); + // Update packet's conntract metadata. + __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); + #endif + #endif // CONNTRACK_METRICS return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_TX, &key); } @@ -377,11 +405,15 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac // Update the packet accordingly. p->is_reply = true; p->traffic_direction = entry->traffic_direction; - // Update packet count and bytes count on conntrack entry. - WRITE_ONCE(entry->conntrack_metadata.packets_reply_count, READ_ONCE(entry->conntrack_metadata.packets_reply_count) + 1); - WRITE_ONCE(entry->conntrack_metadata.bytes_reply_count, READ_ONCE(entry->conntrack_metadata.bytes_reply_count) + p->bytes); - // Update packet's conntract metadata. - __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + // Update packet count and bytes count on conntrack entry. + WRITE_ONCE(entry->conntrack_metadata.packets_reply_count, READ_ONCE(entry->conntrack_metadata.packets_reply_count) + 1); + WRITE_ONCE(entry->conntrack_metadata.bytes_reply_count, READ_ONCE(entry->conntrack_metadata.bytes_reply_count) + p->bytes); + // Update packet's conntract metadata. + __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); + #endif + #endif // CONNTRACK_METRICS return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_RX, &reverse_key); } diff --git a/pkg/plugin/conntrack/_cprog/dynamic.h b/pkg/plugin/conntrack/_cprog/dynamic.h new file mode 100644 index 0000000000..965f8f2bea --- /dev/null +++ b/pkg/plugin/conntrack/_cprog/dynamic.h @@ -0,0 +1 @@ +#define CONNTRACK_METRICS 0 \ No newline at end of file diff --git a/pkg/plugin/conntrack/conntrack_linux.go b/pkg/plugin/conntrack/conntrack_linux.go index 0f67b427ef..2ed6db731b 100644 --- a/pkg/plugin/conntrack/conntrack_linux.go +++ b/pkg/plugin/conntrack/conntrack_linux.go @@ -6,11 +6,15 @@ package conntrack import ( "context" + "fmt" + "path" + "runtime" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/rlimit" "github.com/microsoft/retina/internal/ktime" + "github.com/microsoft/retina/pkg/loader" "github.com/microsoft/retina/pkg/log" plugincommon "github.com/microsoft/retina/pkg/plugin/common" _ "github.com/microsoft/retina/pkg/plugin/conntrack/_cprog" // nolint // This is needed so cprog is included when vendoring @@ -66,6 +70,23 @@ func New() (*Conntrack, error) { return ct, nil } +// Generate dynamic header file for conntrack eBPF program. +func GenerateDynamic(ctx context.Context, conntrackMetrics int) error { + // Get absolute path to this file during runtime. + _, filename, _, ok := runtime.Caller(0) + if !ok { + return errors.New("unable to get absolute path for conntrack file") + } + dir := path.Dir(filename) + dynamicHeaderPath := fmt.Sprintf("%s/%s/%s", dir, bpfSourceDir, dynamicHeaderFileName) + st := fmt.Sprintf("#define CONNTRACK_METRICS %d\n", conntrackMetrics) + err := loader.WriteFile(ctx, dynamicHeaderPath, st) + if err != nil { + return errors.Wrap(err, "failed to write conntrack dynamic header") + } + return nil +} + // Run starts the Conntrack garbage collection loop. func (ct *Conntrack) Run(ctx context.Context) error { ticker := time.NewTicker(ct.gcFrequency) diff --git a/pkg/plugin/conntrack/types_linux.go b/pkg/plugin/conntrack/types_linux.go index 637fcade4a..63fa7e90a9 100644 --- a/pkg/plugin/conntrack/types_linux.go +++ b/pkg/plugin/conntrack/types_linux.go @@ -9,7 +9,10 @@ import ( ) const ( - defaultGCFrequency = 15 * time.Second + defaultGCFrequency = 15 * time.Second + bpfSourceDir = "_cprog" + bpfSourceFileName = "conntrack.c" + dynamicHeaderFileName = "dynamic.h" ) type Conntrack struct { diff --git a/pkg/plugin/packetparser/_cprog/dynamic.h b/pkg/plugin/packetparser/_cprog/dynamic.h index ecadc42211..ac14b29aab 100644 --- a/pkg/plugin/packetparser/_cprog/dynamic.h +++ b/pkg/plugin/packetparser/_cprog/dynamic.h @@ -1,2 +1,3 @@ #define BYPASS_LOOKUP_IP_OF_INTEREST 0 #define DATA_AGGREGATION_LEVEL 0 +#define CONNTRACK_METRICS 0 diff --git a/pkg/plugin/packetparser/_cprog/packetparser.c b/pkg/plugin/packetparser/_cprog/packetparser.c index b607e24165..3603d83a73 100644 --- a/pkg/plugin/packetparser/_cprog/packetparser.c +++ b/pkg/plugin/packetparser/_cprog/packetparser.c @@ -201,10 +201,14 @@ static void parse(struct __sk_buff *skb, __u8 obs) return; } - // Initialize the conntrack metadata. - struct conntrackmetadata conntrack_metadata; - __builtin_memset(&conntrack_metadata, 0, sizeof(conntrack_metadata)); - p.conntrack_metadata = conntrack_metadata; + #ifdef CONNTRACK_METRICS + #if CONNTRACK_METRICS == 1 + // Initialize conntrack metadata in packet struct. + struct conntrackmetadata conntrack_metadata; + __builtin_memset(&conntrack_metadata, 0, sizeof(conntrack_metadata)); + p.conntrack_metadata = conntrack_metadata; + #endif + #endif // CONNTRACK_METRICS // Process the packet in ct bool report __attribute__((unused)); diff --git a/pkg/plugin/packetparser/packetparser_linux.go b/pkg/plugin/packetparser/packetparser_linux.go index 5a8dd690dd..28106cf08e 100644 --- a/pkg/plugin/packetparser/packetparser_linux.go +++ b/pkg/plugin/packetparser/packetparser_linux.go @@ -32,6 +32,7 @@ import ( "github.com/microsoft/retina/pkg/log" "github.com/microsoft/retina/pkg/metrics" plugincommon "github.com/microsoft/retina/pkg/plugin/common" + "github.com/microsoft/retina/pkg/plugin/conntrack" _ "github.com/microsoft/retina/pkg/plugin/lib/_amd64" // nolint _ "github.com/microsoft/retina/pkg/plugin/lib/_arm64" // nolint _ "github.com/microsoft/retina/pkg/plugin/lib/common/libbpf/_include/asm" // nolint @@ -82,8 +83,20 @@ func (p *packetParser) Generate(ctx context.Context) error { p.l.Info("bypassing lookup IP of interest") bypassLookupIPOfInterest = 1 } + conntrackMetrics := 0 + // Check if packetparser has Conntrack metrics enabled. + if p.cfg.EnableConntrackMetrics { + p.l.Info("conntrack metrics enabled") + conntrackMetrics = 1 + // Generate dynamic header for conntrack. + err := conntrack.GenerateDynamic(ctx, conntrackMetrics) + if err != nil { + return errors.Wrap(err, "failed to generate dynamic header for conntrack") + } + p.l.Info("Conntrack header generated") + } p.l.Info("data aggregation level", zap.String("level", p.cfg.DataAggregationLevel.String())) - st := fmt.Sprintf("#define BYPASS_LOOKUP_IP_OF_INTEREST %d\n#define DATA_AGGREGATION_LEVEL %d\n", bypassLookupIPOfInterest, p.cfg.DataAggregationLevel) + st := fmt.Sprintf("#define BYPASS_LOOKUP_IP_OF_INTEREST %d\n#define DATA_AGGREGATION_LEVEL %d\n#define CONNTRACK_METRICS %d\n", bypassLookupIPOfInterest, p.cfg.DataAggregationLevel, conntrackMetrics) err := loader.WriteFile(ctx, dynamicHeaderPath, st) if err != nil { return errors.Wrap(err, "failed to write dynamic header") From acc3ffd4fbff6f79e198446dec9d000bc5cf4034 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Mon, 9 Dec 2024 16:32:17 +0000 Subject: [PATCH 05/14] feat(ct-metrics): update TestPacketParseGenerate --- .../packetparser/packetparser_linux_test.go | 67 +++++++++++++------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/pkg/plugin/packetparser/packetparser_linux_test.go b/pkg/plugin/packetparser/packetparser_linux_test.go index 1a7a1bc350..47a82f6f38 100644 --- a/pkg/plugin/packetparser/packetparser_linux_test.go +++ b/pkg/plugin/packetparser/packetparser_linux_test.go @@ -49,6 +49,9 @@ var ( EnablePodLevel: true, DataAggregationLevel: kcfg.High, } + cfgConntrackMetricsEnabled = &kcfg.Config{ + EnableConntrackMetrics: true, + } ) func TestCleanAll(t *testing.T) { @@ -533,30 +536,50 @@ func TestPacketParseGenerate(t *testing.T) { currDir := path.Dir(filename) dynamicHeaderPath := fmt.Sprintf("%s/%s/%s", currDir, bpfSourceDir, dynamicHeaderFileName) - // Instantiate the packetParser struct with a mocked logger and context. - p := &packetParser{ - cfg: cfgPodLevelEnabled, - l: log.Logger().Named(name), - } - ctx := context.Background() - - // Call the Generate function and check if it returns an error. - if err := p.Generate(ctx); err != nil { - t.Fatalf("failed to generate PacketParser header: %v", err) - } - - // Verify that the dynamic header file was created in the expected location and contains the expected contents. - if _, err := os.Stat(dynamicHeaderPath); os.IsNotExist(err) { - t.Fatalf("dynamic header file does not exist: %v", err) + tests := []struct { + name string + cfg *kcfg.Config + expectedContents string + }{ + { + name: "PodLevelEnabled", + cfg: cfgPodLevelEnabled, + expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define DATA_AGGREGATION_LEVEL 0\n#define CONNTRACK_METRICS 0\n", + }, + { + name: "ConntrackMetricsEnabled", + cfg: cfgConntrackMetricsEnabled, + expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 0\n#define DATA_AGGREGATION_LEVEL 0\n#define CONNTRACK_METRICS 1\n", + }, } - expectedContents := "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define DATA_AGGREGATION_LEVEL 0\n" - actualContents, err := os.ReadFile(dynamicHeaderPath) - if err != nil { - t.Fatalf("failed to read dynamic header file: %v", err) - } - if string(actualContents) != expectedContents { - t.Errorf("unexpected dynamic header file contents: got %q, want %q", string(actualContents), expectedContents) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Instantiate the packetParser struct with a mocked logger and context. + p := &packetParser{ + cfg: tt.cfg, + l: log.Logger().Named(name), + } + ctx := context.Background() + + // Call the Generate function and check if it returns an error. + if err := p.Generate(ctx); err != nil { + t.Fatalf("failed to generate PacketParser header: %v", err) + } + + // Verify that the dynamic header file was created in the expected location and contains the expected contents. + if _, err := os.Stat(dynamicHeaderPath); os.IsNotExist(err) { + t.Fatalf("dynamic header file does not exist: %v", err) + } + + actualContents, err := os.ReadFile(dynamicHeaderPath) + if err != nil { + t.Fatalf("failed to read dynamic header file: %v", err) + } + if string(actualContents) != tt.expectedContents { + t.Errorf("unexpected dynamic header file contents: got %q, want %q", string(actualContents), tt.expectedContents) + } + }) } } From ed2706cfe2c8a0368210984ec8c3b6d7f8f0d703 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Tue, 10 Dec 2024 15:41:00 +0000 Subject: [PATCH 06/14] feat(ct-metrics): remove traffic_direction from conntrackmetadata --- pkg/plugin/conntrack/_cprog/conntrack.c | 9 --------- pkg/plugin/conntrack/conntrack_bpfel_x86.go | 2 -- .../packetforward/packetforward_bpfel_x86.o | Bin 4504 -> 0 bytes .../packetparser/packetparser_bpfel_x86.go | 4 ---- 4 files changed, 15 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index 7e24d2fa3a..5c4f3b15db 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -29,11 +29,6 @@ struct conntrackmetadata { */ __u64 packets_forward_count; __u64 packets_reply_count; - /* - This is the inital direction of the connection. - It is set to egress if the connection is initiated from the host and ingress otherwise. - */ - __u8 traffic_direction; }; struct packet @@ -153,10 +148,6 @@ static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, stru #if CONNTRACK_METRICS == 1 new_value.conntrack_metadata.packets_forward_count = 1; new_value.conntrack_metadata.bytes_forward_count = p->bytes; - // The initial SYN is captured. Set the traffic direction of the connection. - // This is important for the case where the SYN packet is not captured - // and the connection is created with unknown direction. - new_value.conntrack_metadata.traffic_direction = new_value.traffic_direction; // Update initial conntrack metadata for the connection. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); #endif diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.go b/pkg/plugin/conntrack/conntrack_bpfel_x86.go index a3a3a07951..964ea4179e 100644 --- a/pkg/plugin/conntrack/conntrack_bpfel_x86.go +++ b/pkg/plugin/conntrack/conntrack_bpfel_x86.go @@ -25,8 +25,6 @@ type conntrackCtEntry struct { BytesReplyCount uint64 PacketsForwardCount uint64 PacketsReplyCount uint64 - TrafficDirection uint8 - _ [7]byte } } diff --git a/pkg/plugin/packetforward/packetforward_bpfel_x86.o b/pkg/plugin/packetforward/packetforward_bpfel_x86.o index 5f175500269f66b9fa5956441e65ccf97025395a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 4504 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 EKhp6_`2YX_ diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.go b/pkg/plugin/packetparser/packetparser_bpfel_x86.go index 440a4ccc59..8843327040 100644 --- a/pkg/plugin/packetparser/packetparser_bpfel_x86.go +++ b/pkg/plugin/packetparser/packetparser_bpfel_x86.go @@ -25,8 +25,6 @@ type packetparserCtEntry struct { BytesReplyCount uint64 PacketsForwardCount uint64 PacketsReplyCount uint64 - TrafficDirection uint8 - _ [7]byte } } @@ -68,8 +66,6 @@ type packetparserPacket struct { BytesReplyCount uint64 PacketsForwardCount uint64 PacketsReplyCount uint64 - TrafficDirection uint8 - _ [7]byte } } From 14f219c67ad4894630299f1ad0b2acb1fb9cf23a Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Wed, 11 Dec 2024 09:11:35 +0000 Subject: [PATCH 07/14] feat(ct-metrics): simplify preprocessor for CONNTRACK_METRICS --- pkg/plugin/conntrack/_cprog/conntrack.c | 14 -------------- pkg/plugin/conntrack/_cprog/dynamic.h | 3 ++- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index 5c4f3b15db..01ed2d223c 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -145,12 +145,10 @@ static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, stru new_value.traffic_direction = _ct_get_traffic_direction(observation_point); #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 new_value.conntrack_metadata.packets_forward_count = 1; new_value.conntrack_metadata.bytes_forward_count = p->bytes; // Update initial conntrack metadata for the connection. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif #endif // CONNTRACK_METRICS // Update packet @@ -179,12 +177,10 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c new_value.last_report_tx_dir = now; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 new_value.conntrack_metadata.packets_forward_count = 1; new_value.conntrack_metadata.bytes_forward_count = p->bytes; // Update packet's conntrack metadata. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; - #endif #endif // CONNTRACK_METRICS // Update packet @@ -230,10 +226,8 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c new_value.flags_seen_rx_dir = p->flags; new_value.last_report_rx_dir = now; #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 new_value.conntrack_metadata.bytes_reply_count = p->bytes; new_value.conntrack_metadata.packets_reply_count = 1; - #endif #endif // CONNTRACK_METRICS bpf_map_update_elem(&retina_conntrack, &reverse_key, &new_value, BPF_ANY); } else { // Otherwise, the packet is considered as a packet in the send direction. @@ -241,18 +235,14 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 new_value.conntrack_metadata.bytes_forward_count = p->bytes; new_value.conntrack_metadata.packets_forward_count = 1; - #endif #endif // CONNTRACK_METRICS bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); } #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 // Update packet's conntrack metadata. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif #endif // CONNTRACK_METRICS return true; } @@ -373,13 +363,11 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac p->is_reply = false; p->traffic_direction = entry->traffic_direction; #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 // Update packet count and bytes count on conntrack entry. WRITE_ONCE(entry->conntrack_metadata.packets_forward_count, READ_ONCE(entry->conntrack_metadata.packets_forward_count) + 1); WRITE_ONCE(entry->conntrack_metadata.bytes_forward_count, READ_ONCE(entry->conntrack_metadata.bytes_forward_count) + p->bytes); // Update packet's conntract metadata. __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif #endif // CONNTRACK_METRICS return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_TX, &key); } @@ -397,13 +385,11 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac p->is_reply = true; p->traffic_direction = entry->traffic_direction; #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 // Update packet count and bytes count on conntrack entry. WRITE_ONCE(entry->conntrack_metadata.packets_reply_count, READ_ONCE(entry->conntrack_metadata.packets_reply_count) + 1); WRITE_ONCE(entry->conntrack_metadata.bytes_reply_count, READ_ONCE(entry->conntrack_metadata.bytes_reply_count) + p->bytes); // Update packet's conntract metadata. __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif #endif // CONNTRACK_METRICS return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_RX, &reverse_key); } diff --git a/pkg/plugin/conntrack/_cprog/dynamic.h b/pkg/plugin/conntrack/_cprog/dynamic.h index 965f8f2bea..80abbd931f 100644 --- a/pkg/plugin/conntrack/_cprog/dynamic.h +++ b/pkg/plugin/conntrack/_cprog/dynamic.h @@ -1 +1,2 @@ -#define CONNTRACK_METRICS 0 \ No newline at end of file +// Place holder header file that will be replaced by the actual header file during runtime +// DO NOT DELETE From c20e4acb577a18b086a3700b9d745ddd3de36207 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Wed, 11 Dec 2024 11:12:21 +0000 Subject: [PATCH 08/14] feat(ct-metrics): add generated arm64 bpf go wrappers --- pkg/plugin/conntrack/conntrack_bpfel_arm64.go | 6 +++++ .../packetparser/packetparser_bpfel_arm64.go | 24 ++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/pkg/plugin/conntrack/conntrack_bpfel_arm64.go b/pkg/plugin/conntrack/conntrack_bpfel_arm64.go index b96504c3e0..94ff4f8b3f 100644 --- a/pkg/plugin/conntrack/conntrack_bpfel_arm64.go +++ b/pkg/plugin/conntrack/conntrack_bpfel_arm64.go @@ -20,6 +20,12 @@ type conntrackCtEntry struct { FlagsSeenTxDir uint8 FlagsSeenRxDir uint8 IsDirectionUnknown bool + ConntrackMetadata struct { + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 + } } type conntrackCtV4Key struct { diff --git a/pkg/plugin/packetparser/packetparser_bpfel_arm64.go b/pkg/plugin/packetparser/packetparser_bpfel_arm64.go index 30c6bb43d3..15b5d3e23c 100644 --- a/pkg/plugin/packetparser/packetparser_bpfel_arm64.go +++ b/pkg/plugin/packetparser/packetparser_bpfel_arm64.go @@ -20,6 +20,12 @@ type packetparserCtEntry struct { FlagsSeenTxDir uint8 FlagsSeenRxDir uint8 IsDirectionUnknown bool + ConntrackMetadata struct { + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 + } } type packetparserCtV4Key struct { @@ -49,12 +55,18 @@ type packetparserPacket struct { Tsval uint32 Tsecr uint32 } - ObservationPoint uint8 - TrafficDirection uint8 - Proto uint8 - Flags uint8 - IsReply bool - _ [3]byte + ObservationPoint uint8 + TrafficDirection uint8 + Proto uint8 + Flags uint8 + IsReply bool + _ [3]byte + ConntrackMetadata struct { + BytesForwardCount uint64 + BytesReplyCount uint64 + PacketsForwardCount uint64 + PacketsReplyCount uint64 + } } // loadPacketparser returns the embedded CollectionSpec for packetparser. From dc27a722cf6e1cd8d38963d1192f5a9c17afe4af Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Wed, 11 Dec 2024 11:44:55 +0000 Subject: [PATCH 09/14] feat(ct-metrics): use uint32 for packet counters --- pkg/plugin/conntrack/_cprog/conntrack.c | 4 ++-- pkg/plugin/conntrack/conntrack_bpfel_arm64.go | 4 ++-- pkg/plugin/conntrack/conntrack_bpfel_x86.go | 4 ++-- pkg/plugin/packetparser/packetparser_bpfel_arm64.go | 8 ++++---- pkg/plugin/packetparser/packetparser_bpfel_x86.go | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index 01ed2d223c..040fa35394 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -27,8 +27,8 @@ struct conntrackmetadata { packets_*_count indicates the number of packets sent and received in the forward and reply direction. These values will be based on the conntrack entry. */ - __u64 packets_forward_count; - __u64 packets_reply_count; + __u32 packets_forward_count; + __u32 packets_reply_count; }; struct packet diff --git a/pkg/plugin/conntrack/conntrack_bpfel_arm64.go b/pkg/plugin/conntrack/conntrack_bpfel_arm64.go index 94ff4f8b3f..efb93738a6 100644 --- a/pkg/plugin/conntrack/conntrack_bpfel_arm64.go +++ b/pkg/plugin/conntrack/conntrack_bpfel_arm64.go @@ -23,8 +23,8 @@ type conntrackCtEntry struct { ConntrackMetadata struct { BytesForwardCount uint64 BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + PacketsForwardCount uint32 + PacketsReplyCount uint32 } } diff --git a/pkg/plugin/conntrack/conntrack_bpfel_x86.go b/pkg/plugin/conntrack/conntrack_bpfel_x86.go index 964ea4179e..baa46bcbfa 100644 --- a/pkg/plugin/conntrack/conntrack_bpfel_x86.go +++ b/pkg/plugin/conntrack/conntrack_bpfel_x86.go @@ -23,8 +23,8 @@ type conntrackCtEntry struct { ConntrackMetadata struct { BytesForwardCount uint64 BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + PacketsForwardCount uint32 + PacketsReplyCount uint32 } } diff --git a/pkg/plugin/packetparser/packetparser_bpfel_arm64.go b/pkg/plugin/packetparser/packetparser_bpfel_arm64.go index 15b5d3e23c..ebfa05e37f 100644 --- a/pkg/plugin/packetparser/packetparser_bpfel_arm64.go +++ b/pkg/plugin/packetparser/packetparser_bpfel_arm64.go @@ -23,8 +23,8 @@ type packetparserCtEntry struct { ConntrackMetadata struct { BytesForwardCount uint64 BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + PacketsForwardCount uint32 + PacketsReplyCount uint32 } } @@ -64,8 +64,8 @@ type packetparserPacket struct { ConntrackMetadata struct { BytesForwardCount uint64 BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + PacketsForwardCount uint32 + PacketsReplyCount uint32 } } diff --git a/pkg/plugin/packetparser/packetparser_bpfel_x86.go b/pkg/plugin/packetparser/packetparser_bpfel_x86.go index 8843327040..36de41247b 100644 --- a/pkg/plugin/packetparser/packetparser_bpfel_x86.go +++ b/pkg/plugin/packetparser/packetparser_bpfel_x86.go @@ -23,8 +23,8 @@ type packetparserCtEntry struct { ConntrackMetadata struct { BytesForwardCount uint64 BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + PacketsForwardCount uint32 + PacketsReplyCount uint32 } } @@ -64,8 +64,8 @@ type packetparserPacket struct { ConntrackMetadata struct { BytesForwardCount uint64 BytesReplyCount uint64 - PacketsForwardCount uint64 - PacketsReplyCount uint64 + PacketsForwardCount uint32 + PacketsReplyCount uint32 } } From 004663aa14d5fd9ad6e94ef851b2009d9e450091 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Fri, 13 Dec 2024 09:04:45 +0000 Subject: [PATCH 10/14] feat(ct-metrics): add unit test for conntrack GenerateDynamic --- pkg/plugin/conntrack/conntrack_linux.go | 15 ++- pkg/plugin/conntrack/conntrack_linux_test.go | 92 +++++++++++++++++++ pkg/plugin/packetparser/packetparser_linux.go | 3 +- 3 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 pkg/plugin/conntrack/conntrack_linux_test.go diff --git a/pkg/plugin/conntrack/conntrack_linux.go b/pkg/plugin/conntrack/conntrack_linux.go index 2ed6db731b..ab7496b880 100644 --- a/pkg/plugin/conntrack/conntrack_linux.go +++ b/pkg/plugin/conntrack/conntrack_linux.go @@ -70,15 +70,20 @@ func New() (*Conntrack, error) { return ct, nil } -// Generate dynamic header file for conntrack eBPF program. -func GenerateDynamic(ctx context.Context, conntrackMetrics int) error { +// Build dynamic header path +func BuildDynamicHeaderPath() string { // Get absolute path to this file during runtime. _, filename, _, ok := runtime.Caller(0) if !ok { - return errors.New("unable to get absolute path for conntrack file") + return "" } - dir := path.Dir(filename) - dynamicHeaderPath := fmt.Sprintf("%s/%s/%s", dir, bpfSourceDir, dynamicHeaderFileName) + currDir := path.Dir(filename) + return fmt.Sprintf("%s/%s/%s", currDir, bpfSourceDir, dynamicHeaderFileName) +} + +// Generate dynamic header file for conntrack eBPF program. +func GenerateDynamic(ctx context.Context, dynamicHeaderPath string, conntrackMetrics int) error { + st := fmt.Sprintf("#define CONNTRACK_METRICS %d\n", conntrackMetrics) err := loader.WriteFile(ctx, dynamicHeaderPath, st) if err != nil { diff --git a/pkg/plugin/conntrack/conntrack_linux_test.go b/pkg/plugin/conntrack/conntrack_linux_test.go new file mode 100644 index 0000000000..1e16c1c316 --- /dev/null +++ b/pkg/plugin/conntrack/conntrack_linux_test.go @@ -0,0 +1,92 @@ +package conntrack + +import ( + "context" + "fmt" + "os" + "path" + "runtime" + "testing" +) + +func TestBuildDynamicHeaderPath(t *testing.T) { + tests := []struct { + name string + expectedPath string + }{ + { + name: "ExpectedPath", + expectedPath: fmt.Sprintf("%s/%s/%s", path.Dir(getCurrentFilePath(t)), bpfSourceDir, dynamicHeaderFileName), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actualPath := BuildDynamicHeaderPath() + if actualPath != tt.expectedPath { + t.Errorf("unexpected dynamic header path: got %q, want %q", actualPath, tt.expectedPath) + } + }) + } +} + +func TestGenerateDynamic(t *testing.T) { + tests := []struct { + name string + conntrackMetrics int + expectedContents string + }{ + { + name: "ConntrackMetricsEnabled", + conntrackMetrics: 1, + expectedContents: "#define CONNTRACK_METRICS 1\n", + }, + { + name: "ConntrackMetricsDisabled", + conntrackMetrics: 0, + expectedContents: "#define CONNTRACK_METRICS 0\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a temporary directory + tempDir, err := os.MkdirTemp("", "conntrack_test") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + // Clean up the temporary directory after the test completes + defer os.RemoveAll(tempDir) + + // Override the dynamicHeaderPath to use the temporary directory + dynamicHeaderPath := path.Join(tempDir, dynamicHeaderFileName) + + // Call the GenerateDynamic function and check if it returns an error. + ctx := context.Background() + if err := GenerateDynamic(ctx, dynamicHeaderPath, tt.conntrackMetrics); err != nil { + t.Fatalf("failed to generate dynamic header: %v", err) + } + + // Verify that the dynamic header file was created in the expected location and contains the expected contents. + if _, err := os.Stat(dynamicHeaderPath); os.IsNotExist(err) { + t.Fatalf("dynamic header file does not exist: %v", err) + } + + actualContents, err := os.ReadFile(dynamicHeaderPath) + if err != nil { + t.Fatalf("failed to read dynamic header file: %v", err) + } + if string(actualContents) != tt.expectedContents { + t.Errorf("unexpected dynamic header file contents: got %q, want %q", string(actualContents), tt.expectedContents) + } + }) + } +} + +func getCurrentFilePath(t *testing.T) string { + _, filename, _, ok := runtime.Caller(1) + if !ok { + t.Fatal("failed to determine test file path") + } + return filename +} diff --git a/pkg/plugin/packetparser/packetparser_linux.go b/pkg/plugin/packetparser/packetparser_linux.go index 28106cf08e..dd4bc46567 100644 --- a/pkg/plugin/packetparser/packetparser_linux.go +++ b/pkg/plugin/packetparser/packetparser_linux.go @@ -89,7 +89,8 @@ func (p *packetParser) Generate(ctx context.Context) error { p.l.Info("conntrack metrics enabled") conntrackMetrics = 1 // Generate dynamic header for conntrack. - err := conntrack.GenerateDynamic(ctx, conntrackMetrics) + dynamicHeaderPath := conntrack.BuildDynamicHeaderPath() + err := conntrack.GenerateDynamic(ctx, dynamicHeaderPath, conntrackMetrics) if err != nil { return errors.Wrap(err, "failed to generate dynamic header for conntrack") } From 414ec4faa9425360cc9b892931869db32fa46838 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Fri, 13 Dec 2024 09:43:01 +0000 Subject: [PATCH 11/14] feat(ct-metrics): refactor packetparser Generate and update tests --- pkg/plugin/packetparser/_cprog/dynamic.h | 1 - pkg/plugin/packetparser/_cprog/packetparser.c | 2 -- pkg/plugin/packetparser/packetparser_linux.go | 33 +++++++++++++++---- .../packetparser/packetparser_linux_test.go | 20 +++++++++-- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/pkg/plugin/packetparser/_cprog/dynamic.h b/pkg/plugin/packetparser/_cprog/dynamic.h index ac14b29aab..ecadc42211 100644 --- a/pkg/plugin/packetparser/_cprog/dynamic.h +++ b/pkg/plugin/packetparser/_cprog/dynamic.h @@ -1,3 +1,2 @@ #define BYPASS_LOOKUP_IP_OF_INTEREST 0 #define DATA_AGGREGATION_LEVEL 0 -#define CONNTRACK_METRICS 0 diff --git a/pkg/plugin/packetparser/_cprog/packetparser.c b/pkg/plugin/packetparser/_cprog/packetparser.c index 3603d83a73..417de742d9 100644 --- a/pkg/plugin/packetparser/_cprog/packetparser.c +++ b/pkg/plugin/packetparser/_cprog/packetparser.c @@ -202,12 +202,10 @@ static void parse(struct __sk_buff *skb, __u8 obs) } #ifdef CONNTRACK_METRICS - #if CONNTRACK_METRICS == 1 // Initialize conntrack metadata in packet struct. struct conntrackmetadata conntrack_metadata; __builtin_memset(&conntrack_metadata, 0, sizeof(conntrack_metadata)); p.conntrack_metadata = conntrack_metadata; - #endif #endif // CONNTRACK_METRICS // Process the packet in ct diff --git a/pkg/plugin/packetparser/packetparser_linux.go b/pkg/plugin/packetparser/packetparser_linux.go index dd4bc46567..3194ef9785 100644 --- a/pkg/plugin/packetparser/packetparser_linux.go +++ b/pkg/plugin/packetparser/packetparser_linux.go @@ -69,36 +69,57 @@ func (p *packetParser) Name() string { return name } -func (p *packetParser) Generate(ctx context.Context) error { +func generateDynamicHeaderPath() (string, error) { + // Get absolute path to this file during runtime. _, filename, _, ok := runtime.Caller(0) if !ok { - return errors.New("unable to get absolute path to this file") + return "", errors.New("unable to get absolute path to this file") } dir := path.Dir(filename) - dynamicHeaderPath := fmt.Sprintf("%s/%s/%s", dir, bpfSourceDir, dynamicHeaderFileName) + return fmt.Sprintf("%s/%s/%s", dir, bpfSourceDir, dynamicHeaderFileName), nil +} + +func (p *packetParser) Generate(ctx context.Context) error { + // Variable to store content of dynamic header. + var st string + + dynamicHeaderPath, err := generateDynamicHeaderPath() + if err != nil { + return err + } + // Check if packetparser will bypassing lookup IP of interest. bypassLookupIPOfInterest := 0 if p.cfg.BypassLookupIPOfInterest { p.l.Info("bypassing lookup IP of interest") bypassLookupIPOfInterest = 1 + st = fmt.Sprintf("#define BYPASS_LOOKUP_IP_OF_INTEREST %d\n", bypassLookupIPOfInterest) } + conntrackMetrics := 0 // Check if packetparser has Conntrack metrics enabled. if p.cfg.EnableConntrackMetrics { p.l.Info("conntrack metrics enabled") conntrackMetrics = 1 + // Generate dynamic header for conntrack. dynamicHeaderPath := conntrack.BuildDynamicHeaderPath() err := conntrack.GenerateDynamic(ctx, dynamicHeaderPath, conntrackMetrics) if err != nil { return errors.Wrap(err, "failed to generate dynamic header for conntrack") } - p.l.Info("Conntrack header generated") + + // Process packetparser dynamic.h conntrack metrics definition. + st += fmt.Sprintf("#define CONNTRACK_METRICS %d\n", conntrackMetrics) } + + // Process packetparser data aggregation level. p.l.Info("data aggregation level", zap.String("level", p.cfg.DataAggregationLevel.String())) - st := fmt.Sprintf("#define BYPASS_LOOKUP_IP_OF_INTEREST %d\n#define DATA_AGGREGATION_LEVEL %d\n#define CONNTRACK_METRICS %d\n", bypassLookupIPOfInterest, p.cfg.DataAggregationLevel, conntrackMetrics) - err := loader.WriteFile(ctx, dynamicHeaderPath, st) + st += fmt.Sprintf("#define DATA_AGGREGATION_LEVEL %d\n", p.cfg.DataAggregationLevel) + + // Generate dynamic header for packetparser. + err = loader.WriteFile(ctx, dynamicHeaderPath, st) if err != nil { return errors.Wrap(err, "failed to write dynamic header") } diff --git a/pkg/plugin/packetparser/packetparser_linux_test.go b/pkg/plugin/packetparser/packetparser_linux_test.go index 47a82f6f38..3f36b6c2f5 100644 --- a/pkg/plugin/packetparser/packetparser_linux_test.go +++ b/pkg/plugin/packetparser/packetparser_linux_test.go @@ -37,6 +37,7 @@ var ( cfgPodLevelEnabled = &kcfg.Config{ EnablePodLevel: true, BypassLookupIPOfInterest: true, + EnableConntrackMetrics: false, } cfgPodLevelDisabled = &kcfg.Config{ EnablePodLevel: false, @@ -50,7 +51,10 @@ var ( DataAggregationLevel: kcfg.High, } cfgConntrackMetricsEnabled = &kcfg.Config{ - EnableConntrackMetrics: true, + EnablePodLevel: true, + DataAggregationLevel: kcfg.High, + BypassLookupIPOfInterest: true, + EnableConntrackMetrics: true, } ) @@ -544,12 +548,22 @@ func TestPacketParseGenerate(t *testing.T) { { name: "PodLevelEnabled", cfg: cfgPodLevelEnabled, - expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define DATA_AGGREGATION_LEVEL 0\n#define CONNTRACK_METRICS 0\n", + expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define DATA_AGGREGATION_LEVEL 0\n", }, { name: "ConntrackMetricsEnabled", cfg: cfgConntrackMetricsEnabled, - expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 0\n#define DATA_AGGREGATION_LEVEL 0\n#define CONNTRACK_METRICS 1\n", + expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define CONNTRACK_METRICS 1\n#define DATA_AGGREGATION_LEVEL 1\n", + }, + { + name: "DataAggregationLevelLow", + cfg: cfgDataAggregationLevelLow, + expectedContents: "#define DATA_AGGREGATION_LEVEL 0\n", + }, + { + name: "DataAggregationLevelHigh", + cfg: cfgDataAggregationLevelHigh, + expectedContents: "#define DATA_AGGREGATION_LEVEL 1\n", }, } From d5b9fd5f4f910fb33107779c9fd0107b76bc523c Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Fri, 13 Dec 2024 09:55:24 +0000 Subject: [PATCH 12/14] feat(ct-metrics): rename C constant to ENABLE_CONNTRACK_METRICS --- pkg/plugin/conntrack/_cprog/conntrack.c | 28 +++++++++---------- pkg/plugin/conntrack/conntrack_linux.go | 2 +- pkg/plugin/conntrack/conntrack_linux_test.go | 4 +-- pkg/plugin/packetparser/_cprog/packetparser.c | 4 +-- pkg/plugin/packetparser/packetparser_linux.go | 2 +- .../packetparser/packetparser_linux_test.go | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/plugin/conntrack/_cprog/conntrack.c b/pkg/plugin/conntrack/_cprog/conntrack.c index 040fa35394..9d415a1eb5 100644 --- a/pkg/plugin/conntrack/_cprog/conntrack.c +++ b/pkg/plugin/conntrack/_cprog/conntrack.c @@ -144,12 +144,12 @@ static __always_inline bool _ct_create_new_tcp_connection(struct packet *p, stru new_value.is_direction_unknown = false; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS new_value.conntrack_metadata.packets_forward_count = 1; new_value.conntrack_metadata.bytes_forward_count = p->bytes; // Update initial conntrack metadata for the connection. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS // Update packet p->is_reply = false; @@ -176,12 +176,12 @@ static __always_inline bool _ct_handle_udp_connection(struct packet *p, struct c new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; new_value.traffic_direction = _ct_get_traffic_direction(observation_point); - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS new_value.conntrack_metadata.packets_forward_count = 1; new_value.conntrack_metadata.bytes_forward_count = p->bytes; // Update packet's conntrack metadata. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata));; - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS // Update packet p->is_reply = false; @@ -225,25 +225,25 @@ static __always_inline bool _ct_handle_tcp_connection(struct packet *p, struct c p->is_reply = true; new_value.flags_seen_rx_dir = p->flags; new_value.last_report_rx_dir = now; - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS new_value.conntrack_metadata.bytes_reply_count = p->bytes; new_value.conntrack_metadata.packets_reply_count = 1; - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS bpf_map_update_elem(&retina_conntrack, &reverse_key, &new_value, BPF_ANY); } else { // Otherwise, the packet is considered as a packet in the send direction. p->is_reply = false; new_value.flags_seen_tx_dir = p->flags; new_value.last_report_tx_dir = now; - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS new_value.conntrack_metadata.bytes_forward_count = p->bytes; new_value.conntrack_metadata.packets_forward_count = 1; - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS bpf_map_update_elem(&retina_conntrack, &key, &new_value, BPF_ANY); } - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS // Update packet's conntrack metadata. __builtin_memcpy(&p->conntrack_metadata, &new_value.conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS return true; } @@ -362,13 +362,13 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac // Update the packet accordingly. p->is_reply = false; p->traffic_direction = entry->traffic_direction; - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS // Update packet count and bytes count on conntrack entry. WRITE_ONCE(entry->conntrack_metadata.packets_forward_count, READ_ONCE(entry->conntrack_metadata.packets_forward_count) + 1); WRITE_ONCE(entry->conntrack_metadata.bytes_forward_count, READ_ONCE(entry->conntrack_metadata.bytes_forward_count) + p->bytes); // Update packet's conntract metadata. __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_TX, &key); } @@ -384,13 +384,13 @@ static __always_inline __attribute__((unused)) bool ct_process_packet(struct pac // Update the packet accordingly. p->is_reply = true; p->traffic_direction = entry->traffic_direction; - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS // Update packet count and bytes count on conntrack entry. WRITE_ONCE(entry->conntrack_metadata.packets_reply_count, READ_ONCE(entry->conntrack_metadata.packets_reply_count) + 1); WRITE_ONCE(entry->conntrack_metadata.bytes_reply_count, READ_ONCE(entry->conntrack_metadata.bytes_reply_count) + p->bytes); // Update packet's conntract metadata. __builtin_memcpy(&p->conntrack_metadata, &entry->conntrack_metadata, sizeof(struct conntrackmetadata)); - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS return _ct_should_report_packet(entry, p->flags, CT_PACKET_DIR_RX, &reverse_key); } diff --git a/pkg/plugin/conntrack/conntrack_linux.go b/pkg/plugin/conntrack/conntrack_linux.go index ab7496b880..3dae5c325d 100644 --- a/pkg/plugin/conntrack/conntrack_linux.go +++ b/pkg/plugin/conntrack/conntrack_linux.go @@ -84,7 +84,7 @@ func BuildDynamicHeaderPath() string { // Generate dynamic header file for conntrack eBPF program. func GenerateDynamic(ctx context.Context, dynamicHeaderPath string, conntrackMetrics int) error { - st := fmt.Sprintf("#define CONNTRACK_METRICS %d\n", conntrackMetrics) + st := fmt.Sprintf("#define ENABLE_CONNTRACK_METRICS %d\n", conntrackMetrics) err := loader.WriteFile(ctx, dynamicHeaderPath, st) if err != nil { return errors.Wrap(err, "failed to write conntrack dynamic header") diff --git a/pkg/plugin/conntrack/conntrack_linux_test.go b/pkg/plugin/conntrack/conntrack_linux_test.go index 1e16c1c316..5154377426 100644 --- a/pkg/plugin/conntrack/conntrack_linux_test.go +++ b/pkg/plugin/conntrack/conntrack_linux_test.go @@ -39,12 +39,12 @@ func TestGenerateDynamic(t *testing.T) { { name: "ConntrackMetricsEnabled", conntrackMetrics: 1, - expectedContents: "#define CONNTRACK_METRICS 1\n", + expectedContents: "#define ENABLE_CONNTRACK_METRICS 1\n", }, { name: "ConntrackMetricsDisabled", conntrackMetrics: 0, - expectedContents: "#define CONNTRACK_METRICS 0\n", + expectedContents: "#define ENABLE_CONNTRACK_METRICS 0\n", }, } diff --git a/pkg/plugin/packetparser/_cprog/packetparser.c b/pkg/plugin/packetparser/_cprog/packetparser.c index 417de742d9..40ec8fb03e 100644 --- a/pkg/plugin/packetparser/_cprog/packetparser.c +++ b/pkg/plugin/packetparser/_cprog/packetparser.c @@ -201,12 +201,12 @@ static void parse(struct __sk_buff *skb, __u8 obs) return; } - #ifdef CONNTRACK_METRICS + #ifdef ENABLE_CONNTRACK_METRICS // Initialize conntrack metadata in packet struct. struct conntrackmetadata conntrack_metadata; __builtin_memset(&conntrack_metadata, 0, sizeof(conntrack_metadata)); p.conntrack_metadata = conntrack_metadata; - #endif // CONNTRACK_METRICS + #endif // ENABLE_CONNTRACK_METRICS // Process the packet in ct bool report __attribute__((unused)); diff --git a/pkg/plugin/packetparser/packetparser_linux.go b/pkg/plugin/packetparser/packetparser_linux.go index 3194ef9785..cf570de7fd 100644 --- a/pkg/plugin/packetparser/packetparser_linux.go +++ b/pkg/plugin/packetparser/packetparser_linux.go @@ -111,7 +111,7 @@ func (p *packetParser) Generate(ctx context.Context) error { } // Process packetparser dynamic.h conntrack metrics definition. - st += fmt.Sprintf("#define CONNTRACK_METRICS %d\n", conntrackMetrics) + st += fmt.Sprintf("#define ENABLE_CONNTRACK_METRICS %d\n", conntrackMetrics) } // Process packetparser data aggregation level. diff --git a/pkg/plugin/packetparser/packetparser_linux_test.go b/pkg/plugin/packetparser/packetparser_linux_test.go index 3f36b6c2f5..aa27cfa356 100644 --- a/pkg/plugin/packetparser/packetparser_linux_test.go +++ b/pkg/plugin/packetparser/packetparser_linux_test.go @@ -553,7 +553,7 @@ func TestPacketParseGenerate(t *testing.T) { { name: "ConntrackMetricsEnabled", cfg: cfgConntrackMetricsEnabled, - expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define CONNTRACK_METRICS 1\n#define DATA_AGGREGATION_LEVEL 1\n", + expectedContents: "#define BYPASS_LOOKUP_IP_OF_INTEREST 1\n#define ENABLE_CONNTRACK_METRICS 1\n#define DATA_AGGREGATION_LEVEL 1\n", }, { name: "DataAggregationLevelLow", From 4d40b6c767f8b5dd9402dfa53c7d3c68ac446c08 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Fri, 13 Dec 2024 10:54:12 +0000 Subject: [PATCH 13/14] feat(ct-metrics): fix linting --- pkg/plugin/conntrack/conntrack_linux.go | 1 - pkg/plugin/conntrack/conntrack_linux_test.go | 4 ++-- pkg/plugin/packetparser/packetparser_linux.go | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pkg/plugin/conntrack/conntrack_linux.go b/pkg/plugin/conntrack/conntrack_linux.go index 3dae5c325d..dc2fb0c449 100644 --- a/pkg/plugin/conntrack/conntrack_linux.go +++ b/pkg/plugin/conntrack/conntrack_linux.go @@ -83,7 +83,6 @@ func BuildDynamicHeaderPath() string { // Generate dynamic header file for conntrack eBPF program. func GenerateDynamic(ctx context.Context, dynamicHeaderPath string, conntrackMetrics int) error { - st := fmt.Sprintf("#define ENABLE_CONNTRACK_METRICS %d\n", conntrackMetrics) err := loader.WriteFile(ctx, dynamicHeaderPath, st) if err != nil { diff --git a/pkg/plugin/conntrack/conntrack_linux_test.go b/pkg/plugin/conntrack/conntrack_linux_test.go index 5154377426..971ddaead5 100644 --- a/pkg/plugin/conntrack/conntrack_linux_test.go +++ b/pkg/plugin/conntrack/conntrack_linux_test.go @@ -63,12 +63,12 @@ func TestGenerateDynamic(t *testing.T) { // Call the GenerateDynamic function and check if it returns an error. ctx := context.Background() - if err := GenerateDynamic(ctx, dynamicHeaderPath, tt.conntrackMetrics); err != nil { + if err = GenerateDynamic(ctx, dynamicHeaderPath, tt.conntrackMetrics); err != nil { t.Fatalf("failed to generate dynamic header: %v", err) } // Verify that the dynamic header file was created in the expected location and contains the expected contents. - if _, err := os.Stat(dynamicHeaderPath); os.IsNotExist(err) { + if _, err = os.Stat(dynamicHeaderPath); os.IsNotExist(err) { t.Fatalf("dynamic header file does not exist: %v", err) } diff --git a/pkg/plugin/packetparser/packetparser_linux.go b/pkg/plugin/packetparser/packetparser_linux.go index cf570de7fd..fb54c8bb5e 100644 --- a/pkg/plugin/packetparser/packetparser_linux.go +++ b/pkg/plugin/packetparser/packetparser_linux.go @@ -70,7 +70,6 @@ func (p *packetParser) Name() string { } func generateDynamicHeaderPath() (string, error) { - // Get absolute path to this file during runtime. _, filename, _, ok := runtime.Caller(0) if !ok { @@ -104,8 +103,8 @@ func (p *packetParser) Generate(ctx context.Context) error { conntrackMetrics = 1 // Generate dynamic header for conntrack. - dynamicHeaderPath := conntrack.BuildDynamicHeaderPath() - err := conntrack.GenerateDynamic(ctx, dynamicHeaderPath, conntrackMetrics) + ctDynamicHeaderPath := conntrack.BuildDynamicHeaderPath() + err = conntrack.GenerateDynamic(ctx, ctDynamicHeaderPath, conntrackMetrics) if err != nil { return errors.Wrap(err, "failed to generate dynamic header for conntrack") } From 9eec75ca9cf70bf058e5db63f14903f3727f5f83 Mon Sep 17 00:00:00 2001 From: Simone Rodigari <srodigari@microsoft.com> Date: Fri, 13 Dec 2024 15:31:52 +0000 Subject: [PATCH 14/14] feat(ct-metrics): revert files accidentally committed --- pkg/plugin/dropreason/kprobe_bpfel_x86.o | Bin 24384 -> 0 bytes pkg/plugin/filter/filter_bpfel_x86.o | Bin 2144 -> 0 bytes pkg/plugin/mock/plugin.go | 29 ++++++++++++----------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/pkg/plugin/dropreason/kprobe_bpfel_x86.o b/pkg/plugin/dropreason/kprobe_bpfel_x86.o index a0098ede38482561f4961ac10fc98c79ff9e3792..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 24384 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 diff --git a/pkg/plugin/filter/filter_bpfel_x86.o b/pkg/plugin/filter/filter_bpfel_x86.o index bf6c879c816f34c58c39471bdebc664f91e53879..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 2144 zcmbtVJ8KkC6h6C&W;I4lREUwtfFM!ACL{>45H<^mh$-T$(89@NcQzR^nH^?lL*fI2 zAXwUnji6X*Wf{9ziG@GFA7CL^T4*7x-*@NU$?g~s@gv_ok9*F!=RS7wU}kPMpUa6s zPJYOaWxOIU_w48;rIw@@k(HreEt*?MvLGu%Kb5`xie8bPyvWa1t2LM@`UrD$ZEOFQ zwr}WOXcJEp@z3LnZ#fy%S?{n0c}lMZcfJdQHgnz3t<)4w3ECF=dqKT*69<n%elNsv z5V73_NDJ%%iL)Se3mgN10%Oi8RsN{8AO3POSKyi#bsT$So<_dUXwumy<23SzjFyQ} z7lS$U-8~L@12lQJpmU!k@HV&)+ytxO0q{K-fCoV)-GQLL1pOg>m^zHX)Pw9h0{aQb zb9e><g`*grD^=7@V{hHBU?cv(0OT+f{HZPUCvn1F#S3Dw?dhvmr*GQVF5Q~3uiu=X zv29s(!prW4gpphG)_pgSsuMeiY)(!{GiZ2Afm^kb<mS|lr?Tus5^sdAtRQ>M@tZD7 z*)`u;YCv1J-5`!Uw;`by1YWQtksEt~W7j-Cb|djZr&^6<v0nGZE_3wj^_6BQ#n@d( zr<E|OFO7BN6f0tBG~%dPiLIn}3msS&th?!~SF=VI!<x;0lB%}ryQ?FoJIak(r;&K( zjNF$ZA{!!n^Oim{o-;WIE`S8z(Hjuwt8fe>?~Fdab>c1f0UTZyRg1R42o&01z`lD) zUD_v5F!vbBU(ls~Rh>b6cl-q{D7iC?LLF#}M>`59G4`k3O7b?~V{Zia#=U&ho1{*% z9zmT`C6F-od}5~dLdFg=cApd@*H126Y31v)Mc#aJ7acd1J`LYrTebOa@GPZ>M-V%U zkQ=K?vL33)T)8}MPw4gjS4^XT`k-YBo!1{VC*#-0mU<aRAHQ|&C~3poi<v7JMN8)L zTT;@t5g&Q*0N)?M?_VA?`Nq$0IBR70@ESg1JQ3+=@{ObY8)tl!pSAIwi?%_xm3po~ z+pYi4yY$bqGV7cE55bo`Qx^ZEH?&Ek4cYbizhJlZzv{qwoy+s%cQ(8JBz#?StvbXe gm|{xbf3~-*eF+(vBT_Zy_4@0&qPZipzWLw#8+^aPQ~&?~ diff --git a/pkg/plugin/mock/plugin.go b/pkg/plugin/mock/plugin.go index 403a472c3c..888d9267dd 100644 --- a/pkg/plugin/mock/plugin.go +++ b/pkg/plugin/mock/plugin.go @@ -5,11 +5,11 @@ // // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/microsoft/retina/pkg/plugin (interfaces: Plugin) +// Source: github.com/microsoft/retina/pkg/plugin/ (interfaces: Plugin) // // Generated by this command: // -// mockgen -destination=mock/plugin.go -copyright_file=../lib/ignore_headers.txt -package=plugin github.com/microsoft/retina/pkg/plugin Plugin +// mockgen -destination=mock/plugin.go -copyright_file=../lib/ignore_headers.txt -package=plugin github.com/microsoft/retina/pkg/plugin/ Plugin // // Package plugin is a generated GoMock package. @@ -27,6 +27,7 @@ import ( type MockPlugin struct { ctrl *gomock.Controller recorder *MockPluginMockRecorder + isgomock struct{} } // MockPluginMockRecorder is the mock recorder for MockPlugin. @@ -47,31 +48,31 @@ func (m *MockPlugin) EXPECT() *MockPluginMockRecorder { } // Compile mocks base method. -func (m *MockPlugin) Compile(arg0 context.Context) error { +func (m *MockPlugin) Compile(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Compile", arg0) + ret := m.ctrl.Call(m, "Compile", ctx) ret0, _ := ret[0].(error) return ret0 } // Compile indicates an expected call of Compile. -func (mr *MockPluginMockRecorder) Compile(arg0 any) *gomock.Call { +func (mr *MockPluginMockRecorder) Compile(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compile", reflect.TypeOf((*MockPlugin)(nil).Compile), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compile", reflect.TypeOf((*MockPlugin)(nil).Compile), ctx) } // Generate mocks base method. -func (m *MockPlugin) Generate(arg0 context.Context) error { +func (m *MockPlugin) Generate(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Generate", arg0) + ret := m.ctrl.Call(m, "Generate", ctx) ret0, _ := ret[0].(error) return ret0 } // Generate indicates an expected call of Generate. -func (mr *MockPluginMockRecorder) Generate(arg0 any) *gomock.Call { +func (mr *MockPluginMockRecorder) Generate(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Generate", reflect.TypeOf((*MockPlugin)(nil).Generate), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Generate", reflect.TypeOf((*MockPlugin)(nil).Generate), ctx) } // Init mocks base method. @@ -117,17 +118,17 @@ func (mr *MockPluginMockRecorder) SetupChannel(arg0 any) *gomock.Call { } // Start mocks base method. -func (m *MockPlugin) Start(arg0 context.Context) error { +func (m *MockPlugin) Start(ctx context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Start", arg0) + ret := m.ctrl.Call(m, "Start", ctx) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. -func (mr *MockPluginMockRecorder) Start(arg0 any) *gomock.Call { +func (mr *MockPluginMockRecorder) Start(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPlugin)(nil).Start), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPlugin)(nil).Start), ctx) } // Stop mocks base method.