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