xref: /aosp_15_r20/external/bcc/examples/networking/xdp/xdp_drop_count.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1#!/usr/bin/python
2#
3# xdp_drop_count.py Drop incoming packets on XDP layer and count for which
4#                   protocol type
5#
6# Copyright (c) 2016 PLUMgrid
7# Copyright (c) 2016 Jan Ruth
8# Licensed under the Apache License, Version 2.0 (the "License")
9
10from bcc import BPF
11import pyroute2
12import time
13import sys
14
15flags = 0
16def usage():
17    print("Usage: {0} [-S] <ifdev>".format(sys.argv[0]))
18    print("       -S: use skb mode\n")
19    print("       -D: use driver mode\n")
20    print("       -H: use hardware offload mode\n")
21    print("e.g.: {0} eth0\n".format(sys.argv[0]))
22    exit(1)
23
24if len(sys.argv) < 2 or len(sys.argv) > 3:
25    usage()
26
27offload_device = None
28if len(sys.argv) == 2:
29    device = sys.argv[1]
30elif len(sys.argv) == 3:
31    device = sys.argv[2]
32
33maptype = "percpu_array"
34if len(sys.argv) == 3:
35    if "-S" in sys.argv:
36        # XDP_FLAGS_SKB_MODE
37        flags |= BPF.XDP_FLAGS_SKB_MODE
38    if "-D" in sys.argv:
39        # XDP_FLAGS_DRV_MODE
40        flags |= BPF.XDP_FLAGS_DRV_MODE
41    if "-H" in sys.argv:
42        # XDP_FLAGS_HW_MODE
43        maptype = "array"
44        offload_device = device
45        flags |= BPF.XDP_FLAGS_HW_MODE
46
47mode = BPF.XDP
48#mode = BPF.SCHED_CLS
49
50if mode == BPF.XDP:
51    ret = "XDP_DROP"
52    ctxtype = "xdp_md"
53else:
54    ret = "TC_ACT_SHOT"
55    ctxtype = "__sk_buff"
56
57# load BPF program
58b = BPF(text = """
59#include <uapi/linux/bpf.h>
60#include <linux/in.h>
61#include <linux/if_ether.h>
62#include <linux/if_packet.h>
63#include <linux/if_vlan.h>
64#include <linux/ip.h>
65#include <linux/ipv6.h>
66
67BPF_TABLE(MAPTYPE, uint32_t, long, dropcnt, 256);
68
69static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
70    struct iphdr *iph = data + nh_off;
71
72    if ((void*)&iph[1] > data_end)
73        return 0;
74    return iph->protocol;
75}
76
77static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) {
78    struct ipv6hdr *ip6h = data + nh_off;
79
80    if ((void*)&ip6h[1] > data_end)
81        return 0;
82    return ip6h->nexthdr;
83}
84
85int xdp_prog1(struct CTXTYPE *ctx) {
86
87    void* data_end = (void*)(long)ctx->data_end;
88    void* data = (void*)(long)ctx->data;
89
90    struct ethhdr *eth = data;
91
92    // drop packets
93    int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX
94    long *value;
95    uint16_t h_proto;
96    uint64_t nh_off = 0;
97    uint32_t index;
98
99    nh_off = sizeof(*eth);
100
101    if (data + nh_off  > data_end)
102        return rc;
103
104    h_proto = eth->h_proto;
105
106    // parse double vlans
107    #pragma unroll
108    for (int i=0; i<2; i++) {
109        if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
110            struct vlan_hdr *vhdr;
111
112            vhdr = data + nh_off;
113            nh_off += sizeof(struct vlan_hdr);
114            if (data + nh_off > data_end)
115                return rc;
116                h_proto = vhdr->h_vlan_encapsulated_proto;
117        }
118    }
119
120    if (h_proto == htons(ETH_P_IP))
121        index = parse_ipv4(data, nh_off, data_end);
122    else if (h_proto == htons(ETH_P_IPV6))
123       index = parse_ipv6(data, nh_off, data_end);
124    else
125        index = 0;
126
127    value = dropcnt.lookup(&index);
128    if (value)
129        __sync_fetch_and_add(value, 1);
130
131    return rc;
132}
133""", cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype,
134			 "-DMAPTYPE=\"%s\"" % maptype],
135     device=offload_device)
136
137fn = b.load_func("xdp_prog1", mode, offload_device)
138
139if mode == BPF.XDP:
140    b.attach_xdp(device, fn, flags)
141else:
142    ip = pyroute2.IPRoute()
143    ipdb = pyroute2.IPDB(nl=ip)
144    idx = ipdb.interfaces[device].index
145    ip.tc("add", "clsact", idx)
146    ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name,
147          parent="ffff:fff2", classid=1, direct_action=True)
148
149dropcnt = b.get_table("dropcnt")
150prev = [0] * 256
151print("Printing drops per IP protocol-number, hit CTRL+C to stop")
152while 1:
153    try:
154        for k in dropcnt.keys():
155            val = dropcnt[k].value if maptype == "array" else dropcnt.sum(k).value
156            i = k.value
157            if val:
158                delta = val - prev[i]
159                prev[i] = val
160                print("{}: {} pkt/s".format(i, delta))
161        time.sleep(1)
162    except KeyboardInterrupt:
163        print("Removing filter from device")
164        break
165
166if mode == BPF.XDP:
167    b.remove_xdp(device, flags)
168else:
169    ip.tc("del", "clsact", idx)
170    ipdb.release()
171