Skip to content

Commit b17ac57

Browse files
hzhou8dceara
authored andcommitted
northd: Support /31 router ports (RFC 3021).
A /31 point-to-point link has no broadcast address; both addresses in the range are usable hosts. However, ovn-northd computed a traditional subnet broadcast for the router port and included it in the priority-100 lr_in_ip_input L3 admission control drop flow (ip4.src == {...}). On a /31, that "broadcast" address is the peer, so all traffic originating from the /31 peer was dropped. Skip the broadcast address in the admission control drop set for /31 networks so that /31 router ports work as expected. Fixes: 936b640 ("ovn: Implement basic logical L3 routing.") Assisted-by: Claude Opus 4.8, Cursor Signed-off-by: Han Zhou <hzhou@ovn.org> Signed-off-by: Dumitru Ceara <dceara@redhat.com> (cherry picked from commit 1e738aa)
1 parent 29da768 commit b17ac57

4 files changed

Lines changed: 126 additions & 3 deletions

File tree

northd/northd.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12857,9 +12857,13 @@ op_put_v4_networks(struct ds *ds, const struct ovn_port *op, bool add_bcast)
1285712857
}
1285812858

1285912859
ds_put_cstr(ds, "{");
12860-
for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
12860+
for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
1286112861
ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s);
12862-
if (add_bcast) {
12862+
/* A /31 point-to-point link (RFC 3021) has no broadcast address:
12863+
* both addresses in the range are usable hosts. Including the
12864+
* computed "broadcast" here would drop legitimate traffic from the
12865+
* /31 peer, so skip it for /31 networks. */
12866+
if (add_bcast && op->lrp_networks.ipv4_addrs[i].plen != 31) {
1286312867
ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].bcast_s);
1286412868
}
1286512869
}

northd/ovn-northd.8.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3700,7 +3700,9 @@ icmp6_error {
37003700
</li>
37013701
<li>
37023702
<code>ip4.src</code> is the broadcast address of any IP network
3703-
known to the router.
3703+
known to the router. Point-to-point (<code>/31</code>, RFC 3021)
3704+
networks have no broadcast address and are excluded, so that
3705+
traffic from a <code>/31</code> peer is not dropped.
37043706
</li>
37053707
</ul>
37063708
</li>

tests/ovn-northd.at

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,6 +2307,33 @@ OVN_CLEANUP_NORTHD
23072307
AT_CLEANUP
23082308
])
23092309

2310+
OVN_FOR_EACH_NORTHD_NO_HV([
2311+
AT_SETUP([router LRP /31 L3 admission control])
2312+
ovn_start
2313+
2314+
# A /31 point-to-point link (RFC 3021) has no broadcast address; both
2315+
# addresses of the /31 are usable hosts. The L3 admission control flow must
2316+
# therefore not drop the computed "broadcast" address, otherwise traffic from
2317+
# the /31 peer is dropped.
2318+
check ovn-nbctl lr-add lr
2319+
check ovn-nbctl lrp-add lr lrp31 00:00:00:00:00:01 10.0.0.0/31
2320+
check ovn-nbctl lrp-add lr lrp24 00:00:00:00:00:03 10.0.2.1/24
2321+
2322+
check ovn-nbctl --wait=sb sync
2323+
ovn-sbctl dump-flows lr > lrflows
2324+
AT_CAPTURE_FILE([lrflows])
2325+
2326+
# The /31 peer (10.0.0.1) must not appear as a dropped broadcast source;
2327+
# only the wider /24 network keeps its broadcast address.
2328+
AT_CHECK([grep "lr_in_ip_input" lrflows | grep "priority=100" | grep "reg9" | ovn_strip_lflows], [0], [dnl
2329+
table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {10.0.0.0} && reg9[[0]] == 0), action=(drop;)
2330+
table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {10.0.2.1, 10.0.2.255} && reg9[[0]] == 0), action=(drop;)
2331+
])
2332+
2333+
OVN_CLEANUP_NORTHD
2334+
AT_CLEANUP
2335+
])
2336+
23102337
# This test case tests that when a logical switch has load balancers associated
23112338
# (with VIPs configured), the below logical flow is added by ovn-northd.
23122339
# table=ls_out_pre_lb, priority=100, match=(ip), action=(reg0[[0]] = 1; next;)

tests/ovn.at

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14256,6 +14256,96 @@ OVN_CLEANUP([hv1],[hv2])
1425614256
AT_CLEANUP
1425714257
])
1425814258

