summaryrefslogtreecommitdiffstats
path: root/eigrpd/eigrp_topology.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--eigrpd/eigrp_topology.c545
1 files changed, 545 insertions, 0 deletions
diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c
new file mode 100644
index 0000000..846e211
--- /dev/null
+++ b/eigrpd/eigrp_topology.c
@@ -0,0 +1,545 @@
+/*
+ * EIGRP Topology Table.
+ * Copyright (C) 2013-2016
+ * Authors:
+ * Donnie Savage
+ * Jan Janovic
+ * Matej Perina
+ * Peter Orsag
+ * Peter Paluch
+ * Frantisek Gazo
+ * Tomas Hvorkovy
+ * Martin Kontsek
+ * Lukas Koribsky
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra 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 "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "log.h"
+#include "linklist.h"
+#include "vty.h"
+#include "lib_errors.h"
+
+#include "eigrpd/eigrp_types.h"
+#include "eigrpd/eigrp_structs.h"
+#include "eigrpd/eigrpd.h"
+#include "eigrpd/eigrp_interface.h"
+#include "eigrpd/eigrp_neighbor.h"
+#include "eigrpd/eigrp_packet.h"
+#include "eigrpd/eigrp_zebra.h"
+#include "eigrpd/eigrp_vty.h"
+#include "eigrpd/eigrp_network.h"
+#include "eigrpd/eigrp_dump.h"
+#include "eigrpd/eigrp_topology.h"
+#include "eigrpd/eigrp_fsm.h"
+#include "eigrpd/eigrp_metric.h"
+
+DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_ROUTE_DESCRIPTOR, "EIGRP Nexthop Entry");
+DEFINE_MTYPE(EIGRPD, EIGRP_PREFIX_DESCRIPTOR, "EIGRP Prefix");
+
+static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *rd1,
+ struct eigrp_route_descriptor *rd2);
+
+/*
+ * Returns linkedlist used as topology table
+ * cmp - assigned function for comparing topology nodes
+ * del - assigned function executed before deleting topology node by list
+ * function
+ */
+struct route_table *eigrp_topology_new(void)
+{
+ return route_table_init();
+}
+
+/*
+ * Returns new created toplogy node
+ * cmp - assigned function for comparing topology entry
+ */
+struct eigrp_prefix_descriptor *eigrp_prefix_descriptor_new(void)
+{
+ struct eigrp_prefix_descriptor *new;
+ new = XCALLOC(MTYPE_EIGRP_PREFIX_DESCRIPTOR,
+ sizeof(struct eigrp_prefix_descriptor));
+ new->entries = list_new();
+ new->rij = list_new();
+ new->entries->cmp = (int (*)(void *, void *))eigrp_route_descriptor_cmp;
+ new->distance = new->fdistance = new->rdistance = EIGRP_MAX_METRIC;
+ new->destination = NULL;
+
+ return new;
+}
+
+/*
+ * Topology entry comparison
+ */
+static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *entry1,
+ struct eigrp_route_descriptor *entry2)
+{
+ if (entry1->distance < entry2->distance)
+ return -1;
+ if (entry1->distance > entry2->distance)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Returns new topology entry
+ */
+
+struct eigrp_route_descriptor *eigrp_route_descriptor_new(void)
+{
+ struct eigrp_route_descriptor *new;
+
+ new = XCALLOC(MTYPE_EIGRP_ROUTE_DESCRIPTOR,
+ sizeof(struct eigrp_route_descriptor));
+ new->reported_distance = EIGRP_MAX_METRIC;
+ new->distance = EIGRP_MAX_METRIC;
+
+ return new;
+}
+
+/*
+ * Freeing topology table list
+ */
+void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table)
+{
+ eigrp_topology_delete_all(eigrp, table);
+ route_table_finish(table);
+}
+
+/*
+ * Adding topology node to topology table
+ */
+void eigrp_prefix_descriptor_add(struct route_table *topology,
+ struct eigrp_prefix_descriptor *pe)
+{
+ struct route_node *rn;
+
+ rn = route_node_get(topology, pe->destination);
+ if (rn->info) {
+ if (IS_DEBUG_EIGRP_EVENT)
+ zlog_debug(
+ "%s: %pFX Should we have found this entry in the topo table?",
+ __func__, pe->destination);
+ route_unlock_node(rn);
+ }
+
+ rn->info = pe;
+}
+
+/*
+ * Adding topology entry to topology node
+ */
+void eigrp_route_descriptor_add(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *node,
+ struct eigrp_route_descriptor *entry)
+{
+ struct list *l = list_new();
+
+ listnode_add(l, entry);
+
+ if (listnode_lookup(node->entries, entry) == NULL) {
+ listnode_add_sort(node->entries, entry);
+ entry->prefix = node;
+
+ eigrp_zebra_route_add(eigrp, node->destination,
+ l, node->fdistance);
+ }
+
+ list_delete(&l);
+}
+
+/*
+ * Deleting topology node from topology table
+ */
+void eigrp_prefix_descriptor_delete(struct eigrp *eigrp,
+ struct route_table *table,
+ struct eigrp_prefix_descriptor *pe)
+{
+ struct eigrp_route_descriptor *ne;
+ struct listnode *node, *nnode;
+ struct route_node *rn;
+
+ if (!eigrp)
+ return;
+
+ rn = route_node_lookup(table, pe->destination);
+ if (!rn)
+ return;
+
+ /*
+ * Emergency removal of the node from this list.
+ * Whatever it is.
+ */
+ listnode_delete(eigrp->topology_changes_internalIPV4, pe);
+
+ for (ALL_LIST_ELEMENTS(pe->entries, node, nnode, ne))
+ eigrp_route_descriptor_delete(eigrp, pe, ne);
+ list_delete(&pe->entries);
+ list_delete(&pe->rij);
+ eigrp_zebra_route_delete(eigrp, pe->destination);
+ prefix_free(&pe->destination);
+
+ rn->info = NULL;
+ route_unlock_node(rn); // Lookup above
+ route_unlock_node(rn); // Initial creation
+ XFREE(MTYPE_EIGRP_PREFIX_DESCRIPTOR, pe);
+}
+
+/*
+ * Deleting topology entry from topology node
+ */
+void eigrp_route_descriptor_delete(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *node,
+ struct eigrp_route_descriptor *entry)
+{
+ if (listnode_lookup(node->entries, entry) != NULL) {
+ listnode_delete(node->entries, entry);
+ eigrp_zebra_route_delete(eigrp, node->destination);
+ XFREE(MTYPE_EIGRP_ROUTE_DESCRIPTOR, entry);
+ }
+}
+
+/*
+ * Deleting all nodes from topology table
+ */
+void eigrp_topology_delete_all(struct eigrp *eigrp,
+ struct route_table *topology)
+{
+ struct route_node *rn;
+ struct eigrp_prefix_descriptor *pe;
+
+ for (rn = route_top(topology); rn; rn = route_next(rn)) {
+ pe = rn->info;
+
+ if (!pe)
+ continue;
+
+ eigrp_prefix_descriptor_delete(eigrp, topology, pe);
+ }
+}
+
+struct eigrp_prefix_descriptor *
+eigrp_topology_table_lookup_ipv4(struct route_table *table,
+ struct prefix *address)
+{
+ struct eigrp_prefix_descriptor *pe;
+ struct route_node *rn;
+
+ rn = route_node_lookup(table, address);
+ if (!rn)
+ return NULL;
+
+ pe = rn->info;
+
+ route_unlock_node(rn);
+
+ return pe;
+}
+
+/*
+ * For a future optimization, put the successor list into it's
+ * own separate list from the full list?
+ *
+ * That way we can clean up all the list_new and list_delete's
+ * that we are doing. DBS
+ */
+struct list *
+eigrp_topology_get_successor(struct eigrp_prefix_descriptor *table_node)
+{
+ struct list *successors = list_new();
+ struct eigrp_route_descriptor *data;
+ struct listnode *node1, *node2;
+
+ for (ALL_LIST_ELEMENTS(table_node->entries, node1, node2, data)) {
+ if (data->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG) {
+ listnode_add(successors, data);
+ }
+ }
+
+ /*
+ * If we have no successors return NULL
+ */
+ if (!successors->count) {
+ list_delete(&successors);
+ successors = NULL;
+ }
+
+ return successors;
+}
+
+struct list *
+eigrp_topology_get_successor_max(struct eigrp_prefix_descriptor *table_node,
+ unsigned int maxpaths)
+{
+ struct list *successors = eigrp_topology_get_successor(table_node);
+
+ if (successors && successors->count > maxpaths) {
+ do {
+ struct listnode *node = listtail(successors);
+
+ list_delete_node(successors, node);
+
+ } while (successors->count > maxpaths);
+ }
+
+ return successors;
+}
+
+struct eigrp_route_descriptor *
+eigrp_route_descriptor_lookup(struct list *entries, struct eigrp_neighbor *nbr)
+{
+ struct eigrp_route_descriptor *data;
+ struct listnode *node, *nnode;
+ for (ALL_LIST_ELEMENTS(entries, node, nnode, data)) {
+ if (data->adv_router == nbr) {
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+/* Lookup all prefixes from specified neighbor */
+struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *eigrp,
+ struct eigrp_neighbor *nbr)
+{
+ struct listnode *node2, *node22;
+ struct eigrp_route_descriptor *entry;
+ struct eigrp_prefix_descriptor *pe;
+ struct route_node *rn;
+
+ /* create new empty list for prefixes storage */
+ struct list *prefixes = list_new();
+
+ /* iterate over all prefixes in topology table */
+ for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+ pe = rn->info;
+ /* iterate over all neighbor entry in prefix */
+ for (ALL_LIST_ELEMENTS(pe->entries, node2, node22, entry)) {
+ /* if entry is from specified neighbor, add to list */
+ if (entry->adv_router == nbr) {
+ listnode_add(prefixes, pe);
+ }
+ }
+ }
+
+ /* return list of prefixes from specified neighbor */
+ return prefixes;
+}
+
+enum metric_change
+eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg)
+{
+ struct eigrp *eigrp = msg->eigrp;
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *entry = msg->entry;
+ enum metric_change change = METRIC_SAME;
+ uint32_t new_reported_distance;
+
+ assert(entry);
+
+ switch (msg->data_type) {
+ case EIGRP_CONNECTED:
+ if (prefix->nt == EIGRP_TOPOLOGY_TYPE_CONNECTED)
+ return change;
+
+ change = METRIC_DECREASE;
+ break;
+ case EIGRP_INT:
+ if (prefix->nt == EIGRP_TOPOLOGY_TYPE_CONNECTED) {
+ change = METRIC_INCREASE;
+ goto distance_done;
+ }
+ if (eigrp_metrics_is_same(msg->metrics,
+ entry->reported_metric)) {
+ return change; // No change
+ }
+
+ new_reported_distance =
+ eigrp_calculate_metrics(eigrp, msg->metrics);
+
+ if (entry->reported_distance < new_reported_distance) {
+ change = METRIC_INCREASE;
+ goto distance_done;
+ } else
+ change = METRIC_DECREASE;
+
+ entry->reported_metric = msg->metrics;
+ entry->reported_distance = new_reported_distance;
+ eigrp_calculate_metrics(eigrp, msg->metrics);
+ entry->distance = eigrp_calculate_total_metrics(eigrp, entry);
+ break;
+ case EIGRP_EXT:
+ if (prefix->nt == EIGRP_TOPOLOGY_TYPE_REMOTE_EXTERNAL) {
+ if (eigrp_metrics_is_same(msg->metrics,
+ entry->reported_metric))
+ return change;
+ } else {
+ change = METRIC_INCREASE;
+ goto distance_done;
+ }
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: Please implement handler",
+ __func__);
+ break;
+ }
+distance_done:
+ /*
+ * Move to correct position in list according to new distance
+ */
+ listnode_delete(prefix->entries, entry);
+ listnode_add_sort(prefix->entries, entry);
+
+ return change;
+}
+
+void eigrp_topology_update_all_node_flags(struct eigrp *eigrp)
+{
+ struct eigrp_prefix_descriptor *pe;
+ struct route_node *rn;
+
+ if (!eigrp)
+ return;
+
+ for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
+ pe = rn->info;
+
+ if (!pe)
+ continue;
+
+ eigrp_topology_update_node_flags(eigrp, pe);
+ }
+}
+
+void eigrp_topology_update_node_flags(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *dest)
+{
+ struct listnode *node;
+ struct eigrp_route_descriptor *entry;
+
+ for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) {
+ if (entry->reported_distance < dest->fdistance) {
+ // is feasible successor, can be successor
+ if (((uint64_t)entry->distance
+ <= (uint64_t)dest->distance
+ * (uint64_t)eigrp->variance)
+ && entry->distance != EIGRP_MAX_METRIC) {
+ // is successor
+ entry->flags |=
+ EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
+ entry->flags &=
+ ~EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG;
+ } else {
+ // is feasible successor only
+ entry->flags |=
+ EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG;
+ entry->flags &=
+ ~EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
+ }
+ } else {
+ entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG;
+ entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
+ }
+ }
+}
+
+void eigrp_update_routing_table(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *prefix)
+{
+ struct list *successors;
+ struct listnode *node;
+ struct eigrp_route_descriptor *entry;
+
+ successors = eigrp_topology_get_successor_max(prefix, eigrp->max_paths);
+
+ if (successors) {
+ eigrp_zebra_route_add(eigrp, prefix->destination, successors,
+ prefix->fdistance);
+ for (ALL_LIST_ELEMENTS_RO(successors, node, entry))
+ entry->flags |= EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG;
+
+ list_delete(&successors);
+ } else {
+ eigrp_zebra_route_delete(eigrp, prefix->destination);
+ for (ALL_LIST_ELEMENTS_RO(prefix->entries, node, entry))
+ entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG;
+ }
+}
+
+void eigrp_topology_neighbor_down(struct eigrp *eigrp,
+ struct eigrp_neighbor *nbr)
+{
+ struct listnode *node2, *node22;
+ struct eigrp_prefix_descriptor *pe;
+ struct eigrp_route_descriptor *entry;
+ struct route_node *rn;
+
+ for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
+ pe = rn->info;
+
+ if (!pe)
+ continue;
+
+ for (ALL_LIST_ELEMENTS(pe->entries, node2, node22, entry)) {
+ struct eigrp_fsm_action_message msg;
+
+ if (entry->adv_router != nbr)
+ continue;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.metrics.delay = EIGRP_MAX_METRIC;
+ msg.packet_type = EIGRP_OPC_UPDATE;
+ msg.eigrp = eigrp;
+ msg.data_type = EIGRP_INT;
+ msg.adv_router = nbr;
+ msg.entry = entry;
+ msg.prefix = pe;
+ eigrp_fsm_event(&msg);
+ }
+ }
+
+ eigrp_query_send_all(eigrp);
+ eigrp_update_send_all(eigrp, nbr->ei);
+}
+
+void eigrp_update_topology_table_prefix(struct eigrp *eigrp,
+ struct route_table *table,
+ struct eigrp_prefix_descriptor *prefix)
+{
+ struct listnode *node1, *node2;
+
+ struct eigrp_route_descriptor *entry;
+ for (ALL_LIST_ELEMENTS(prefix->entries, node1, node2, entry)) {
+ if (entry->distance == EIGRP_MAX_METRIC) {
+ eigrp_route_descriptor_delete(eigrp, prefix, entry);
+ }
+ }
+ if (prefix->distance == EIGRP_MAX_METRIC
+ && prefix->nt != EIGRP_TOPOLOGY_TYPE_CONNECTED) {
+ eigrp_prefix_descriptor_delete(eigrp, table, prefix);
+ }
+}