TC-BPF(8)
โจ TC-BPF(8) โจ
TC-BPF(8) is the Linux manual page for BPF classifier and actions in tc(8), and it's the page for all things BPF inside tc. Let's get into it ๐ฆ
๐ธ NAME ๐ธ
BPF, bestie ๐ It's a BPF programmable classifier and actions for ingress/egress queueing disciplines. Basically you write your own little traffic-sorting brain and plug it into the ingress and egress qdiscs. Iconic.
๐ง SYNOPSIS ๐ง
๐ eBPF classifier (filter) or action:
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 classifier (filter) or action:
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 ๐
okay so, Extended Berkeley Packet Filter (eBPF) and classic Berkeley Packet Filter are both available as a fully programmable and highly efficient classifier and actions ๐ฅ The classic one was originally just called BPF, but to keep things clear this page calls it cBPF. Both give you a tiny instruction set for small programs that can be loaded safely into the kernel and run inside a tiny virtual machine in kernel space. An in-kernel verifier makes sure a program always terminates, and that it neither crashes nor leaks data from the kernel. Not a single drama ๐
In Linux, it's generally considered that eBPF is the successor of cBPF. The kernel internally transforms cBPF expressions into eBPF expressions and then executes the eBPF ones. Execution can happen in an interpreter, or at setup time the code can be just-in-time compiled (JIT'ed) to run as native machine code โก
Right now, the eBPF JIT compiler is available for these architectures:
x86_64(since Linux 3.18)arm64(since Linux 3.18)s390(since Linux 4.1)ppc64(since Linux 4.8)sparc64(since Linux 4.12)mips64(since Linux 4.13)arm32(since Linux 4.14)x86_32(since Linux 4.18)
And these ones have cBPF but have not (yet) switched to eBPF JIT support ๐ฅฒ
ppc32sparc32mips32
eBPF's instruction set follows similar principles to cBPF's, but it's modelled much closer to the underlying architecture so it can mimic native instruction sets and run faster. It's designed to be JIT'ed with a one to one mapping, which also opens the door for compilers to generate optimized eBPF code through an eBPF backend that runs almost as fast as natively compiled code. Since LLVM provides such an eBPF backend, eBPF programs can be written in a subset of the C language, which is so convenient ๐ซถ eBPF infrastructure also comes with a construct called "maps". eBPF maps are key/value stores that are shared between multiple eBPF programs, and also between eBPF programs and user space applications.
For the traffic control subsystem, classifiers and actions that can be attached to ingress and egress qdiscs can be written in eBPF or cBPF. The big advantage over other classifiers and actions is that eBPF/cBPF gives you the generic framework, and you get to implement your highly specialized use cases efficiently. So the classifier or action you write this way won't suffer from feature bloat, and it can execute its task super efficiently. It allows for non-linear classification and even merging the action part into the classification. Combined with efficient eBPF map data structures, user space can push new policies like classids into the kernel without reloading a classifier. It can also gather statistics that get pushed into one map while another map is used for dynamically load balancing traffic based on the determined load. Just a few examples, but you get the vibe ๐
๐ PARAMETERS ๐
๐งฉ object-file
object-fileโ this points to an object file in the Executable and Linkable Format (ELF) that contains eBPF opcodes and eBPF map definitions. The LLVM compiler infrastructure, with clang(1) as its C language front end, is one project that can emit eBPF object files you can pass to the eBPF classifier (more details in the EXAMPLES section). This option is mandatory when you want to load an eBPF classifier or action ๐จ
๐งฉ section
sectionโ the name of the ELF section in the object file where your eBPF classifier or action lives. By default the section name for the classifier is"classifier", and for the action it's"action". A single object file can hold multiple classifiers and actions, so if yours uses a different name than the defaults, you need to specify it here ๐ท๏ธ
๐งฉ export
exportโ this points to a Unix domain socket file. If the eBPF object file also has a section named"maps"with eBPF map specifications, the map file descriptors can be handed off through the Unix domain socket to an eBPF "agent". That agent holds on to all the descriptors aftertcis done with them. It can be some third party app that implements the IPC counterpart for the import, and it uses them to call into the bpf(2) system call to read out or update eBPF map data from user space. Think monitoring, or pushing down new policies ๐
๐งฉ verbose
verboseโ if set, it dumps the eBPF verifier output even when loading the eBPF program worked fine. By default, the verifier log only gets emitted to you when something goes wrong ๐
๐งฉ direct-action | da
direct-action,daโ tells the eBPF classifier to not invoke external TC actions. Instead, it uses the TC action return codes (TC_ACT_OK,TC_ACT_SHOTetc.) for classifiers. Main character energy ๐
๐งฉ skip_hw | skip_sw
skip_hw,skip_swโ these are hardware offload control flags. By default TC will try to offload filters to hardware when that's possible.skip_hwexplicitly disables the attempt to offload.skip_swforces the offload and disables running the eBPF program in the kernel. If hardware offload isn't possible and this flag was set, the kernel reports an error and the filter won't be installed at all ๐ญ
๐งฉ police
policeโ an optional parameter for an eBPF/cBPF classifier. It specifies a police in tc(1) that gets attached to the classifier, for example on an ingress qdisc ๐
๐งฉ action
actionโ also an optional parameter for an eBPF/cBPF classifier. It specifies a subsequent action in tc(1) that gets attached to a classifier โก๏ธ
๐งฉ classid | flowid
classid,flowidโ this gives the default traffic control class identifier for this eBPF/cBPF classifier. The default can also be overwritten by the return code of the eBPF/cBPF program. A default return code of-1means the class identifier you provided here gets used. A return code of0from the eBPF/cBPF program implies that no match took place, and any return code other than those two overrides the default classid. This gives you efficient, non-linear classification with a single eBPF/cBPF program, instead of multiple individual programs for various class identifiers that would each need to reparse the packet contents ๐ง
๐งฉ bytecode
bytecodeโ only used for loading cBPF classifiers and actions. The cBPF bytecode gets passed directly as a text string in the form's,c t f k,c t f k,c t f k,...', wheresis the number of subsequent 4-tuples. Each 4-tuple consists of the decimalsc t f k:cis the cBPF opcode,tis the jump true offset target,fis the jump false offset target, andkis the immediate constant/literal. There are various tools that generate code in this loadable format, for examplebpf_asm, which ships with the Linux kernel source tree undertools/net/. So it's definitely not expected that you hack this by hand ๐ โโ๏ธ Thebytecodeorbytecode-fileoption is mandatory when a cBPF classifier or action is to be loaded.
๐งฉ bytecode-file
bytecode-fileโ also used to load a cBPF classifier or action. It's basically the same asbytecode, except the cBPF bytecode isn't passed straight through the command line. It lives in a text file instead ๐
๐ EXAMPLES ๐
๐ ๏ธ eBPF TOOLING
A full blown example, including eBPF agent code, can be found inside the iproute2 source package under examples/bpf/ ๐
As prerequisites, the kernel needs to have the eBPF system call, namely bpf(2), enabled. It also ships with the cls_bpf and act_bpf kernel modules for the traffic control subsystem. To enable eBPF/eBPF JIT support, depending on which of the two the given architecture supports:
echo 1 > /proc/sys/net/core/bpf_jit_enable
A given restricted C file can be compiled via LLVM like this:
clang -O2 -emit-llvm -c bpf.c -o - | llc -march=bpf -filetype=obj -o bpf.o
Okay so bestie, we're picking up right where the examples get spicy ๐
Adding an eBPF classifier from an object file is trivial if the classifier sits in the default ELF section ๐ฆ (quick heads-up: you can also write obj instead of object-file, because shortcuts slay โจ):
bcc bpf.c
tc filter add dev em1 parent 1: bpf obj bpf.o flowid 1:1
If your classifier lives in ELF section mycls, you run the same command but add sec mycls ๐:
tc filter add dev em1 parent 1: bpf obj bpf.o sec mycls flowid 1:1
Dumping the config tells you where the classifier came from. Here it's from object file bpf.o under section mycls, which is so helpful when you've got a dozen filters floating around ๐:
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]
Same program, but now on the ingress qdisc instead of egress ๐ธ:
tc qdisc add dev em1 handle ffff: ingress
tc filter add dev em1 parent ffff: bpf obj bpf.o sec mycls flowid
ffff:1
And then you dump it from there again โจ:
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]
Heads up, ingress has a limit ๐ญ It doesn't have an actual underlying queueing discipline. What it can do is classify, mangle, redirect, or drop packets. If you need queueing on the ingress side, ingress has to redirect packets to the ifb device. Otherwise you can use policing ๐ซถ. Ingress is also great for an early drop point for unwanted packets before they reach the upper layers of the networking stack, for network accounting with eBPF maps that can be shared with egress, or as an early mangle and/or redirection point to other networking devices ๐.
You can stuff multiple eBPF actions and classifiers into one object file, spread across different sections. When you do that, the non-default section names have to be given explicitly, and in this example that applies to both actions ๐:
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
The payoff is that the classifier and both actions can share eBPF maps with each other, as long as the programs are written to do that โญ.
Now here's the fun part. If you want to touch eBPF maps from user space beyond the lifetime of the tc(8) setup, you can hand ownership over to an eBPF agent through Unix domain sockets ๐ง. There are two ways to pull this off:
- Write your own eBPF agent. It sets up the Unix domain socket and speaks the protocol that tc(8) dictates. You can find a code example in the iproute2 source package under
examples/bpf/๐. - Use
tc execto pass the eBPF map file descriptors through a Unix domain socket and spawn an application such as sh(1) ๐. The big perk here: tc places the file descriptors into the environment, so they're available just like the stdin, stdout, and stderr file descriptors. That means if your user applications run inside this fd-owner shell, they can die and restart without losing their eBPF map file descriptors. Here's an example using the classifier and action mix from before:
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
Assuming the maps are shared between the classifier and the actions, you only have to export them once, for example from inside the classifier or action command. tc sets up all the eBPF map file descriptors the first time the object file gets parsed ๐.
When a shell gets spawned, the environment picks up a few eBPF related variables ๐ฅ:
BPF_NUM_MAPSโ the total number of maps transferred over the Unix domain socket.BPF_MAP<X>โ its value is the file descriptor number you can access from your eBPF agent. You can use it directly as the file descriptor for the bpf(2) system call to read or change eBPF map values.<X>is the identifier of the eBPF map, and it matches theidmember ofstruct bpf_elf_mapfrom the tc eBPF map specification.
Here's what the environment looks like in this example ๐ง:
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 agents are seriously useful ๐. They can prepopulate eBPF maps from user space, monitor statistics through maps, and then use that feedback to, say, rewrite classids in eBPF map values while things are running. Since agents are just normal applications, they can also receive traffic control policies from external controllers and push them down into eBPF maps so they adapt to network conditions on the fly. And eBPF maps can be shared with other eBPF program types too (tracing, for example), so you can build some really powerful combos ๐ฅ.
โจ eBPF PROGRAMMING โจ
eBPF classifiers and actions are written in restricted C syntax ๐ . In the future there could be new language frontends too, so keep an eye out ๐.
The header linux/bpf.h gives you eBPF helper functions you can call from an eBPF program. This man page only has two minimal, stand-alone examples. If you want to see the possibilities in action, check out the fully fledged flow dissector example in examples/bpf from the iproute2 source package ๐ฆ.
Here are the supported 32 bit classifier return codes from the C program and what they mean ๐:
0โ a mismatch.-1โ the default classid configured from the command line.- anything else โ overrides the default classid, which gives you a facility for non-linear matching โจ.
Here are the supported 32 bit action return codes from the C program and what they mean (from linux/pkt_cls.h) ๐
:
TC_ACT_OK(0) โ terminates the packet processing pipeline and lets the packet proceed ๐ซถ.TC_ACT_SHOT(2) โ terminates the packet processing pipeline and drops the packet ๐.TC_ACT_UNSPEC(-1) โ uses the default action configured from tc (same as returning -1 from a classifier) ๐.TC_ACT_PIPE(3) โ moves on to the next action, if there is one ๐ธ.TC_ACT_RECLASSIFY(1) โ terminates the packet processing pipeline and starts classification from the beginning ๐.- anything else โ an unspecified return code, so no promises there ๐ญ.
Both classifier and action return codes are supported in eBPF and cBPF programs ๐.
To show off that restricted C syntax, here's a minimal toy classifier. It assumes egress packets (say, ones coming from a container) have already been marked in the interval [0, 255]. The program keeps stats on each mark for user space, and it maps the classid to the root qdisc, using the mark itself as the minor handle ๐:
#include <stdint.h>
#include <asm/types.h>
#include <linux/bpf.h>
#include <linux/pkt_sched.h>
#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";
Here's another small one: a port redirector ๐ง. It demuxes destination port 80 into the interval [8080, 8087], steered by RSS, and it can be attached to the ingress qdisc. Adding the egress counterpart and IPv6 support is left as an exercise for the reader, bestie ๐ค:
#include <asm/types.h>
#include <asm/byteorder.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#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";
Okay and now the helper header that both of those examples lean on is helpers.h, and it looks like this ๐
/* 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 time, bestie ๐ : we strongly recommend only having a single eBPF classifier loaded in tc ๐ฆ and doing all your matching and mangling from inside that one program, instead of stacking up a whole list of individual classifiers and separate actions ๐. One classifier tailored to your exact use-case is the most efficient thing to run โจ.
๐ eBPF DEBUGGING ๐
okay so both tc filter and tc action commands for bpf accept an optional verbose parameter ๐, which lets you peek at the eBPF verifier log. Lowkey the best part: it gets dumped by default whenever something goes wrong ๐ญ.
If the eBPF/cBPF JIT compiler is turned on, you can also tell it to emit a debug output of the resulting opcode image into the kernel log ๐, which you can read with dmesg(1) ๐:
echo 2 > /proc/sys/net/core/bpf_jit_enable
The Linux kernel source tree also ships a little helper under tools/net/ called bpf_jit_disasm ๐ง. It reads that opcode image dump out of the kernel log and spits back the disassembly โจ:
bpf_jit_disasm -o
And then there's the Linux kernel's extensive eBPF/cBPF test suite module called test_bpf ๐. Upon ...
modprobe test_bpf
... it runs a whole buffet of test cases and dumps the results into the kernel log, which you can inspect with dmesg(1) ๐. The results can differ depending on whether the JIT compiler is enabled or not ๐คทโโ๏ธ. If any test cases fail, the module will fail to load ๐. In that case, we urge you to file a bug report to the related JIT authors, Linux kernel and networking mailing lists ๐ซถ.
๐ธ cBPF ๐ธ
Real talk: we generally recommend switching to implementing eBPF classifiers and actions ๐. But for the sake of completeness, a few words on how to program in cBPF will be lost here ๐ญ.
Likewise, the bpf_jit_enable switch can be enabled as mentioned already โจ. And tooling like bpf_jit_disasm doesn't care whether eBPF or cBPF code is being loaded ๐.
Unlike eBPF, classifiers and actions are not written in restricted C ๐ โโ๏ธ. They're written in a minimal assembler-like language, or with the help of other tooling ๐ ๏ธ.
The raw interface with tc takes opcodes directly ๐ค. For example, the most minimal classifier, matching on every packet and resulting in the default classid of 1:1, looks like this ๐:
tc filter add dev em1 parent 1: bpf bytecode '1,6 0 0 4294967295,' flowid 1:1
The first decimal of the bytecode sequence is the number of subsequent 4-tuples of cBPF opcodes ๐ข. Each 4-tuple is made of c t f k decimals, where c is the cBPF opcode, t is the jump true offset target, f is the jump false offset target, and k is the immediate constant/literal ๐. This particular one is an unconditional return from the program with an immediate value of -1 โจ.
So, for egress classification, Willem de Bruijn implemented a minimal stand-alone helper tool ๐ under the GNU General Public License version 2 for the iptables(8) BPF extension. It basically abuses the libpcap internal classic BPF compiler ๐ง . His code, derived here for usage with tc(8), looks like this:
#include <pcap.h>
#include <stdio.h>
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;
}
With this small helper, any tcpdump(8) filter expression can be abused as a classifier, where a match results in the default 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
Basically, such a minimal generator is equivalent to this ๐ง:
tcpdump -iem1 -ddd 'tcp[tcpflags] & tcp-syn != 0' | tr '\n' ',' > /var/bpf/tcp-syn
Heads up ๐: libpcap doesn't support all of Linux's specific cBPF extensions in its compiler. So the Linux kernel also ships a minimal BPF assembler calledbpf_asmundertools/net/for full control ๐ . For detailed syntax and semantics on writing these programs by hand, see the references under FURTHER READING.
Here's a trivial toy bpf_asm example for classifying IPv4/TCP packets, saved in a text file called foobar ๐ฆ:
ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ret #-1
drop: ret #0
And you can load a classifier like that with:
bpf_asm foobar > /var/bpf/tcp-syn
tc filter add dev em1 parent 1: bpf bytecode-file /var/bpf/tcp-syn flowid 1:1
For BPF classifiers, the Linux kernel additionally provides a small BPF debugger under tools/net/ called bpf_dbg ๐ฅ. It can be used to test a classifier against pcap files, single-step through it, or add various breakpoints into the classifier program and dump register contents during runtime ๐.
Implementing an action in classic BPF is rather limited, because packet mangling is not supported ๐ญ. That's why it's generally recommended to make the switch to eBPF whenever possible ๐.
๐ FURTHER READING ๐
Further and more technical details about the BPF architecture can be found in the Linux kernel source tree under Documentation/networking/filter.txt ๐ค.
Further details on eBPF tc(8) examples can be found in the iproute2 source tree under examples/bpf/ ๐ธ.
๐ SEE ALSO ๐
tc(8), tc-ematch(8), bpf(2), bpf(4) ๐
๐ AUTHORS ๐
Manpage written by Daniel Borkmann ๐.
Please report corrections or improvements to the Linux kernel networking mailing list: <netdev@vger.kernel.org> ๐ซถ.
Okay bestie, that's the whole thing โจ๐ The sign-off is from iproute2, last touched 18 May 201 ๐ (the date got cut off, so you know the vibe ๐). Stay iconic, and go write some eBPF ๐ฆ๐
๐ฆ where this page comes from
- alpine 3.22: iproute2-doc 6.15.0-r0 ยท GPL-2.0-or-later
- alpine 3.23: iproute2-doc 6.17.0-r0 ยท GPL-2.0-or-later
- alpine 3.24: iproute2-doc 7.0.0-r0 ยท GPL-2.0-or-later
- arch rolling: iproute2 7.2.0-1
- arch rolling: iproute2 7.3.0-1 ยท GPL-2.0-or-later
- centos-stream 10: iproute-tc 7.0.0-2.el10 ยท GPL-2.0-or-later
- centos-stream 9: iproute-tc 6.17.0-3.el9 ยท GPL-2.0-or-later
- debian bookworm: iproute2 6.1.0-3 ยท GPL-2.0-only
- debian trixie: iproute2 6.15.0-1 ยท GPL-2.0-only
- fedora 43: iproute-tc 6.14.0-2.fc43 ยท GPL-2.0-or-later
- fedora 44: iproute-tc 6.17.0-2.fc44 ยท GPL-2.0-or-later
- nixos 26.05: iproute2 7.0.0
- nixpkgs unstable: iproute2 7.2.0
- opensuse-leap 16.0: iproute2 6.12-160000.4.1 ยท GPL-2.0-only
- opensuse-leap 16.0: iproute2 6.12-160000.4.1 ยท GPL-2.0-only
- opensuse-leap 16.0: iproute2 6.12-160000.4.1 ยท GPL-2.0-only
- opensuse-leap 16.0: iproute2 6.12-160000.4.1 ยท GPL-2.0-only
- ubuntu noble: iproute2 6.1.0-1ubuntu6 ยท GPL-2.0-only
- ubuntu noble: iproute2 6.1.0-1ubuntu6.4 ยท GPL-2.0-only
- ubuntu questing: iproute2 6.16.0-1ubuntu2
- ubuntu questing: iproute2 6.16.0-1ubuntu2.1 ยท GPL-2.0-only
- ubuntu questing: iproute2 6.16.0-1ubuntu2.2
- ubuntu resolute: iproute2 6.19.0-1ubuntu1 ยท GPL-2.0-only
- ubuntu resolute: iproute2 6.19.0-1ubuntu1.1 ยท GPL-2.0-only
- ubuntu stonking: iproute2 7.2.0-1ubuntu1 ยท GPL-2.0-only
- ๐ girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms