forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test script for XDP_TX action (iovisor#2066)
Mostly a cut and paste of the xdp_drop_count.py with needed changes for doing a source and destination MAC swap. Signed-off-by: Andy Gospodarek <[email protected]>
- Loading branch information
1 parent
a07ab90
commit c7f5887
Showing
1 changed file
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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] <ifdev>".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 <uapi/linux/bpf.h> | ||
#include <linux/in.h> | ||
#include <linux/if_ether.h> | ||
#include <linux/if_packet.h> | ||
#include <linux/if_vlan.h> | ||
#include <linux/ip.h> | ||
#include <linux/ipv6.h> | ||
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() |