summaryrefslogtreecommitdiffstats
path: root/ospfd/ospf_asbr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ospfd/ospf_asbr.c')
-rw-r--r--ospfd/ospf_asbr.c1224
1 files changed, 1224 insertions, 0 deletions
diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c
new file mode 100644
index 0000000..5baad17
--- /dev/null
+++ b/ospfd/ospf_asbr.c
@@ -0,0 +1,1224 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OSPF AS Boundary Router functions.
+ * Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada
+ */
+
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "memory.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "vty.h"
+#include "filter.h"
+#include "log.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_errors.h"
+
+/* Remove external route. */
+void ospf_external_route_remove(struct ospf *ospf, struct prefix_ipv4 *p)
+{
+ struct route_node *rn;
+ struct ospf_route * or ;
+
+ rn = route_node_lookup(ospf->old_external_route, (struct prefix *)p);
+ if (rn)
+ if ((or = rn->info)) {
+ zlog_info("Route[%pFX]: external path deleted", p);
+
+ /* Remove route from zebra. */
+ if (or->type == OSPF_DESTINATION_NETWORK)
+ ospf_zebra_delete(
+ ospf, (struct prefix_ipv4 *)&rn->p, or);
+
+ ospf_route_free(or);
+ rn->info = NULL;
+
+ route_unlock_node(rn);
+ route_unlock_node(rn);
+ return;
+ }
+
+ zlog_info("Route[%pFX]: no such external path", p);
+}
+
+/* Add an External info for AS-external-LSA. */
+struct external_info *ospf_external_info_new(struct ospf *ospf, uint8_t type,
+ unsigned short instance)
+{
+ struct external_info *new;
+
+ new = XCALLOC(MTYPE_OSPF_EXTERNAL_INFO, sizeof(struct external_info));
+ new->ospf = ospf;
+ new->type = type;
+ new->instance = instance;
+ new->to_be_processed = 0;
+
+ ospf_reset_route_map_set_values(&new->route_map_set);
+ return new;
+}
+
+static void ospf_external_info_free(struct external_info *ei)
+{
+ XFREE(MTYPE_OSPF_EXTERNAL_INFO, ei);
+}
+
+void ospf_reset_route_map_set_values(struct route_map_set_values *values)
+{
+ values->metric = -1;
+ values->metric_type = -1;
+}
+
+int ospf_route_map_set_compare(struct route_map_set_values *values1,
+ struct route_map_set_values *values2)
+{
+ return values1->metric == values2->metric
+ && values1->metric_type == values2->metric_type;
+}
+
+/* Add an External info for AS-external-LSA. */
+struct external_info *
+ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance,
+ struct prefix_ipv4 p, ifindex_t ifindex,
+ struct in_addr nexthop, route_tag_t tag, uint32_t metric)
+{
+ struct external_info *new;
+ struct route_node *rn;
+ struct ospf_external *ext;
+
+ ext = ospf_external_lookup(ospf, type, instance);
+ if (!ext)
+ ext = ospf_external_add(ospf, type, instance);
+
+ rn = route_node_get(EXTERNAL_INFO(ext), (struct prefix *)&p);
+ /* If old info exists, -- discard new one or overwrite with new one? */
+ if (rn && rn->info) {
+ new = rn->info;
+ if ((new->ifindex == ifindex)
+ && (new->nexthop.s_addr == nexthop.s_addr)
+ && (new->tag == tag)) {
+ route_unlock_node(rn);
+ return NULL; /* NULL => no LSA to refresh */
+ }
+
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "Redistribute[%s][%d][%u]: %pFX discarding old info with NH %pI4.",
+ ospf_redist_string(type), instance,
+ ospf->vrf_id, &p, &nexthop.s_addr);
+ XFREE(MTYPE_OSPF_EXTERNAL_INFO, rn->info);
+ }
+
+ /* Create new External info instance. */
+ new = ospf_external_info_new(ospf, type, instance);
+ new->p = p;
+ new->ifindex = ifindex;
+ new->nexthop = nexthop;
+ new->tag = tag;
+ new->orig_tag = tag;
+ new->aggr_route = NULL;
+ new->metric = metric;
+ new->min_metric = 0;
+ new->max_metric = OSPF_LS_INFINITY;
+
+ /* we don't unlock rn from the get() because we're attaching the info */
+ if (rn)
+ rn->info = new;
+
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ zlog_debug(
+ "Redistribute[%s][%u]: %pFX external info created, with NH %pI4, metric:%u",
+ ospf_redist_string(type), ospf->vrf_id, &p,
+ &nexthop.s_addr, metric);
+ }
+ return new;
+}
+
+void ospf_external_info_delete(struct ospf *ospf, uint8_t type,
+ unsigned short instance, struct prefix_ipv4 p)
+{
+ struct route_node *rn;
+ struct ospf_external *ext;
+
+ ext = ospf_external_lookup(ospf, type, instance);
+ if (!ext)
+ return;
+
+ rn = route_node_lookup(EXTERNAL_INFO(ext), (struct prefix *)&p);
+ if (rn) {
+ ospf_external_info_free(rn->info);
+ rn->info = NULL;
+ route_unlock_node(rn);
+ route_unlock_node(rn);
+ }
+}
+
+struct external_info *ospf_external_info_lookup(struct ospf *ospf, uint8_t type,
+ unsigned short instance,
+ struct prefix_ipv4 *p)
+{
+ struct route_node *rn;
+ struct ospf_external *ext;
+
+ ext = ospf_external_lookup(ospf, type, instance);
+ if (!ext)
+ return NULL;
+
+ rn = route_node_lookup(EXTERNAL_INFO(ext), (struct prefix *)p);
+ if (rn) {
+ route_unlock_node(rn);
+ if (rn->info)
+ return rn->info;
+ }
+
+ return NULL;
+}
+
+struct ospf_lsa *ospf_external_info_find_lsa(struct ospf *ospf,
+ struct prefix_ipv4 *p)
+{
+ struct ospf_lsa *lsa;
+ struct as_external_lsa *al;
+ struct in_addr mask, id;
+
+ /* First search the lsdb with address specific LSID
+ * where all the host bits are set, if there a matched
+ * LSA, return.
+ * Ex: For route 10.0.0.0/16, LSID is 10.0.255.255
+ * If no lsa with above LSID, use received address as
+ * LSID and check if any LSA in LSDB.
+ * If LSA found, check if the mask is same b/w the matched
+ * LSA and received prefix, if same then it is the LSA for
+ * this prefix.
+ * Ex: For route 10.0.0.0/16, LSID is 10.0.0.0
+ */
+
+ masklen2ip(p->prefixlen, &mask);
+ id.s_addr = p->prefix.s_addr | (~mask.s_addr);
+ lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, id,
+ ospf->router_id);
+ if (lsa) {
+ if (p->prefixlen == IPV4_MAX_BITLEN) {
+ al = (struct as_external_lsa *)lsa->data;
+
+ if (mask.s_addr != al->mask.s_addr)
+ return NULL;
+ }
+ return lsa;
+ }
+
+ lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA,
+ p->prefix, ospf->router_id);
+
+ if (lsa) {
+ al = (struct as_external_lsa *)lsa->data;
+ if (mask.s_addr == al->mask.s_addr)
+ return lsa;
+ }
+
+ return NULL;
+}
+
+
+/* Update ASBR status. */
+void ospf_asbr_status_update(struct ospf *ospf, uint8_t status)
+{
+ zlog_info("ASBR[%s:Status:%d]: Update",
+ ospf_get_name(ospf), status);
+
+ /* ASBR on. */
+ if (status) {
+ /* Already ASBR. */
+ if (IS_OSPF_ASBR(ospf)) {
+ zlog_info("ASBR[%s:Status:%d]: Already ASBR",
+ ospf_get_name(ospf), status);
+ return;
+ }
+ SET_FLAG(ospf->flags, OSPF_FLAG_ASBR);
+ } else {
+ /* Already non ASBR. */
+ if (!IS_OSPF_ASBR(ospf)) {
+ zlog_info("ASBR[%s:Status:%d]: Already non ASBR",
+ ospf_get_name(ospf), status);
+ return;
+ }
+ UNSET_FLAG(ospf->flags, OSPF_FLAG_ASBR);
+ }
+
+ /* Transition from/to status ASBR, schedule timer. */
+ ospf_spf_calculate_schedule(ospf, SPF_FLAG_ASBR_STATUS_CHANGE);
+ ospf_router_lsa_update(ospf);
+}
+
+/* If there's redistribution configured, we need to refresh external
+ * LSAs (e.g. when default-metric changes or NSSA settings change).
+ */
+static void ospf_asbr_redist_update_timer(struct event *thread)
+{
+ struct ospf *ospf = EVENT_ARG(thread);
+ int type;
+
+ ospf->t_asbr_redist_update = NULL;
+
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("Running ASBR redistribution update on timer");
+
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ struct list *red_list;
+ struct listnode *node;
+ struct ospf_redist *red;
+
+ red_list = ospf->redist[type];
+ if (!red_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red))
+ ospf_external_lsa_refresh_type(ospf, type,
+ red->instance,
+ LSA_REFRESH_FORCE);
+ }
+
+ ospf_external_lsa_refresh_default(ospf);
+}
+
+void ospf_schedule_asbr_redist_update(struct ospf *ospf)
+{
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("Scheduling ASBR redistribution update");
+
+ event_add_timer(master, ospf_asbr_redist_update_timer, ospf,
+ OSPF_ASBR_REDIST_UPDATE_DELAY,
+ &ospf->t_asbr_redist_update);
+}
+
+void ospf_redistribute_withdraw(struct ospf *ospf, uint8_t type,
+ unsigned short instance)
+{
+ struct route_node *rn;
+ struct external_info *ei;
+ struct ospf_external *ext;
+
+ ext = ospf_external_lookup(ospf, type, instance);
+ if (!ext)
+ return;
+
+ /* Delete external info for specified type. */
+ if (!EXTERNAL_INFO(ext))
+ return;
+
+ for (rn = route_top(EXTERNAL_INFO(ext)); rn; rn = route_next(rn)) {
+ ei = rn->info;
+
+ if (!ei)
+ continue;
+
+ struct ospf_external_aggr_rt *aggr;
+
+ if (is_default_prefix4(&ei->p)
+ && ospf->default_originate != DEFAULT_ORIGINATE_NONE)
+ continue;
+
+ aggr = ei->aggr_route;
+
+ if (aggr)
+ ospf_unlink_ei_from_aggr(ospf, aggr, ei);
+ else if (ospf_external_info_find_lsa(ospf, &ei->p))
+ ospf_external_lsa_flush(ospf, type, &ei->p,
+ ei->ifindex /*, ei->nexthop */);
+
+ ospf_external_info_free(ei);
+ route_unlock_node(rn);
+ rn->info = NULL;
+ }
+}
+
+
+/* External Route Aggregator Handlers */
+bool is_valid_summary_addr(struct prefix_ipv4 *p)
+{
+ /* Default prefix validation*/
+ if (p->prefix.s_addr == INADDR_ANY)
+ return false;
+
+ /*Host route shouldn't be configured as summary addres*/
+ if (p->prefixlen == IPV4_MAX_BITLEN)
+ return false;
+
+ return true;
+}
+void ospf_asbr_external_aggregator_init(struct ospf *instance)
+{
+ instance->rt_aggr_tbl = route_table_init();
+
+ instance->t_external_aggr = NULL;
+
+ instance->aggr_action = 0;
+
+ instance->aggr_delay_interval = OSPF_EXTL_AGGR_DEFAULT_DELAY;
+}
+
+static unsigned int ospf_external_rt_hash_key(const void *data)
+{
+ const struct external_info *ei = data;
+ unsigned int key = 0;
+
+ key = prefix_hash_key(&ei->p);
+ return key;
+}
+
+static bool ospf_external_rt_hash_cmp(const void *d1, const void *d2)
+{
+ const struct external_info *ei1 = d1;
+ const struct external_info *ei2 = d2;
+
+ return prefix_same((struct prefix *)&ei1->p, (struct prefix *)&ei2->p);
+}
+
+static struct ospf_external_aggr_rt *
+ospf_external_aggregator_new(struct prefix_ipv4 *p)
+{
+ struct ospf_external_aggr_rt *aggr;
+
+ aggr = (struct ospf_external_aggr_rt *)XCALLOC(
+ MTYPE_OSPF_EXTERNAL_RT_AGGR,
+ sizeof(struct ospf_external_aggr_rt));
+
+ if (!aggr)
+ return NULL;
+
+ aggr->p.family = p->family;
+ aggr->p.prefix = p->prefix;
+ aggr->p.prefixlen = p->prefixlen;
+ aggr->match_extnl_hash = hash_create(ospf_external_rt_hash_key,
+ ospf_external_rt_hash_cmp,
+ "Ospf external route hash");
+ return aggr;
+}
+
+static void ospf_aggr_handle_external_info(void *data)
+{
+ struct external_info *ei = (struct external_info *)data;
+ struct ospf_external_aggr_rt *aggr = NULL;
+ struct ospf *ospf = ei->ospf;
+ struct ospf_lsa *lsa = NULL;
+
+ ei->aggr_route = NULL;
+
+ ei->to_be_processed = true;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Handle extrenal route(%pI4/%d)", __func__,
+ &ei->p.prefix, ei->p.prefixlen);
+
+ assert(ospf);
+
+ if (!ospf_redistribute_check(ospf, ei, NULL))
+ return;
+
+ aggr = ospf_external_aggr_match(ospf, &ei->p);
+ if (aggr) {
+ (void)ospf_originate_summary_lsa(ospf, aggr, ei);
+ return;
+ }
+
+ lsa = ospf_external_info_find_lsa(ospf, &ei->p);
+ if (lsa)
+ ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE, 1);
+ else
+ (void)ospf_external_lsa_originate(ospf, ei);
+}
+
+static void ospf_aggr_unlink_external_info(void *data)
+{
+ struct external_info *ei = (struct external_info *)data;
+
+ ei->aggr_route = NULL;
+
+ ei->to_be_processed = true;
+}
+
+void ospf_external_aggregator_free(struct ospf_external_aggr_rt *aggr)
+{
+ hash_clean_and_free(&aggr->match_extnl_hash,
+ (void *)ospf_aggr_unlink_external_info);
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Release the aggregator Address(%pI4/%d)",
+ __func__, &aggr->p.prefix, aggr->p.prefixlen);
+
+ XFREE(MTYPE_OSPF_EXTERNAL_RT_AGGR, aggr);
+}
+
+static void ospf_external_aggr_add(struct ospf *ospf,
+ struct ospf_external_aggr_rt *aggr)
+{
+ struct route_node *rn;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Adding Aggregate route to Aggr table (%pI4/%d)",
+ __func__, &aggr->p.prefix, aggr->p.prefixlen);
+ rn = route_node_get(ospf->rt_aggr_tbl, (struct prefix *)&aggr->p);
+ if (rn->info)
+ route_unlock_node(rn);
+ else
+ rn->info = aggr;
+}
+
+static void ospf_external_aggr_delete(struct ospf *ospf, struct route_node *rn)
+{
+ struct ospf_external_aggr_rt *aggr = rn->info;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Deleting Aggregate route (%pI4/%d)", __func__,
+ &aggr->p.prefix, aggr->p.prefixlen);
+
+ /* Sent a Max age LSA if it is already originated. */
+ if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Flushing Aggregate route (%pI4/%d)",
+ __func__, &aggr->p.prefix,
+ aggr->p.prefixlen);
+ ospf_external_lsa_flush(ospf, 0, &aggr->p, 0);
+ }
+
+ rn->info = NULL;
+ route_unlock_node(rn);
+}
+
+struct ospf_external_aggr_rt *
+ospf_extrenal_aggregator_lookup(struct ospf *ospf, struct prefix_ipv4 *p)
+{
+ struct route_node *rn;
+ struct ospf_external_aggr_rt *summary_rt = NULL;
+
+ rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p);
+ if (rn) {
+ summary_rt = rn->info;
+ route_unlock_node(rn);
+ return summary_rt;
+ }
+ return NULL;
+}
+
+struct ospf_external_aggr_rt *ospf_external_aggr_match(struct ospf *ospf,
+ struct prefix_ipv4 *p)
+{
+ struct route_node *node;
+ struct ospf_external_aggr_rt *summary_rt = NULL;
+
+ node = route_node_match(ospf->rt_aggr_tbl, (struct prefix *)p);
+ if (node) {
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ if (node->info) {
+ struct ospf_external_aggr_rt *ag = node->info;
+
+ zlog_debug(
+ "%s: Matching aggregator found.prefix:%pI4/%d Aggregator %pI4/%d",
+ __func__, &p->prefix, p->prefixlen,
+ &ag->p.prefix, ag->p.prefixlen);
+ }
+
+ summary_rt = node->info;
+ route_unlock_node(node);
+ return summary_rt;
+ }
+ return NULL;
+}
+
+void ospf_unlink_ei_from_aggr(struct ospf *ospf,
+ struct ospf_external_aggr_rt *aggr,
+ struct external_info *ei)
+{
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Unlinking extrenal route(%pI4/%d) from aggregator(%pI4/%d), external route count:%ld",
+ __func__, &ei->p.prefix, ei->p.prefixlen,
+ &aggr->p.prefix, aggr->p.prefixlen,
+ OSPF_EXTERNAL_RT_COUNT(aggr));
+ hash_release(aggr->match_extnl_hash, ei);
+ ei->aggr_route = NULL;
+
+ /* Flush the aggreagte route if matching
+ * external route count becomes zero.
+ */
+ if (!OSPF_EXTERNAL_RT_COUNT(aggr)
+ && CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) {
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Flushing the aggreagte route (%pI4/%d)",
+ __func__, &aggr->p.prefix,
+ aggr->p.prefixlen);
+
+ /* Flush the aggregate LSA */
+ ospf_external_lsa_flush(ospf, 0, &aggr->p, 0);
+
+ /* Unset the Origination flag */
+ UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ }
+}
+
+static void ospf_link_ei_to_aggr(struct ospf_external_aggr_rt *aggr,
+ struct external_info *ei)
+{
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Linking extrenal route(%pI4/%d) to aggregator(%pI4/%d)",
+ __func__, &ei->p.prefix, ei->p.prefixlen,
+ &aggr->p.prefix, aggr->p.prefixlen);
+ (void)hash_get(aggr->match_extnl_hash, ei, hash_alloc_intern);
+ ei->aggr_route = aggr;
+}
+
+struct ospf_lsa *ospf_originate_summary_lsa(struct ospf *ospf,
+ struct ospf_external_aggr_rt *aggr,
+ struct external_info *ei)
+{
+ struct ospf_lsa *lsa;
+ struct external_info ei_aggr;
+ struct as_external_lsa *asel;
+ struct ospf_external_aggr_rt *old_aggr;
+ route_tag_t tag = 0;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Prepare to originate Summary route(%pI4/%d)",
+ __func__, &aggr->p.prefix, aggr->p.prefixlen);
+
+ /* This case to handle when the overlapping aggregator address
+ * is availbe.Best match will be considered.So need to delink
+ * from old aggregator and link to the new aggr.
+ */
+ if (ei->aggr_route) {
+ if (ei->aggr_route != aggr) {
+ old_aggr = ei->aggr_route;
+ ospf_unlink_ei_from_aggr(ospf, old_aggr, ei);
+ }
+ }
+
+ /* Add the external route to hash table */
+ ospf_link_ei_to_aggr(aggr, ei);
+
+ lsa = ospf_external_info_find_lsa(ospf, &aggr->p);
+ /* Don't originate external LSA,
+ * If it is configured not to advertise.
+ */
+ if (CHECK_FLAG(aggr->flags, OSPF_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)
+ ospf_external_lsa_flush(ospf, 0, &aggr->p, 0);
+ UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Don't originate the summary address,It is configured to not-advertise.",
+ __func__);
+ return NULL;
+ }
+
+ /* Prepare the extrenal_info for aggregator */
+ memset(&ei_aggr, 0, sizeof(ei_aggr));
+ ei_aggr.p = aggr->p;
+ ei_aggr.tag = aggr->tag;
+ ei_aggr.type = 0;
+ ei_aggr.instance = ospf->instance;
+ ei_aggr.route_map_set.metric = -1;
+ ei_aggr.route_map_set.metric_type = -1;
+
+ /* Summary route already originated,
+ * So, Do nothing.
+ */
+ if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (!lsa) {
+ flog_warn(EC_OSPF_LSA_MISSING,
+ "%s: Could not refresh/originate %pI4/%d",
+ __func__, &aggr->p.prefix, aggr->p.prefixlen);
+ return NULL;
+ }
+
+ asel = (struct as_external_lsa *)lsa->data;
+ tag = (unsigned long)ntohl(asel->e[0].route_tag);
+
+ /* If tag modified , then re-originate the route
+ * with modified tag details.
+ */
+ if (tag != ei_aggr.tag) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Route tag changed(old:%d new:%d,So refresh the summary route.(%pI4/%d)",
+ __func__, tag, ei_aggr.tag,
+ &aggr->p.prefix, aggr->p.prefixlen);
+
+ ospf_external_lsa_refresh(ospf, lsa, &ei_aggr,
+ LSA_REFRESH_FORCE, 1);
+ }
+ return lsa;
+ }
+
+ if (lsa && IS_LSA_MAXAGE(lsa)) {
+ /* This is special case.
+ * If a summary route need to be originated but where
+ * summary route already exist in lsdb with maxage, then
+ * it need to be refreshed.
+ */
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: LSA is in MAX-AGE so refreshing LSA(%pI4/%d)",
+ __func__, &aggr->p.prefix, aggr->p.prefixlen);
+
+ ospf_external_lsa_refresh(ospf, lsa, &ei_aggr,
+ LSA_REFRESH_FORCE, 1);
+ SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ return lsa;
+ }
+
+ /* If the external route prefix same as aggregate route
+ * and if external route is already originated as TYPE-5
+ * then it need to be refreshed and originate bit should
+ * be set.
+ */
+ if (lsa && prefix_same((struct prefix *)&ei_aggr.p,
+ (struct prefix *)&ei->p)) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: External route prefix is same as aggr so refreshing LSA(%pI4/%d)",
+ __func__, &aggr->p.prefix, aggr->p.prefixlen);
+ ospf_external_lsa_refresh(ospf, lsa, &ei_aggr,
+ LSA_REFRESH_FORCE, 1);
+ SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ return lsa;
+ }
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Originate Summary route(%pI4/%d)", __func__,
+ &aggr->p.prefix, aggr->p.prefixlen);
+
+ /* Originate summary LSA */
+ lsa = ospf_external_lsa_originate(ospf, &ei_aggr);
+ if (lsa) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Set the origination bit for aggregator",
+ __func__);
+ SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ }
+
+ return lsa;
+}
+void ospf_unset_all_aggr_flag(struct ospf *ospf)
+{
+ struct route_node *rn = NULL;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("Unset the origination bit for all aggregator");
+
+ for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ struct ospf_external_aggr_rt *aggr = rn->info;
+
+ UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ }
+}
+
+static void ospf_delete_all_marked_aggregators(struct ospf *ospf)
+{
+ struct route_node *rn = NULL;
+
+ /* Loop through all the aggregators, Delete all aggregators
+ * which are marked as DELETE. Set action to NONE for remaining
+ * aggregators
+ */
+ for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ struct ospf_external_aggr_rt *aggr = rn->info;
+
+ if (aggr->action != OSPF_ROUTE_AGGR_DEL) {
+ aggr->action = OSPF_ROUTE_AGGR_NONE;
+ continue;
+ }
+ ospf_external_aggr_delete(ospf, rn);
+ ospf_external_aggregator_free(aggr);
+ }
+}
+
+static void ospf_handle_aggregated_exnl_rt(struct ospf *ospf,
+ struct ospf_external_aggr_rt *aggr,
+ struct external_info *ei)
+{
+ struct ospf_lsa *lsa;
+ struct as_external_lsa *al;
+ struct in_addr mask;
+
+ /* Handling the case where the external route prefix
+ * and aggregate prefix is same
+ * If same don't flush the originated external LSA.
+ */
+ if (prefix_same((struct prefix *)&aggr->p, (struct prefix *)&ei->p)) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: External Route prefix same as Aggregator(%pI4/%d), so don't flush.",
+ __func__, &ei->p.prefix, ei->p.prefixlen);
+ return;
+ }
+
+ lsa = ospf_external_info_find_lsa(ospf, &ei->p);
+ if (lsa) {
+ al = (struct as_external_lsa *)lsa->data;
+ masklen2ip(ei->p.prefixlen, &mask);
+
+ if (mask.s_addr != al->mask.s_addr)
+ return;
+
+ ospf_external_lsa_flush(ospf, ei->type, &ei->p, 0);
+ }
+}
+
+static void ospf_handle_exnl_rt_after_aggr_del(struct ospf *ospf,
+ struct external_info *ei)
+{
+ struct ospf_lsa *lsa;
+
+ /* Process only marked external routes.
+ * These routes were part of a deleted
+ * aggregator.So, originate now.
+ */
+ if (!ei->to_be_processed)
+ return;
+
+ ei->to_be_processed = false;
+
+ lsa = ospf_external_info_find_lsa(ospf, &ei->p);
+
+ if (lsa)
+ ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE, 0);
+ else {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Originate external route(%pI4/%d)",
+ __func__, &ei->p.prefix, ei->p.prefixlen);
+
+ ospf_external_lsa_originate(ospf, ei);
+ }
+}
+
+static void ospf_handle_external_aggr_add(struct ospf *ospf)
+{
+ struct external_info *ei;
+ struct route_node *rn = NULL;
+ struct route_table *rt = NULL;
+ int type = 0;
+
+ /* Delete all the aggregators which are marked as
+ * OSPF_ROUTE_AGGR_DEL.
+ */
+ ospf_delete_all_marked_aggregators(ospf);
+
+ for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) {
+ struct list *ext_list;
+ struct listnode *node;
+ struct ospf_external *ext;
+ struct ospf_external_aggr_rt *aggr;
+
+ ext_list = ospf->external[type];
+ if (!ext_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) {
+ rt = ext->external_info;
+ if (!rt)
+ continue;
+
+ for (rn = route_top(rt); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ ei = rn->info;
+ if (is_default_prefix4(&ei->p))
+ continue;
+
+ /* Check the AS-external-LSA
+ * should be originated.
+ */
+ if (!ospf_redistribute_check(ospf, ei, NULL))
+ continue;
+
+ aggr = ospf_external_aggr_match(ospf, &ei->p);
+
+ /* If matching aggregator found, Add
+ * the external route reference 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) {
+ ospf_originate_summary_lsa(ospf, aggr,
+ ei);
+
+ /* All aggregated external rts
+ * are handled here.
+ */
+ ospf_handle_aggregated_exnl_rt(
+ ospf, aggr, ei);
+ continue;
+ }
+
+ /* External routes which are only out
+ * of aggregation will be handled here.
+ */
+ ospf_handle_exnl_rt_after_aggr_del(ospf, ei);
+ }
+ }
+ }
+}
+
+static void
+ospf_aggr_handle_advertise_change(struct ospf *ospf,
+ struct ospf_external_aggr_rt *aggr,
+ struct external_info *ei_aggr)
+{
+ struct ospf_lsa *lsa;
+
+ /* Check if advertise option modified. */
+ if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) {
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Don't originate the summary address,It is configured to not-advertise.",
+ __func__);
+
+ if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) {
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: No-advertise,So Flush the Aggregate route(%pI4/%d)",
+ __func__, &aggr->p.prefix,
+ aggr->p.prefixlen);
+
+ ospf_external_lsa_flush(ospf, 0, &aggr->p, 0);
+
+ UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ }
+ return;
+ }
+
+ if (!CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Now it is advatisable", __func__);
+
+ lsa = ospf_external_info_find_lsa(ospf, &ei_aggr->p);
+ if (lsa && IS_LSA_MAXAGE(lsa)) {
+ /* This is special case.
+ * If a summary route need to be originated but where
+ * summary route already exist in lsdb with maxage, then
+ * it need to be refreshed.
+ */
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: It is already with Maxage, So refresh it (%pI4/%d)",
+ __func__, &aggr->p.prefix,
+ aggr->p.prefixlen);
+
+ ospf_external_lsa_refresh(ospf, lsa, ei_aggr,
+ LSA_REFRESH_FORCE, 1);
+
+ SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED);
+
+ } else {
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Originate Aggregate LSA (%pI4/%d)",
+ __func__, &aggr->p.prefix,
+ aggr->p.prefixlen);
+
+ /* Originate summary LSA */
+ lsa = ospf_external_lsa_originate(ospf, ei_aggr);
+ if (lsa)
+ SET_FLAG(aggr->flags,
+ OSPF_EXTERNAL_AGGRT_ORIGINATED);
+ }
+ }
+}
+
+static void ospf_handle_external_aggr_update(struct ospf *ospf)
+{
+ struct route_node *rn = NULL;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Process modified aggregators.", __func__);
+
+ for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) {
+ struct ospf_external_aggr_rt *aggr;
+ struct ospf_lsa *lsa = NULL;
+ struct as_external_lsa *asel = NULL;
+ struct external_info ei_aggr;
+ route_tag_t tag = 0;
+
+ if (!rn->info)
+ continue;
+
+ aggr = rn->info;
+
+ if (aggr->action == OSPF_ROUTE_AGGR_DEL) {
+ aggr->action = OSPF_ROUTE_AGGR_NONE;
+ ospf_external_aggr_delete(ospf, rn);
+
+ hash_clean_and_free(
+ &aggr->match_extnl_hash,
+ (void *)ospf_aggr_handle_external_info);
+
+ ospf_external_aggregator_free(aggr);
+ } else if (aggr->action == OSPF_ROUTE_AGGR_MODIFY) {
+
+ aggr->action = OSPF_ROUTE_AGGR_NONE;
+
+ /* Prepare the extrenal_info for aggregator */
+ memset(&ei_aggr, 0, sizeof(ei_aggr));
+ ei_aggr.p = aggr->p;
+ ei_aggr.tag = aggr->tag;
+ ei_aggr.type = 0;
+ ei_aggr.instance = ospf->instance;
+ ei_aggr.route_map_set.metric = -1;
+ ei_aggr.route_map_set.metric_type = -1;
+
+ /* Check if tag modified */
+ if (CHECK_FLAG(aggr->flags,
+ OSPF_EXTERNAL_AGGRT_ORIGINATED)) {
+ lsa = ospf_external_info_find_lsa(ospf,
+ &ei_aggr.p);
+ if (!lsa) {
+ flog_warn(EC_OSPF_LSA_MISSING,
+ "%s: Could not refresh/originate %pI4/%d",
+ __func__, &aggr->p.prefix,
+ aggr->p.prefixlen);
+ continue;
+ }
+
+ asel = (struct as_external_lsa *)lsa->data;
+ tag = (unsigned long)ntohl(
+ asel->e[0].route_tag);
+
+ /* If tag modified , then re-originate the
+ * route with modified tag details.
+ */
+ if (tag != ei_aggr.tag) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Route tag changed(old:%d new:%d,So refresh the summary route.(%pI4/%d)",
+ __func__, tag,
+ ei_aggr.tag,
+ &aggr->p.prefix,
+ aggr->p.prefixlen);
+
+ ospf_external_lsa_refresh(
+ ospf, lsa, &ei_aggr,
+ LSA_REFRESH_FORCE, 1);
+ }
+ }
+
+ /* Advertise option modified ?
+ * If so, handled it here.
+ */
+ ospf_aggr_handle_advertise_change(ospf, aggr, &ei_aggr);
+ }
+ }
+}
+
+static void ospf_asbr_external_aggr_process(struct event *thread)
+{
+ struct ospf *ospf = EVENT_ARG(thread);
+ int operation = 0;
+
+ ospf->t_external_aggr = NULL;
+ operation = ospf->aggr_action;
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: operation:%d", __func__, operation);
+
+ switch (operation) {
+ case OSPF_ROUTE_AGGR_ADD:
+ ospf_handle_external_aggr_add(ospf);
+ break;
+ case OSPF_ROUTE_AGGR_DEL:
+ case OSPF_ROUTE_AGGR_MODIFY:
+ ospf_handle_external_aggr_update(ospf);
+ break;
+ default:
+ break;
+ }
+}
+static void ospf_external_aggr_timer(struct ospf *ospf,
+ struct ospf_external_aggr_rt *aggr,
+ enum ospf_aggr_action_t operation)
+{
+ aggr->action = operation;
+
+ if (ospf->t_external_aggr) {
+ if (ospf->aggr_action == OSPF_ROUTE_AGGR_ADD) {
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s: Not required to retsart timer,set is already added.",
+ __func__);
+ return;
+ }
+
+ if (operation == OSPF_ROUTE_AGGR_ADD) {
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug(
+ "%s, Restarting Aggregator delay timer.",
+ __func__);
+ EVENT_OFF(ospf->t_external_aggr);
+ }
+ }
+
+ if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
+ zlog_debug("%s: Start Aggregator delay timer %u(in seconds).",
+ __func__, ospf->aggr_delay_interval);
+
+ ospf->aggr_action = operation;
+ event_add_timer(master, ospf_asbr_external_aggr_process, ospf,
+ ospf->aggr_delay_interval, &ospf->t_external_aggr);
+}
+
+int ospf_asbr_external_aggregator_set(struct ospf *ospf, struct prefix_ipv4 *p,
+ route_tag_t tag)
+{
+ struct ospf_external_aggr_rt *aggregator;
+
+ aggregator = ospf_extrenal_aggregator_lookup(ospf, p);
+
+ if (aggregator) {
+ if (CHECK_FLAG(aggregator->flags,
+ OSPF_EXTERNAL_AGGRT_NO_ADVERTISE))
+ UNSET_FLAG(aggregator->flags,
+ OSPF_EXTERNAL_AGGRT_NO_ADVERTISE);
+ else if (aggregator->tag == tag)
+ return OSPF_SUCCESS;
+
+ aggregator->tag = tag;
+
+ ospf_external_aggr_timer(ospf, aggregator,
+ OSPF_ROUTE_AGGR_MODIFY);
+ } else {
+ aggregator = ospf_external_aggregator_new(p);
+ if (!aggregator)
+ return OSPF_FAILURE;
+
+ aggregator->tag = tag;
+
+ ospf_external_aggr_add(ospf, aggregator);
+ ospf_external_aggr_timer(ospf, aggregator, OSPF_ROUTE_AGGR_ADD);
+ }
+
+ return OSPF_SUCCESS;
+}
+
+int ospf_asbr_external_aggregator_unset(struct ospf *ospf,
+ struct prefix_ipv4 *p, route_tag_t tag)
+{
+ struct route_node *rn;
+ struct ospf_external_aggr_rt *aggr;
+
+ rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p);
+ if (!rn)
+ return OSPF_INVALID;
+ route_unlock_node(rn);
+
+ aggr = rn->info;
+
+ if (tag && (tag != aggr->tag))
+ return OSPF_INVALID;
+
+ if (!OSPF_EXTERNAL_RT_COUNT(aggr)) {
+ ospf_external_aggr_delete(ospf, rn);
+ ospf_external_aggregator_free(aggr);
+ return OSPF_SUCCESS;
+ }
+
+ ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_DEL);
+
+ return OSPF_SUCCESS;
+}
+
+int ospf_asbr_external_rt_no_advertise(struct ospf *ospf, struct prefix_ipv4 *p)
+{
+ struct ospf_external_aggr_rt *aggr;
+ route_tag_t tag = 0;
+
+ aggr = ospf_extrenal_aggregator_lookup(ospf, p);
+ if (aggr) {
+ if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE))
+ return OSPF_SUCCESS;
+
+ SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE);
+
+ aggr->tag = tag;
+
+ if (!OSPF_EXTERNAL_RT_COUNT(aggr))
+ return OSPF_SUCCESS;
+
+ ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_MODIFY);
+ } else {
+ aggr = ospf_external_aggregator_new(p);
+
+ if (!aggr)
+ return OSPF_FAILURE;
+
+ SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE);
+ ospf_external_aggr_add(ospf, aggr);
+ ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_ADD);
+ }
+
+ return OSPF_SUCCESS;
+}
+
+int ospf_asbr_external_rt_advertise(struct ospf *ospf, struct prefix_ipv4 *p)
+{
+ struct route_node *rn;
+ struct ospf_external_aggr_rt *aggr;
+
+ rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p);
+ if (!rn)
+ return OSPF_INVALID;
+ route_unlock_node(rn);
+
+ aggr = rn->info;
+
+ if (!CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE))
+ return OSPF_INVALID;
+
+ UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE);
+
+ if (!OSPF_EXTERNAL_RT_COUNT(aggr))
+ return OSPF_SUCCESS;
+
+ ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_MODIFY);
+ return OSPF_SUCCESS;
+}
+
+int ospf_external_aggregator_timer_set(struct ospf *ospf, uint16_t interval)
+{
+ ospf->aggr_delay_interval = interval;
+ return OSPF_SUCCESS;
+}