Skip to content

Commit a80cdfa

Browse files
committed
Add PID tracking in TC mode with p/pid flags
1 parent df247d4 commit a80cdfa

11 files changed

+216
-38
lines changed

counter.c

+119-4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct statkey_t {
4949
__u16 src_port; // source port
5050
__u16 dst_port; // destination port
5151
__u8 proto; // transport protocol
52+
pid_t pid; // process ID
5253
} statkey;
5354

5455
// Map value struct with counters
@@ -243,6 +244,70 @@ static inline void process_eth(void *data, void *data_end, __u64 pkt_len) {
243244
}
244245
}
245246

247+
/**
248+
* Process the Ethernet header and extract relevant information, including the
249+
* process ID, to populate the key.
250+
*
251+
* @param data pointer to the start of the Ethernet header
252+
* @param data_end pointer to the end of the packet data
253+
* @param pkt_len length of the packet
254+
* @param pid process ID to be included in the key
255+
*
256+
* @return none
257+
*
258+
* @throws none
259+
*/
260+
static inline void process_eth_pid(void *data, void *data_end, __u64 pkt_len,
261+
pid_t pid) {
262+
struct ethhdr *eth = data;
263+
264+
// validate Ethernet size
265+
if ((void *)eth + sizeof(*eth) > data_end) {
266+
return;
267+
}
268+
269+
// initialize key
270+
statkey key;
271+
__builtin_memset(&key, 0, sizeof(key));
272+
273+
// populate PID (may be 0)
274+
key.pid = pid;
275+
276+
// process only IPv4 and IPv6
277+
switch (bpf_ntohs(eth->h_proto)) {
278+
case ETH_P_IP: {
279+
struct iphdr *ip4 = (void *)eth + sizeof(*eth);
280+
281+
if (process_ip4(ip4, data_end, &key) == NOK)
282+
return;
283+
}
284+
285+
break;
286+
case ETH_P_IPV6: {
287+
struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
288+
289+
if (process_ip6(ip6, data_end, &key) == NOK)
290+
return;
291+
}
292+
293+
break;
294+
default:
295+
return;
296+
}
297+
298+
// lookup value in hash
299+
statvalue *val = (statvalue *)bpf_map_lookup_elem(&pkt_count, &key);
300+
if (val) {
301+
// atomic XADD, doesn't need bpf_spin_lock()
302+
__sync_fetch_and_add(&val->packets, 1);
303+
__sync_fetch_and_add(&val->bytes, pkt_len);
304+
} else {
305+
statvalue initval = {.packets = 1, .bytes = pkt_len};
306+
307+
bpf_map_update_elem(&pkt_count, &key, &initval, BPF_NOEXIST);
308+
}
309+
}
310+
246311
/**
247312
* Process the packet for traffic control and take necessary actions.
248313
*
@@ -259,6 +324,24 @@ static inline void tc_process_packet(struct __sk_buff *skb) {
259324
process_eth(data, data_end, skb->len);
260325
}
261326

327+
/**
328+
* Process the packet for traffic control and take necessary actions, with
329+
* the process ID included in the key.
330+
*
331+
* @param skb pointer to the packet buffer
332+
* @param pid process ID to be included in the key
333+
*
334+
* @return none
335+
*
336+
* @throws none
337+
*/
338+
static inline void tc_process_packet_pid(struct __sk_buff *skb, pid_t pid) {
339+
void *data = (void *)(long)skb->data;
340+
void *data_end = (void *)(long)skb->data_end;
341+
342+
process_eth_pid(data, data_end, skb->len, pid);
343+
}
344+
262345
/**
263346
* Process the packet for XDP (eXpress Data Path) and take necessary actions.
264347
*
@@ -275,8 +358,14 @@ static inline void xdp_process_packet(struct xdp_md *xdp) {
275358
process_eth(data, data_end, data_end - data);
276359
}
277360

278-
/*
279-
* Main eBPF XDP program
361+
/**
362+
* Process the packet for XDP and take necessary actions.
363+
*
364+
* @param xdp pointer to the XDP context
365+
*
366+
* @return XDP_PASS
367+
*
368+
* @throws none
280369
*/
281370
SEC("xdp")
282371
int xdp_count_packets(struct xdp_md *xdp) {
@@ -285,8 +374,14 @@ int xdp_count_packets(struct xdp_md *xdp) {
285374
return XDP_PASS;
286375
}
287376

288-
/*
289-
* Main eBPF TC program
377+
/**
378+
* Process the packet for TC (Traffic Control) and take necessary actions.
379+
*
380+
* @param skb pointer to the skb
381+
*
382+
* @return TC_ACT_UNSPEC
383+
*
384+
* @throws none
290385
*/
291386
SEC("tc")
292387
int tc_count_packets(struct __sk_buff *skb) {
@@ -295,4 +390,24 @@ int tc_count_packets(struct __sk_buff *skb) {
295390
return TC_ACT_UNSPEC;
296391
}
297392

393+
/**
394+
* Process the packet for TC (Traffic Control) and take necessary actions. This
395+
* variant of the function also gets the current PID and passes it to the
396+
* `tc_process_packet` function.
397+
*
398+
* @param skb pointer to the skb
399+
*
400+
* @return TC_ACT_UNSPEC
401+
*
402+
* @throws none
403+
*/
404+
SEC("tc")
405+
int tc_count_packets_pid(struct __sk_buff *skb) {
406+
// get PID where possible
407+
pid_t pid = bpf_get_current_pid_tgid() & 0xFFFFFFFF;
408+
tc_process_packet_pid(skb, pid);
409+
410+
return TC_ACT_UNSPEC;
411+
}
412+
298413
char __license[] SEC("license") = "Dual MIT/GPL";

counter_bpfeb.go

+8-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

counter_bpfeb.o

2.85 KB
Binary file not shown.

counter_bpfel.go

+8-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

counter_bpfel.o

2.88 KB
Binary file not shown.

flags.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ const (
3939
)
4040

4141
var (
42-
ifname, xdpMode *string
43-
jsonOutput, version, help, useXDP *bool
44-
timeout *time.Duration
45-
xdpAttachFlags link.XDPAttachFlags
42+
ifname, xdpMode *string
43+
jsonOutput, version, help, useXDP, usePID *bool
44+
timeout *time.Duration
45+
xdpAttachFlags link.XDPAttachFlags
4646
)
4747

4848
func parseFags() {
@@ -51,6 +51,7 @@ func parseFags() {
5151
help = fs.Bool('?', "help", "display help")
5252
jsonOutput = fs.Bool('j', "json", "if true, output in JSON format")
5353
useXDP = fs.Bool('x', "xdp", "if true, use XDP instead of TC (this disables egress statistics)")
54+
usePID = fs.Bool('p', "pid", "if true, use process ID for packets (works with TC only)")
5455

5556
version = fs.BoolLong("version", "display program version")
5657

go.mod

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ go 1.23
55
toolchain go1.23.2
66

77
require (
8-
github.com/cilium/ebpf v0.17.1
9-
github.com/goccy/go-json v0.10.4
8+
github.com/cilium/ebpf v0.17.2
9+
github.com/goccy/go-json v0.10.5
1010
github.com/peterbourgon/ff/v4 v4.0.0-alpha.4
1111
)
1212

1313
require (
14+
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect
1415
golang.org/x/sync v0.10.0 // indirect
15-
golang.org/x/sys v0.28.0 // indirect
16+
golang.org/x/sys v0.30.0 // indirect
1617
)

go.sum

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
github.com/cilium/ebpf v0.17.1 h1:G8mzU81R2JA1nE5/8SRubzqvBMmAmri2VL8BIZPWvV0=
2-
github.com/cilium/ebpf v0.17.1/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8=
1+
github.com/cilium/ebpf v0.17.2 h1:IQTaTVu0vKA8WTemFuBnxW9YbAwMkJVKHsNHW4lHv/g=
2+
github.com/cilium/ebpf v0.17.2/go.mod h1:9X5VAsIOck/nCAp0+nCSVzub1Q7x+zKXXItTMYfNE+E=
33
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
44
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
5-
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
6-
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
5+
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
6+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
77
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
88
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
9+
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=
10+
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
911
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
1012
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
1113
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
@@ -24,11 +26,11 @@ github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3
2426
github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc=
2527
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
2628
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
27-
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
28-
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
29+
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
30+
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
2931
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
3032
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
31-
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
32-
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
33+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
34+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
3335
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
3436
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

0 commit comments

Comments
 (0)