diff options
-rw-r--r-- | doc/user/ospf6d.rst | 8 | ||||
-rw-r--r-- | ospf6d/ospf6_area.c | 79 | ||||
-rw-r--r-- | ospf6d/ospf6_area.h | 7 | ||||
-rw-r--r-- | ospf6d/ospf6_asbr.c | 3 | ||||
-rw-r--r-- | ospf6d/ospf6_nssa.c | 96 | ||||
-rw-r--r-- | ospf6d/ospf6_nssa.h | 3 | ||||
-rw-r--r-- | ospf6d/ospf6_spf.c | 4 | ||||
-rw-r--r-- | ospf6d/ospf6_top.h | 12 | ||||
-rw-r--r-- | ospf6d/ospf6_zebra.c | 57 | ||||
-rw-r--r-- | ospf6d/ospf6_zebra.h | 1 | ||||
-rw-r--r-- | tests/topotests/ospf6_topo2/test_ospf6_topo2.py | 44 |
11 files changed, 301 insertions, 13 deletions
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index f0b0638ee..6e430ce01 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -178,7 +178,7 @@ OSPF6 area .. clicmd:: area A.B.C.D nssa [no-summary] -.. clicmd:: area (0-4294967295) nssa [no-summary] +.. clicmd:: area (0-4294967295) nssa [no-summary] [default-information-originate [metric-type (1-2)] [metric (0-16777214)]] Configure the area to be a NSSA (Not-So-Stubby Area). @@ -198,6 +198,12 @@ OSPF6 area advertisement of summaries into the area. In that case, a single Type-3 LSA containing a default route is originated into the NSSA. + NSSA ABRs and ASBRs can be configured with `default-information-originate` + option to originate a Type-7 default route into the NSSA area. In the case + of NSSA ASBRs, the origination of the default route is conditioned to the + existence of a default route in the RIB that wasn't learned via the OSPF + protocol. + .. clicmd:: area A.B.C.D export-list NAME .. clicmd:: area (0-4294967295) export-list NAME diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 2f5328c66..999266b8d 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -43,6 +43,7 @@ #include "ospf6_intra.h" #include "ospf6_abr.h" #include "ospf6_asbr.h" +#include "ospf6_zebra.h" #include "ospf6d.h" #include "lib/json.h" #include "ospf6_nssa.h" @@ -234,6 +235,36 @@ static void ospf6_area_no_summary_unset(struct ospf6 *ospf6, } } +static void ospf6_nssa_default_originate_set(struct ospf6 *ospf6, + struct ospf6_area *area, + int metric, int metric_type) +{ + if (!area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = true; + if (++ospf6->nssa_default_import_check.refcnt == 1) { + ospf6->nssa_default_import_check.status = false; + ospf6_zebra_import_default_route(ospf6, false); + } + } + + area->nssa_default_originate.metric_value = metric; + area->nssa_default_originate.metric_type = metric_type; +} + +static void ospf6_nssa_default_originate_unset(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + if (area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = false; + if (--ospf6->nssa_default_import_check.refcnt == 0) { + ospf6->nssa_default_import_check.status = false; + ospf6_zebra_import_default_route(ospf6, true); + } + area->nssa_default_originate.metric_value = -1; + area->nssa_default_originate.metric_type = -1; + } +} + /** * Make new area structure. * @@ -648,6 +679,17 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6) } if (IS_AREA_NSSA(oa)) { vty_out(vty, " area %s nssa", oa->name); + if (oa->nssa_default_originate.enabled) { + vty_out(vty, " default-information-originate"); + if (oa->nssa_default_originate.metric_value + != -1) + vty_out(vty, " metric %d", + oa->nssa_default_originate + .metric_value); + if (oa->nssa_default_originate.metric_type + != DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type 1"); + } if (oa->no_summary) vty_out(vty, " no-summary"); vty_out(vty, "\n"); @@ -1278,11 +1320,20 @@ DEFUN (no_ospf6_area_stub_no_summary, } DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd, - "area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]", + "area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\ + |no-summary$no_summary\ + }]", "OSPF6 area parameters\n" "OSPF6 area ID in IP address format\n" "OSPF6 area ID as a decimal value\n" "Configure OSPF6 area as nssa\n" + "Originate Type 7 default into NSSA area\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" "Do not inject inter-area routes into area\n") { struct ospf6_area *area; @@ -1296,23 +1347,44 @@ DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd, return CMD_WARNING_CONFIG_FAILED; } + if (dflt_originate) { + if (mval_str == NULL) + mval = -1; + if (mtype_str == NULL) + mtype = DEFAULT_METRIC_TYPE; + ospf6_nssa_default_originate_set(ospf6, area, mval, mtype); + } else + ospf6_nssa_default_originate_unset(ospf6, area); + if (no_summary) ospf6_area_no_summary_set(ospf6, area); else ospf6_area_no_summary_unset(ospf6, area); - if (ospf6_check_and_set_router_abr(ospf6)) + + if (ospf6_check_and_set_router_abr(ospf6)) { ospf6_abr_defaults_to_stub(ospf6); + ospf6_abr_nssa_type_7_defaults(ospf6); + } return CMD_SUCCESS; } DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, - "no area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]", + "no area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\ + |no-summary\ + }]", NO_STR "OSPF6 area parameters\n" "OSPF6 area ID in IP address format\n" "OSPF6 area ID as a decimal value\n" "Configure OSPF6 area as nssa\n" + "Originate Type 7 default into NSSA area\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" "Do not inject inter-area routes into area\n") { struct ospf6_area *area; @@ -1322,6 +1394,7 @@ DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, ospf6_area_nssa_unset(ospf6, area); ospf6_area_no_summary_unset(ospf6, area); + ospf6_nssa_default_originate_unset(ospf6, area); return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index 43ac60b26..77cbad8b9 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -52,6 +52,13 @@ struct ospf6_area { /* Area type */ int no_summary; + /* NSSA default-information-originate */ + struct { + bool enabled; + int metric_type; + int metric_value; + } nssa_default_originate; + /* Brouter traversal protection */ bool intra_brouter_calc; diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 85abd9d01..df40c608a 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -49,6 +49,7 @@ #include "ospf6_abr.h" #include "ospf6_intra.h" #include "ospf6_flood.h" +#include "ospf6_nssa.h" #include "ospf6d.h" #include "ospf6_spf.h" #include "ospf6_nssa.h" @@ -85,7 +86,7 @@ static struct ospf6_lsa *ospf6_originate_type5_type7_lsas( for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { if (IS_AREA_NSSA(oa)) - ospf6_nssa_lsa_originate(route, oa); + ospf6_nssa_lsa_originate(route, oa, true); } return lsa; diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index abaa16f41..2339d339f 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -368,6 +368,11 @@ static void ospf6_abr_task(struct ospf6 *ospf6) if (IS_OSPF6_DEBUG_ABR) zlog_debug("%s : announce stub defaults", __func__); ospf6_abr_defaults_to_stub(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : announce NSSA Type-7 defaults", + __func__); + ospf6_abr_nssa_type_7_defaults(ospf6); } if (IS_OSPF6_DEBUG_ABR) @@ -872,6 +877,83 @@ static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6) zlog_debug("ospf_abr_remove_unapproved_translates(): Stop"); } +static void ospf6_abr_nssa_type_7_default_create(struct ospf6 *ospf6, + struct ospf6_area *oa) +{ + struct ospf6_route *def; + int metric; + int metric_type; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Announcing Type-7 default route into NSSA area %s", + oa->name); + + def = ospf6_route_create(ospf6); + def->type = OSPF6_DEST_TYPE_NETWORK; + def->prefix.family = AF_INET6; + def->prefix.prefixlen = 0; + memset(&def->prefix.u.prefix6, 0, sizeof(struct in6_addr)); + def->type = OSPF6_DEST_TYPE_NETWORK; + def->path.subtype = OSPF6_PATH_SUBTYPE_DEFAULT_RT; + if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR)) + def->path.area_id = ospf6->backbone->area_id; + else + def->path.area_id = oa->area_id; + + /* Compute default route type and metric. */ + if (oa->nssa_default_originate.metric_value != -1) + metric = oa->nssa_default_originate.metric_value; + else + metric = DEFAULT_DEFAULT_ALWAYS_METRIC; + if (oa->nssa_default_originate.metric_type != -1) + metric_type = oa->nssa_default_originate.metric_type; + else + metric_type = DEFAULT_METRIC_TYPE; + def->path.metric_type = metric_type; + def->path.cost = metric; + if (metric_type == 1) + def->path.type = OSPF6_PATH_TYPE_EXTERNAL1; + else + def->path.type = OSPF6_PATH_TYPE_EXTERNAL2; + + ospf6_nssa_lsa_originate(def, oa, false); + ospf6_route_delete(def); +} + +static void ospf6_abr_nssa_type_7_default_delete(struct ospf6 *ospf6, + struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa; + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), 0, + oa->ospf6->router_id, oa->lsdb); + if (lsa && !OSPF6_LSA_IS_MAXAGE(lsa)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "Withdrawing Type-7 default route from area %s", + oa->name); + + ospf6_lsa_purge(lsa); + } +} + +/* NSSA Type-7 default route. */ +void ospf6_abr_nssa_type_7_defaults(struct ospf6 *ospf6) +{ + struct listnode *node; + struct ospf6_area *oa; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (IS_AREA_NSSA(oa) && oa->nssa_default_originate.enabled + && (IS_OSPF6_ABR(ospf6) + || (IS_OSPF6_ASBR(ospf6) + && ospf6->nssa_default_import_check.status))) + ospf6_abr_nssa_type_7_default_create(ospf6, oa); + else + ospf6_abr_nssa_type_7_default_delete(ospf6, oa); + } +} + static void ospf6_abr_nssa_task(struct ospf6 *ospf6) { /* called only if any_nssa */ @@ -1173,7 +1255,7 @@ static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area) /* This means the Type-5 LSA was originated for this route */ if (route->path.origin.id != 0 && info->type != DEFAULT_ROUTE) - ospf6_nssa_lsa_originate(route, area); + ospf6_nssa_lsa_originate(route, area, true); } /* Loop through the aggregation table to originate type-7 LSAs @@ -1193,7 +1275,7 @@ static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area) "Originating Type-7 LSAs for area %s", area->name); - ospf6_nssa_lsa_originate(aggr->route, area); + ospf6_nssa_lsa_originate(aggr->route, area, true); } } @@ -1287,7 +1369,7 @@ static struct in6_addr *ospf6_get_nssa_fwd_addr(struct ospf6_area *oa) } void ospf6_nssa_lsa_originate(struct ospf6_route *route, - struct ospf6_area *area) + struct ospf6_area *area, bool p_bit) { char buffer[OSPF6_MAX_LSASIZE]; struct ospf6_lsa_header *lsa_header; @@ -1318,7 +1400,7 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route, UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); /* external route tag */ - if (info->tag) + if (info && info->tag) SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); else UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); @@ -1333,7 +1415,8 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route, as_external_lsa->prefix.prefix_options = route->prefix_options; /* Set the P bit */ - as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P; + if (p_bit) + as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P; /* don't use refer LS-type */ as_external_lsa->prefix.prefix_refer_lstype = htons(0); @@ -1354,7 +1437,8 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route, UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); /* External Route Tag */ - if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) { + if (info + && CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) { route_tag_t network_order = htonl(info->tag); memcpy(p, &network_order, sizeof(network_order)); diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h index 631503edc..99cb04c00 100644 --- a/ospf6d/ospf6_nssa.h +++ b/ospf6d/ospf6_nssa.h @@ -64,8 +64,9 @@ extern void ospf6_schedule_abr_task(struct ospf6 *ospf6); extern void ospf6_area_nssa_update(struct ospf6_area *area); void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6); extern void ospf6_nssa_lsa_originate(struct ospf6_route *route, - struct ospf6_area *area); + struct ospf6_area *area, bool p_bit); extern void install_element_ospf6_debug_nssa(void); +extern void ospf6_abr_nssa_type_7_defaults(struct ospf6 *osof6); int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route, int type); extern int ospf6_abr_translate_nssa(struct ospf6_area *area, diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index e4de6ccf9..37e133f2f 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -646,8 +646,10 @@ static int ospf6_spf_calculation_thread(struct thread *t) /* External LSA calculation */ ospf6_ase_calculate_timer_add(ospf6); - if (ospf6_check_and_set_router_abr(ospf6)) + if (ospf6_check_and_set_router_abr(ospf6)) { ospf6_abr_defaults_to_stub(ospf6); + ospf6_abr_nssa_type_7_defaults(ospf6); + } monotime(&end); timersub(&end, &start, &runtime); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 3188b1f58..8a9f87ecf 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -143,6 +143,18 @@ struct ospf6 { /* OSPF6 redistribute configuration */ struct list *redist[ZEBRA_ROUTE_MAX + 1]; + /* NSSA default-information-originate */ + struct { + /* # of NSSA areas requesting default information */ + uint16_t refcnt; + + /* + * Whether a default route known through non-OSPF protocol is + * present in the RIB. + */ + bool status; + } nssa_default_import_check; + uint8_t flag; #define OSPF6_FLAG_ABR 0x04 #define OSPF6_FLAG_ASBR 0x08 diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index c2e91d09b..9f491f1e8 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -37,6 +37,7 @@ #include "ospf6_lsa.h" #include "ospf6_lsdb.h" #include "ospf6_asbr.h" +#include "ospf6_nssa.h" #include "ospf6_zebra.h" #include "ospf6d.h" #include "ospf6_area.h" @@ -129,6 +130,61 @@ void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id) AFI_IP6, type, 0, vrf_id); } +void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg) +{ + struct prefix prefix = {}; + int command; + + if (zclient->sock < 0) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Not connected to Zebra"); + return; + } + + prefix.family = AF_INET6; + prefix.prefixlen = 0; + + if (unreg) + command = ZEBRA_IMPORT_ROUTE_UNREGISTER; + else + command = ZEBRA_IMPORT_ROUTE_REGISTER; + + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__, + zserv_command_string(command), &prefix, + ospf6->vrf_id); + + if (zclient_send_rnh(zclient, command, &prefix, true, ospf6->vrf_id) + == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed", + __func__); +} + +static int ospf6_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +{ + struct ospf6 *ospf6; + struct zapi_route nhr; + + ospf6 = ospf6_lookup_by_vrf_id(vrf_id); + if (ospf6 == NULL || !IS_OSPF6_ASBR(ospf6)) + return 0; + + if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + zlog_err("%s[%u]: Failure to decode route", __func__, + ospf6->vrf_id); + return -1; + } + + if (nhr.prefix.family != AF_INET6 || nhr.prefix.prefixlen != 0 + || nhr.type == ZEBRA_ROUTE_OSPF6) + return 0; + + ospf6->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf6_abr_nssa_type_7_defaults(ospf6); + + return 0; +} + static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) { struct connected *c; @@ -664,6 +720,7 @@ void ospf6_zebra_init(struct thread_master *master) ospf6_zebra_if_address_update_delete; zclient->redistribute_route_add = ospf6_zebra_read_route; zclient->redistribute_route_del = ospf6_zebra_read_route; + zclient->import_check_update = ospf6_zebra_import_check_update; /* Install command element for zebra node. */ install_element(VIEW_NODE, &show_ospf6_zebra_cmd); diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index 77e48673c..572bed9f5 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -55,6 +55,7 @@ extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id); #define ospf6_zebra_is_redistribute(type, vrf_id) \ vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id) extern void ospf6_zebra_init(struct thread_master *tm); +extern void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg); extern void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6); extern void ospf6_zebra_delete_discard(struct ospf6_route *request, diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py index 997058f92..303bcd014 100644 --- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py +++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py @@ -431,6 +431,50 @@ def test_nssa_no_summary(): assert result is None, assertmsg +def test_nssa_default_originate(): + """ + Test the following: + * A type-7 default route should be originated into the NSSA area + when the default-information-originate option is configured; + * Once the default-information-originate option is unconfigured, the + previously originated Type-7 default route should be removed. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Configure r2 to announce a Type-7 default route. + # + config = """ + configure terminal + router ospf6 + no default-information originate + area 2 nssa default-information-originate + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting Type-7 default-route to be added") + routes = {"::/0": {}} + expect_ospfv3_routes("r4", routes, wait=30, type="external-2") + + # + # Configure r2 to stop announcing a Type-7 default route. + # + config = """ + configure terminal + router ospf6 + area 2 nssa + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting Type-7 default route to be removed") + test_func = partial(dont_expect_route, "r4", "::/0", type="external-2") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "r4's Type-7 default route still exists" + assert result is None, assertmsg + + def test_area_filters(): """ Test ABR import/export filters. |