Skip to content

Instantly share code, notes, and snippets.

@yjuba
Last active May 7, 2017 07:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yjuba/b4d0cf3396941bbb4338f9bd04e098ef to your computer and use it in GitHub Desktop.
Save yjuba/b4d0cf3396941bbb4338f9bd04e098ef to your computer and use it in GitHub Desktop.
#define KBUILD_MODNAME "drop_80"
#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_HASH(counter, uint32_t, long);
static inline int tcp_dest_port(void *data, u64 nh_off, void *data_end) {
struct tcphdr *tcph = data + nh_off;
if (data + nh_off + sizeof(struct tcphdr) > data_end) {
return 0;
}
return tcph->dest;
}
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;
}
int drop_80(struct xdp_md *ctx) {
void* data_end = (void*)(long)ctx->data_end;
void* data = (void*)(long)ctx->data;
struct ethhdr *eth = data;
long *value;
long zero = 0;
uint16_t h_proto;
uint64_t nh_off = 0;
uint16_t dest_port;
uint32_t index;
nh_off = sizeof(*eth);
if (data + nh_off > data_end) {
return XDP_PASS;
}
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 XDP_PASS;
}
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 XDP_PASS;
}
h_proto = vhdr->h_vlan_encapsulated_proto;
}
if (h_proto == htons(ETH_P_IP)) {
h_proto = parse_ipv4(data, nh_off, data_end);
nh_off += sizeof(struct iphdr);
} else if (h_proto == htons(ETH_P_IPV6)) {
h_proto = parse_ipv6(data, nh_off, data_end);
nh_off += sizeof(struct ipv6hdr);
} else {
return XDP_PASS;
}
if (h_proto == IPPROTO_TCP) {
dest_port = ntohs(tcp_dest_port(data, nh_off, data_end));
if (dest_port == 80) {
index = (uint32_t)dest_port;
value = counter.lookup_or_init(&index, &zero);
(*value) += 1;
return XDP_DROP;
}
}
return XDP_PASS;
}
#!/usr/bin/env python
from bcc import BPF, libbcc
import ctypes
import time
def load_xdp(source_path, func_name, device):
bpf = BPF(src_file=source_path)
func = bpf.load_func(func_name, BPF.XDP)
bpf.attach_xdp(device, func)
return bpf
def drop_packet(device, pin_path):
bpf = load_xdp("./drop_80.c", "drop_80", device)
print("XDP loaded.")
counter = bpf.get_table("counter")
ret = libbcc.lib.bpf_obj_pin(counter.map_fd, ctypes.c_char_p(pin_path))
old_val = {}
while True:
try:
for k, v in counter.items():
if k.value not in old_val or old_val[k.value] != v.value:
print("Port:{}\tCount:{}".format(k.value, v.value))
old_val[k.value] = v.value
time.sleep(1)
except KeyboardInterrupt:
break;
bpf.remove_xdp(device)
if __name__ == '__main__':
import sys
if len(sys.argv) < 3:
print("Usage: {} <DEVICE> <PIN_PATH>".format(sys.argv[0]))
exit(1)
drop_packet(sys.argv[1], sys.argv[2])
#!/usr/bin/python
from bcc import libbcc, table
import ctypes
import sys
class DropHash(table.HashTable):
def __init__(self, path, keytype, leaftype):
fd = libbcc.lib.bpf_obj_get(ctypes.c_char_p(path))
if fd < 0:
raise ValueError("Failed to open eBPF map")
self.map_fd = fd
self.Key = keytype
self.Leaf = leaftype
def fetch_counter(path):
return DropHash(path, ctypes.c_uint32, ctypes.c_long)
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
print("Usage: {} <PIN_PATH>".format(sys.argv[0]))
exit(1)
for k, v in fetch_counter(sys.argv[1]).items():
print("Port:{}\tCount:{}".format(k.value, v.value))
.PHONY: help
.DEFAULT_GOAL := help
bpf_path = /sys/fs/bpf
counter_path = /sys/fs/bpf/counter
interface = ens3
mount-bpf: ## Mount /sys/fs/bpf
@sudo mount -t bpf bpf $(bpf_path)
exec-drop80: ## Execute drop_80 module
@sudo python ./drop_80.py $(interface) $(counter_path)
fetch-counter: ## Fetch drop_80 counter
@sudo python ./fetch_counter.py $(counter_path)
cleanup: ## Unlink /sys/fs/bpf/counter and remove xdp module
@sudo unlink $(counter_path)
@sudo /sbin/ip link set dev $(interface) xdp off
help:
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment