From af08538d66f0d458405fe9389f0a699eebdf6b59 Mon Sep 17 00:00:00 2001 From: Simone Magnani Date: Wed, 19 Jun 2024 18:54:46 +0200 Subject: [PATCH] Simplified example, counting ingress/egress packets Signed-off-by: Simone Magnani --- examples/README.md | 2 +- examples/headers/common.h | 149 ++++------------------------------ examples/tcx/bpf_bpfeb.go | 23 ++---- examples/tcx/bpf_bpfeb.o | Bin 7112 -> 3304 bytes examples/tcx/bpf_bpfel.go | 23 ++---- examples/tcx/bpf_bpfel.o | Bin 7112 -> 3304 bytes examples/tcx/main.go | 61 +++++--------- examples/tcx/tcx.c | 166 +++++++------------------------------- 8 files changed, 76 insertions(+), 348 deletions(-) diff --git a/examples/README.md b/examples/README.md index 8cf3236c0..a8309ef03 100644 --- a/examples/README.md +++ b/examples/README.md @@ -20,7 +20,7 @@ Please see our [guide on what makes a good example](https://ebpf-go.dev/contribu * [tcp_connect](fentry/) - Trace outgoing IPv4 TCP connections. * [tcp_close](tcprtt/) - Log RTT of IPv4 TCP connections using eBPF CO-RE helpers. * TCx - Attach a program to Linux TC (Traffic Control) to process incoming and outgoing packets. - * [tcx](./tcx/) - monitor the number of incoming and outgoing packets for each network flow identified with the traditional 5-tuple session identifier (IP addresses, L4 Ports, IP protocol). + * [tcx](./tcx/) - Print packet counts for ingress and egress. * XDP - Attach a program to a network interface to process incoming packets. * [xdp](xdp/) - Print packet counts by IPv4 source address. diff --git a/examples/headers/common.h b/examples/headers/common.h index cf8c9ecb4..55d4fe9d9 100644 --- a/examples/headers/common.h +++ b/examples/headers/common.h @@ -24,15 +24,6 @@ typedef __u32 __wsum; #include "bpf_helpers.h" -// compatibility with kernel definitions -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define __LITTLE_ENDIAN_BITFIELD -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define __BIG_ENDIAN_BITFIELD -#else -# error "Fix your compiler's __BYTE_ORDER__?!" -#endif - enum bpf_map_type { BPF_MAP_TYPE_UNSPEC = 0, BPF_MAP_TYPE_HASH = 1, @@ -73,6 +64,19 @@ enum xdp_action { XDP_REDIRECT = 4, }; +enum tc_action { + TC_ACT_UNSPEC = -1, + TC_ACT_OK = 0, + TC_ACT_RECLASSIFY = 1, + TC_ACT_SHOT = 2, + TC_ACT_PIPE = 3, + TC_ACT_STOLEN = 4, + TC_ACT_QUEUED = 5, + TC_ACT_REPEAT = 6, + TC_ACT_REDIRECT = 7, + TC_ACT_JUMP = 0x10000000 +}; + struct xdp_md { __u32 data; __u32 data_end; @@ -84,10 +88,7 @@ struct xdp_md { typedef __u16 __sum16; -#define ETH_P_IP 0x0800 -#define IPPROTO_ICMP 1 -#define IPPROTO_TCP 6 -#define IPPROTO_UDP 17 +#define ETH_P_IP 0x0800 struct ethhdr { unsigned char h_dest[6]; @@ -109,128 +110,6 @@ struct iphdr { __be32 daddr; }; -/*struct __sk_buff https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf.h */ -#define __bpf_md_ptr(type, name) \ -union { \ - type name; \ - __u64 :64; \ -} __attribute__((aligned(8))) - -struct __sk_buff { - __u32 len; - __u32 pkt_type; - __u32 mark; - __u32 queue_mapping; - __u32 protocol; - __u32 vlan_present; - __u32 vlan_tci; - __u32 vlan_proto; - __u32 priority; - __u32 ingress_ifindex; - __u32 ifindex; - __u32 tc_index; - __u32 cb[5]; - __u32 hash; - __u32 tc_classid; - __u32 data; - __u32 data_end; - __u32 napi_id; - - /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ - __u32 family; - __u32 remote_ip4; /* Stored in network byte order */ - __u32 local_ip4; /* Stored in network byte order */ - __u32 remote_ip6[4]; /* Stored in network byte order */ - __u32 local_ip6[4]; /* Stored in network byte order */ - __u32 remote_port; /* Stored in network byte order */ - __u32 local_port; /* stored in host byte order */ - /* ... here. */ - - __u32 data_meta; - __bpf_md_ptr(struct bpf_flow_keys *, flow_keys); - __u64 tstamp; - __u32 wire_len; - __u32 gso_segs; - __bpf_md_ptr(struct bpf_sock *, sk); - __u32 gso_size; -}; - -/*TCP Header https://github.com/torvalds/linux/blob/master/include/uapi/linux/tcp.h */ -struct tcphdr { - __be16 source; - __be16 dest; - __be32 seq; - __be32 ack_seq; -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u16 res1:4, - doff:4, - fin:1, - syn:1, - rst:1, - psh:1, - ack:1, - urg:1, - ece:1, - cwr:1; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u16 doff:4, - res1:4, - cwr:1, - ece:1, - urg:1, - ack:1, - psh:1, - rst:1, - syn:1, - fin:1; -#else -#error "Adjust your defines" -#endif - __be16 window; - __sum16 check; - __be16 urg_ptr; -}; - -/*UDP Header https://github.com/torvalds/linux/blob/master/include/uapi/linux/udp.h */ -struct udphdr { - __be16 source; - __be16 dest; - __be16 len; - __sum16 check; -}; - -/*ICMP Header https://github.com/torvalds/linux/blob/master/include/uapi/linux/icmp.h */ -struct icmphdr { - __u8 type; - __u8 code; - __sum16 checksum; - union { - struct { - __be16 id; - __be16 sequence; - } echo; - __be32 gateway; - struct { - __be16 __unused; - __be16 mtu; - } frag; - __u8 reserved[4]; - } un; -}; - -enum tc_action { - TC_ACT_UNSPEC = -1, - TC_ACT_OK = 0, - TC_ACT_RECLASSIFY = 1, - TC_ACT_SHOT = 2, - TC_ACT_PIPE = 3, - TC_ACT_STOLEN = 4, - TC_ACT_QUEUED = 5, - TC_ACT_REPEAT = 6, - TC_ACT_REDIRECT = 7, - TC_ACT_JUMP = 0x10000000 -}; - enum { BPF_ANY = 0, BPF_NOEXIST = 1, diff --git a/examples/tcx/bpf_bpfeb.go b/examples/tcx/bpf_bpfeb.go index e83ac6ea3..36b1dceb9 100644 --- a/examples/tcx/bpf_bpfeb.go +++ b/examples/tcx/bpf_bpfeb.go @@ -12,20 +12,6 @@ import ( "github.com/cilium/ebpf" ) -type bpfSessionKey struct { - Saddr uint32 - Daddr uint32 - Sport uint16 - Dport uint16 - Proto uint8 - _ [3]byte -} - -type bpfSessionValue struct { - InCount uint32 - EgCount uint32 -} - // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) @@ -75,7 +61,8 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - StatsMap *ebpf.MapSpec `ebpf:"stats_map"` + EgressPktCount *ebpf.MapSpec `ebpf:"egress_pkt_count"` + IngressPktCount *ebpf.MapSpec `ebpf:"ingress_pkt_count"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -97,12 +84,14 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - StatsMap *ebpf.Map `ebpf:"stats_map"` + EgressPktCount *ebpf.Map `ebpf:"egress_pkt_count"` + IngressPktCount *ebpf.Map `ebpf:"ingress_pkt_count"` } func (m *bpfMaps) Close() error { return _BpfClose( - m.StatsMap, + m.EgressPktCount, + m.IngressPktCount, ) } diff --git a/examples/tcx/bpf_bpfeb.o b/examples/tcx/bpf_bpfeb.o index 9e23b089d261edc9777524a92e3e0eb059828417..f551e703823d41f239bc96208ec0f5b75c84d8dd 100644 GIT binary patch literal 3304 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 Gz5fQ29g&6r diff --git a/examples/tcx/bpf_bpfel.go b/examples/tcx/bpf_bpfel.go index 945151de5..7767e316d 100644 --- a/examples/tcx/bpf_bpfel.go +++ b/examples/tcx/bpf_bpfel.go @@ -12,20 +12,6 @@ import ( "github.com/cilium/ebpf" ) -type bpfSessionKey struct { - Saddr uint32 - Daddr uint32 - Sport uint16 - Dport uint16 - Proto uint8 - _ [3]byte -} - -type bpfSessionValue struct { - InCount uint32 - EgCount uint32 -} - // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) @@ -75,7 +61,8 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - StatsMap *ebpf.MapSpec `ebpf:"stats_map"` + EgressPktCount *ebpf.MapSpec `ebpf:"egress_pkt_count"` + IngressPktCount *ebpf.MapSpec `ebpf:"ingress_pkt_count"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -97,12 +84,14 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - StatsMap *ebpf.Map `ebpf:"stats_map"` + EgressPktCount *ebpf.Map `ebpf:"egress_pkt_count"` + IngressPktCount *ebpf.Map `ebpf:"ingress_pkt_count"` } func (m *bpfMaps) Close() error { return _BpfClose( - m.StatsMap, + m.EgressPktCount, + m.IngressPktCount, ) } diff --git a/examples/tcx/bpf_bpfel.o b/examples/tcx/bpf_bpfel.o index 314606148bef7e2929085d27997321d8aa36ef01..abbf0184cee680cc577f812c4d979190b27804d1 100644 GIT binary patch literal 3304 zcmcImO=ule6h3*WZDNx&iD`{e@Gc}IAxzR3s!$8B!BnfMgc_j{Ve&GWmxR2`d%T$` z2?`CU=te|DbXQT4w{#)2JG<=CRS{gc6?CmxRO0vDpLx?s%)*~T&OPTl=iGDdy>su$ zyE!+1xzy1ijymKIX(MM9X?3^tvR2E|i^$tHIC%2GKF!~Mshx77B=TT?e}A};>^-I%7=K)z^l<mSHWNRn zrzjQ{4Za_5`YUO*D)Ht@dqT6m=2z1wkR%Xq(wj_zZ7<+*u@;-cg^b2YGYt}zWm4lv zHKyJY1=2fFSRTPeHFZG=HJH!OsByh0m{%HAf2-2)YxVkO+VI0#xHSqqp++!s%+A@U zs!=@wxlI<6Dc*?!zZxdNx?hP_{mSa937b&oIJLVOCTTNLX=AmLgt|<29-|}KcH9w- zUC0i}?NIOAot!iN{kD%T=EVzd?54LXr6X zz5<(TDKZ3o8GHuJJ4m0qm)}7D7A#+wdBi`1Pi~9uUBr$Q6yo2(LvM>bXYn}dcJGS3 zWbyOF?}{u~90HfVHf!Mg7T5Wm$h!5rdMK&$LulH?n6LM_#jakNL;zp@>@=b?SBJ9jwb|ug@vxcoX_yF3+;{@w^A*xJBme6GULfFB5>4^(Zm|$#`Y4 zldotyJF6k~8j{kZ={5B3<}e#ym;LbUPxKGvF#Qub%)4U$FX(>8Pcs81aXvA>E6L&O z24vc_24?t!0ygK_j&BwGcMAAk0smOQzZCFZ0lRnM*5`hQINaAicxphrB-~EK zYlby%cJZ=DaG(=peZW0c`!c;+?Zy_{OcU)n|89{D?J~>bIz^TzGk9p3No7U6cxOvX zyAGM3ojvDIQ%phpCv+28IuFV8XO}VZ4dsbqYKqJV#GlNX5(eUU^)JHKIlo_r*ac^_X9L?E&$tT9k@sP?Ei9W$Fwrt0WohEBraV1$gTaHmwCl+VNcBJAK;mSo0fz|1>(rtCR zJKyfvQq(l}r9mH3aG?c3LMswVl@h4ilv*tTtMwNZ+7B%hME}Jf1#Q6}Oe%`O{k@qt zr`6uOyC89r&HS%1rfJh`J$mwm3?$mGnTO_I71|QrZ0`&O$WNuE*&&wFv%~8h3E| zq2lSL14^H1zpwG`r!+qBK+o?T1*twxbpO$#N6psaeFJ7zU7_vD@iqYCnFFgeo}DTa_C1+HTdYWs|xcQUROS?X$}6@6~nk_v!lCbxKQpCcdESnmM5BHu18qYo`6b zt()Gb(YpSB>!%meuHX4`I_G?`fAIKA1Nxxp<=yo9y}!lC9N8&!$?aEC69mQibnrZw zaYm;K^}*r2#d`LUFyjY}qBx&FA$8bHjBG(R#rbSMfrdV|u>KjeZD>r#&{%fRy3qK9 zq|lxdneQxT&>GQ((OyIYHGBq_ekE4NPG8Zufvfe3*myjgb3)?=-q%Gft2-W#O(@QO zUq?gC7S_LsX2!{9$cz&rugew({JUtfhV(9EUZ)h=kI?GTDrg^|J%+Y~_H#7QPQHJc zIEZn+h22GS(C;GnZ_!Nd--!$x{ZZr;_(viSga1S13i!t&&x0qBg!@@S+adBj$ooa+ zK{z5aKOu4oyhr3=@Uv+95LW>|FM0a@U6UW{tCgIJB|m%dH=6M58`b>A8WbcAmoQy zj&W=XjR`8%*JD^4eb>`^T;u%oX@VZ4`eFh8jjTF|3qNk328V9P(`*FU_Kz@VwjYA+ z`+8#LJV32T&HV^Oo*$pTA8NmyEs)vir@-L6wW*0OAP4Oi5&bi)1aUn1`H)=q?B&a6 zuex9EfBAwtcy-`{>r&tafnUhG6W%lh>Cw>=jjC%_qDLO5OYXfi$Jdik*Adjaui zv~v)q!@!+P7twDp;f|EY#wh3IsW=h35>}d^Yh|zOu~*U0kCPg2rjVoSxpdwwmb?J{ zY7l08qvbgjOMao`htn9-INSo)AM^90-V_-gW?Zevj8HZmWC@B)E{!!C#X^N?b-}Pl zDW5L-F0^CmNk2DDC2z72dahqQMY%#Iol{a(*;+Ep(-dUq#1qzJJFlSR7W zmpqrJJ{}a@z#9*=;J@n8Sgvq`D;Us7am-yuC`I?F^;+Z#`EiFY6ig`3ppcn>JW`!L zR&LxKE9WzqCnb+3kB6D5WQMj~FZiQQJ5QMB>YQ;}wWh6=yK6%ioxR5TeAL}x+SDDc zL0BqhLMO5f9ZUovGq1twk2$T?R9={MjyYXzPOoEfwK+4|3AWDOENYFaI)~0Uybjs0 zkPlif4EDJLZvQ}A)wkNAU#!_*aKjHXS*O)6cJ`XBq=GjNC(L{@oVIQBDUa6j!33pe zUK&0xZ5xwe=E_GMNb!00q;^ueHshfVtL33|>|w2~I#5nK7y@{Ka9-S8p)gS{x?awk zY&}{%Y#q)~41oLK3LHX+UVxfC=Di!P?sO(;&Nec1aRIlPJmn&4=GD%FY|=-Eb8g_G zd-h9LIW-RabUx#bd8me)&X2mdF|{5=rp{iIrUTpId3p_Z5_Njlw=b_>Qr*LN`!P_r zGW4npe2c3Z7>GeRu_2r+o-{h+3>{RpS2el@=C)?m;8W&dT=hKEz0!4ae#XlO9wkwq zfD9tycS?uve*z>67h7oI)*EMjud7VwZb49@RYY kbP94W+R;i( z#W9{_jF$m^elngz+c&FoGX5Fbp11LyB6h}gz&(Qh1iby0evYy~<2v9zvGZ@Lre&g` z2(AMj7JLl$oqs0EMsOUsBKSP)d;d;!GlJ`Z=LKikzAHvZsm9N^9(YkOn4%S2d3C-^VGv6YyrDaWh&J-~d$uIhI`MhH>)Rs9~u zXj!nSAG%O>Q@?wHP5o9QnCpiwmA|PUZ!pf!nCrJou&JLD!Cb!{!KQwF5zO@)5^U<1 zjbN@{Q81QWQAMz+->hI$zng+hy?7mXy!WwwjTqd#;B9zUYJ4(I3xW?bZjI9|!QG5o z;U$zi_XMl@BGH=Qe#AGw5LfTgoL|*96{i8L8{-++4GJclT~F8Yc! zowr2KHILFaMbEX4lK#z>pgybr7va~(eB4(@`wlMDjCS!MGd*O-`b{A>jCOM3?z6RSDq6a$dKeHvErkcIgk z$z=SeG_2xT3(r}2!NR8AroUy&zGC533$IzYLDtLk*KFZd3-?&K&%#3%&RTfN!m}2h zv+#n27cIPO;S~$7T6oRE4YEI~^S3Z=Gm4D8$HILUHs{6oXD$1bg=Z~1XW<14FIsrn z!YdYDweXsS8{|Bg@itqy)xtd%Hs{^MoBNBwS<8RQ!m}2hv+#n27cIPO;S~!%-u%RV zN`~H4NXe3yOP(9Nm{dT$vsK@tDVfV%pX3+U5`H%0C#-p!;~@ORGCw=lUtB-&CY!`N zcbFcbWH3FcxTK>?=gxJxpJ#SLvo5zsBgMMh=QL8R%k4U;q2|C{g89?m|4d`(L6&Gk zUmx-ZA<(Zit6t@}Y{0Cmt7^26vao@?52BO;__TPlUrbDc5rn@0&fn@%f(@IK|4)p5 z2Mjz<6TeFw`TouM?KLp_%sf}bUHKZBIe&w!6>s*X!%h)0`HkvT2pn&d*|(SZqiuxj z^}7yRt@*FW{2htq`SXV_li$prza7`g-@zY!L95on*!j87TKU%`|B}RWe)hBTw?LP; p^r!$_0o7J=Q!T Ingress:%10d Egress:%10d\n", - intToIp(key.Saddr), portToLittleEndian(key.Sport), - intToIp(key.Daddr), portToLittleEndian(key.Dport), - protoMap[key.Proto], val.InCount, val.EgCount)) + // retrieve value from the ingress map + if err := ingressMap.Lookup(&key, &ingressPacketCount); err != nil { + return "", err } - return sb.String(), iter.Err() -} - -// intToIp convert an int32 value retrieved from the network traffic (big endian) into a netip.Addr -func intToIp(val uint32) netip.Addr { - a4 := [4]byte{} - binary.LittleEndian.PutUint32(a4[:], val) - return netip.AddrFrom4(a4) -} + // retrieve value from the egress map + if err := egressMap.Lookup(&key, &egressPacketCount); err != nil { + return "", err + } -// portToLittleEndian convert a uint16 value retrieved from the network traffic (big endian) into a little endian -func portToLittleEndian(val uint16) uint16 { - p2 := [2]byte{} - binary.LittleEndian.PutUint16(p2[:], val) - return binary.LittleEndian.Uint16(p2[:]) + return fmt.Sprintf("%10v Ingress, %10v Egress", ingressPacketCount, egressPacketCount), nil } diff --git a/examples/tcx/tcx.c b/examples/tcx/tcx.c index d6b00c117..096a81ccf 100644 --- a/examples/tcx/tcx.c +++ b/examples/tcx/tcx.c @@ -1,155 +1,47 @@ //go:build ignore #include "common.h" -#include "bpf_endian.h" char __license[] SEC("license") = "Dual MIT/GPL"; -// Session identifier -struct session_key { - __u32 saddr; // IP source address - __u32 daddr; // IP dest address - __u16 sport; // Source port (set to 0 if ICMP) - __u16 dport; // Dest port (set to 0 if ICMP) - __u8 proto; // Protocol ID -}; - -// Session value -struct session_value { - __u32 in_count; // Ingress packet count - __u32 eg_count; // Egress packet count -}; - -#define MAX_MAP_ENTRIES 16 - -// Define an Hash map for storing packet Ingress and Egress count by 5-tuple session identifier -// User-space logic is responsible for cleaning the map, if potentially new entries needs to be monitored. +/* Define an ARRAY map for storing ingress packet count */ struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, MAX_MAP_ENTRIES); - __type(key, struct session_key); - __type(value, struct session_value); -} stats_map SEC(".maps"); - -// Attempt to parse the 5-tuple session identifier from the packet. -// Returns 0 if the operation failed, i.e. not IPv4 packet or not UDP, TCP or ICMP. -static __always_inline int parse_session_identifier(void *data, void *data_end, struct session_key *key) { - // First, parse the ethernet header. - struct ethhdr *eth = data; - if ((void *)(eth + 1) > data_end) { - return 0; - } - - // Check for IPv4 packet. - if (eth->h_proto != bpf_htons(ETH_P_IP)) { - return 0; - } - - // Then parse the IP header. - struct iphdr *ip = (void *)(eth + 1); - if ((void *)(ip + 1) > data_end) { - return 0; - } + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u64); + __uint(max_entries, 1); +} ingress_pkt_count SEC(".maps"); - // Then parse the L4 header. - switch (ip->protocol) { - case IPPROTO_TCP: { - // TCP protocol carried, parse TCP header. - struct tcphdr *tcp = (void *)(ip + 1); - if ((void *)(tcp + 1) > data_end) - return 0; - key->sport = (__u16)(tcp->source); - key->dport = (__u16)(tcp->dest); - break; - } - case IPPROTO_UDP: { - // UDP protocol carried, parse UDP header. - struct udphdr *udp = (void *)(ip + 1); - if ((void *)(udp + 1) > data_end) - return 0; - key->sport = (__u16)(udp->source); - key->dport = (__u16)(udp->dest); - break; - } - case IPPROTO_ICMP: { - // ICMP protocol carried, no source/dest port. - break; - } - // Unchecked protocols, ignore packet and return. - default: { - return 0; - } - } - - // Fill session key with IP header data - key->proto = (__u8)(ip->protocol); - key->saddr = (__u32)(ip->saddr); - key->daddr = (__u32)(ip->daddr); +/* Define an ARRAY map for storing egress packet count */ +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u64); + __uint(max_entries, 1); +} egress_pkt_count SEC(".maps"); + + +/* +Upon arrival of each network packet, retrieve and increment +the packet count from the provided map. +Returns TC_ACT_OK, allowing the packet to proceed. +*/ +static __always_inline int update_map_pkt_count(void *map) { + __u32 key = 0; + __u64 *count = bpf_map_lookup_elem(map, &key); + if (count) { + __sync_fetch_and_add(count, 1); + } - return 1; + return TC_ACT_OK; } -// TC Ingress hook, to monitoring TCP/UDP/ICMP network connections and count packets. SEC("tc") int ingress_prog_func(struct __sk_buff *skb) { - void *data = (void *)(long)skb->data; - void *data_end = (void *)(long)skb->data_end; - - struct session_key key = {}; - if (!parse_session_identifier(data, data_end, &key)) { - goto ingress_done; - } - - struct session_value *val = bpf_map_lookup_elem(&stats_map, &key); - if (!val) { - // No entry in the map for this 5-tuple identifier yet, so set the initial value to 1. - struct session_value new_val = {.in_count = 1}; - bpf_map_update_elem(&stats_map, &key, &new_val, BPF_ANY); - goto ingress_done; - } - - // Entry already exists for this 5-tuple identifier, so increment it atomically using an LLVM built-in. - __sync_fetch_and_add(&val->in_count, 1); - -ingress_done: - - // Return code corresponds to the PASS action in TC - return TC_ACT_OK; + return update_map_pkt_count(&ingress_pkt_count); } -// TC Egress hook, same as Ingress but with IPs and Ports inverted in the key. -// This way, the connections match the same entry for the Ingress in the bpf map. SEC("tc") int egress_prog_func(struct __sk_buff *skb) { - void *data = (void *)(long)skb->data; - void *data_end = (void *)(long)skb->data_end; - - struct session_key key = {}; - if (!parse_session_identifier(data, data_end, &key)) { - goto egress_done; - } - - // Swap addresses and L4 port before doing the map lookup. - __u32 tmp = key.saddr; - __u16 tmp2 = key.sport; - key.saddr = key.daddr; - key.sport = key.dport; - key.daddr = tmp; - key.dport = tmp2; - - struct session_value *val = bpf_map_lookup_elem(&stats_map, &key); - if (!val) { - // No entry in the map for this 5-tuple identifier yet, so set the initial value to 1. - struct session_value new_val = {.eg_count = 1}; - bpf_map_update_elem(&stats_map, &key, &new_val, BPF_ANY); - goto egress_done; - } - - // Entry already exists for this 5-tuple identifier, so increment it atomically using an LLVM built-in. - __sync_fetch_and_add(&val->eg_count, 1); - -egress_done: - - // Return code corresponds to the PASS action in TC - return TC_ACT_OK; + return update_map_pkt_count(&egress_pkt_count); }