Scripts/programs to test FreeBSD ethernet interfaces.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

617 lines
14 KiB

  1. #!/bin/sh -
  2. #
  3. # Copyright 2021 John-Mark Gurney.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. # 1. Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # 2. Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. #
  14. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. # SUCH DAMAGE.
  25. #
  26. #
  27. # ls testinterfaces.sh | entr sh -c 'fsync testinterfaces.sh && sh testinterfaces.sh run ue0 ue1'
  28. # ifconfig ue0 -vnet testjail; ifconfig ue1 -vnet checkjail
  29. #
  30. # Tests to add:
  31. # Multicast filter programming
  32. # bad checksum filtering
  33. # TSO/LRO functionality
  34. # polling?
  35. # TOE?
  36. # WOL
  37. # vlan hw tso
  38. # vlan hwfilter
  39. # hardware timestamp (may not be testable)
  40. #
  41. testjail=testjail
  42. checkjail=checkjail
  43. # Function to setup jails as needed for tests.
  44. setup()
  45. {
  46. if ! jail -c persist=1 path=/ name=testjail vnet=new vnet.interface="$testiface"; then
  47. echo failed to start test jail
  48. exit 1
  49. fi
  50. if ! jail -c persist=1 path=/ name=checkjail vnet=new vnet.interface="$checkiface"; then
  51. echo failed to start check jail
  52. exit 1
  53. fi
  54. run test ifconfig lo0 up
  55. run check ifconfig lo0 up
  56. }
  57. # Verify that jails are present
  58. checkjails()
  59. {
  60. if ! jls -j "$testjail" >/dev/null 2>/dev/null; then
  61. echo "test jail not present."
  62. return 1
  63. fi
  64. if ! jls -j "$checkjail" >/dev/null 2>/dev/null; then
  65. echo "check jail not present."
  66. return 1
  67. fi
  68. }
  69. # Destroy jails
  70. cleanup()
  71. {
  72. jail -r "$testjail" 2>/dev/null
  73. jail -r "$checkjail" 2>/dev/null
  74. }
  75. # Subroutin for down'ing the interface, and removing any addresses from it.
  76. # There are issues where if the addresses are not cleared, the same address
  77. # won't always work.
  78. cleaniface()
  79. {
  80. jailname="$1"
  81. ifacename="$2"
  82. run "$jailname" ifconfig $ifacename down
  83. run "$jailname" ifconfig $ifacename | grep inet | while read a b c; do
  84. if [ x"$a" = x"inet" -o x"$a" = x"inet6" ]; then
  85. #if [ x"$a" = x"inet" ]; then
  86. run "$jailname" ifconfig $ifacename "$a" "$b" remove
  87. fi
  88. done
  89. run "$jailname" ifconfig $ifacename
  90. }
  91. # Setup the temp directory, switch to it, and more
  92. starttest()
  93. {
  94. tmpdir=$(mktemp -d -t testiface)
  95. if [ $? != 0 ]; then
  96. echo "failed to make temp directory"
  97. exit 2
  98. fi
  99. oldpwd=$(pwd)
  100. cd "$tmpdir"
  101. run test tcpdump -n -p -i $(getiface test) -w "$tmpdir"/test.tcpdump 2> /dev/null &
  102. testdumppid="$!"
  103. # XXX - -p should be used on both interfaces
  104. run check tcpdump -n -i $(getiface check) -w "$tmpdir"/check.tcpdump 2> /dev/null &
  105. checkdumppid="$!"
  106. echo "$testdumppid $checkdumppid"
  107. }
  108. endtest()
  109. {
  110. echo "$testdumppid" "$checkdumppid"
  111. if ! kill "$testdumppid" "$checkdumppid"; then
  112. echo "unable to kill off tcpdumps"
  113. fi
  114. if ! wait "$testdumppid" "$checkdumppid"; then
  115. echo "tcpdumps failed to exit"
  116. fi
  117. cd "$oldpwd"
  118. # only attempt to clean up when successful
  119. if [ "$1" -eq 0 ]; then
  120. echo rm -rf "$tmpdir"
  121. else
  122. echo "test failed, data in $tmpdir"
  123. fi
  124. }
  125. # Run a command in a jail. The first arg is either check or test.
  126. # Remainder of the args are passed for execution.
  127. run()
  128. {
  129. if [ x"$1" = x"check" ]; then
  130. jid="$checkjail"
  131. elif [ x"$1" = x"test" ]; then
  132. jid="$testjail"
  133. else
  134. echo Invalid: "$1" >&2
  135. exit 1
  136. fi
  137. shift
  138. jexec "$jid" "$@"
  139. }
  140. # Return the interface that is in the specified jail.
  141. # The first arg is either check or test.
  142. getiface()
  143. {
  144. if [ x"$1" = x"check" ]; then
  145. echo "$checkiface"
  146. elif [ x"$1" = x"test" ]; then
  147. echo "$testiface"
  148. else
  149. echo Invalid: "$1" >&2
  150. exit 1
  151. fi
  152. }
  153. # Run ifconfig on the base interface in a jail.
  154. # The first arg is either check or test.
  155. # Remainder of the args are passed to ifconfig for execution.
  156. ifjail()
  157. {
  158. j="$1"
  159. shift
  160. iface=$(getiface "$j")
  161. run "$j" ifconfig "$iface" "$@"
  162. }
  163. # Convert an ifconfig option to the options returned by ifconfig
  164. iftoopt()
  165. {
  166. case "$1" in
  167. -txcsum6|txcsum6)
  168. echo TXCSUM_IPV6
  169. ;;
  170. -rxcsum6|rxcsum6)
  171. echo RXCSUM_IPV6
  172. ;;
  173. -txcsum|txcsum)
  174. echo TXCSUM
  175. ;;
  176. -rxcsum|rxcsum)
  177. echo RXCSUM
  178. ;;
  179. -vlanhwtag|vlanhwtag)
  180. echo VLAN_HWTAGGING
  181. ;;
  182. *)
  183. echo "Unknown option: $1" >&2
  184. exit 5
  185. ;;
  186. esac
  187. }
  188. # Get the list of capabilities or options for the interface in the jail.
  189. # The first arg is either check or test.
  190. # If the second arg is specified, it must be one of cap or opt,
  191. # specifying which of the capabilities or options to be returned
  192. # respectively. If this arg is not specified, it defaults to
  193. # capabilities.
  194. getcaps()
  195. {
  196. if [ x"$2" = x"" -o x"$2" = x"cap" ]; then
  197. ifoptcap=capabilities
  198. elif [ x"$2" = x"opt" ]; then
  199. ifoptcap=options
  200. else
  201. echo "Invalid getcaps arg: $2" >&2
  202. exit 1
  203. fi
  204. run "$1" ifconfig -m $(getiface "$1") |
  205. awk '$1 ~ /^'"$ifoptcap"'=/ { split($0, a, "<"); split(a[2], b, ">"); split(b[1], caps, ","); for (i in caps) print caps[i] }' |
  206. sort
  207. }
  208. # Verify that the interface in a jail is capable of the spefified feature.
  209. # The first arg is either check or test.
  210. # The second arg is the capability as printed in the options line.
  211. # This is checked against the capabilities line printed by -m.
  212. # Return 0 if present, return 1 if not present
  213. hascap()
  214. {
  215. getcaps "$1" | grep "^$(iftoopt "$2")$" >/dev/null
  216. }
  217. # Verify that the interface in a jail has a specific capability enabled.
  218. # The first arg is either check or test.
  219. # The second arg is the capability as printed in the options line.
  220. # This is checked against the options line.
  221. # Return 0 if present, return 1 if not present
  222. verifycap()
  223. {
  224. if [ x"${2#-}" = x"${2}" ]; then
  225. # without leading hyphen, present == success
  226. presentretval="0"
  227. absentretval="1"
  228. else
  229. # with leading hyphen, not present == success
  230. presentretval="1"
  231. absentretval="0"
  232. fi
  233. if getcaps "$1" opt | grep "^$(iftoopt "$2")$" >/dev/null; then
  234. return $presentretval
  235. else
  236. return $absentretval
  237. fi
  238. }
  239. # Make sure that the carrier on both interface is up before returning.
  240. # There is currently no timeout.
  241. waitcarrier()
  242. {
  243. local i
  244. for i in test check; do
  245. while :; do
  246. # make sure carrier is present and any IPv6 addresses
  247. # are no longer tentative
  248. if ifjail "$i" | grep 'status: active' >/dev/null &&
  249. ( ! ifjail "$i" | grep 'tentative' >/dev/null); then
  250. break
  251. fi
  252. sleep .5
  253. done
  254. done
  255. }
  256. ##################################
  257. ######### START OF TESTS #########
  258. ##################################
  259. #
  260. # Run tests verify that vlan hardware tagging works (or at least doesn't
  261. # break anything. There isn't an easy way to verify that packets in the
  262. # kernel get/do not get the mbuf tag. (dtrace MAY be an option)
  263. hwvlantest()
  264. {
  265. local i err
  266. err=0
  267. for i in "-vlanhwtag" "vlanhwtag"; do
  268. if ! hascap test "$i"; then
  269. echo "skipping: $i"
  270. continue
  271. fi
  272. echo "testing: $i"
  273. ifjail test "$i"
  274. verifycap test "$i" || return 1
  275. ifjail check -vlanhwtag
  276. verifycap check -vlanhwtag || return 1
  277. cleaniface test $testiface
  278. cleaniface check $checkiface
  279. sleep .5
  280. ifjail test up
  281. ifjail check up
  282. # make sure they don't exist
  283. run test ifconfig $testiface.42 destroy 2>/dev/null
  284. run check ifconfig $checkiface.42 destroy 2>/dev/null
  285. run test ifconfig $testiface.42 create 172.30.5.5/24
  286. run check ifconfig $checkiface.42 create 172.30.5.4/24
  287. waitcarrier
  288. #run check tcpdump -XXX -p -q -c 2 -n -i "$checkiface" ip &
  289. #run test tcpdump -XXX -p -q -c 2 -n -i "$testiface" ip &
  290. #run test ifconfig -a
  291. if ! run test ping -o -c 4 -t 5 -i .5 172.30.5.4 >/dev/null; then
  292. echo FAILED on "$i"!!!!
  293. #run test ifconfig -a
  294. err=1
  295. fi
  296. #run test ifconfig $testiface.42 destroy
  297. #run check ifconfig $checkiface.42 destroy
  298. if [ x"$err" != x"0" ]; then
  299. return 1
  300. fi
  301. done
  302. }
  303. # Internal function for testing checksum.
  304. # The first argument is the flag for the interface under test.
  305. # If it ends with a 6, then IPv6 will be used for testing.
  306. csuminternal()
  307. {
  308. local i
  309. i="$1"
  310. #if ! hascap test "$i"; then
  311. # echo "skipping $i, test interface not capable"
  312. # continue
  313. #fi
  314. echo "testing: $i"
  315. if [ x"$i" = x"${i%6}" ]; then
  316. # IPv4
  317. testnet="172.29.5.5/24"
  318. checknet="172.29.5.4/24"
  319. checkaddr="172.29.5.4"
  320. pingcmd="ping"
  321. ncopt=""
  322. else
  323. # IPv6
  324. testnet="inet6 fc00:0b5d:041c:7e37::7e37/64"
  325. checknet="inet6 fc00:0b5d:041c:7e37::c43c/64"
  326. checkaddr="fc00:0b5d:041c:7e37::c43c"
  327. pingcmd="ping6"
  328. ncopt="-6"
  329. fi
  330. cleaniface test $testiface
  331. cleaniface check $checkiface
  332. ifjail test "$i"
  333. verifycap test "$i" || return 1
  334. ifjail check -txcsum -rxcsum -txcsum6 -rxcsum6
  335. verifycap check "-txcsum" || return 1
  336. verifycap check "-rxcsum" || return 1
  337. sleep .5
  338. ifjail test up
  339. ifjail check up
  340. run test ifconfig $testiface $testnet
  341. run check ifconfig $checkiface $checknet
  342. waitcarrier
  343. sleep .5
  344. #run check tcpdump -XXX -p -q -n -i "$checkiface" not ip6 &
  345. #run test tcpdump -XXX -p -q -n -i "$testiface" not ip6 &
  346. #run test ifconfig -a
  347. #run check ifconfig -a
  348. # make sure connectivity works
  349. if ! run test "$pingcmd" -o -c 4 -t 5 -i .5 "$checkaddr" >/dev/null; then
  350. ifjail test
  351. ifjail check
  352. echo ping FAILED on "$i"!!!!
  353. return 1
  354. fi
  355. mkfifo /tmp/tififo.$$
  356. run check sh -c 'cat /tmp/tififo.'$$' | nc '"$ncopt"' -l 3333 | cat > /tmp/tififo.'$$ 2>/dev/null &
  357. sleep .5
  358. value="foobar $$ TCP $i"
  359. res=$( (echo "$value"; sleep .5) | run test nc -w 2 -N "$checkaddr" 3333)
  360. # We cannot use kill %% to kill the nc -l process as it only
  361. # kills jexec, and NOT the jail process
  362. # The following is heavy, but should be the only one running
  363. run check killall nc >/dev/null 2>/dev/null
  364. rm /tmp/tififo.$$
  365. if [ x"$res" != x"$value" ]; then
  366. echo TCP FAILED on "$i"!!!!
  367. return 1
  368. fi
  369. mkfifo /tmp/tififo.$$
  370. run check sh -c 'cat /tmp/tififo.'$$' | nc '"$ncopt"' -u -l 3333 | cat > /tmp/tififo.'$$ 2>/dev/null &
  371. sleep .5
  372. value="foobar $$ UDP $i"
  373. res=$( (echo "$value"; sleep .5) | run test nc -u -w 2 -N "$checkaddr" 3333)
  374. # We cannot use kill %% to kill the nc -l process as it only
  375. # kills jexec, and NOT the jail process
  376. # The following is heavy, but should be the only one running
  377. run check killall nc >/dev/null 2>/dev/null
  378. rm /tmp/tififo.$$
  379. if [ x"$res" != x"$value" ]; then
  380. echo UDP FAILED on "$i"!!!!
  381. return 1
  382. fi
  383. # make sure that checksum validation works by sending invalid packets
  384. # this includes using dtrace to verify the results from the driver
  385. }
  386. # Iterate through the various hardware checksum off loading flags.
  387. csumtest()
  388. {
  389. local i
  390. #for i in "-rxcsum6" "rxcsum6" "-txcsum6" "txcsum6" "-rxcsum" "rxcsum" "-txcsum" "txcsum"; do
  391. for i in "-rxcsum" "rxcsum" "-txcsum" "txcsum"; do
  392. if ! csuminternal "$i"; then
  393. return 1
  394. fi
  395. done
  396. }
  397. # Verify that packets can be sent and received w/ a larger MTU.
  398. # This test is a bit tricky in that it sets both interfaces to the
  399. # larger MTU. Better option would be to set both sides to the larger
  400. # MTU, but add a route w/ the -mtu specified for the return (or transmit)
  401. # side so that we aren't testing both sides at the same time.
  402. mtutest()
  403. {
  404. local i err
  405. oldtestmtu=$(ifjail test | awk '{ print $6; exit 0 }')
  406. oldcheckmtu=$(ifjail check | awk '{ print $6; exit 0 }')
  407. err=0
  408. for i in 1500 1504 2025 4074 7000 8000 9000 9216; do
  409. echo "testing mtu: $i"
  410. if ! ifjail test mtu "$i" 2>/dev/null; then
  411. echo "Failed to set mtu $i on test interface, skipping..."
  412. continue
  413. fi
  414. if ! ifjail check mtu "$i" 2>/dev/null; then
  415. echo "Failed to set mtu $i on check interface."
  416. echo "Please use different check interface."
  417. return 1
  418. fi
  419. cleaniface test $testiface
  420. cleaniface check $checkiface
  421. #run test arp -an
  422. #run check arp -an
  423. #run test ifconfig $testiface
  424. #run check ifconfig $checkiface
  425. sleep .5
  426. ifjail test up
  427. ifjail check up
  428. waitcarrier
  429. run test ifconfig $testiface 172.29.5.5/24
  430. run check ifconfig $checkiface 172.29.5.4/24
  431. # Subtract off ethernet header, ICMP header and ethernet CRC
  432. if ! run test ping -D -s $(($i - 8 - 20)) -o -c 4 -t 5 -i .5 172.29.5.4 >/dev/null; then
  433. echo failed for MTU size $i
  434. err=1
  435. break
  436. fi
  437. done
  438. # restore MTU
  439. ifjail test mtu $oldtestmtu
  440. ifjail check mtu $oldcheckmtu
  441. if [ x"$err" != x"0" ]; then
  442. return 1
  443. fi
  444. }
  445. runtest()
  446. {
  447. test="$1"
  448. starttest
  449. eval "$test"test
  450. res="$?"
  451. endtest "$res"
  452. if [ "$res" -ne 0 ]; then
  453. echo "$test"test failed.
  454. exit 1
  455. fi
  456. }
  457. usage()
  458. {
  459. echo "Usage: $0 {init,run,deinit,$(echo "$alltests" | sed -e 's/ /,/g')} <ifaceundertest> <checkinterface>"
  460. echo ""
  461. echo "The ifaceundertest is the name of the interface which featres are being"
  462. echo "tested."
  463. echo ""
  464. echo "The checinterface is the name of the interface used to validate that the"
  465. echo "test interface operatates correctly. Features of this interface will"
  466. echo "be turned off to prevent any of it's hardware offloading capabilities"
  467. echo "interfering with the test."
  468. }
  469. cmd="$1"
  470. testiface="$2"
  471. checkiface="$3"
  472. if [ -z "$cmd" ]; then
  473. echo "command must be specified!"
  474. echo ""
  475. usage
  476. exit 1
  477. fi
  478. if [ -z "$testiface" -o -z "$checkiface" ]; then
  479. echo "both interfaces must be specified!"
  480. echo ""
  481. usage
  482. exit 1
  483. fi
  484. alltests="mtu csum hwvlan"
  485. case "$cmd" in
  486. init)
  487. setup
  488. ;;
  489. mtu|csum|hwvlan)
  490. if ! checkjails; then
  491. exit 1
  492. fi
  493. echo starting $cmd, test $testiface, check $checkiface
  494. runtest "$cmd"
  495. echo "done"
  496. ;;
  497. run)
  498. if ! checkjails; then
  499. exit 1
  500. fi
  501. echo starting, test $testiface, check $checkiface
  502. for i in $alltests; do
  503. runtest "$i"
  504. done
  505. echo "done"
  506. ;;
  507. deinit)
  508. cleanup
  509. ;;
  510. *)
  511. echo "invalid cmd: $cmd"
  512. echo
  513. usage
  514. exit 1
  515. ;;
  516. esac