Skip to content

Commit f7a907d

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 a1b7c8d commit f7a907d

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
@@ -12067,9 +12067,13 @@ op_put_v4_networks(struct ds *ds, const struct ovn_port *op, bool add_bcast)
1206712067
}
1206812068

1206912069
ds_put_cstr(ds, "{");
12070-
for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
12070+
for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
1207112071
ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s);
12072-
if (add_bcast) {
12072+
/* A /31 point-to-point link (RFC 3021) has no broadcast address:
12073+
* both addresses in the range are usable hosts. Including the
12074+
* computed "broadcast" here would drop legitimate traffic from the
12075+
* /31 peer, so skip it for /31 networks. */
12076+
if (add_bcast && op->lrp_networks.ipv4_addrs[i].plen != 31) {
1207312077
ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].bcast_s);
1207412078
}
1207512079
}

northd/ovn-northd.8.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3279,7 +3279,9 @@ icmp6_error {
32793279
</li>
32803280
<li>
32813281
<code>ip4.src</code> is the broadcast address of any IP network
3282-
known to the router.
3282+
known to the router. Point-to-point (<code>/31</code>, RFC 3021)
3283+
networks have no broadcast address and are excluded, so that
3284+
traffic from a <code>/31</code> peer is not dropped.
32833285
</li>
32843286
</ul>
32853287
</li>

tests/ovn-northd.at

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,6 +2233,33 @@ OVN_CLEANUP_NORTHD
22332233
AT_CLEANUP
22342234
])
22352235

2236+
OVN_FOR_EACH_NORTHD_NO_HV([
2237+
AT_SETUP([router LRP /31 L3 admission control])
2238+
ovn_start
2239+
2240+
# A /31 point-to-point link (RFC 3021) has no broadcast address; both
2241+
# addresses of the /31 are usable hosts. The L3 admission control flow must
2242+
# therefore not drop the computed "broadcast" address, otherwise traffic from
2243+
# the /31 peer is dropped.
2244+
check ovn-nbctl lr-add lr
2245+
check ovn-nbctl lrp-add lr lrp31 00:00:00:00:00:01 10.0.0.0/31
2246+
check ovn-nbctl lrp-add lr lrp24 00:00:00:00:00:03 10.0.2.1/24
2247+
2248+
check ovn-nbctl --wait=sb sync
2249+
ovn-sbctl dump-flows lr > lrflows
2250+
AT_CAPTURE_FILE([lrflows])
2251+
2252+
# The /31 peer (10.0.0.1) must not appear as a dropped broadcast source;
2253+
# only the wider /24 network keeps its broadcast address.
2254+
AT_CHECK([grep "lr_in_ip_input" lrflows | grep "priority=100" | grep "reg9" | ovn_strip_lflows], [0], [dnl
2255+
table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {10.0.0.0} && reg9[[0]] == 0), action=(drop;)
2256+
table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {10.0.2.1, 10.0.2.255} && reg9[[0]] == 0), action=(drop;)
2257+
])
2258+
2259+
OVN_CLEANUP_NORTHD
2260+
AT_CLEANUP
2261+
])
2262+
22362263
# This test case tests that when a logical switch has load balancers associated
22372264
# (with VIPs configured), the below logical flow is added by ovn-northd.
22382265
# 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
@@ -14180,6 +14180,96 @@ OVN_CLEANUP([hv1],[hv2])
1418014180
AT_CLEANUP
1418114181
])
1418214182

