@@ -37383,6 +37383,113 @@ OVN_CLEANUP([hv1])
3738337383AT_CLEANUP
3738437384])
3738537385
37386+ OVN_FOR_EACH_NORTHD([
37387+ AT_SETUP([MAC binding aging - probing unicast to broadcast transition])
37388+ CHECK_SCAPY
37389+ ovn_start
37390+
37391+ aging_th=5
37392+ net_add n1
37393+ sim_add hv1
37394+ as hv1
37395+ check ovs-vsctl add-br br-phys
37396+ ovn_attach n1 br-phys 192.168.0.1
37397+ ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg
37398+
37399+ check ovn-nbctl \
37400+ -- ls-add ls1 \
37401+ -- lr-add lr \
37402+ -- set logical_router lr options:mac_binding_age_threshold=$aging_th \
37403+ -- lrp-add lr lr-ls1 00:00:00:00:10:00 10.10.10.1/24 42.42.42.1/24 \
37404+ fd11::1/64 fd12::1/64 \
37405+ -- lsp-add-router-port ls1 ls1-lr lr-ls1 \
37406+ -- lsp-add ls1 vif1 \
37407+ -- lsp-set-addresses vif1 "unknown"
37408+
37409+ check ovs-vsctl \
37410+ -- add-port br-int vif1 \
37411+ -- set interface vif1 external-ids:iface-id=vif1 \
37412+ options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap
37413+
37414+ OVN_POPULATE_ARP
37415+ wait_for_ports_up
37416+ check ovn-nbctl --wait=hv sync
37417+
37418+ # Wait for pinctrl thread to be connected.
37419+ OVS_WAIT_UNTIL([grep pinctrl hv1/ovn-controller.log | grep -q connected])
37420+
37421+ # Create one IPv4 and one IPv6 MAC binding.
37422+ send_garp hv1 vif1 2 00:00:00:00:10:1a ff:ff:ff:ff:ff:ff 10.10.10.100 10.10.10.100
37423+ wait_row_count mac_binding 1 ip="10.10.10.100" logical_port="lr-ls1"
37424+
37425+ send_na hv1 vif1 00:00:00:00:10:1a 00:00:00:00:10:00 fd11::64 fd11::1
37426+ wait_row_count mac_binding 1 ip=\"fd11::64\" logical_port=\"lr-ls1\"
37427+
37428+ # The first 2 probes are unicast (arp_attempts 0-1); the entry then falls
37429+ # back to broadcast ARP / multicast NS.
37430+ dump_arp 1 00:00:00:00:10:00 ff:ff:ff:ff:ff:ff 10.10.10.1 10.10.10.100 00:00:00:00:00:00 > expected_bcast
37431+ OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected_bcast])
37432+
37433+ dump_ns 33:33:ff:00:00:64 00:00:00:00:10:00 ff02::1:ff00:64 fd11::1 fd11::64 > expected_mcast
37434+ OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected_mcast])
37435+
37436+ dump_arp 1 00:00:00:00:10:00 00:00:00:00:10:1a 10.10.10.1 10.10.10.100 00:00:00:00:10:1a > expected_ucast_v4
37437+ OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected_ucast_v4])
37438+
37439+ dump_ns 00:00:00:00:10:1a 00:00:00:00:10:00 fd11::64 fd11::1 fd11::64 > expected_ucast_v6
37440+ OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected_ucast_v6])
37441+
37442+ # Verify the reset-to-zero mechanism using a distinct pair of neighbours
37443+ # kept alive (never re-created) for the whole check. A single entry emits
37444+ # at most ARP_BROADCAST_THRESHOLD (2) unicast probes before falling back to
37445+ # broadcast, so a 4th unicast probe proves arp_attempts was reset.
37446+ send_garp hv1 vif1 2 00:00:00:00:10:1b ff:ff:ff:ff:ff:ff 10.10.10.101 10.10.10.101
37447+ wait_row_count mac_binding 1 ip="10.10.10.101" logical_port="lr-ls1"
37448+ v4_uuid=$(fetch_column Mac_Binding _uuid ip=10.10.10.101)
37449+
37450+ send_na hv1 vif1 00:00:00:00:10:1b 00:00:00:00:10:00 fd11::65 fd11::1
37451+ wait_row_count mac_binding 1 ip=\"fd11::65\" logical_port=\"lr-ls1\"
37452+ v6_uuid=$(fetch_column Mac_Binding _uuid ip=\"fd11::65\")
37453+
37454+ # Keep the entries active (Tx towards them) and let a couple of unicast
37455+ # probes go out (arp_attempts reaches 1, below the broadcast threshold).
37456+ send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 10.10.10.101 42.42.42.100
37457+ send_udp6 hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a fd11::65 fd12::100
37458+
37459+ dump_arp 1 00:00:00:00:10:00 00:00:00:00:10:1b 10.10.10.1 10.10.10.101 00:00:00:00:10:1b > ucast_v4_101.pkt
37460+ dump_ns 00:00:00:00:10:1b 00:00:00:00:10:00 fd11::65 fd11::1 fd11::65 > ucast_v6_65.pkt
37461+ OVS_WAIT_UNTIL([test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
37462+ grep -Fc "$(cat ucast_v4_101.pkt)") -ge 2])
37463+ OVS_WAIT_UNTIL([test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
37464+ grep -Fc "$(cat ucast_v6_65.pkt)") -ge 2])
37465+
37466+ # The neighbours answer, refreshing the rows in place (resetting
37467+ # arp_attempts). Confirm the timestamps advanced.
37468+ v4_ts=$(fetch_column Mac_Binding timestamp ip=10.10.10.101)
37469+ v6_ts=$(fetch_column Mac_Binding timestamp ip=\"fd11::65\")
37470+ send_garp hv1 vif1 2 00:00:00:00:10:1b 00:00:00:00:10:00 10.10.10.101 10.10.10.1
37471+ send_na hv1 vif1 00:00:00:00:10:1b 00:00:00:00:10:00 fd11::65 fd11::1
37472+ OVS_WAIT_UNTIL([test $(fetch_column Mac_Binding timestamp ip=10.10.10.101) -gt $v4_ts])
37473+ OVS_WAIT_UNTIL([test $(fetch_column Mac_Binding timestamp ip=\"fd11::65\") -gt $v6_ts])
37474+
37475+ # Probing must restart from unicast: wait for a 4th unicast ARP/NS probe
37476+ # while the rows are still the original ones (same UUID, never re-created).
37477+ dump_arp 1 00:00:00:00:10:00 00:00:00:00:10:1b 10.10.10.1 10.10.10.101 00:00:00:00:10:1b > ucast_v4.pkt
37478+ dump_ns 00:00:00:00:10:1b 00:00:00:00:10:00 fd11::65 fd11::1 fd11::65 > ucast_v6.pkt
37479+ OVS_WAIT_UNTIL([
37480+ send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 10.10.10.101 42.42.42.100
37481+ send_udp6 hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a fd11::65 fd12::100
37482+ test "$(fetch_column Mac_Binding _uuid ip=10.10.10.101)" = "$v4_uuid" && \
37483+ test "$(fetch_column Mac_Binding _uuid ip=\"fd11::65\")" = "$v6_uuid" && \
37484+ test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
37485+ grep -Fc "$(cat ucast_v4.pkt)") -ge 4 && \
37486+ test $($PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
37487+ grep -Fc "$(cat ucast_v6.pkt)") -ge 4])
37488+
37489+ OVN_CLEANUP([hv1])
37490+ AT_CLEANUP
37491+ ])
37492+
3738637493OVN_FOR_EACH_NORTHD([
3738737494AT_SETUP([MAC binding aging - probing distributed GW router])
3738837495CHECK_SCAPY
0 commit comments