Skip to content

Commit 562b06f

Browse files
committed
Allow monitoring bandwidth usage of CGroups
This adds the CGroup V2 ingress and egress hook points. The user can attach to any given CGroup and see bandwidth statistics just for that CGroup. This also has the advantage of seeing VPN traffic. Attaching to the root CGroup shows statics for the entire system.
1 parent 8b404ac commit 562b06f

7 files changed

+168
-42
lines changed

counter.c

+75-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
// SOFTWARE.
2121

22-
// go:build ignore
22+
//go:build ignore
2323

2424
#include "vmlinux.h"
2525

@@ -44,6 +44,7 @@
4444

4545
#define OK 1
4646
#define NOK 0
47+
#define ALLOW_PKT 1
4748

4849
// Map key struct for IP traffic
4950
typedef struct statkey_t {
@@ -236,6 +237,7 @@ static inline void process_eth(void *data, void *data_end, __u64 pkt_len) {
236237

237238
// validate Ethernet size
238239
if ((void *)eth + sizeof(*eth) > data_end) {
240+
bpf_printk("size validation failure");
239241
return;
240242
}
241243

@@ -264,6 +266,7 @@ static inline void process_eth(void *data, void *data_end, __u64 pkt_len) {
264266
break;
265267
}
266268
default:
269+
bpf_printk("wrong packet type: %d", eth->h_proto);
267270
return;
268271
}
269272

@@ -280,6 +283,63 @@ static inline void process_eth(void *data, void *data_end, __u64 pkt_len) {
280283
}
281284
}
282285

286+
/**
287+
* Process SKB as it is seen by the cgroup, which is without the ethernet
288+
* headers
289+
*
290+
* @param skb The CGroup skb
291+
*
292+
* @return none
293+
*
294+
* @throws none
295+
*/
296+
static inline void process_cgroup_skb(struct __sk_buff *skb) {
297+
void *data = (void *)(long)skb->data;
298+
void *data_end = (void *)(long)skb->data_end;
299+
__u64 pkt_len = skb->len;
300+
301+
// initialize key
302+
statkey key;
303+
__builtin_memset(&key, 0, sizeof(key));
304+
305+
switch (bpf_ntohs(skb->protocol)) {
306+
case ETH_P_IP: {
307+
struct iphdr *ip4 = data;
308+
309+
if (process_ip4(ip4, data_end, &key) == NOK) {
310+
return;
311+
}
312+
313+
break;
314+
}
315+
case ETH_P_IPV6: {
316+
struct ipv6hdr *ip6 = data;
317+
318+
if (process_ip6(ip6, data_end, &key) == NOK) {
319+
return;
320+
}
321+
322+
break;
323+
}
324+
default:
325+
bpf_printk("wrong packet type: %d", skb->protocol);
326+
return;
327+
328+
}
329+
330+
// lookup value in hash
331+
statvalue *val = (statvalue *)bpf_map_lookup_elem(&pkt_count, &key);
332+
if (val) {
333+
// atomic XADD, doesn't need bpf_spin_lock()
334+
__sync_fetch_and_add(&val->packets, 1);
335+
__sync_fetch_and_add(&val->bytes, pkt_len);
336+
} else {
337+
statvalue initval = {.packets = 1, .bytes = pkt_len};
338+
339+
bpf_map_update_elem(&pkt_count, &key, &initval, BPF_NOEXIST);
340+
}
341+
}
342+
283343
/**
284344
* Process the packet for traffic control and take necessary actions.
285345
*
@@ -351,6 +411,20 @@ int tc_count_packets(struct __sk_buff *skb) {
351411
return TC_ACT_UNSPEC;
352412
}
353413

414+
SEC("cgroup_skb/ingress")
415+
int cgroup_skb_ingress(struct __sk_buff *skb) {
416+
process_cgroup_skb(skb);
417+
418+
return ALLOW_PKT;
419+
}
420+
421+
SEC("cgroup_skb/egress")
422+
int cgroup_skb_egress(struct __sk_buff *skb) {
423+
process_cgroup_skb(skb);
424+
425+
return ALLOW_PKT;
426+
}
427+
354428
/**
355429
* Process TCP socket information and populate the key structure with
356430
* extracted data.

counter_arm64_bpfel.go

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

counter_arm64_bpfel.o

18.7 KB
Binary file not shown.

counter_x86_bpfel.go

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

counter_x86_bpfel.o

22.2 KB
Binary file not shown.

flags.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const (
4040
)
4141

4242
var (
43-
ifname, xdpMode *string
43+
ifname, xdpMode, useCGroup *string
4444
jsonOutput, version, help, useXDP, useKProbes, enableTUI *bool
4545
timeout, refresh *time.Duration
4646
xdpAttachFlags link.XDPAttachFlags
@@ -51,6 +51,7 @@ func parseFags() {
5151

5252
help = fs.Bool('?', "help", "display help")
5353
jsonOutput = fs.Bool('j', "json", "if true, output in JSON format")
54+
useCGroup = fs.String('c', "cgroup", "", "the path to a CGroup V2 to measure statistics on")
5455
useXDP = fs.Bool('x', "xdp", "if true, use XDP instead of TC (this disables egress statistics)")
5556
useKProbes = fs.Bool('k', "kprobes", "if true, use KProbes for per-proces TCP/UDP statistics")
5657
enableTUI = fs.Bool('g', "tui", "if true, enable TUI")

main.go

+39
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ func main() {
6868
}()
6969

7070
switch {
71+
case *useCGroup != "":
72+
links = startCgroup(objs, *useCGroup, links)
7173
// KProbes w/ PID tracking
7274
case *useKProbes:
7375
hooks := []kprobeHook{
@@ -289,3 +291,40 @@ func startTC(objs counterObjects, iface *net.Interface, links []link.Link) []lin
289291

290292
return links
291293
}
294+
295+
func startCgroup(objs counterObjects, cgroupPath string, links []link.Link) []link.Link {
296+
var l link.Link
297+
298+
err := features.HaveProgramType(ebpf.CGroupSKB)
299+
if errors.Is(err, ebpf.ErrNotSupported) {
300+
log.Fatalf("CgroupSKB not supported on this kernel")
301+
}
302+
303+
if err != nil {
304+
log.Fatalf("Error checking CGroupSKB support: %v", err)
305+
}
306+
307+
l, err = link.AttachCgroup(link.CgroupOptions{
308+
Program: objs.CgroupSkbIngress,
309+
Attach: ebpf.AttachCGroupInetIngress,
310+
Path: cgroupPath,
311+
})
312+
if err != nil {
313+
log.Fatalf("Error attaching CgroupSkbIngress to %s: %v", cgroupPath, err)
314+
}
315+
316+
l, err = link.AttachCgroup(link.CgroupOptions{
317+
Program: objs.CgroupSkbEgress,
318+
Attach: ebpf.AttachCGroupInetEgress,
319+
Path: cgroupPath,
320+
})
321+
if err != nil {
322+
log.Fatalf("Error attaching CgroupSkbEgress to %s: %v", cgroupPath, err)
323+
}
324+
325+
links = append(links, l)
326+
327+
log.Printf("Starting on CGroup %s", cgroupPath)
328+
329+
return links
330+
}

0 commit comments

Comments
 (0)