summaryrefslogtreecommitdiffstats
path: root/ospf6d/ospf6_asbr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ospf6d/ospf6_asbr.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ospf6d/ospf6_asbr.c')
-rw-r--r--ospf6d/ospf6_asbr.c3723
1 files changed, 3723 insertions, 0 deletions
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
new file mode 100644
index 0000000..99c3e16
--- /dev/null
+++ b/ospf6d/ospf6_asbr.c
@@ -0,0 +1,3723 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2003 Yasuhiro Ohara
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "vty.h"
+#include "routemap.h"
+#include "table.h"
+#include "plist.h"
+#include "frrevent.h"
+#include "linklist.h"
+#include "lib/northbound_cli.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_route.h"
+#include "ospf6_zebra.h"
+#include "ospf6_message.h"
+#include "ospf6_spf.h"
+
+#include "ospf6_top.h"
+#include "ospf6d.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_asbr.h"
+#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"
+#include "ospf6_gr.h"
+#include "lib/json.h"
+
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_RT_AGGR, "OSPF6 ASBR Summarisation");
+
+static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type);
+static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
+ struct ospf6_redist *red, int type);
+
+#include "ospf6d/ospf6_asbr_clippy.c"
+
+unsigned char conf_debug_ospf6_asbr = 0;
+
+#define ZROUTE_NAME(x) zebra_route_string(x)
+
+/* Originate Type-5 and Type-7 LSA */
+static struct ospf6_lsa *ospf6_originate_type5_type7_lsas(
+ struct ospf6_route *route,
+ struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ struct listnode *lnode;
+ struct ospf6_area *oa = NULL;
+
+ lsa = ospf6_as_external_lsa_originate(route, ospf6);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ if (IS_AREA_NSSA(oa))
+ ospf6_nssa_lsa_originate(route, oa, true);
+ }
+
+ return lsa;
+}
+
+/* AS External LSA origination */
+struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6)
+{
+ char buffer[OSPF6_MAX_LSASIZE];
+ struct ospf6_lsa_header *lsa_header;
+ struct ospf6_lsa *lsa;
+ struct ospf6_external_info *info = route->route_option;
+
+ struct ospf6_as_external_lsa *as_external_lsa;
+ caddr_t p;
+
+ if (ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return NULL;
+ }
+
+ if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL))
+ zlog_debug("Originate AS-External-LSA for %pFX",
+ &route->prefix);
+
+ /* 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 == 2)
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+ else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+
+ /* forwarding address */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&info->forwarding))
+ 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 (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->prefix_options;
+
+ /* 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 */
+ if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F)) {
+ memcpy(p, &info->forwarding, sizeof(struct in6_addr));
+ p += sizeof(struct in6_addr);
+ }
+
+ /* 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_AS_EXTERNAL);
+ lsa_header->id = route->path.origin.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)p - (caddr_t)lsa_header);
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+
+ /* create LSA */
+ lsa = ospf6_lsa_create(lsa_header);
+
+ /* Originate */
+ ospf6_lsa_originate_process(lsa, ospf6);
+
+ return lsa;
+}
+
+void ospf6_orig_as_external_lsa(struct event *thread)
+{
+ struct ospf6_interface *oi;
+ struct ospf6_lsa *lsa;
+ uint32_t type, adv_router;
+
+ oi = (struct ospf6_interface *)EVENT_ARG(thread);
+
+ if (oi->state == OSPF6_INTERFACE_DOWN)
+ return;
+ if (IS_AREA_NSSA(oi->area) || IS_AREA_STUB(oi->area))
+ return;
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ adv_router = oi->area->ospf6->router_id;
+ for (ALL_LSDB_TYPED_ADVRTR(oi->area->ospf6->lsdb, type, adv_router,
+ lsa)) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug(
+ "%s: Send update of AS-External LSA %s seq 0x%x",
+ __func__, lsa->name,
+ ntohl(lsa->header->seqnum));
+
+ ospf6_flood_interface(NULL, lsa, oi);
+ }
+}
+
+static route_tag_t ospf6_as_external_lsa_get_tag(struct ospf6_lsa *lsa)
+{
+ struct ospf6_as_external_lsa *external;
+ ptrdiff_t tag_offset;
+ route_tag_t network_order;
+
+ if (!lsa)
+ return 0;
+
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+
+ if (!CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T))
+ return 0;
+
+ tag_offset = sizeof(*external)
+ + OSPF6_PREFIX_SPACE(external->prefix.prefix_length);
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F))
+ tag_offset += sizeof(struct in6_addr);
+
+ memcpy(&network_order, (caddr_t)external + tag_offset,
+ sizeof(network_order));
+ return ntohl(network_order);
+}
+
+void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
+ struct ospf6_route *route,
+ struct ospf6 *ospf6)
+{
+ struct ospf6_route *old_route, *next_route;
+ struct ospf6_path *ecmp_path, *o_path = NULL;
+ struct listnode *anode, *anext;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ bool route_found = false;
+
+ /* check for old entry match with new route origin,
+ * delete old entry.
+ */
+ for (old_route = old; old_route; old_route = next_route) {
+ bool route_updated = false;
+
+ next_route = old_route->next;
+
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
+ */
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if (old_route->path.type != route->path.type)
+ continue;
+
+ /* Current and New route has same origin,
+ * delete old entry.
+ */
+ for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext,
+ o_path)) {
+ /* Check old route path and route has same
+ * origin.
+ */
+ if (o_path->area_id != route->path.area_id
+ || !ospf6_ls_origin_same(o_path, &route->path))
+ continue;
+
+ /* Cost is not same then delete current path */
+ if ((o_path->cost == route->path.cost)
+ && (o_path->u.cost_e2 == route->path.u.cost_e2))
+ continue;
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ zlog_debug(
+ "%s: route %pFX cost old %u new %u is not same, replace route",
+ __func__, &old_route->prefix, o_path->cost,
+ route->path.cost);
+ }
+
+ /* Remove selected current rout path's nh from
+ * effective nh list.
+ */
+ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(old_route->nh_list,
+ rnode, rnext, rnh)) {
+ if (!ospf6_nexthop_is_same(rnh, nh))
+ continue;
+ listnode_delete(old_route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(rnh);
+ }
+ }
+
+ listnode_delete(old_route->paths, o_path);
+ ospf6_path_free(o_path);
+ route_updated = true;
+
+ /* Current route's path (adv_router info) is similar
+ * to route being added.
+ * Replace current route's path with paths list head.
+ * Update FIB with effective NHs.
+ */
+ if (listcount(old_route->paths)) {
+ for (ALL_LIST_ELEMENTS(old_route->paths,
+ anode, anext, o_path)) {
+ ospf6_merge_nexthops(
+ old_route->nh_list,
+ o_path->nh_list);
+ }
+ /* Update RIB/FIB with effective
+ * nh_list
+ */
+ if (ospf6->route_table->hook_add)
+ (*ospf6->route_table->hook_add)(
+ old_route);
+
+ if (old_route->path.origin.id
+ == route->path.origin.id
+ && old_route->path.origin.adv_router
+ == route->path.origin
+ .adv_router) {
+ struct ospf6_path *h_path;
+
+ h_path = (struct ospf6_path *)
+ listgetdata(listhead(
+ old_route->paths));
+ old_route->path.origin.type =
+ h_path->origin.type;
+ old_route->path.origin.id =
+ h_path->origin.id;
+ old_route->path.origin.adv_router =
+ h_path->origin.adv_router;
+ }
+ } else {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ zlog_debug(
+ "%s: route %pFX old cost %u new cost %u, delete old entry.",
+ __func__, &old_route->prefix,
+ old_route->path.cost,
+ route->path.cost);
+ }
+ if (old == old_route)
+ old = next_route;
+ ospf6_route_remove(old_route,
+ ospf6->route_table);
+ }
+ }
+ if (route_updated)
+ break;
+ }
+
+ /* Add new route */
+ for (old_route = old; old_route; old_route = old_route->next) {
+
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
+ */
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if (old_route->path.type != route->path.type)
+ continue;
+
+ /* Old Route and New Route have Equal Cost, Merge NHs */
+ if ((old_route->path.cost == route->path.cost)
+ && (old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ zlog_debug(
+ "%s: old route %pFX path cost %u e2 %u",
+ __func__, &old_route->prefix,
+ old_route->path.cost,
+ old_route->path.u.cost_e2);
+ }
+ route_found = true;
+ /* check if this path exists already in
+ * route->paths list, if so, replace nh_list
+ * from asbr_entry.
+ */
+ for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
+ o_path)) {
+ if (o_path->area_id == route->path.area_id
+ && ospf6_ls_origin_same(o_path, &route->path))
+ break;
+ }
+ /* If path is not found in old_route paths's list,
+ * add a new path to route paths list and merge
+ * nexthops in route->path->nh_list.
+ * Otherwise replace existing path's nh_list.
+ */
+ if (o_path == NULL) {
+ ecmp_path = ospf6_path_dup(&route->path);
+
+ /* Add a nh_list to new ecmp path */
+ ospf6_copy_nexthops(ecmp_path->nh_list,
+ route->nh_list);
+
+ /* Add the new path to route's path list */
+ listnode_add_sort(old_route->paths, ecmp_path);
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ zlog_debug(
+ "%s: route %pFX another path added with nh %u, effective paths %u nh %u",
+ __func__, &route->prefix,
+ listcount(ecmp_path->nh_list),
+ old_route->paths ? listcount(
+ old_route->paths)
+ : 0,
+ listcount(old_route->nh_list));
+ }
+ } else {
+ list_delete_all_node(o_path->nh_list);
+ ospf6_copy_nexthops(o_path->nh_list,
+ route->nh_list);
+ }
+
+ /* Reset nexthop lists, rebuild from brouter table
+ * for each adv. router.
+ */
+ list_delete_all_node(old_route->nh_list);
+
+ for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
+ o_path)) {
+ struct ospf6_route *asbr_entry;
+
+ asbr_entry = ospf6_route_lookup(
+ &o_path->ls_prefix,
+ ospf6->brouter_table);
+ if (asbr_entry == NULL) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "%s: ls_prfix %pFX asbr_entry not found.",
+ __func__,
+ &old_route->prefix);
+ continue;
+ }
+ ospf6_route_merge_nexthops(old_route,
+ asbr_entry);
+ }
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "%s: route %pFX with effective paths %u nh %u",
+ __func__, &route->prefix,
+ old_route->paths
+ ? listcount(old_route->paths)
+ : 0,
+ old_route->nh_list
+ ? listcount(old_route->nh_list)
+ : 0);
+
+ /* Update RIB/FIB */
+ if (ospf6->route_table->hook_add)
+ (*ospf6->route_table->hook_add)(old_route);
+
+ /* Delete the new route its info added to existing
+ * route.
+ */
+ ospf6_route_delete(route);
+
+ break;
+ }
+ }
+
+ if (!route_found) {
+ /* Add new route to existing node in ospf6 route table. */
+ ospf6_route_add(route, ospf6->route_table);
+ }
+}
+
+/* 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 = 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);
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("Calculate AS-External route for %s", lsa->name);
+
+ ospf6 = ospf6_get_by_lsdb(lsa);
+
+ if (lsa->header->adv_router == ospf6->router_id) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("Ignore self-originated AS-External-LSA");
+ return;
+ }
+
+ if (OSPF6_ASBR_METRIC(external) == OSPF_LS_INFINITY) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("Ignore LSA with LSInfinity Metric");
+ return;
+ }
+
+ if (CHECK_FLAG(external->prefix.prefix_options,
+ OSPF6_PREFIX_OPTION_NU)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("Ignore LSA with NU bit set Metric");
+ return;
+ }
+
+ 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) {
+ 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;
+ }
+
+ /*
+ * RFC 3101 - Section 2.5:
+ * "For a Type-7 LSA the matching routing table entry must
+ * specify an intra-area path through the LSA's originating
+ * NSSA".
+ */
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
+ && (asbr_entry->path.area_id != oa->area_id
+ || asbr_entry->path.type != OSPF6_PATH_TYPE_INTRA)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "Intra-area route to NSSA ASBR not found: %pFX",
+ &asbr_id);
+ return;
+ }
+ }
+
+ /*
+ * RFC 3101 - Section 2.5:
+ * "If the destination is a Type-7 default route (destination ID =
+ * DefaultDestination) and one of the following is true, then do
+ * nothing with this LSA and consider the next in the list:
+ *
+ * o The calculating router is a border router and the LSA has
+ * its P-bit clear. Appendix E describes a technique
+ * whereby an NSSA border router installs a Type-7 default
+ * LSA without propagating it.
+ *
+ * o The calculating router is a border router and is
+ * suppressing the import of summary routes as Type-3
+ * summary-LSAs".
+ */
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
+ && external->prefix.prefix_length == 0
+ && CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR)
+ && (CHECK_FLAG(external->prefix.prefix_options,
+ OSPF6_PREFIX_OPTION_P)
+ || oa->no_summary)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("Skipping Type-7 default route");
+ 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(fwd_addr));
+ fwd_addr.family = AF_INET6;
+ fwd_addr.prefixlen = IPV6_MAX_BITLEN;
+ 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(ospf6);
+ route->type = OSPF6_DEST_TYPE_NETWORK;
+ route->prefix.family = AF_INET6;
+ route->prefix.prefixlen = external->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&route->prefix.u.prefix6, external,
+ &external->prefix);
+ route->prefix_options = external->prefix.prefix_options;
+
+ route->path.area_id = asbr_entry->path.area_id;
+ route->path.origin.type = lsa->header->type;
+ route->path.origin.id = lsa->header->id;
+ route->path.origin.adv_router = lsa->header->adv_router;
+ memcpy(&route->path.ls_prefix, &asbr_id, sizeof(struct prefix));
+
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
+ route->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
+ route->path.metric_type = 2;
+ route->path.cost = asbr_entry->path.cost;
+ route->path.u.cost_e2 = OSPF6_ASBR_METRIC(external);
+ } else {
+ route->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
+ route->path.metric_type = 1;
+ route->path.cost =
+ asbr_entry->path.cost + OSPF6_ASBR_METRIC(external);
+ route->path.u.cost_e2 = 0;
+ }
+
+ route->path.tag = ospf6_as_external_lsa_get_tag(lsa);
+
+ ospf6_route_copy_nexthops(route, asbr_entry);
+
+ path = ospf6_path_dup(&route->path);
+ ospf6_copy_nexthops(path->nh_list, asbr_entry->nh_list);
+ listnode_add_sort(route->paths, path);
+
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "%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));
+
+ 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. */
+ 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);
+ }
+}
+
+void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
+ struct ospf6_route *asbr_entry)
+{
+ struct ospf6_as_external_lsa *external;
+ struct prefix prefix;
+ struct ospf6_route *route, *nroute, *route_to_del;
+ struct ospf6_area *oa = NULL;
+ struct ospf6 *ospf6;
+ int type;
+ bool debug = false;
+
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || (IS_OSPF6_DEBUG_NSSA))
+ debug = true;
+
+ ospf6 = ospf6_get_by_lsdb(lsa);
+ type = ntohs(lsa->header->type);
+
+ if (type == OSPF6_LSTYPE_TYPE_7) {
+ if (debug)
+ zlog_debug("%s: Withdraw Type 7 route for %s",
+ __func__, lsa->name);
+ oa = lsa->lsdb->data;
+ } else {
+ if (debug)
+ zlog_debug("%s: Withdraw AS-External route for %s",
+ __func__, lsa->name);
+
+ if (ospf6_check_and_set_router_abr(ospf6))
+ oa = ospf6->backbone;
+ else
+ oa = listnode_head(ospf6->area_list);
+ }
+
+ if (oa == NULL) {
+ if (debug)
+ zlog_debug("%s: Invalid area", __func__);
+ return;
+ }
+
+ if (lsa->header->adv_router == oa->ospf6->router_id) {
+ if (debug)
+ zlog_debug("Ignore self-originated AS-External-LSA");
+ return;
+ }
+
+ route_to_del = ospf6_route_create(ospf6);
+ route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
+ route_to_del->prefix.family = AF_INET6;
+ route_to_del->prefix.prefixlen = external->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, external,
+ &external->prefix);
+
+ route_to_del->path.origin.type = lsa->header->type;
+ route_to_del->path.origin.id = lsa->header->id;
+ route_to_del->path.origin.adv_router = lsa->header->adv_router;
+
+ if (asbr_entry) {
+ route_to_del->path.area_id = asbr_entry->path.area_id;
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
+ route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
+ route_to_del->path.metric_type = 2;
+ route_to_del->path.cost = asbr_entry->path.cost;
+ route_to_del->path.u.cost_e2 =
+ OSPF6_ASBR_METRIC(external);
+ } else {
+ route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
+ route_to_del->path.metric_type = 1;
+ route_to_del->path.cost = asbr_entry->path.cost
+ + OSPF6_ASBR_METRIC(external);
+ route_to_del->path.u.cost_e2 = 0;
+ }
+ }
+
+ memset(&prefix, 0, sizeof(struct prefix));
+ prefix.family = AF_INET6;
+ prefix.prefixlen = external->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix);
+
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ route = ospf6_route_lookup(&prefix, oa->route_table);
+ else
+ route = ospf6_route_lookup(&prefix, oa->ospf6->route_table);
+
+ if (route == NULL) {
+ if (debug)
+ zlog_debug("AS-External route %pFX not found", &prefix);
+ ospf6_route_delete(route_to_del);
+ return;
+ }
+
+ if (debug)
+ zlog_debug(
+ "%s: Current route %pFX cost %u e2 %u, route to del cost %u e2 %u",
+ __func__, &prefix, route->path.cost, route->path.u.cost_e2,
+ route_to_del->path.cost, route_to_del->path.u.cost_e2);
+
+ for (ospf6_route_lock(route);
+ route && ospf6_route_is_prefix(&prefix, route); route = nroute) {
+ nroute = ospf6_route_next(route);
+
+ if (route->type != OSPF6_DEST_TYPE_NETWORK)
+ continue;
+
+ /* Route has multiple ECMP paths, remove matching
+ * path. Update current route's effective nh list
+ * after removal of one of the path.
+ */
+ if (listcount(route->paths) > 1) {
+ struct listnode *anode, *anext;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ struct ospf6_path *o_path;
+ bool nh_updated = false;
+
+ /* Iterate all paths of route to find maching with LSA
+ * remove from route path list. If route->path is same,
+ * replace from paths list.
+ */
+ for (ALL_LIST_ELEMENTS(route->paths, anode, anext,
+ o_path)) {
+ if ((o_path->origin.type != lsa->header->type)
+ || (o_path->origin.adv_router
+ != lsa->header->adv_router)
+ || (o_path->origin.id != lsa->header->id))
+ continue;
+
+ /* Compare LSA cost with current
+ * route info.
+ */
+ if (asbr_entry
+ && (o_path->cost != route_to_del->path.cost
+ || o_path->u.cost_e2
+ != route_to_del->path.u
+ .cost_e2)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(
+ AS_EXTERNAL)) {
+ zlog_debug(
+ "%s: route %pFX to delete is not same, cost %u del cost %u. skip",
+ __func__, &prefix,
+ route->path.cost,
+ route_to_del->path
+ .cost);
+ }
+ continue;
+ }
+
+ if (debug) {
+ zlog_debug(
+ "%s: route %pFX path found with cost %u nh %u to remove.",
+ __func__, &prefix, route->path.cost,
+ listcount(o_path->nh_list));
+ }
+
+ /* Remove found path's nh_list from
+ * the route's nh_list.
+ */
+ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
+ nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(route->nh_list,
+ rnode, rnext,
+ rnh)) {
+ if (!ospf6_nexthop_is_same(rnh,
+ nh))
+ continue;
+ listnode_delete(route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(rnh);
+ }
+ }
+ /* Delete the path from route's path list */
+ listnode_delete(route->paths, o_path);
+ ospf6_path_free(o_path);
+ nh_updated = true;
+ }
+
+ if (nh_updated) {
+ /* Iterate all paths and merge nexthop,
+ * unlesss any of the nexthop similar to
+ * ones deleted as part of path deletion.
+ */
+
+ for (ALL_LIST_ELEMENTS(route->paths, anode,
+ anext, o_path)) {
+ ospf6_merge_nexthops(route->nh_list,
+ o_path->nh_list);
+ }
+
+ if (debug) {
+ zlog_debug(
+ "%s: AS-External %u route %pFX update paths %u nh %u",
+ __func__,
+ (route->path.type
+ == OSPF6_PATH_TYPE_EXTERNAL1)
+ ? 1
+ : 2,
+ &route->prefix, listcount(route->paths),
+ route->nh_list ? listcount(
+ route->nh_list)
+ : 0);
+ }
+
+ if (listcount(route->paths)) {
+ /* Update RIB/FIB with effective
+ * nh_list
+ */
+ if (oa->ospf6->route_table->hook_add)
+ (*oa->ospf6->route_table
+ ->hook_add)(route);
+
+ /* route's primary path is similar
+ * to LSA, replace route's primary
+ * path with route's paths list head.
+ */
+ if ((route->path.origin.id ==
+ lsa->header->id) &&
+ (route->path.origin.adv_router
+ == lsa->header->adv_router)) {
+ struct ospf6_path *h_path;
+
+ h_path = (struct ospf6_path *)
+ listgetdata(
+ listhead(route->paths));
+ route->path.origin.type =
+ h_path->origin.type;
+ route->path.origin.id =
+ h_path->origin.id;
+ route->path.origin.adv_router =
+ h_path->origin.adv_router;
+ }
+ } else {
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ ospf6_route_remove(
+ route, oa->route_table);
+ else
+ ospf6_route_remove(
+ route,
+ oa->ospf6->route_table);
+ }
+ }
+ continue;
+
+ } else {
+ /* Compare LSA origin and cost with current route info.
+ * if any check fails skip del this route node.
+ */
+ if (asbr_entry
+ && (!ospf6_route_is_same_origin(route, route_to_del)
+ || (route->path.type != route_to_del->path.type)
+ || (route->path.cost != route_to_del->path.cost)
+ || (route->path.u.cost_e2
+ != route_to_del->path.u.cost_e2))) {
+ if (debug) {
+ zlog_debug(
+ "%s: route %pFX to delete is not same, cost %u del cost %u. skip",
+ __func__, &prefix, route->path.cost,
+ route_to_del->path.cost);
+ }
+ continue;
+ }
+
+ if ((route->path.origin.type != lsa->header->type)
+ || (route->path.origin.adv_router
+ != lsa->header->adv_router)
+ || (route->path.origin.id != lsa->header->id))
+ continue;
+ }
+ if (debug) {
+ zlog_debug(
+ "%s: AS-External %u route remove %pFX cost %u(%u) nh %u",
+ __func__,
+ route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
+ ? 1
+ : 2,
+ &route->prefix, route->path.cost, route->path.u.cost_e2,
+ listcount(route->nh_list));
+ }
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ ospf6_route_remove(route, oa->route_table);
+ else
+ ospf6_route_remove(route, oa->ospf6->route_table);
+ }
+ if (route != NULL)
+ ospf6_route_unlock(route);
+
+ ospf6_route_delete(route_to_del);
+}
+
+void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry, struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+ uint32_t router;
+
+ if (!CHECK_FLAG(asbr_entry->flag, OSPF6_ROUTE_BEST)) {
+ char buf[16];
+ inet_ntop(AF_INET, &ADV_ROUTER_IN_PREFIX(&asbr_entry->prefix),
+ buf, sizeof(buf));
+ zlog_info("ignore non-best path: lsentry %s add", buf);
+ return;
+ }
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix);
+ for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa)) {
+ if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_asbr_lsa_add(lsa);
+ }
+}
+
+void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry,
+ struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+ uint32_t router;
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix);
+ for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa))
+ ospf6_asbr_lsa_remove(lsa, asbr_entry);
+}
+
+
+/* redistribute function */
+static void ospf6_asbr_routemap_set(struct ospf6_redist *red,
+ const char *mapname)
+{
+ if (ROUTEMAP_NAME(red)) {
+ route_map_counter_decrement(ROUTEMAP(red));
+ free(ROUTEMAP_NAME(red));
+ }
+
+ ROUTEMAP_NAME(red) = strdup(mapname);
+ ROUTEMAP(red) = route_map_lookup_by_name(mapname);
+ route_map_counter_increment(ROUTEMAP(red));
+}
+
+static void ospf6_asbr_routemap_unset(struct ospf6_redist *red)
+{
+ if (ROUTEMAP_NAME(red))
+ free(ROUTEMAP_NAME(red));
+
+ route_map_counter_decrement(ROUTEMAP(red));
+
+ ROUTEMAP_NAME(red) = NULL;
+ ROUTEMAP(red) = NULL;
+}
+
+static void ospf6_asbr_routemap_update_timer(struct event *thread)
+{
+ struct ospf6 *ospf6 = EVENT_ARG(thread);
+ struct ospf6_redist *red;
+ int type;
+
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ red = ospf6_redist_lookup(ospf6, type, 0);
+
+ if (!red)
+ continue;
+
+ if (!CHECK_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED))
+ continue;
+
+ if (ROUTEMAP_NAME(red))
+ ROUTEMAP(red) =
+ route_map_lookup_by_name(ROUTEMAP_NAME(red));
+
+ if (ROUTEMAP(red)) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug(
+ "%s: route-map %s update, reset redist %s",
+ __func__, ROUTEMAP_NAME(red),
+ ZROUTE_NAME(type));
+
+ ospf6_zebra_no_redistribute(type, ospf6->vrf_id);
+ ospf6_zebra_redistribute(type, ospf6->vrf_id);
+ }
+
+ UNSET_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED);
+ }
+}
+
+void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6,
+ struct ospf6_redist *red)
+{
+ SET_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED);
+
+ if (event_is_scheduled(ospf6->t_distribute_update))
+ return;
+
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: trigger redistribute reset thread", __func__);
+
+ event_add_timer_msec(master, ospf6_asbr_routemap_update_timer, ospf6,
+ OSPF_MIN_LS_INTERVAL, &ospf6->t_distribute_update);
+}
+
+void ospf6_asbr_routemap_update(const char *mapname)
+{
+ int type;
+ struct listnode *node, *nnode;
+ struct ospf6 *ospf6 = NULL;
+ struct ospf6_redist *red;
+
+ if (om6 == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red || (ROUTEMAP_NAME(red) == NULL))
+ continue;
+ ROUTEMAP(red) =
+ route_map_lookup_by_name(ROUTEMAP_NAME(red));
+
+ if (mapname == NULL
+ || strcmp(ROUTEMAP_NAME(red), mapname))
+ continue;
+ if (ROUTEMAP(red)) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug(
+ "%s: route-map %s update, reset redist %s",
+ __func__,
+ mapname,
+ ZROUTE_NAME(
+ type));
+
+ route_map_counter_increment(ROUTEMAP(red));
+ ospf6_asbr_distribute_list_update(ospf6, red);
+ } else {
+ /*
+ * if the mapname matches a
+ * route-map on ospf6 but the
+ * map doesn't exist, it is
+ * being deleted. flush and then
+ * readvertise
+ */
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug(
+ "%s: route-map %s deleted, reset redist %s",
+ __func__,
+ mapname,
+ ZROUTE_NAME(
+ type));
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
+ ospf6_asbr_routemap_set(red, mapname);
+ ospf6_asbr_redistribute_set(ospf6, type);
+ }
+ }
+ }
+}
+
+static void ospf6_asbr_routemap_event(const char *name)
+{
+ int type;
+ struct listnode *node, *nnode;
+ struct ospf6 *ospf6;
+ struct ospf6_redist *red;
+
+ if (om6 == NULL)
+ return;
+ for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (red && ROUTEMAP_NAME(red)
+ && (strcmp(ROUTEMAP_NAME(red), name) == 0))
+ ospf6_asbr_distribute_list_update(ospf6, red);
+ }
+ }
+}
+
+int ospf6_asbr_is_asbr(struct ospf6 *o)
+{
+ return (o->external_table->count || IS_OSPF6_ASBR(o));
+}
+
+struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type,
+ unsigned short instance)
+{
+ struct list *red_list;
+ struct listnode *node;
+ struct ospf6_redist *red;
+
+ red_list = ospf6->redist[type];
+ if (!red_list)
+ return (NULL);
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red))
+ if (red->instance == instance)
+ return red;
+
+ return NULL;
+}
+
+static struct ospf6_redist *ospf6_redist_add(struct ospf6 *ospf6, int type,
+ uint8_t instance)
+{
+ struct ospf6_redist *red;
+
+ red = ospf6_redist_lookup(ospf6, type, instance);
+ if (red)
+ return red;
+
+ if (!ospf6->redist[type])
+ ospf6->redist[type] = list_new();
+
+ red = XCALLOC(MTYPE_OSPF6_REDISTRIBUTE, sizeof(struct ospf6_redist));
+ red->instance = instance;
+ red->dmetric.type = -1;
+ red->dmetric.value = -1;
+ ROUTEMAP_NAME(red) = NULL;
+ ROUTEMAP(red) = NULL;
+
+ listnode_add(ospf6->redist[type], red);
+ ospf6->redistribute++;
+
+ return red;
+}
+
+static void ospf6_redist_del(struct ospf6 *ospf6, struct ospf6_redist *red,
+ int type)
+{
+ if (red) {
+ listnode_delete(ospf6->redist[type], red);
+ if (!ospf6->redist[type]->count) {
+ list_delete(&ospf6->redist[type]);
+ }
+ XFREE(MTYPE_OSPF6_REDISTRIBUTE, red);
+ ospf6->redistribute--;
+ }
+}
+
+/*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(struct ospf6 *ospf6, int type)
+{
+ ospf6_zebra_redistribute(type, ospf6->vrf_id);
+
+ ++ospf6->redist_count;
+ ospf6_asbr_status_update(ospf6, ospf6->redist_count);
+}
+
+static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
+ struct ospf6_redist *red, int type)
+{
+ struct ospf6_route *route;
+ struct ospf6_external_info *info;
+
+ ospf6_zebra_no_redistribute(type, ospf6->vrf_id);
+
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route)) {
+ info = route->route_option;
+ if (info->type != type)
+ continue;
+
+ ospf6_asbr_redistribute_remove(info->type, 0, &route->prefix,
+ ospf6);
+ }
+
+ ospf6_asbr_routemap_unset(red);
+ --ospf6->redist_count;
+ ospf6_asbr_status_update(ospf6, ospf6->redist_count);
+}
+
+/* When an area is unstubified, flood all the external LSAs in the area */
+void ospf6_asbr_send_externals_to_area(struct ospf6_area *oa)
+{
+ struct ospf6_lsa *lsa, *lsanext;
+
+ for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) {
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: Flooding AS-External LSA %s",
+ __func__, lsa->name);
+
+ ospf6_flood_area(NULL, lsa, oa);
+ }
+ }
+}
+
+/* When an area is stubified, remove all the external LSAs in the area */
+void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa)
+{
+ struct ospf6_lsa *lsa, *lsanext;
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ struct ospf6 *ospf6 = oa->ospf6;
+ const struct route_node *iterend;
+
+ /* skip if router is in other non-stub/non-NSSA areas */
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area))
+ if (!IS_AREA_STUB(area) && !IS_AREA_NSSA(area))
+ return;
+
+ /* if router is only in a stub area then purge AS-External LSAs */
+ iterend = ospf6_lsdb_head(ospf6->lsdb, 0, 0, 0, &lsa);
+ while (lsa != NULL) {
+ assert(lsa->lock > 1);
+ lsanext = ospf6_lsdb_next(iterend, lsa);
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL)
+ ospf6_lsdb_remove(lsa, ospf6->lsdb);
+ lsa = lsanext;
+ }
+}
+
+static struct ospf6_external_aggr_rt *
+ospf6_external_aggr_match(struct ospf6 *ospf6, struct prefix *p)
+{
+ struct route_node *node;
+
+ node = route_node_match(ospf6->rt_aggr_tbl, p);
+ if (node == NULL)
+ return NULL;
+
+ if (IS_OSPF6_DEBUG_AGGR) {
+ struct ospf6_external_aggr_rt *ag = node->info;
+ zlog_debug("%s: Matching aggregator found.prefix: %pFX Aggregator %pFX",
+ __func__,
+ p,
+ &ag->p);
+ }
+
+ route_unlock_node(node);
+
+ return node->info;
+}
+
+static void ospf6_external_lsa_fwd_addr_set(struct ospf6 *ospf6,
+ const struct in6_addr *nexthop,
+ struct in6_addr *fwd_addr)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ struct prefix nh;
+
+ /* Initialize forwarding address to zero. */
+ memset(fwd_addr, 0, sizeof(*fwd_addr));
+
+ vrf = vrf_lookup_by_id(ospf6->vrf_id);
+ if (!vrf)
+ return;
+
+ nh.family = AF_INET6;
+ nh.u.prefix6 = *nexthop;
+ nh.prefixlen = IPV6_MAX_BITLEN;
+
+ /*
+ * Use the route's nexthop as the forwarding address if it meets the
+ * following conditions:
+ * - It's a global address.
+ * - The associated nexthop interface is OSPF-enabled.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(nexthop) || IN6_IS_ADDR_LINKLOCAL(nexthop))
+ return;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct ospf6_interface *oi = ifp->info;
+ struct connected *connected;
+ struct listnode *node;
+
+ if (!oi || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE))
+ continue;
+
+ FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
+ if (connected->address->family != AF_INET6)
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(&connected->address->u.prefix6))
+ continue;
+ if (!prefix_match(connected->address, &nh))
+ continue;
+
+ *fwd_addr = *nexthop;
+ return;
+ }
+ }
+}
+
+void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
+ struct prefix *prefix,
+ unsigned int nexthop_num,
+ const struct in6_addr *nexthop,
+ route_tag_t tag, struct ospf6 *ospf6)
+{
+ route_map_result_t ret;
+ struct ospf6_route troute;
+ struct ospf6_external_info tinfo;
+ struct ospf6_route *route, *match;
+ struct ospf6_external_info *info;
+ struct ospf6_redist *red;
+
+ red = ospf6_redist_lookup(ospf6, type, 0);
+
+ if (!red)
+ return;
+
+ if ((type != DEFAULT_ROUTE)
+ && !ospf6_zebra_is_redistribute(type, ospf6->vrf_id))
+ return;
+
+ memset(&troute, 0, sizeof(troute));
+ memset(&tinfo, 0, sizeof(tinfo));
+
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Redistribute %pFX (%s)", prefix,
+ type == DEFAULT_ROUTE
+ ? "default-information-originate"
+ : ZROUTE_NAME(type));
+
+ /* if route-map was specified but not found, do not advertise */
+ 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;
+ }
+ }
+
+ /* apply route-map */
+ if (ROUTEMAP(red)) {
+ troute.route_option = &tinfo;
+ troute.ospf6 = ospf6;
+ tinfo.ifindex = ifindex;
+ tinfo.tag = tag;
+
+ ret = route_map_apply(ROUTEMAP(red), prefix, &troute);
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Denied by route-map \"%s\"",
+ ROUTEMAP_NAME(red));
+ ospf6_asbr_redistribute_remove(type, ifindex, prefix,
+ ospf6);
+ return;
+ }
+ }
+
+ match = ospf6_route_lookup(prefix, ospf6->external_table);
+ if (match) {
+ info = match->route_option;
+ /* copy result of route-map */
+ if (ROUTEMAP(red)) {
+ if (troute.path.metric_type)
+ match->path.metric_type =
+ troute.path.metric_type;
+ else
+ match->path.metric_type =
+ metric_type(ospf6, type, 0);
+ if (troute.path.cost)
+ match->path.cost = troute.path.cost;
+ else
+ match->path.cost = metric_value(ospf6, type, 0);
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding))
+ memcpy(&info->forwarding, &tinfo.forwarding,
+ sizeof(struct in6_addr));
+ info->tag = tinfo.tag;
+ } else {
+ /* If there is no route-map, simply update the tag and
+ * metric fields
+ */
+ match->path.metric_type = metric_type(ospf6, type, 0);
+ match->path.cost = metric_value(ospf6, type, 0);
+ info->tag = tag;
+ }
+
+ info->type = type;
+
+ if (nexthop_num && nexthop) {
+ ospf6_route_add_nexthop(match, ifindex, nexthop);
+ ospf6_external_lsa_fwd_addr_set(ospf6, nexthop,
+ &info->forwarding);
+ } else
+ ospf6_route_add_nexthop(match, ifindex, NULL);
+
+ match->path.origin.id = htonl(info->id);
+ ospf6_handle_external_lsa_origination(ospf6, match, prefix);
+
+ ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+
+ return;
+ }
+
+ /* create new entry */
+ route = ospf6_route_create(ospf6);
+ route->type = OSPF6_DEST_TYPE_NETWORK;
+ prefix_copy(&route->prefix, prefix);
+
+ info = (struct ospf6_external_info *)XCALLOC(
+ MTYPE_OSPF6_EXTERNAL_INFO, sizeof(struct ospf6_external_info));
+ route->route_option = info;
+
+ /* copy result of route-map */
+ if (ROUTEMAP(red)) {
+ if (troute.path.metric_type)
+ route->path.metric_type = troute.path.metric_type;
+ else
+ route->path.metric_type = metric_type(ospf6, type, 0);
+ if (troute.path.cost)
+ route->path.cost = troute.path.cost;
+ else
+ route->path.cost = metric_value(ospf6, type, 0);
+ if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding))
+ memcpy(&info->forwarding, &tinfo.forwarding,
+ sizeof(struct in6_addr));
+ info->tag = tinfo.tag;
+ } else {
+ /* If there is no route-map, simply update the tag and metric
+ * fields
+ */
+ route->path.metric_type = metric_type(ospf6, type, 0);
+ route->path.cost = metric_value(ospf6, type, 0);
+ info->tag = tag;
+ }
+
+ info->type = type;
+ if (nexthop_num && nexthop) {
+ ospf6_route_add_nexthop(route, ifindex, nexthop);
+ ospf6_external_lsa_fwd_addr_set(ospf6, nexthop,
+ &info->forwarding);
+ } else
+ ospf6_route_add_nexthop(route, ifindex, NULL);
+
+ route = ospf6_route_add(route, ospf6->external_table);
+ ospf6_handle_external_lsa_origination(ospf6, route, prefix);
+
+ ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+
+}
+
+static void ospf6_asbr_external_lsa_remove_by_id(struct ospf6 *ospf6,
+ uint32_t id)
+{
+ struct ospf6_lsa *lsa;
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(id), ospf6->router_id, ospf6->lsdb);
+ if (!lsa)
+ return;
+
+ ospf6_external_lsa_purge(ospf6, lsa);
+
+}
+
+static void
+ospf6_link_route_to_aggr(struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ (void)hash_get(aggr->match_extnl_hash, rt, hash_alloc_intern);
+ rt->aggr_route = aggr;
+}
+
+static void
+ospf6_asbr_summary_remove_lsa_and_route(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+
+ /* Send a Max age LSA if it is already originated.*/
+ if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED))
+ return;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Flushing Aggregate route (%pFX)",
+ __func__,
+ &aggr->p);
+
+ ospf6_asbr_external_lsa_remove_by_id(ospf6, aggr->id);
+
+ if (aggr->route) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Remove the blackhole route",
+ __func__);
+
+ ospf6_zebra_route_update_remove(aggr->route, ospf6);
+ if (aggr->route->route_option)
+ XFREE(MTYPE_OSPF6_EXTERNAL_INFO,
+ aggr->route->route_option);
+ ospf6_route_delete(aggr->route);
+ aggr->route = NULL;
+ }
+
+ aggr->id = 0;
+ /* Unset the Origination flag */
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+}
+
+static void
+ospf6_unlink_route_from_aggr(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Unlinking external route(%pFX) from aggregator(%pFX), external route count:%ld",
+ __func__,
+ &rt->prefix,
+ &aggr->p,
+ OSPF6_EXTERNAL_RT_COUNT(aggr));
+
+ hash_release(aggr->match_extnl_hash, rt);
+ rt->aggr_route = NULL;
+
+ /* Flush the aggregate route if matching
+ * external route count becomes zero.
+ */
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr))
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+}
+
+void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
+ struct prefix *prefix, struct ospf6 *ospf6)
+{
+ struct ospf6_route *match;
+ struct ospf6_external_info *info = NULL;
+
+ match = ospf6_route_lookup(prefix, ospf6->external_table);
+ if (match == NULL) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("No such route %pFX to withdraw", prefix);
+ return;
+ }
+
+ info = match->route_option;
+ assert(info);
+
+ if (info->type != type) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Original protocol mismatch: %pFX", prefix);
+ return;
+ }
+
+ /* This means aggregation on this route was not done, hence remove LSA
+ * if any originated for this prefix
+ */
+ if (!match->aggr_route)
+ ospf6_asbr_external_lsa_remove_by_id(ospf6, info->id);
+ else
+ ospf6_unlink_route_from_aggr(ospf6, match->aggr_route, match);
+
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Removing route from external table %pFX",
+ prefix);
+
+ ospf6_route_remove(match, ospf6->external_table);
+ XFREE(MTYPE_OSPF6_EXTERNAL_INFO, info);
+
+ ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+}
+
+DEFPY (ospf6_redistribute,
+ ospf6_redistribute_cmd,
+ "redistribute " FRR_REDIST_STR_OSPF6D "[{metric (0-16777214)|metric-type (1-2)$metric_type|route-map RMAP_NAME$rmap_str}]",
+ "Redistribute\n"
+ FRR_REDIST_HELP_STR_OSPF6D
+ "Metric for redistributed routes\n"
+ "OSPF default metric\n"
+ "OSPF exterior metric type for redistributed routes\n"
+ "Set OSPF External Type 1/2 metrics\n"
+ "Route map reference\n"
+ "Route map name\n")
+{
+ int type;
+ struct ospf6_redist *red;
+ int idx_protocol = 1;
+ char *proto = argv[idx_protocol]->text;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ type = proto_redistnum(AFI_IP6, proto);
+ if (type < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!metric_str)
+ metric = -1;
+ if (!metric_type_str)
+ metric_type = -1;
+
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red) {
+ red = ospf6_redist_add(ospf6, type, 0);
+ } else {
+ /* Check if nothing has changed. */
+ if (red->dmetric.value == metric
+ && red->dmetric.type == metric_type
+ && ((!ROUTEMAP_NAME(red) && !rmap_str)
+ || (ROUTEMAP_NAME(red) && rmap_str
+ && strmatch(ROUTEMAP_NAME(red), rmap_str))))
+ return CMD_SUCCESS;
+
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
+ }
+
+ red->dmetric.value = metric;
+ red->dmetric.type = metric_type;
+ if (rmap_str)
+ ospf6_asbr_routemap_set(red, rmap_str);
+ else
+ ospf6_asbr_routemap_unset(red);
+ ospf6_asbr_redistribute_set(ospf6, type);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_redistribute,
+ no_ospf6_redistribute_cmd,
+ "no redistribute " FRR_REDIST_STR_OSPF6D "[{metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]",
+ NO_STR
+ "Redistribute\n"
+ FRR_REDIST_HELP_STR_OSPF6D
+ "Metric for redistributed routes\n"
+ "OSPF default metric\n"
+ "OSPF exterior metric type for redistributed routes\n"
+ "Set OSPF External Type 1/2 metrics\n"
+ "Route map reference\n"
+ "Route map name\n")
+{
+ int type;
+ struct ospf6_redist *red;
+ int idx_protocol = 2;
+ char *proto = argv[idx_protocol]->text;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ type = proto_redistnum(AFI_IP6, proto);
+ if (type < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red)
+ return CMD_SUCCESS;
+
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
+ ospf6_redist_del(ospf6, red, type);
+
+ return CMD_SUCCESS;
+}
+
+int ospf6_redistribute_config_write(struct vty *vty, 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)
+ continue;
+ if (type == ZEBRA_ROUTE_OSPF6)
+ continue;
+
+ vty_out(vty, " redistribute %s", ZROUTE_NAME(type));
+ if (red->dmetric.value >= 0)
+ vty_out(vty, " metric %d", red->dmetric.value);
+ if (red->dmetric.type == 1)
+ vty_out(vty, " metric-type 1");
+ if (ROUTEMAP_NAME(red))
+ vty_out(vty, " route-map %s", ROUTEMAP_NAME(red));
+ vty_out(vty, "\n");
+ }
+
+ return 0;
+}
+
+static void ospf6_redistribute_show_config(struct vty *vty, struct ospf6 *ospf6,
+ json_object *json_array,
+ json_object *json, bool use_json)
+{
+ int type;
+ int nroute[ZEBRA_ROUTE_MAX];
+ int total;
+ struct ospf6_route *route;
+ struct ospf6_external_info *info;
+ json_object *json_route;
+ struct ospf6_redist *red;
+
+ total = 0;
+ memset(nroute, 0, sizeof(nroute));
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route)) {
+ info = route->route_option;
+ nroute[info->type]++;
+ total++;
+ }
+
+ if (!use_json)
+ vty_out(vty, "Redistributing External Routes from:\n");
+
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+
+ red = ospf6_redist_lookup(ospf6, type, 0);
+
+ if (!red)
+ continue;
+ if (type == ZEBRA_ROUTE_OSPF6)
+ continue;
+
+ if (use_json) {
+ json_route = json_object_new_object();
+ json_object_string_add(json_route, "routeType",
+ ZROUTE_NAME(type));
+ json_object_int_add(json_route, "numberOfRoutes",
+ nroute[type]);
+ json_object_boolean_add(json_route,
+ "routeMapNamePresent",
+ ROUTEMAP_NAME(red));
+ }
+
+ if (ROUTEMAP_NAME(red)) {
+ if (use_json) {
+ json_object_string_add(json_route,
+ "routeMapName",
+ ROUTEMAP_NAME(red));
+ json_object_boolean_add(json_route,
+ "routeMapFound",
+ ROUTEMAP(red));
+ } else
+ vty_out(vty,
+ " %d: %s with route-map \"%s\"%s\n",
+ nroute[type], ZROUTE_NAME(type),
+ ROUTEMAP_NAME(red),
+ (ROUTEMAP(red) ? ""
+ : " (not found !)"));
+ } else {
+ if (!use_json)
+ vty_out(vty, " %d: %s\n", nroute[type],
+ ZROUTE_NAME(type));
+ }
+
+ if (use_json)
+ json_object_array_add(json_array, json_route);
+ }
+ if (use_json) {
+ json_object_object_add(json, "redistributedRoutes", json_array);
+ json_object_int_add(json, "totalRoutes", total);
+ } else
+ vty_out(vty, "Total %d routes\n", total);
+}
+
+static void ospf6_redistribute_default_set(struct ospf6 *ospf6, int originate)
+{
+ struct prefix_ipv6 p = {};
+ struct in6_addr nexthop = {};
+ int cur_originate = ospf6->default_originate;
+
+ p.family = AF_INET6;
+ p.prefixlen = 0;
+
+ ospf6->default_originate = originate;
+
+ switch (cur_originate) {
+ case DEFAULT_ORIGINATE_NONE:
+ break;
+ case DEFAULT_ORIGINATE_ZEBRA:
+ zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE,
+ zclient, AFI_IP6, ospf6->vrf_id);
+ ospf6_asbr_redistribute_remove(DEFAULT_ROUTE, 0,
+ (struct prefix *)&p, ospf6);
+
+ break;
+ case DEFAULT_ORIGINATE_ALWAYS:
+ ospf6_asbr_redistribute_remove(DEFAULT_ROUTE, 0,
+ (struct prefix *)&p, ospf6);
+ break;
+ }
+
+ switch (originate) {
+ case DEFAULT_ORIGINATE_NONE:
+ break;
+ case DEFAULT_ORIGINATE_ZEBRA:
+ zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD,
+ zclient, AFI_IP6, ospf6->vrf_id);
+
+ break;
+ case DEFAULT_ORIGINATE_ALWAYS:
+ ospf6_asbr_redistribute_add(DEFAULT_ROUTE, 0,
+ (struct prefix *)&p, 0, &nexthop, 0,
+ ospf6);
+ break;
+ }
+}
+
+/* Default Route originate. */
+DEFPY (ospf6_default_route_originate,
+ ospf6_default_route_originate_cmd,
+ "default-information originate [{always$always|metric (0-16777214)$mval|metric-type (1-2)$mtype|route-map RMAP_NAME$rtmap}]",
+ "Control distribution of default route\n"
+ "Distribute a default route\n"
+ "Always advertise default route\n"
+ "OSPFv3 default metric\n"
+ "OSPFv3 metric\n"
+ "OSPFv3 metric type for default routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ int default_originate = DEFAULT_ORIGINATE_ZEBRA;
+ struct ospf6_redist *red;
+ bool sameRtmap = false;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ int cur_originate = ospf6->default_originate;
+
+ red = ospf6_redist_add(ospf6, DEFAULT_ROUTE, 0);
+
+ if (always != NULL)
+ default_originate = DEFAULT_ORIGINATE_ALWAYS;
+
+ if (mval_str == NULL)
+ mval = -1;
+
+ if (mtype_str == NULL)
+ mtype = -1;
+
+ /* To check if user is providing same route map */
+ if ((!rtmap && !ROUTEMAP_NAME(red)) ||
+ (rtmap && ROUTEMAP_NAME(red) &&
+ (strcmp(rtmap, ROUTEMAP_NAME(red)) == 0)))
+ sameRtmap = true;
+
+ /* Don't allow if the same lsa is already originated. */
+ if ((sameRtmap) && (red->dmetric.type == mtype)
+ && (red->dmetric.value == mval)
+ && (cur_originate == default_originate))
+ return CMD_SUCCESS;
+
+ /* Updating Metric details */
+ red->dmetric.type = mtype;
+ red->dmetric.value = mval;
+
+ /* updating route map details */
+ if (rtmap)
+ ospf6_asbr_routemap_set(red, rtmap);
+ else
+ ospf6_asbr_routemap_unset(red);
+
+ ospf6_redistribute_default_set(ospf6, default_originate);
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_ospf6_default_information_originate,
+ no_ospf6_default_information_originate_cmd,
+ "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]",
+ NO_STR
+ "Control distribution of default information\n"
+ "Distribute a default route\n"
+ "Always advertise default route\n"
+ "OSPFv3 default metric\n"
+ "OSPFv3 metric\n"
+ "OSPFv3 metric type for default routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ struct ospf6_redist *red;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+
+ red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0);
+ if (!red)
+ return CMD_SUCCESS;
+
+ ospf6_asbr_routemap_unset(red);
+ ospf6_redist_del(ospf6, red, DEFAULT_ROUTE);
+
+ ospf6_redistribute_default_set(ospf6, DEFAULT_ORIGINATE_NONE);
+ return CMD_SUCCESS;
+}
+
+/* Routemap Functions */
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_match_address_prefixlist(void *rule,
+ const struct prefix *prefix,
+
+ void *object)
+{
+ struct prefix_list *plist;
+
+ plist = prefix_list_lookup(AFI_IP6, (char *)rule);
+ if (plist == NULL)
+ return RMAP_NOMATCH;
+
+ return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+static void *
+ospf6_routemap_rule_match_address_prefixlist_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void ospf6_routemap_rule_match_address_prefixlist_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ ospf6_routemap_rule_match_address_prefixlist_cmd = {
+ "ipv6 address prefix-list",
+ ospf6_routemap_rule_match_address_prefixlist,
+ ospf6_routemap_rule_match_address_prefixlist_compile,
+ ospf6_routemap_rule_match_address_prefixlist_free,
+};
+
+/* `match interface IFNAME' */
+/* Match function should return 1 if match is success else return
+ zero. */
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct interface *ifp;
+ struct ospf6_route *route;
+ struct ospf6_external_info *ei;
+
+ route = object;
+ ei = route->route_option;
+ ifp = if_lookup_by_name((char *)rule, route->ospf6->vrf_id);
+
+ if (ifp != NULL && ei->ifindex == ifp->ifindex)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `interface' match statement. `arg' should be
+ interface name. */
+static void *ospf6_routemap_rule_match_interface_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `interface' value. */
+static void ospf6_routemap_rule_match_interface_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for interface matching. */
+static const struct route_map_rule_cmd
+ ospf6_routemap_rule_match_interface_cmd = {
+ "interface",
+ ospf6_routemap_rule_match_interface,
+ ospf6_routemap_rule_match_interface_compile,
+ ospf6_routemap_rule_match_interface_free
+};
+
+/* Match function for matching route tags */
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_match_tag(void *rule, const struct prefix *p, void *object)
+{
+ route_tag_t *tag = rule;
+ struct ospf6_route *route = object;
+ struct ospf6_external_info *info = route->route_option;
+
+ if (info->tag == *tag)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static const struct route_map_rule_cmd
+ ospf6_routemap_rule_match_tag_cmd = {
+ "tag",
+ ospf6_routemap_rule_match_tag,
+ route_map_rule_tag_compile,
+ route_map_rule_tag_free,
+};
+
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_set_metric_type(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ char *metric_type = rule;
+ struct ospf6_route *route = object;
+
+ if (strcmp(metric_type, "type-2") == 0)
+ route->path.metric_type = 2;
+ else
+ route->path.metric_type = 1;
+
+ return RMAP_OKAY;
+}
+
+static void *ospf6_routemap_rule_set_metric_type_compile(const char *arg)
+{
+ if (strcmp(arg, "type-2") && strcmp(arg, "type-1"))
+ return NULL;
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void ospf6_routemap_rule_set_metric_type_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ ospf6_routemap_rule_set_metric_type_cmd = {
+ "metric-type",
+ ospf6_routemap_rule_set_metric_type,
+ ospf6_routemap_rule_set_metric_type_compile,
+ ospf6_routemap_rule_set_metric_type_free,
+};
+
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_set_metric(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ char *metric = rule;
+ struct ospf6_route *route = object;
+
+ route->path.cost = atoi(metric);
+ return RMAP_OKAY;
+}
+
+static void *ospf6_routemap_rule_set_metric_compile(const char *arg)
+{
+ uint32_t metric;
+ char *endp;
+ metric = strtoul(arg, &endp, 0);
+ if (metric > OSPF_LS_INFINITY || *endp != '\0')
+ return NULL;
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void ospf6_routemap_rule_set_metric_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ ospf6_routemap_rule_set_metric_cmd = {
+ "metric",
+ ospf6_routemap_rule_set_metric,
+ ospf6_routemap_rule_set_metric_compile,
+ ospf6_routemap_rule_set_metric_free,
+};
+
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_set_forwarding(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ char *forwarding = rule;
+ struct ospf6_route *route = object;
+ struct ospf6_external_info *info = route->route_option;
+
+ if (inet_pton(AF_INET6, forwarding, &info->forwarding) != 1) {
+ memset(&info->forwarding, 0, sizeof(struct in6_addr));
+ return RMAP_ERROR;
+ }
+
+ return RMAP_OKAY;
+}
+
+static void *ospf6_routemap_rule_set_forwarding_compile(const char *arg)
+{
+ struct in6_addr a;
+ if (inet_pton(AF_INET6, arg, &a) != 1)
+ return NULL;
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void ospf6_routemap_rule_set_forwarding_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ ospf6_routemap_rule_set_forwarding_cmd = {
+ "forwarding-address",
+ ospf6_routemap_rule_set_forwarding,
+ ospf6_routemap_rule_set_forwarding_compile,
+ ospf6_routemap_rule_set_forwarding_free,
+};
+
+static enum route_map_cmd_result_t
+ospf6_routemap_rule_set_tag(void *rule, const struct prefix *p, void *object)
+{
+ route_tag_t *tag = rule;
+ struct ospf6_route *route = object;
+ struct ospf6_external_info *info = route->route_option;
+
+ info->tag = *tag;
+ return RMAP_OKAY;
+}
+
+static const struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = {
+ "tag",
+ ospf6_routemap_rule_set_tag,
+ route_map_rule_tag_compile,
+ route_map_rule_tag_free,
+};
+
+/* add "set metric-type" */
+DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd,
+ "set metric-type <type-1|type-2>",
+ SET_STR
+ "Type of metric for destination routing protocol\n"
+ "OSPF[6] external type 1 metric\n"
+ "OSPF[6] external type 2 metric\n")
+{
+ char *ext = argv[2]->text;
+
+ const char *xpath =
+ "./set-action[action='frr-ospf-route-map:metric-type']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-ospf-route-map:metric-type", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ext);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/* delete "set metric-type" */
+DEFUN_YANG (ospf6_routemap_no_set_metric_type, ospf6_routemap_no_set_metric_type_cmd,
+ "no set metric-type [<type-1|type-2>]",
+ NO_STR
+ SET_STR
+ "Type of metric for destination routing protocol\n"
+ "OSPF[6] external type 1 metric\n"
+ "OSPF[6] external type 2 metric\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-ospf-route-map:metric-type']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/* add "set forwarding-address" */
+DEFUN_YANG (ospf6_routemap_set_forwarding, ospf6_routemap_set_forwarding_cmd,
+ "set forwarding-address X:X::X:X",
+ "Set value\n"
+ "Forwarding Address\n"
+ "IPv6 Address\n")
+{
+ int idx_ipv6 = 2;
+ const char *xpath =
+ "./set-action[action='frr-ospf6-route-map:forwarding-address']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-ospf6-route-map:ipv6-address", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_ipv6]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/* delete "set forwarding-address" */
+DEFUN_YANG (ospf6_routemap_no_set_forwarding, ospf6_routemap_no_set_forwarding_cmd,
+ "no set forwarding-address [X:X::X:X]",
+ NO_STR
+ "Set value\n"
+ "Forwarding Address\n"
+ "IPv6 Address\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-ospf6-route-map:forwarding-address']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static void ospf6_routemap_init(void)
+{
+ route_map_init();
+
+ route_map_add_hook(ospf6_asbr_routemap_update);
+ route_map_delete_hook(ospf6_asbr_routemap_update);
+ route_map_event_hook(ospf6_asbr_routemap_event);
+
+ route_map_set_metric_hook(generic_set_add);
+ route_map_no_set_metric_hook(generic_set_delete);
+
+ route_map_set_tag_hook(generic_set_add);
+ route_map_no_set_tag_hook(generic_set_delete);
+
+ route_map_match_tag_hook(generic_match_add);
+ route_map_no_match_tag_hook(generic_match_delete);
+
+ route_map_match_ipv6_address_prefix_list_hook(generic_match_add);
+ route_map_no_match_ipv6_address_prefix_list_hook(generic_match_delete);
+
+ route_map_match_interface_hook(generic_match_add);
+ route_map_no_match_interface_hook(generic_match_delete);
+
+ route_map_install_match(
+ &ospf6_routemap_rule_match_address_prefixlist_cmd);
+ route_map_install_match(&ospf6_routemap_rule_match_interface_cmd);
+ route_map_install_match(&ospf6_routemap_rule_match_tag_cmd);
+
+ route_map_install_set(&ospf6_routemap_rule_set_metric_type_cmd);
+ route_map_install_set(&ospf6_routemap_rule_set_metric_cmd);
+ route_map_install_set(&ospf6_routemap_rule_set_forwarding_cmd);
+ route_map_install_set(&ospf6_routemap_rule_set_tag_cmd);
+
+ /* ASE Metric Type (e.g. Type-1/Type-2) */
+ install_element(RMAP_NODE, &ospf6_routemap_set_metric_type_cmd);
+ install_element(RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd);
+
+ /* ASE Metric */
+ install_element(RMAP_NODE, &ospf6_routemap_set_forwarding_cmd);
+ install_element(RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd);
+}
+
+
+/* Display functions */
+static char *ospf6_as_external_lsa_get_prefix_str(struct ospf6_lsa *lsa,
+ char *buf, int buflen,
+ int pos)
+{
+ struct ospf6_as_external_lsa *external;
+ struct in6_addr in6;
+ int prefix_length = 0;
+ char tbuf[16];
+
+ if (lsa) {
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+
+ if (pos == 0) {
+ ospf6_prefix_in6_addr(&in6, external,
+ &external->prefix);
+ prefix_length = external->prefix.prefix_length;
+ } else {
+ in6 = *((struct in6_addr
+ *)((caddr_t)external
+ + sizeof(struct
+ ospf6_as_external_lsa)
+ + OSPF6_PREFIX_SPACE(
+ external->prefix
+ .prefix_length)));
+ }
+ if (buf) {
+ inet_ntop(AF_INET6, &in6, buf, buflen);
+ if (prefix_length) {
+ snprintf(tbuf, sizeof(tbuf), "/%d",
+ prefix_length);
+ strlcat(buf, tbuf, buflen);
+ }
+ }
+ }
+ return (buf);
+}
+
+static int ospf6_as_external_lsa_show(struct vty *vty, struct ospf6_lsa *lsa,
+ json_object *json_obj, bool use_json)
+{
+ struct ospf6_as_external_lsa *external;
+ char buf[64];
+
+ assert(lsa->header);
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+
+ /* bits */
+ snprintf(buf, sizeof(buf), "%c%c%c",
+ (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E) ? 'E'
+ : '-'),
+ (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F) ? 'F'
+ : '-'),
+ (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T) ? 'T'
+ : '-'));
+
+ if (use_json) {
+ json_object_string_add(json_obj, "bits", buf);
+ json_object_int_add(json_obj, "metric",
+ (unsigned long)OSPF6_ASBR_METRIC(external));
+ ospf6_prefix_options_printbuf(external->prefix.prefix_options,
+ buf, sizeof(buf));
+ json_object_string_add(json_obj, "prefixOptions", buf);
+ json_object_int_add(
+ json_obj, "referenceLsType",
+ ntohs(external->prefix.prefix_refer_lstype));
+ json_object_string_add(json_obj, "prefix",
+ ospf6_as_external_lsa_get_prefix_str(
+ lsa, buf, sizeof(buf), 0));
+
+ /* Forwarding-Address */
+ json_object_boolean_add(
+ json_obj, "forwardingAddressPresent",
+ CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F));
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F))
+ json_object_string_add(
+ json_obj, "forwardingAddress",
+ ospf6_as_external_lsa_get_prefix_str(
+ lsa, buf, sizeof(buf), 1));
+
+ /* Tag */
+ json_object_boolean_add(
+ json_obj, "tagPresent",
+ CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T));
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T))
+ json_object_int_add(json_obj, "tag",
+ ospf6_as_external_lsa_get_tag(lsa));
+ } else {
+ vty_out(vty, " Bits: %s\n", buf);
+ vty_out(vty, " Metric: %5lu\n",
+ (unsigned long)OSPF6_ASBR_METRIC(external));
+
+ ospf6_prefix_options_printbuf(external->prefix.prefix_options,
+ buf, sizeof(buf));
+ vty_out(vty, " Prefix Options: %s\n", buf);
+
+ vty_out(vty, " Referenced LSType: %d\n",
+ ntohs(external->prefix.prefix_refer_lstype));
+
+ vty_out(vty, " Prefix: %s\n",
+ ospf6_as_external_lsa_get_prefix_str(lsa, buf,
+ sizeof(buf), 0));
+
+ /* Forwarding-Address */
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
+ vty_out(vty, " Forwarding-Address: %s\n",
+ ospf6_as_external_lsa_get_prefix_str(
+ lsa, buf, sizeof(buf), 1));
+ }
+
+ /* Tag */
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) {
+ vty_out(vty, " Tag: %" ROUTE_TAG_PRI "\n",
+ ospf6_as_external_lsa_get_tag(lsa));
+ }
+ }
+
+ return 0;
+}
+
+static void ospf6_asbr_external_route_show(struct vty *vty,
+ struct ospf6_route *route,
+ json_object *json_array,
+ bool use_json)
+{
+ struct ospf6_external_info *info = route->route_option;
+ char prefix[PREFIX2STR_BUFFER], id[16], forwarding[64];
+ uint32_t tmp_id;
+ json_object *json_route;
+ char route_type[2];
+
+ prefix2str(&route->prefix, prefix, sizeof(prefix));
+ tmp_id = ntohl(info->id);
+ inet_ntop(AF_INET, &tmp_id, id, sizeof(id));
+ if (!IN6_IS_ADDR_UNSPECIFIED(&info->forwarding))
+ inet_ntop(AF_INET6, &info->forwarding, forwarding,
+ sizeof(forwarding));
+ else
+ snprintf(forwarding, sizeof(forwarding), ":: (ifindex %d)",
+ ospf6_route_get_first_nh_index(route));
+
+ if (use_json) {
+ json_route = json_object_new_object();
+ snprintf(route_type, sizeof(route_type), "%c",
+ zebra_route_char(info->type));
+ json_object_string_add(json_route, "routeType", route_type);
+ json_object_string_add(json_route, "destination", prefix);
+ json_object_string_add(json_route, "id", id);
+ json_object_int_add(json_route, "metricType",
+ route->path.metric_type);
+ json_object_int_add(
+ json_route, "routeCost",
+ (unsigned long)(route->path.metric_type == 2
+ ? route->path.u.cost_e2
+ : route->path.cost));
+ json_object_string_add(json_route, "forwarding", forwarding);
+
+ json_object_array_add(json_array, json_route);
+ } else
+
+ vty_out(vty, "%c %-32pFX %-15s type-%d %5lu %s\n",
+ zebra_route_char(info->type), &route->prefix, id,
+ route->path.metric_type,
+ (unsigned long)(route->path.metric_type == 2
+ ? route->path.u.cost_e2
+ : route->path.cost),
+ forwarding);
+}
+
+DEFUN(show_ipv6_ospf6_redistribute, show_ipv6_ospf6_redistribute_cmd,
+ "show ipv6 ospf6 [vrf <NAME|all>] redistribute [json]",
+ SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "redistributing External information\n" JSON_STR)
+{
+ struct ospf6_route *route;
+ struct ospf6 *ospf6 = NULL;
+ json_object *json = NULL;
+ bool uj = use_json(argc, argv);
+ struct listnode *node;
+ const char *vrf_name = NULL;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ json_object *json_array_routes = NULL;
+ json_object *json_array_redistribute = NULL;
+
+ OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_array_routes = json_object_new_array();
+ json_array_redistribute = json_object_new_array();
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
+ if (all_vrf
+ || ((ospf6->name == NULL && vrf_name == NULL)
+ || (ospf6->name && vrf_name
+ && strcmp(ospf6->name, vrf_name) == 0))) {
+ ospf6_redistribute_show_config(
+ vty, ospf6, json_array_redistribute, json, uj);
+
+ for (route = ospf6_route_head(ospf6->external_table);
+ route; route = ospf6_route_next(route)) {
+ ospf6_asbr_external_route_show(
+ vty, route, json_array_routes, uj);
+ }
+
+ if (uj) {
+ json_object_object_add(json, "routes",
+ json_array_routes);
+ vty_json(vty, json);
+ }
+
+ if (!all_vrf)
+ break;
+ }
+ }
+
+ OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6);
+
+ return CMD_SUCCESS;
+}
+
+static struct ospf6_lsa_handler as_external_handler = {
+ .lh_type = OSPF6_LSTYPE_AS_EXTERNAL,
+ .lh_name = "AS-External",
+ .lh_short_name = "ASE",
+ .lh_show = ospf6_as_external_lsa_show,
+ .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);
+
+ install_element(OSPF6_NODE, &ospf6_default_route_originate_cmd);
+ install_element(OSPF6_NODE,
+ &no_ospf6_default_information_originate_cmd);
+ install_element(OSPF6_NODE, &ospf6_redistribute_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_redistribute_cmd);
+}
+
+void ospf6_asbr_redistribute_disable(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)
+ continue;
+ if (type == ZEBRA_ROUTE_OSPF6)
+ continue;
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
+ ospf6_redist_del(ospf6, red, type);
+ }
+ red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0);
+ if (red) {
+ ospf6_asbr_routemap_unset(red);
+ ospf6_redist_del(ospf6, red, type);
+ ospf6_redistribute_default_set(ospf6, DEFAULT_ORIGINATE_NONE);
+ }
+}
+
+void ospf6_asbr_redistribute_reset(struct ospf6 *ospf6)
+{
+ int type;
+ struct ospf6_redist *red;
+ char buf[RMAP_NAME_MAXLEN];
+
+ for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) {
+ buf[0] = '\0';
+ if (type == ZEBRA_ROUTE_OSPF6)
+ continue;
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red)
+ continue;
+
+ if (type == DEFAULT_ROUTE) {
+ ospf6_redistribute_default_set(
+ ospf6, ospf6->default_originate);
+ continue;
+ }
+ if (ROUTEMAP_NAME(red))
+ strlcpy(buf, ROUTEMAP_NAME(red), sizeof(buf));
+
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
+ if (buf[0])
+ ospf6_asbr_routemap_set(red, buf);
+ ospf6_asbr_redistribute_set(ospf6, type);
+ }
+}
+
+void ospf6_asbr_terminate(void)
+{
+ /* Cleanup route maps */
+ route_map_finish();
+}
+
+DEFUN (debug_ospf6_asbr,
+ debug_ospf6_asbr_cmd,
+ "debug ospf6 asbr",
+ DEBUG_STR
+ OSPF6_STR
+ "Debug OSPFv3 ASBR function\n"
+ )
+{
+ OSPF6_DEBUG_ASBR_ON();
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf6_asbr,
+ no_debug_ospf6_asbr_cmd,
+ "no debug ospf6 asbr",
+ NO_STR
+ DEBUG_STR
+ OSPF6_STR
+ "Debug OSPFv3 ASBR function\n"
+ )
+{
+ OSPF6_DEBUG_ASBR_OFF();
+ return CMD_SUCCESS;
+}
+
+int config_write_ospf6_debug_asbr(struct vty *vty)
+{
+ if (IS_OSPF6_DEBUG_ASBR)
+ vty_out(vty, "debug ospf6 asbr\n");
+ return 0;
+}
+
+static void ospf6_default_originate_write(struct vty *vty, struct ospf6 *o)
+{
+ struct ospf6_redist *red;
+
+ vty_out(vty, " default-information originate");
+ if (o->default_originate == DEFAULT_ORIGINATE_ALWAYS)
+ vty_out(vty, " always");
+
+ red = ospf6_redist_lookup(o, DEFAULT_ROUTE, 0);
+ if (red == NULL) {
+ vty_out(vty, "\n");
+ return;
+ }
+
+ if (red->dmetric.value >= 0)
+ vty_out(vty, " metric %d", red->dmetric.value);
+
+ if (red->dmetric.type >= 0)
+ vty_out(vty, " metric-type %d", red->dmetric.type);
+
+ if (ROUTEMAP_NAME(red))
+ vty_out(vty, " route-map %s", ROUTEMAP_NAME(red));
+
+ vty_out(vty, "\n");
+}
+
+int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *o)
+{
+ if (o == NULL)
+ return 0;
+
+ /* Print default originate configuration. */
+ if (o->default_originate != DEFAULT_ORIGINATE_NONE)
+ ospf6_default_originate_write(vty, o);
+
+ return 0;
+}
+
+void install_element_ospf6_debug_asbr(void)
+{
+ install_element(ENABLE_NODE, &debug_ospf6_asbr_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf6_asbr_cmd);
+ install_element(CONFIG_NODE, &debug_ospf6_asbr_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf6_asbr_cmd);
+}
+
+/* ASBR Summarisation */
+void ospf6_fill_aggr_route_details(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct ospf6_route *rt_aggr = aggr->route;
+ struct ospf6_external_info *ei_aggr = rt_aggr->route_option;
+
+ rt_aggr->prefix = aggr->p;
+ ei_aggr->tag = aggr->tag;
+ ei_aggr->type = 0;
+ ei_aggr->id = aggr->id;
+
+ /* When metric is not configured, apply the default metric */
+ rt_aggr->path.cost = ((aggr->metric == -1) ?
+ DEFAULT_DEFAULT_METRIC
+ : (unsigned int)(aggr->metric));
+ rt_aggr->path.metric_type = aggr->mtype;
+
+ rt_aggr->path.origin.id = htonl(aggr->id);
+}
+
+static void
+ospf6_summary_add_aggr_route_and_blackhole(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct ospf6_route *rt_aggr;
+ struct ospf6_route *old_rt = NULL;
+ struct ospf6_external_info *info;
+
+ /* Check if a route is already present. */
+ if (aggr->route)
+ old_rt = aggr->route;
+
+ /* Create summary route and save it. */
+ rt_aggr = ospf6_route_create(ospf6);
+ rt_aggr->type = OSPF6_DEST_TYPE_NETWORK;
+ /* Needed to install route while calling zebra api */
+ SET_FLAG(rt_aggr->flag, OSPF6_ROUTE_BEST);
+
+ info = XCALLOC(MTYPE_OSPF6_EXTERNAL_INFO, sizeof(*info));
+ rt_aggr->route_option = info;
+ aggr->route = rt_aggr;
+
+ /* Prepare the external_info for aggregator
+ * Fill all the details which will get advertised
+ */
+ ospf6_fill_aggr_route_details(ospf6, aggr);
+
+ /* Add next-hop to Null interface. */
+ ospf6_add_route_nexthop_blackhole(rt_aggr);
+
+ /* Free the old route, if any. */
+ if (old_rt) {
+ ospf6_zebra_route_update_remove(old_rt, ospf6);
+
+ if (old_rt->route_option)
+ XFREE(MTYPE_OSPF6_EXTERNAL_INFO, old_rt->route_option);
+
+ ospf6_route_delete(old_rt);
+ }
+
+ ospf6_zebra_route_update_add(rt_aggr, ospf6);
+}
+
+static void ospf6_originate_new_aggr_lsa(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct prefix prefix_id;
+ struct ospf6_lsa *lsa = NULL;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Originate new aggregate route(%pFX)", __func__,
+ &aggr->p);
+
+ aggr->id = ospf6->external_id++;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "Advertise AS-External Id:%pI4 prefix %pFX metric %u",
+ &prefix_id.u.prefix4, &aggr->p, aggr->metric);
+
+ ospf6_summary_add_aggr_route_and_blackhole(ospf6, aggr);
+
+ /* Originate summary LSA */
+ lsa = ospf6_originate_type5_type7_lsas(aggr->route, ospf6);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Set the origination bit for aggregator",
+ __func__);
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ }
+}
+
+static void
+ospf6_aggr_handle_advertise_change(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ /* Check if advertise option modified. */
+ if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.",
+ __func__);
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+
+ return;
+ }
+
+ /* There are no routes present under this aggregation config, hence
+ * nothing to originate here
+ */
+ if (OSPF6_EXTERNAL_RT_COUNT(aggr) == 0) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: No routes present under this aggregation",
+ __func__);
+ return;
+ }
+
+ if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Now it is advertisable",
+ __func__);
+
+ ospf6_originate_new_aggr_lsa(ospf6, aggr);
+
+ return;
+ }
+}
+
+static void
+ospf6_originate_summary_lsa(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ struct ospf6_lsa *lsa = NULL, *aggr_lsa = NULL;
+ struct ospf6_external_info *info = NULL;
+ struct ospf6_external_aggr_rt *old_aggr;
+ struct ospf6_as_external_lsa *external;
+ struct ospf6_route *rt_aggr = NULL;
+ route_tag_t tag = 0;
+ unsigned int metric = 0;
+ int mtype;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Prepare to originate Summary route(%pFX)",
+ __func__, &aggr->p);
+
+ /* This case to handle when the overlapping aggregator address
+ * is available. Best match will be considered.So need to delink
+ * from old aggregator and link to the new aggr.
+ */
+ if (rt->aggr_route) {
+ if (rt->aggr_route != aggr) {
+ old_aggr = rt->aggr_route;
+ ospf6_unlink_route_from_aggr(ospf6, old_aggr, rt);
+ }
+ }
+
+ /* Add the external route to hash table */
+ ospf6_link_route_to_aggr(aggr, rt);
+
+ /* The key for ID field is a running number and not prefix */
+ info = rt->route_option;
+ assert(info);
+ if (info->id)
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id,
+ ospf6->lsdb);
+
+ aggr_lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(aggr->id), ospf6->router_id, ospf6->lsdb);
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Aggr LSA ID: %d flags %x.",
+ __func__, aggr->id, aggr->aggrflags);
+ /* Don't originate external LSA,
+ * If it is configured not to advertise.
+ */
+ if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+ /* If it is already originated as external LSA,
+ * But, it is configured not to advertise then
+ * flush the originated external lsa.
+ */
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Purge the external LSA %s.",
+ __func__, lsa->name);
+ ospf6_external_lsa_purge(ospf6, lsa);
+ info->id = 0;
+ rt->path.origin.id = 0;
+ }
+
+ if (aggr_lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Purge the aggr external LSA %s.",
+ __func__, lsa->name);
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+ }
+
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.",
+ __func__);
+ return;
+ }
+
+ /* Summary route already originated,
+ * So, Do nothing.
+ */
+ if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (!aggr_lsa) {
+ zlog_warn(
+ "%s: Could not refresh/originate %pFX",
+ __func__,
+ &aggr->p);
+ /* Remove the assert later */
+ assert(aggr_lsa);
+ return;
+ }
+
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END
+ (aggr_lsa->header);
+ metric = (unsigned long)OSPF6_ASBR_METRIC(external);
+ tag = ospf6_as_external_lsa_get_tag(aggr_lsa);
+ mtype = CHECK_FLAG(external->bits_metric,
+ OSPF6_ASBR_BIT_E) ? 2 : 1;
+
+ /* Prepare the external_info for aggregator */
+ ospf6_fill_aggr_route_details(ospf6, aggr);
+ rt_aggr = aggr->route;
+ /* If tag/metric/metric-type modified , then re-originate the
+ * route with modified tag/metric/metric-type details.
+ */
+ if ((tag != aggr->tag)
+ || (metric != (unsigned int)rt_aggr->path.cost)
+ || (mtype != aggr->mtype)) {
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Routetag(old:%d new:%d)/Metric(o:%u,n:%u)/mtype(o:%d n:%d) modified,So refresh the summary route.(%pFX)",
+ __func__, tag, aggr->tag,
+ metric,
+ aggr->metric,
+ mtype, aggr->mtype,
+ &aggr->p);
+
+ aggr_lsa = ospf6_originate_type5_type7_lsas(aggr->route,
+ ospf6);
+ if (aggr_lsa)
+ SET_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ }
+
+ return;
+ }
+
+ /* If the external route prefix same as aggregate route
+ * and if external route is already originated as TYPE-5
+ * then just update the aggr info and remove the route info
+ */
+ if (lsa && prefix_same(&aggr->p, &rt->prefix)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Route prefix is same as aggr so no need to re-originate LSA(%pFX)",
+ __PRETTY_FUNCTION__, &aggr->p);
+
+ aggr->id = info->id;
+ info->id = 0;
+ rt->path.origin.id = 0;
+
+ ospf6_summary_add_aggr_route_and_blackhole(ospf6, aggr);
+
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+
+ return;
+ }
+
+ ospf6_originate_new_aggr_lsa(ospf6, aggr);
+}
+
+static void ospf6_aggr_handle_external_info(void *data)
+{
+ struct ospf6_route *rt = (struct ospf6_route *)data;
+ struct ospf6_external_aggr_rt *aggr = NULL;
+ struct ospf6_lsa *lsa = NULL;
+ struct ospf6_external_info *info;
+ struct ospf6 *ospf6 = NULL;
+
+ rt->aggr_route = NULL;
+
+ rt->to_be_processed = true;
+
+ if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL))
+ zlog_debug("%s: Handle external route for origination/refresh (%pFX)",
+ __func__,
+ &rt->prefix);
+
+ ospf6 = rt->ospf6;
+ assert(ospf6);
+
+ aggr = ospf6_external_aggr_match(ospf6,
+ &rt->prefix);
+ if (aggr) {
+ ospf6_originate_summary_lsa(ospf6, aggr, rt);
+ return;
+ }
+
+ info = rt->route_option;
+ if (info->id) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id,
+ ospf6->lsdb);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: LSA found, refresh it",
+ __func__);
+ EVENT_OFF(lsa->refresh);
+ event_add_event(master, ospf6_lsa_refresh, lsa, 0,
+ &lsa->refresh);
+ return;
+ }
+ }
+
+ info->id = ospf6->external_id++;
+ rt->path.origin.id = htonl(info->id);
+
+ (void)ospf6_originate_type5_type7_lsas(rt, ospf6);
+}
+
+void ospf6_asbr_summary_config_delete(struct ospf6 *ospf6,
+ struct route_node *rn)
+{
+ struct ospf6_external_aggr_rt *aggr = rn->info;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Deleting Aggregate route (%pFX)",
+ __func__,
+ &aggr->p);
+
+ ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr);
+
+ rn->info = NULL;
+ route_unlock_node(rn);
+}
+
+static int
+ospf6_handle_external_aggr_modify(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct ospf6_lsa *lsa = NULL;
+ struct ospf6_as_external_lsa *asel = NULL;
+ struct ospf6_route *rt_aggr;
+ unsigned int metric = 0;
+ route_tag_t tag = 0;
+ int mtype;
+
+ lsa = ospf6_lsdb_lookup(
+ htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(aggr->id), ospf6->router_id,
+ ospf6->lsdb);
+ if (!lsa) {
+ zlog_warn(
+ "%s: Could not refresh/originate %pFX",
+ __func__,
+ &aggr->p);
+
+ return OSPF6_FAILURE;
+ }
+
+ asel = (struct ospf6_as_external_lsa *)
+ OSPF6_LSA_HEADER_END(lsa->header);
+ metric = (unsigned long)OSPF6_ASBR_METRIC(asel);
+ tag = ospf6_as_external_lsa_get_tag(lsa);
+ mtype = CHECK_FLAG(asel->bits_metric,
+ OSPF6_ASBR_BIT_E) ? 2 : 1;
+
+ /* Fill all the details for advertisement */
+ ospf6_fill_aggr_route_details(ospf6, aggr);
+ rt_aggr = aggr->route;
+ /* If tag/metric/metric-type modified , then
+ * re-originate the route with modified
+ * tag/metric/metric-type details.
+ */
+ if ((tag != aggr->tag)
+ || (metric
+ != (unsigned int)rt_aggr->path.cost)
+ || (mtype
+ != aggr->mtype)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug(
+ "%s: Changed tag(old:%d new:%d)/metric(o:%u n:%d)/mtype(o:%d n:%d),So refresh the summary route.(%pFX)",
+ __func__, tag,
+ aggr->tag,
+ metric,
+ (unsigned int)rt_aggr->path.cost,
+ mtype, aggr->mtype,
+ &aggr->p);
+
+ (void)ospf6_originate_type5_type7_lsas(
+ aggr->route,
+ ospf6);
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+static void ospf6_handle_external_aggr_update(struct ospf6 *ospf6)
+{
+ struct route_node *rn = NULL;
+ int ret;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Process modified aggregators.", __func__);
+
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ struct ospf6_external_aggr_rt *aggr;
+
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ if (aggr->action == OSPF6_ROUTE_AGGR_DEL) {
+ aggr->action = OSPF6_ROUTE_AGGR_NONE;
+ ospf6_asbr_summary_config_delete(ospf6, rn);
+
+ hash_clean_and_free(&aggr->match_extnl_hash,
+ ospf6_aggr_handle_external_info);
+
+ XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr);
+
+ } else if (aggr->action == OSPF6_ROUTE_AGGR_MODIFY) {
+
+ aggr->action = OSPF6_ROUTE_AGGR_NONE;
+
+ /* Check if tag/metric/metric-type modified */
+ if (CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_ORIGINATED)
+ && !CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+
+ ret = ospf6_handle_external_aggr_modify(ospf6,
+ aggr);
+ if (ret == OSPF6_FAILURE)
+ continue;
+ }
+
+ /* Advertise option modified ?
+ * If so, handled it here.
+ */
+ ospf6_aggr_handle_advertise_change(ospf6, aggr);
+ }
+ }
+}
+
+static void ospf6_aggr_unlink_external_info(void *data)
+{
+ struct ospf6_route *rt = (struct ospf6_route *)data;
+
+ rt->aggr_route = NULL;
+
+ rt->to_be_processed = true;
+}
+
+void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr)
+{
+ hash_clean_and_free(&aggr->match_extnl_hash,
+ ospf6_aggr_unlink_external_info);
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Release the aggregator Address(%pFX)",
+ __func__,
+ &aggr->p);
+
+ XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr);
+}
+
+static void
+ospf6_delete_all_marked_aggregators(struct ospf6 *ospf6)
+{
+ struct route_node *rn = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ /* Loop through all the aggregators, Delete all aggregators
+ * which are marked as DELETE. Set action to NONE for remaining
+ * aggregators
+ */
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ if (aggr->action != OSPF6_ROUTE_AGGR_DEL) {
+ aggr->action = OSPF6_ROUTE_AGGR_NONE;
+ continue;
+ }
+ ospf6_asbr_summary_config_delete(ospf6, rn);
+ ospf6_external_aggregator_free(aggr);
+ }
+}
+
+static void ospf6_handle_exnl_rt_after_aggr_del(struct ospf6 *ospf6,
+ struct ospf6_route *rt)
+{
+ struct ospf6_lsa *lsa;
+
+ /* Process only marked external routes.
+ * These routes were part of a deleted
+ * aggregator.So, originate now.
+ */
+ if (!rt->to_be_processed)
+ return;
+
+ rt->to_be_processed = false;
+
+ lsa = ospf6_find_external_lsa(ospf6, &rt->prefix);
+
+ if (lsa) {
+ EVENT_OFF(lsa->refresh);
+ event_add_event(master, ospf6_lsa_refresh, lsa, 0,
+ &lsa->refresh);
+ } else {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Originate external route(%pFX)",
+ __func__,
+ &rt->prefix);
+
+ (void)ospf6_originate_type5_type7_lsas(rt, ospf6);
+ }
+}
+
+static void ospf6_handle_aggregated_exnl_rt(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ struct ospf6_route *rt)
+{
+ struct ospf6_lsa *lsa;
+ struct ospf6_as_external_lsa *ext_lsa;
+ struct ospf6_external_info *info;
+
+ /* Handling the case where the external route prefix
+ * and aggegate prefix is same
+ * If same don't flush the originated external LSA.
+ */
+ if (prefix_same(&aggr->p, &rt->prefix)) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: External Route prefix same as Aggregator(%pFX), so don't flush.",
+ __func__,
+ &rt->prefix);
+
+ return;
+ }
+
+ info = rt->route_option;
+ assert(info);
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id), ospf6->router_id, ospf6->lsdb);
+ if (lsa) {
+ ext_lsa = (struct ospf6_as_external_lsa
+ *)((char *)(lsa->header)
+ + sizeof(struct ospf6_lsa_header));
+
+ if (rt->prefix.prefixlen != ext_lsa->prefix.prefix_length)
+ return;
+
+ ospf6_external_lsa_purge(ospf6, lsa);
+
+ /* Resetting the ID of route */
+ rt->path.origin.id = 0;
+ info->id = 0;
+ }
+}
+
+static void
+ospf6_handle_external_aggr_add(struct ospf6 *ospf6)
+{
+ struct ospf6_route *rt = NULL;
+ struct ospf6_external_info *ei = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ /* Delete all the aggregators which are marked as
+ * OSPF6_ROUTE_AGGR_DEL.
+ */
+ ospf6_delete_all_marked_aggregators(ospf6);
+
+ for (rt = ospf6_route_head(ospf6->external_table); rt;
+ rt = ospf6_route_next(rt)) {
+ ei = rt->route_option;
+ if (ei == NULL)
+ continue;
+
+ if (is_default_prefix(&rt->prefix))
+ continue;
+
+ aggr = ospf6_external_aggr_match(ospf6,
+ &rt->prefix);
+
+ /* If matching aggregator found, Add
+ * the external route refrenace to the
+ * aggregator and originate the aggr
+ * route if it is advertisable.
+ * flush the external LSA if it is
+ * already originated for this external
+ * prefix.
+ */
+ if (aggr) {
+ ospf6_originate_summary_lsa(ospf6, aggr, rt);
+
+ /* All aggregated external rts
+ * are handled here.
+ */
+ ospf6_handle_aggregated_exnl_rt(
+ ospf6, aggr, rt);
+ continue;
+ }
+
+ /* External routes which are only out
+ * of aggregation will be handled here.
+ */
+ ospf6_handle_exnl_rt_after_aggr_del(
+ ospf6, rt);
+ }
+}
+
+static void ospf6_asbr_summary_process(struct event *thread)
+{
+ struct ospf6 *ospf6 = EVENT_ARG(thread);
+ int operation = 0;
+
+ operation = ospf6->aggr_action;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: operation:%d",
+ __func__,
+ operation);
+
+ switch (operation) {
+ case OSPF6_ROUTE_AGGR_ADD:
+ ospf6_handle_external_aggr_add(ospf6);
+ break;
+ case OSPF6_ROUTE_AGGR_DEL:
+ case OSPF6_ROUTE_AGGR_MODIFY:
+ ospf6_handle_external_aggr_update(ospf6);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr,
+ ospf6_aggr_action_t operation)
+{
+ aggr->action = operation;
+
+ if (event_is_scheduled(ospf6->t_external_aggr)) {
+ if (ospf6->aggr_action == OSPF6_ROUTE_AGGR_ADD) {
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Not required to restart timer,set is already added.",
+ __func__);
+ return;
+ }
+
+ if (operation == OSPF6_ROUTE_AGGR_ADD) {
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s, Restarting Aggregator delay timer.",
+ __func__);
+ EVENT_OFF(ospf6->t_external_aggr);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Start Aggregator delay timer %u(in seconds).",
+ __func__, ospf6->aggr_delay_interval);
+
+ ospf6->aggr_action = operation;
+ event_add_timer(master, ospf6_asbr_summary_process, ospf6,
+ ospf6->aggr_delay_interval, &ospf6->t_external_aggr);
+}
+
+int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6,
+ struct prefix *p)
+{
+ struct route_node *rn;
+ struct ospf6_external_aggr_rt *aggr;
+
+ rn = route_node_lookup(ospf6->rt_aggr_tbl, p);
+ if (!rn)
+ return OSPF6_INVALID;
+
+ aggr = rn->info;
+
+ route_unlock_node(rn);
+
+ if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ return OSPF6_INVALID;
+
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr))
+ return OSPF6_SUCCESS;
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_MODIFY);
+
+ return OSPF6_SUCCESS;
+}
+
+int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6, uint16_t interval)
+{
+ ospf6->aggr_delay_interval = interval;
+
+ return OSPF6_SUCCESS;
+}
+
+static unsigned int ospf6_external_rt_hash_key(const void *data)
+{
+ const struct ospf6_route *rt = data;
+ unsigned int key = 0;
+
+ key = prefix_hash_key(&rt->prefix);
+ return key;
+}
+
+static bool ospf6_external_rt_hash_cmp(const void *d1, const void *d2)
+{
+ const struct ospf6_route *rt1 = d1;
+ const struct ospf6_route *rt2 = d2;
+
+ return prefix_same(&rt1->prefix, &rt2->prefix);
+}
+
+static struct ospf6_external_aggr_rt *
+ospf6_external_aggr_new(struct prefix *p)
+{
+ struct ospf6_external_aggr_rt *aggr;
+
+ aggr = XCALLOC(MTYPE_OSPF6_EXTERNAL_RT_AGGR,
+ sizeof(struct ospf6_external_aggr_rt));
+
+ prefix_copy(&aggr->p, p);
+ aggr->metric = -1;
+ aggr->mtype = DEFAULT_METRIC_TYPE;
+ aggr->match_extnl_hash = hash_create(ospf6_external_rt_hash_key,
+ ospf6_external_rt_hash_cmp,
+ "Ospf6 external route hash");
+ return aggr;
+}
+
+static void ospf6_external_aggr_add(struct ospf6 *ospf6,
+ struct ospf6_external_aggr_rt *aggr)
+{
+ struct route_node *rn;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Adding Aggregate route to Aggr table (%pFX)",
+ __func__,
+ &aggr->p);
+
+ rn = route_node_get(ospf6->rt_aggr_tbl, &aggr->p);
+ if (rn->info)
+ route_unlock_node(rn);
+ else
+ rn->info = aggr;
+}
+
+int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6,
+ struct prefix *p)
+{
+ struct ospf6_external_aggr_rt *aggr;
+ route_tag_t tag = 0;
+
+ aggr = ospf6_external_aggr_config_lookup(ospf6, p);
+ if (aggr) {
+ if (CHECK_FLAG(aggr->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ return OSPF6_SUCCESS;
+
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+
+ aggr->tag = tag;
+ aggr->metric = -1;
+
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr))
+ return OSPF6_SUCCESS;
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_MODIFY);
+ } else {
+ aggr = ospf6_external_aggr_new(p);
+
+ if (!aggr)
+ return OSPF6_FAILURE;
+
+ SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+ ospf6_external_aggr_add(ospf6, aggr);
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_ADD);
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+struct ospf6_external_aggr_rt *
+ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p)
+{
+ struct route_node *rn;
+
+ rn = route_node_lookup(ospf6->rt_aggr_tbl, p);
+ if (rn) {
+ route_unlock_node(rn);
+ return rn->info;
+ }
+
+ return NULL;
+}
+
+
+int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p,
+ route_tag_t tag, int metric, int mtype)
+{
+ struct ospf6_external_aggr_rt *aggregator;
+
+ aggregator = ospf6_external_aggr_config_lookup(ospf6, p);
+
+ if (aggregator) {
+ if (CHECK_FLAG(aggregator->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE))
+ UNSET_FLAG(aggregator->aggrflags,
+ OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE);
+ else if ((aggregator->tag == tag)
+ && (aggregator->metric == metric)
+ && (aggregator->mtype == mtype))
+ return OSPF6_SUCCESS;
+
+ aggregator->tag = tag;
+ aggregator->metric = metric;
+ aggregator->mtype = mtype;
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggregator,
+ OSPF6_ROUTE_AGGR_MODIFY);
+ } else {
+ aggregator = ospf6_external_aggr_new(p);
+ if (!aggregator)
+ return OSPF6_FAILURE;
+
+ aggregator->tag = tag;
+ aggregator->metric = metric;
+ aggregator->mtype = mtype;
+
+ ospf6_external_aggr_add(ospf6, aggregator);
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggregator,
+ OSPF6_ROUTE_AGGR_ADD);
+ }
+
+ return OSPF6_SUCCESS;
+}
+
+int ospf6_external_aggr_config_unset(struct ospf6 *ospf6,
+ struct prefix *p)
+{
+ struct route_node *rn;
+ struct ospf6_external_aggr_rt *aggr;
+
+ rn = route_node_lookup(ospf6->rt_aggr_tbl, p);
+ if (!rn)
+ return OSPF6_INVALID;
+
+ aggr = rn->info;
+
+ route_unlock_node(rn);
+
+ if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) {
+ ospf6_asbr_summary_config_delete(ospf6, rn);
+ ospf6_external_aggregator_free(aggr);
+ return OSPF6_SUCCESS;
+ }
+
+ ospf6_start_asbr_summary_delay_timer(ospf6, aggr,
+ OSPF6_ROUTE_AGGR_DEL);
+
+ return OSPF6_SUCCESS;
+}
+
+void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6,
+ struct ospf6_route *rt,
+ struct prefix *p)
+{
+
+ struct ospf6_external_aggr_rt *aggr;
+ struct ospf6_external_info *info;
+ struct prefix prefix_id;
+
+ if (!is_default_prefix(p)) {
+ aggr = ospf6_external_aggr_match(ospf6,
+ p);
+
+ if (aggr) {
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("%s: Send Aggregate LSA (%pFX)",
+ __func__,
+ &aggr->p);
+
+ ospf6_originate_summary_lsa(
+ ospf6, aggr, rt);
+
+ /* Handling the case where the
+ * external route prefix
+ * and aggegate prefix is same
+ * If same don't flush the
+ * originated
+ * external LSA.
+ */
+ ospf6_handle_aggregated_exnl_rt(
+ ospf6, aggr, rt);
+ return;
+ }
+ }
+
+ info = rt->route_option;
+
+ /* When the info->id = 0, it means it is being originated for the
+ * first time.
+ */
+ if (!info->id) {
+ info->id = ospf6->external_id++;
+ } else {
+ prefix_id.family = AF_INET;
+ prefix_id.prefixlen = 32;
+ prefix_id.u.prefix4.s_addr = htonl(info->id);
+ }
+
+ rt->path.origin.id = htonl(info->id);
+
+ if (IS_OSPF6_DEBUG_ASBR) {
+ zlog_debug("Advertise new AS-External Id:%pI4 prefix %pFX metric %u",
+ &prefix_id.u.prefix4, p, rt->path.metric_type);
+ }
+
+ ospf6_originate_type5_type7_lsas(rt, ospf6);
+
+}
+
+void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6)
+{
+ struct route_node *rn = NULL;
+ struct ospf6_external_aggr_rt *aggr;
+
+ if (IS_OSPF6_DEBUG_AGGR)
+ zlog_debug("Unset the origination bit for all aggregator");
+
+ /* Resetting the running external ID counter so that the origination
+ * of external LSAs starts from the beginning 0.0.0.1
+ */
+ ospf6->external_id = OSPF6_EXT_INIT_LS_ID;
+
+ for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED);
+ }
+}