summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alpine/APKBUILD.in4
-rw-r--r--debian/control2
-rw-r--r--doc/user/ospfd.rst9
-rw-r--r--docker/alpine/Dockerfile29
-rwxr-xr-xdocker/alpine/libyang/APKBUILD2
-rw-r--r--docker/centos-8/Dockerfile6
-rw-r--r--docker/ubi8-minimal/Dockerfile7
-rw-r--r--lib/libospf.h2
-rw-r--r--ospfd/ospf_interface.c3
-rw-r--r--ospfd/ospf_interface.h3
-rw-r--r--ospfd/ospf_lsa.c72
-rw-r--r--ospfd/ospf_route.c6
-rw-r--r--ospfd/ospf_vty.c83
-rw-r--r--redhat/frr.spec.in2
-rw-r--r--snapcraft/snapcraft.yaml.in2
-rw-r--r--tests/topotests/ospf_prefix_suppression/r1/frr.conf47
-rw-r--r--tests/topotests/ospf_prefix_suppression/r2/frr.conf57
-rw-r--r--tests/topotests/ospf_prefix_suppression/r3/frr.conf25
-rw-r--r--tests/topotests/ospf_prefix_suppression/test_ospf_prefix_suppression.py951
19 files changed, 1268 insertions, 44 deletions
diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in
index fd3c02f47e..63fb5c46b9 100644
--- a/alpine/APKBUILD.in
+++ b/alpine/APKBUILD.in
@@ -18,8 +18,8 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre2
perl pkgconf python3 python3-dev readline readline-dev sqlite-libs pcre2-dev
squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev
- py3-sphinx elfutils elfutils-dev libyang-dev protobuf-c-compiler protobuf-c-dev
- lua5.3-dev lua5.3"
+ py3-sphinx elfutils elfutils-dev protobuf-c-compiler protobuf-c-dev
+ lua5.3-dev lua5.3 gzip"
checkdepends="pytest py-setuptools"
install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
diff --git a/debian/control b/debian/control
index 9b7dcfe418..22d08b315d 100644
--- a/debian/control
+++ b/debian/control
@@ -22,7 +22,7 @@ Build-Depends: bison,
librtr-dev (>= 0.8.0~) <!pkg.frr.nortrlib>,
libsnmp-dev,
libssh-dev <!pkg.frr.nortrlib>,
- libyang2-dev,
+ libyang2-dev (>= 2.1.80),
lsb-base,
pkg-config,
python3:native,
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 4e30ef2aec..b61c9448db 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/docker/alpine/Dockerfile b/docker/alpine/Dockerfile
index 54621a49fd..d1c3060069 100644
--- a/docker/alpine/Dockerfile
+++ b/docker/alpine/Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Create a basic stage set up to build APKs
-FROM alpine:3.17 as alpine-builder
+FROM alpine:3.18 as alpine-builder
RUN apk add \
--update-cache \
abuild \
@@ -12,22 +12,29 @@ RUN apk add \
&& echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN adduser -D -G abuild builder && su builder -c 'abuild-keygen -a -n'
-# This stage builds a dist tarball from the source
-FROM alpine:3.17 as source-builder
+# This stage builds an APK for libyang
+FROM alpine-builder as alpine-apk-builder-libyang
+RUN mkdir -p /src/libyang
+COPY docker/alpine/libyang/APKBUILD /src/libyang
+RUN chown -R builder /pkgs /src
+USER builder
+RUN cd /src/libyang \
+ && abuild checksum \
+ && git init \
+ && abuild -r -P /pkgs/apk
-RUN mkdir -p /src/alpine
+# This stage builds a dist tarball from the source
+FROM alpine:3.18 as source-builder
+RUN mkdir -p /src/alpine /pkgs/apk
COPY alpine/APKBUILD.in /src/alpine
+COPY --from=alpine-apk-builder-libyang /pkgs/apk/src /pkgs/apk
+RUN cd /pkgs/apk/x86_64 && apk add --allow-untrusted *.apk
RUN source /src/alpine/APKBUILD.in \
&& apk add \
--no-cache \
--update-cache \
$makedepends \
- gzip \
- py-pip \
- rtrlib \
- protobuf-c-dev \
&& pip install pytest
-RUN mkdir -p /pkgs/apk
COPY . /src
ARG PKGVER
RUN cd /src \
@@ -40,6 +47,8 @@ RUN cd /src \
# This stage builds an APK from the dist tarball
FROM alpine-builder as alpine-apk-builder
COPY --from=source-builder /src/frr-*.tar.gz /src/alpine/* /dist/
+COPY --from=alpine-apk-builder-libyang /pkgs/apk/src /pkgs/apk
+RUN cd /pkgs/apk/x86_64 && apk add --allow-untrusted *.apk
RUN find /pkgs/apk -type f -name APKINDEX.tar.gz -delete
RUN chown -R builder /dist /pkgs
USER builder
@@ -49,7 +58,7 @@ RUN cd /dist \
&& abuild -r -P /pkgs/apk
# This stage installs frr from the apk
-FROM alpine:3.17
+FROM alpine:3.18
RUN mkdir -p /pkgs/apk
COPY --from=alpine-apk-builder /pkgs/apk/ /pkgs/apk/
RUN apk add \
diff --git a/docker/alpine/libyang/APKBUILD b/docker/alpine/libyang/APKBUILD
index aa792e7f0b..04e943fe48 100755
--- a/docker/alpine/libyang/APKBUILD
+++ b/docker/alpine/libyang/APKBUILD
@@ -1,7 +1,7 @@
# Contributor: Sören Tempel <soeren+alpine@soeren-tempel.net>
# Maintainer: Christian Franke <nobody@nowhere.ws>
pkgname=libyang
-pkgver=2.0.194
+pkgver=2.1.80
pkgrel=0
pkgdesc="YANG data modelling language parser and toolkit"
url="https://github.com/CESNET/libyang"
diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile
index 88a7d6a007..baed1fe4f3 100644
--- a/docker/centos-8/Dockerfile
+++ b/docker/centos-8/Dockerfile
@@ -10,8 +10,8 @@ RUN dnf install --enablerepo=powertools -y rpm-build git autoconf pcre-devel \
groff pkgconfig json-c-devel pam-devel bison flex python3-pytest \
c-ares-devel python3-devel python3-sphinx libcap-devel platform-python-devel \
protobuf-c-devel \
- https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
- https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
+ https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-00181/RedHat-8-x86_64-Packages/libyang-2.1.80-1.el8.x86_64.rpm \
+ https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-00181/RedHat-8-x86_64-Packages/libyang-devel-2.1.80-1.el8.x86_64.rpm \
https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \
https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.x86_64.rpm
@@ -42,7 +42,7 @@ RUN sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-* \
&& sed -i -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-*
RUN mkdir -p /pkgs/rpm \
- && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
+ && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-00181/RedHat-8-x86_64-Packages/libyang-2.1.80-1.el8.x86_64.rpm \
https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm
COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/
diff --git a/docker/ubi8-minimal/Dockerfile b/docker/ubi8-minimal/Dockerfile
index adb04219be..53f8d25697 100644
--- a/docker/ubi8-minimal/Dockerfile
+++ b/docker/ubi8-minimal/Dockerfile
@@ -37,13 +37,14 @@ RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 i
systemd-devel \
texinfo \
tzdata \
+ protobuf-c-devel \
&& microdnf --disableplugin=subscription-manager clean all
-RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \
+RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-181/RedHat-8-x86_64-Packages/libyang-2.1.80-1.el8.x86_64.rpm \
&& rpm -i /tmp/libyang2.rpm \
&& rm -f /tmp/libyang2.rpm
-RUN curl -sSL -o /tmp/libyang2-devel.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-devel-2.0.7-1.el8.x86_64.rpm \
+RUN curl -sSL -o /tmp/libyang2-devel.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-181/RedHat-8-x86_64-Packages/libyang-devel-2.1.80-1.el8.x86_64.rpm \
&& rpm -i /tmp/libyang2-devel.rpm \
&& rm -f /tmp/libyang2-devel.rpm
@@ -102,7 +103,7 @@ RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 i
systemd \
&& microdnf --disableplugin=subscription-manager clean all
-RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \
+RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANG2/shared/build-181/RedHat-8-x86_64-Packages/libyang-2.1.80-1.el8.x86_64.rpm \
&& rpm -i /tmp/libyang2.rpm \
&& rm -f /tmp/libyang2.rpm
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 3ffa7c0bb1..170909fa90 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -463,6 +463,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 a23802719b..d3dbd4821f 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",
@@ -12285,6 +12349,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));
@@ -13097,6 +13177,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/redhat/frr.spec.in b/redhat/frr.spec.in
index 5eb0853ed7..c7fd7e625a 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -184,7 +184,7 @@ BuildRequires: make
BuildRequires: ncurses-devel
BuildRequires: readline-devel
BuildRequires: texinfo
-BuildRequires: libyang-devel >= 2
+BuildRequires: libyang-devel >= 2.1.80
%if 0%{?rhel} && 0%{?rhel} < 7
#python27-devel is available from ius community repo for RedHat/CentOS 6
BuildRequires: python27-devel
diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in
index fa34304898..607cbc7fe3 100644
--- a/snapcraft/snapcraft.yaml.in
+++ b/snapcraft/snapcraft.yaml.in
@@ -302,7 +302,7 @@ parts:
- libpcre2-8-0
source: https://github.com/CESNET/libyang.git
source-type: git
- source-tag: v2.0.7
+ source-tag: v2.1.80
plugin: cmake
configflags:
- -DCMAKE_INSTALL_PREFIX:PATH=/usr
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))