summaryrefslogtreecommitdiff
path: root/ospfd/ospf_orr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ospfd/ospf_orr.c')
-rw-r--r--ospfd/ospf_orr.c594
1 files changed, 594 insertions, 0 deletions
diff --git a/ospfd/ospf_orr.c b/ospfd/ospf_orr.c
new file mode 100644
index 0000000000..eed948b190
--- /dev/null
+++ b/ospfd/ospf_orr.c
@@ -0,0 +1,594 @@
+/*
+ * OSPF BGP-IGP IGP metric update handling routines
+ * Copyright (C) 2021 Samsung R&D Institute India - Bangalore.
+ * Madhurilatha Kuruganti
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <string.h>
+
+#include "monotime.h"
+#include "memory.h"
+#include "thread.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "command.h"
+#include "plist.h"
+#include "log.h"
+#include "zclient.h"
+#include <lib/json.h>
+#include "defaults.h"
+#include "orr_msg.h"
+
+#include "ospfd.h"
+#include "ospf_asbr.h"
+#include "ospf_dump.h"
+#include "ospf_lsa.h"
+#include "ospf_orr.h"
+#include "ospf_route.h"
+#include "ospf_spf.h"
+#include "ospf_te.h"
+
+static void ospf_show_orr_root(struct orr_root *root);
+static void ospf_show_orr(struct ospf *ospf, afi_t afi, safi_t safi);
+static struct orr_root *ospf_orr_root_new(struct ospf *ospf, afi_t afi,
+ safi_t safi, struct prefix *p,
+ char *group_name)
+{
+ struct list *orr_root_list = NULL;
+ struct orr_root *root = NULL;
+
+ if (!ospf->orr_root[afi][safi])
+ ospf->orr_root[afi][safi] = list_new();
+
+ orr_root_list = ospf->orr_root[afi][safi];
+ root = XCALLOC(MTYPE_OSPF_ORR_ROOT, sizeof(struct orr_root));
+
+ listnode_add(orr_root_list, root);
+
+ root->afi = afi;
+ root->safi = safi;
+ prefix_copy(&root->prefix, p);
+ IPV4_ADDR_COPY(&root->router_id, &p->u.prefix4);
+ strlcpy(root->group_name, group_name, sizeof(root->group_name));
+ root->new_rtrs = NULL;
+ root->new_table = NULL;
+
+ ospf_orr_debug(
+ "%s: For %s %s, ORR Group %s, created ORR Root entry %pFX.",
+ __func__, afi2str(afi), safi2str(safi), root->group_name, p);
+
+ return root;
+}
+
+static struct orr_root *ospf_orr_root_lookup(struct ospf *ospf, afi_t afi,
+ safi_t safi, struct in_addr *rid)
+{
+ struct list *orr_root_list = NULL;
+ struct orr_root *root = NULL;
+ struct listnode *node;
+
+ orr_root_list = ospf->orr_root[afi][safi];
+ if (!orr_root_list)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, root))
+ if (IPV4_ADDR_SAME(&root->router_id, rid))
+ return root;
+
+ ospf_orr_debug("%s: For %s %s, ORR Root '%pI4' not found.", __func__,
+ afi2str(afi), safi2str(safi), rid);
+
+ return NULL;
+}
+
+static struct orr_root *ospf_orr_root_lookup_by_adv_rid(struct ospf *ospf,
+ afi_t afi, safi_t safi,
+ struct in_addr *rid)
+{
+ struct list *orr_root_list = NULL;
+ struct orr_root *root = NULL;
+ struct listnode *node;
+
+ orr_root_list = ospf->orr_root[afi][safi];
+ if (!orr_root_list)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, root))
+ if (IPV4_ADDR_SAME(&root->adv_router, rid))
+ return root;
+
+ return NULL;
+}
+
+/*
+ * Lookup each area's LSDB if is there is any opaque area LSA received and
+ * update the root database with the advertising router.
+ */
+static struct ospf_lsa *
+ospf_orr_lookup_opaque_area_lsa_by_id(struct in_addr rid)
+{
+ struct ospf_lsa *lsa = NULL;
+ struct ospf_area *area = NULL;
+ struct ospf *ospf = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+
+ /* if ospf is not enabled ignore */
+ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ if (!ospf)
+ return NULL;
+
+ /* Lookup for Opaque area LSA in each area. */
+ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
+ lsa = ospf_lsa_lookup_by_mpls_te_rid(area, OSPF_OPAQUE_AREA_LSA,
+ rid);
+ if (!lsa)
+ continue;
+ ospf_orr_debug(
+ "%s: Opaque Area LSA found in area %pI4 for %pI4",
+ __func__, &area->area_id, &rid);
+ return lsa;
+ }
+ return NULL;
+}
+
+/*
+ * Lookup each area's LSDB if is there is any opaque area LSA received and
+ * update the root database with the advertising router.
+ */
+static struct ospf_lsa *ospf_orr_lookup_router_lsa_by_id(struct in_addr rid)
+{
+ struct ospf_lsa *lsa = NULL;
+ struct ospf_area *area = NULL;
+ struct ospf *ospf = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+
+ /* if ospf is not enabled ignore */
+ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ if (!ospf)
+ return NULL;
+
+ /* Lookup for Router LSA in each area. */
+ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
+ lsa = ospf_lsa_lookup_by_adv_rid(area, OSPF_ROUTER_LSA, rid);
+ if (!lsa)
+ continue;
+ ospf_orr_debug("%s: Router LSA found in area %pI4 for %pI4",
+ __func__, &area->area_id, &rid);
+ return lsa;
+ }
+ return NULL;
+}
+
+/*
+ * BGP-IGP IGP metric msg between BGP and IGP
+ */
+int ospf_orr_igp_metric_register(struct orr_igp_metric_reg msg)
+{
+ afi_t afi;
+ safi_t safi;
+ struct ospf *ospf = NULL;
+ struct ospf_lsa *lsa = NULL;
+ struct orr_root *root = NULL;
+
+ /* if ospf is not enabled ignore */
+ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ if (!ospf)
+ return -1;
+
+ if (msg.proto != ZEBRA_ROUTE_BGP)
+ return -1;
+
+ afi = family2afi(msg.prefix.family);
+ safi = msg.safi;
+
+ ospf_orr_debug(
+ "%s: Received IGP metric %s message from BGP for ORR Group %s from location %pFX",
+ __func__, msg.reg ? "Register" : "Unregister", msg.group_name,
+ &msg.prefix);
+
+ /* Get ORR Root entry for the given address-family */
+ root = ospf_orr_root_lookup(ospf, afi, safi, &msg.prefix.u.prefix4);
+
+ /* Should not hit this condition */
+ if ((root && msg.reg) || (!root && !msg.reg))
+ return -1;
+
+ /* Create ORR Root entry and calculate SPF from root */
+ if (!root) {
+ root = ospf_orr_root_new(ospf, afi, safi, &msg.prefix,
+ msg.group_name);
+ if (!root) {
+ ospf_orr_debug(
+ "%s: For %s %s, Failed to create ORR Root entry %pFX.",
+ __func__, afi2str(afi), safi2str(safi),
+ &msg.prefix);
+ return -1;
+ }
+ ospf->orr_spf_request++;
+
+ lsa = ospf_orr_lookup_opaque_area_lsa_by_id(root->router_id);
+ if (!lsa || !lsa->data)
+ return -1;
+
+ IPV4_ADDR_COPY(&root->adv_router, &lsa->data->adv_router);
+
+ /* Lookup LSDB for Router LSA */
+ if (!root->router_lsa_rcvd) {
+ lsa = ospf_orr_lookup_router_lsa_by_id(
+ root->adv_router);
+ if (!lsa || !lsa->data)
+ return -1;
+ root->router_lsa_rcvd = lsa;
+ }
+
+ /* Compute SPF for all root nodes */
+ ospf_orr_spf_calculate_schedule(ospf);
+ }
+ /* Delete ORR Root entry. SPF calculation not required. */
+ else {
+ listnode_delete(ospf->orr_root[afi][safi], root);
+ XFREE(MTYPE_OSPF_ORR_ROOT, root);
+
+ /* If last node is deleted in the list */
+ if (!ospf->orr_root[afi][safi]->count)
+ list_delete(&ospf->orr_root[afi][safi]);
+
+ ospf->orr_spf_request--;
+ }
+
+ if (IS_DEBUG_OSPF_ORR)
+ ospf_show_orr(ospf, afi, safi);
+
+ return 0;
+}
+
+void ospf_orr_igp_metric_send_update_add(struct orr_root *root,
+ unsigned short instance)
+{
+ int ret;
+ uint8_t count = 0;
+ struct route_node *rn;
+ struct ospf_route *or;
+ struct orr_igp_metric_info msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.proto = ZEBRA_ROUTE_OSPF;
+ msg.safi = root->safi;
+ msg.instId = instance;
+ msg.add = true;
+ prefix_copy(&msg.root, &root->prefix);
+
+ /* Update prefix table from ORR Route table */
+ for (rn = route_top(root->new_table); rn; rn = route_next(rn)) {
+ or = rn->info;
+ if (!or)
+ continue;
+
+ if (or->type != OSPF_DESTINATION_NETWORK &&
+ or->type != OSPF_DESTINATION_DISCARD)
+ continue;
+
+ if (ospf_route_match_same(root->old_table,
+ (struct prefix_ipv4 *)&rn->p, or))
+ continue;
+
+ if (count < ORR_MAX_PREFIX) {
+ prefix_copy(&msg.nexthop[count].prefix,
+ (struct prefix_ipv4 *)&rn->p);
+ msg.nexthop[count].metric = or->cost;
+ count++;
+ } else {
+ msg.num_entries = count;
+ ret = zclient_send_opaque(zclient,
+ ORR_IGP_METRIC_UPDATE,
+ (uint8_t *)&msg, sizeof(msg));
+ if (ret != ZCLIENT_SEND_SUCCESS)
+ ospf_orr_debug(
+ "%s: Failed to send message to BGP.",
+ __func__);
+ count = 0;
+ prefix_copy(&msg.nexthop[count].prefix,
+ (struct prefix_ipv4 *)&rn->p);
+ msg.nexthop[count].metric = or->cost;
+ count++;
+ }
+ }
+ if (count > 0 && count <= ORR_MAX_PREFIX) {
+ msg.num_entries = count;
+ ret = zclient_send_opaque(zclient, ORR_IGP_METRIC_UPDATE,
+ (uint8_t *)&msg, sizeof(msg));
+ if (ret != ZCLIENT_SEND_SUCCESS)
+ ospf_orr_debug("%s: Failed to send message to BGP.",
+ __func__);
+ }
+}
+
+void ospf_orr_igp_metric_send_update_delete(struct orr_root *root,
+ unsigned short instance)
+{
+ int ret;
+ uint8_t count = 0;
+ struct route_node *rn;
+ struct ospf_route *or;
+ struct orr_igp_metric_info msg;
+
+ if (!root->old_table)
+ return;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.proto = ZEBRA_ROUTE_OSPF;
+ msg.instId = instance;
+ msg.safi = root->safi;
+ msg.add = false;
+ prefix_copy(&msg.root, &root->prefix);
+
+ /* Update prefix table from ORR Route table */
+ for (rn = route_top(root->old_table); rn; rn = route_next(rn)) {
+ or = rn->info;
+ if (!or)
+ continue;
+
+ if (or->path_type != OSPF_PATH_INTRA_AREA &&
+ or->path_type != OSPF_PATH_INTER_AREA)
+ continue;
+
+ if (or->type != OSPF_DESTINATION_NETWORK &&
+ or->type != OSPF_DESTINATION_DISCARD)
+ continue;
+
+ if (ospf_route_exist_new_table(root->new_table,
+ (struct prefix_ipv4 *)&rn->p))
+ continue;
+
+ if (count < ORR_MAX_PREFIX) {
+ prefix_copy(&msg.nexthop[count].prefix,
+ (struct prefix_ipv4 *)&rn->p);
+ msg.nexthop[count].metric = or->cost;
+ count++;
+ } else {
+ msg.num_entries = count;
+ ret = zclient_send_opaque(zclient,
+ ORR_IGP_METRIC_UPDATE,
+ (uint8_t *)&msg, sizeof(msg));
+ if (ret != ZCLIENT_SEND_SUCCESS)
+ ospf_orr_debug(
+ "%s: Failed to send message to BGP.",
+ __func__);
+ count = 0;
+ prefix_copy(&msg.nexthop[count].prefix,
+ (struct prefix_ipv4 *)&rn->p);
+ msg.nexthop[count].metric = or->cost;
+ count++;
+ }
+ }
+ if (count > 0 && count <= ORR_MAX_PREFIX) {
+ msg.num_entries = count;
+ ret = zclient_send_opaque(zclient, ORR_IGP_METRIC_UPDATE,
+ (uint8_t *)&msg, sizeof(msg));
+ if (ret != ZCLIENT_SEND_SUCCESS)
+ ospf_orr_debug("%s: Failed to send message to BGP.",
+ __func__);
+ }
+}
+
+static void ospf_show_orr_root(struct orr_root *root)
+{
+ if (!root)
+ return;
+
+ ospf_orr_debug("%s: Address Family: %s %s", __func__,
+ afi2str(root->afi), safi2str(root->safi));
+ ospf_orr_debug("%s: ORR Group: %s", __func__, root->group_name);
+ ospf_orr_debug("%s: Router-Address: %pI4:", __func__, &root->router_id);
+ ospf_orr_debug("%s: Advertising Router: %pI4:", __func__,
+ &root->adv_router);
+}
+
+static void ospf_show_orr(struct ospf *ospf, afi_t afi, safi_t safi)
+{
+ struct listnode *node = NULL;
+ struct orr_root *orr_root = NULL;
+ struct list *orr_root_list = NULL;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ orr_root_list = ospf->orr_root[afi][safi];
+ if (!orr_root_list)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, orr_root))
+ ospf_show_orr_root(orr_root);
+ }
+}
+
+void ospf_orr_root_table_update(struct ospf_lsa *lsa, bool add)
+{
+ afi_t afi;
+ safi_t safi;
+ struct lsa_header *lsah = lsa->data;
+ uint32_t lsid = ntohl(lsah->id.s_addr);
+ uint8_t opaque_type = GET_OPAQUE_TYPE(lsid);
+ uint32_t opaque_id = GET_OPAQUE_ID(lsid);
+ struct tlv_header *tlvh = TLV_HDR_TOP(lsah);
+ struct te_tlv_router_addr *router_addr = NULL;
+ struct orr_root *root = NULL;
+ struct ospf *ospf = NULL;
+
+ /* if ospf is not enabled ignore */
+ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ if (!ospf)
+ return;
+
+ if (opaque_type != OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA)
+ return;
+
+ if (!tlvh || (ntohs(tlvh->type) != TE_TLV_ROUTER_ADDR) ||
+ (ntohs(tlvh->length) != TE_LINK_SUBTLV_DEF_SIZE))
+ return;
+
+ router_addr = (struct te_tlv_router_addr *)tlvh;
+ if (IS_DEBUG_OSPF_ORR) {
+ zlog_debug("[OSPF-ORR] %s: Opaque-area LSA %s LSDB", __func__,
+ add ? "added to" : "deleted from");
+ zlog_debug("[OSPF-ORR] %s: Opaque-Type %u (%s)", __func__,
+ opaque_type, "Traffic Engineering LSA");
+ zlog_debug("[OSPF-ORR] %s: Opaque-ID 0x%x", __func__,
+ opaque_id);
+ zlog_debug("[OSPF-ORR] %s: Opaque-Info: %u octets of data%s",
+ __func__, ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE,
+ VALID_OPAQUE_INFO_LEN(lsah) ? ""
+ : "(Invalid length?)");
+ zlog_debug("[OSPF-ORR] %s: Router-Address: %pI4", __func__,
+ &router_addr->value);
+ zlog_debug("[OSPF-ORR] %s: Advertising Router: %pI4", __func__,
+ &lsa->data->adv_router);
+ }
+ /*
+ * When Opaque LSA is added or removed from LSDB check if there is any
+ * change in MPLS-TE Router address and Advertising router address and
+ * update the table accordingly if there is no change in the mapping
+ * ignore update
+ *
+ * Get ORR Root entry for the given address-family
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ root = ospf_orr_root_lookup(ospf, afi, safi,
+ &router_addr->value);
+ if (root) {
+ IPV4_ADDR_COPY(&root->adv_router,
+ &lsa->data->adv_router);
+ if (IS_DEBUG_OSPF_ORR)
+ ospf_show_orr(ospf, afi, safi);
+ break;
+ }
+ }
+}
+
+void ospf_orr_root_update_rcvd_lsa(struct ospf_lsa *lsa)
+{
+ afi_t afi;
+ safi_t safi;
+ struct orr_root *root = NULL;
+
+ if (!lsa || !lsa->area || !lsa->area->ospf)
+ return;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ root = ospf_orr_root_lookup_by_adv_rid(
+ lsa->area->ospf, afi, safi, &lsa->data->adv_router);
+ if (root) {
+ SET_FLAG(lsa->flags, OSPF_LSA_ORR);
+ ospf_refresher_register_lsa(lsa->area->ospf, lsa);
+ root->router_lsa_rcvd = lsa;
+ }
+
+ ospf_orr_debug("%s: Received LSA[Type%d:%pI4]", __func__,
+ lsa->data->type, &lsa->data->adv_router);
+
+ /* Compute SPF for all root nodes */
+ ospf_orr_spf_calculate_schedule(lsa->area->ospf);
+ return;
+ }
+}
+
+/* Do not Install routes to root table. Just update table ponters */
+void ospf_orr_route_install(struct orr_root *root, struct route_table *rt,
+ unsigned short instance)
+{
+ /*
+ * rt contains new routing table, new_table contains an old one.
+ * updating pointers
+ */
+ if (root->old_table)
+ ospf_route_table_free(root->old_table);
+
+ root->old_table = root->new_table;
+ root->new_table = rt;
+
+ /* Send update to BGP to delete old routes. */
+ ospf_orr_igp_metric_send_update_delete(root, instance);
+
+ /* REVISIT: Skipping external route table for now */
+
+ /* Send update to BGP to add new routes. */
+ ospf_orr_igp_metric_send_update_add(root, instance);
+}
+
+void ospf_orr_spf_calculate_schedule(struct ospf *ospf)
+{
+ /* OSPF instance does not exist. */
+ if (ospf == NULL)
+ return;
+
+ /* No roots nodes rgistered for rSPF */
+ if (!ospf->orr_spf_request)
+ return;
+
+ /* ORR SPF calculation timer is already scheduled. */
+ if (ospf->t_orr_calc) {
+ ospf_orr_debug(
+ "SPF: calculation timer is already scheduled: %p",
+ (void *)ospf->t_orr_calc);
+ return;
+ }
+
+ ospf->t_orr_calc = NULL;
+
+ ospf_orr_debug("%s: SPF: calculation timer scheduled", __func__);
+
+ thread_add_timer(master, ospf_orr_spf_calculate_schedule_worker, ospf,
+ OSPF_ORR_CALC_INTERVAL, &ospf->t_orr_calc);
+}
+
+void ospf_orr_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
+ struct route_table *new_table,
+ struct route_table *all_rtrs,
+ struct route_table *new_rtrs,
+ struct ospf_lsa *lsa_rcvd)
+{
+ ospf_spf_calculate(area, lsa_rcvd, new_table, all_rtrs, new_rtrs, false,
+ true);
+}
+
+void ospf_orr_spf_calculate_areas(struct ospf *ospf,
+ struct route_table *new_table,
+ struct route_table *all_rtrs,
+ struct route_table *new_rtrs,
+ struct ospf_lsa *lsa_rcvd)
+{
+ struct ospf_area *area;
+ struct listnode *node, *nnode;
+
+ /* Calculate SPF for each area. */
+ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
+ /*
+ * Do backbone last, so as to first discover intra-area paths
+ * for any back-bone virtual-links
+ */
+ if (ospf->backbone && ospf->backbone == area)
+ continue;
+
+ ospf_orr_spf_calculate_area(ospf, area, new_table, all_rtrs,
+ new_rtrs, lsa_rcvd);
+ }
+
+ /* SPF for backbone, if required */
+ if (ospf->backbone)
+ ospf_orr_spf_calculate_area(ospf, ospf->backbone, new_table,
+ all_rtrs, new_rtrs, lsa_rcvd);
+}