1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4# +-------------------------+ +-------------------------+ 5# | H1 | | H2 | 6# | $h1 + | | + $h2 | 7# | 192.0.2.1/28 | | | | 192.0.2.34/28 | 8# | 2001:db8:1::1/64 | | | | 2001:db8:3::2/64 | 9# +-------------------|-----+ +-|-----------------------+ 10# | | 11# +-------------------|-----+ +-|-----------------------+ 12# | R1 | | | | R2 | 13# | $rp11 + | | + $rp21 | 14# | 192.0.2.2/28 | | 192.0.2.33/28 | 15# | 2001:db8:1::2/64 | | 2001:db8:3::1/64 | 16# | | | | 17# | $rp12 + | | + $rp22 | 18# | 192.0.2.17/28 | | | | 192.0.2.18..27/28 | 19# | 2001:db8:2::17/64 | | | | 2001:db8:2::18..27/64 | 20# +-------------------|-----+ +-|-----------------------+ 21# | | 22# `----------' 23 24ALL_TESTS=" 25 ping_ipv4 26 ping_ipv6 27 test_mpath_seed_stability_ipv4 28 test_mpath_seed_stability_ipv6 29 test_mpath_seed_get 30 test_mpath_seed_ipv4 31 test_mpath_seed_ipv6 32" 33NUM_NETIFS=6 34source lib.sh 35 36h1_create() 37{ 38 simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64 39 ip -4 route add 192.0.2.32/28 vrf v$h1 nexthop via 192.0.2.2 40 ip -6 route add 2001:db8:3::/64 vrf v$h1 nexthop via 2001:db8:1::2 41} 42 43h1_destroy() 44{ 45 ip -6 route del 2001:db8:3::/64 vrf v$h1 nexthop via 2001:db8:1::2 46 ip -4 route del 192.0.2.32/28 vrf v$h1 nexthop via 192.0.2.2 47 simple_if_fini $h1 192.0.2.1/28 2001:db8:1::1/64 48} 49 50h2_create() 51{ 52 simple_if_init $h2 192.0.2.34/28 2001:db8:3::2/64 53 ip -4 route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.33 54 ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:3::1 55} 56 57h2_destroy() 58{ 59 ip -6 route del 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:3::1 60 ip -4 route del 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.33 61 simple_if_fini $h2 192.0.2.34/28 2001:db8:3::2/64 62} 63 64router1_create() 65{ 66 simple_if_init $rp11 192.0.2.2/28 2001:db8:1::2/64 67 __simple_if_init $rp12 v$rp11 192.0.2.17/28 2001:db8:2::17/64 68} 69 70router1_destroy() 71{ 72 __simple_if_fini $rp12 192.0.2.17/28 2001:db8:2::17/64 73 simple_if_fini $rp11 192.0.2.2/28 2001:db8:1::2/64 74} 75 76router2_create() 77{ 78 simple_if_init $rp21 192.0.2.33/28 2001:db8:3::1/64 79 __simple_if_init $rp22 v$rp21 192.0.2.18/28 2001:db8:2::18/64 80 ip -4 route add 192.0.2.0/28 vrf v$rp21 nexthop via 192.0.2.17 81 ip -6 route add 2001:db8:1::/64 vrf v$rp21 nexthop via 2001:db8:2::17 82} 83 84router2_destroy() 85{ 86 ip -6 route del 2001:db8:1::/64 vrf v$rp21 nexthop via 2001:db8:2::17 87 ip -4 route del 192.0.2.0/28 vrf v$rp21 nexthop via 192.0.2.17 88 __simple_if_fini $rp22 192.0.2.18/28 2001:db8:2::18/64 89 simple_if_fini $rp21 192.0.2.33/28 2001:db8:3::1/64 90} 91 92nexthops_create() 93{ 94 local i 95 for i in $(seq 10); do 96 ip nexthop add id $((1000 + i)) via 192.0.2.18 dev $rp12 97 ip nexthop add id $((2000 + i)) via 2001:db8:2::18 dev $rp12 98 done 99 100 ip nexthop add id 1000 group $(seq -s / 1001 1010) hw_stats on 101 ip nexthop add id 2000 group $(seq -s / 2001 2010) hw_stats on 102 ip -4 route add 192.0.2.32/28 vrf v$rp11 nhid 1000 103 ip -6 route add 2001:db8:3::/64 vrf v$rp11 nhid 2000 104} 105 106nexthops_destroy() 107{ 108 local i 109 110 ip -6 route del 2001:db8:3::/64 vrf v$rp11 nhid 2000 111 ip -4 route del 192.0.2.32/28 vrf v$rp11 nhid 1000 112 ip nexthop del id 2000 113 ip nexthop del id 1000 114 115 for i in $(seq 10 -1 1); do 116 ip nexthop del id $((2000 + i)) 117 ip nexthop del id $((1000 + i)) 118 done 119} 120 121setup_prepare() 122{ 123 h1=${NETIFS[p1]} 124 rp11=${NETIFS[p2]} 125 126 rp12=${NETIFS[p3]} 127 rp22=${NETIFS[p4]} 128 129 rp21=${NETIFS[p5]} 130 h2=${NETIFS[p6]} 131 132 sysctl_save net.ipv4.fib_multipath_hash_seed 133 134 vrf_prepare 135 136 h1_create 137 h2_create 138 router1_create 139 router2_create 140 141 forwarding_enable 142} 143 144cleanup() 145{ 146 pre_cleanup 147 148 forwarding_restore 149 150 nexthops_destroy 151 router2_destroy 152 router1_destroy 153 h2_destroy 154 h1_destroy 155 156 vrf_cleanup 157 158 sysctl_restore net.ipv4.fib_multipath_hash_seed 159} 160 161ping_ipv4() 162{ 163 ping_test $h1 192.0.2.34 164} 165 166ping_ipv6() 167{ 168 ping6_test $h1 2001:db8:3::2 169} 170 171test_mpath_seed_get() 172{ 173 RET=0 174 175 local i 176 for ((i = 0; i < 100; i++)); do 177 local seed_w=$((999331 * i)) 178 sysctl -qw net.ipv4.fib_multipath_hash_seed=$seed_w 179 local seed_r=$(sysctl -n net.ipv4.fib_multipath_hash_seed) 180 ((seed_r == seed_w)) 181 check_err $? "mpath seed written as $seed_w, but read as $seed_r" 182 done 183 184 log_test "mpath seed set/get" 185} 186 187nh_stats_snapshot() 188{ 189 local group_id=$1; shift 190 191 ip -j -s -s nexthop show id $group_id | 192 jq -c '[.[].group_stats | sort_by(.id) | .[].packets]' 193} 194 195get_active_nh() 196{ 197 local s0=$1; shift 198 local s1=$1; shift 199 200 jq -n --argjson s0 "$s0" --argjson s1 "$s1" -f /dev/stdin <<-"EOF" 201 [range($s0 | length)] | 202 map($s1[.] - $s0[.]) | 203 map(if . > 8 then 1 else 0 end) | 204 index(1) 205 EOF 206} 207 208probe_nh() 209{ 210 local group_id=$1; shift 211 local -a mz=("$@") 212 213 local s0=$(nh_stats_snapshot $group_id) 214 "${mz[@]}" 215 local s1=$(nh_stats_snapshot $group_id) 216 217 get_active_nh "$s0" "$s1" 218} 219 220probe_seed() 221{ 222 local group_id=$1; shift 223 local seed=$1; shift 224 local -a mz=("$@") 225 226 sysctl -qw net.ipv4.fib_multipath_hash_seed=$seed 227 probe_nh "$group_id" "${mz[@]}" 228} 229 230test_mpath_seed() 231{ 232 local group_id=$1; shift 233 local what=$1; shift 234 local -a mz=("$@") 235 local ii 236 237 RET=0 238 239 local -a tally=(0 0 0 0 0 0 0 0 0 0) 240 for ((ii = 0; ii < 100; ii++)); do 241 local act=$(probe_seed $group_id $((999331 * ii)) "${mz[@]}") 242 ((tally[act]++)) 243 done 244 245 local tally_str="${tally[@]}" 246 for ((ii = 0; ii < ${#tally[@]}; ii++)); do 247 ((tally[ii] > 0)) 248 check_err $? "NH #$ii not hit, tally='$tally_str'" 249 done 250 251 log_test "mpath seed $what" 252 sysctl -qw net.ipv4.fib_multipath_hash_seed=0 253} 254 255test_mpath_seed_ipv4() 256{ 257 test_mpath_seed 1000 IPv4 \ 258 $MZ $h1 -A 192.0.2.1 -B 192.0.2.34 -q \ 259 -p 64 -d 0 -c 10 -t udp 260} 261 262test_mpath_seed_ipv6() 263{ 264 test_mpath_seed 2000 IPv6 \ 265 $MZ -6 $h1 -A 2001:db8:1::1 -B 2001:db8:3::2 -q \ 266 -p 64 -d 0 -c 10 -t udp 267} 268 269check_mpath_seed_stability() 270{ 271 local seed=$1; shift 272 local act_0=$1; shift 273 local act_1=$1; shift 274 275 ((act_0 == act_1)) 276 check_err $? "seed $seed: active NH moved from $act_0 to $act_1 after seed change" 277} 278 279test_mpath_seed_stability() 280{ 281 local group_id=$1; shift 282 local what=$1; shift 283 local -a mz=("$@") 284 285 RET=0 286 287 local seed_0=0 288 local seed_1=3221338814 289 local seed_2=3735928559 290 291 # Initial active NH before touching the seed at all. 292 local act_ini=$(probe_nh $group_id "${mz[@]}") 293 294 local act_0_0=$(probe_seed $group_id $seed_0 "${mz[@]}") 295 local act_1_0=$(probe_seed $group_id $seed_1 "${mz[@]}") 296 local act_2_0=$(probe_seed $group_id $seed_2 "${mz[@]}") 297 298 local act_0_1=$(probe_seed $group_id $seed_0 "${mz[@]}") 299 local act_1_1=$(probe_seed $group_id $seed_1 "${mz[@]}") 300 local act_2_1=$(probe_seed $group_id $seed_2 "${mz[@]}") 301 302 check_mpath_seed_stability initial $act_ini $act_0_0 303 check_mpath_seed_stability $seed_0 $act_0_0 $act_0_1 304 check_mpath_seed_stability $seed_1 $act_1_0 $act_1_1 305 check_mpath_seed_stability $seed_2 $act_2_0 $act_2_1 306 307 log_test "mpath seed stability $what" 308 sysctl -qw net.ipv4.fib_multipath_hash_seed=0 309} 310 311test_mpath_seed_stability_ipv4() 312{ 313 test_mpath_seed_stability 1000 IPv4 \ 314 $MZ $h1 -A 192.0.2.1 -B 192.0.2.34 -q \ 315 -p 64 -d 0 -c 10 -t udp 316} 317 318test_mpath_seed_stability_ipv6() 319{ 320 test_mpath_seed_stability 2000 IPv6 \ 321 $MZ -6 $h1 -A 2001:db8:1::1 -B 2001:db8:3::2 -q \ 322 -p 64 -d 0 -c 10 -t udp 323} 324 325trap cleanup EXIT 326 327setup_prepare 328setup_wait 329nexthops_create 330 331tests_run 332 333exit $EXIT_STATUS 334