From e2eea4fe8e51915ccefeb3419282280e6b95cd63 Mon Sep 17 00:00:00 2001 From: Acee Date: Mon, 17 Jul 2023 13:47:16 -0400 Subject: [PATCH] ospfd: Implement OSPF prefix-suppression as specified in RFC 6860 Signed-off-by: Acee --- doc/user/ospfd.rst | 9 + lib/libospf.h | 2 +- ospfd/ospf_interface.c | 3 + ospfd/ospf_interface.h | 3 + ospfd/ospf_lsa.c | 72 +- ospfd/ospf_route.c | 6 + ospfd/ospf_vty.c | 83 ++ .../ospf_prefix_suppression/r1/frr.conf | 47 + .../ospf_prefix_suppression/r2/frr.conf | 57 ++ .../ospf_prefix_suppression/r3/frr.conf | 25 + .../test_ospf_prefix_suppression.py | 951 ++++++++++++++++++ 11 files changed, 1236 insertions(+), 22 deletions(-) create mode 100644 tests/topotests/ospf_prefix_suppression/r1/frr.conf create mode 100644 tests/topotests/ospf_prefix_suppression/r2/frr.conf create mode 100644 tests/topotests/ospf_prefix_suppression/r3/frr.conf create mode 100644 tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 232b1c3934..9365318178 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -699,6 +699,15 @@ Interfaces OSPF (:ref:`redistribute-routes-to-ospf`). This is the only way to advertise non-OSPF links into stub areas. +.. clicmd:: ip ospf prefix-suppression [A.B.C.D] + + Configure OSPF to not advertise the IPv4 prefix associated with the + OSPF interface. The associated IPv4 prefix will be omitted from an OSPF + router-LSA or advertised with a host mask in an OSPF network-LSA as + specified in RFC 6860, "Hiding Transit-Only Networks in OSPF". If an + optional IPv4 address is specified, the prefix suppression will apply + to the OSPF interface associated with the specified interface address. + .. clicmd:: ip ospf area (A.B.C.D|(0-4294967295)) diff --git a/lib/libospf.h b/lib/libospf.h index e3c1adb810..9a643256c2 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -70,7 +70,7 @@ extern "C" { #define OSPF_FAST_HELLO_DEFAULT 0 #define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false #define OSPF_OPAQUE_CAPABLE_DEFAULT true - +#define OSPF_PREFIX_SUPPRESSION_DEFAULT false #define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ #define OSPF_AREA_RANGE_COST_UNSPEC -1U diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 72de198116..bdab672b47 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -600,6 +600,7 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr) !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) && !OSPF_IF_PARAM_CONFIGURED(oip, if_area) && !OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) && + !OSPF_IF_PARAM_CONFIGURED(oip, prefix_suppression) && listcount(oip->auth_crypt) == 0) { ospf_del_if_params(ifp, oip); rn->info = NULL; @@ -709,6 +710,8 @@ int ospf_if_new_hook(struct interface *ifp) SET_IF_PARAM(IF_DEF_PARAMS(ifp), opaque_capable); IF_DEF_PARAMS(ifp)->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; + IF_DEF_PARAMS(ifp)->prefix_suppression = OSPF_PREFIX_SUPPRESSION_DEFAULT; + rc = ospf_opaque_new_if(ifp); return rc; } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 38ec45c757..47b70f8039 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -81,6 +81,9 @@ struct ospf_if_params { /* Fast-Hellos */ DECLARE_IF_PARAM(uint8_t, fast_hello); + /* Prefix-Suppression */ + DECLARE_IF_PARAM(bool, prefix_suppression); + /* Authentication data. */ uint8_t auth_simple[OSPF_AUTH_SIMPLE_SIZE + 1]; /* Simple password. */ uint8_t auth_simple__config : 1; diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 27e7e02759..7ef9834274 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -539,16 +539,23 @@ static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi) } /* no need for a stub link for unnumbered interfaces */ - if (oi->ptp_dmvpn - || !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { - /* Regardless of the state of the neighboring router, we must - add a Type 3 link (stub network). - N.B. Options 1 & 2 share basically the same logic. */ - masklen2ip(oi->address->prefixlen, &mask); - id.s_addr = CONNECTED_PREFIX(oi->connected)->u.prefix4.s_addr - & mask.s_addr; - links += link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, - oi->output_cost); + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + } else { + if (oi->ptp_dmvpn || + !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { + /* Regardless of the state of the neighboring router, we must + add a Type 3 link (stub network). + N.B. Options 1 & 2 share basically the same logic. */ + masklen2ip(oi->address->prefixlen, &mask); + id.s_addr = + CONNECTED_PREFIX(oi->connected)->u.prefix4.s_addr & + mask.s_addr; + links += link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, + 0, oi->output_cost); + } } return links; @@ -563,10 +570,15 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi) /* Describe Type 3 Link. */ if (oi->state == ISM_Waiting) { + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + return 0; + } if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "LSA[Type1]: Interface %s is in state Waiting. Adding stub interface", - oi->ifp->name); + zlog_debug("LSA[Type1]: Interface %s is in state Waiting. Adding stub interface", + oi->ifp->name); masklen2ip(oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, @@ -587,10 +599,15 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi) } /* Describe type 3 link. */ else { + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + return 0; + } if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "LSA[Type1]: Interface %s has no DR. Adding stub interface", - oi->ifp->name); + zlog_debug("LSA[Type1]: Interface %s has no DR. Adding stub interface", + oi->ifp->name); masklen2ip(oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, @@ -603,7 +620,7 @@ static int lsa_link_loopback_set(struct stream **s, struct ospf_interface *oi) struct in_addr id, mask; /* Describe Type 3 Link. */ - if (oi->state != ISM_Loopback) + if ((oi->state != ISM_Loopback) || OSPF_IF_PARAM(oi, prefix_suppression)) return 0; mask.s_addr = 0xffffffff; @@ -645,9 +662,15 @@ static int lsa_link_ptomp_set(struct stream **s, struct ospf_interface *oi) struct in_addr id, mask; uint16_t cost = ospf_link_cost(oi); - mask.s_addr = 0xffffffff; - id.s_addr = oi->address->u.prefix4.s_addr; - links += link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + } else { + mask.s_addr = 0xffffffff; + id.s_addr = oi->address->u.prefix4.s_addr; + links += link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); + } if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug("PointToMultipoint: running ptomultip_set"); @@ -1006,7 +1029,14 @@ static void ospf_network_lsa_body_set(struct stream *s, struct route_node *rn; struct ospf_neighbor *nbr; - masklen2ip(oi->address->prefixlen, &mask); + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + mask.s_addr = 0xffffffff; + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type2]: Interface %s network mask set to host mask due prefix-suppression", + oi->ifp->name); + } else { + masklen2ip(oi->address->prefixlen, &mask); + } stream_put_ipv4(s, mask.s_addr); /* The network-LSA lists those routers that are fully adjacent to diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index cdb1eb0095..85da212d22 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -462,6 +462,12 @@ void ospf_intra_add_transit(struct route_table *rt, struct vertex *v, the IP network number, which can be obtained by masking the Vertex ID (Link State ID) with its associated subnet mask (found in the body of the associated network-LSA). */ + if (lsa->mask.s_addr == 0xffffffff) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Suppress installing LSA[Type2,%pI4] route due to host mask", + &(lsa->header.id)); + return; + } p.family = AF_INET; p.prefix = v->id; p.prefixlen = ip_masklen(lsa->mask); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 54fd60af23..267898afcc 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -4076,6 +4076,20 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, ospf_interface_bfd_show(vty, ifp, json_interface_sub); + if (use_json) { + json_object_boolean_add(json_interface_sub, + "prefixSuppression", + OSPF_IF_PARAM(oi, + prefix_suppression)); + json_object_boolean_add(json_oi, "prefixSuppression", + OSPF_IF_PARAM(oi, + prefix_suppression)); + } else { + if (OSPF_IF_PARAM(oi, prefix_suppression)) + vty_out(vty, + " Suppress advertisement of interface IP prefix\n"); + } + /* OSPF Authentication information */ ospf_interface_auth_show(vty, oi, json_interface_sub, use_json); @@ -9865,6 +9879,56 @@ DEFPY(ip_ospf_capability_opaque, ip_ospf_capability_opaque_addr_cmd, } +DEFPY(ip_ospf_prefix_suppression, ip_ospf_prefix_suppression_addr_cmd, + "[no] ip ospf prefix-suppression [A.B.C.D]$ip_addr", NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Supress OSPF prefix advertisement on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct route_node *rn; + bool prefix_suppression_change; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (ip_addr.s_addr != INADDR_ANY) { + params = ospf_get_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + + prefix_suppression_change = (params->prefix_suppression == (bool)no); + params->prefix_suppression = (no) ? false : true; + if (params->prefix_suppression != OSPF_PREFIX_SUPPRESSION_DEFAULT) + SET_IF_PARAM(params, prefix_suppression); + else { + UNSET_IF_PARAM(params, prefix_suppression); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + } + + /* + * If there is a change to the prefix suppression, update the Router-LSA. + */ + if (prefix_suppression_change) { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi && (oi->state > ISM_Down) && + (ip_addr.s_addr == INADDR_ANY || + IPV4_ADDR_SAME(&oi->address->u.prefix4, &ip_addr))) { + (void)ospf_router_lsa_update_area(oi->area); + if (oi->state == ISM_DR) + ospf_network_lsa_update(oi); + } + } + } + return CMD_SUCCESS; +} + DEFUN (ospf_max_metric_router_lsa_admin, ospf_max_metric_router_lsa_admin_cmd, "max-metric router-lsa administrative", @@ -12243,6 +12307,22 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, "\n"); } + /* prefix-suppression print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, + prefix_suppression) && + params->prefix_suppression != + OSPF_PREFIX_SUPPRESSION_DEFAULT) { + if (params->prefix_suppression == false) + vty_out(vty, + " no ip ospf prefix-suppression"); + else + vty_out(vty, + " ip ospf prefix-suppression"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + while (1) { if (rn == NULL) rn = route_top(IF_OIFS_PARAMS(ifp)); @@ -13055,6 +13135,9 @@ static void ospf_vty_if_init(void) /* "ip ospf capability opaque" commands. */ install_element(INTERFACE_NODE, &ip_ospf_capability_opaque_addr_cmd); + /* "ip ospf prefix-suppression" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_prefix_suppression_addr_cmd); + /* These commands are compatibitliy for previous version. */ install_element(INTERFACE_NODE, &ospf_authentication_key_cmd); install_element(INTERFACE_NODE, &ospf_message_digest_key_cmd); diff --git a/tests/topotests/ospf_prefix_suppression/r1/frr.conf b/tests/topotests/ospf_prefix_suppression/r1/frr.conf new file mode 100644 index 0000000000..437b474153 --- /dev/null +++ b/tests/topotests/ospf_prefix_suppression/r1/frr.conf @@ -0,0 +1,47 @@ +! +hostname r1 +password zebra +log file /tmp/r1-frr.log +ip forwarding +! +interface r1-eth0 + ip address 10.1.1.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r1-eth1 + ip address 10.1.2.1/24 + ip ospf network non-broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r1-eth2 + ip address 10.1.3.1/24 + ip ospf network point-to-point + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r1-eth3 + ip address 10.1.4.1/24 + ip ospf network point-to-multipoint + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r1-eth4 + ip address 10.1.7.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +router ospf + ospf router-id 1.1.1.1 + distance 20 + network 10.1.1.0/24 area 0 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 10.1.4.0/24 area 0 + network 10.1.7.0/24 area 0 +! diff --git a/tests/topotests/ospf_prefix_suppression/r2/frr.conf b/tests/topotests/ospf_prefix_suppression/r2/frr.conf new file mode 100644 index 0000000000..68390f15f1 --- /dev/null +++ b/tests/topotests/ospf_prefix_suppression/r2/frr.conf @@ -0,0 +1,57 @@ +! +hostname r2 +password zebra +log file /tmp/r1-frr.log +ip forwarding +! +interface r2-eth0 + ip address 10.1.1.2/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r2-eth1 + ip address 10.1.2.2/24 + ip ospf network non-broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r2-eth2 + ip address 10.1.3.2/24 + ip ospf network point-to-point + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r2-eth3 + ip address 10.1.4.2/24 + ip ospf network point-to-multipoint + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r2-eth4 + ip address 10.1.5.2/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r2-eth5 + ip address 10.1.6.2/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +router ospf + ospf router-id 2.2.2.2 + distance 20 + network 10.1.1.0/24 area 0 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 10.1.4.0/24 area 0 + network 10.1.5.0/24 area 0 + network 10.1.6.0/24 area 1 +! diff --git a/tests/topotests/ospf_prefix_suppression/r3/frr.conf b/tests/topotests/ospf_prefix_suppression/r3/frr.conf new file mode 100644 index 0000000000..984a39d989 --- /dev/null +++ b/tests/topotests/ospf_prefix_suppression/r3/frr.conf @@ -0,0 +1,25 @@ +! +hostname r3 +password zebra +log file /tmp/r1-frr.log +ip forwarding +! +interface r3-eth0 + ip address 10.1.5.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r3-eth1 + ip address 10.1.6.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +router ospf + ospf router-id 3.3.3.3 + distance 20 + network 10.1.5.0/24 area 0 + network 10.1.6.0/24 area 1 diff --git a/tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py b/tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py new file mode 100644 index 0000000000..d5ea7ebc40 --- /dev/null +++ b/tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py @@ -0,0 +1,951 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_prefix_suppression.py +# +# Copyright (c) 2023 LabN Consulting +# Acee Lindem +# + +import os +import sys +import json +from time import sleep +from functools import partial +import pytest + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from lib.common_config import ( + run_frr_cmd, + shutdown_bringup_interface, + start_router_daemons, + step, +) + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ + + + +-----+ +-----+ +-----+ + eth4 | | eth0 | | eth4 eth0 | | + ------+ +-------------+ +--------------+ | +10.1.7.0/24 | | 10.1.1.0/24 | | 10.1.5.0/24 | | + | | | |.2 .3| | + | | eth1 | | | | + | +-------------+ | | | + | R1 | 10.1.2.0/24 | R2 | | R3 | + | | | | | | + | | eth2 | | | | + | +-------------+ | | | + | | 10.1.3.0/24 | | | | + | | | | | | + | | eth3 | | eth5 eth1 | | + | +-------------+ +--------------+ | + | | 10.1.4.0/24 | | 10.1.6.0/24 | | + .1 +-----+.1 .2+-----+.2 .3+-----+ + +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 3 routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + + # Interconect router 1, 2 (0) + switch = tgen.add_switch("s1-1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 1, 2 (1) + switch = tgen.add_switch("s2-1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 1, 2 (2) + switch = tgen.add_switch("s3-1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 1, 2 (3) + switch = tgen.add_switch("s4-1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 2, 3 (0) + switch = tgen.add_switch("s5-2-3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # Interconect router 2, 3 (1) + switch = tgen.add_switch("s6-2-3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # Add standalone network to router 1 + switch = tgen.add_switch("s7-1") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + logger.info("OSPF Prefix Suppression:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_all_routes_advertised(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + # Verify OSPF routes are installed + r3 = tgen.gears["r3"] + input_dict = { + "10.1.1.0/24": [ + { + "prefix": "10.1.1.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.1.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.1.0/24 not installed on router r3" + assert result is None, assertmsg + + input_dict = { + "10.1.2.0/24": [ + { + "prefix": "10.1.2.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.2.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.2.0/24 not installed on router r3" + assert result is None, assertmsg + + input_dict = { + "10.1.3.0/24": [ + { + "prefix": "10.1.3.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.3.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.3.0/24 not installed on router r3" + assert result is None, assertmsg + + input_dict = { + "10.1.4.1/32": [ + { + "prefix": "10.1.4.1/32", + "prefixLen": 32, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.4.1/32 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.4.1/32 not installed on router r3" + assert result is None, assertmsg + + input_dict = { + "10.1.4.2/32": [ + { + "prefix": "10.1.4.2/32", + "prefixLen": 32, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.4.2/32 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.4.2/32 not installed on router r3" + assert result is None, assertmsg + + input_dict = { + "10.1.7.0/24": [ + { + "prefix": "10.1.7.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.7.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.7.0/24 not installed on router r3" + assert result is None, assertmsg + + input_dict = {} + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.8.0/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.8.0/24 installed on router r3" + assert result is None, assertmsg + + +def test_broadcast_stub_suppression(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Configure R1 interface r1-eth4 with prefix suppression") + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth4\nip ospf prefix-suppression") + + step("Verify the R1 configuration of 'ip ospf prefix-suppression'") + prefix_suppression_cfg = ( + tgen.net["r1"] + .cmd('vtysh -c "show running ospfd" | grep "^ ip ospf prefix-suppression"') + .rstrip() + ) + assertmsg = "'ip ospf prefix-suppression' applied, but not present in configuration" + assert prefix_suppression_cfg == " ip ospf prefix-suppression", assertmsg + + step("Verify that ospf-prefix suppression is applied to the R1 interface") + r1_eth4_with_prefix_suppression = { + "interfaces": { + "r1-eth4": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.7.1", + "ospfIfType": "Broadcast", + "prefixSuppression": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth4 json", + r1_eth4_with_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "R1 OSPF interface r1-eth4 doesn't have prefix-suppression enabled" + assert result is None, assertmsg + + step( + "Verify that ospf-prefix suppression is applied to the R1 interface (non-JSON)" + ) + prefix_suppression_show = ( + tgen.net["r1"] + .cmd( + 'vtysh -c "show ip ospf interface r1-eth4" | grep "^ Suppress advertisement of interface IP prefix"' + ) + .rstrip() + ) + assertmsg = ( + "'ip ospf prefix-suppression' applied, but not present in interface show" + ) + assert ( + prefix_suppression_show == " Suppress advertisement of interface IP prefix" + ), assertmsg + + step("Verify the ospf prefix is not advertised and not present on r3") + r3 = tgen.gears["r3"] + input_dict = {} + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.7.0/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.7.0/24 installed on router r3" + assert result is None, assertmsg + + step("Remove R1 interface r1-eth4 prefix-suppression configuration") + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth4\nno ip ospf prefix-suppression") + + step("Verify no R1 configuration of 'ip ospf prefix-suppression") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf prefix-suppression'", warn=False + ) + assertmsg = ( + "'ip ospf prefix-suppression' not applied, but present in R1 configuration" + ) + assert rc, assertmsg + + step("Verify that ospf-prefix suppression is not applied to the R1 interface") + r1_eth4_without_prefix_suppression = { + "interfaces": { + "r1-eth4": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.7.1", + "ospfIfType": "Broadcast", + "prefixSuppression": False, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth4 json", + r1_eth4_without_prefix_suppression, + ) + + step("Verify that 10.1.7.0/24 route is now installed on R3") + input_dict = { + "10.1.7.0/24": [ + { + "prefix": "10.1.7.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.7.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.7.0/24 not installed on router r3" + assert result is None, assertmsg + + +def test_broadcast_transit_suppression(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step( + "Configure R1 interface r1-eth0 with prefix suppression using interface address" + ) + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf prefix-suppression 10.1.1.1") + + step("Verify the R1 configuration of 'ip ospf prefix-suppression 10.1.1.1'") + prefix_suppression_cfg = ( + tgen.net["r1"] + .cmd( + 'vtysh -c "show running ospfd" | grep "^ ip ospf prefix-suppression 10.1.1.1"' + ) + .rstrip() + ) + assertmsg = "'ip ospf prefix-suppression 10.1.1.1' applied, but not present in configuration" + assert prefix_suppression_cfg == " ip ospf prefix-suppression 10.1.1.1", assertmsg + + step( + "Configure R2 interface r2-eth0 with prefix suppression using interface address" + ) + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth0\nip ospf prefix-suppression 10.1.1.2") + + step("Verify that ospf-prefix suppression is applied to the R1 interface") + r1_eth0_with_prefix_suppression = { + "interfaces": { + "r1-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.1.1", + "ospfIfType": "Broadcast", + "networkType": "BROADCAST", + "prefixSuppression": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth0 json", + r1_eth0_with_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "R1 OSPF interface r1-eth0 doesn't have prefix-suppression enabled" + assert result is None, assertmsg + + step("Verify the OSPF prefix is not advertised and not present on r3") + r3 = tgen.gears["r3"] + input_dict = {} + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.1.0/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.1.0/24 installed on router r3" + assert result is None, assertmsg + + step("Verify the OSPF Network-LSA prefixes are also not present on R3 ") + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.1.1/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.1.1/24 installed on router r3" + assert result is None, assertmsg + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.1.2/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.1.2/24 installed on router r3" + assert result is None, assertmsg + + step( + "Remove R1 interface r1-eth0 prefix-suppression configuration using interface address" + ) + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth0\nno ip ospf prefix-suppression 10.1.1.1") + + step( + "Remove R2 interface r2-eth0 prefix-suppression configuration using interface address" + ) + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth0\nno ip ospf prefix-suppression 10.1.1.2") + + step("Verify no R1 configuration of 'ip ospf prefix-suppression") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf prefix-suppression 10.1.1.1'", warn=False + ) + assertmsg = "'ip ospf prefix-suppression 10.1.1.1' not applied, but present in R1 configuration" + assert rc, assertmsg + + step("Verify that ospf-prefix suppression is not applied to the R1 interface") + r1_eth0_without_prefix_suppression = { + "interfaces": { + "r1-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.1.1", + "ospfIfType": "Broadcast", + "networkType": "BROADCAST", + "prefixSuppression": False, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth0 json", + r1_eth0_without_prefix_suppression, + ) + + step("Verify that 10.1.1.0/24 route is now installed on R3") + input_dict = { + "10.1.1.0/24": [ + { + "prefix": "10.1.1.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.1.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.1.0/24 not installed on router r3" + assert result is None, assertmsg + + +def test_nbma_transit_suppression(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Configure R1 interface r1-eth1 with prefix suppression") + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth1\nip ospf prefix-suppression") + + step("Configure R2 interface r2-eth1 with prefix suppression") + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth1\nip ospf prefix-suppression") + + step("Verify that ospf-prefix suppression is applied to the R1 interface") + r1_eth1_with_prefix_suppression = { + "interfaces": { + "r1-eth1": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.2.1", + "ospfIfType": "Broadcast", + "networkType": "NBMA", + "prefixSuppression": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth1 json", + r1_eth1_with_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "R1 OSPF interface r1-eth1 doesn't have prefix-suppression enabled" + assert result is None, assertmsg + + step("Verify the OSPF prefix is not advertised and not present on r3") + r3 = tgen.gears["r3"] + input_dict = {} + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.2.0/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.2.0/24 installed on router r3" + assert result is None, assertmsg + + step("Verify the OSPF Network-LSA prefixes are also not present on R3 ") + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.2.1/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.2.1/24 installed on router r3" + assert result is None, assertmsg + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.2.2/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.2.2/24 installed on router r3" + assert result is None, assertmsg + + step("Remove R1 interface r1-eth1 prefix-suppression configuration") + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth1\nno ip ospf prefix-suppression") + + step("Remove R2 interface eth1 prefix-suppression configuration") + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth1\nno ip ospf prefix-suppression") + + step("Verify no R1 configuration of 'ip ospf prefix-suppression") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf prefix-suppression'", warn=False + ) + assertmsg = ( + "'ip ospf prefix-suppression' not applied, but present in R1 configuration" + ) + assert rc, assertmsg + + step("Verify that ospf-prefix suppression is not applied to the R1 interface") + r1_eth1_without_prefix_suppression = { + "interfaces": { + "r1-eth1": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.2.1", + "ospfIfType": "Broadcast", + "networkType": "NBMA", + "prefixSuppression": False, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth1 json", + r1_eth1_without_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "Prefix suppression on interface r1-eth1" + assert result is None, assertmsg + + step("Verify that 10.1.2.0/24 route is now installed on R3") + input_dict = { + "10.1.2.0/24": [ + { + "prefix": "10.1.2.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.2.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.2.0/24 not installed on router r3" + assert result is None, assertmsg + + +def test_p2p_suppression(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step( + "Configure R1 interface r1-eth2 with prefix suppression with interface address" + ) + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth2\nip ospf prefix-suppression 10.1.3.1") + + step( + "Configure R2 interface r2-eth1 with prefix suppression with interface address" + ) + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth2\nip ospf prefix-suppression 10.1.3.2") + + step("Verify the R1 configuration of 'ip ospf prefix-suppression 10.1.3.1'") + prefix_suppression_cfg = ( + tgen.net["r1"] + .cmd( + 'vtysh -c "show running ospfd" | grep "^ ip ospf prefix-suppression 10.1.3.1"' + ) + .rstrip() + ) + assertmsg = "'ip ospf prefix-suppression 10.1.3.1' applied, but not present in configuration" + assert prefix_suppression_cfg == " ip ospf prefix-suppression 10.1.3.1", assertmsg + + step("Verify that ospf-prefix suppression is applied to the R1 interface") + r1_eth2_with_prefix_suppression = { + "interfaces": { + "r1-eth2": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.3.1", + "ospfIfType": "Broadcast", + "networkType": "POINTOPOINT", + "prefixSuppression": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth2 json", + r1_eth2_with_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "R1 OSPF interface r1-eth2 doesn't have prefix-suppression enabled" + assert result is None, assertmsg + + step("Verify the OSPF prefix is not advertised and not present on r3") + r3 = tgen.gears["r3"] + input_dict = {} + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.3.0/24 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.3.0/24 installed on router r3" + assert result is None, assertmsg + + step( + "Remove R1 interface r1-eth2 prefix-suppression configuration using interface address" + ) + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth2\nno ip ospf prefix-suppression 10.1.3.1") + + step( + "Remove R2 interface r2-eth2 prefix-suppression configuration using interface address" + ) + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth2\nno ip ospf prefix-suppression 10.1.3.2") + + step("Verify no R1 configuration of 'ip ospf prefix-suppression") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf prefix-suppression 10.1.3.1'", warn=False + ) + assertmsg = "'ip ospf prefix-suppressio 10.1.3.1' not applied, but present in R1 configuration" + assert rc, assertmsg + + step("Verify that ospf-prefix suppression is not applied to the R1 interface") + r1_eth2_without_prefix_suppression = { + "interfaces": { + "r1-eth2": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.3.1", + "ospfIfType": "Broadcast", + "networkType": "POINTOPOINT", + "prefixSuppression": False, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth2 json", + r1_eth2_without_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "Prefix suppression on interface r1-eth2" + assert result is None, assertmsg + + step("Verify that 10.1.3.0/24 route is now installed on R3") + input_dict = { + "10.1.3.0/24": [ + { + "prefix": "10.1.3.0/24", + "prefixLen": 24, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.3.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.3.0/24 not installed on router r3" + assert result is None, assertmsg + + +def test_p2mp_suppression(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Configure R1 interface r1-eth3 with prefix suppression") + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth3\nip ospf prefix-suppression") + + step("Configure R2 interface r2-eth3 with prefix suppression") + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth3\nip ospf prefix-suppression") + + step("Verify that ospf-prefix suppression is applied to the R1 interface") + r1_eth3_with_prefix_suppression = { + "interfaces": { + "r1-eth3": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.4.1", + "ospfIfType": "Broadcast", + "networkType": "POINTOMULTIPOINT", + "prefixSuppression": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth3 json", + r1_eth3_with_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "R1 OSPF interface r1-eth3 doesn't have prefix-suppression enabled" + assert result is None, assertmsg + + step("Verify the OSPF P2MP prefixes are not advertised and not present on r3") + r3 = tgen.gears["r3"] + input_dict = {} + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.4.1/32 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.4.1/32 installed on router r3" + assert result is None, assertmsg + + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.4.2/32 json", input_dict, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.4.2/32 installed on router r3" + assert result is None, assertmsg + + step("Remove R1 interface r1-eth3 prefix-suppression configuration") + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf t\ninterface r1-eth3\nno ip ospf prefix-suppression") + + step("Remove R2 interface r2-eth3 prefix-suppression configuration") + r2 = tgen.gears["r2"] + r2.vtysh_cmd("conf t\ninterface r2-eth3\nno ip ospf prefix-suppression") + + step("Verify no R1 configuration of 'ip ospf prefix-suppression") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf prefix-suppression'", warn=False + ) + assertmsg = ( + "'ip ospf prefix-suppression' not applied, but present in R1 configuration" + ) + assert rc, assertmsg + + step("Verify that ospf-prefix suppression is not applied to the R1 interface") + r1_eth3_without_prefix_suppression = { + "interfaces": { + "r1-eth3": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.1.4.1", + "ospfIfType": "Broadcast", + "networkType": "POINTOMULTIPOINT", + "prefixSuppression": False, + } + } + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip ospf interface r1-eth3 json", + r1_eth3_without_prefix_suppression, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "Prefix suppression on interface r1-eth3" + assert result is None, assertmsg + + step("Verify that 10.1.4.1/32 route is now installed on R3") + input_dict = { + "10.1.4.1/32": [ + { + "prefix": "10.1.4.1/32", + "prefixLen": 32, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.4.1/32 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.4.1/32 not installed on router r3" + assert result is None, assertmsg + + step("Verify that 10.1.4.2/32 route is now installed on R3") + input_dict = { + "10.1.4.2/32": [ + { + "prefix": "10.1.4.2/32", + "prefixLen": 32, + "protocol": "ospf", + "nexthops": [ + { + "ip": "10.1.5.2", + "interfaceName": "r3-eth0", + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r3, "show ip route 10.1.4.2/32 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "10.1.4.2/32 not installed on router r3" + assert result is None, assertmsg + + +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)) -- 2.39.5