diff options
| author | Kaushik <kaushiknath.null@gmail.com> | 2021-03-25 04:29:51 -0700 |
|---|---|---|
| committer | Rafael Zalamena <rzalamena@opensourcerouting.org> | 2021-06-04 07:23:10 -0300 |
| commit | ad500b22b5fc3bc34009b7212c7c3b2f6c4375aa (patch) | |
| tree | f24f206101ffcbfc968ad97fd54a71b94c134ccb | |
| parent | 10ddcc321a59de099ee357fd5cca229a8c3110bf (diff) | |
ospf6d: Support for nssa in ospfv3
The following is implemented.
1. Configuring area as NSSA.
2. Generating Type 7 LSA.
3. Conversion of Type 7 to Type 5 ( Default Behavior).
4. NSSA ABR selection.
Reviewed-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
Co-authored-by: Kaushik <kaushiknath.null@gmail.com>
Co-authored-by: Soman K.S <somanks@gmail.com>
Signed-off-by: Kaushik <kaushiknath.null@gmail.com>
| -rw-r--r-- | ospf6d/ospf6_abr.c | 100 | ||||
| -rw-r--r-- | ospf6d/ospf6_abr.h | 4 | ||||
| -rw-r--r-- | ospf6d/ospf6_area.c | 68 | ||||
| -rw-r--r-- | ospf6d/ospf6_area.h | 11 | ||||
| -rw-r--r-- | ospf6d/ospf6_asbr.c | 232 | ||||
| -rw-r--r-- | ospf6d/ospf6_asbr.h | 6 | ||||
| -rw-r--r-- | ospf6d/ospf6_flood.c | 44 | ||||
| -rw-r--r-- | ospf6d/ospf6_flood.h | 2 | ||||
| -rw-r--r-- | ospf6d/ospf6_interface.c | 20 | ||||
| -rw-r--r-- | ospf6d/ospf6_interface.h | 2 | ||||
| -rw-r--r-- | ospf6d/ospf6_intra.c | 34 | ||||
| -rw-r--r-- | ospf6d/ospf6_lsa.c | 2 | ||||
| -rw-r--r-- | ospf6d/ospf6_lsa.h | 9 | ||||
| -rw-r--r-- | ospf6d/ospf6_lsdb.c | 11 | ||||
| -rw-r--r-- | ospf6d/ospf6_nssa.c | 1396 | ||||
| -rw-r--r-- | ospf6d/ospf6_nssa.h | 76 | ||||
| -rw-r--r-- | ospf6d/ospf6_proto.h | 2 | ||||
| -rw-r--r-- | ospf6d/ospf6_spf.c | 185 | ||||
| -rw-r--r-- | ospf6d/ospf6_spf.h | 8 | ||||
| -rw-r--r-- | ospf6d/ospf6_top.c | 25 | ||||
| -rw-r--r-- | ospf6d/ospf6_top.h | 7 | ||||
| -rw-r--r-- | ospf6d/ospf6d.c | 5 | ||||
| -rw-r--r-- | ospf6d/subdir.am | 3 |
23 files changed, 2167 insertions, 85 deletions
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 1af8aed1a9..a43118cb21 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -49,6 +49,7 @@ #include "ospf6_asbr.h" #include "ospf6_abr.h" #include "ospf6d.h" +#include "ospf6_nssa.h" unsigned char conf_debug_ospf6_abr; @@ -57,14 +58,29 @@ int ospf6_is_router_abr(struct ospf6 *o) struct listnode *node; struct ospf6_area *oa; int area_count = 0; + bool is_backbone = false; - for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) + for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s, area_id %pI4", __func__, &oa->area_id); if (IS_AREA_ENABLED(oa)) area_count++; - if (area_count > 1) + if (o->backbone == oa) + is_backbone = true; + } + + if ((area_count > 1) && (is_backbone)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : set flag OSPF6_FLAG_ABR", __func__); + SET_FLAG(o->flag, OSPF6_FLAG_ABR); return 1; - return 0; + } else { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : reset flag OSPF6_FLAG_ABR", __func__); + UNSET_FLAG(o->flag, OSPF6_FLAG_ABR); + return 0; + } } static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route *route, @@ -156,37 +172,72 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, uint16_t type; int is_debug = 0; + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : start area %s, route %pFX", __func__, + area->name, &route->prefix); + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + summary_table = area->summary_router; + else + summary_table = area->summary_prefix; + + summary = ospf6_route_lookup(&route->prefix, summary_table); + if (summary) { + old = ospf6_lsdb_lookup(summary->path.origin.type, + summary->path.origin.id, + area->ospf6->router_id, area->lsdb); + /* Reset the OSPF6_LSA_UNAPPROVED flag */ + if (old) + UNSET_FLAG(old->flag, OSPF6_LSA_UNAPPROVED); + } + /* Only destination type network, range or ASBR are considered */ if (route->type != OSPF6_DEST_TYPE_NETWORK && route->type != OSPF6_DEST_TYPE_RANGE && ((route->type != OSPF6_DEST_TYPE_ROUTER) || !CHECK_FLAG(route->path.router_bits, OSPF6_ROUTER_BIT_E))) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: Route type %d flag 0x%x is none of network, range nor ASBR, ignore", + __func__, route->type, route->path.router_bits); return 0; } /* AS External routes are never considered */ if (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 || route->path.type == OSPF6_PATH_TYPE_EXTERNAL2) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Path type is external, skip", + __func__); return 0; } /* do not generate if the path's area is the same as target area */ if (route->path.area_id == area->area_id) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: The route is in the area itself, ignore", + __func__); return 0; } /* do not generate if the nexthops belongs to the target area */ if (ospf6_abr_nexthops_belong_to_area(route, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: The route's nexthop is in the same area, ignore", + __func__); return 0; } if (route->type == OSPF6_DEST_TYPE_ROUTER) { if (ADV_ROUTER_IN_PREFIX(&route->prefix) == area->ospf6->router_id) { - zlog_debug( - "%s: Skipping ASBR announcement for ABR (%pI4)", - __func__, - &ADV_ROUTER_IN_PREFIX(&route->prefix)); + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: Skipping ASBR announcement for ABR (%pI4)", + __func__, + &ADV_ROUTER_IN_PREFIX(&route->prefix)); return 0; } } @@ -195,12 +246,12 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER)) { is_debug++; - zlog_debug( - "Originating summary in area %s for ASBR %pI4", - area->name, - &ADV_ROUTER_IN_PREFIX(&route->prefix)); + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "Originating summary in area %s for ASBR %pI4", + area->name, + &ADV_ROUTER_IN_PREFIX(&route->prefix)); } - summary_table = area->summary_router; } else { if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX)) @@ -235,15 +286,8 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, zlog_debug( "Originating summary in area %s for %pFX cost %u", area->name, &route->prefix, route->path.cost); - summary_table = area->summary_prefix; } - summary = ospf6_route_lookup(&route->prefix, summary_table); - if (summary) - old = ospf6_lsdb_lookup(summary->path.origin.type, - summary->path.origin.id, - area->ospf6->router_id, area->lsdb); - /* if this route has just removed, remove corresponding LSA */ if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) { if (is_debug) @@ -496,9 +540,15 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, /* create LSA */ lsa = ospf6_lsa_create(lsa_header); + /* Reset the unapproved flag */ + UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + /* Originate */ ospf6_lsa_originate_area(lsa, area); + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : finish area %s", __func__, area->name); + return 1; } @@ -566,8 +616,7 @@ ospf6_abr_range_summary_needs_update(struct ospf6_route *range, uint32_t cost) return (redo_summary); } -static void ospf6_abr_range_update(struct ospf6_route *range, - struct ospf6 *ospf6) +void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6) { uint32_t cost = 0; struct listnode *node, *nnode; @@ -579,6 +628,10 @@ static void ospf6_abr_range_update(struct ospf6_route *range, /* update range's cost and active flag */ cost = ospf6_abr_range_compute_cost(range, ospf6); + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: range %pFX, cost %d", __func__, &range->prefix, + cost); + /* Non-zero cost is a proxy for active longer prefixes in this range. * If there are active routes covered by this range AND either the * configured @@ -595,6 +648,9 @@ static void ospf6_abr_range_update(struct ospf6_route *range, */ if (ospf6_abr_range_summary_needs_update(range, cost)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: range %pFX update", __func__, + &range->prefix); for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) summary_orig += ospf6_abr_originate_summary_to_area(range, oa); @@ -628,6 +684,8 @@ void ospf6_abr_originate_summary(struct ospf6_route *route, struct ospf6 *ospf6) struct ospf6_area *oa; struct ospf6_route *range = NULL; + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: route %pFX", __func__, &route->prefix); if (route->type == OSPF6_DEST_TYPE_NETWORK) { oa = ospf6_area_lookup(route->path.area_id, ospf6); diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h index 6a912ac630..7c1ff4d389 100644 --- a/ospf6d/ospf6_abr.h +++ b/ospf6d/ospf6_abr.h @@ -56,6 +56,7 @@ struct ospf6_inter_router_lsa { } #define OSPF6_ABR_RANGE_CLEAR_COST(range) (range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC) +#define IS_OSPF6_ABR(o) ((o)->flag & OSPF6_FLAG_ABR) extern int ospf6_is_router_abr(struct ospf6 *o); @@ -88,5 +89,8 @@ extern void ospf6_abr_old_path_update(struct ospf6_route *old_route, struct ospf6_route_table *table); extern void ospf6_abr_init(void); extern void ospf6_abr_reexport(struct ospf6_area *oa); +extern void ospf6_abr_range_update(struct ospf6_route *range, + struct ospf6 *ospf6); +extern void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6); #endif /*OSPF6_ABR_H*/ diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index d65e40279d..d879f365be 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -45,10 +45,25 @@ #include "ospf6_asbr.h" #include "ospf6d.h" #include "lib/json.h" +#include "ospf6_nssa.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name"); +/* Utility functions. */ +int str2area_id(const char *str, struct in_addr *area_id, int *area_id_fmt) +{ + char *ep; + + area_id->s_addr = htonl(strtoul(str, &ep, 10)); + if (*ep && !inet_aton(str, area_id)) + return -1; + + *area_id_fmt = *ep ? OSPF6_AREA_FMT_DECIMAL : OSPF6_AREA_FMT_DOTTEDQUAD; + + return 0; +} + int ospf6_area_cmp(void *va, void *vb) { struct ospf6_area *oa = (struct ospf6_area *)va; @@ -60,6 +75,7 @@ int ospf6_area_cmp(void *va, void *vb) static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa) { switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_ROUTER: case OSPF6_LSTYPE_NETWORK: if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) { @@ -82,6 +98,10 @@ static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa) (struct ospf6_area *)lsa->lsdb->data); break; + case OSPF6_LSTYPE_TYPE_7: + ospf6_asbr_lsa_add(lsa); + break; + default: break; } @@ -611,6 +631,8 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6) else vty_out(vty, " area %s stub\n", oa->name); } + if (IS_AREA_NSSA(oa)) + vty_out(vty, " area %s nssa\n", oa->name); if (PREFIX_NAME_IN(oa)) vty_out(vty, " area %s filter-list prefix %s in\n", oa->name, PREFIX_NAME_IN(oa)); @@ -1216,6 +1238,48 @@ DEFUN (no_ospf6_area_stub_no_summary, return CMD_SUCCESS; } +DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd, + "area <A.B.C.D|(0-4294967295)> nssa", + "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") +{ + int idx_ipv4_number = 1; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + + if (!ospf6_area_nssa_set(ospf6, area)) { + vty_out(vty, + "First deconfigure all virtual link through this area\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, + "no area <A.B.C.D|(0-4294967295)> nssa", + 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") +{ + int idx_ipv4_number = 2; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + + ospf6_area_nssa_unset(ospf6, area); + + return CMD_SUCCESS; +} + + void ospf6_area_init(void) { install_element(VIEW_NODE, &show_ipv6_ospf6_spf_tree_cmd); @@ -1237,6 +1301,10 @@ void ospf6_area_init(void) install_element(OSPF6_NODE, &area_filter_list_cmd); install_element(OSPF6_NODE, &no_area_filter_list_cmd); + + /* "area nssa" commands. */ + install_element(OSPF6_NODE, &ospf6_area_nssa_cmd); + install_element(OSPF6_NODE, &no_ospf6_area_nssa_cmd); } void ospf6_area_interface_delete(struct ospf6_interface *oi) diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index 761fe75f73..fa761d732d 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -106,17 +106,27 @@ struct ospf6_area { uint32_t full_nbrs; /* Fully adjacent neighbors. */ uint8_t intra_prefix_originate; /* Force intra_prefix lsa originate */ + uint8_t NSSATranslatorRole; /* NSSA configured role */ +#define OSPF6_NSSA_ROLE_NEVER 0 +#define OSPF6_NSSA_ROLE_CANDIDATE 1 +#define OSPF6_NSSA_ROLE_ALWAYS 2 + uint8_t NSSATranslatorState; /* NSSA operational role */ +#define OSPF6_NSSA_TRANSLATE_DISABLED 0 +#define OSPF6_NSSA_TRANSLATE_ENABLED 1 }; +#define OSPF6_AREA_DEFAULT 0x00 #define OSPF6_AREA_ENABLE 0x01 #define OSPF6_AREA_ACTIVE 0x02 #define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */ #define OSPF6_AREA_STUB 0x08 +#define OSPF6_AREA_NSSA 0x10 #define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE)) #define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE)) #define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT)) #define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB)) +#define IS_AREA_NSSA(oa) (CHECK_FLAG((oa)->flag, OSPF6_AREA_NSSA)) #define OSPF6_CMD_AREA_GET(str, oa, ospf6) \ { \ @@ -153,5 +163,6 @@ extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6); extern void ospf6_area_init(void); struct ospf6_interface; extern void ospf6_area_interface_delete(struct ospf6_interface *oi); +int str2area_id(const char *str, struct in_addr *area_id, int *area_id_fmt); #endif /* OSPF_AREA_H */ diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 35f50898a6..9d2c1eb10e 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -50,6 +50,8 @@ #include "ospf6_intra.h" #include "ospf6_flood.h" #include "ospf6d.h" +#include "ospf6_spf.h" +#include "ospf6_nssa.h" #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); @@ -69,8 +71,8 @@ unsigned char conf_debug_ospf6_asbr = 0; #define ZROUTE_NAME(x) zebra_route_string(x) /* AS External LSA origination */ -static void ospf6_as_external_lsa_originate(struct ospf6_route *route, - struct ospf6 *ospf6) +void ospf6_as_external_lsa_originate(struct ospf6_route *route, + struct ospf6 *ospf6) { char buffer[OSPF6_MAX_LSASIZE]; struct ospf6_lsa_header *lsa_header; @@ -458,13 +460,48 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, } } +/* Check if the forwarding address is local address */ +static int ospf6_ase_forward_address_check(struct ospf6 *ospf6, + struct in6_addr *fwd_addr) +{ + struct listnode *anode, *node, *cnode; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct interface *ifp; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, node, oi)) { + if (!if_is_operative(oi->interface) + || oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + ifp = oi->interface; + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + if (IPV6_ADDR_SAME(&c->address->u.prefix6, + fwd_addr)) + return 0; + } + } + } + + return 1; +} + void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) { struct ospf6_as_external_lsa *external; struct prefix asbr_id; - struct ospf6_route *asbr_entry, *route, *old; + struct ospf6_route *asbr_entry, *route, *old = NULL; struct ospf6_path *path; struct ospf6 *ospf6; + int type; + struct ospf6_area *oa = NULL; + struct prefix fwd_addr; + ptrdiff_t offset; + + type = ntohs(lsa->header->type); + oa = lsa->lsdb->data; external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( lsa->header); @@ -495,11 +532,53 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) ospf6_linkstate_prefix(lsa->header->adv_router, htonl(0), &asbr_id); asbr_entry = ospf6_route_lookup(&asbr_id, ospf6->brouter_table); - if (asbr_entry == NULL - || !CHECK_FLAG(asbr_entry->path.router_bits, OSPF6_ROUTER_BIT_E)) { + if (asbr_entry == NULL) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) zlog_debug("ASBR entry not found: %pFX", &asbr_id); return; + } else { + /* The router advertising external LSA can be ASBR or ABR */ + if (!CHECK_FLAG(asbr_entry->path.router_bits, + OSPF6_ROUTER_BIT_E)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "External bit reset ASBR route entry : %pFX", + &asbr_id); + return; + } + } + + /* Check the forwarding address */ + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) { + offset = sizeof(*external) + + OSPF6_PREFIX_SPACE(external->prefix.prefix_length); + memset(&fwd_addr, 0, sizeof(struct prefix)); + fwd_addr.family = AF_INET6; + fwd_addr.prefixlen = IPV6_MAX_PREFIXLEN; + memcpy(&fwd_addr.u.prefix6, (caddr_t)external + offset, + sizeof(struct in6_addr)); + + if (!IN6_IS_ADDR_UNSPECIFIED(&fwd_addr.u.prefix6)) { + if (!ospf6_ase_forward_address_check( + ospf6, &fwd_addr.u.prefix6)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "Fwd address %pFX is local address", + &fwd_addr); + return; + } + + /* Find the forwarding entry */ + asbr_entry = ospf6_route_lookup_bestmatch( + &fwd_addr, ospf6->route_table); + if (asbr_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "Fwd address not found: %pFX", + &fwd_addr); + return; + } + } } route = ospf6_route_create(); @@ -540,22 +619,38 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) zlog_debug( - "%s: AS-External %u route add %pFX cost %u(%u) nh %u", - __func__, + "%s: %s %u route add %pFX cost %u(%u) nh %u", __func__, + (type == OSPF6_LSTYPE_AS_EXTERNAL) ? "AS-External" + : "NSSA", (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) ? 1 : 2, &route->prefix, route->path.cost, route->path.u.cost_e2, listcount(route->nh_list)); - old = ospf6_route_lookup(&route->prefix, ospf6->route_table); + if (type == OSPF6_LSTYPE_AS_EXTERNAL) + old = ospf6_route_lookup(&route->prefix, ospf6->route_table); + else if (type == OSPF6_LSTYPE_TYPE_7) + old = ospf6_route_lookup(&route->prefix, oa->route_table); if (!old) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("%s : Adding new route", __func__); /* Add the new route to ospf6 instance route table. */ - ospf6_route_add(route, ospf6->route_table); + if (type == OSPF6_LSTYPE_AS_EXTERNAL) + ospf6_route_add(route, ospf6->route_table); + /* Add the route to the area route table */ + else if (type == OSPF6_LSTYPE_TYPE_7) { + ospf6_route_add(route, oa->route_table); + } } else { /* RFC 2328 16.4 (6) * ECMP: Keep new equal preference path in current * route's path list, update zebra with new effective * list along with addition of ECMP path. */ + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("%s : old route %pFX cost %u(%u) nh %u", + __func__, &route->prefix, route->path.cost, + route->path.u.cost_e2, + listcount(route->nh_list)); ospf6_asbr_update_route_ecmp_path(old, route, ospf6); } } @@ -937,7 +1032,7 @@ void ospf6_asbr_distribute_list_update(int type, struct ospf6 *ospf6) &ospf6->t_distribute_update); } -static void ospf6_asbr_routemap_update(const char *mapname) +void ospf6_asbr_routemap_update(const char *mapname) { int type; struct listnode *node, *nnode; @@ -1015,7 +1110,7 @@ static void ospf6_asbr_routemap_event(const char *name) int ospf6_asbr_is_asbr(struct ospf6 *o) { - return o->external_table->count; + return (o->external_table->count || IS_OSPF6_ASBR(o)); } struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type, @@ -1072,9 +1167,50 @@ static void ospf6_redist_del(struct ospf6 *ospf6, struct ospf6_redist *red, } } +/*Set the status of the ospf instance to ASBR based on the status parameter, + * rechedule SPF calculation, originate router LSA*/ +void ospf6_asbr_status_update(struct ospf6 *ospf6, int status) +{ + struct listnode *lnode, *lnnode; + struct ospf6_area *oa; + + zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status); + + if (status) { + if (IS_OSPF6_ASBR(ospf6)) { + zlog_info("ASBR[%s:Status:%d]: Already ASBR", + ospf6->name, status); + return; + } + SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); + } else { + if (!IS_OSPF6_ASBR(ospf6)) { + zlog_info("ASBR[%s:Status:%d]: Already non ASBR", + ospf6->name, status); + return; + } + UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); + } + + /* Transition from/to status ASBR, schedule timer. */ + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE); + + /* Reoriginate router LSA for all areas */ + for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa)) + OSPF6_ROUTER_LSA_SCHEDULE(oa); +} + static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id) { + struct ospf6 *ospf6 = NULL; ospf6_zebra_redistribute(type, vrf_id); + + ospf6 = ospf6_lookup_by_vrf_id(vrf_id); + + if (!ospf6) + return; + + ospf6_asbr_status_update(ospf6, ++ospf6->redist_count); } static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, @@ -1096,6 +1232,8 @@ static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, } ospf6_asbr_routemap_unset(red); + zlog_debug("%s: redist_count %d", __func__, ospf6->redist_count); + ospf6_asbr_status_update(ospf6, --ospf6->redist_count); } /* When an area is unstubified, flood all the external LSAs in the area */ @@ -1139,35 +1277,6 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) } } -/* Update ASBR status. */ -static void ospf6_asbr_status_update(struct ospf6 *ospf6, uint8_t status) -{ - struct listnode *lnode, *lnnode; - struct ospf6_area *oa; - - zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status); - - if (status) { - if (IS_OSPF6_ASBR(ospf6)) { - zlog_info("ASBR[%s:Status:%d]: Already ASBR", - ospf6->name, status); - return; - } - SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); - } else { - if (!IS_OSPF6_ASBR(ospf6)) { - zlog_info("ASBR[%s:Status:%d]: Already non ASBR", - ospf6->name, status); - return; - } - UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); - } - - ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE); - for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa)) - OSPF6_ROUTER_LSA_SCHEDULE(oa); -} - void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct prefix *prefix, unsigned int nexthop_num, @@ -1175,6 +1284,8 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct ospf6 *ospf6) { route_map_result_t ret; + struct listnode *lnode; + struct ospf6_area *oa; struct ospf6_route troute; struct ospf6_external_info tinfo; struct ospf6_route *route, *match; @@ -1276,6 +1387,11 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, match->path.origin.id = htonl(info->id); ospf6_as_external_lsa_originate(match, ospf6); ospf6_asbr_status_update(ospf6, ospf6->redistribute); + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + if (IS_AREA_NSSA(oa)) + ospf6_nssa_lsa_originate(match, oa); + } + return; } @@ -1334,13 +1450,19 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, route->path.origin.id = htonl(info->id); ospf6_as_external_lsa_originate(route, ospf6); ospf6_asbr_status_update(ospf6, ospf6->redistribute); + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + if (IS_AREA_NSSA(oa)) + ospf6_nssa_lsa_originate(route, oa); + } } void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, struct prefix *prefix, struct ospf6 *ospf6) { + struct ospf6_area *oa; struct ospf6_route *match; struct ospf6_external_info *info = NULL; + struct listnode *lnode; struct route_node *node; struct ospf6_lsa *lsa; struct prefix prefix_id; @@ -1369,8 +1491,27 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), htonl(info->id), ospf6->router_id, ospf6->lsdb); - if (lsa) + if (lsa) { + if (IS_OSPF6_DEBUG_ASBR) { + zlog_debug("withdraw type 5 LSA for route %pFX", + prefix); + } ospf6_lsa_purge(lsa); + } + + /* Delete the NSSA LSA */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), + htonl(info->id), ospf6->router_id, + oa->lsdb); + if (lsa) { + if (IS_OSPF6_DEBUG_ASBR) { + zlog_debug("withdraw type 7 LSA for route %pFX", + prefix); + } + ospf6_lsa_purge(lsa); + } + } /* remove binding in external_id_table */ prefix_id.family = AF_INET; @@ -2278,11 +2419,20 @@ static struct ospf6_lsa_handler as_external_handler = { .lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str, .lh_debug = 0}; +static struct ospf6_lsa_handler nssa_external_handler = { + .lh_type = OSPF6_LSTYPE_TYPE_7, + .lh_name = "NSSA", + .lh_short_name = "Type7", + .lh_show = ospf6_as_external_lsa_show, + .lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str, + .lh_debug = 0}; + void ospf6_asbr_init(void) { ospf6_routemap_init(); ospf6_install_lsa_handler(&as_external_handler); + ospf6_install_lsa_handler(&nssa_external_handler); install_element(VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd); diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 4774ac435a..418c157f36 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -71,6 +71,7 @@ struct ospf6_as_external_lsa { } extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa); + extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, struct ospf6_route *asbr_entry); extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry, @@ -106,4 +107,9 @@ extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, extern void ospf6_asbr_distribute_list_update(int type, struct ospf6 *ospf6); struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type, unsigned short instance); +extern void ospf6_asbr_routemap_update(const char *mapname); +extern void ospf6_as_external_lsa_originate(struct ospf6_route *route, + struct ospf6 *ospf6); +extern void ospf6_asbr_status_update(struct ospf6 *ospf6, int status); + #endif /* OSPF6_ASBR_H */ diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 76e81aab7b..ac16e53d63 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -40,6 +40,7 @@ #include "ospf6_neighbor.h" #include "ospf6_flood.h" +#include "ospf6_nssa.h" unsigned char conf_debug_ospf6_flooding; @@ -213,12 +214,19 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) { struct timeval now; struct ospf6_lsa *old; + struct ospf6_area *area = NULL; /* Remove the old instance from all neighbors' Link state retransmission list (RFC2328 13.2 last paragraph) */ old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, lsa->header->adv_router, lsa->lsdb); if (old) { + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : old LSA %s", __func__, + lsa->name); + lsa->external_lsa_id = old->external_lsa_id; + } THREAD_OFF(old->expire); THREAD_OFF(old->refresh); ospf6_flood_clear(old); @@ -265,6 +273,22 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) lsa->installed = now; ospf6_lsdb_add(lsa, lsa->lsdb); + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) { + area = OSPF6_AREA(lsa->lsdb->data); + ospf6_translated_nssa_refresh(area, lsa, NULL); + ospf6_schedule_abr_task(area->ospf6); + } + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_ROUTER) { + area = OSPF6_AREA(lsa->lsdb->data); + if (old == NULL) { + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type) + || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) + zlog_debug("%s: New router LSA %s", __func__, + lsa->name); + ospf6_abr_nssa_check_status(area->ospf6); + } + } return; } @@ -370,7 +394,8 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, continue; } - if (oi->area->ospf6->inst_shutdown) { + if ((oi->area->ospf6->inst_shutdown) + || CHECK_FLAG(lsa->flag, OSPF6_LSA_FLUSH)) { if (is_debug) zlog_debug( "%s: Send LSA %s (age %d) update now", @@ -486,7 +511,12 @@ static void ospf6_flood_process(struct ospf6_neighbor *from, continue; if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL - && IS_AREA_STUB(oa)) + && (IS_AREA_STUB(oa) || IS_AREA_NSSA(oa))) + continue; + + /* Check for NSSA LSA */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && !IS_AREA_NSSA(oa) && !OSPF6_LSA_IS_MAXAGE(lsa)) continue; ospf6_flood_area(from, lsa, oa); @@ -526,7 +556,7 @@ static void ospf6_flood_clear_interface(struct ospf6_lsa *lsa, } } -static void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa) +void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa) { struct listnode *node, *nnode; struct ospf6_interface *oi; @@ -555,7 +585,11 @@ static void ospf6_flood_clear_process(struct ospf6_lsa *lsa, continue; if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL - && IS_AREA_STUB(oa)) + && (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa)))) + continue; + /* Check for NSSA LSA */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && !IS_AREA_NSSA(oa)) continue; ospf6_flood_clear_area(lsa, oa); @@ -567,6 +601,8 @@ void ospf6_flood_clear(struct ospf6_lsa *lsa) struct ospf6 *ospf6; ospf6 = ospf6_get_by_lsdb(lsa); + if (ospf6 == NULL) + return; ospf6_flood_clear_process(lsa, ospf6); } diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h index 6931024fff..5515a1c3fe 100644 --- a/ospf6d/ospf6_flood.h +++ b/ospf6d/ospf6_flood.h @@ -67,4 +67,6 @@ extern void ospf6_flood_interface(struct ospf6_neighbor *from, extern int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on, struct ospf6_lsa *lsa); +extern void ospf6_flood_clear_area(struct ospf6_lsa *lsa, + struct ospf6_area *oa); #endif /* OSPF6_FLOOD_H */ diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index f037ea1f4d..b71d884fdc 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1197,6 +1197,26 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, return 0; } +/* Find the global address to be used as a forwarding address in NSSA LSA.*/ +struct in6_addr *ospf6_interface_get_global_address(struct interface *ifp) +{ + struct listnode *n; + struct connected *c; + struct in6_addr *l = (struct in6_addr *)NULL; + + /* for each connected address */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + /* if family not AF_INET6, ignore */ + if (c->address->family != AF_INET6) + continue; + + if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + l = &c->address->u.prefix6; + } + return l; +} + + static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id, int argc, struct cmd_token **argv, int idx_ifname, int intf_idx, diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 48b2cbff74..fb1b947cf8 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -189,6 +189,8 @@ extern void ospf6_interface_if_add(struct interface *); extern void ospf6_interface_state_update(struct interface *); extern void ospf6_interface_connected_route_update(struct interface *); extern void ospf6_interface_connected_route_add(struct connected *); +extern struct in6_addr * +ospf6_interface_get_global_address(struct interface *ifp); /* interface event */ extern int interface_up(struct thread *); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 12d11d45c1..68ff7548cb 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -193,6 +193,17 @@ static void ospf6_router_lsa_options_set(struct ospf6_area *oa, UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E); } + /* If the router is ASBR and the area-type is NSSA set the + * translate bit in router LSA. + */ + if (IS_AREA_NSSA(oa) + && (ospf6_asbr_is_asbr(oa->ospf6) || IS_OSPF6_ABR(oa->ospf6))) { + if (oa->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS) + SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT); + } else { + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT); + } + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_V); UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_W); } @@ -2167,8 +2178,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || IS_OSPF6_DEBUG_ROUTE(MEMORY)) - zlog_info("%s: border-router calculation for area %s", __func__, - oa->name); + zlog_debug("%s: border-router calculation for area %s", + __func__, oa->name); hook_add = oa->ospf6->brouter_table->hook_add; hook_remove = oa->ospf6->brouter_table->hook_remove; @@ -2189,8 +2200,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id) || IS_OSPF6_DEBUG_ROUTE(MEMORY)) { - zlog_info("%p: mark as removing: area %s brouter %s", - (void *)brouter, oa->name, brouter_name); + zlog_debug("%p: mark as removing: area %s brouter %s", + (void *)brouter, oa->name, brouter_name); ospf6_brouter_debug_print(brouter); } } @@ -2223,8 +2234,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id) || IS_OSPF6_DEBUG_ROUTE(MEMORY)) { - zlog_info("%p: transfer: area %s brouter %s", - (void *)brouter, oa->name, brouter_name); + zlog_debug("%p: transfer: area %s brouter %s", + (void *)brouter, oa->name, brouter_name); ospf6_brouter_debug_print(brouter); } } @@ -2297,7 +2308,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) brouter_id) || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( oa->area_id)) - zlog_info( + zlog_debug( "%s: brouter %s disappears via area %s", __func__, brouter_name, oa->name); /* This is used to protect nbrouter from removed from @@ -2325,8 +2336,9 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) brouter_id) || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( oa->area_id)) - zlog_info("brouter %s still exists via area %s", - brouter_name, oa->name); + zlog_debug( + "brouter %s still exists via area %s", + brouter_name, oa->name); /* But re-originate summaries */ ospf6_abr_originate_summary(brouter, oa->ospf6); } @@ -2341,8 +2353,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || IS_OSPF6_DEBUG_ROUTE(MEMORY)) - zlog_info("%s: border-router calculation for area %s: done", - __func__, oa->name); + zlog_debug("%s: border-router calculation for area %s: done", + __func__, oa->name); } static struct ospf6_lsa_handler router_handler = { diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index f5f429b041..c97ad74981 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -458,6 +458,7 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, case OSPF6_LSTYPE_INTER_PREFIX: case OSPF6_LSTYPE_INTER_ROUTER: case OSPF6_LSTYPE_AS_EXTERNAL: + case OSPF6_LSTYPE_TYPE_7: if (use_json) { json_object_string_add( json_obj, "type", @@ -486,7 +487,6 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, case OSPF6_LSTYPE_ROUTER: case OSPF6_LSTYPE_NETWORK: case OSPF6_LSTYPE_GROUP_MEMBERSHIP: - case OSPF6_LSTYPE_TYPE_7: case OSPF6_LSTYPE_LINK: case OSPF6_LSTYPE_INTRA_PREFIX: while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt) diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 84c3b85631..15b0d4ebbc 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -80,6 +80,11 @@ #define OSPF6_SCOPE_AS 0x4000 #define OSPF6_SCOPE_RESERVED 0x6000 +/* AS-external-LSA refresh method. */ +#define LSA_REFRESH_IF_CHANGED 0 +#define LSA_REFRESH_FORCE 1 + + /* XXX U-bit handling should be treated here */ #define OSPF6_LSA_SCOPE(type) (ntohs(type) & OSPF6_LSTYPE_SCOPE_MASK) @@ -113,6 +118,7 @@ struct ospf6_lsa_header { #define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2) #define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1)) + struct ospf6_lsa { char name[64]; /* dump string */ @@ -133,6 +139,8 @@ struct ospf6_lsa { struct ospf6_lsdb *lsdb; + in_addr_t external_lsa_id; + /* lsa instance */ struct ospf6_lsa_header *header; }; @@ -143,6 +151,7 @@ struct ospf6_lsa { #define OSPF6_LSA_IMPLIEDACK 0x08 #define OSPF6_LSA_UNAPPROVED 0x10 #define OSPF6_LSA_SEQWRAPPED 0x20 +#define OSPF6_LSA_FLUSH 0x40 struct ospf6_lsa_handler { uint16_t lh_type; /* host byte order */ diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 18f121e3a2..304f03fde8 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -320,9 +320,17 @@ int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb) struct ospf6_lsa *lsa, *lsanext; for (ALL_LSDB(lsdb, lsa, lsanext)) { - if (!OSPF6_LSA_IS_MAXAGE(lsa)) + if (!OSPF6_LSA_IS_MAXAGE(lsa)) { + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) + zlog_debug("Not MaxAge %s", lsa->name); continue; + } + if (lsa->retrans_count != 0) { + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) + zlog_debug("Remove MaxAge %s retrans_count %d", + lsa->name, lsa->retrans_count); + reschedule = 1; continue; } @@ -341,6 +349,7 @@ int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb) THREAD_OFF(lsa->refresh); thread_execute(master, ospf6_lsa_refresh, lsa, 0); } else { + zlog_debug("calling ospf6_lsdb_remove %s", lsa->name); ospf6_lsdb_remove(lsa, lsdb); } } diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c new file mode 100644 index 0000000000..4d9b0a1978 --- /dev/null +++ b/ospf6d/ospf6_nssa.c @@ -0,0 +1,1396 @@ +/* + * OSPFv3 Not So Stubby Area implementation. + * + * Copyright (C) 2021 Kaushik Nath + * Copyright (C) 2021 Soman K.S + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <zebra.h> +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "linklist.h" +#include "command.h" +#include "thread.h" +#include "plist.h" +#include "filter.h" + +#include "ospf6_proto.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_route.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_zebra.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" +#include "ospf6_intra.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6d.h" +#include "ospf6_nssa.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA"); +unsigned char config_debug_ospf6_nssa = 0; +/* Determine whether this router is elected translator or not for area */ +static int ospf6_abr_nssa_am_elected(struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa; + struct ospf6_router_lsa *router_lsa; + in_addr_t *best = NULL; + uint16_t type; + + type = htons(OSPF6_LSTYPE_ROUTER); + + /* Verify all the router LSA to compare the router ID */ + for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) { + + router_lsa = (struct ospf6_router_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* ignore non-ABR routers */ + if (!CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B)) + continue; + + /* Router has Nt flag - always translate */ + if (CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: router %pI4 asserts Nt", + __func__, &lsa->header->id); + return 1; + } + + if (best == NULL) + best = &lsa->header->adv_router; + else if (IPV4_ADDR_CMP(best, &lsa->header->adv_router) < 0) + best = &lsa->header->adv_router; + } + + if (best == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: best electable ABR not found", + __func__); + return 0; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: best electable ABR is: %pI4", __func__, best); + + if (IPV4_ADDR_CMP(best, &oa->ospf6->router_id) <= 0) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: elected ABR is: %pI4", __func__, best); + return 1; + } else { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: not elected best %pI4, router ID %pI4", + __func__, best, &oa->ospf6->router_id); + return 0; + } +} + +/* Flush the translated LSA when translation is disabled */ +static void ospf6_flush_translated_lsa(struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *type7; + struct ospf6_lsa *type5; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: start area %s", __func__, area->name); + + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, type7)) { + type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + type7->external_lsa_id, + ospf6->router_id, ospf6->lsdb); + if (type5 && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) + ospf6_lsa_premature_aging(type5); + } + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: finish area %s", __func__, area->name); +} + +/* Check NSSA status for all nssa areas*/ +void ospf6_abr_nssa_check_status(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *lnode, *nnode; + + for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, nnode, area)) { + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: checking area %s flag %x", __func__, + area->name, area->flag); + + if (!IS_AREA_NSSA(area)) + continue; + + if (!CHECK_FLAG(area->ospf6->flag, OSPF6_FLAG_ABR)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: not ABR", __func__); + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_DISABLED; + ospf6_flush_translated_lsa(area); + } else { + /* Router is ABR */ + if (area->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS) + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_ENABLED; + else { + /* We are a candidate for Translation */ + if (ospf6_abr_nssa_am_elected(area) > 0) { + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_ENABLED; + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: elected translator", + __func__); + } else { + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_DISABLED; + ospf6_flush_translated_lsa(area); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: not elected", + __func__); + } + } + } + } + /* RFC3101, 3.1: + * All NSSA border routers must set the E-bit in the Type-1 + * router-LSAs of their directly attached non-stub areas, even + * when they are not translating. + */ + if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR) && (ospf6->anyNSSA)) + ospf6_asbr_status_update(ospf6, ++ospf6->redist_count); + else + ospf6_asbr_status_update(ospf6, --ospf6->redist_count); +} + +/* Mark the summary LSA's as unapproved, when ABR status changes.*/ +static void ospf6_abr_unapprove_summaries(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *area; + struct ospf6_lsa *lsa; + uint16_t type; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : considering area %pI4", __func__, + &area->area_id); + /* Inter area router LSA */ + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s : approved unset on summary link id %pI4", + __func__, &lsa->header->id); + SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + } + /* Inter area prefix LSA */ + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s : approved unset on asbr-summary link id %pI4", + __func__, &lsa->header->id); + SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + } + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Stop", __func__); +} + +/* Re-advertise inter-area router LSA's */ +void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6) +{ + struct ospf6_route *brouter; + struct listnode *node, *nnode; + struct ospf6_area *oa; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Re-examining Inter-Router prefixes"); + + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) { + for (brouter = ospf6_route_head(oa->ospf6->brouter_table); + brouter; brouter = ospf6_route_next(brouter)) + ospf6_abr_originate_summary_to_area(brouter, oa); + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Finished re-examining Inter-Router prefixes"); +} + +/* Advertise prefixes configured using area <area-id> range command */ +static void ospf6_abr_announce_aggregates(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct ospf6_route *range; + struct listnode *node, *nnode; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("ospf6_abr_announce_aggregates(): Start"); + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + for (range = ospf6_route_head(area->range_table); range; + range = ospf6_route_next(range)) + ospf6_abr_range_update(range, ospf6); + } + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "ospf_abr_announce_aggregates(): looking at area %pI4", + &area->area_id); + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("ospf6_abr_announce_aggregates(): Stop"); +} + +/* Flush the summary LSA's which are not approved.*/ +void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *area; + struct ospf6_lsa *lsa; + uint16_t type; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : looking at area %pI4", __func__, + &area->area_id); + + /* Inter area router LSA */ + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) { + lsa->header->age = htons(OSPF_LSA_MAXAGE); + THREAD_OFF(lsa->refresh); + thread_execute(master, ospf6_lsa_expire, lsa, + 0); + } + } + + /* Inter area prefix LSA */ + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) { + lsa->header->age = htons(OSPF_LSA_MAXAGE); + THREAD_OFF(lsa->refresh); + thread_execute(master, ospf6_lsa_expire, lsa, + 0); + } + } + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Stop", __func__); +} + +/* + * This is the function taking care about ABR stuff, i.e. + * summary-LSA origination and flooding. + */ +static void ospf6_abr_task(struct ospf6 *ospf6) +{ + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Start", __func__); + + if (ospf6->route_table == NULL || ospf6->brouter_table == NULL) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Routing tables are not yet ready", + __func__); + return; + } + + ospf6_abr_unapprove_summaries(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : prepare aggregates", __func__); + + ospf6_abr_range_reset_cost(ospf6); + + if (IS_OSPF6_ABR(ospf6)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : process network RT", __func__); + ospf6_abr_prefix_resummarize(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : process router RT", __func__); + ospf6_asbr_prefix_readvertise(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : announce aggregates", __func__); + ospf6_abr_announce_aggregates(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 : remove unapproved summaries", __func__); + ospf6_abr_remove_unapproved_summaries(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Stop", __func__); +} + +/* For NSSA Translations + * Mark the translated LSA's as unapproved. */ +static void ospf6_abr_unapprove_translates(struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + uint16_t type; + struct ospf6_area *oa; + struct listnode *node; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_unapprove_translates(): Start"); + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) { + SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : approved unset on link id %pI4", + __func__, &lsa->header->id); + } + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_unapprove_translates(): Stop"); +} + +/* Generate the translated external lsa from NSSA lsa */ +static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area, + struct ospf6_lsa *type7) +{ + char *buffer; + struct ospf6_lsa *lsa; + struct ospf6_as_external_lsa *ext, *extnew; + struct ospf6_lsa_header *lsa_header; + caddr_t old_ptr, new_ptr; + struct ospf6_as_external_lsa *nssa; + struct prefix prefix; + struct ospf6_route *match; + struct ospf6 *ospf6 = area->ospf6; + ptrdiff_t tag_offset = 0; + route_tag_t network_order; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Start", __func__); + + if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Translation disabled for area %s", + __func__, area->name); + return NULL; + } + + buffer = XCALLOC(MTYPE_OSPF6_LSA, OSPF6_MAX_LSASIZE); + lsa_header = (struct ospf6_lsa_header *)buffer; + extnew = (struct ospf6_as_external_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + ext = (struct ospf6_as_external_lsa + *)((caddr_t)(type7->header) + + sizeof(struct ospf6_lsa_header)); + old_ptr = + (caddr_t)((caddr_t)ext + sizeof(struct ospf6_as_external_lsa)); + new_ptr = (caddr_t)((caddr_t)extnew + + sizeof(struct ospf6_as_external_lsa)); + + memcpy(extnew, ext, sizeof(struct ospf6_as_external_lsa)); + + /* find the translated Type-5 for this Type-7 */ + nssa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + type7->header); + + prefix.family = AF_INET6; + prefix.prefixlen = nssa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa, &nssa->prefix); + + /* Find the LSA from the external route */ + match = ospf6_route_lookup(&prefix, area->route_table); + if (match == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : no matching route %pFX", __func__, + &prefix); + return NULL; + } + + /* set Prefix */ + memcpy(new_ptr, old_ptr, OSPF6_PREFIX_SPACE(ext->prefix.prefix_length)); + ospf6_prefix_apply_mask(&extnew->prefix); + new_ptr += OSPF6_PREFIX_SPACE(extnew->prefix.prefix_length); + + tag_offset = + sizeof(*ext) + OSPF6_PREFIX_SPACE(ext->prefix.prefix_length); + + /* Forwarding address */ + if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_F)) { + memcpy(new_ptr, (caddr_t)ext + tag_offset, + sizeof(struct in6_addr)); + new_ptr += sizeof(struct in6_addr); + tag_offset += sizeof(struct in6_addr); + } + /* External Route Tag */ + if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_T)) { + memcpy(&network_order, (caddr_t)ext + tag_offset, + sizeof(network_order)); + network_order = htonl(network_order); + memcpy(new_ptr, &network_order, sizeof(network_order)); + new_ptr += sizeof(network_order); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + lsa_header->id = htonl(ospf6->external_id); + ospf6->external_id++; + lsa_header->adv_router = ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, ospf6->lsdb); + lsa_header->length = htons((caddr_t)new_ptr - (caddr_t)lsa_header); + type7->external_lsa_id = lsa_header->id; + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + SET_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT); + UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + + /* Originate */ + ospf6_lsa_originate_process(lsa, ospf6); + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Originated type5 LSA id %pI4", __func__, + &lsa_header->id); + return lsa; +} + +/* Delete LSA from retransmission list */ +static void ospf6_ls_retransmit_delete_nbr_as(struct ospf6 *ospf6, + struct ospf6_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf6_area *area; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : start lsa %s", __func__, lsa->name); + + /*The function ospf6_flood_clear_area removes LSA from + * retransmit list. + */ + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) + ospf6_flood_clear_area(lsa, area); + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : finish lsa %s", __func__, lsa->name); +} + +/* Refresh translated AS-external-LSA. */ +struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *area, + struct ospf6_lsa *type7, + struct ospf6_lsa *type5) +{ + struct ospf6_lsa *new = NULL; + struct ospf6_as_external_lsa *ext_lsa; + struct prefix prefix; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : start area %s", __func__, area->name); + + /* Sanity checks. */ + assert(type7); + + /* Find the AS external LSA */ + if (type5 == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: No translated Type-5 found for Type-7 with Id %pI4", + __func__, &type7->header->id); + + /* find the translated Type-5 for this Type-7 */ + ext_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + type7->header); + + prefix.family = AF_INET6; + prefix.prefixlen = ext_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, ext_lsa, + &ext_lsa->prefix); + + /* Find the AS external LSA from Type-7 LSA */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: try to find external LSA id %d", + __func__, type7->external_lsa_id); + type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + type7->external_lsa_id, + ospf6->router_id, ospf6->lsdb); + } + + if (type5) { + if (CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) { + /* Delete LSA from neighbor retransmit-list. */ + ospf6_ls_retransmit_delete_nbr_as(ospf6, type5); + + /* Flush the LSA */ + ospf6_lsa_premature_aging(type5); + } else { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Invalid translated LSA %s", + __func__, type5->name); + return NULL; + } + } + + /* create new translated LSA */ + if (ospf6_lsa_age_current(type7) != OSPF_LSA_MAXAGE) { + if ((new = ospf6_lsa_translated_nssa_new(area, type7)) + == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: Could not translate Type-7 for %pI4", + __func__, &type7->header->id); + return NULL; + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: finish", __func__); + + return new; +} + +/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */ +struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *oa, + struct ospf6_lsa *type7) +{ + struct ospf6_lsa *new; + + if (ntohs(type7->header->type) != OSPF6_LSTYPE_TYPE_7) + return NULL; + + if ((new = ospf6_lsa_translated_nssa_new(oa, type7)) == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : Could not translate Type-7, Id %pI4, to Type-5", + __func__, &type7->header->id); + return NULL; + } + + return new; +} + +int ospf6_abr_translate_nssa(struct ospf6_area *area, struct ospf6_lsa *lsa) +{ + /* Incoming Type-7 or later aggregated Type-7 + * + * LSA is skipped if P-bit is off. + * LSA is aggregated if within range. + * + * The Type-7 is translated, Installed/Approved as a Type-5 into + * global LSDB, then Flooded through AS + * + * Later, any Unapproved Translated Type-5's are flushed/discarded + */ + + struct ospf6_lsa *old = NULL, *new = NULL; + struct ospf6_as_external_lsa *nssa_lsa; + struct prefix prefix; + struct ospf6_route *match; + struct ospf6 *ospf6; + + ospf6 = area->ospf6; + nssa_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (!CHECK_FLAG(nssa_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_P)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4, P-bit off, NO Translation", + __func__, &lsa->header->id); + return 1; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4 external ID %pI4, Translating type 7 to 5", + __func__, &lsa->header->id, &lsa->external_lsa_id); + + prefix.family = AF_INET6; + prefix.prefixlen = nssa_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa_lsa, &nssa_lsa->prefix); + + if (!CHECK_FLAG(nssa_lsa->bits_metric, OSPF6_ASBR_BIT_F)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4, Forward address is 0, NO Translation", + __func__, &lsa->header->id); + return 1; + } + + /* Find the existing AS-External LSA for this prefix */ + match = ospf6_route_lookup(&prefix, ospf6->external_table); + if (match) { + old = ospf6_lsdb_lookup(OSPF6_LSTYPE_AS_EXTERNAL, + match->path.origin.id, ospf6->router_id, + ospf6->lsdb); + } + + /* Check Type 5 LSA using the matching external ID */ + if (old == NULL) { + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + lsa->external_lsa_id, ospf6->router_id, + ospf6->lsdb); + } + + if (old) { + /* Do not continue if type 5 LSA not approved */ + if (CHECK_FLAG(old->flag, OSPF6_LSA_UNAPPROVED)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4 type 5 is not approved", + __func__, &old->header->id); + return 1; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : found old translated LSA Id %pI4, refreshing", + __func__, &old->header->id); + + /* refresh */ + new = ospf6_translated_nssa_refresh(area, lsa, old); + if (!new) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : could not refresh translated LSA Id %pI4", + __func__, &old->header->id); + } + } else { + /* no existing external route for this LSA Id + * originate translated LSA + */ + + if (ospf6_translated_nssa_originate(area, lsa) == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : Could not translate Type-7 for %pI4 to Type-5", + __func__, &lsa->header->id); + return 1; + } + } + + return 0; +} + +static void ospf6_abr_process_nssa_translates(struct ospf6 *ospf6) +{ + /* Scan through all NSSA_LSDB records for all areas; + * If P-bit is on, translate all Type-7's to 5's and aggregate or\ + * flood install as approved in Type-5 LSDB with XLATE Flag on + * later, do same for all aggregates... At end, DISCARD all + * remaining UNAPPROVED Type-5's (Aggregate is for future ) */ + + struct listnode *node; + struct ospf6_area *oa; + struct ospf6_lsa *lsa; + int type; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + + /* skip if not translator */ + if (oa->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) { + zlog_debug("%s area %pI4 NSSATranslatorState %d", + __func__, &oa->area_id, + oa->NSSATranslatorState); + continue; + } + + /* skip if not Nssa Area */ + if (!IS_AREA_NSSA(oa)) { + zlog_debug("%s area %pI4 Flag %x", __func__, + &oa->area_id, oa->flag); + continue; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : looking at area %pI4", __func__, + &oa->area_id); + + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) { + zlog_debug("%s : lsa %s , id %pI4 , adv router %pI4", + lsa->name, __func__, &lsa->header->id, + &lsa->header->adv_router); + ospf6_abr_translate_nssa(oa, lsa); + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Stop", __func__); +} + +/* Generate translated type-5 LSA from the configured area ranges*/ +static void ospf6_abr_translate_nssa_range(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6_route *range; + struct ospf6_lsa *lsa; + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) { + for (range = ospf6_route_head(oa->range_table); range; + range = ospf6_route_next(range)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "Translating range %pFX of area %pI4", + &range->prefix, &oa->area_id); + if (CHECK_FLAG(range->flag, + OSPF6_ROUTE_DO_NOT_ADVERTISE)) + continue; + + /* Find the NSSA LSA from the route */ + /* Generate and flood external LSA */ + lsa = ospf6_lsdb_lookup(OSPF6_LSTYPE_TYPE_7, + range->path.origin.id, + ospf6->router_id, oa->lsdb); + if (lsa) + ospf6_abr_translate_nssa(oa, lsa); + } + } +} + +static void ospf6_abr_send_nssa_aggregates(struct ospf6 *ospf6) +{ + struct listnode *node; + struct ospf6_area *area; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) + continue; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : looking at area %pI4", __func__, + &area->area_id); + + ospf6_abr_translate_nssa_range(ospf6); + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Stop", __func__); +} + +/*Flood max age LSA's for the unapproved LSA's */ +static int ospf6_abr_remove_unapproved_translates_apply(struct ospf6_lsa *lsa) +{ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT) + && CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) { + zlog_debug("%s : removing unapproved translates, lsa : %s", + __func__, lsa->name); + + /* FLUSH THROUGHOUT AS */ + ospf6_lsa_premature_aging(lsa); + } + return 0; +} + +static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + uint16_t type; + + /* All AREA PROCESS should have APPROVED necessary LSAs */ + /* Remove any left over and not APPROVED */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_remove_unapproved_translates(): Start"); + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa)) + ospf6_abr_remove_unapproved_translates_apply(lsa); + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf_abr_remove_unapproved_translates(): Stop"); +} + +static void ospf6_abr_nssa_task(struct ospf6 *ospf6) +{ + /* called only if any_nssa */ + struct ospf6_route *range; + struct ospf6_area *area; + struct listnode *node, *nnode; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Check for NSSA-ABR Tasks():"); + + if (!IS_OSPF6_ABR(ospf6)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s Not ABR", __func__); + return; + } + + if (!ospf6->anyNSSA) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s Not NSSA", __func__); + return; + } + + /* Each area must confirm TranslatorRole */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_nssa_task(): Start"); + + /* For all Global Entries flagged "local-translate", unset APPROVED */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_nssa_task(): unapprove translates"); + + ospf6_abr_unapprove_translates(ospf6); + + /* RESET all Ranges in every Area, same as summaries */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_nssa_task(): NSSA initialize aggregates"); + ospf6_abr_range_reset_cost(ospf6); + + /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or + * Aggregate as Type-7 + * Install or Approve in Type-5 Global LSDB + */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_nssa_task(): process translates"); + ospf6_abr_process_nssa_translates(ospf6); + + /* Translate/Send any "ranged" aggregates, and also 5-Install and + * Approve + * Scan Type-7's for aggregates, translate to Type-5's, + * Install/Flood/Approve + */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_nssa_task(): send NSSA aggregates"); + ospf6_abr_send_nssa_aggregates(ospf6); /*TURNED OFF FOR NOW */ + + /* Flush any unapproved previous translates from Global Data Base */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "ospf6_abr_nssa_task(): remove unapproved translates"); + ospf6_abr_remove_unapproved_translates(ospf6); + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + for (range = ospf6_route_head(area->range_table); range; + range = ospf6_route_next(range)) { + if (CHECK_FLAG(range->flag, + OSPF6_ROUTE_DO_NOT_ADVERTISE)) + ospf6_zebra_delete_discard(range, ospf6); + else + ospf6_zebra_add_discard(range, ospf6); + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("ospf6_abr_nssa_task(): Stop"); +} + +int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route, + int type) +{ + route_map_result_t ret; + struct prefix *prefix; + struct ospf6_redist *red; + + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) + return 0; + + prefix = &route->prefix; + + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + return 0; + + /* Change to new redist structure */ + if (ROUTEMAP_NAME(red)) { + if (ROUTEMAP(red) == NULL) + ospf6_asbr_routemap_update(NULL); + if (ROUTEMAP(red) == NULL) { + zlog_warn( + "route-map \"%s\" not found, suppress redistributing", + ROUTEMAP_NAME(red)); + return 0; + } + } + + /* Change to new redist structure */ + if (ROUTEMAP(red)) { + ret = route_map_apply(ROUTEMAP(red), prefix, route); + if (ret == RMAP_DENYMATCH) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Denied by route-map \"%s\"", + ROUTEMAP_NAME(red)); + return 0; + } + } + + return 1; +} + +static void ospf6_external_lsa_refresh_type(struct ospf6 *ospf6, uint8_t type, + unsigned short instance, int force) +{ + struct ospf6_route *route; + struct ospf6_external_info *info; + struct ospf6_lsa *lsa; + + if (type == ZEBRA_ROUTE_MAX) + return; + + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) { + info = route->route_option; + + /* Find the external LSA in the database */ + if (!is_default_prefix(&route->prefix)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), + ospf6->router_id, ospf6->lsdb); + + if (lsa) { + THREAD_OFF(lsa->refresh); + + /* LSA is maxage, immediate refresh */ + if (OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_flood(NULL, lsa); + else + thread_add_timer(master, + ospf6_lsa_refresh, lsa, + OSPF_LS_REFRESH_TIME, + &lsa->refresh); + } else { + /* LSA not found in the database + * Verify and originate external LSA + */ + if (ospf6_redistribute_check(ospf6, route, + type)) + ospf6_as_external_lsa_originate(route, + ospf6); + } + } + } +} + +/* Refresh default route */ +static void ospf6_external_lsa_refresh_default(struct ospf6 *ospf6) +{ + struct ospf6_route *route; + struct ospf6_external_info *info; + struct ospf6_lsa *lsa; + + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) { + if (is_default_prefix(&route->prefix)) { + info = route->route_option; + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), + ospf6->router_id, ospf6->lsdb); + + if (lsa) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p", + (void *)lsa); + if (OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_flood(NULL, lsa); + else + thread_add_timer(master, + ospf6_lsa_refresh, lsa, + OSPF_LS_REFRESH_TIME, + &lsa->refresh); + } else if (!lsa) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "LSA[Type5:0.0.0.0]: Originate AS-external-LSA"); + ospf6_as_external_lsa_originate(route, ospf6); + } + } + } +} + +/* If there's redistribution configured, we need to refresh external + * LSAs in order to install Type-7 and flood to all NSSA Areas + */ +void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6) +{ + int type; + struct ospf6_redist *red; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + return; + + ospf6_external_lsa_refresh_type(ospf6, type, red->instance, + LSA_REFRESH_IF_CHANGED); + } + ospf6_external_lsa_refresh_default(ospf6); +} + +/* This function performs ABR related processing */ +static int ospf6_abr_task_timer(struct thread *thread) +{ + struct ospf6 *ospf6 = THREAD_ARG(thread); + + ospf6->t_abr_task = NULL; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Running ABR task on timer"); + + ospf6_is_router_abr(ospf6); + ospf6_abr_nssa_check_status(ospf6); + ospf6_abr_task(ospf6); + /* if nssa-abr, then scan Type-7 LSDB */ + ospf6_abr_nssa_task(ospf6); + ospf6_asbr_nssa_redist_task(ospf6); + + return 0; +} + +void ospf6_schedule_abr_task(struct ospf6 *ospf6) +{ + if (ospf6->t_abr_task) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("ABR task already scheduled"); + return; + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Scheduling ABR task"); + + thread_add_timer(master, ospf6_abr_task_timer, ospf6, + OSPF6_ABR_TASK_DELAY, &ospf6->t_abr_task); +} + +/* Flush the NSSA LSAs from the area */ +static void ospf6_nssa_flush_area(struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *lsa = NULL, *type5 = NULL; + struct ospf6 *ospf6 = area->ospf6; + const struct route_node *rt = NULL; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: area %s", __func__, area->name); + + /* Flush the NSSA LSA */ + type = htons(OSPF6_LSTYPE_TYPE_7); + rt = ospf6_lsdb_head(area->lsdb_self, 0, type, ospf6->router_id, &lsa); + while (lsa) { + lsa->header->age = htons(OSPF_LSA_MAXAGE); + SET_FLAG(lsa->flag, OSPF6_LSA_FLUSH); + ospf6_flood(NULL, lsa); + /* Flush the translated LSA */ + if (ospf6_is_router_abr(ospf6)) { + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + type5 = ospf6_lsdb_lookup( + htons(type), lsa->external_lsa_id, + ospf6->router_id, ospf6->lsdb); + if (type5 + && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) { + type5->header->age = htons(OSPF_LSA_MAXAGE); + SET_FLAG(type5->flag, OSPF6_LSA_FLUSH); + ospf6_flood(NULL, type5); + } + } + lsa = ospf6_lsdb_next(rt, lsa); + } +} + +static void ospf6_area_nssa_update(struct ospf6_area *area) +{ + struct ospf6_route *route; + + if (IS_AREA_NSSA(area)) { + if (!ospf6_is_router_abr(area->ospf6)) + OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); + area->ospf6->anyNSSA++; + OSPF6_OPT_SET(area->options, OSPF6_OPT_N); + area->NSSATranslatorRole = OSPF6_NSSA_ROLE_CANDIDATE; + } else if (IS_AREA_ENABLED(area)) { + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) + zlog_debug("Normal area for if %s", area->name); + OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_N); + if (ospf6_is_router_abr(area->ospf6)) + OSPF6_OPT_SET(area->options, OSPF6_OPT_E); + area->ospf6->anyNSSA--; + area->NSSATranslatorState = OSPF6_NSSA_TRANSLATE_DISABLED; + } + + /* Refresh router LSA */ + if (IS_AREA_NSSA(area)) { + OSPF6_ROUTER_LSA_SCHEDULE(area); + + /* Check if router is ABR */ + if (ospf6_is_router_abr(area->ospf6)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Router is ABR area %s", area->name); + ospf6_schedule_abr_task(area->ospf6); + } else { + /* Router is not ABR */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("NSSA area %s", area->name); + + /* Originate NSSA LSA */ + for (route = ospf6_route_head( + area->ospf6->external_table); + route; route = ospf6_route_next(route)) + ospf6_nssa_lsa_originate(route, area); + } + } else { + /* Disable NSSA */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Normal area %s", area->name); + ospf6_nssa_flush_area(area); + ospf6_area_disable(area); + ospf6_area_delete(area); + } +} + +int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area) +{ + + if (!IS_AREA_NSSA(area)) { + SET_FLAG(area->flag, OSPF6_AREA_NSSA); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("area %s nssa set", area->name); + ospf6_area_nssa_update(area); + } + + return 1; +} + +int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area) +{ + if (IS_AREA_NSSA(area)) { + UNSET_FLAG(area->flag, OSPF6_AREA_NSSA); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("area %s nssa reset", area->name); + ospf6_area_nssa_update(area); + } + + return 1; +} + +/* Find the NSSA forwarding address */ +static struct in6_addr *ospf6_get_nssa_fwd_addr(struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + if (if_is_operative(oi->interface)) + if (oi->area && IS_AREA_NSSA(oi->area)) + return ospf6_interface_get_global_address( + oi->interface); + } + return NULL; +} + +void ospf6_nssa_lsa_originate(struct ospf6_route *route, + struct ospf6_area *area) +{ + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa; + struct ospf6_external_info *info = route->route_option; + struct in6_addr *fwd_addr; + + struct ospf6_as_external_lsa *as_external_lsa; + char buf[PREFIX2STR_BUFFER]; + caddr_t p; + + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("Originate AS-External-LSA for %s", buf); + } + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + as_external_lsa = (struct ospf6_as_external_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + p = (caddr_t)((caddr_t)as_external_lsa + + sizeof(struct ospf6_as_external_lsa)); + + /* Fill AS-External-LSA */ + /* Metric type */ + if (route->path.metric_type == OSPF6_PATH_TYPE_EXTERNAL2) + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + + /* external route tag */ + if (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); + + /* Set metric */ + OSPF6_ASBR_METRIC_SET(as_external_lsa, route->path.cost); + + /* prefixlen */ + as_external_lsa->prefix.prefix_length = route->prefix.prefixlen; + + /* PrefixOptions */ + as_external_lsa->prefix.prefix_options = route->path.prefix_options; + + /* Set the 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); + + /* set Prefix */ + memcpy(p, &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(route->prefix.prefixlen)); + ospf6_prefix_apply_mask(&as_external_lsa->prefix); + p += OSPF6_PREFIX_SPACE(route->prefix.prefixlen); + + /* Forwarding address */ + fwd_addr = ospf6_get_nssa_fwd_addr(area); + if (fwd_addr) { + memcpy(p, fwd_addr, sizeof(struct in6_addr)); + p += sizeof(struct in6_addr); + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + } else + 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)) { + route_tag_t network_order = htonl(info->tag); + + memcpy(p, &network_order, sizeof(network_order)); + p += sizeof(network_order); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_TYPE_7); + lsa_header->id = route->path.origin.id; + lsa_header->adv_router = area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, area->ospf6->lsdb); + lsa_header->length = htons((caddr_t)p - (caddr_t)lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, area); +} + +void ospf6_abr_check_translate_nssa(struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *type5 = NULL; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : start", __func__); + + type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + lsa->external_lsa_id, ospf6->router_id, + ospf6->lsdb); + + if (ospf6_is_router_abr(ospf6) && (type5 == NULL)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Originating type5 LSA", __func__); + ospf6_lsa_translated_nssa_new(area, lsa); + } +} + +DEFUN(debug_ospf6_nssa, debug_ospf6_nssa_cmd, + "debug ospf6 nssa", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 NSSA function\n") +{ + OSPF6_DEBUG_NSSA_ON(); + return CMD_SUCCESS; +} + +DEFUN(no_debug_ospf6_nssa, no_debug_ospf6_nssa_cmd, + "no debug ospf6 nssa", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 NSSA function\n") +{ + OSPF6_DEBUG_NSSA_OFF(); + return CMD_SUCCESS; +} + +void config_write_ospf6_debug_nssa(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_NSSA) + vty_out(vty, "debug ospf6 nssa\n"); +} + +void install_element_ospf6_debug_nssa(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_nssa_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_nssa_cmd); + install_element(CONFIG_NODE, &debug_ospf6_nssa_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_nssa_cmd); +} diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h new file mode 100644 index 0000000000..a171d76d44 --- /dev/null +++ b/ospf6d/ospf6_nssa.h @@ -0,0 +1,76 @@ +/* + * OSPFv3 Not So Stubby Area implementation. + * + * Copyright (C) 2021 Kaushik Nath + * Copyright (C) 2021 Soman K.S + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef OSPF6_NSSA_H +#define OSPF6_NSSA_H + +#define OSPF6_OPTION_NP 0x08 +#define OSPF6_LS_INFINITY 0xffffff + +#define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */ + +#define OSPF6_DEBUG_NSSA 0x09 +/* Debug option */ +extern unsigned char config_debug_ospf6_nssa; + +#define OSPF6_DEBUG_NSSA_ON() (config_debug_ospf6_nssa = 1) +#define OSPF6_DEBUG_NSSA_OFF() (config_debug_ospf6_nssa = 0) +#define IS_OSPF6_DEBUG_NSSA config_debug_ospf6_nssa + +#define CHECK_LSA_TYPE_1_TO_5_OR_7(type) \ + ((type == OSPF6_ROUTER_LSA_MIN_SIZE) \ + || (type == OSPF6_NETWORK_LSA_MIN_SIZE) \ + || (type == OSPF6_LINK_LSA_MIN_SIZE) \ + || (type == OSPF6_INTRA_PREFIX_LSA_MIN_SIZE) \ + || (type == OSPF6_AS_NSSA_LSA)) + +#define OSPF6_LSA_APPROVED 0x08 +#define OSPF6_LSA_LOCAL_XLT 0x40 + +#define OSPF6_ABR_TASK_DELAY 7 + +int ospf6_area_nssa_no_summary_set(struct ospf6 *ospf6, struct in_addr area_id); +int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area); +int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area); + +extern void ospf6_nssa_lsa_flush(struct ospf6 *ospf6, struct prefix_ipv6 *p); +extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *, + struct ospf6_lsa *, + struct ospf6_lsa *); +extern struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *, + struct ospf6_lsa *); + +extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6); + +extern void ospf6_schedule_abr_task(struct ospf6 *ospf6); +void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6); +extern void ospf6_nssa_lsa_originate(struct ospf6_route *route, + struct ospf6_area *area); +extern void install_element_ospf6_debug_nssa(void); +int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route, + int type); +extern int ospf6_abr_translate_nssa(struct ospf6_area *area, + struct ospf6_lsa *lsa); +extern void ospf6_abr_check_translate_nssa(struct ospf6_area *area, + struct ospf6_lsa *lsa); +extern void ospf6_abr_nssa_check_status(struct ospf6 *ospf6); +extern void config_write_ospf6_debug_nssa(struct vty *vty); +#endif /* OSPF6_NSSA_H */ diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index da6b270e01..b98dc38b72 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -35,6 +35,8 @@ #define OSPF6_ROUTER_BIT_V (1 << 2) #define OSPF6_ROUTER_BIT_E (1 << 1) #define OSPF6_ROUTER_BIT_B (1 << 0) +#define OSPF6_ROUTER_BIT_NT (1 << 4) + /* OSPF options */ /* present in HELLO, DD, LSA */ diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 7652d71c59..1d32844fec 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -37,11 +37,13 @@ #include "ospf6_area.h" #include "ospf6_proto.h" #include "ospf6_abr.h" +#include "ospf6_asbr.h" #include "ospf6_spf.h" #include "ospf6_intra.h" #include "ospf6_interface.h" #include "ospf6d.h" #include "ospf6_abr.h" +#include "ospf6_nssa.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex"); @@ -640,6 +642,9 @@ static int ospf6_spf_calculation_thread(struct thread *t) areas_processed++; } + /* External LSA calculation */ + ospf6_ase_calculate_timer_add(ospf6); + if (ospf6_is_router_abr(ospf6)) ospf6_abr_defaults_to_stub(ospf6); @@ -1105,3 +1110,183 @@ void ospf6_remove_temp_router_lsa(struct ospf6_area *area) ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb); } } + +int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_area *area) +{ + struct ospf6_route *route, *old; + struct ospf6_as_external_lsa *external; + struct prefix prefix; + void (*hook_add)(struct ospf6_route *) = NULL; + void (*hook_remove)(struct ospf6_route *) = NULL; + + assert(lsa); + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : start", __func__); + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: Processing Type-7", __func__); + + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: Rejecting Local translated LSA", + __func__); + return 0; + } + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + prefix.family = AF_INET6; + prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix); + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) { + hook_add = ospf6->route_table->hook_add; + hook_remove = ospf6->route_table->hook_remove; + ospf6->route_table->hook_add = NULL; + ospf6->route_table->hook_remove = NULL; + + old = ospf6_route_lookup(&prefix, ospf6->route_table); + if (old) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : remove external route %pFX", + __func__, &prefix); + old->flag = OSPF6_ROUTE_REMOVE; + } + + if (!OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_asbr_lsa_add(lsa); + + ospf6->route_table->hook_add = hook_add; + ospf6->route_table->hook_remove = hook_remove; + + route = ospf6_route_lookup(&prefix, ospf6->route_table); + if (route == NULL) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : no external route %pFX", + __func__, &prefix); + return 0; + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) + && (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || (old == route))) { + UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) + ospf6_route_remove(route, ospf6->route_table); + else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) { + if (hook_add) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + "%s: add external route %pFX", + __func__, &prefix); + (*hook_add)(route); + } + route->flag = 0; + } + } else if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) { + hook_add = area->route_table->hook_add; + hook_remove = area->route_table->hook_remove; + area->route_table->hook_add = NULL; + area->route_table->hook_remove = NULL; + + old = ospf6_route_lookup(&prefix, area->route_table); + if (old) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + "%s: set remove flag nssa route %pFX, area %s", + __func__, &prefix, area->name); + old->flag = OSPF6_ROUTE_REMOVE; + } + + if (!OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_asbr_lsa_add(lsa); + + area->route_table->hook_add = hook_add; + area->route_table->hook_remove = hook_remove; + + route = ospf6_route_lookup(&prefix, area->route_table); + if (route == NULL) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : no route %pFX, area %s", + __func__, &prefix, area->name); + return 0; + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) + && (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || (old == route))) { + UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : remove route %pFX, area %s", + __func__, &prefix, area->name); + ospf6_route_remove(route, area->route_table); + } else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) { + if (hook_add) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + "%s: add nssa route %pFX, area %s", + __func__, &prefix, area->name); + (*hook_add)(route); + } + ospf6_abr_check_translate_nssa(area, lsa); + route->flag = 0; + } + } + return 0; +} + +static int ospf6_ase_calculate_timer(struct thread *t) +{ + struct ospf6 *ospf6; + struct ospf6_lsa *lsa; + struct listnode *node, *nnode; + struct ospf6_area *area; + uint16_t type; + + ospf6 = THREAD_ARG(t); + ospf6->t_ase_calc = NULL; + + /* Calculate external route for each AS-external-LSA */ + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa)) + ospf6_ase_calculate_route(ospf6, lsa, NULL); + + /* This version simple adds to the table all NSSA areas */ + if (ospf6->anyNSSA) { + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : looking at area %s", __func__, + area->name); + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) { + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) + ospf6_ase_calculate_route(ospf6, lsa, + area); + } + } + } + return 0; +} + +void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6) +{ + if (ospf6 == NULL) + return; + + thread_add_timer(master, ospf6_ase_calculate_timer, ospf6, + OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc); +} diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index 4660bfd05d..1ccad57b62 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -35,7 +35,9 @@ extern unsigned char conf_debug_ospf6_spf; #define IS_OSPF6_DEBUG_SPF(level) \ (conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level) -PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue); +#define OSPF6_ASE_CALC_INTERVAL 1 + +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue) /* Transit Vertex */ struct ospf6_vertex { /* type of this vertex */ @@ -161,5 +163,7 @@ extern struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, struct ospf6_lsdb *lsdb, uint32_t adv_router); extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area); - +extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6); +extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_area *area); #endif /* OSPF6_SPF_H */ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 42405ca35e..33b5dd1960 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -52,6 +52,7 @@ #include "ospf6_spf.h" #include "ospf6d.h" #include "lib/json.h" +#include "ospf6_nssa.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_TOP, "OSPF6 top"); @@ -259,7 +260,15 @@ static void ospf6_top_lsdb_hook_remove(struct ospf6_lsa *lsa) static void ospf6_top_route_hook_add(struct ospf6_route *route) { - struct ospf6 *ospf6 = route->table->scope; + struct ospf6 *ospf6 = NULL; + struct ospf6_area *oa = NULL; + + if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL) + ospf6 = route->table->scope; + else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) { + oa = (struct ospf6_area *)route->table->scope; + ospf6 = oa->ospf6; + } ospf6_abr_originate_summary(route, ospf6); ospf6_zebra_route_update_add(route, ospf6); @@ -267,7 +276,15 @@ static void ospf6_top_route_hook_add(struct ospf6_route *route) static void ospf6_top_route_hook_remove(struct ospf6_route *route) { - struct ospf6 *ospf6 = route->table->scope; + struct ospf6 *ospf6 = NULL; + struct ospf6_area *oa = NULL; + + if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL) + ospf6 = route->table->scope; + else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) { + oa = (struct ospf6_area *)route->table->scope; + ospf6 = oa->ospf6; + } route->flag |= OSPF6_ROUTE_REMOVE; ospf6_abr_originate_summary(route, ospf6); @@ -917,8 +934,10 @@ DEFUN (ospf6_interface_area, ospf6_interface_enable(oi); /* If the router is ABR, originate summary routes */ - if (ospf6_is_router_abr(ospf6)) + if (ospf6_is_router_abr(ospf6)) { ospf6_abr_enable_area(oa); + ospf6_schedule_abr_task(oa->ospf6); + } return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 238d6a40ce..e4dfebe1de 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -95,6 +95,8 @@ struct ospf6 { struct list *redist[ZEBRA_ROUTE_MAX + 1]; uint8_t flag; +#define OSPF6_FLAG_ABR 0x04 +#define OSPF6_FLAG_ASBR 0x08 int redistribute; /* Num of redistributed protocols. */ @@ -145,14 +147,17 @@ struct ospf6 { * to support ECMP. */ uint16_t max_multipath; + /* Count of NSSA areas */ + uint8_t anyNSSA; + struct thread *t_abr_task; /* ABR task timer. */ + uint32_t redist_count; QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf6); #define OSPF6_DISABLED 0x01 #define OSPF6_STUB_ROUTER 0x02 -#define OSPF6_FLAG_ASBR 0x04 #define OSPF6_MAX_IF_ADDRS 100 #define OSPF6_MAX_IF_ADDRS_JUMBO 200 #define OSPF6_DEFAULT_MTU 1500 diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index da8c695f65..65f0aa664e 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -46,6 +46,7 @@ #include "ospf6d.h" #include "ospf6_bfd.h" #include "lib/json.h" +#include "ospf6_nssa.h" DEFINE_MGROUP(OSPF6D, "ospf6d"); @@ -94,6 +95,7 @@ static int config_write_ospf6_debug(struct vty *vty) config_write_ospf6_debug_asbr(vty); config_write_ospf6_debug_abr(vty); config_write_ospf6_debug_flood(vty); + config_write_ospf6_debug_nssa(vty); return 0; } @@ -153,6 +155,8 @@ static uint16_t parse_type_spec(int idx_lsa, int argc, struct cmd_token **argv) type = htons(OSPF6_LSTYPE_INTER_PREFIX); else if (strmatch(argv[idx_lsa]->text, "link")) type = htons(OSPF6_LSTYPE_LINK); + else if (strmatch(argv[idx_lsa]->text, "type-7")) + type = htons(OSPF6_LSTYPE_TYPE_7); } return type; @@ -1419,6 +1423,7 @@ void ospf6_init(struct thread_master *master) install_element_ospf6_debug_asbr(); install_element_ospf6_debug_abr(); install_element_ospf6_debug_flood(); + install_element_ospf6_debug_nssa(); install_element_ospf6_clear_interface(); diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 00388afd38..2e1891be71 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -6,6 +6,7 @@ if OSPF6D noinst_LIBRARIES += ospf6d/libospf6.a sbin_PROGRAMS += ospf6d/ospf6d vtysh_scan += \ + ospf6d/ospf6_nssa.c \ ospf6d/ospf6_abr.c \ ospf6d/ospf6_asbr.c \ ospf6d/ospf6_area.c \ @@ -30,6 +31,7 @@ man8 += $(MANBUILD)/frr-ospf6d.8 endif ospf6d_libospf6_a_SOURCES = \ + ospf6d/ospf6_nssa.c \ ospf6d/ospf6_abr.c \ ospf6d/ospf6_area.c \ ospf6d/ospf6_asbr.c \ @@ -53,6 +55,7 @@ ospf6d_libospf6_a_SOURCES = \ # end noinst_HEADERS += \ + ospf6d/ospf6_nssa.h \ ospf6d/ospf6_abr.h \ ospf6d/ospf6_area.h \ ospf6d/ospf6_asbr.h \ |
