mirror of
https://github.com/xdp-project/bpf-examples.git
synced 2024-05-06 15:54:53 +00:00
Merge pull request #15 from freysteinn/master
Added an eBPF Qdisc classifier example
This commit is contained in:
31
tc-basic-classifier/Makefile
Normal file
31
tc-basic-classifier/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
|
||||
TC_BPF_TARGETS := filter
|
||||
BPF_TARGETS += $(TC_BPF_TARGETS)
|
||||
|
||||
EXTRA_DEPS += config.mk
|
||||
|
||||
LIB_DIR = ../lib
|
||||
|
||||
include $(LIB_DIR)/common.mk
|
||||
include config.mk
|
||||
|
||||
all: config.mk
|
||||
|
||||
config.mk: configure
|
||||
@sh configure
|
||||
|
||||
ifndef HAVE_TC_LIBBPF
|
||||
# If the iproute2 'tc' tool doesn't understand BTF debug info
|
||||
# use llvm-strip to remove this debug info from object file
|
||||
#
|
||||
# *BUT* cannot strip everything as it removes ELF elems needed for
|
||||
# creating maps
|
||||
#
|
||||
.PHONY: strip_tc_obj
|
||||
strip_tc_obj: ${TC_BPF_TARGETS:=.o}
|
||||
$(Q) echo "TC don't support libbpf - strip BTF info"
|
||||
$(Q) llvm-strip --no-strip-all --remove-section .BTF $?
|
||||
|
||||
all: strip_tc_obj
|
||||
endif
|
66
tc-basic-classifier/README.org
Normal file
66
tc-basic-classifier/README.org
Normal file
@@ -0,0 +1,66 @@
|
||||
#+TITLE: eBPF Qdisc classifier example
|
||||
|
||||
This example demonstrates how to write a simple eBPF Qdisc classifier that
|
||||
classifies flows depending on their destination TCP port. The example script,
|
||||
runner.sh shows how you can use the eBPF Qdisc classifier and implement the same
|
||||
functionality using u32. The script creates two network namespaces called
|
||||
Left and Right, representing two different hosts, as seen in Figure 1. The
|
||||
script then illustrates the classifiers in action using iperf3.
|
||||
|
||||
#+CAPTION: The figure depicts the network and classifier setup of the two network namespaces setup provided by the runner.sh script.
|
||||
#+NAME: fig:Figure 1
|
||||
[[./overview.png]]
|
||||
|
||||
The Left namespace loads a Qdisc classifier that rate-limit TCP ports 8080 and
|
||||
8082 to get a higher rate than default traffic. The runner.sh script shows the
|
||||
higher rate limits by connecting to both target ports and TCP port 8082 to
|
||||
establish the default rate limit of 20 Mbps.
|
||||
|
||||
To run the application, choose either the "bpf" or "u32" parameters:
|
||||
|
||||
#+BEGIN_SRC bash
|
||||
[root@bpfexamples]# ./runner.sh bpf
|
||||
bash-5.0# ./runner.sh bpf
|
||||
Starting setup
|
||||
Starting iperf3
|
||||
Connecting to host 172.16.16.20, port 8080
|
||||
[ 5] local 172.16.16.10 port 56332 connected to 172.16.16.20 port 8080
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 9.51 MBytes 79.8 Mbits/sec 0 86.3 KBytes
|
||||
[ 5] 1.00-2.00 sec 9.38 MBytes 78.7 Mbits/sec 0 112 KBytes
|
||||
[ 5] 2.00-3.00 sec 8.95 MBytes 75.1 Mbits/sec 0 112 KBytes
|
||||
[ 5] 3.00-4.00 sec 9.20 MBytes 77.2 Mbits/sec 0 112 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-4.00 sec 37.0 MBytes 77.7 Mbits/sec 0 sender
|
||||
[ 5] 0.00-4.01 sec 36.5 MBytes 76.4 Mbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
Connecting to host 172.16.16.20, port 8081
|
||||
[ 5] local 172.16.16.10 port 45084 connected to 172.16.16.20 port 8081
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 4.92 MBytes 41.3 Mbits/sec 0 62.2 KBytes
|
||||
[ 5] 1.00-2.00 sec 4.47 MBytes 37.5 Mbits/sec 0 62.2 KBytes
|
||||
[ 5] 2.00-3.00 sec 4.66 MBytes 39.1 Mbits/sec 0 62.2 KBytes
|
||||
[ 5] 3.00-4.00 sec 4.47 MBytes 37.5 Mbits/sec 0 62.2 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-4.00 sec 18.5 MBytes 38.9 Mbits/sec 0 sender
|
||||
[ 5] 0.00-4.01 sec 18.3 MBytes 38.2 Mbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
Connecting to host 172.16.16.20, port 8082
|
||||
[ 5] local 172.16.16.10 port 33196 connected to 172.16.16.20 port 8082
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 2.94 MBytes 24.6 Mbits/sec 0 153 KBytes
|
||||
[ 5] 1.00-2.00 sec 2.49 MBytes 20.9 Mbits/sec 0 153 KBytes
|
||||
[ 5] 2.00-3.00 sec 2.17 MBytes 18.2 Mbits/sec 0 153 KBytes
|
||||
[ 5] 3.00-4.00 sec 2.17 MBytes 18.2 Mbits/sec 0 153 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-4.00 sec 9.77 MBytes 20.5 Mbits/sec 0 sender
|
||||
[ 5] 0.00-4.01 sec 9.14 MBytes 19.1 Mbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
[root@bpfexamples]# ./runner.sh bpf
|
||||
#+END_SRC
|
29
tc-basic-classifier/configure
vendored
Executable file
29
tc-basic-classifier/configure
vendored
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# This is not an autoconf generated configure
|
||||
#
|
||||
|
||||
# Output file which is input to Makefile
|
||||
CONFIG=config.mk
|
||||
|
||||
# Assume tc is in $PATH
|
||||
TC=tc
|
||||
|
||||
check_tc_libbpf()
|
||||
{
|
||||
tc_version=$($TC -V)
|
||||
if echo $tc_version | grep -q libbpf; then
|
||||
libbpf_version=${tc_version##*libbpf }
|
||||
echo "HAVE_TC_LIBBPF:=y" >> $CONFIG
|
||||
echo "BPF_CFLAGS += -DHAVE_TC_LIBBPF" >> $CONFIG
|
||||
echo "yes ($libbpf_version)"
|
||||
else
|
||||
echo "no"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "# Generated config" > $CONFIG
|
||||
echo "Detecting available features on system"
|
||||
|
||||
echo -n " - libbpf support in tc tool: "
|
||||
check_tc_libbpf
|
70
tc-basic-classifier/filter.c
Normal file
70
tc-basic-classifier/filter.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright 2021 Frey Alfredsson <freysteinn@freysteinn.com> */
|
||||
/* Based on code by Jesper Dangaard Brouer <brouer@redhat.com> */
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include "../include/xdp/parsing_helpers.h"
|
||||
|
||||
/*
|
||||
* This example eBPF code mirrors the TC u32 rules set in the runner.sh
|
||||
* script, where the script gives different rate limits depending on if the TCP
|
||||
* traffic is for ports 8080 or 8081. It must be loaded with the direct-action
|
||||
* flag on TC to function, as this is a Qdisc classifier, not a Qdisc action. The
|
||||
* runner.sh script shows an example of how it is loaded and used.
|
||||
*/
|
||||
|
||||
SEC("classifier")
|
||||
int cls_filter(struct __sk_buff *skb)
|
||||
{
|
||||
void *data_end = (void *)(unsigned long long)skb->data_end;
|
||||
void *data = (void *)(unsigned long long)skb->data;
|
||||
|
||||
struct hdr_cursor nh;
|
||||
struct ethhdr *eth;
|
||||
int eth_type;
|
||||
int ip_type;
|
||||
int tcp_type;
|
||||
struct iphdr *iphdr;
|
||||
struct ipv6hdr *ipv6hdr;
|
||||
struct tcphdr *tcphdr;
|
||||
skb->tc_classid = 0x30; /* Default class */
|
||||
|
||||
nh.pos = data;
|
||||
|
||||
/* Parse Ethernet and IP/IPv6 headers */
|
||||
eth_type = parse_ethhdr(&nh, data_end, ð);
|
||||
if (eth_type == bpf_htons(ETH_P_IP)) {
|
||||
ip_type = parse_iphdr(&nh, data_end, &iphdr);
|
||||
if (ip_type != IPPROTO_TCP)
|
||||
goto out;
|
||||
}
|
||||
else if (eth_type == bpf_htons(ETH_P_IPV6)) {
|
||||
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
|
||||
if (ip_type != IPPROTO_TCP)
|
||||
goto out;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Classify TCP ports 8080 and 8081 */
|
||||
tcp_type = parse_tcphdr(&nh, data_end, &tcphdr);
|
||||
if (tcphdr + 1 > data_end) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (tcphdr->dest) {
|
||||
case bpf_htons(8080):
|
||||
skb->tc_classid = 0x10; /* Handles are always in hex */
|
||||
break;
|
||||
case bpf_htons(8081):
|
||||
skb->tc_classid = 0x20;
|
||||
}
|
||||
|
||||
out:
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
BIN
tc-basic-classifier/overview.png
Executable file
BIN
tc-basic-classifier/overview.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
103
tc-basic-classifier/runner.sh
Executable file
103
tc-basic-classifier/runner.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright 2021 Frey Alfredsson <freysteinn@freysteinn.com>
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
### Configuration
|
||||
IP="ip"
|
||||
TC="tc"
|
||||
|
||||
# Left and right IPs
|
||||
L_IP=172.16.16.10
|
||||
R_IP=172.16.16.20
|
||||
L_CIDR="${L_IP}/24"
|
||||
R_CIDR="${R_IP}/24"
|
||||
|
||||
LIMIT=100mbit
|
||||
START_RATE=5mbit
|
||||
P8080_LIMIT=80mbit
|
||||
P8081_LIMIT=40mbit
|
||||
DEFAULT_LIMIT=20mbit
|
||||
|
||||
### Constants
|
||||
L_NS="left"
|
||||
R_NS="right"
|
||||
L_DEV="$L_NS-veth"
|
||||
R_DEV="$R_NS-veth"
|
||||
|
||||
|
||||
### Helper functions
|
||||
function filter-help() {
|
||||
cat <<-EOF
|
||||
Usage: [<bpf> | <u32>]
|
||||
Runs an example that either uses an eBPF based Qdisc filter or an u32
|
||||
filter to rate limit traffic destined for TCP ports 8080 and 8081. It
|
||||
relies on iperf3 to demonstrates the functionality.
|
||||
|
||||
The following are mandatory arguments. Without them prints this help.
|
||||
u32 Runs the example with the u32 Qdisc filter
|
||||
bpf Runs the example with the bpf Qdisc filterdiff
|
||||
|
||||
Please look at the script's source code to see how the examples differ.
|
||||
EOF
|
||||
}
|
||||
|
||||
### Script main
|
||||
if [ $# -ne 1 ] || [[ "${1-}" != "bpf" && "${1-}" != "u32" ]]; then
|
||||
filter-help
|
||||
exit 1
|
||||
fi
|
||||
mode="$1"
|
||||
echo "Starting setup"
|
||||
|
||||
# Remove network namespaces if this is the second run
|
||||
$IP netns delete "$L_NS" &> /dev/null || true
|
||||
$IP netns delete "$R_NS" &> /dev/null || true
|
||||
|
||||
# Create network namespaces
|
||||
$IP netns add "$L_NS"
|
||||
$IP netns add "$R_NS"
|
||||
|
||||
# Create connected virtual nics
|
||||
$IP link add "$L_DEV" type veth peer "$R_DEV"
|
||||
|
||||
# Add the virtual nics to the network namespaces
|
||||
$IP link set "$L_DEV" netns "$L_NS"
|
||||
$IP link set "$R_DEV" netns "$R_NS"
|
||||
|
||||
# Add IP addresses to links
|
||||
$IP -netns "$L_NS" addr add "$L_CIDR" dev "$L_DEV"
|
||||
$IP -netns "$R_NS" addr add "$R_CIDR" dev "$R_DEV"
|
||||
|
||||
# Enable links
|
||||
$IP -netns "$L_NS" link set "$L_DEV" up
|
||||
$IP -netns "$R_NS" link set "$R_DEV" up
|
||||
|
||||
# Setting up the qdiscs on Left
|
||||
$TC -netns "$L_NS" qdisc add dev "$L_DEV" root handle 1:0 htb default 30
|
||||
TC_CLASS_ADD="$TC -netns $L_NS class add dev $L_DEV parent"
|
||||
$TC_CLASS_ADD 1:0 classid 1:1 htb rate "$LIMIT"
|
||||
$TC_CLASS_ADD 1:1 classid 1:10 htb rate "$START_RATE" ceil "$P8080_LIMIT"
|
||||
$TC_CLASS_ADD 1:1 classid 1:20 htb rate "$START_RATE" ceil "$P8081_LIMIT"
|
||||
$TC_CLASS_ADD 1:1 classid 1:30 htb rate "$START_RATE" ceil "$DEFAULT_LIMIT"
|
||||
|
||||
# Setup filters
|
||||
if [ "$mode" == "bpf" ]; then
|
||||
$TC -netns $L_NS filter add dev $L_DEV protocol ip parent 1:0 \
|
||||
bpf obj filter.o classid 1: direct-action
|
||||
else
|
||||
U32="$TC -netns $L_NS filter add dev $L_DEV protocol ip parent 1:0 prio 1 u32"
|
||||
$U32 match ip dport 8080 FFFF flowid 1:10
|
||||
$U32 match ip dport 8081 FFFF flowid 1:20
|
||||
fi
|
||||
# Setup iperf3
|
||||
echo "Starting iperf3"
|
||||
$IP netns exec "$R_NS" iperf3 -s -p 8080 &> /dev/null &
|
||||
$IP netns exec "$R_NS" iperf3 -s -p 8081 &> /dev/null &
|
||||
$IP netns exec "$R_NS" iperf3 -s -p 8082 &> /dev/null &
|
||||
sleep 1
|
||||
$IP netns exec "$L_NS" iperf3 -t 4 -c "$R_IP" -p 8080
|
||||
$IP netns exec "$L_NS" iperf3 -t 4 -c "$R_IP" -p 8081
|
||||
$IP netns exec "$L_NS" iperf3 -t 4 -c "$R_IP" -p 8082
|
Reference in New Issue
Block a user