Skip to content

Commit a2091d5

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> (cherry picked from commit 20b9f0b)
1 parent 566793f commit a2091d5

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
@@ -14653,15 +14653,6 @@ build_neigh_learning_flows_for_lrouter(
1465314653
ds_cstr(actions), lflow_ref);
1465414654
}
1465514655

14656-
ds_clear(actions);
14657-
ds_put_format(actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
14658-
" = lookup_nd(inport, ip6.src, nd.sll); %snext;",
14659-
learn_from_arp_request ? "" :
14660-
REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
14661-
" = lookup_nd_ip(inport, ip6.src); ");
14662-
ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_ns",
14663-
ds_cstr(actions), lflow_ref);
14664-
1466514656
/* For other packet types, we can skip neighbor learning.
1466614657
* So set REGBIT_LOOKUP_NEIGHBOR_RESULT to 1. */
1466714658
ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 0, "1",
@@ -14725,7 +14716,7 @@ build_neigh_learning_flows_for_lrouter_port(
1472514716
"always_learn_from_arp_request", true);
1472614717

1472714718
/* Check if we need to learn mac-binding from ARP requests. */
14728-
for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
14719+
for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
1472914720
if (!learn_from_arp_request) {
1473014721
/* ARP request to this address should always get learned,
1473114722
* so add a priority-110 flow to set
@@ -14771,6 +14762,57 @@ build_neigh_learning_flows_for_lrouter_port(
1477114762
WITH_HINT(&op->nbrp->header_));
1477214763
}
1477314764

14765+
/* Check if we need to learn mac-binding from ND NS. */
14766+
for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
14767+
if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].addr)) {
14768+
continue;
14769+
}
14770+
14771+
if (!learn_from_arp_request) {
14772+
/* ND NS request to this address should always get learned,
14773+
* so add a priority-110 flow to set
14774+
* REGBIT_LOOKUP_NEIGHBOR_IP_RESULT to 1. */
14775+
ds_clear(match);
14776+
ds_put_format(match,
14777+
"inport == %s && ip6.src == %s/%u && "
14778+
"nd.target == %s && nd_ns",
14779+
op->json_key,
14780+
op->lrp_networks.ipv6_addrs[i].network_s,
14781+
op->lrp_networks.ipv6_addrs[i].plen,
14782+
op->lrp_networks.ipv6_addrs[i].addr_s);
14783+
if (lrp_is_l3dgw(op)) {
14784+
ds_put_format(match, " && is_chassis_resident(%s)",
14785+
op->cr_port->json_key);
14786+
}
14787+
const char *actions_s = REGBIT_LOOKUP_NEIGHBOR_RESULT
14788+
" = lookup_nd(inport, ip6.src, nd.sll); "
14789+
REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" = 1;"
14790+
" next;";
14791+
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 110,
14792+
ds_cstr(match), actions_s, lflow_ref,
14793+
WITH_HINT(&op->nbrp->header_));
14794+
}
14795+
ds_clear(match);
14796+
ds_put_format(match,
14797+
"inport == %s && ip6.src == %s/%u && nd_ns",
14798+
op->json_key,
14799+
op->lrp_networks.ipv6_addrs[i].network_s,
14800+
op->lrp_networks.ipv6_addrs[i].plen);
14801+
if (lrp_is_l3dgw(op)) {
14802+
ds_put_format(match, " && is_chassis_resident(%s)",
14803+
op->cr_port->json_key);
14804+
}
14805+
ds_clear(actions);
14806+
ds_put_format(actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
14807+
" = lookup_nd(inport, ip6.src, nd.sll); %snext;",
14808+
learn_from_arp_request ? "" :
14809+
REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
14810+
" = lookup_nd_ip(inport, ip6.src); ");
14811+
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
14812+
ds_cstr(match), ds_cstr(actions), lflow_ref,
14813+
WITH_HINT(&op->nbrp->header_));
14814+
}
14815+
1477414816
if (lrp_is_l3dgw(op)) {
1477514817
ds_clear(match);
1477614818
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
@@ -9009,7 +9009,7 @@ AT_SETUP([LR neighbor lookup and learning flows])
90099009
ovn_start
90109010

90119011
# Create logical routers
9012-
check ovn-nbctl --wait=sb lr-add lr0
9012+
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
90139013

90149014
ovn-sbctl dump-flows lr0 > lrflows
90159015
AT_CAPTURE_FILE([lrflows])
@@ -9024,8 +9024,9 @@ AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
90249024
table=??(lr_in_learn_neighbor), priority=95 , match=(nd_ns && (ip6.src == 0 || nd.sll == 0)), action=(next;)
90259025
table=??(lr_in_lookup_neighbor), priority=0 , match=(1), action=(reg9[[2]] = 1; next;)
90269026
table=??(lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;)
9027+
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;)
9028+
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;)
90279029
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); next;)
9028-
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_ns), action=(reg9[[2]] = lookup_nd(inport, ip6.src, nd.sll); next;)
90299030
table=??(lr_in_lookup_neighbor), priority=105 , match=(nd_na && nd.tll == 0), action=(reg9[[2]] = lookup_nd(inport, nd.target, eth.src); next;)
90309031
])
90319032

@@ -9044,9 +9045,12 @@ AT_CHECK([cat lrflows | grep -e lr_in_lookup_neighbor -e lr_in_learn_neighbor |
90449045
table=??(lr_in_learn_neighbor), priority=95 , match=(nd_ns && (ip6.src == 0 || nd.sll == 0)), action=(next;)
90459046
table=??(lr_in_lookup_neighbor), priority=0 , match=(1), action=(reg9[[2]] = 1; next;)
90469047
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;)
9048+
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;)
9049+
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;)
90479050
table=??(lr_in_lookup_neighbor), priority=100 , match=(nd_na), action=(reg9[[2]] = lookup_nd(inport, nd.target, nd.tll); reg9[[3]] = 1; next;)
9048-
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;)
90499051
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;)
9052+
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;)
9053+
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;)
90509054
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;)
90519055
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;)
90529056
])

tests/ovn.at

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5724,6 +5724,136 @@ OVN_CLEANUP([hv1], [hv2])
57245724
AT_CLEANUP
57255725
])
57265726

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

0 commit comments

Comments
 (0)