On Fri, Sep 26, 2025 at 2:45 PM Xavier Simonart via dev <
[email protected]> wrote:
> Since [0], ND/NA received on non-resident chassis are dropped/ignored
> on DGP to avoid having all chassis of an HA group trying to update
> mac_binding, which was resulting in transaction errors.
> However, it can happen that the whole pipeline, including the distributed
> gateway router, is run on a chassis where the DGP is not resident.
> This happens for instance in the following setup, with vif1 and ext on hv1
> and lr-pub on hv2.
> vif1 --- ls1 --- lr --- pub -+- ln
> +- ext
>
> In this setup, unicast NA from ext towards vif1 should not be ignored,
> even on the non-resident chassis.
>
> This patch amends [0] to only ignore multicast ND/NA on non-resident
> chassis.
> [0]: 987adc9e589e ("controller: Avoid IPv6 Mac_Binding related transaction
> errors.")
>
> Fixes: 987adc9e589e ("controller: Avoid IPv6 Mac_Binding related
> transaction errors.")
>
> Signed-off-by: Xavier Simonart <[email protected]>
> Reported-at: https://issues.redhat.com/browse/FDP-1567
> Reported-at: https://issues.redhat.com/browse/FDP-1728
> ---
> northd/northd.c | 1 +
> tests/multinode-macros.at | 5 +-
> tests/multinode.at | 130 ++++++++++++++++++++++++++++++++++++++
> tests/ovn-northd.at | 6 +-
> 4 files changed, 138 insertions(+), 4 deletions(-)
>
> diff --git a/northd/northd.c b/northd/northd.c
> index fe5199a86..e0767f16c 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -13763,6 +13763,7 @@ build_neigh_learning_flows_for_lrouter_port(
> if (lrp_is_l3dgw(op)) {
> ds_clear(match);
> ds_put_format(match, "inport == %s && (nd_na || nd_ns) && "
> + "eth.mcast && "
> "!is_chassis_resident(%s)", op->json_key,
> op->cr_port->json_key);
> ovn_lflow_add_with_hint(lflows, op->od,
> diff --git a/tests/multinode-macros.at b/tests/multinode-macros.at
> index 1d2c5e4fe..85dc7a606 100644
> --- a/tests/multinode-macros.at
> +++ b/tests/multinode-macros.at
> @@ -313,7 +313,7 @@ m_check_column() {
> # interface in the NETNS namespace. It also configures it with the
> provided
> # IP and (optionally) default gateway GW.
> m_add_internal_port() {
> - local node=$1 ns=$2 br=$3 port=$4 ip=$5 gw=$6
> + local node=$1 ns=$2 br=$3 port=$4 ip=$5 gw=$6 ip6=$7
>
> if ! m_as $node ip netns list | grep $ns; then
> check m_as $node ip netns add $ns
> @@ -328,6 +328,9 @@ m_add_internal_port() {
> if test -n "$gw"; then
> check m_as $node ip netns exec $ns ip route add default via $gw
> dev $port
> fi
> + if test -n "$ip6"; then
> + check m_as $node ip netns exec $ns ip addr add $ip6 dev $port
> + fi
> }
>
> # m_wait_for_ports_up [PORT...]
> diff --git a/tests/multinode.at b/tests/multinode.at
> index 959c5ba8a..3e3fa3f32 100644
> --- a/tests/multinode.at
> +++ b/tests/multinode.at
> @@ -3834,3 +3834,133 @@ M_NS_CHECK_EXEC([ovn-chassis-1], [vm1], [ping -q
> -c 3 -i 0.3 -w 2 20.0.0.2 | FOR
> ])
>
> AT_CLEANUP
> +
> +AT_SETUP([IPv6 NA received on non resident chassis])
> +m_as ovn-chassis-1 ovs-vsctl set open .
> external-ids:ovn-bridge-mappings=public:br-ex
> +m_as ovn-chassis-2 ovs-vsctl set open .
> external-ids:ovn-bridge-mappings=public:br-ex
> +
> +check_fake_multinode_setup
> +
> +# Delete the multinode NB and OVS resources before starting the test.
> +cleanup_multinode_resources
> +
> +m_as ovn-chassis-1 ip link del ls1p1-p
> +m_as ovn-chassis-1 ip link del ls1p2-p
> +m_as ovn-chassis-1 ip link del ls2p1-p
> +
> +OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q genev_sys])
> +OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q genev_sys])
> +
> +check multinode_nbctl ls-add ls1
> +check multinode_nbctl lsp-add ls1 ls1p1
> +check multinode_nbctl lsp-set-addresses ls1p1 "00:00:00:01:01:02
> 192.168.1.1 2001::1"
> +check multinode_nbctl lsp-add ls1 ls1p2
> +check multinode_nbctl lsp-set-addresses ls1p2 "00:00:00:01:02:02
> 192.168.1.2 2001::2"
> +check multinode_nbctl lr-add lr1
> +check multinode_nbctl lrp-add lr1 lr1-ls1 00:00:00:00:00:01
> 192.168.1.254/24 2001::a/64
> +check multinode_nbctl lsp-add ls1 ls1-lr1
> +check multinode_nbctl lsp-set-addresses ls1-lr1 "00:00:00:00:00:01
> 192.168.1.254 2001::a"
> +check multinode_nbctl lsp-set-type ls1-lr1 router
> +check multinode_nbctl lsp-set-options ls1-lr1 router-port=lr1-ls1
> +
> +check multinode_nbctl lrp-add lr1 lr1-ls2 00:00:00:00:00:02
> 192.168.2.254/24 2002::a/64
> +check multinode_nbctl ls-add ls2
> +check multinode_nbctl lsp-add ls2 ls2-lr1
> +check multinode_nbctl lsp-set-addresses ls2-lr1 "00:00:00:00:00:02
> 192.168.2.254 2002::a"
> +check multinode_nbctl lsp-set-type ls2-lr1 router
> +check multinode_nbctl lsp-set-options ls2-lr1 router-port=lr1-ls2
> +check multinode_nbctl lsp-add ls2 ls2p1
> +check multinode_nbctl lsp-set-addresses ls2p1 "00:00:00:02:01:02
> 192.168.2.1 2002::1"
> +
> +check multinode_nbctl lsp-add ls1 ls1p3
> +check multinode_nbctl lsp-set-addresses ls1p3 "00:00:00:01:03:02
> 192.168.1.3 2001::3"
> +check multinode_nbctl lrp-add lr1 lr1-pub 0a:0a:56:33:02:ff
> 172.18.86.254/24 6812:86::254/64
> +check multinode_nbctl ls-add pub \
> + -- lsp-add pub pub-lr1 \
> + -- lsp-set-type pub-lr1 router \
> + -- lsp-set-addresses pub-lr1 router \
> + -- lsp-set-options pub-lr1 router-port=lr1-pub \
> + -- lsp-add pub pub-ln \
> + -- lsp-set-type pub-ln localnet \
> + -- lsp-set-addresses pub-ln unknown \
> + -- lsp-set-options pub-ln network_name=public
> +
> +check multinode_nbctl lrp-set-gateway-chassis lr1-pub ovn-chassis-2
> +
> +check multinode_nbctl lr-nat-add lr1 dnat_and_snat 172.18.86.11
> 192.168.1.1 ls1p1 0a:0a:56:33:02:11
> +check multinode_nbctl lr-nat-add lr1 dnat_and_snat 6812:86::11 2001::1
> ls1p1 0a:0a:56:33:02:11
> +
> +m_as ovn-chassis-1 /data/create_fake_vm.sh ls1p1 ls1p1 00:00:00:01:01:02
> 1500 192.168.1.1 24 192.168.1.254 2001::1/64 2001::a
> +m_as ovn-chassis-1 /data/create_fake_vm.sh ls1p2 ls1p2 00:00:00:01:02:02
> 1500 192.168.1.2 24 192.168.1.254 2001::2/64 2001::a
> +m_as ovn-chassis-1 /data/create_fake_vm.sh ls2p1 ls2p1 00:00:00:02:01:02
> 1500 192.168.2.1 24 192.168.2.254 2002::2/64 2002::a
> +m_add_internal_port ovn-chassis-1 ovn-ext1 br-ex ext1 172.18.86.101/24
> "" 6812:86::101/64
> +m_add_internal_port ovn-chassis-2 ovn-ext2 br-ex ext2 172.18.86.102/24
> "" 6812:86::102/64
> +
> +m_central_as ovn-sbctl --all destroy mac_binding
> +
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ls1p1], [ping -q -c 3 -i 0.3 -w 2
> 172.18.86.101 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="172.18.86.101" logical_port="lr1-pub"
> +
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ls1p1], [ping -q -c 3 -i 0.3 -w 2
> 172.18.86.102 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="172.18.86.102" logical_port="lr1-pub"
> +
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ls1p1], [ping6 -q -c 3 -i 0.3 -w 2
> 6812:86::101 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="6812\:86\:\:101" logical_port="lr1-pub"
> +
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ls1p1], [ping6 -q -c 3 -i 0.3 -w 2
> 6812:86::102 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="6812\:86\:\:102" logical_port="lr1-pub"
> +
> +# We remove all mac_bindings to check that neighbor learning works also
> in the other direction.
> +m_central_as ovn-sbctl --all destroy mac_binding
> +
> +# Also remove addresses learned on ovn-ext
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ovn-ext1], [ip -6 neigh del 6812:86::11
> dev ext1 lladdr 0a:0a:56:33:02:11])
> +M_NS_CHECK_EXEC([ovn-chassis-2], [ovn-ext2], [ip -6 neigh del 6812:86::11
> dev ext2 lladdr 0a:0a:56:33:02:11])
> +
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ovn-ext1], [ping -q -c 3 -i 0.3 -w 2
> 172.18.86.11 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="172.18.86.101" logical_port="lr1-pub"
> +
> +M_NS_CHECK_EXEC([ovn-chassis-2], [ovn-ext2], [ping -q -c 3 -i 0.3 -w 2
> 172.18.86.11 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="172.18.86.101" logical_port="lr1-pub"
> +
> +M_NS_CHECK_EXEC([ovn-chassis-1], [ovn-ext1], [ping6 -q -c 3 -i 0.3 -w 2
> 6812:86::11 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="6812\:86\:\:101" logical_port="lr1-pub"
> +
> +M_NS_CHECK_EXEC([ovn-chassis-2], [ovn-ext2], [ping6 -q -c 3 -i 0.3 -w 2
> 6812:86::11 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="6812\:86\:\:102" logical_port="lr1-pub"
> +
> +# Ping from ext2 while no mac_binding yet.
> +m_central_as ovn-sbctl --all destroy mac_binding
> +M_NS_CHECK_EXEC([ovn-chassis-2], [ovn-ext2], [ip -6 neigh del 6812:86::11
> dev ext2 lladdr 0a:0a:56:33:02:11])
> +
> +M_NS_CHECK_EXEC([ovn-chassis-2], [ovn-ext2], [ping6 -q -c 3 -i 0.3 -w 2
> 6812:86::11 | FORMAT_PING], \
> +[0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +m_wait_row_count mac_binding 1 ip="6812\:86\:\:102" logical_port="lr1-pub"
> +
> +AT_CLEANUP
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 83a142d20..796c4d770 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -7311,9 +7311,9 @@ AT_CHECK([grep lr_in_lookup_neighbor lrflows | grep
> cr-DR | ovn_strip_lflows], [
> table=??(lr_in_lookup_neighbor), priority=100 , match=(inport ==
> "DR-S1" && arp.spa == 172.16.1.0/24 && arp.op == 1 &&
> is_chassis_resident("cr-DR-S1")), action=(reg9[[2]] = lookup_arp(inport,
> arp.spa, arp.sha); next;)
> table=??(lr_in_lookup_neighbor), priority=100 , match=(inport ==
> "DR-S2" && arp.spa == 172.16.2.0/24 && arp.op == 1 &&
> is_chassis_resident("cr-DR-S2")), action=(reg9[[2]] = lookup_arp(inport,
> arp.spa, arp.sha); next;)
> table=??(lr_in_lookup_neighbor), priority=100 , match=(inport ==
> "DR-S3" && arp.spa == 172.16.3.0/24 && arp.op == 1 &&
> is_chassis_resident("cr-DR-S3")), action=(reg9[[2]] = lookup_arp(inport,
> arp.spa, arp.sha); next;)
> - table=??(lr_in_lookup_neighbor), priority=120 , match=(inport ==
> "DR-S1" && (nd_na || nd_ns) && !is_chassis_resident("cr-DR-S1")),
> action=(reg9[[2]] = 1; next;)
> - table=??(lr_in_lookup_neighbor), priority=120 , match=(inport ==
> "DR-S2" && (nd_na || nd_ns) && !is_chassis_resident("cr-DR-S2")),
> action=(reg9[[2]] = 1; next;)
> - table=??(lr_in_lookup_neighbor), priority=120 , match=(inport ==
> "DR-S3" && (nd_na || nd_ns) && !is_chassis_resident("cr-DR-S3")),
> action=(reg9[[2]] = 1; next;)
> + table=??(lr_in_lookup_neighbor), priority=120 , match=(inport ==
> "DR-S1" && (nd_na || nd_ns) && eth.mcast &&
> !is_chassis_resident("cr-DR-S1")), action=(reg9[[2]] = 1; next;)
> + table=??(lr_in_lookup_neighbor), priority=120 , match=(inport ==
> "DR-S2" && (nd_na || nd_ns) && eth.mcast &&
> !is_chassis_resident("cr-DR-S2")), action=(reg9[[2]] = 1; next;)
> + table=??(lr_in_lookup_neighbor), priority=120 , match=(inport ==
> "DR-S3" && (nd_na || nd_ns) && eth.mcast &&
> !is_chassis_resident("cr-DR-S3")), action=(reg9[[2]] = 1; next;)
> ])
> # Check the flows in lr_in_gw_redirect stage
> AT_CHECK([grep lr_in_gw_redirect lrflows | grep cr-DR |
> ovn_strip_lflows], [0], [dnl
> --
> 2.47.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Thank you Xavier,
I went ahead and merged this into main and backported all the way down to
24.03.
Regards,
Ales
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev