BPF classifier and actions in tc(8) Linux BPF classifier and actions in tc(8) (NAME) tc-bpf - BPF / (SYNOPSIS) () eBPF: tc filter ... bpf [ object-file OBJ_FILE ] [ section CLS_NAME ] [ export UDS_FILE ] [ verbose ] [ direct-action | da ] [ skip_hw | skip_sw ] [ police POLICE_SPEC ] [ action ACTION_SPEC ] [ classid CLASSID ] tc action ... bpf [ object-file OBJ_FILE ] [ section CLS_NAME ] [ export UDS_FILE ] [ verbose ] () cBPF: tc filter ... bpf [ bytecode-file BPF_FILE | bytecode BPF_BYTECODE ] [ police POLICE_SPEC ] [ action ACTION_SPEC ] [ classid CLASSID ] tc action ... bpf [ bytecode-file BPF_FILE | bytecode BPF_BYTECODE ] (DESCRIPTION) ( eBPF ) ( BPF cBPF ) . . . eBPF cBPF . cBPF eBPF . (JIT) . JIT eBPF : * x86_64 ( 3.18) * arm64 ( 3.18) * s390 ( 4.1) * ppc64 ( 4.8) * sparc64 ( 4.12) * mips64 ( 4.13) * arm32 ( 4.14) * x86_32 ( 4.18) cBPF () eBPF JIT : * ppc32 * sparc32 * mips32 eBPF cBPF . JIT eBPF eBPF . LLVM eBPF eBPF C . eBPF <<>> (maps) . eBPF / eBPF eBPF . qdisc (ingress) (egress) eBPF cBPF . eBPF/cBPF . (feature bloat) . . eBPF classid . (PARAMETERS) object-file (object file) (ELF) eBPF eBPF . LLVM clang(1) C eBPF eBPF ( (EXAMPLES) ). eBPF . section ELF eBPF . "classifier" "action" . . export (Unix domain socket) . eBPF "maps" eBPF (file descriptors) <<>> (agent) eBPF tc . IPC bpf(2) eBPF . verbose eBPF eBPF . . direct-action | da eBPF TC TC ( TC_ACT_OK TC_ACT_SHOT ) . skip_hw | skip_sw (hardware offload). TC . skip_hw . skip_sw eBPF . . police eBPF/cBPF (police) tc(1) qdisc (ingress). action eBPF/cBPF tc(1) . classid flowid (class identifier) eBPF/cBPF . eBPF/cBPF . -1 . 0 eBPF/cBPF classid . eBPF/cBPF . bytecode cBPF . cBPF 's,c t f k,c t f k,c t f k,...' s 4- . 4- c t f k c cBPF t (jump true offset target) f (jump false offset target) k / (immediate constant/literal) . bpf_asm tools/net/ . bytecode bytecode-file cBPF . bytecode-file cBPF . bytecode cBPF . (EXAMPLES) eBPF (eBPF TOOLING) eBPF iproute2 : examples/bpf/ eBPF bpf(2) cls_bpf act_bpf . eBPF/eBPF JIT : echo 1 > /proc/sys/net/core/bpf_jit_enable C LLVM : clang -O2 -emit-llvm -c bpf.c -o - | llc -march=bpf -filetype=obj -o bpf.o (alias) : __bcc() { clang -O2 -emit-llvm -c $1 -o - | llc -march=bpf -filetype=obj -o "`basename $1 .c`.o" } alias bcc=__bcc classid ( -1) : #include #ifndef __section # define __section(x) __attribute__((section(x), used)) #endif __section("classifier") int cls_main(struct __sk_buff *skb) { return -1; } char __license[] __section("license") = "GPL"; eBPF (eBPF PROGRAMMING) . . eBPF . tc . C . eBPF GPL . objdump(1) ELF: objdump -h bpf.o [...] 3 classifier 000007f8 0000000000000000 0000000000000000 00000040 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 4 action-mark 00000088 0000000000000000 0000000000000000 00000838 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 5 action-rand 00000098 0000000000000000 0000000000000000 000008c0 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 6 maps 00000030 0000000000000000 0000000000000000 00000958 2**2 CONTENTS, ALLOC, LOAD, DATA 7 license 00000004 0000000000000000 0000000000000000 00000988 2**0 CONTENTS, ALLOC, LOAD, DATA [...] eBPF ELF ( "object-file" "obj" ): bcc bpf.c tc filter add dev em1 parent 1: bpf obj bpf.o flowid 1:1 ELF "mycls" : tc filter add dev em1 parent 1: bpf obj bpf.o sec mycls flowid 1:1 "bpf.o" "mycls" : tc filter show dev em1 filter parent 1: protocol all pref 49152 bpf filter parent 1: protocol all pref 49152 bpf handle 0x1 flowid 1:1 bpf.o:[mycls] qdisc ... tc qdisc add dev em1 handle ffff: ingress tc filter add dev em1 parent ffff: bpf obj bpf.o sec mycls flowid ffff:1 ... : tc filter show dev em1 parent ffff: filter protocol all pref 49152 bpf filter protocol all pref 49152 bpf handle 0x1 flowid ffff:1 bpf.o:[mycls] (ingress) . (mangle) (drop) . ifb (policing) . eBPF / . eBPF . : tc filter add dev em1 parent 1: bpf obj bpf.o flowid 1:1 \ action bpf obj bpf.o sec action-mark \ action bpf obj bpf.o sec action-rand ok eBPF . eBPF tc(8) eBPF . : 1) eBPF tc(8) . iproute2 : examples/bpf/ 2) tc exec eBPF sh(1) . tc stdin stdout stderr fd eBPF . : tc exec bpf imp /tmp/bpf tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf flowid 1:1 \ action bpf obj bpf.o sec action-mark \ action bpf obj bpf.o sec action-rand ok eBPF (export) . tc eBPF . eBPF . BPF_NUM_MAPS . BPF_MAP eBPF bpf(2) eBPF . eBPF . id struct bpf_elf_map eBPF tc . : sh# env | grep BPF BPF_NUM_MAPS=3 BPF_MAP1=6 BPF_MAP0=5 BPF_MAP2=7 sh# ls -la /proc/self/fd [...] lrwx------. 1 root root 64 Apr 14 16:46 5 -> anon_inode:bpf-map lrwx------. 1 root root 64 Apr 14 16:46 6 -> anon_inode:bpf-map lrwx------. 1 root root 64 Apr 14 16:46 7 -> anon_inode:bpf-map sh# my_bpf_agent eBPF eBPF classid eBPF . eBPF eBPF . eBPF eBPF ( tracing) . eBPF (eBPF PROGRAMMING) eBPF C ( ). linux/bpf.h eBPF eBPF . (flow dissector) eBPF examples/bpf iproute2 . C : 0 -1 classid else classid C ( linux/pkt_cls.h ): TC_ACT_OK (0) TC_ACT_SHOT (2) (drop) TC_ACT_UNSPEC (-1) tc ( -1 ) TC_ACT_PIPE (3) ( ) TC_ACT_RECLASSIFY (1) else eBPF cBPF . C [0, 255] (mark) . classid (minor handle) qdisc : #include #include #include #include #include "helpers.h" struct tuple { long packets; long bytes; }; #define BPF_MAP_ID_STATS 1 /* agent's map identifier */ #define BPF_MAX_MARK 256 struct bpf_elf_map __section("maps") map_stats = { .type = BPF_MAP_TYPE_ARRAY, .id = BPF_MAP_ID_STATS, .size_key = sizeof(uint32_t), .size_value = sizeof(struct tuple), .max_elem = BPF_MAX_MARK, .pinning = PIN_GLOBAL_NS, }; static inline void cls_update_stats(const struct __sk_buff *skb, uint32_t mark) { struct tuple *tu; tu = bpf_map_lookup_elem(&map_stats, &mark); if (likely(tu)) { __sync_fetch_and_add(&tu->packets, 1); __sync_fetch_and_add(&tu->bytes, skb->len); } } __section("cls") int cls_main(struct __sk_buff *skb) { uint32_t mark = skb->mark; if (unlikely(mark >= BPF_MAX_MARK)) return 0; cls_update_stats(skb, mark); return TC_H_MAKE(TC_H_ROOT, mark); } char __license[] __section("license") = "GPL"; (port redirector) 80 RSS [8080, 8087] (demux) qdisc (ingress) . IPv6 : #include #include #include #include #include #include #include #include #include "helpers.h" static inline void set_tcp_dport(struct __sk_buff *skb, int nh_off, __u16 old_port, __u16 new_port) { bpf_l4_csum_replace(skb, nh_off + offsetof(struct tcphdr, check), old_port, new_port, sizeof(new_port)); bpf_skb_store_bytes(skb, nh_off + offsetof(struct tcphdr, dest), &new_port, sizeof(new_port), 0); } static inline int lb_do_ipv4(struct __sk_buff *skb, int nh_off) { __u16 dport, dport_new = 8080, off; __u8 ip_proto, ip_vl; ip_proto = load_byte(skb, nh_off + offsetof(struct iphdr, protocol)); if (ip_proto != IPPROTO_TCP) return 0; ip_vl = load_byte(skb, nh_off); if (likely(ip_vl == 0x45)) nh_off += sizeof(struct iphdr); else nh_off += (ip_vl & 0xF) << 2; dport = load_half(skb, nh_off + offsetof(struct tcphdr, dest)); if (dport != 80) return 0; off = skb->queue_mapping & 7; set_tcp_dport(skb, nh_off - BPF_LL_OFF, __constant_htons(80), __cpu_to_be16(dport_new + off)); return -1; } __section("lb") int lb_main(struct __sk_buff *skb) { int ret = 0, nh_off = BPF_LL_OFF + ETH_HLEN; if (likely(skb->protocol == __constant_htons(ETH_P_IP))) ret = lb_do_ipv4(skb, nh_off); return ret; } char __license[] __section("license") = "GPL"; helpers.h : /* Misc helper macros. */ #define __section(x) __attribute__((section(x), used)) #define offsetof(x, y) __builtin_offsetof(x, y) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) /* Object pinning settings */ #define PIN_NONE 0 #define PIN_OBJECT_NS 1 #define PIN_GLOBAL_NS 2 /* ELF map definition */ struct bpf_elf_map { __u32 type; __u32 size_key; __u32 size_value; __u32 max_elem; __u32 flags; __u32 id; __u32 pinning; __u32 inner_id; __u32 inner_idx; }; /* Some used BPF function calls. */ static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = (void *) BPF_FUNC_skb_store_bytes; static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = (void *) BPF_FUNC_l4_csum_replace; static void *(*bpf_map_lookup_elem)(void *map, void *key) = (void *) BPF_FUNC_map_lookup_elem; /* Some used BPF intrinsics. */ unsigned long long load_byte(void *skb, unsigned long long off) asm ("llvm.bpf.load.byte"); unsigned long long load_half(void *skb, unsigned long long off) asm ("llvm.bpf.load.half"); (best practice) eBPF tc . . eBPF (eBPF DEBUGGING) filter action tc bpf verbose eBPF . . JIT eBPF/cBPF dmesg(1) : echo 2 > /proc/sys/net/core/bpf_jit_enable tools/net/ bpf_jit_disasm : bpf_jit_disasm -o eBPF/cBPF test_bpf . ... modprobe test_bpf ... dmesg(1) . JIT . . JIT . cBPF eBPF cBPF . bpf_jit_enable . bpf_jit_disasm eBPF cBPF . eBPF C . tc . classid 1:1 : tc filter add dev em1 parent 1: bpf bytecode '1,6 0 0 4294967295,' flowid 1:1 4- cBPF . 4- c t f k c cBPF t f k / . -1 . (egress) (Willem de Bruijn) BPF iptables(8) BPF libpcap tc(8) : #include #include int main(int argc, char **argv) { struct bpf_program prog; struct bpf_insn *ins; int i, ret, dlt = DLT_RAW; if (argc < 2 || argc > 3) return 1; if (argc == 3) { dlt = pcap_datalink_name_to_val(argv[1]); if (dlt == -1) return 1; } ret = pcap_compile_nopcap(-1, dlt, &prog, argv[argc - 1], 1, PCAP_NETMASK_UNKNOWN); if (ret) return 1; printf("%d,", prog.bf_len); ins = prog.bf_insns; for (i = 0; i < prog.bf_len - 1; ++ins, ++i) printf("%u %u %u %u,", ins->code, ins->jt, ins->jf, ins->k); printf("%u %u %u %u", ins->code, ins->jt, ins->jf, ins->k); pcap_freecode(&prog); return 0; } tcpdump(8) classid : bpftool EN10MB 'tcp[tcpflags] & tcp-syn != 0' > /var/bpf/tcp-syn tc filter add dev em1 parent 1: bpf bytecode-file /var/bpf/tcp-syn flowid 1:1 : tcpdump -iem1 -ddd 'tcp[tcpflags] & tcp-syn != 0' | tr ' ',' > /var/bpf/tcp-syn libpcap cBPF tools/net/ BPF bpf_asm . (FURTHER READING) . bpf_asm IPv4/TCP foobar: ldh [12] jne #0x800, drop ldb [23] jneq #6, drop ret #-1 drop: ret #0 : bpf_asm foobar > /var/bpf/tcp-syn tc filter add dev em1 parent 1: bpf bytecode-file /var/bpf/tcp-syn flowid 1:1 BPF tools/net/ BPF bpf_dbg pcap . BPF . eBPF . (FURTHER READING) BPF Documentation/networking/filter.txt . eBPF tc(8) iproute2 examples/bpf/ . (SEE ALSO) tc(8), tc-ematch(8) bpf(2) bpf(4) (AUTHORS) (Daniel Borkmann) . : iproute2 18 May 2015 BPF classifier and actions in tc(8)