Skip to content

Commit 20b9f0b

Browse files
almusildceara
authored andcommitted
northd: Make sure we learn from ND NS as we do with ARP requests.
When always_learn_from_arp_request=false we still allow to learn from requests targeted to the LRP own address. This was working for IPv4, but not for IPv6. Add missing flows to make sure it works for both IP families. Fixes: 783337b ("Learn the mac binding only if required") Reported-at: https://redhat.atlassian.net/browse/FDP-4201 Assisted-by: Claude Opus 4.6, OpenCode Signed-off-by: Ales Musil <amusil@redhat.com> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
1 parent 93fd611 commit 20b9f0b

3 files changed

Lines changed: 189 additions & 13 deletions

File tree

northd/northd.c

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15017,15 +15017,6 @@ build_neigh_learning_flows_for_lrouter(
1501715017
ds_cstr(actions), lflow_ref);
1501815018
}
1501915019

15020-
ds_clear(actions);
15021-
ds_put_format(actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
15022-
" = lookup_nd(inport, ip6.src, nd.sll); %snext;",
15023-
learn_from_arp_request ? "" :
15024-
REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
15025-
" = lookup_nd_ip(inport, ip6.src); ");
15026-
ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_ns",
15027-
ds_cstr(actions), lflow_ref);
15028-
1502915020
/* For other packet types, we can skip neighbor learning.
1503015021
* So set REGBIT_LOOKUP_NEIGHBOR_RESULT to 1. */
1503115022
ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 0, "1",
@@ -15089,7 +15080,7 @@ build_neigh_learning_flows_for_lrouter_port(
1508915080
"always_learn_from_arp_request", true);
1509015081

1509115082
/* Check if we need to learn mac-binding from ARP requests. */
15092-
for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
15083+
for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
1509315084
if (!learn_from_arp_request) {
1509415085
/* ARP request to this address should always get learned,
1509515086
* so add a priority-110 flow to set
@@ -15135,6 +15126,57 @@ build_neigh_learning_flows_for_lrouter_port(
1513515126
WITH_HINT(&op->nbrp->header_));
1513615127
}
1513715128

15129+
/* Check if we need to learn mac-binding from ND NS. */
15130+
for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
15131+
if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].addr)) {
15132+
continue;
15133+
}
15134+
15135+
if (!learn_from_arp_request) {
15136+
/* ND NS request to this address should always get learned,
15137+
* so add a priority-110 flow to set
15138+
* REGBIT_LOOKUP_NEIGHBOR_IP_RESULT to 1. */
15139+
ds_clear(match);
15140+
ds_put_format(match,
15141+
"inport == %s && ip6.src == %s/%u && "
15142+
"nd.target == %s && nd_ns",
15143+
op->json_key,
15144+
op->lrp_networks.ipv6_addrs[i].network_s,
15145+
op->lrp_networks.ipv6_addrs[i].plen,
15146+
op->lrp_networks.ipv6_addrs[i].addr_s);
15147+
if (lrp_is_l3dgw(op)) {
15148+
ds_put_format(match, " && is_chassis_resident(%s)",
15149+
op->cr_port->json_key);
15150+
}
15151+
const char *actions_s = REGBIT_LOOKUP_NEIGHBOR_RESULT
15152+
" = lookup_nd(inport, ip6.src, nd.sll); "
15153+
REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" = 1;"
15154+
" next;";
15155+
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 110,
15156+
ds_cstr(match), actions_s, lflow_ref,
15157+
WITH_HINT(&op->nbrp->header_));
15158+
}
15159+
ds_clear(match);
15160+
ds_put_format(match,
15161+
"inport == %s && ip6.src == %s/%u && nd_ns",
15162+
op->json_key,
15163+
op->lrp_networks.ipv6_addrs[i].network_s,
15164+
op->lrp_networks.ipv6_addrs[i].plen);
15165+
if (lrp_is_l3dgw(op)) {
15166+
ds_put_format(match, " && is_chassis_resident(%s)",
15167+
op->cr_port->json_key);
15168+
}
15169+
ds_clear(actions);
15170+
ds_put_format(actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
15171+
" = lookup_nd(inport, ip6.src, nd.sll); %snext;",
15172+
learn_from_arp_request ? "" :
15173+
REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
15174+
" = lookup_nd_ip(inport, ip6.src); ");
15175+
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
15176+
ds_cstr(match), ds_cstr(actions), lflow_ref,
15177+
WITH_HINT(&op->nbrp->header_));
15178+
}
15179+
1513815180
if (lrp_is_l3dgw(op)) {
1513915181
ds_clear(match);
1514015182
ds_put_format(match, "inport == %s && (nd_na || nd_ns) && "

tests/ovn-northd.at

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9184,7 +9184,7 @@ AT_SETUP([LR neighbor lookup and learning flows])
91849184
ovn_start
91859185

91869186
# Create logical routers
9187-
check ovn-nbctl --wait=sb lr-add lr0
9187+
check ovn-nbctl --wait=sb lr-add lr0 -- lrp-add lr0 lrp 00:00:00:00:00:01 192.168.0.1/24 fd10::1/96
91889188

91899189
ovn-sbctl dump-flows lr0 > lrflows
91909190
AT_CAPTURE_FILE([lrflows])
@@ -9199,8 +9199,9 @@ AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
91999199
table=??(lr_in_learn_neighbor), priority=95 , match=(nd_ns && (ip6.src == 0 || nd.sll == 0)), action=(next;)
92009200
table=??(lr_in_lookup_neighbor), priority=0 , match=(1), action=(reg9[[2]] = 1; next;)
92019201
table=??(lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;)
9202+
table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "lrp" && arp.spa == 192.168.0.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;)
9203+
table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "lrp" && ip6.src == fd10::/96 && nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
92029204
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;)
9203-
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
92049205
table=??(lr_in_lookup_neighbor), priority=105 , match=(nd_na && nd.tll == 0), action=(reg9[[2]] = lookup_nd(inport, nd.target, eth.src); next;)
92059206
])
92069207

