summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/user/ospfd.rst14
-rw-r--r--lib/libospf.h1
-rw-r--r--ospfd/ospf_flood.c9
-rw-r--r--ospfd/ospf_gr.c11
-rw-r--r--ospfd/ospf_interface.c32
-rw-r--r--ospfd/ospf_interface.h3
-rw-r--r--ospfd/ospf_opaque.c6
-rw-r--r--ospfd/ospf_packet.c16
-rw-r--r--ospfd/ospf_vty.c79
-rw-r--r--tests/topotests/ospfapi/test_ospf_clientapi.py439
10 files changed, 585 insertions, 25 deletions
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 3430d8a282..232b1c3934 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -912,7 +912,7 @@ Opaque LSA
- *ospfd* supports Opaque LSA (:rfc:`2370`) as partial support for
+ *ospfd* supports Opaque LSA (:rfc:`5250`) as partial support for
MPLS Traffic Engineering LSAs. The opaque-lsa capability must be
enabled in the configuration. An alternate command could be
"mpls-te on" (:ref:`ospf-traffic-engineering`). Note that FRR
@@ -920,6 +920,18 @@ Opaque LSA
extensions that are used with MPLS-TE; it does not support a
complete RSVP-TE solution.
+.. clicmd:: ip ospf capability opaque [A.B.C.D]
+
+ Enable or disable OSPF LSA database exchange and flooding on an interface.
+ The default is that opaque capability is enabled as long as the opaque
+ capability is enabled with the :clicmd:`capability opaque` command at the
+ OSPF instance level (using the command above). Note that disabling opaque
+ LSA support on an interface will impact the applications using opaque LSAs
+ if the opaque LSAs are not received on other flooding paths by all the
+ OSPF routers using those applications. For example, OSPF Graceful Restart
+ uses opaque-link LSAs and disabling support on an interface will disable
+ graceful restart signaling on that interface.
+
.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external)
.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID
diff --git a/lib/libospf.h b/lib/libospf.h
index 9eaca9a1a8..e3c1adb810 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -69,6 +69,7 @@ extern "C" {
#define OSPF_MTU_IGNORE_DEFAULT 0
#define OSPF_FAST_HELLO_DEFAULT 0
#define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false
+#define OSPF_OPAQUE_CAPABLE_DEFAULT true
#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */
#define OSPF_AREA_RANGE_COST_UNSPEC -1U
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 5ae15fd887..dd8c9268f1 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -568,6 +568,15 @@ int ospf_flood_through_interface(struct ospf_interface *oi,
if (!ospf_if_is_enable(oi))
return 0;
+ if (IS_OPAQUE_LSA(lsa->data->type) &&
+ !OSPF_IF_PARAM(oi, opaque_capable)) {
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
+ zlog_debug(
+ "%s: Skipping interface %s (%s) with opaque disabled.",
+ __func__, IF_NAME(oi), ospf_get_name(oi->ospf));
+ return 0;
+ }
+
/* If flood reduction is configured, set the DC bit on the lsa. */
if (IS_LSA_SELF(lsa)) {
if (OSPF_FR_CONFIG(oi->area->ospf, oi->area)) {
diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c
index 2a346f2388..c23c42052f 100644
--- a/ospfd/ospf_gr.c
+++ b/ospfd/ospf_gr.c
@@ -773,8 +773,15 @@ static void ospf_gr_prepare(void)
}
/* Send a Grace-LSA to all neighbors. */
- for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi))
- ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false);
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) {
+ if (OSPF_IF_PARAM(oi, opaque_capable))
+ ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART,
+ false);
+ else
+ zlog_debug(
+ "GR: skipping grace LSA on interface %s (%s) with opaque capability disabled",
+ IF_NAME(oi), ospf_get_name(oi->ospf));
+ }
/* Record end of the grace period in non-volatile memory. */
ospf_gr_nvm_update(ospf, true);
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 840756c05c..72de198116 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -548,6 +548,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
UNSET_IF_PARAM(oip, auth_crypt);
UNSET_IF_PARAM(oip, auth_type);
UNSET_IF_PARAM(oip, if_area);
+ UNSET_IF_PARAM(oip, opaque_capable);
oip->auth_crypt = list_new();
@@ -556,6 +557,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
oip->ptp_dmvpn = 0;
oip->p2mp_delay_reflood = OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
+ oip->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT;
return oip;
}
@@ -585,19 +587,20 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr)
oip = rn->info;
route_unlock_node(rn);
- if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd)
- && !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay)
- && !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval)
- && !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface)
- && !OSPF_IF_PARAM_CONFIGURED(oip, v_hello)
- && !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello)
- && !OSPF_IF_PARAM_CONFIGURED(oip, v_wait)
- && !OSPF_IF_PARAM_CONFIGURED(oip, priority)
- && !OSPF_IF_PARAM_CONFIGURED(oip, type)
- && !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple)
- && !OSPF_IF_PARAM_CONFIGURED(oip, auth_type)
- && !OSPF_IF_PARAM_CONFIGURED(oip, if_area)
- && listcount(oip->auth_crypt) == 0) {
+ if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, v_wait) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, priority) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, type) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, if_area) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) &&
+ listcount(oip->auth_crypt) == 0) {
ospf_del_if_params(ifp, oip);
rn->info = NULL;
route_unlock_node(rn);
@@ -703,6 +706,9 @@ int ospf_if_new_hook(struct interface *ifp)
SET_IF_PARAM(IF_DEF_PARAMS(ifp), auth_type);
IF_DEF_PARAMS(ifp)->auth_type = OSPF_AUTH_NOTSET;
+ SET_IF_PARAM(IF_DEF_PARAMS(ifp), opaque_capable);
+ IF_DEF_PARAMS(ifp)->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT;
+
rc = ospf_opaque_new_if(ifp);
return rc;
}
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index ec1afa1b8b..38ec45c757 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -112,6 +112,9 @@ struct ospf_if_params {
/* point-to-multipoint delayed reflooding configuration */
bool p2mp_delay_reflood;
+
+ /* Opaque LSA capability at interface level (see RFC5250) */
+ DECLARE_IF_PARAM(bool, opaque_capable);
};
enum { MEMBER_ALLROUTERS = 0,
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 6894c6a009..27f47a6d79 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -1851,9 +1851,9 @@ static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t)
return;
}
- if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)
- || !ospf_if_is_enable(oi)
- || ospf_nbr_count_opaque_capable(oi) == 0) {
+ if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) ||
+ !OSPF_IF_PARAM(oi, opaque_capable) || !ospf_if_is_enable(oi) ||
+ ospf_nbr_count_opaque_capable(oi) == 0) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...",
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 105c04c7a1..cfa0d5d574 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -953,8 +953,9 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh,
}
#endif /* REJECT_IF_TBIT_ON */
- if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)
- && CHECK_FLAG(hello->options, OSPF_OPTION_O)) {
+ if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) &&
+ OSPF_IF_PARAM(oi, opaque_capable) &&
+ CHECK_FLAG(hello->options, OSPF_OPTION_O)) {
/*
* This router does know the correct usage of O-bit
* the bit should be set in DD packet only.
@@ -1362,8 +1363,9 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh,
}
#endif /* REJECT_IF_TBIT_ON */
- if (CHECK_FLAG(dd->options, OSPF_OPTION_O)
- && !CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) {
+ if (CHECK_FLAG(dd->options, OSPF_OPTION_O) &&
+ (!CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) ||
+ !OSPF_IF_PARAM(oi, opaque_capable))) {
/*
* This node is not configured to handle O-bit, for now.
* Clear it to ignore unsupported capability proposed by
@@ -1448,7 +1450,8 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh,
/* This is where the real Options are saved */
nbr->options = dd->options;
- if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) {
+ if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) &&
+ OSPF_IF_PARAM(oi, opaque_capable)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"Neighbor[%pI4] is %sOpaque-capable.",
@@ -3435,7 +3438,8 @@ static int ospf_make_db_desc(struct ospf_interface *oi,
/* Set Options. */
options = OPTIONS(oi);
- if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE))
+ if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) &&
+ OSPF_IF_PARAM(oi, opaque_capable))
SET_FLAG(options, OSPF_OPTION_O);
if (OSPF_FR_CONFIG(oi->ospf, oi->area))
SET_FLAG(options, OSPF_OPTION_DC);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 863055bf42..54fd60af23 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -3811,6 +3811,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
lookup_msg(ospf_ism_state_msg,
oi->state, NULL));
json_object_int_add(json_oi, "priority", PRIORITY(oi));
+ json_object_boolean_add(
+ json_interface_sub, "opaqueCapable",
+ OSPF_IF_PARAM(oi, opaque_capable));
} else {
vty_out(vty, " Area %s\n",
ospf_area_desc_string(oi->area));
@@ -3830,6 +3833,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
OSPF_IF_PARAM(oi, transmit_delay),
lookup_msg(ospf_ism_state_msg, oi->state, NULL),
PRIORITY(oi));
+ if (!OSPF_IF_PARAM(oi, opaque_capable))
+ vty_out(vty,
+ " Opaque LSA capability disabled on interface\n");
}
/* Show DR information. */
@@ -9803,6 +9809,61 @@ DEFUN (no_ip_ospf_mtu_ignore,
return CMD_SUCCESS;
}
+DEFPY(ip_ospf_capability_opaque, ip_ospf_capability_opaque_addr_cmd,
+ "[no] ip ospf capability opaque [A.B.C.D]$ip_addr",
+ NO_STR
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Disable OSPF capability on this interface\n"
+ "Disable OSPF opaque LSA capability on this interface\n"
+ "Address of interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct route_node *rn;
+ bool old_opaque_capable;
+ bool opaque_capable_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);
+ }
+
+ old_opaque_capable = params->opaque_capable;
+ params->opaque_capable = (no) ? false : true;
+ opaque_capable_change = (old_opaque_capable != params->opaque_capable);
+ if (params->opaque_capable != OSPF_OPAQUE_CAPABLE_DEFAULT)
+ SET_IF_PARAM(params, opaque_capable);
+ else {
+ UNSET_IF_PARAM(params, opaque_capable);
+ 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 opaque capability, flap the interface
+ * to reset all the neighbor adjacencies.
+ */
+ if (opaque_capable_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))) {
+ OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
+ OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp);
+ }
+ }
+ }
+ return CMD_SUCCESS;
+}
+
DEFUN (ospf_max_metric_router_lsa_admin,
ospf_max_metric_router_lsa_admin_cmd,
@@ -12167,6 +12228,21 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
if (params && params->ldp_sync_info)
ospf_ldp_sync_if_write_config(vty, params);
+ /* Capability opaque print. */
+ if (OSPF_IF_PARAM_CONFIGURED(params, opaque_capable) &&
+ params->opaque_capable !=
+ OSPF_OPAQUE_CAPABLE_DEFAULT) {
+ if (params->opaque_capable == false)
+ vty_out(vty,
+ " no ip ospf capability opaque");
+ else
+ vty_out(vty,
+ " ip ospf capability opaque");
+ 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));
@@ -12976,6 +13052,9 @@ static void ospf_vty_if_init(void)
install_element(INTERFACE_NODE, &ip_ospf_passive_cmd);
install_element(INTERFACE_NODE, &no_ip_ospf_passive_cmd);
+ /* "ip ospf capability opaque" commands. */
+ install_element(INTERFACE_NODE, &ip_ospf_capability_opaque_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/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py
index 39ebbcfb62..7a7ea85e2f 100644
--- a/tests/topotests/ospfapi/test_ospf_clientapi.py
+++ b/tests/topotests/ospfapi/test_ospf_clientapi.py
@@ -17,6 +17,7 @@ import subprocess
import sys
import time
from datetime import datetime, timedelta
+from functools import partial
import pytest
from lib.common_config import (
@@ -31,6 +32,12 @@ from lib.micronet import Timeout, comm_error
from lib.topogen import Topogen, TopoRouter
from lib.topotest import interface_set_status, json_cmp
+# 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
+
pytestmark = [pytest.mark.ospfd]
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -1142,6 +1149,438 @@ def test_ospf_opaque_restart(tgen):
_test_opaque_add_restart_add(tgen, apibin)
+def _test_opaque_interface_disable(tgen, apibin):
+ "Test disabling opaque capability on an interface"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ tc_name = "opaque_interface_disable"
+
+ p = None
+ pread = None
+ # Log to our stdin, stderr
+ pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+")
+ try:
+ # STEP 1 in test_ospf_opaque_interface_disable and STEP 56 in CI tests
+ step("Disable OSPF opaque LSA Copability on r1's interface to r2")
+ r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno ip ospf capability opaque")
+ time.sleep(15)
+
+ # STEP 2 in test_ospf_opaque_interface_disable and STEP 57 in CI tests
+ step("Verify the r1 configuration of 'no ip ospf capability opaque'")
+ no_capability_opaque_cfg = (
+ tgen.net["r1"]
+ .cmd(
+ 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque"'
+ )
+ .rstrip()
+ )
+ assertmsg = (
+ "'no ip ospf capability opaque' applied, but not present in configuration"
+ )
+ assert no_capability_opaque_cfg == " no ip ospf capability opaque", assertmsg
+
+ # STEP 3 in test_ospf_opaque_interface_disable and STEP 58 in CI tests
+ step("Verify the ospf opaque option is not applied to the r1 interface")
+ r1_interface_without_opaque = {
+ "interfaces": {
+ "r1-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.1",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": False,
+ }
+ }
+ }
+ r1_interface_with_opaque = {
+ "interfaces": {
+ "r1-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.1",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ r1_neighbor_without_opaque = {
+ "neighbors": {
+ "2.0.0.0": [
+ {
+ "optionsList": "*|-|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ r2_neighbor_without_opaque = {
+ "neighbors": {
+ "1.0.0.0": [
+ {
+ "optionsList": "*|-|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ # STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests
+ step("Verify that the r1 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests
+ step("Verify that the r1 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 6 in test_ospf_opaque_interface_disable and STEP 61 in CI tests
+ step(
+ "Verify no r2 configuration of 'no ip ospf capability opaque' in r2 configuration"
+ )
+ rc, _, _ = tgen.net["r2"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
+ )
+ assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration"
+ assert rc, assertmsg
+
+ # STEP 7 in test_ospf_opaque_interface_disable and STEP 62 in CI tests
+ step("Verify the ospf opaque option is applied to the r2 interface")
+ r2_interface_without_opaque = {
+ "interfaces": {
+ "r2-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.2",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": False,
+ }
+ }
+ }
+ r2_interface_with_opaque = {
+ "interfaces": {
+ "r2-eth0": {
+ "ifUp": True,
+ "ospfEnabled": True,
+ "ipAddress": "10.0.1.2",
+ "ospfIfType": "Broadcast",
+ "opaqueCapable": True,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF interface has opaque capability disabled"
+ assert result is None, assertmsg
+
+ # STEP 8 in test_ospf_opaque_interface_disable and STEP 63 in CI tests
+ step("Install opaque LSAs on r1")
+ pread = r2.popen(
+ ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,9,10.0.1.1,230,1,feedaceedeadbeef",
+ "add,10,1.2.3.4,231,1,feedaceecafebeef",
+ "add,11,232,1,feedaceebaddbeef",
+ "wait,20",
+ ]
+ )
+ opaque_LSAs_in_database = {
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ "linkLocalOpaqueLsaCount": 1,
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 1,
+ },
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ "asExternalOpaqueLsaCount": 1,
+ }
+ opaque_area_empty_database = {
+ "routerId":"2.0.0.0",
+ "areaLocalOpaqueLsa":{
+ "areas":{
+ "1.2.3.4":[
+ ]
+ }
+ }
+ }
+
+ # STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests
+ step("Check that LSAs are added on r1")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF database doesn't contain opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests
+ step("Check that LSAs are not added on r2")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database opaque-area json",
+ opaque_area_empty_database, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF area database contains opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 11 in test_ospf_opaque_interface_disable and STEP 66 in CI tests
+ step("Enable OSPF opaque LSA Copability on r1's interface to r2")
+ r1.vtysh_multicmd("conf t\ninterface r1-eth0\nip ospf capability opaque")
+ time.sleep(15)
+
+ # STEP 12 in test_ospf_opaque_interface_disable and STEP 67 in CI tests
+ step("Verify no r1 configuration of 'no ip ospf capability opaque'")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
+ )
+ assertmsg = "'no ip ospf capability opaque' not applied, but not present in r1 configuration"
+ assert rc, assertmsg
+
+ # STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests
+ step("Verify the ospf opaque option is applied to the r1 interface")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ r1_neighbor_with_opaque = {
+ "neighbors": {
+ "2.0.0.0": [
+ {
+ "optionsList": "*|O|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ r2_neighbor_with_opaque = {
+ "neighbors": {
+ "1.0.0.0": [
+ {
+ "optionsList": "*|O|-|-|-|-|E|-",
+ }
+ ]
+ }
+ }
+ # STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests
+ step("Verify that the r1 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests
+ step("Verify that the r2 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests
+ step("Check that LSAs are now added to r2")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF database doesn't contains opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 17 in test_ospf_opaque_interface_disable and STEP 72 in CI tests
+ step(
+ "Disable Opaque Capability on r2's interface to r1 using the interface address"
+ )
+ r2.vtysh_multicmd(
+ "conf t\ninterface r2-eth0\nno ip ospf capability opaque 10.0.1.2"
+ )
+
+ # STEP 18 in test_ospf_opaque_interface_disable and STEP 73 in CI tests
+ step("Clear the OSPF process on r2 to clear the OSPF LSDB")
+ r2.vtysh_multicmd("clear ip ospf process")
+ time.sleep(15)
+
+ # STEP 19 in test_ospf_opaque_interface_disable and STEP 74 in CI tests
+ step("Verify the r2 configuration of 'no ip ospf capability opaque 10.0.1.2'")
+ no_capability_opaque_cfg = (
+ tgen.net["r2"]
+ .cmd_nostatus(
+ 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque 10.0.1.2"'
+ )
+ .rstrip()
+ )
+ assertmsg = "'no ip ospf capability opaque 10.0.1.2' applied, but not present in configuration"
+ assert (
+ no_capability_opaque_cfg == " no ip ospf capability opaque 10.0.1.2"
+ ), assertmsg
+
+ # STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests
+ step("Verify the ospf opaque option is not applied to the r2 interface")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ # STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests
+ step("Verify that the r1 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests
+ step("Verify that the r2 neighbor options don't include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor has opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests
+ step("Verify that r1 still has the opaque LSAs")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF database doesn't contain opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests
+ step("Verify that r2 doesn't have the opaque LSAs")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database opaque-area json",
+ opaque_area_empty_database, True
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF area database contains opaque LSAs"
+ assert result is None, assertmsg
+
+ # STEP 25 in test_ospf_opaque_interface_disable and STEP 80 in CI tests
+ step("Remove the 'no ip ospf capability opaque 10.0.1.2' config from r2 ")
+ r2.vtysh_multicmd(
+ "conf t\ninterface r2-eth0\nip ospf capability opaque 10.0.1.2"
+ )
+ time.sleep(15)
+
+ # STEP 26 in test_ospf_opaque_interface_disable and STEP 81 in CI tests
+ step("Verify the r2 removal of 'no ip ospf capability opaque 10.0.1.2'")
+ rc, _, _ = tgen.net["r2"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
+ )
+ assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration"
+ assert rc, assertmsg
+
+ # STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests
+ step("Verify the ospf opaque option is applied to the r2 interface")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF interface doesn't have opaque capability disabled"
+ assert result is None, assertmsg
+
+ # STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests
+ step("Verify that the r2 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests
+ step("Verify that the r1 neighbor options include opaque")
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList"
+ assert result is None, assertmsg
+
+ # STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests
+ step("Verify that r2 now has the opaque LSAs")
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "r2 OSPF database doesn't contain opaque LSAs"
+ assert result is None, assertmsg
+
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if pread:
+ pread.terminate()
+ pread.wait()
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_interface_disable(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_opaque_interface_disable(tgen, apibin)
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))