123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- #!/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
|