190 lines
4.4 KiB
Python
Executable File
190 lines
4.4 KiB
Python
Executable File
#!/usr/bin/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 |= BPF.XDP_FLAGS_SKB_MODE
|
|
|
|
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 = """
|
|
#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_PERCPU_ARRAY(dropcnt, long, 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 (index == 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()
|