1#!/bin/bash 2# 3# SPDX-License-Identifier: GPL-2.0 4# Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc. 5# 6# Bash-shell example on using iproute2 tools 'tc' and 'ip' to load 7# eBPF programs, both for XDP and clsbpf. Shell script function 8# wrappers and even long options parsing is illustrated, for ease of 9# use. 10# 11# Related to sample/bpf/xdp2skb_meta_kern.c, which contains BPF-progs 12# that need to collaborate between XDP and TC hooks. Thus, it is 13# convenient that the same tool load both programs that need to work 14# together. 15# 16BPF_FILE=xdp2skb_meta_kern.o 17DIR=$(dirname $0) 18 19export TC=/usr/sbin/tc 20export IP=/usr/sbin/ip 21 22function usage() { 23 echo "" 24 echo "Usage: $0 [-vfh] --dev ethX" 25 echo " -d | --dev : Network device (required)" 26 echo " --flush : Cleanup flush TC and XDP progs" 27 echo " --list : (\$LIST) List TC and XDP progs" 28 echo " -v | --verbose : (\$VERBOSE) Verbose" 29 echo " --dry-run : (\$DRYRUN) Dry-run only (echo commands)" 30 echo "" 31} 32 33## -- General shell logging cmds -- 34function err() { 35 local exitcode=$1 36 shift 37 echo "ERROR: $@" >&2 38 exit $exitcode 39} 40 41function info() { 42 if [[ -n "$VERBOSE" ]]; then 43 echo "# $@" 44 fi 45} 46 47## -- Helper function calls -- 48 49# Wrapper call for TC and IP 50# - Will display the offending command on failure 51function _call_cmd() { 52 local cmd="$1" 53 local allow_fail="$2" 54 shift 2 55 if [[ -n "$VERBOSE" ]]; then 56 echo "$(basename $cmd) $@" 57 fi 58 if [[ -n "$DRYRUN" ]]; then 59 return 60 fi 61 $cmd "$@" 62 local status=$? 63 if (( $status != 0 )); then 64 if [[ "$allow_fail" == "" ]]; then 65 err 2 "Exec error($status) occurred cmd: \"$cmd $@\"" 66 fi 67 fi 68} 69function call_tc() { 70 _call_cmd "$TC" "" "$@" 71} 72function call_tc_allow_fail() { 73 _call_cmd "$TC" "allow_fail" "$@" 74} 75function call_ip() { 76 _call_cmd "$IP" "" "$@" 77} 78 79## --- Parse command line arguments / parameters --- 80# Using external program "getopt" to get --long-options 81OPTIONS=$(getopt -o vfhd: \ 82 --long verbose,flush,help,list,dev:,dry-run -- "$@") 83if (( $? != 0 )); then 84 err 4 "Error calling getopt" 85fi 86eval set -- "$OPTIONS" 87 88unset DEV 89unset FLUSH 90while true; do 91 case "$1" in 92 -d | --dev ) # device 93 DEV=$2 94 info "Device set to: DEV=$DEV" >&2 95 shift 2 96 ;; 97 -v | --verbose) 98 VERBOSE=yes 99 # info "Verbose mode: VERBOSE=$VERBOSE" >&2 100 shift 101 ;; 102 --dry-run ) 103 DRYRUN=yes 104 VERBOSE=yes 105 info "Dry-run mode: enable VERBOSE and don't call TC+IP" >&2 106 shift 107 ;; 108 -f | --flush ) 109 FLUSH=yes 110 shift 111 ;; 112 --list ) 113 LIST=yes 114 shift 115 ;; 116 -- ) 117 shift 118 break 119 ;; 120 -h | --help ) 121 usage; 122 exit 0 123 ;; 124 * ) 125 shift 126 break 127 ;; 128 esac 129done 130 131FILE="$DIR/$BPF_FILE" 132if [[ ! -e $FILE ]]; then 133 err 3 "Missing BPF object file ($FILE)" 134fi 135 136if [[ -z $DEV ]]; then 137 usage 138 err 2 "Please specify network device -- required option --dev" 139fi 140 141## -- Function calls -- 142 143function list_tc() 144{ 145 local device="$1" 146 shift 147 info "Listing current TC ingress rules" 148 call_tc filter show dev $device ingress 149} 150 151function list_xdp() 152{ 153 local device="$1" 154 shift 155 info "Listing current XDP device($device) setting" 156 call_ip link show dev $device | grep --color=auto xdp 157} 158 159function flush_tc() 160{ 161 local device="$1" 162 shift 163 info "Flush TC on device: $device" 164 call_tc_allow_fail filter del dev $device ingress 165 call_tc_allow_fail qdisc del dev $device clsact 166} 167 168function flush_xdp() 169{ 170 local device="$1" 171 shift 172 info "Flush XDP on device: $device" 173 call_ip link set dev $device xdp off 174} 175 176function attach_tc_mark() 177{ 178 local device="$1" 179 local file="$2" 180 local prog="tc_mark" 181 shift 2 182 183 # Re-attach clsact to clear/flush existing role 184 call_tc_allow_fail qdisc del dev $device clsact 2> /dev/null 185 call_tc qdisc add dev $device clsact 186 187 # Attach BPF prog 188 call_tc filter add dev $device ingress \ 189 prio 1 handle 1 bpf da obj $file sec $prog 190} 191 192function attach_xdp_mark() 193{ 194 local device="$1" 195 local file="$2" 196 local prog="xdp_mark" 197 shift 2 198 199 # Remove XDP prog in-case it's already loaded 200 # TODO: Need ip-link option to override/replace existing XDP prog 201 flush_xdp $device 202 203 # Attach XDP/BPF prog 204 call_ip link set dev $device xdp obj $file sec $prog 205} 206 207if [[ -n $FLUSH ]]; then 208 flush_tc $DEV 209 flush_xdp $DEV 210 exit 0 211fi 212 213if [[ -n $LIST ]]; then 214 list_tc $DEV 215 list_xdp $DEV 216 exit 0 217fi 218 219attach_tc_mark $DEV $FILE 220attach_xdp_mark $DEV $FILE 221