| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # shellcheck disable=SC2329 |
| |
| source ../lib.sh |
| |
| ALL_TESTS=" |
| test_clean_hsrv0 |
| test_cut_link_hsrv0 |
| test_packet_loss_hsrv0 |
| test_high_packet_loss_hsrv0 |
| test_reordering_hsrv0 |
| |
| test_clean_hsrv1 |
| test_cut_link_hsrv1 |
| test_packet_loss_hsrv1 |
| test_high_packet_loss_hsrv1 |
| test_reordering_hsrv1 |
| |
| test_clean_prp |
| test_cut_link_prp |
| test_packet_loss_prp |
| test_high_packet_loss_prp |
| test_reordering_prp |
| " |
| |
| # The tests are running ping for 5sec with a relatively short interval in |
| # different scenarios with faulty links (cut links, packet loss, delay, |
| # reordering) that should be recoverable by HSR/PRP. The ping interval (10ms) |
| # is short enough that the base delay (50ms) leads to a queue in the netem |
| # qdiscs which is needed for reordering. |
| |
| setup_hsr_topo() |
| { |
| # Three HSR nodes in a ring, every node has a LAN A interface connected |
| # to the LAN B interface of the next node. |
| # |
| # node1 node2 |
| # |
| # vethA -------- vethB |
| # hsr1 hsr2 |
| # vethB vethA |
| # \ / |
| # vethA vethB |
| # hsr3 |
| # |
| # node3 |
| |
| local ver="$1" |
| |
| setup_ns node1 node2 node3 |
| |
| # veth links |
| # shellcheck disable=SC2154 # variables assigned by setup_ns |
| ip link add vethA netns "$node1" type veth peer name vethB netns "$node2" |
| # shellcheck disable=SC2154 # variables assigned by setup_ns |
| ip link add vethA netns "$node2" type veth peer name vethB netns "$node3" |
| ip link add vethA netns "$node3" type veth peer name vethB netns "$node1" |
| |
| # MAC addresses (not needed for HSR operation, but helps with debugging) |
| ip -net "$node1" link set address 00:11:22:00:01:01 dev vethA |
| ip -net "$node1" link set address 00:11:22:00:01:02 dev vethB |
| |
| ip -net "$node2" link set address 00:11:22:00:02:01 dev vethA |
| ip -net "$node2" link set address 00:11:22:00:02:02 dev vethB |
| |
| ip -net "$node3" link set address 00:11:22:00:03:01 dev vethA |
| ip -net "$node3" link set address 00:11:22:00:03:02 dev vethB |
| |
| # HSR interfaces |
| ip -net "$node1" link add name hsr1 type hsr proto 0 version "$ver" \ |
| slave1 vethA slave2 vethB supervision 45 |
| ip -net "$node2" link add name hsr2 type hsr proto 0 version "$ver" \ |
| slave1 vethA slave2 vethB supervision 45 |
| ip -net "$node3" link add name hsr3 type hsr proto 0 version "$ver" \ |
| slave1 vethA slave2 vethB supervision 45 |
| |
| # IP addresses |
| ip -net "$node1" addr add 100.64.0.1/24 dev hsr1 |
| ip -net "$node2" addr add 100.64.0.2/24 dev hsr2 |
| ip -net "$node3" addr add 100.64.0.3/24 dev hsr3 |
| |
| # Set all links up |
| ip -net "$node1" link set vethA up |
| ip -net "$node1" link set vethB up |
| ip -net "$node1" link set hsr1 up |
| |
| ip -net "$node2" link set vethA up |
| ip -net "$node2" link set vethB up |
| ip -net "$node2" link set hsr2 up |
| |
| ip -net "$node3" link set vethA up |
| ip -net "$node3" link set vethB up |
| ip -net "$node3" link set hsr3 up |
| } |
| |
| setup_prp_topo() |
| { |
| # Two PRP nodes, connected by two links (treated as LAN A and LAN B). |
| # |
| # vethA ----- vethA |
| # prp1 prp2 |
| # vethB ----- vethB |
| # |
| # node1 node2 |
| |
| setup_ns node1 node2 |
| |
| # veth links |
| ip link add vethA netns "$node1" type veth peer name vethA netns "$node2" |
| ip link add vethB netns "$node1" type veth peer name vethB netns "$node2" |
| |
| # MAC addresses will be copied from LAN A interface |
| ip -net "$node1" link set address 00:11:22:00:00:01 dev vethA |
| ip -net "$node2" link set address 00:11:22:00:00:02 dev vethA |
| |
| # PRP interfaces |
| ip -net "$node1" link add name prp1 type hsr \ |
| slave1 vethA slave2 vethB supervision 45 proto 1 |
| ip -net "$node2" link add name prp2 type hsr \ |
| slave1 vethA slave2 vethB supervision 45 proto 1 |
| |
| # IP addresses |
| ip -net "$node1" addr add 100.64.0.1/24 dev prp1 |
| ip -net "$node2" addr add 100.64.0.2/24 dev prp2 |
| |
| # All links up |
| ip -net "$node1" link set vethA up |
| ip -net "$node1" link set vethB up |
| ip -net "$node1" link set prp1 up |
| |
| ip -net "$node2" link set vethA up |
| ip -net "$node2" link set vethB up |
| ip -net "$node2" link set prp2 up |
| } |
| |
| wait_for_hsr_node_table() |
| { |
| log_info "Wait for node table entries to be merged." |
| WAIT=5 |
| while [ "${WAIT}" -gt 0 ]; do |
| nts=$(cat /sys/kernel/debug/hsr/hsr*/node_table) |
| |
| # We need entries in the node tables, and they need to be merged |
| if (echo "$nts" | grep -qE "^([0-9a-f]{2}:){5}") && \ |
| ! (echo "$nts" | grep -q "00:00:00:00:00:00"); then |
| return |
| fi |
| |
| sleep 1 |
| ((WAIT--)) |
| done |
| check_err 1 "Failed to wait for merged node table entries" |
| } |
| |
| setup_topo() |
| { |
| local proto="$1" |
| |
| if [ "$proto" = "HSRv0" ]; then |
| setup_hsr_topo 0 |
| wait_for_hsr_node_table |
| elif [ "$proto" = "HSRv1" ]; then |
| setup_hsr_topo 1 |
| wait_for_hsr_node_table |
| elif [ "$proto" = "PRP" ]; then |
| setup_prp_topo |
| else |
| check_err 1 "Unknown protocol (${proto})" |
| fi |
| } |
| |
| check_ping() |
| { |
| local node="$1" |
| local dst="$2" |
| local accepted_dups="$3" |
| local ping_args="-q -i 0.01 -c 400" |
| |
| log_info "Running ping $node -> $dst" |
| # shellcheck disable=SC2086 |
| output=$(ip netns exec "$node" ping $ping_args "$dst" | \ |
| grep "packets transmitted") |
| log_info "$output" |
| |
| dups=0 |
| loss=0 |
| |
| if [[ "$output" =~ \+([0-9]+)" duplicates" ]]; then |
| dups="${BASH_REMATCH[1]}" |
| fi |
| if [[ "$output" =~ ([0-9\.]+\%)" packet loss" ]]; then |
| loss="${BASH_REMATCH[1]}" |
| fi |
| |
| if [ "$dups" -gt "$accepted_dups" ]; then |
| check_err 1 "Unexpected duplicate packets (${dups})" |
| fi |
| if [ "$loss" != "0%" ]; then |
| check_err 1 "Unexpected packet loss (${loss})" |
| fi |
| } |
| |
| test_clean() |
| { |
| local proto="$1" |
| |
| RET=0 |
| tname="${FUNCNAME[0]} - ${proto}" |
| |
| setup_topo "$proto" |
| if ((RET != ksft_pass)); then |
| log_test "${tname} setup" |
| return |
| fi |
| |
| check_ping "$node1" "100.64.0.2" 0 |
| |
| log_test "${tname}" |
| } |
| |
| test_clean_hsrv0() |
| { |
| test_clean "HSRv0" |
| } |
| |
| test_clean_hsrv1() |
| { |
| test_clean "HSRv1" |
| } |
| |
| test_clean_prp() |
| { |
| test_clean "PRP" |
| } |
| |
| test_cut_link() |
| { |
| local proto="$1" |
| |
| RET=0 |
| tname="${FUNCNAME[0]} - ${proto}" |
| |
| setup_topo "$proto" |
| if ((RET != ksft_pass)); then |
| log_test "${tname} setup" |
| return |
| fi |
| |
| # Cutting link from subshell, so check_ping can run in the normal shell |
| # with access to global variables from the test harness. |
| ( |
| sleep 2 |
| log_info "Cutting link" |
| ip -net "$node1" link set vethB down |
| ) & |
| check_ping "$node1" "100.64.0.2" 0 |
| |
| wait |
| log_test "${tname}" |
| } |
| |
| |
| test_cut_link_hsrv0() |
| { |
| test_cut_link "HSRv0" |
| } |
| |
| test_cut_link_hsrv1() |
| { |
| test_cut_link "HSRv1" |
| } |
| |
| test_cut_link_prp() |
| { |
| test_cut_link "PRP" |
| } |
| |
| test_packet_loss() |
| { |
| local proto="$1" |
| local loss="$2" |
| |
| RET=0 |
| tname="${FUNCNAME[0]} - ${proto}, ${loss}" |
| |
| setup_topo "$proto" |
| if ((RET != ksft_pass)); then |
| log_test "${tname} setup" |
| return |
| fi |
| |
| # Packet loss with lower delay makes sure the packets on the lossy link |
| # arrive first. |
| tc -net "$node1" qdisc add dev vethA root netem delay 50ms |
| tc -net "$node1" qdisc add dev vethB root netem delay 20ms loss "$loss" |
| |
| check_ping "$node1" "100.64.0.2" 40 |
| |
| log_test "${tname}" |
| } |
| |
| test_packet_loss_hsrv0() |
| { |
| test_packet_loss "HSRv0" "20%" |
| } |
| |
| test_packet_loss_hsrv1() |
| { |
| test_packet_loss "HSRv1" "20%" |
| } |
| |
| test_packet_loss_prp() |
| { |
| test_packet_loss "PRP" "20%" |
| } |
| |
| test_high_packet_loss_hsrv0() |
| { |
| test_packet_loss "HSRv0" "80%" |
| } |
| |
| test_high_packet_loss_hsrv1() |
| { |
| test_packet_loss "HSRv1" "80%" |
| } |
| |
| test_high_packet_loss_prp() |
| { |
| test_packet_loss "PRP" "80%" |
| } |
| |
| test_reordering() |
| { |
| local proto="$1" |
| |
| RET=0 |
| tname="${FUNCNAME[0]} - ${proto}" |
| |
| setup_topo "$proto" |
| if ((RET != ksft_pass)); then |
| log_test "${tname} setup" |
| return |
| fi |
| |
| tc -net "$node1" qdisc add dev vethA root netem delay 50ms |
| tc -net "$node1" qdisc add dev vethB root netem delay 50ms reorder 20% |
| |
| check_ping "$node1" "100.64.0.2" 40 |
| |
| log_test "${tname}" |
| } |
| |
| test_reordering_hsrv0() |
| { |
| test_reordering "HSRv0" |
| } |
| |
| test_reordering_hsrv1() |
| { |
| test_reordering "HSRv1" |
| } |
| |
| test_reordering_prp() |
| { |
| test_reordering "PRP" |
| } |
| |
| cleanup() |
| { |
| cleanup_all_ns |
| } |
| |
| trap cleanup EXIT |
| |
| tests_run |
| |
| exit $EXIT_STATUS |