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