From 76b1ac210edc46f7fad21dd59a5cad3ee69ae110 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 13 Apr 2026 14:13:28 -0300 Subject: [PATCH 1/2] tests: topology to test OSPF overlapping prefix Add test for the OSPF connected overlapping prefix bug where an external route is ommited because a connected route overlaps the prefix. Signed-off-by: Rafael Zalamena --- .../__init__.py | 0 .../r1/frr.conf | 15 ++ .../r2/frr.conf | 10 ++ .../test_ospf_connected_overlapping_prefix.py | 132 ++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 tests/topotests/ospf_connected_overlapping_prefix/__init__.py create mode 100644 tests/topotests/ospf_connected_overlapping_prefix/r1/frr.conf create mode 100644 tests/topotests/ospf_connected_overlapping_prefix/r2/frr.conf create mode 100644 tests/topotests/ospf_connected_overlapping_prefix/test_ospf_connected_overlapping_prefix.py diff --git a/tests/topotests/ospf_connected_overlapping_prefix/__init__.py b/tests/topotests/ospf_connected_overlapping_prefix/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/ospf_connected_overlapping_prefix/r1/frr.conf b/tests/topotests/ospf_connected_overlapping_prefix/r1/frr.conf new file mode 100644 index 000000000000..1fce9fe55fc7 --- /dev/null +++ b/tests/topotests/ospf_connected_overlapping_prefix/r1/frr.conf @@ -0,0 +1,15 @@ +interface r1-eth0 + ip address 10.0.0.1/24 + ip ospf area 0 + ip ospf dead-interval 8 + ip ospf hello-interval 2 +exit +! +interface r1-eth1 + ip address 10.0.0.129/30 +exit +! +router ospf + ospf router-id 10.254.254.1 + redistribute connected +exit diff --git a/tests/topotests/ospf_connected_overlapping_prefix/r2/frr.conf b/tests/topotests/ospf_connected_overlapping_prefix/r2/frr.conf new file mode 100644 index 000000000000..bf881bc62869 --- /dev/null +++ b/tests/topotests/ospf_connected_overlapping_prefix/r2/frr.conf @@ -0,0 +1,10 @@ +interface r2-eth0 + ip address 10.0.0.2/24 + ip ospf area 0 + ip ospf dead-interval 8 + ip ospf hello-interval 2 +exit +! +router ospf + ospf router-id 10.254.254.2 +exit diff --git a/tests/topotests/ospf_connected_overlapping_prefix/test_ospf_connected_overlapping_prefix.py b/tests/topotests/ospf_connected_overlapping_prefix/test_ospf_connected_overlapping_prefix.py new file mode 100644 index 000000000000..ac72f57519b8 --- /dev/null +++ b/tests/topotests/ospf_connected_overlapping_prefix/test_ospf_connected_overlapping_prefix.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_connected_overlapping_prefix.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2026 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_ospf_connected_overlapping_prefix.py: test OSPF overlapping prefix bug. +""" + +import os +import sys +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, get_topogen + +pytestmark = [pytest.mark.ospfd] + + +def setup_module(mod): + topodef = { + "s1": ("r1", "r2"), + "s2": "r1" + } + + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config(f"{CWD}/{rname}/frr.conf") + + tgen.start_router() + + +def expect_ospf_neighbor(router, neighbor): + tgen = get_topogen() + + expected = { + "neighbors": { + neighbor: [{ + "converged": "Full" + }] + } + } + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip ospf neighbor json", + expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, f"Router {router} failed to converge" + + +def test_ospf_neighbor_convergence(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + expect_ospf_neighbor("r1", "10.254.254.2") + expect_ospf_neighbor("r2", "10.254.254.1") + + +def test_ospf_connected_overlapping_prefix(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def expect_r2_route(): + out = tgen.gears["r2"].vtysh_cmd("show ip ospf route json", isjson=True) + return topotest.json_cmp(out, { + "10.0.0.128/30": { + "routeType": "N E2" + } + }) + + # Expect that the overlapped connected route shows up as external + _, result = topotest.run_and_expect(expect_r2_route, None, count=30, wait=1) + assert result is None, f"Router r2 should have learned external route" + + +def test_ospf_connected_prefix_not_in_external(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def expect_r2_route(): + out = tgen.gears["r2"].vtysh_cmd("show ip ospf route json", isjson=True) + return topotest.json_cmp(out, { + "10.0.0.0/24": { + "routeType": "N" + } + }) + + # Expect that the connected route shows up as non external + _, result = topotest.run_and_expect(expect_r2_route, None, count=30, wait=1) + assert result is None, f"Router r2 should not have learned external route" + + +def teardown_module(): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From 57b1005336479fceb1669211b992011bc1b4e225 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 13 Apr 2026 17:08:35 -0300 Subject: [PATCH 2/2] ospfd: fix redistribution for overlapping prefixes OSPF should not originate AS-external LSAs for networks that are already advertised internally (i.e. via OSPF-enabled interfaces). The redistribution check for connected routes used `prefix_match()`, which incorrectly suppressed routes whose prefixes only overlap with an OSPF-enabled interface. Use `prefix_same()` instead, so only identical prefixes are skipped and distinct connected networks are correctly redistributed. Signed-off-by: Rafael Zalamena --- ospfd/ospf_zebra.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 2f3276f7a597..7ade4835b9de 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -1028,10 +1028,17 @@ int ospf_distribute_check_connected(struct ospf *ospf, struct external_info *ei) struct listnode *node; struct ospf_interface *oi; + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct prefix address; - for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) - if (prefix_match(oi->address, (struct prefix *)&ei->p)) + /* Clean up the address by removing the mask part */ + prefix_copy(&address, oi->address); + apply_mask(&address); + + if (prefix_same(&address, (struct prefix *)&ei->p)) return 0; + } + return 1; }