test_neigh.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. #!/bin/bash
  2. # SPDX-License-Identifier: GPL-2.0
  3. source lib.sh
  4. TESTS="
  5. extern_valid_ipv4
  6. extern_valid_ipv6
  7. "
  8. VERBOSE=0
  9. ################################################################################
  10. # Utilities
  11. run_cmd()
  12. {
  13. local cmd="$1"
  14. local out
  15. local stderr="2>/dev/null"
  16. if [ "$VERBOSE" = "1" ]; then
  17. echo "COMMAND: $cmd"
  18. stderr=
  19. fi
  20. out=$(eval "$cmd" "$stderr")
  21. rc=$?
  22. if [ "$VERBOSE" -eq 1 ] && [ -n "$out" ]; then
  23. echo " $out"
  24. fi
  25. return $rc
  26. }
  27. ################################################################################
  28. # Setup
  29. setup()
  30. {
  31. set -e
  32. setup_ns ns1 ns2
  33. ip -n "$ns1" link add veth0 type veth peer name veth1 netns "$ns2"
  34. ip -n "$ns1" link set dev veth0 up
  35. ip -n "$ns2" link set dev veth1 up
  36. ip -n "$ns1" address add 192.0.2.1/24 dev veth0
  37. ip -n "$ns1" address add 2001:db8:1::1/64 dev veth0 nodad
  38. ip -n "$ns2" address add 192.0.2.2/24 dev veth1
  39. ip -n "$ns2" address add 2001:db8:1::2/64 dev veth1 nodad
  40. ip netns exec "$ns1" sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
  41. ip netns exec "$ns2" sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
  42. sleep 5
  43. set +e
  44. }
  45. exit_cleanup_all()
  46. {
  47. cleanup_all_ns
  48. exit "${EXIT_STATUS}"
  49. }
  50. ################################################################################
  51. # Tests
  52. extern_valid_common()
  53. {
  54. local af_str=$1; shift
  55. local ip_addr=$1; shift
  56. local tbl_name=$1; shift
  57. local subnet=$1; shift
  58. local mac
  59. mac=$(ip -n "$ns2" -j link show dev veth1 | jq -r '.[]["address"]')
  60. RET=0
  61. # Check that simple addition works.
  62. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  63. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  64. check_err $? "No \"extern_valid\" flag after addition"
  65. log_test "$af_str \"extern_valid\" flag: Add entry"
  66. RET=0
  67. # Check that an entry cannot be added with "extern_valid" flag and an
  68. # invalid state.
  69. run_cmd "ip -n $ns1 neigh flush dev veth0"
  70. run_cmd "ip -n $ns1 neigh add $ip_addr nud none dev veth0 extern_valid"
  71. check_fail $? "Managed to add an entry with \"extern_valid\" flag and an invalid state"
  72. log_test "$af_str \"extern_valid\" flag: Add with an invalid state"
  73. RET=0
  74. # Check that entry cannot be added with both "extern_valid" flag and
  75. # "use" / "managed" flag.
  76. run_cmd "ip -n $ns1 neigh flush dev veth0"
  77. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid use"
  78. check_fail $? "Managed to add an entry with \"extern_valid\" flag and \"use\" flag"
  79. log_test "$af_str \"extern_valid\" flag: Add with \"use\" flag"
  80. RET=0
  81. # Check that "extern_valid" flag can be toggled using replace.
  82. run_cmd "ip -n $ns1 neigh flush dev veth0"
  83. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0"
  84. run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  85. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  86. check_err $? "Did not manage to set \"extern_valid\" flag with replace"
  87. run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0"
  88. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  89. check_fail $? "Did not manage to clear \"extern_valid\" flag with replace"
  90. log_test "$af_str \"extern_valid\" flag: Replace entry"
  91. RET=0
  92. # Check that an existing "extern_valid" entry can be marked as
  93. # "managed".
  94. run_cmd "ip -n $ns1 neigh flush dev veth0"
  95. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  96. run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid managed"
  97. check_err $? "Did not manage to add \"managed\" flag to an existing \"extern_valid\" entry"
  98. log_test "$af_str \"extern_valid\" flag: Replace entry with \"managed\" flag"
  99. RET=0
  100. # Check that entry cannot be replaced with "extern_valid" flag and an
  101. # invalid state.
  102. run_cmd "ip -n $ns1 neigh flush dev veth0"
  103. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  104. run_cmd "ip -n $ns1 neigh replace $ip_addr nud none dev veth0 extern_valid"
  105. check_fail $? "Managed to replace an entry with \"extern_valid\" flag and an invalid state"
  106. log_test "$af_str \"extern_valid\" flag: Replace with an invalid state"
  107. RET=0
  108. # Check that an "extern_valid" entry is flushed when the interface is
  109. # put administratively down.
  110. run_cmd "ip -n $ns1 neigh flush dev veth0"
  111. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  112. run_cmd "ip -n $ns1 link set dev veth0 down"
  113. run_cmd "ip -n $ns1 link set dev veth0 up"
  114. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0"
  115. check_fail $? "\"extern_valid\" entry not flushed upon interface down"
  116. log_test "$af_str \"extern_valid\" flag: Interface down"
  117. RET=0
  118. # Check that an "extern_valid" entry is not flushed when the interface
  119. # loses its carrier.
  120. run_cmd "ip -n $ns1 neigh flush dev veth0"
  121. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  122. run_cmd "ip -n $ns2 link set dev veth1 down"
  123. run_cmd "ip -n $ns2 link set dev veth1 up"
  124. run_cmd "sleep 2"
  125. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0"
  126. check_err $? "\"extern_valid\" entry flushed upon carrier down"
  127. log_test "$af_str \"extern_valid\" flag: Carrier down"
  128. RET=0
  129. # Check that when entry transitions to "reachable" state it maintains
  130. # the "extern_valid" flag. Wait "delay_probe" seconds for ARP request /
  131. # NS to be sent.
  132. local delay_probe
  133. delay_probe=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["delay_probe"]')
  134. run_cmd "ip -n $ns1 neigh flush dev veth0"
  135. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  136. run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid use"
  137. run_cmd "sleep $((delay_probe / 1000 + 2))"
  138. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"REACHABLE\""
  139. check_err $? "Entry did not transition to \"reachable\" state"
  140. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  141. check_err $? "Entry did not maintain \"extern_valid\" flag after transition to \"reachable\" state"
  142. log_test "$af_str \"extern_valid\" flag: Transition to \"reachable\" state"
  143. RET=0
  144. # Drop all packets, trigger resolution and check that entry goes back
  145. # to "stale" state instead of "failed".
  146. local mcast_reprobes
  147. local retrans_time
  148. local ucast_probes
  149. local app_probes
  150. local probes
  151. local delay
  152. run_cmd "ip -n $ns1 neigh flush dev veth0"
  153. run_cmd "tc -n $ns2 qdisc add dev veth1 clsact"
  154. run_cmd "tc -n $ns2 filter add dev veth1 ingress proto all matchall action drop"
  155. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  156. run_cmd "ip -n $ns1 neigh replace $ip_addr lladdr $mac nud stale dev veth0 extern_valid use"
  157. retrans_time=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["retrans"]')
  158. ucast_probes=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["ucast_probes"]')
  159. app_probes=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["app_probes"]')
  160. mcast_reprobes=$(ip -n "$ns1" -j ntable show dev veth0 name "$tbl_name" | jq '.[]["mcast_reprobes"]')
  161. delay=$((delay_probe + (ucast_probes + app_probes + mcast_reprobes) * retrans_time))
  162. run_cmd "sleep $((delay / 1000 + 2))"
  163. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"STALE\""
  164. check_err $? "Entry did not return to \"stale\" state"
  165. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  166. check_err $? "Entry did not maintain \"extern_valid\" flag after returning to \"stale\" state"
  167. probes=$(ip -n "$ns1" -j -s neigh get "$ip_addr" dev veth0 | jq '.[]["probes"]')
  168. if [[ $probes -eq 0 ]]; then
  169. check_err 1 "No probes were sent"
  170. fi
  171. log_test "$af_str \"extern_valid\" flag: Transition back to \"stale\" state"
  172. run_cmd "tc -n $ns2 qdisc del dev veth1 clsact"
  173. RET=0
  174. # Forced garbage collection runs whenever the number of entries is
  175. # larger than "thresh3" and deletes stale entries that have not been
  176. # updated in the last 5 seconds.
  177. #
  178. # Check that an "extern_valid" entry survives a forced garbage
  179. # collection. Add an entry, wait 5 seconds and add more entries than
  180. # "thresh3" so that forced garbage collection will run.
  181. #
  182. # Note that the garbage collection thresholds are global resources and
  183. # that changes in the initial namespace affect all the namespaces.
  184. local forced_gc_runs_t0
  185. local forced_gc_runs_t1
  186. local orig_thresh1
  187. local orig_thresh2
  188. local orig_thresh3
  189. run_cmd "ip -n $ns1 neigh flush dev veth0"
  190. orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]')
  191. orig_thresh2=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh2")) | .["thresh2"]')
  192. orig_thresh3=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh3")) | .["thresh3"]')
  193. run_cmd "ip ntable change name $tbl_name thresh3 10 thresh2 9 thresh1 8"
  194. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  195. run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0"
  196. run_cmd "sleep 5"
  197. forced_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]')
  198. for i in {1..20}; do
  199. run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0"
  200. done
  201. forced_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]')
  202. if [[ $forced_gc_runs_t1 -eq $forced_gc_runs_t0 ]]; then
  203. check_err 1 "Forced garbage collection did not run"
  204. fi
  205. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  206. check_err $? "Entry with \"extern_valid\" flag did not survive forced garbage collection"
  207. run_cmd "ip -n $ns1 neigh get ${subnet}3 dev veth0"
  208. check_fail $? "Entry without \"extern_valid\" flag survived forced garbage collection"
  209. log_test "$af_str \"extern_valid\" flag: Forced garbage collection"
  210. run_cmd "ip ntable change name $tbl_name thresh3 $orig_thresh3 thresh2 $orig_thresh2 thresh1 $orig_thresh1"
  211. RET=0
  212. # Periodic garbage collection runs every "base_reachable"/2 seconds and
  213. # if the number of entries is larger than "thresh1", then it deletes
  214. # stale entries that have not been used in the last "gc_stale" seconds.
  215. #
  216. # Check that an "extern_valid" entry survives a periodic garbage
  217. # collection. Add an "extern_valid" entry, add more than "thresh1"
  218. # regular entries, wait "base_reachable" (longer than "gc_stale")
  219. # seconds and check that the "extern_valid" entry was not deleted.
  220. #
  221. # Note that the garbage collection thresholds and "base_reachable" are
  222. # global resources and that changes in the initial namespace affect all
  223. # the namespaces.
  224. local periodic_gc_runs_t0
  225. local periodic_gc_runs_t1
  226. local orig_base_reachable
  227. local orig_gc_stale
  228. run_cmd "ip -n $ns1 neigh flush dev veth0"
  229. orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]')
  230. orig_base_reachable=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["base_reachable"]')
  231. run_cmd "ip ntable change name $tbl_name thresh1 10 base_reachable 10000"
  232. orig_gc_stale=$(ip -n "$ns1" -j ntable show name "$tbl_name" dev veth0 | jq '.[]["gc_stale"]')
  233. run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale 1000"
  234. run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
  235. run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0"
  236. # Wait orig_base_reachable/2 for the new interval to take effect.
  237. run_cmd "sleep $(((orig_base_reachable / 1000) / 2 + 2))"
  238. for i in {1..20}; do
  239. run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0"
  240. done
  241. periodic_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
  242. run_cmd "sleep 10"
  243. periodic_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
  244. [[ $periodic_gc_runs_t1 -ne $periodic_gc_runs_t0 ]]
  245. check_err $? "Periodic garbage collection did not run"
  246. run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
  247. check_err $? "Entry with \"extern_valid\" flag did not survive periodic garbage collection"
  248. run_cmd "ip -n $ns1 neigh get ${subnet}3 dev veth0"
  249. check_fail $? "Entry without \"extern_valid\" flag survived periodic garbage collection"
  250. log_test "$af_str \"extern_valid\" flag: Periodic garbage collection"
  251. run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale $orig_gc_stale"
  252. run_cmd "ip ntable change name $tbl_name thresh1 $orig_thresh1 base_reachable $orig_base_reachable"
  253. }
  254. extern_valid_ipv4()
  255. {
  256. extern_valid_common "IPv4" 192.0.2.2 "arp_cache" 192.0.2.
  257. }
  258. extern_valid_ipv6()
  259. {
  260. extern_valid_common "IPv6" 2001:db8:1::2 "ndisc_cache" 2001:db8:1::
  261. }
  262. ################################################################################
  263. # Usage
  264. usage()
  265. {
  266. cat <<EOF
  267. usage: ${0##*/} OPTS
  268. -t <test> Test(s) to run (default: all)
  269. (options: $TESTS)
  270. -p Pause on fail
  271. -v Verbose mode (show commands and output)
  272. EOF
  273. }
  274. ################################################################################
  275. # Main
  276. while getopts ":t:pvh" opt; do
  277. case $opt in
  278. t) TESTS=$OPTARG;;
  279. p) PAUSE_ON_FAIL=yes;;
  280. v) VERBOSE=$((VERBOSE + 1));;
  281. h) usage; exit 0;;
  282. *) usage; exit 1;;
  283. esac
  284. done
  285. require_command jq
  286. if ! ip neigh help 2>&1 | grep -q "extern_valid"; then
  287. echo "SKIP: iproute2 ip too old, missing \"extern_valid\" support"
  288. exit "$ksft_skip"
  289. fi
  290. trap exit_cleanup_all EXIT
  291. for t in $TESTS
  292. do
  293. setup; $t; cleanup_all_ns;
  294. done