diff --git a/examples/networking/xdp/xdp_macswap_count.py b/examples/networking/xdp/xdp_macswap_count.py new file mode 100755 index 000000000000..145d00497146 --- /dev/null +++ b/examples/networking/xdp/xdp_macswap_count.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# +# xdp_macswap_count.py Swap Source and Destination MAC addresses on +# incoming packets and transmit packets back on +# same interface in XDP layer and count for which +# protocol type +# +# Copyright (c) 2016 PLUMgrid +# Copyright (c) 2016 Jan Ruth +# Copyright (c) 2018 Andy Gospodarek +# Licensed under the Apache License, Version 2.0 (the "License") + +from bcc import BPF +import pyroute2 +import time +import sys + +flags = 0 +def usage(): + print("Usage: {0} [-S] ".format(sys.argv[0])) + print(" -S: use skb mode\n") + print("e.g.: {0} eth0\n".format(sys.argv[0])) + exit(1) + +if len(sys.argv) < 2 or len(sys.argv) > 3: + usage() + +if len(sys.argv) == 2: + device = sys.argv[1] + +if len(sys.argv) == 3: + if "-S" in sys.argv: + # XDP_FLAGS_SKB_MODE + flags |= 2 << 0 + + if "-S" == sys.argv[1]: + device = sys.argv[2] + else: + device = sys.argv[1] + +mode = BPF.XDP +#mode = BPF.SCHED_CLS + +if mode == BPF.XDP: + ret = "XDP_TX" + ctxtype = "xdp_md" +else: + ret = "TC_ACT_SHOT" + ctxtype = "__sk_buff" + +# load BPF program +b = BPF(text = """ +#define KBUILD_MODNAME "foo" +#include +#include +#include +#include +#include +#include +#include + + +BPF_TABLE("percpu_array", uint32_t, long, dropcnt, 256); + +static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) { + struct iphdr *iph = data + nh_off; + + if ((void*)&iph[1] > data_end) + return 0; + return iph->protocol; +} + +static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) { + struct ipv6hdr *ip6h = data + nh_off; + + if ((void*)&ip6h[1] > data_end) + return 0; + return ip6h->nexthdr; +} + +static void swap_src_dst_mac(void *data) +{ + unsigned short *p = data; + unsigned short dst[3]; + + dst[0] = p[0]; + dst[1] = p[1]; + dst[2] = p[2]; + p[0] = p[3]; + p[1] = p[4]; + p[2] = p[5]; + p[3] = dst[0]; + p[4] = dst[1]; + p[5] = dst[2]; +} + +int xdp_prog1(struct CTXTYPE *ctx) { + + void* data_end = (void*)(long)ctx->data_end; + void* data = (void*)(long)ctx->data; + + struct ethhdr *eth = data; + + // drop packets + int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX + long *value; + uint16_t h_proto; + uint64_t nh_off = 0; + uint32_t index; + + nh_off = sizeof(*eth); + + if (data + nh_off > data_end) + return rc; + + h_proto = eth->h_proto; + + if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { + struct vlan_hdr *vhdr; + + vhdr = data + nh_off; + nh_off += sizeof(struct vlan_hdr); + if (data + nh_off > data_end) + return rc; + h_proto = vhdr->h_vlan_encapsulated_proto; + } + if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { + struct vlan_hdr *vhdr; + + vhdr = data + nh_off; + nh_off += sizeof(struct vlan_hdr); + if (data + nh_off > data_end) + return rc; + h_proto = vhdr->h_vlan_encapsulated_proto; + } + + if (h_proto == htons(ETH_P_IP)) + index = parse_ipv4(data, nh_off, data_end); + else if (h_proto == htons(ETH_P_IPV6)) + index = parse_ipv6(data, nh_off, data_end); + else + index = 0; + + if (h_proto == IPPROTO_UDP) { + swap_src_dst_mac(data); + rc = XDP_TX; + } + + value = dropcnt.lookup(&index); + if (value) + *value += 1; + + return rc; +} +""", cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype]) + +fn = b.load_func("xdp_prog1", mode) + +if mode == BPF.XDP: + b.attach_xdp(device, fn, flags) +else: + ip = pyroute2.IPRoute() + ipdb = pyroute2.IPDB(nl=ip) + idx = ipdb.interfaces[device].index + ip.tc("add", "clsact", idx) + ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name, + parent="ffff:fff2", classid=1, direct_action=True) + +dropcnt = b.get_table("dropcnt") +prev = [0] * 256 +print("Printing drops per IP protocol-number, hit CTRL+C to stop") +while 1: + try: + for k in dropcnt.keys(): + val = dropcnt.sum(k).value + i = k.value + if val: + delta = val - prev[i] + prev[i] = val + print("{}: {} pkt/s".format(i, delta)) + time.sleep(1) + except KeyboardInterrupt: + print("Removing filter from device") + break; + +if mode == BPF.XDP: + b.remove_xdp(device, flags) +else: + ip.tc("del", "clsact", idx) + ipdb.release()