14183+
OVN_FOR_EACH_NORTHD([
14184+
AT_SETUP([/31 router port (RFC 3021)])
14185+
CHECK_SCAPY
14186+
ovn_start
14187+
14188+
# Logical network:
14189+
# 2 LS 'sw0' and 'sw1' connected via router R1.
14190+
# R1 connects to 'sw0' with a /31 point-to-point network (RFC 3021). The
14191+
# host 'sw0p1' uses 10.0.0.1, which is the address that OVN previously
14192+
# computed as the /31 "broadcast" and dropped in the L3 admission control.
14193+
# This test verifies that traffic sourced from the /31 peer is forwarded.
14194+
14195+
check ovn-nbctl lr-add R1
14196+
14197+
check ovn-nbctl ls-add sw0
14198+
check ovn-nbctl ls-add sw1
14199+
14200+
# Connect sw0 to R1 with a /31 network. The router owns 10.0.0.0/31.
14201+
check ovn-nbctl lrp-add R1 sw0 00:00:00:01:02:03 10.0.0.0/31
14202+
check ovn-nbctl lsp-add sw0 rp-sw0 -- set Logical_Switch_Port rp-sw0 \
14203+
type=router options:router-port=sw0 addresses=\"00:00:00:01:02:03\"
14204+
14205+
# Connect sw1 to R1.
14206+
check ovn-nbctl lrp-add R1 sw1 00:00:00:01:02:04 20.0.0.1/24
14207+
check ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
14208+
type=router options:router-port=sw1 addresses=\"00:00:00:01:02:04\"
14209+
14210+
# Create logical port sw0p1 in sw0. Its IP (10.0.0.1) is the /31 peer of
14211+
# the router port and equals the address OVN treated as the broadcast.
14212+
check ovn-nbctl lsp-add sw0 sw0p1 \
14213+
-- lsp-set-addresses sw0p1 "f0:00:00:01:02:03 10.0.0.1"
14214+
14215+
# Create logical port sw1p1 in sw1.
14216+
check ovn-nbctl lsp-add sw1 sw1p1 \
14217+
-- lsp-set-addresses sw1p1 "f0:00:00:01:02:04 20.0.0.2"
14218+
14219+
# Create two hypervisors and OVS ports corresponding to logical ports.
14220+
net_add n1
14221+
14222+
sim_add hv1
14223+
as hv1
14224+
check ovs-vsctl add-br br-phys
14225+
ovn_attach n1 br-phys 192.168.0.1
14226+
check ovs-vsctl -- add-port br-int hv1-vif1 -- \
14227+
set interface hv1-vif1 external-ids:iface-id=sw0p1 \
14228+
options:tx_pcap=hv1/vif1-tx.pcap \
14229+
options:rxq_pcap=hv1/vif1-rx.pcap \
14230+
ofport-request=1
14231+
14232+
sim_add hv2
14233+
as hv2
14234+
check ovs-vsctl add-br br-phys
14235+
ovn_attach n1 br-phys 192.168.0.2
14236+
check ovs-vsctl -- add-port br-int hv2-vif1 -- \
14237+
set interface hv2-vif1 external-ids:iface-id=sw1p1 \
14238+
options:tx_pcap=hv2/vif1-tx.pcap \
14239+
options:rxq_pcap=hv2/vif1-rx.pcap \
14240+
ofport-request=1
14241+
14242+
# Pre-populate the hypervisors' ARP tables so that we don't lose any
14243+
# packets for ARP resolution (native tunneling doesn't queue packets
14244+
# for ARP resolution).
14245+
OVN_POPULATE_ARP
14246+
14247+
wait_for_ports_up
14248+
check ovn-nbctl --wait=hv sync
14249+
14250+
# Send an IP packet from sw0p1 (10.0.0.1, the /31 peer) to sw1p1 (20.0.0.2).
14251+
# Before the /31 fix this packet was dropped by the L3 admission control,
14252+
# which treated 10.0.0.1 as the broadcast address of the 10.0.0.0/31 network.
14253+
packet=$(fmt_pkt "Ether(dst='00:00:00:01:02:03', \
14254+
src='f0:00:00:01:02:03') / \
14255+
IP(src='10.0.0.1', dst='20.0.0.2', ttl=64) / \
14256+
UDP(sport=53, dport=4369)")
14257+
as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
14258+
14259+
# Packet to expect at 'sw1p1'.
14260+
packet=$(fmt_pkt "Ether(dst='f0:00:00:01:02:04', \
14261+
src='00:00:00:01:02:04') / \
14262+
IP(src='10.0.0.1', dst='20.0.0.2', ttl=63) / \
14263+
UDP(sport=53, dport=4369)")
14264+
echo $packet > expected
14265+
14266+
OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
14267+
14268+
OVN_CLEANUP([hv1],[hv2])
14269+
14270+
AT_CLEANUP
14271+
])
14272+
1418314273
OVN_FOR_EACH_NORTHD([
1418414274
AT_SETUP([2 HVs, 1 lport/HV, localport ports])
1418514275
ovn_start

0 commit comments

Comments
 (0)