| #!/bin/bash |
| # |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc. |
| # |
| # Bash-shell example on using iproute2 tools 'tc' and 'ip' to load |
| # eBPF programs, both for XDP and clsbpf. Shell script function |
| # wrappers and even long options parsing is illustrated, for ease of |
| # use. |
| # |
| # Related to sample/bpf/xdp2skb_meta_kern.c, which contains BPF-progs |
| # that need to collaborate between XDP and TC hooks. Thus, it is |
| # convenient that the same tool load both programs that need to work |
| # together. |
| # |
| BPF_FILE=xdp2skb_meta_kern.o |
| DIR=$(dirname $0) |
| |
| export TC=/usr/sbin/tc |
| export IP=/usr/sbin/ip |
| |
| function usage() { |
| echo "" |
| echo "Usage: $0 [-vfh] --dev ethX" |
| echo " -d | --dev : Network device (required)" |
| echo " --flush : Cleanup flush TC and XDP progs" |
| echo " --list : (\$LIST) List TC and XDP progs" |
| echo " -v | --verbose : (\$VERBOSE) Verbose" |
| echo " --dry-run : (\$DRYRUN) Dry-run only (echo commands)" |
| echo "" |
| } |
| |
| ## -- General shell logging cmds -- |
| function err() { |
| local exitcode=$1 |
| shift |
| echo "ERROR: $@" >&2 |
| exit $exitcode |
| } |
| |
| function info() { |
| if [[ -n "$VERBOSE" ]]; then |
| echo "# $@" |
| fi |
| } |
| |
| ## -- Helper function calls -- |
| |
| # Wrapper call for TC and IP |
| # - Will display the offending command on failure |
| function _call_cmd() { |
| local cmd="$1" |
| local allow_fail="$2" |
| shift 2 |
| if [[ -n "$VERBOSE" ]]; then |
| echo "$(basename $cmd) $@" |
| fi |
| if [[ -n "$DRYRUN" ]]; then |
| return |
| fi |
| $cmd "$@" |
| local status=$? |
| if (( $status != 0 )); then |
| if [[ "$allow_fail" == "" ]]; then |
| err 2 "Exec error($status) occurred cmd: \"$cmd $@\"" |
| fi |
| fi |
| } |
| function call_tc() { |
| _call_cmd "$TC" "" "$@" |
| } |
| function call_tc_allow_fail() { |
| _call_cmd "$TC" "allow_fail" "$@" |
| } |
| function call_ip() { |
| _call_cmd "$IP" "" "$@" |
| } |
| |
| ## --- Parse command line arguments / parameters --- |
| # Using external program "getopt" to get --long-options |
| OPTIONS=$(getopt -o vfhd: \ |
| --long verbose,flush,help,list,dev:,dry-run -- "$@") |
| if (( $? != 0 )); then |
| err 4 "Error calling getopt" |
| fi |
| eval set -- "$OPTIONS" |
| |
| unset DEV |
| unset FLUSH |
| while true; do |
| case "$1" in |
| -d | --dev ) # device |
| DEV=$2 |
| info "Device set to: DEV=$DEV" >&2 |
| shift 2 |
| ;; |
| -v | --verbose) |
| VERBOSE=yes |
| # info "Verbose mode: VERBOSE=$VERBOSE" >&2 |
| shift |
| ;; |
| --dry-run ) |
| DRYRUN=yes |
| VERBOSE=yes |
| info "Dry-run mode: enable VERBOSE and don't call TC+IP" >&2 |
| shift |
| ;; |
| -f | --flush ) |
| FLUSH=yes |
| shift |
| ;; |
| --list ) |
| LIST=yes |
| shift |
| ;; |
| -- ) |
| shift |
| break |
| ;; |
| -h | --help ) |
| usage; |
| exit 0 |
| ;; |
| * ) |
| shift |
| break |
| ;; |
| esac |
| done |
| |
| FILE="$DIR/$BPF_FILE" |
| if [[ ! -e $FILE ]]; then |
| err 3 "Missing BPF object file ($FILE)" |
| fi |
| |
| if [[ -z $DEV ]]; then |
| usage |
| err 2 "Please specify network device -- required option --dev" |
| fi |
| |
| ## -- Function calls -- |
| |
| function list_tc() |
| { |
| local device="$1" |
| shift |
| info "Listing current TC ingress rules" |
| call_tc filter show dev $device ingress |
| } |
| |
| function list_xdp() |
| { |
| local device="$1" |
| shift |
| info "Listing current XDP device($device) setting" |
| call_ip link show dev $device | grep --color=auto xdp |
| } |
| |
| function flush_tc() |
| { |
| local device="$1" |
| shift |
| info "Flush TC on device: $device" |
| call_tc_allow_fail filter del dev $device ingress |
| call_tc_allow_fail qdisc del dev $device clsact |
| } |
| |
| function flush_xdp() |
| { |
| local device="$1" |
| shift |
| info "Flush XDP on device: $device" |
| call_ip link set dev $device xdp off |
| } |
| |
| function attach_tc_mark() |
| { |
| local device="$1" |
| local file="$2" |
| local prog="tc_mark" |
| shift 2 |
| |
| # Re-attach clsact to clear/flush existing role |
| call_tc_allow_fail qdisc del dev $device clsact 2> /dev/null |
| call_tc qdisc add dev $device clsact |
| |
| # Attach BPF prog |
| call_tc filter add dev $device ingress \ |
| prio 1 handle 1 bpf da obj $file sec $prog |
| } |
| |
| function attach_xdp_mark() |
| { |
| local device="$1" |
| local file="$2" |
| local prog="xdp_mark" |
| shift 2 |
| |
| # Remove XDP prog in-case it's already loaded |
| # TODO: Need ip-link option to override/replace existing XDP prog |
| flush_xdp $device |
| |
| # Attach XDP/BPF prog |
| call_ip link set dev $device xdp obj $file sec $prog |
| } |
| |
| if [[ -n $FLUSH ]]; then |
| flush_tc $DEV |
| flush_xdp $DEV |
| exit 0 |
| fi |
| |
| if [[ -n $LIST ]]; then |
| list_tc $DEV |
| list_xdp $DEV |
| exit 0 |
| fi |
| |
| attach_tc_mark $DEV $FILE |
| attach_xdp_mark $DEV $FILE |