@@ -9219,9 +9220,12 @@ AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
92199220
table=??(lr_in_learn_neighbor), priority=95 , match=(nd_ns && (ip6.src == 0 || nd.sll == 0)), action=(next;)
92209221
table=??(lr_in_lookup_neighbor), priority=0 , match=(1), action=(reg9[[2]] = 1; next;)
92219222
table=??(lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); reg9[[3]] = 1; next;)
9223+
table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "lrp" && arp.spa == 192.168.0.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); reg9[[3]] = lookup_arp_ip(inport, arp.spa); next;)
9224+
table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "lrp" && ip6.src == fd10::/96 && nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); reg9[[3]] = lookup_nd_ip(inport, ip6.src); next;)
92229225
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); reg9[[3]] = 1; next;)
9223-
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); reg9[[3]] = lookup_nd_ip(inport, ip6.src); next;)
92249226
table=??(lr_in_lookup_neighbor), priority=105 , match=(nd_na && nd.tll == 0), action=(reg9[[2]] = lookup_nd(inport, nd.target, eth.src); reg9[[3]] = 1; next;)
9227+
table=??(lr_in_lookup_neighbor), priority=110 , match=(inport == "lrp" && arp.spa == 192.168.0.0/24 && arp.tpa == 192.168.0.1 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); reg9[[3]] = 1; next;)
9228+
table=??(lr_in_lookup_neighbor), priority=110 , match=(inport == "lrp" && ip6.src == fd10::/96 && nd.target == fd10::1 && nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); reg9[[3]] = 1; next;)
92259229
table=??(lr_in_lookup_neighbor), priority=110 , match=(nd_na && ip6.src == fe80::/10 && ip6.dst == ff00::/8), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); reg9[[3]] = lookup_nd_ip(inport, nd.target); next;)
92269230
table=??(lr_in_lookup_neighbor), priority=115 , match=(nd_na && nd.tll == 0 && ip6.src == fe80::/10 && ip6.dst == ff00::/8), action=(reg9[[2]] = lookup_nd(inport, nd.target, eth.src); reg9[[3]] = lookup_nd_ip(inport, nd.target); next;)
92279231
])

