1#!/bin/sh 2# 3# Copyright (c) 2018-2020 Retina b.v. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 3. Neither the name of the University nor the names of its contributors 15# may be used to endorse or promote products derived from this software 16# without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28# SUCH DAMAGE. 29# 30# $FreeBSD$ 31 32set -e 33 34progname="$(basename $0 .sh)" 35entries_lst="/tmp/$progname.entries.lst" 36entries2_lst="/tmp/$progname.entries2.lst" 37 38HOOKS=3 39HOOKSADD=42 40ITERATIONS=7 41SUBITERATIONS=71 42 43find_iface () { 44 # Figure out the first ethernet interface 45 ifconfig -u -l ether | awk '{print $1}' 46} 47 48loaded_modules='' 49load_modules () { 50 for kmod in "$@"; do 51 if ! kldstat -q -m $kmod; then 52 test_comment "Loading $kmod..." 53 kldload $kmod 54 loaded_modules="$loaded_modules $kmod" 55 fi 56 done 57} 58unload_modules () { 59 for kmod in $loaded_modules; do 60 # These cannot be unloaded 61 test $kmod = 'ng_ether' -o $kmod = 'ng_socket' \ 62 && continue 63 64 test_comment "Unloading $kmod..." 65 kldunload $kmod 66 done 67 loaded_modules='' 68} 69 70configure_nodes () { 71 ngctl mkpeer $eth: macfilter lower ether # Connect the lower hook of the ether instance $eth to the ether hook of a new macfilter instance 72 ngctl name $eth:lower MF # Give the macfilter instance a name 73 ngctl mkpeer $eth: one2many upper one # Connect the upper hook of the ether instance $eth to the one hook of a new one2many instance 74 ngctl name $eth:upper O2M # Give the one2many instance a name 75 ngctl msg O2M: setconfig "{ xmitAlg=3 failAlg=1 enabledLinks=[ 1 1 ] }" # XMIT_FAILOVER -> send replies always out many0 76 77 ngctl connect MF: O2M: default many0 # Connect macfilter:default to the many0 hook of a one2many instance 78 for i in $(seq 1 1 $HOOKS); do 79 ngctl connect MF: O2M: out$i many$i 80 done 81} 82 83deconfigure_nodes () { 84 ngctl shutdown MF: 85 ngctl shutdown O2M: 86} 87 88cleanup () { 89 test_title "Cleaning up" 90 91 deconfigure_nodes 92 unload_modules 93 94 rm -f $entries_lst $entries2_lst 95} 96 97TSTNR=0 98TSTFAILS=0 99TSTSUCCS=0 100 101_test_next () { TSTNR=$((TSTNR + 1)); } 102_test_succ () { TSTSUCCS=$((TSTSUCCS + 1)); } 103_test_fail () { TSTFAILS=$((TSTFAILS + 1)); } 104 105test_cnt () { echo "1..${1:-$TSTNR}"; } 106test_title () { 107 local msg="$1" 108 109 printf '### %s ' "$msg" 110 printf '#%.0s' $(seq $((80 - ${#msg} - 5))) 111 printf "\n" 112} 113test_comment () { echo "# $1"; } 114test_bailout () { echo "Bail out!${1+:- $1}"; exit 1; } 115test_bail_on_fail () { test $TSTFAILS -eq 0 || test_bailout $1; } 116test_ok () { 117 local msg="$1" 118 119 _test_next 120 _test_succ 121 echo "ok $TSTNR - $msg" 122 123 return 0 124} 125test_not_ok () { 126 local msg="$1" 127 128 _test_next 129 _test_fail 130 echo "not ok $TSTNR - $msg" 131 132 return 1 133} 134test_eq () { 135 local v1="$1" v2="$2" msg="$3" 136 137 if [ "$v1" = "$v2" ]; then 138 test_ok "$v1 $msg" 139 else 140 test_not_ok "$v1 vs $v2 $msg" 141 fi 142} 143test_ne () { 144 local v1="$1" v2="$2" msg="$3" 145 146 if [ "$v1" != "$v2" ]; then 147 test_ok "$v1 $msg" 148 else 149 test_not_ok "$v1 vs $v2 $msg" 150 fi 151} 152test_lt () { 153 local v1=$1 v2=$2 msg="$3" 154 155 if [ "$v1" -lt "$v2" ]; then 156 test_ok "$v1 $msg" 157 else 158 test_not_ok "$v1 >= $v2 $msg" 159 fi 160} 161test_le () { 162 local v1=$1 v2=$2 msg="$3" 163 164 if [ "$v1" -le "$v2" ]; then 165 test_ok "$v1 $msg" 166 else 167 test_not_ok "$v1 >= $v2 $msg" 168 fi 169} 170test_gt () { 171 local v1=$1 v2=$2 msg="$3" 172 173 if [ "$v1" -gt "$v2" ]; then 174 test_ok "$v1 $msg" 175 else 176 test_not_ok "$v1 <= $v2 $msg" 177 fi 178} 179test_ge () { 180 local v1=$1 v2=$2 msg="$3" 181 182 if [ "$v1" -ge "$v2" ]; then 183 test_ok "$v1 $msg" 184 else 185 test_not_ok "$v1 <= $v2 $msg" 186 fi 187} 188test_failure () { 189 msg=$1 190 shift 191 if ! "$@"; then 192 test_ok "$msg - \"$@\" failed as expected" 193 else 194 test_not_ok "$msg - expected \"$@\" to fail but succeeded" 195 fi 196} 197test_success () { 198 msg=$1 199 shift 200 if ! "$@"; then 201 test_not_ok "$msg - \"$@\" failed unexpectedly" 202 else 203 test_ok "$msg - \"$@\" succeeded" 204 fi 205} 206 207gethooks () { 208 ngctl msg MF: 'gethooks' \ 209 | perl -ne '$h{$1}=1 while s/hookname="(.*?)"//; sub END {print join(":", sort keys %h)."\n"}' 210} 211 212countmacs () { 213 local hookname=${1:-'[^"]*'} 214 215 ngctl msg MF: 'gethooks' \ 216 | perl -ne 'sub BEGIN {$c=0} $c += $1 while s/hookname="'$hookname'" hookid=\d+ maccnt=(\d+)//; sub END {print "$c\n"}' 217} 218randomedge () { 219 local edge="out$(seq 0 1 $HOOKS | sort -R | head -1)" 220 test $edge = 'out0' \ 221 && echo default \ 222 || echo $edge 223} 224genmac () { 225 echo "00:00:00:00:$(printf "%02x" $1):$(printf "%02x" $2)" 226} 227 228 229 230################################################################################ 231### Start ###################################################################### 232################################################################################ 233 234test_title "Setting up system..." 235load_modules netgraph ng_socket ng_ether ng_macfilter ng_one2many 236eth=$(find_iface) 237if [ -z "$eth" ]; then 238 echo "1..0 # SKIP could not find a valid interface" 239 echo "Available interfaces:" 240 ifconfig 241 exit 1 242fi 243test_comment "Using $eth..." 244 245 246test_title "Configuring netgraph nodes..." 247configure_nodes 248 249trap 'exit 99' 1 2 3 13 14 15 250trap 'cleanup' EXIT 251 252created_hooks=$(gethooks) 253rc=0 254 255# Update this number when adding new tests 256test_cnt 46 257 258 259################################################################################ 260### Tests ###################################################################### 261################################################################################ 262 263################################################################################ 264test_title "Test: Duplicate default hook" 265test_failure "duplicate connect of default hook" ngctl connect MF: O2M: default many99 266 267################################################################################ 268test_title "Test: Add and remove hooks" 269test_success "connect MF:xxx1 to O2M:many$((HOOKS + 1))" ngctl connect MF: O2M: xxx1 many$((HOOKS + 1)) 270test_success "connect MF:xxx2 to O2M:many$((HOOKS + 2))" ngctl connect MF: O2M: xxx2 many$((HOOKS + 2)) 271test_success "connect MF:xxx3 to O2M:many$((HOOKS + 3))" ngctl connect MF: O2M: xxx3 many$((HOOKS + 3)) 272hooks=$(gethooks) 273test_eq $created_hooks:xxx1:xxx2:xxx3 $hooks 'hooks after adding xxx1-3' 274 275test_success "rmhook MF:xxx$i" ngctl rmhook MF: xxx1 276hooks=$(gethooks) 277test_eq $created_hooks:xxx2:xxx3 $hooks 'hooks after removing xxx1' 278test_success "rmhook MF:xxx$i" ngctl rmhook MF: xxx2 279hooks=$(gethooks) 280test_eq $created_hooks:xxx3 $hooks 'hooks after removing xxx2' 281test_success "rmhook MF:xxx$i" ngctl rmhook MF: xxx3 282hooks=$(gethooks) 283test_eq $created_hooks $hooks 'hooks after removing xxx3' 284 285test_bail_on_fail 286 287################################################################################ 288test_title "Test: Add many hooks" 289added_hooks="" 290for i in $(seq 10 1 $HOOKSADD); do 291 added_hooks="$added_hooks:xxx$i" 292 ngctl connect MF: O2M: xxx$i many$((HOOKS + i)) 293done 294hooks=$(gethooks) 295test_eq $created_hooks$added_hooks $hooks 'hooks after adding many hooks' 296 297for h in $(echo $added_hooks | perl -ne 'chomp; %h=map { $_=>1 } split /:/; print "$_\n" for grep {$_} keys %h'); do 298 ngctl rmhook MF: $h 299done 300hooks=$(gethooks) 301test_eq $created_hooks $hooks 'hooks after adding many hooks' 302 303test_bail_on_fail 304 305 306################################################################################ 307test_title "Test: Adding many MACs..." 308I=1 309for i in $(seq $ITERATIONS | sort -R); do 310 test_comment "Iteration $I/$ITERATIONS..." 311 for j in $(seq 0 1 $SUBITERATIONS); do 312 test $i = 2 && edge='out2' || edge='out1' 313 ether=$(genmac $j $i) 314 315 ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }" 316 done 317 I=$((I + 1)) 318done 319 320n=$(countmacs out1) 321n2=$(( ( ITERATIONS - 1 ) * ( SUBITERATIONS + 1 ) )) 322test_eq $n $n2 'MACs in table for out1' 323n=$(countmacs out2) 324n2=$(( 1 * ( SUBITERATIONS + 1 ) )) 325test_eq $n $n2 'MACs in table for out2' 326n=$(countmacs out3) 327n2=0 328test_eq $n $n2 'MACs in table for out3' 329 330test_bail_on_fail 331 332 333################################################################################ 334test_title "Test: Changing hooks for MACs..." 335for i in $(seq $ITERATIONS); do 336 edge='out3' 337 ether=$(genmac 0 $i) 338 339 ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }" 340done 341 342n=$(countmacs out1) 343n2=$(( ( ITERATIONS - 1 ) * ( SUBITERATIONS + 1 - 1 ) )) 344test_eq $n $n2 'MACs in table for out1' 345n=$(countmacs out2) 346n2=$(( 1 * ( SUBITERATIONS + 1 - 1 ) )) 347test_eq $n $n2 'MACs in table for out2' 348n=$(countmacs out3) 349n2=$ITERATIONS 350test_eq $n $n2 'MACs in table for out3' 351 352test_bail_on_fail 353 354 355################################################################################ 356test_title "Test: Removing all MACs one by one..." 357I=1 358for i in $(seq $ITERATIONS | sort -R); do 359 test_comment "Iteration $I/$ITERATIONS..." 360 for j in $(seq 0 1 $SUBITERATIONS | sort -R); do 361 edge="default" 362 ether=$(genmac $j $i) 363 364 ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }" 365 done 366 I=$(($I + 1)) 367done 368n=$(countmacs) 369n2=0 370test_eq $n $n2 'MACs in table' 371 372test_bail_on_fail 373 374 375################################################################################ 376test_title "Test: Randomly adding MACs on random hooks..." 377rm -f $entries_lst 378for i in $(seq $ITERATIONS); do 379 test_comment "Iteration $i/$ITERATIONS..." 380 for j in $(seq 0 1 $SUBITERATIONS | sort -R); do 381 edge=$(randomedge) 382 ether=$(genmac $j $i) 383 384 ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }" 385 386 echo $ether $edge >> $entries_lst 387 done 388 389 n=$(countmacs) 390done 391 392n=$(countmacs out1) 393n2=$(grep -c ' out1' $entries_lst) 394test_eq $n $n2 'MACs in table for out1' 395n=$(countmacs out2) 396n2=$(grep -c ' out2' $entries_lst) 397test_eq $n $n2 'MACs in table for out2' 398n=$(countmacs out3) 399n2=$(grep -c ' out3' $entries_lst) 400test_eq $n $n2 'MACs in table for out3' 401 402test_bail_on_fail 403 404 405################################################################################ 406test_title "Test: Randomly changing MAC assignments..." 407rm -f $entries2_lst 408for i in $(seq $ITERATIONS); do 409 test_comment "Iteration $i/$ITERATIONS..." 410 cat $entries_lst | while read ether edge; do 411 edge2=$(randomedge) 412 413 ngctl msg MF: 'direct' "{ hookname=\"$edge2\" ether=$ether }" 414 415 echo $ether $edge2 >> $entries2_lst 416 done 417 418 n=$(countmacs out1) 419 n2=$(grep -c ' out1' $entries2_lst) 420 test_eq $n $n2 'MACs in table for out1' 421 n=$(countmacs out2) 422 n2=$(grep -c ' out2' $entries2_lst) 423 test_eq $n $n2 'MACs in table for out2' 424 n=$(countmacs out3) 425 n2=$(grep -c ' out3' $entries2_lst) 426 test_eq $n $n2 'MACs in table for out3' 427 428 test_bail_on_fail 429 430 mv $entries2_lst $entries_lst 431done 432 433 434################################################################################ 435test_title "Test: Resetting macfilter..." 436test_success "**** reset failed" ngctl msg MF: reset 437test_eq $(countmacs) 0 'MACs in table' 438 439test_bail_on_fail 440 441 442################################################################################ 443 444exit 0 445