|  | #!/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) | 
|  |  | 
|  | [ -z "$TC" ] && TC=tc | 
|  | [ -z "$IP" ] && IP=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 "$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 |