Back to home page

OSCL-LXR

 
 

    


0001 #
0002 # Common functions used by pktgen scripts
0003 #  - Depending on bash 3 (or higher) syntax
0004 #
0005 # Author: Jesper Dangaaard Brouer
0006 # License: GPL
0007 
0008 set -o errexit
0009 
0010 ## -- General shell logging cmds --
0011 function err() {
0012     local exitcode=$1
0013     shift
0014     echo "ERROR: $@" >&2
0015     exit $exitcode
0016 }
0017 
0018 function warn() {
0019     echo "WARN : $@" >&2
0020 }
0021 
0022 function info() {
0023     if [[ -n "$VERBOSE" ]]; then
0024         echo "INFO : $@" >&2
0025     fi
0026 }
0027 
0028 ## -- Pktgen proc config commands -- ##
0029 export PROC_DIR=/proc/net/pktgen
0030 #
0031 # Three different shell functions for configuring the different
0032 # components of pktgen:
0033 #   pg_ctrl(), pg_thread() and pg_set().
0034 #
0035 # These functions correspond to pktgens different components.
0036 # * pg_ctrl()   control "pgctrl" (/proc/net/pktgen/pgctrl)
0037 # * pg_thread() control the kernel threads and binding to devices
0038 # * pg_set()    control setup of individual devices
0039 function pg_ctrl() {
0040     local proc_file="pgctrl"
0041     proc_cmd ${proc_file} "$@"
0042 }
0043 
0044 function pg_thread() {
0045     local thread=$1
0046     local proc_file="kpktgend_${thread}"
0047     shift
0048     proc_cmd ${proc_file} "$@"
0049 }
0050 
0051 function pg_set() {
0052     local dev=$1
0053     local proc_file="$dev"
0054     shift
0055     proc_cmd ${proc_file} "$@"
0056 }
0057 
0058 # More generic replacement for pgset(), that does not depend on global
0059 # variable for proc file.
0060 function proc_cmd() {
0061     local result
0062     local proc_file=$1
0063     local status=0
0064     # after shift, the remaining args are contained in $@
0065     shift
0066     local proc_ctrl=${PROC_DIR}/$proc_file
0067     if [[ ! -e "$proc_ctrl" ]]; then
0068         err 3 "proc file:$proc_ctrl does not exists (dev added to thread?)"
0069     else
0070         if [[ ! -w "$proc_ctrl" ]]; then
0071             err 4 "proc file:$proc_ctrl not writable, not root?!"
0072         fi
0073     fi
0074 
0075     if [[ "$DEBUG" == "yes" ]]; then
0076         echo "cmd: $@ > $proc_ctrl"
0077     fi
0078     # Quoting of "$@" is important for space expansion
0079     echo "$@" > "$proc_ctrl" || status=$?
0080 
0081     if [[ "$proc_file" != "pgctrl" ]]; then
0082         result=$(grep "Result: OK:" $proc_ctrl) || true
0083         if [[ "$result" == "" ]]; then
0084             grep "Result:" $proc_ctrl >&2
0085         fi
0086     fi
0087     if (( $status != 0 )); then
0088         err 5 "Write error($status) occurred cmd: \"$@ > $proc_ctrl\""
0089     fi
0090 }
0091 
0092 # Old obsolete "pgset" function, with slightly improved err handling
0093 function pgset() {
0094     local result
0095 
0096     if [[ "$DEBUG" == "yes" ]]; then
0097         echo "cmd: $1 > $PGDEV"
0098     fi
0099     echo $1 > $PGDEV
0100     local status=$?
0101 
0102     result=`cat $PGDEV | fgrep "Result: OK:"`
0103     if [[ "$result" == "" ]]; then
0104          cat $PGDEV | fgrep Result:
0105     fi
0106     if (( $status != 0 )); then
0107         err 5 "Write error($status) occurred cmd: \"$1 > $PGDEV\""
0108     fi
0109 }
0110 
0111 if [[ -z "$APPEND" ]]; then
0112         if [[ $EUID -eq 0 ]]; then
0113                 # Cleanup pktgen setup on exit if thats not "append mode"
0114                 trap 'pg_ctrl "reset"' EXIT
0115         fi
0116 fi
0117 
0118 ## -- General shell tricks --
0119 
0120 function root_check_run_with_sudo() {
0121     # Trick so, program can be run as normal user, will just use "sudo"
0122     #  call as root_check_run_as_sudo "$@"
0123     if [ "$EUID" -ne 0 ]; then
0124         if [ -x $0 ]; then # Directly executable use sudo
0125             info "Not root, running with sudo"
0126             sudo -E "$0" "$@"
0127             exit $?
0128         fi
0129         err 4 "cannot perform sudo run of $0"
0130     fi
0131 }
0132 
0133 # Exact input device's NUMA node info
0134 function get_iface_node()
0135 {
0136     local node=$(</sys/class/net/$1/device/numa_node)
0137     if [[ $node == -1 ]]; then
0138         echo 0
0139     else
0140         echo $node
0141     fi
0142 }
0143 
0144 # Given an Dev/iface, get its queues' irq numbers
0145 function get_iface_irqs()
0146 {
0147         local IFACE=$1
0148         local queues="${IFACE}-.*TxRx"
0149 
0150         irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)
0151         [ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)
0152         [ -z "$irqs" ] && irqs=$(for i in `ls -Ux /sys/class/net/$IFACE/device/msi_irqs` ;\
0153             do grep "$i:.*TxRx" /proc/interrupts | grep -v fdir | cut -f 1 -d : ;\
0154             done)
0155         [ -z "$irqs" ] && err 3 "Could not find interrupts for $IFACE"
0156 
0157         echo $irqs
0158 }
0159 
0160 # Given a NUMA node, return cpu ids belonging to it.
0161 function get_node_cpus()
0162 {
0163         local node=$1
0164         local node_cpu_list
0165         local node_cpu_range_list=`cut -f1- -d, --output-delimiter=" " \
0166                           /sys/devices/system/node/node$node/cpulist`
0167 
0168         for cpu_range in $node_cpu_range_list
0169         do
0170             node_cpu_list="$node_cpu_list "`seq -s " " ${cpu_range//-/ }`
0171         done
0172 
0173         echo $node_cpu_list
0174 }
0175 
0176 # Check $1 is in between $2, $3 ($2 <= $1 <= $3)
0177 function in_between() { [[ ($1 -ge $2) && ($1 -le $3) ]] ; }
0178 
0179 # Extend shrunken IPv6 address.
0180 # fe80::42:bcff:fe84:e10a => fe80:0:0:0:42:bcff:fe84:e10a
0181 function extend_addr6()
0182 {
0183     local addr=$1
0184     local sep=: sep2=::
0185     local sep_cnt=$(tr -cd $sep <<< $1 | wc -c)
0186     local shrink
0187 
0188     # separator count should be (2 <= $sep_cnt <= 7)
0189     if ! (in_between $sep_cnt 2 7); then
0190         err 5 "Invalid IP6 address: $1"
0191     fi
0192 
0193     # if shrink '::' occurs multiple, it's malformed.
0194     shrink=( $(egrep -o "$sep{2,}" <<< $addr) )
0195     if [[ ${#shrink[@]} -ne 0 ]]; then
0196         if [[ ${#shrink[@]} -gt 1 || ( ${shrink[0]} != $sep2 ) ]]; then
0197             err 5 "Invalid IP6 address: $1"
0198         fi
0199     fi
0200 
0201     # add 0 at begin & end, and extend addr by adding :0
0202     [[ ${addr:0:1} == $sep ]] && addr=0${addr}
0203     [[ ${addr: -1} == $sep ]] && addr=${addr}0
0204     echo "${addr/$sep2/$(printf ':0%.s' $(seq $[8-sep_cnt])):}"
0205 }
0206 
0207 # Given a single IP(v4/v6) address, whether it is valid.
0208 function validate_addr()
0209 {
0210     # check function is called with (funcname)6
0211     [[ ${FUNCNAME[1]: -1} == 6 ]] && local IP6=6
0212     local bitlen=$[ IP6 ? 128 : 32 ]
0213     local len=$[ IP6 ? 8 : 4 ]
0214     local max=$[ 2**(len*2)-1 ]
0215     local net prefix
0216     local addr sep
0217 
0218     IFS='/' read net prefix <<< $1
0219     [[ $IP6 ]] && net=$(extend_addr6 $net)
0220 
0221     # if prefix exists, check (0 <= $prefix <= $bitlen)
0222     if [[ -n $prefix ]]; then
0223         if ! (in_between $prefix 0 $bitlen); then
0224             err 5 "Invalid prefix: /$prefix"
0225         fi
0226     fi
0227 
0228     # set separator for each IP(v4/v6)
0229     [[ $IP6 ]] && sep=: || sep=.
0230     IFS=$sep read -a addr <<< $net
0231 
0232     # array length
0233     if [[ ${#addr[@]} != $len ]]; then
0234         err 5 "Invalid IP$IP6 address: $1"
0235     fi
0236 
0237     # check each digit (0 <= $digit <= $max)
0238     for digit in "${addr[@]}"; do
0239         [[ $IP6 ]] && digit=$[ 16#$digit ]
0240         if ! (in_between $digit 0 $max); then
0241             err 5 "Invalid IP$IP6 address: $1"
0242         fi
0243     done
0244 
0245     return 0
0246 }
0247 
0248 function validate_addr6() { validate_addr $@ ; }
0249 
0250 # Given a single IP(v4/v6) or CIDR, return minimum and maximum IP addr.
0251 function parse_addr()
0252 {
0253     # check function is called with (funcname)6
0254     [[ ${FUNCNAME[1]: -1} == 6 ]] && local IP6=6
0255     local net prefix
0256     local min_ip max_ip
0257 
0258     IFS='/' read net prefix <<< $1
0259     [[ $IP6 ]] && net=$(extend_addr6 $net)
0260 
0261     if [[ -z $prefix ]]; then
0262         min_ip=$net
0263         max_ip=$net
0264     else
0265         # defining array for converting Decimal 2 Binary
0266         # 00000000 00000001 00000010 00000011 00000100 ...
0267         local d2b='{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}'
0268         [[ $IP6 ]] && d2b+=$d2b
0269         eval local D2B=($d2b)
0270 
0271         local bitlen=$[ IP6 ? 128 : 32 ]
0272         local remain=$[ bitlen-prefix ]
0273         local octet=$[ IP6 ? 16 : 8 ]
0274         local min_mask max_mask
0275         local min max
0276         local ip_bit
0277         local ip sep
0278 
0279         # set separator for each IP(v4/v6)
0280         [[ $IP6 ]] && sep=: || sep=.
0281         IFS=$sep read -ra ip <<< $net
0282 
0283         min_mask="$(printf '1%.s' $(seq $prefix))$(printf '0%.s' $(seq $remain))"
0284         max_mask="$(printf '0%.s' $(seq $prefix))$(printf '1%.s' $(seq $remain))"
0285 
0286         # calculate min/max ip with &,| operator
0287         for i in "${!ip[@]}"; do
0288             digit=$[ IP6 ? 16#${ip[$i]} : ${ip[$i]} ]
0289             ip_bit=${D2B[$digit]}
0290 
0291             idx=$[ octet*i ]
0292             min[$i]=$[ 2#$ip_bit & 2#${min_mask:$idx:$octet} ]
0293             max[$i]=$[ 2#$ip_bit | 2#${max_mask:$idx:$octet} ]
0294             [[ $IP6 ]] && { min[$i]=$(printf '%X' ${min[$i]});
0295                             max[$i]=$(printf '%X' ${max[$i]}); }
0296         done
0297 
0298         min_ip=$(IFS=$sep; echo "${min[*]}")
0299         max_ip=$(IFS=$sep; echo "${max[*]}")
0300     fi
0301 
0302     echo $min_ip $max_ip
0303 }
0304 
0305 function parse_addr6() { parse_addr $@ ; }
0306 
0307 # Given a single or range of port(s), return minimum and maximum port number.
0308 function parse_ports()
0309 {
0310     local port_str=$1
0311     local port_list
0312     local min_port
0313     local max_port
0314 
0315     IFS="-" read -ra port_list <<< $port_str
0316 
0317     min_port=${port_list[0]}
0318     max_port=${port_list[1]:-$min_port}
0319 
0320     echo $min_port $max_port
0321 }
0322 
0323 # Given a minimum and maximum port, verify port number.
0324 function validate_ports()
0325 {
0326     local min_port=$1
0327     local max_port=$2
0328 
0329     # 1 <= port <= 65535
0330     if (in_between $min_port 1 65535); then
0331         if (in_between $max_port 1 65535); then
0332             if [[ $min_port -le $max_port ]]; then
0333                 return 0
0334             fi
0335         fi
0336     fi
0337 
0338     err 5 "Invalid port(s): $min_port-$max_port"
0339 }