linux/samples/pktgen/functions.sh
<<
>>
Prefs
   1#
   2# Common functions used by pktgen scripts
   3#  - Depending on bash 3 (or higher) syntax
   4#
   5# Author: Jesper Dangaaard Brouer
   6# License: GPL
   7
   8set -o errexit
   9
  10## -- General shell logging cmds --
  11function err() {
  12    local exitcode=$1
  13    shift
  14    echo "ERROR: $@" >&2
  15    exit $exitcode
  16}
  17
  18function warn() {
  19    echo "WARN : $@" >&2
  20}
  21
  22function info() {
  23    if [[ -n "$VERBOSE" ]]; then
  24        echo "INFO : $@" >&2
  25    fi
  26}
  27
  28## -- Pktgen proc config commands -- ##
  29export PROC_DIR=/proc/net/pktgen
  30#
  31# Three different shell functions for configuring the different
  32# components of pktgen:
  33#   pg_ctrl(), pg_thread() and pg_set().
  34#
  35# These functions correspond to pktgens different components.
  36# * pg_ctrl()   control "pgctrl" (/proc/net/pktgen/pgctrl)
  37# * pg_thread() control the kernel threads and binding to devices
  38# * pg_set()    control setup of individual devices
  39function pg_ctrl() {
  40    local proc_file="pgctrl"
  41    proc_cmd ${proc_file} "$@"
  42}
  43
  44function pg_thread() {
  45    local thread=$1
  46    local proc_file="kpktgend_${thread}"
  47    shift
  48    proc_cmd ${proc_file} "$@"
  49}
  50
  51function pg_set() {
  52    local dev=$1
  53    local proc_file="$dev"
  54    shift
  55    proc_cmd ${proc_file} "$@"
  56}
  57
  58# More generic replacement for pgset(), that does not depend on global
  59# variable for proc file.
  60function proc_cmd() {
  61    local result
  62    local proc_file=$1
  63    local status=0
  64    # after shift, the remaining args are contained in $@
  65    shift
  66    local proc_ctrl=${PROC_DIR}/$proc_file
  67    if [[ ! -e "$proc_ctrl" ]]; then
  68        err 3 "proc file:$proc_ctrl does not exists (dev added to thread?)"
  69    else
  70        if [[ ! -w "$proc_ctrl" ]]; then
  71            err 4 "proc file:$proc_ctrl not writable, not root?!"
  72        fi
  73    fi
  74
  75    if [[ "$DEBUG" == "yes" ]]; then
  76        echo "cmd: $@ > $proc_ctrl"
  77    fi
  78    # Quoting of "$@" is important for space expansion
  79    echo "$@" > "$proc_ctrl" || status=$?
  80
  81    if [[ "$proc_file" != "pgctrl" ]]; then
  82        result=$(grep "Result: OK:" $proc_ctrl) || true
  83        if [[ "$result" == "" ]]; then
  84            grep "Result:" $proc_ctrl >&2
  85        fi
  86    fi
  87    if (( $status != 0 )); then
  88        err 5 "Write error($status) occurred cmd: \"$@ > $proc_ctrl\""
  89    fi
  90}
  91
  92# Old obsolete "pgset" function, with slightly improved err handling
  93function pgset() {
  94    local result
  95
  96    if [[ "$DEBUG" == "yes" ]]; then
  97        echo "cmd: $1 > $PGDEV"
  98    fi
  99    echo $1 > $PGDEV
 100    local status=$?
 101
 102    result=`cat $PGDEV | fgrep "Result: OK:"`
 103    if [[ "$result" == "" ]]; then
 104         cat $PGDEV | fgrep Result:
 105    fi
 106    if (( $status != 0 )); then
 107        err 5 "Write error($status) occurred cmd: \"$1 > $PGDEV\""
 108    fi
 109}
 110
 111[[ $EUID -eq 0 ]] && trap 'pg_ctrl "reset"' EXIT
 112
 113## -- General shell tricks --
 114
 115function root_check_run_with_sudo() {
 116    # Trick so, program can be run as normal user, will just use "sudo"
 117    #  call as root_check_run_as_sudo "$@"
 118    if [ "$EUID" -ne 0 ]; then
 119        if [ -x $0 ]; then # Directly executable use sudo
 120            info "Not root, running with sudo"
 121            sudo "$0" "$@"
 122            exit $?
 123        fi
 124        err 4 "cannot perform sudo run of $0"
 125    fi
 126}
 127
 128# Exact input device's NUMA node info
 129function get_iface_node()
 130{
 131    local node=$(</sys/class/net/$1/device/numa_node)
 132    if [[ $node == -1 ]]; then
 133        echo 0
 134    else
 135        echo $node
 136    fi
 137}
 138
 139# Given an Dev/iface, get its queues' irq numbers
 140function get_iface_irqs()
 141{
 142        local IFACE=$1
 143        local queues="${IFACE}-.*TxRx"
 144
 145        irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)
 146        [ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)
 147        [ -z "$irqs" ] && irqs=$(for i in `ls -Ux /sys/class/net/$IFACE/device/msi_irqs` ;\
 148            do grep "$i:.*TxRx" /proc/interrupts | grep -v fdir | cut -f 1 -d : ;\
 149            done)
 150        [ -z "$irqs" ] && err 3 "Could not find interrupts for $IFACE"
 151
 152        echo $irqs
 153}
 154
 155# Given a NUMA node, return cpu ids belonging to it.
 156function get_node_cpus()
 157{
 158        local node=$1
 159        local node_cpu_list
 160        local node_cpu_range_list=`cut -f1- -d, --output-delimiter=" " \
 161                          /sys/devices/system/node/node$node/cpulist`
 162
 163        for cpu_range in $node_cpu_range_list
 164        do
 165            node_cpu_list="$node_cpu_list "`seq -s " " ${cpu_range//-/ }`
 166        done
 167
 168        echo $node_cpu_list
 169}
 170
 171# Check $1 is in between $2, $3 ($2 <= $1 <= $3)
 172function in_between() { [[ ($1 -ge $2) && ($1 -le $3) ]] ; }
 173
 174# Extend shrunken IPv6 address.
 175# fe80::42:bcff:fe84:e10a => fe80:0:0:0:42:bcff:fe84:e10a
 176function extend_addr6()
 177{
 178    local addr=$1
 179    local sep=: sep2=::
 180    local sep_cnt=$(tr -cd $sep <<< $1 | wc -c)
 181    local shrink
 182
 183    # separator count should be (2 <= $sep_cnt <= 7)
 184    if ! (in_between $sep_cnt 2 7); then
 185        err 5 "Invalid IP6 address: $1"
 186    fi
 187
 188    # if shrink '::' occurs multiple, it's malformed.
 189    shrink=( $(egrep -o "$sep{2,}" <<< $addr) )
 190    if [[ ${#shrink[@]} -ne 0 ]]; then
 191        if [[ ${#shrink[@]} -gt 1 || ( ${shrink[0]} != $sep2 ) ]]; then
 192            err 5 "Invalid IP6 address: $1"
 193        fi
 194    fi
 195
 196    # add 0 at begin & end, and extend addr by adding :0
 197    [[ ${addr:0:1} == $sep ]] && addr=0${addr}
 198    [[ ${addr: -1} == $sep ]] && addr=${addr}0
 199    echo "${addr/$sep2/$(printf ':0%.s' $(seq $[8-sep_cnt])):}"
 200}
 201
 202# Given a single IP(v4/v6) address, whether it is valid.
 203function validate_addr()
 204{
 205    # check function is called with (funcname)6
 206    [[ ${FUNCNAME[1]: -1} == 6 ]] && local IP6=6
 207    local bitlen=$[ IP6 ? 128 : 32 ]
 208    local len=$[ IP6 ? 8 : 4 ]
 209    local max=$[ 2**(len*2)-1 ]
 210    local net prefix
 211    local addr sep
 212
 213    IFS='/' read net prefix <<< $1
 214    [[ $IP6 ]] && net=$(extend_addr6 $net)
 215
 216    # if prefix exists, check (0 <= $prefix <= $bitlen)
 217    if [[ -n $prefix ]]; then
 218        if ! (in_between $prefix 0 $bitlen); then
 219            err 5 "Invalid prefix: /$prefix"
 220        fi
 221    fi
 222
 223    # set separator for each IP(v4/v6)
 224    [[ $IP6 ]] && sep=: || sep=.
 225    IFS=$sep read -a addr <<< $net
 226
 227    # array length
 228    if [[ ${#addr[@]} != $len ]]; then
 229        err 5 "Invalid IP$IP6 address: $1"
 230    fi
 231
 232    # check each digit (0 <= $digit <= $max)
 233    for digit in "${addr[@]}"; do
 234        [[ $IP6 ]] && digit=$[ 16#$digit ]
 235        if ! (in_between $digit 0 $max); then
 236            err 5 "Invalid IP$IP6 address: $1"
 237        fi
 238    done
 239
 240    return 0
 241}
 242
 243function validate_addr6() { validate_addr $@ ; }
 244
 245# Given a single IP(v4/v6) or CIDR, return minimum and maximum IP addr.
 246function parse_addr()
 247{
 248    # check function is called with (funcname)6
 249    [[ ${FUNCNAME[1]: -1} == 6 ]] && local IP6=6
 250    local net prefix
 251    local min_ip max_ip
 252
 253    IFS='/' read net prefix <<< $1
 254    [[ $IP6 ]] && net=$(extend_addr6 $net)
 255
 256    if [[ -z $prefix ]]; then
 257        min_ip=$net
 258        max_ip=$net
 259    else
 260        # defining array for converting Decimal 2 Binary
 261        # 00000000 00000001 00000010 00000011 00000100 ...
 262        local d2b='{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}'
 263        [[ $IP6 ]] && d2b+=$d2b
 264        eval local D2B=($d2b)
 265
 266        local bitlen=$[ IP6 ? 128 : 32 ]
 267        local remain=$[ bitlen-prefix ]
 268        local octet=$[ IP6 ? 16 : 8 ]
 269        local min_mask max_mask
 270        local min max
 271        local ip_bit
 272        local ip sep
 273
 274        # set separator for each IP(v4/v6)
 275        [[ $IP6 ]] && sep=: || sep=.
 276        IFS=$sep read -ra ip <<< $net
 277
 278        min_mask="$(printf '1%.s' $(seq $prefix))$(printf '0%.s' $(seq $remain))"
 279        max_mask="$(printf '0%.s' $(seq $prefix))$(printf '1%.s' $(seq $remain))"
 280
 281        # calculate min/max ip with &,| operator
 282        for i in "${!ip[@]}"; do
 283            digit=$[ IP6 ? 16#${ip[$i]} : ${ip[$i]} ]
 284            ip_bit=${D2B[$digit]}
 285
 286            idx=$[ octet*i ]
 287            min[$i]=$[ 2#$ip_bit & 2#${min_mask:$idx:$octet} ]
 288            max[$i]=$[ 2#$ip_bit | 2#${max_mask:$idx:$octet} ]
 289            [[ $IP6 ]] && { min[$i]=$(printf '%X' ${min[$i]});
 290                            max[$i]=$(printf '%X' ${max[$i]}); }
 291        done
 292
 293        min_ip=$(IFS=$sep; echo "${min[*]}")
 294        max_ip=$(IFS=$sep; echo "${max[*]}")
 295    fi
 296
 297    echo $min_ip $max_ip
 298}
 299
 300function parse_addr6() { parse_addr $@ ; }
 301
 302# Given a single or range of port(s), return minimum and maximum port number.
 303function parse_ports()
 304{
 305    local port_str=$1
 306    local port_list
 307    local min_port
 308    local max_port
 309
 310    IFS="-" read -ra port_list <<< $port_str
 311
 312    min_port=${port_list[0]}
 313    max_port=${port_list[1]:-$min_port}
 314
 315    echo $min_port $max_port
 316}
 317
 318# Given a minimum and maximum port, verify port number.
 319function validate_ports()
 320{
 321    local min_port=$1
 322    local max_port=$2
 323
 324    # 1 <= port <= 65535
 325    if (in_between $min_port 1 65535); then
 326        if (in_between $max_port 1 65535); then
 327            if [[ $min_port -le $max_port ]]; then
 328                return 0
 329            fi
 330        fi
 331    fi
 332
 333    err 5 "Invalid port(s): $min_port-$max_port"
 334}
 335