14259+
OVN_FOR_EACH_NORTHD([
14260+
AT_SETUP([/31 router port (RFC 3021)])
14261+
CHECK_SCAPY
14262+
ovn_start
14263+
14264+
# Logical network:
14265+
# 2 LS 'sw0' and 'sw1' connected via router R1.
14266+
# R1 connects to 'sw0' with a /31 point-to-point network (RFC 3021). The
14267+
# host 'sw0p1' uses 10.0.0.1, which is the address that OVN previously
14268+
# computed as the /31 "broadcast" and dropped in the L3 admission control.
14269+
# This test verifies that traffic sourced from the /31 peer is forwarded.
14270+
14271+
check ovn-nbctl lr-add R1
14272+
14273+
check ovn-nbctl ls-add sw0
14274+
check ovn-nbctl ls-add sw1
14275+
14276+
# Connect sw0 to R1 with a /31 network. The router owns 10.0.0.0/31.
14277+
check ovn-nbctl lrp-add R1 sw0 00:00:00:01:02:03 10.0.0.0/31
14278+
check ovn-nbctl lsp-add sw0 rp-sw0 -- set Logical_Switch_Port rp-sw0 \
14279+
type=router options:router-port=sw0 addresses=\"00:00:00:01:02:03\"
14280+
14281+
# Connect sw1 to R1.
14282+
check ovn-nbctl lrp-add R1 sw1 00:00:00:01:02:04 20.0.0.1/24
14283+
check ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
14284+
type=router options:router-port=sw1 addresses=\"00:00:00:01:02:04\"
14285+
14286+
# Create logical port sw0p1 in sw0. Its IP (10.0.0.1) is the /31 peer of
14287+
# the router port and equals the address OVN treated as the broadcast.
14288+
check ovn-nbctl lsp-add sw0 sw0p1 \
14289+
-- lsp-set-addresses sw0p1 "f0:00:00:01:02:03 10.0.0.1"
14290+
14291+
# Create logical port sw1p1 in sw1.
14292+
check ovn-nbctl lsp-add sw1 sw1p1 \
14293+
-- lsp-set-addresses sw1p1 "f0:00:00:01:02:04 20.0.0.2"
14294+
14295+
# Create two hypervisors and OVS ports corresponding to logical ports.
14296+
net_add n1
14297+
14298+
sim_add hv1
14299+
as hv1
14300+
check ovs-vsctl add-br br-phys
14301+
ovn_attach n1 br-phys 192.168.0.1
14302+
check ovs-vsctl -- add-port br-int hv1-vif1 -- \
14303+
set interface hv1-vif1 external-ids:iface-id=sw0p1 \
14304+
options:tx_pcap=hv1/vif1-tx.pcap \
14305+
options:rxq_pcap=hv1/vif1-rx.pcap \
14306+
ofport-request=1
14307+
14308+
sim_add hv2
14309+
as hv2
14310+
check ovs-vsctl add-br br-phys
14311+
ovn_attach n1 br-phys 192.168.0.2
14312+
check ovs-vsctl -- add-port br-int hv2-vif1 -- \
14313+
set interface hv2-vif1 external-ids:iface-id=sw1p1 \
14314+
options:tx_pcap=hv2/vif1-tx.pcap \
14315+
options:rxq_pcap=hv2/vif1-rx.pcap \
14316+
ofport-request=1
14317+
14318+
# Pre-populate the hypervisors' ARP tables so that we don't lose any
14319+
# packets for ARP resolution (native tunneling doesn't queue packets
14320+
# for ARP resolution).
14321+
OVN_POPULATE_ARP
14322+
14323+
wait_for_ports_up
14324+
check ovn-nbctl --wait=hv sync
14325+
14326+
# Send an IP packet from sw0p1 (10.0.0.1, the /31 peer) to sw1p1 (20.0.0.2).
14327+
# Before the /31 fix this packet was dropped by the L3 admission control,
14328+
# which treated 10.0.0.1 as the broadcast address of the 10.0.0.0/31 network.
14329+
packet=$(fmt_pkt "Ether(dst='00:00:00:01:02:03', \
14330+
src='f0:00:00:01:02:03') / \
14331+
IP(src='10.0.0.1', dst='20.0.0.2', ttl=64) / \
14332+
UDP(sport=53, dport=4369)")
14333+
as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
14334+
14335+
# Packet to expect at 'sw1p1'.
14336+
packet=$(fmt_pkt "Ether(dst='f0:00:00:01:02:04', \
14337+
src='00:00:00:01:02:04') / \
14338+
IP(src='10.0.0.1', dst='20.0.0.2', ttl=63) / \
14339+
UDP(sport=53, dport=4369)")
14340+
echo $packet > expected
14341+
14342+
OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
14343+
14344+
OVN_CLEANUP([hv1],[hv2])
14345+
14346+
AT_CLEANUP
14347+
])
14348+
1425914349
OVN_FOR_EACH_NORTHD([
1426014350
AT_SETUP([2 HVs, 1 lport/HV, localport ports])
1426114351
ovn_start

0 commit comments

Comments
 (0)