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
 111if [[ -z "$APPEND" ]]; then
 112        if [[ $EUID -eq 0 ]]; then
 113                # Cleanup pktgen setup on exit if thats not "append mode"
 114                trap 'pg_ctrl "reset"' EXIT
 115        fi
 116fi
 117
 118## -- General shell tricks --
 119
 120function root_check_run_with_sudo() {
 121    # Trick so, program can be run as normal user, will just use "sudo"
 122    #  call as root_check_run_as_sudo "$@"
 123    if [ "$EUID" -ne 0 ]; then
 124        if [ -x $0 ]; then # Directly executable use sudo
 125            info "Not root, running with sudo"
 126            sudo -E "$0" "$@"
 127            exit $?
 128        fi
 129        err 4 "cannot perform sudo run of $0"
 130    fi
 131}
 132
 133# Exact input device's NUMA node info
 134function get_iface_node()
 135{
 136    local node=$(</sys/class/net/$1/device/numa_node)
 137    if [[ $node == -1 ]]; then
 138        echo 0
 139    else
 140        echo $node
 141    fi
 142}
 143
 144# Given an Dev/iface, get its queues' irq numbers
 145function get_iface_irqs()
 146{
 147        local IFACE=$1
 148        local queues="${IFACE}-.*TxRx"
 149
 150        irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)
 151        [ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)
 152        [ -z "$irqs" ] && irqs=$(for i in `ls -Ux /sys/class/net/$IFACE/device/msi_irqs` ;\
 153            do grep "$i:.*TxRx" /proc/interrupts | grep -v fdir | cut -f 1 -d : ;\
 154            done)
 155        [ -z "$irqs" ] && err 3 "Could not find interrupts for $IFACE"
 156
 157        echo $irqs
 158}
 159
 160# Given a NUMA node, return cpu ids belonging to it.
 161function get_node_cpus()
 162{
 163        local node=$1
 164        local node_cpu_list
 165        local node_cpu_range_list=`cut -f1- -d, --output-delimiter=" " \
 166                          /sys/devices/system/node/node$node/cpulist`
 167
 168        for cpu_range in $node_cpu_range_list
 169        do
 170            node_cpu_list="$node_cpu_list "`seq -s " " ${cpu_range//-/ }`
 171        done
 172
 173        echo $node_cpu_list
 174}
 175
 176# Check $1 is in between $2, $3 ($2 <= $1 <= $3)
 177function in_between() { [[ ($1 -ge $2) && ($1 -le $3) ]] ; }
 178
 179# Extend shrunken IPv6 address.
 180# fe80::42:bcff:fe84:e10a => fe80:0:0:0:42:bcff:fe84:e10a
 181function extend_addr6()
 182{
 183    local addr=$1
 184    local sep=: sep2=::
 185    local sep_cnt=$(tr -cd $sep <<< $1 | wc -c)
 186    local shrink
 187
 188    # separator count should be (2 <= $sep_cnt <= 7)
 189    if ! (in_between $sep_cnt 2 7); then
 190        err 5 "Invalid IP6 address: $1"
 191    fi
 192
 193    # if shrink '::' occurs multiple, it's malformed.
 194    shrink=( $(egrep -o "$sep{2,}" <<< $addr) )
 195    if [[ ${#shrink[@]} -ne 0 ]]; then
 196        if [[ ${#shrink[@]} -gt 1 || ( ${shrink[0]} != $sep2 ) ]]; then
 197            err 5 "Invalid IP6 address: $1"
 198        fi
 199    fi
 200
 201    # add 0 at begin & end, and extend addr by adding :0
 202    [[ ${addr:0:1} == $sep ]] && addr=0${addr}
 203    [[ ${addr: -1} == $sep ]] && addr=${addr}0
 204    echo "${addr/$sep2/$(printf ':0%.s' $(seq $[8-sep_cnt])):}"
 205}
 206
 207# Given a single IP(v4/v6) address, whether it is valid.
 208function validate_addr()
 209{
 210    # check function is called with (funcname)6
 211    [[ ${FUNCNAME[1]: -1} == 6 ]] && local IP6=6
 212    local bitlen=$[ IP6 ? 128 : 32 ]
 213    local len=$[ IP6 ? 8 : 4 ]
 214    local max=$[ 2**(len*2)-1 ]
 215    local net prefix
 216    local addr sep
 217
 218    IFS='/' read net prefix <<< $1
 219    [[ $IP6 ]] && net=$(extend_addr6 $net)
 220
 221    # if prefix exists, check (0 <= $prefix <= $bitlen)
 222    if [[ -n $prefix ]]; then
 223        if ! (in_between $prefix 0 $bitlen); then
 224            err 5 "Invalid prefix: /$prefix"
 225        fi
 226    fi
 227
 228    # set separator for each IP(v4/v6)
 229    [[ $IP6 ]] && sep=: || sep=.
 230    IFS=$sep read -a addr <<< $net
 231
 232    # array length
 233    if [[ ${#addr[@]} != $len ]]; then
 234        err 5 "Invalid IP$IP6 address: $1"
 235    fi
 236
 237    # check each digit (0 <= $digit <= $max)
 238    for digit in "${addr[@]}"; do
 239        [[ $IP6 ]] && digit=$[ 16#$digit ]
 240        if ! (in_between $digit 0 $max); then
 241            err 5 "Invalid IP$IP6 address: $1"
 242        fi
 243    done
 244
 245    return 0
 246}
 247
 248function validate_addr6() { validate_addr $@ ; }
 249
 250# Given a single IP(v4/v6) or CIDR, return minimum and maximum IP addr.
 251function parse_addr()
 252{
 253    # check function is called with (funcname)6
 254    [[ ${FUNCNAME[1]: -1} == 6 ]] && local IP6=6
 255    local net prefix
 256    local min_ip max_ip
 257
 258    IFS='/' read net prefix <<< $1
 259    [[ $IP6 ]] && net=$(extend_addr6 $net)
 260
 261    if [[ -z $prefix ]]; then
 262        min_ip=$net
 263        max_ip=$net
 264    else
 265        # defining array for converting Decimal 2 Binary
 266        # 00000000 00000001 00000010 00000011 00000100 ...
 267        local d2b='{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}'
 268        [[ $IP6 ]] && d2b+=$d2b
 269        eval local D2B=($d2b)
 270
 271        local bitlen=$[ IP6 ? 128 : 32 ]
 272        local remain=$[ bitlen-prefix ]
 273        local octet=$[ IP6 ? 16 : 8 ]
 274        local min_mask max_mask
 275        local min max
 276        local ip_bit
 277        local ip sep
 278
 279        # set separator for each IP(v4/v6)
 280        [[ $IP6 ]] && sep=: || sep=.
 281        IFS=$sep read -ra ip <<< $net
 282
 283        min_mask="$(printf '1%.s' $(seq $prefix))$(printf '0%.s' $(seq $remain))"
 284        max_mask="$(printf '0%.s' $(seq $prefix))$(printf '1%.s' $(seq $remain))"
 285
 286        # calculate min/max ip with &,| operator
 287        for i in "${!ip[@]}"; do
 288            digit=$[ IP6 ? 16#${ip[$i]} : ${ip[$i]} ]
 289            ip_bit=${D2B[$digit]}
 290
 291            idx=$[ octet*i ]
 292            min[$i]=$[ 2#$ip_bit & 2#${min_mask:$idx:$octet} ]
 293            max[$i]=$[ 2#$ip_bit | 2#${max_mask:$idx:$octet} ]
 294            [[ $IP6 ]] && { min[$i]=$(printf '%X' ${min[$i]});
 295                            max[$i]=$(printf '%X' ${max[$i]}); }
 296        done
 297
 298        min_ip=$(IFS=$sep; echo "${min[*]}")
 299        max_ip=$(IFS=$sep; echo "${max[*]}")
 300    fi
 301
 302    echo $min_ip $max_ip
 303}
 304
 305function parse_addr6() { parse_addr $@ ; }
 306
 307# Given a single or range of port(s), return minimum and maximum port number.
 308function parse_ports()
 309{
 310    local port_str=$1
 311    local port_list
 312    local min_port
 313    local max_port
 314
 315    IFS="-" read -ra port_list <<< $port_str
 316
 317    min_port=${port_list[0]}
 318    max_port=${port_list[1]:-$min_port}
 319
 320    echo $min_port $max_port
 321}
 322
 323# Given a minimum and maximum port, verify port number.
 324function validate_ports()
 325{
 326    local min_port=$1
 327    local max_port=$2
 328
 329    # 1 <= port <= 65535
 330    if (in_between $min_port 1 65535); then
 331        if (in_between $max_port 1 65535); then
 332            if [[ $min_port -le $max_port ]]; then
 333                return 0
 334            fi
 335        fi
 336    fi
 337
 338    err 5 "Invalid port(s): $min_port-$max_port"
 339}
 340