From 8536ded39df82e52e3ca4bbeec434212e182b764 Mon Sep 17 00:00:00 2001 From: radiantly Date: Sun, 6 Nov 2022 10:47:24 +0530 Subject: [PATCH] Use tc's direct action mode --- .github/test_udp.py | 23 +++++++++++++++-------- Makefile | 10 ++++++---- README.md | 5 +++-- bpf.c | 7 ++----- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/.github/test_udp.py b/.github/test_udp.py index d179e35..eef36f6 100644 --- a/.github/test_udp.py +++ b/.github/test_udp.py @@ -1,3 +1,4 @@ +import argparse import random import sys from concurrent.futures import ThreadPoolExecutor, as_completed @@ -31,11 +32,13 @@ def sendPacket(host): def main(): - if len(sys.argv) != 2: - print("Usage: python test_udp.py SERVER") - sys.exit(1) + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true") + parser.add_argument("host", help="The host to test") + + args = parser.parse_args() - host = gethostbyname(sys.argv[1]) + host = gethostbyname(args.host) rtt = [] failures = 0 @@ -43,15 +46,19 @@ def main(): print(f"Sending {PACKETS_TO_SEND} packets to {host}") futures = [executor.submit(sendPacket, host) for _ in range(PACKETS_TO_SEND)] - for i, future in enumerate(as_completed(futures)): + for future in as_completed(futures): result = future.result() if result == -1: failures += 1 else: rtt.append(result) - print( - f"\r{i}/{PACKETS_TO_SEND} packets sent (failures: {failures})", flush=True, end="" - ) + + if args.verbose: + print( + f"\r{failures + len(rtt)}/{PACKETS_TO_SEND} packets sent (failures: {failures})", + flush=True, + end="", + ) print("\r", end="") print(plotille.hist(rtt, bins=10)) diff --git a/Makefile b/Makefile index 233e165..ebed8f9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ -DEVICE=eth0 +# If the DEVICE env variable is not set, choose first non-loopback/non-virt interface +DEVICE ?= $(shell ip -br l | awk '$$1 !~ "lo|vir" { print $$1; exit }') all: qdisc @@ -8,13 +9,14 @@ bpf.o: bpf.c debug: DEBUG = -DDEBUG debug: all + -sudo tc exec bpf dbg qdisc-del: - -sudo tc qdisc del dev $(DEVICE) ingress handle ffff: + -sudo tc qdisc del dev $(DEVICE) clsact qdisc: qdisc-del bpf.o - sudo tc qdisc add dev $(DEVICE) ingress handle ffff: && \ - sudo tc filter add dev $(DEVICE) parent ffff: bpf obj bpf.o sec classifier flowid ffff:1 action bpf obj bpf.o sec action ok + sudo tc qdisc add dev $(DEVICE) clsact && \ + sudo tc filter add dev $(DEVICE) ingress bpf direct-action obj bpf.o clean: qdisc-del -rm bpf.o diff --git a/README.md b/README.md index dfa328b..ad8ead6 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,14 @@ Visit https://udpquiz.com/ to try it out! export DEVICE=eth0 # Compile and load -make -e +make # Once complete, the filter and qdisc can be removed -make -e clean +make clean ``` ## References - [Send ICMP Echo Replies using eBPF](https://fnordig.de/2017/03/04/send-icmp-echo-replies-using-ebpf/) - [Bypassing Captive Portals](https://blog.chebro.dev/posts/bypassing-captive-portals) +- [tc direct action mode for BPF](https://qmonnet.github.io/whirl-offload/2020/04/11/tc-bpf-direct-action/) diff --git a/bpf.c b/bpf.c index a7bd212..53e9ff4 100644 --- a/bpf.c +++ b/bpf.c @@ -21,11 +21,6 @@ SEC("classifier") int cls_main(struct __sk_buff *skb) { - return -1; -} - -SEC("action") -int act_main(struct __sk_buff *skb) { /* packet data is stored in skb->data */ void *data = (void *)(long)skb->data; @@ -56,6 +51,8 @@ int act_main(struct __sk_buff *skb) { __be32 src_ip = ip->saddr; __be32 dst_ip = ip->daddr; + trace_printk("FROM: %d...%d", src_ip & 0xff, src_ip >> 24); + // ignore private ip ranges 10.0.0.0/8, 127.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 if ((src_ip & 0xff) == 10 || ((src_ip & 0xff) == 127) || (src_ip & 0xf0ff) == 0x10ac || (src_ip & 0xffff) == 0xa8c0 || (dst_ip & 0xff) == 10 || ((dst_ip & 0xff) == 127) || (dst_ip & 0xf0ff) == 0x10ac || (dst_ip & 0xffff) == 0xa8c0)