• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env 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("e.g.: {0} eth0\n".format(sys.argv[0]))
20    exit(1)
21
22if len(sys.argv) < 2 or len(sys.argv) > 3:
23    usage()
24
25if len(sys.argv) == 2:
26    device = sys.argv[1]
27
28if len(sys.argv) == 3:
29    if "-S" in sys.argv:
30        # XDP_FLAGS_SKB_MODE
31        flags |= 2 << 0
32
33    if "-S" == sys.argv[1]:
34        device = sys.argv[2]
35    else:
36        device = sys.argv[1]
37
38mode = BPF.XDP
39#mode = BPF.SCHED_CLS
40
41if mode == BPF.XDP:
42    ret = "XDP_DROP"
43    ctxtype = "xdp_md"
44else:
45    ret = "TC_ACT_SHOT"
46    ctxtype = "__sk_buff"
47
48# load BPF program
49b = BPF(text = """
50#define KBUILD_MODNAME "foo"
51#include <uapi/linux/bpf.h>
52#include <linux/in.h>
53#include <linux/if_ether.h>
54#include <linux/if_packet.h>
55#include <linux/if_vlan.h>
56#include <linux/ip.h>
57#include <linux/ipv6.h>
58
59
60BPF_TABLE("percpu_array", uint32_t, long, dropcnt, 256);
61
62static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
63    struct iphdr *iph = data + nh_off;
64
65    if ((void*)&iph[1] > data_end)
66        return 0;
67    return iph->protocol;
68}
69
70static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) {
71    struct ipv6hdr *ip6h = data + nh_off;
72
73    if ((void*)&ip6h[1] > data_end)
74        return 0;
75    return ip6h->nexthdr;
76}
77
78int xdp_prog1(struct CTXTYPE *ctx) {
79
80    void* data_end = (void*)(long)ctx->data_end;
81    void* data = (void*)(long)ctx->data;
82
83    struct ethhdr *eth = data;
84
85    // drop packets
86    int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX
87    long *value;
88    uint16_t h_proto;
89    uint64_t nh_off = 0;
90    uint32_t index;
91
92    nh_off = sizeof(*eth);
93
94    if (data + nh_off  > data_end)
95        return rc;
96
97    h_proto = eth->h_proto;
98
99    if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
100        struct vlan_hdr *vhdr;
101
102        vhdr = data + nh_off;
103        nh_off += sizeof(struct vlan_hdr);
104        if (data + nh_off > data_end)
105            return rc;
106            h_proto = vhdr->h_vlan_encapsulated_proto;
107    }
108    if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
109        struct vlan_hdr *vhdr;
110
111        vhdr = data + nh_off;
112        nh_off += sizeof(struct vlan_hdr);
113        if (data + nh_off > data_end)
114            return rc;
115            h_proto = vhdr->h_vlan_encapsulated_proto;
116    }
117
118    if (h_proto == htons(ETH_P_IP))
119        index = parse_ipv4(data, nh_off, data_end);
120    else if (h_proto == htons(ETH_P_IPV6))
121       index = parse_ipv6(data, nh_off, data_end);
122    else
123        index = 0;
124
125    value = dropcnt.lookup(&index);
126    if (value)
127        *value += 1;
128
129    return rc;
130}
131""", cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype])
132
133fn = b.load_func("xdp_prog1", mode)
134
135if mode == BPF.XDP:
136    b.attach_xdp(device, fn, flags)
137else:
138    ip = pyroute2.IPRoute()
139    ipdb = pyroute2.IPDB(nl=ip)
140    idx = ipdb.interfaces[device].index
141    ip.tc("add", "clsact", idx)
142    ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name,
143          parent="ffff:fff2", classid=1, direct_action=True)
144
145dropcnt = b.get_table("dropcnt")
146prev = [0] * 256
147print("Printing drops per IP protocol-number, hit CTRL+C to stop")
148while 1:
149    try:
150        for k in dropcnt.keys():
151            val = dropcnt.sum(k).value
152            i = k.value
153            if val:
154                delta = val - prev[i]
155                prev[i] = val
156                print("{}: {} pkt/s".format(i, delta))
157        time.sleep(1)
158    except KeyboardInterrupt:
159        print("Removing filter from device")
160        break;
161
162if mode == BPF.XDP:
163    b.remove_xdp(device, flags)
164else:
165    ip.tc("del", "clsact", idx)
166    ipdb.release()
167