tests/ovn.at

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5740,6 +5740,136 @@ OVN_CLEANUP([hv1], [hv2])
57405740
AT_CLEANUP
57415741
])
57425742

5743+
OVN_FOR_EACH_NORTHD([
5744+
AT_SETUP([MAC binding learning from ARP request and ND NS])
5745+
CHECK_SCAPY
5746+
ovn_start
5747+
5748+
dnl Logical network:
5749+
dnl
5750+
dnl One logical router lr0 connected to two logical switches.
5751+
dnl ls-ext has a localnet port ln0 (physnet0) and a router attachment
5752+
dnl for lrp0. ls-int has one VIF lsp1 on hv1.
5753+
dnl
5754+
dnl lrp0 is a distributed gateway port (172.18.0.3/16, fd00::3/64),
5755+
dnl resident on hv1. On hv2 the LS ARP/ND responder for lrp0 is
5756+
dnl conditioned with is_chassis_resident("cr-lrp0"), so ARP/NS
5757+
dnl injected via the localnet on hv2 bypasses the LS responder and
5758+
dnl reaches the router pipeline where lr_in_lookup_neighbor runs.
5759+
5760+
check ovn-nbctl lr-add lr0
5761+
check ovn-nbctl ls-add ls-ext
5762+
check ovn-nbctl ls-add ls-int
5763+
5764+
check ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:ff:01 \
5765+
172.18.0.3/16 fd00::3/64
5766+
check ovn-nbctl lsp-add-router-port ls-ext lrp0-attach lrp0
5767+
5768+
check ovn-nbctl lrp-add lr0 lrp1 00:00:00:00:ff:02 192.168.1.1/24
5769+
check ovn-nbctl lsp-add-router-port ls-int lrp1-attach lrp1
5770+
5771+
check ovn-nbctl lsp-add ls-int lsp1 \
5772+
-- lsp-set-addresses lsp1 "f0:00:00:00:00:01 192.168.1.10"
5773+
5774+
check ovn-nbctl lsp-add-localnet-port ls-ext ln0 physnet0
5775+
check ovn-nbctl lrp-set-gateway-chassis lrp0 hv1 20
5776+
5777+
net_add n1
5778+
for i in 1 2; do
5779+
sim_add hv$i
5780+
as hv$i
5781+
ovs-vsctl add-br br-phys
5782+
ovn_attach n1 br-phys 192.168.0.$i
5783+
as hv$i ovs-vsctl set open . external-ids:ovn-bridge-mappings=physnet0:br-phys
5784+
done
5785+
5786+
check as hv1 ovs-vsctl \
5787+
-- add-port br-int vif1 \
5788+
-- set Interface vif1 external_ids:iface-id=lsp1
5789+
5790+
dnl Add a dummy port on hv2's br-phys to inject external packets.
5791+
check as hv2 ovs-vsctl \
5792+
-- add-port br-phys ext1 \
5793+
-- set Interface ext1 type=internal
5794+
5795+
OVN_POPULATE_ARP
5796+
wait_for_ports_up
5797+
check ovn-nbctl --wait=hv sync
5798+
5799+
dnl External device's addresses.
5800+
sha=f0:00:00:00:10:01
5801+
spa=172.18.0.100
5802+
spa6=fd00::100
5803+
5804+
dnl Router port addresses (target of ARP request / ND NS).
5805+
router_ip=172.18.0.3
5806+
router_ip6=fd00::3
5807+
5808+
AS_BOX([always_learn_from_arp_request=false])
5809+
check ovn-nbctl --wait=hv set logical_router lr0 \
5810+
options:always_learn_from_arp_request=false
5811+
5812+
AS_BOX([ARP request targeting router own IPv4])
5813+
packet=$(dump_arp 1 $sha ff:ff:ff:ff:ff:ff $spa $router_ip 00:00:00:00:00:00)
5814+
as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5815+
wait_row_count MAC_Binding 1 ip="$spa" mac=\"$sha\"
5816+
5817+
check ovn-sbctl --all destroy mac_binding
5818+
check ovn-nbctl --wait=hv sync
5819+
5820+
AS_BOX([ND NS targeting router own IPv6])
5821+
packet=$(dump_ns 33:33:ff:00:00:03 $sha ff02::1:ff00:3 $spa6 $router_ip6)
5822+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5823+
wait_row_count MAC_Binding 1 ip=\"$spa6\" mac=\"$sha\"
5824+
5825+
check ovn-sbctl --all destroy mac_binding
5826+
check ovn-nbctl --wait=hv sync
5827+
5828+
AS_BOX([GARP for unknown IP - no binding expected])
5829+
unknown_ip=172.18.0.200
5830+
packet=$(dump_arp 1 $sha ff:ff:ff:ff:ff:ff $unknown_ip $unknown_ip 00:00:00:00:00:00)
5831+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5832+
check ovn-nbctl --wait=hv sync
5833+
sleep 1
5834+
check_row_count MAC_Binding 0 ip="$unknown_ip"
5835+
5836+
AS_BOX([ND NS for unknown IPv6 - no binding expected])
5837+
unknown_ip6=fd00::200
5838+
packet=$(dump_ns 33:33:ff:00:02:00 $sha ff02::1:ff00:200 $spa6 $unknown_ip6)
5839+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5840+
check ovn-nbctl --wait=hv sync
5841+
sleep 1
5842+
check_row_count MAC_Binding 0 ip=\"$spa6\"
5843+
5844+
AS_BOX([Update existing ARP binding with new MAC])
5845+
check ovn-sbctl --all destroy mac_binding
5846+
check ovn-nbctl --wait=hv sync
5847+
5848+
packet=$(dump_arp 1 $sha ff:ff:ff:ff:ff:ff $spa $router_ip 00:00:00:00:00:00)
5849+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5850+
wait_row_count MAC_Binding 1 ip="$spa" mac=\"$sha\"
5851+
check ovn-nbctl --wait=hv sync
5852+
5853+
sha2=f0:00:00:00:10:02
5854+
packet=$(dump_arp 1 $sha2 ff:ff:ff:ff:ff:ff $spa $router_ip 00:00:00:00:00:00)
5855+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5856+
wait_row_count MAC_Binding 1 ip="$spa" mac=\"$sha2\"
5857+
5858+
AS_BOX([Update existing ND binding with new MAC])
5859+
packet=$(dump_ns 33:33:ff:00:00:03 $sha ff02::1:ff00:3 $spa6 $router_ip6)
5860+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5861+
wait_row_count MAC_Binding 1 ip=\"$spa6\" mac=\"$sha\"
5862+
check ovn-nbctl --wait=hv sync
5863+
5864+
packet=$(dump_ns 33:33:ff:00:00:03 $sha2 ff02::1:ff00:3 $spa6 $router_ip6)
5865+
check as hv2 ovs-appctl netdev-dummy/receive ext1 $packet
5866+
wait_row_count MAC_Binding 1 ip=\"$spa6\" mac=\"$sha2\"
5867+
5868+
OVN_CLEANUP([hv1], [hv2])
5869+
5870+
AT_CLEANUP
5871+
])
5872+
57435873
# 3 hypervisors, one logical switch, 3 logical ports per hypervisor
57445874
OVN_FOR_EACH_NORTHD([
57455875
AT_SETUP([portsecurity : 3 HVs, 1 LS, 3 lports/HV])

0 commit comments

Comments
 (0)