mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-16 07:30:04 +00:00

Store the MaxAge LSA list in a tree instead of a linked list for efficient access. Walking the list can be quite inefficient in some large systems and under certain tests. ospfd maintains the list of LSA's that have been MaxAge'd out in a separate linked list for removal by a remover/walker thread. When a new LSA is to be installed, the old LSA is ejected and when it is ejected, the MaxAge LSA list is traversed to ensure that the old LSA is also removed from this list if it exists on this list. When a large number (> 5K) MaxAge LSAs are bombarding the system, walking this list takes a significant time causing timers to fire and actions to be taken such as expiring neighbors due to expiry of DeadInterval (especially when timer is really low, <= 12s), creating a spiral of instability. By making this MaxAge LSA list be a tree, this problem is mitigated. Signed-off-by: Dinesh Dutt <ddutt@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: Scott Feldman <sfeldma@cumulusnetworks.com>
331 lines
7.1 KiB
C
331 lines
7.1 KiB
C
/*
|
||
* OSPF LSDB support.
|
||
* Copyright (C) 1999, 2000 Alex Zinin, Kunihiro Ishiguro, Toshiaki Takada
|
||
*
|
||
* 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 GNU Zebra; see the file COPYING. If not, write to the Free
|
||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||
* 02111-1307, USA.
|
||
*/
|
||
|
||
#include <zebra.h>
|
||
|
||
#include "prefix.h"
|
||
#include "table.h"
|
||
#include "memory.h"
|
||
#include "log.h"
|
||
|
||
#include "ospfd/ospfd.h"
|
||
#include "ospfd/ospf_asbr.h"
|
||
#include "ospfd/ospf_lsa.h"
|
||
#include "ospfd/ospf_lsdb.h"
|
||
|
||
struct ospf_lsdb *
|
||
ospf_lsdb_new ()
|
||
{
|
||
struct ospf_lsdb *new;
|
||
|
||
new = XCALLOC (MTYPE_OSPF_LSDB, sizeof (struct ospf_lsdb));
|
||
ospf_lsdb_init (new);
|
||
|
||
return new;
|
||
}
|
||
|
||
void
|
||
ospf_lsdb_init (struct ospf_lsdb *lsdb)
|
||
{
|
||
int i;
|
||
|
||
for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
|
||
lsdb->type[i].db = route_table_init ();
|
||
}
|
||
|
||
void
|
||
ospf_lsdb_free (struct ospf_lsdb *lsdb)
|
||
{
|
||
ospf_lsdb_cleanup (lsdb);
|
||
XFREE (MTYPE_OSPF_LSDB, lsdb);
|
||
}
|
||
|
||
void
|
||
ospf_lsdb_cleanup (struct ospf_lsdb *lsdb)
|
||
{
|
||
int i;
|
||
assert (lsdb);
|
||
assert (lsdb->total == 0);
|
||
|
||
ospf_lsdb_delete_all (lsdb);
|
||
|
||
for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
|
||
route_table_finish (lsdb->type[i].db);
|
||
}
|
||
|
||
void
|
||
ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa)
|
||
{
|
||
if (lp && lsa && lsa->data)
|
||
{
|
||
lp->family = 0;
|
||
lp->prefixlen = 64;
|
||
lp->id = lsa->data->id;
|
||
lp->adv_router = lsa->data->adv_router;
|
||
}
|
||
}
|
||
|
||
static void
|
||
ospf_lsdb_delete_entry (struct ospf_lsdb *lsdb, struct route_node *rn)
|
||
{
|
||
struct ospf_lsa *lsa = rn->info;
|
||
|
||
if (!lsa)
|
||
return;
|
||
|
||
assert (rn->table == lsdb->type[lsa->data->type].db);
|
||
|
||
if (IS_LSA_SELF (lsa))
|
||
lsdb->type[lsa->data->type].count_self--;
|
||
lsdb->type[lsa->data->type].count--;
|
||
lsdb->type[lsa->data->type].checksum -= ntohs(lsa->data->checksum);
|
||
lsdb->total--;
|
||
rn->info = NULL;
|
||
route_unlock_node (rn);
|
||
#ifdef MONITOR_LSDB_CHANGE
|
||
if (lsdb->del_lsa_hook != NULL)
|
||
(* lsdb->del_lsa_hook)(lsa);
|
||
#endif /* MONITOR_LSDB_CHANGE */
|
||
ospf_lsa_unlock (&lsa); /* lsdb */
|
||
return;
|
||
}
|
||
|
||
/* Add new LSA to lsdb. */
|
||
void
|
||
ospf_lsdb_add (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
|
||
{
|
||
struct route_table *table;
|
||
struct prefix_ls lp;
|
||
struct route_node *rn;
|
||
|
||
table = lsdb->type[lsa->data->type].db;
|
||
ls_prefix_set (&lp, lsa);
|
||
rn = route_node_get (table, (struct prefix *)&lp);
|
||
|
||
/* nothing to do? */
|
||
if (rn->info && rn->info == lsa)
|
||
{
|
||
route_unlock_node (rn);
|
||
return;
|
||
}
|
||
|
||
/* purge old entry? */
|
||
if (rn->info)
|
||
ospf_lsdb_delete_entry (lsdb, rn);
|
||
|
||
if (IS_LSA_SELF (lsa))
|
||
lsdb->type[lsa->data->type].count_self++;
|
||
lsdb->type[lsa->data->type].count++;
|
||
lsdb->total++;
|
||
|
||
#ifdef MONITOR_LSDB_CHANGE
|
||
if (lsdb->new_lsa_hook != NULL)
|
||
(* lsdb->new_lsa_hook)(lsa);
|
||
#endif /* MONITOR_LSDB_CHANGE */
|
||
lsdb->type[lsa->data->type].checksum += ntohs(lsa->data->checksum);
|
||
rn->info = ospf_lsa_lock (lsa); /* lsdb */
|
||
}
|
||
|
||
void
|
||
ospf_lsdb_delete (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
|
||
{
|
||
struct route_table *table;
|
||
struct prefix_ls lp;
|
||
struct route_node *rn;
|
||
|
||
if (!lsdb)
|
||
{
|
||
zlog_warn ("%s: Called with NULL LSDB", __func__);
|
||
if (lsa)
|
||
zlog_warn ("LSA[Type%d:%s]: LSA %p, lsa->lsdb %p",
|
||
lsa->data->type, inet_ntoa (lsa->data->id),
|
||
lsa, lsa->lsdb);
|
||
return;
|
||
}
|
||
|
||
if (!lsa)
|
||
{
|
||
zlog_warn ("%s: Called with NULL LSA", __func__);
|
||
return;
|
||
}
|
||
|
||
assert (lsa->data->type < OSPF_MAX_LSA);
|
||
table = lsdb->type[lsa->data->type].db;
|
||
ls_prefix_set (&lp, lsa);
|
||
if ((rn = route_node_lookup (table, (struct prefix *) &lp)))
|
||
{
|
||
if (rn->info == lsa)
|
||
ospf_lsdb_delete_entry (lsdb, rn);
|
||
route_unlock_node (rn); /* route_node_lookup */
|
||
}
|
||
}
|
||
|
||
void
|
||
ospf_lsdb_delete_all (struct ospf_lsdb *lsdb)
|
||
{
|
||
struct route_table *table;
|
||
struct route_node *rn;
|
||
int i;
|
||
|
||
for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
|
||
{
|
||
table = lsdb->type[i].db;
|
||
for (rn = route_top (table); rn; rn = route_next (rn))
|
||
if (rn->info != NULL)
|
||
ospf_lsdb_delete_entry (lsdb, rn);
|
||
}
|
||
}
|
||
|
||
void
|
||
ospf_lsdb_clean_stat (struct ospf_lsdb *lsdb)
|
||
{
|
||
struct route_table *table;
|
||
struct route_node *rn;
|
||
struct ospf_lsa *lsa;
|
||
int i;
|
||
|
||
for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
|
||
{
|
||
table = lsdb->type[i].db;
|
||
for (rn = route_top (table); rn; rn = route_next (rn))
|
||
if ((lsa = (rn->info)) != NULL)
|
||
lsa->stat = LSA_SPF_NOT_EXPLORED;
|
||
}
|
||
}
|
||
|
||
struct ospf_lsa *
|
||
ospf_lsdb_lookup (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
|
||
{
|
||
struct route_table *table;
|
||
struct prefix_ls lp;
|
||
struct route_node *rn;
|
||
struct ospf_lsa *find;
|
||
|
||
table = lsdb->type[lsa->data->type].db;
|
||
ls_prefix_set (&lp, lsa);
|
||
rn = route_node_lookup (table, (struct prefix *) &lp);
|
||
if (rn)
|
||
{
|
||
find = rn->info;
|
||
route_unlock_node (rn);
|
||
return find;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
struct ospf_lsa *
|
||
ospf_lsdb_lookup_by_id (struct ospf_lsdb *lsdb, u_char type,
|
||
struct in_addr id, struct in_addr adv_router)
|
||
{
|
||
struct route_table *table;
|
||
struct prefix_ls lp;
|
||
struct route_node *rn;
|
||
struct ospf_lsa *find;
|
||
|
||
table = lsdb->type[type].db;
|
||
|
||
memset (&lp, 0, sizeof (struct prefix_ls));
|
||
lp.family = 0;
|
||
lp.prefixlen = 64;
|
||
lp.id = id;
|
||
lp.adv_router = adv_router;
|
||
|
||
rn = route_node_lookup (table, (struct prefix *) &lp);
|
||
if (rn)
|
||
{
|
||
find = rn->info;
|
||
route_unlock_node (rn);
|
||
return find;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
struct ospf_lsa *
|
||
ospf_lsdb_lookup_by_id_next (struct ospf_lsdb *lsdb, u_char type,
|
||
struct in_addr id, struct in_addr adv_router,
|
||
int first)
|
||
{
|
||
struct route_table *table;
|
||
struct prefix_ls lp;
|
||
struct route_node *rn;
|
||
struct ospf_lsa *find;
|
||
|
||
table = lsdb->type[type].db;
|
||
|
||
memset (&lp, 0, sizeof (struct prefix_ls));
|
||
lp.family = 0;
|
||
lp.prefixlen = 64;
|
||
lp.id = id;
|
||
lp.adv_router = adv_router;
|
||
|
||
if (first)
|
||
rn = route_top (table);
|
||
else
|
||
{
|
||
if ((rn = route_node_lookup (table, (struct prefix *) &lp)) == NULL)
|
||
return NULL;
|
||
rn = route_next (rn);
|
||
}
|
||
|
||
for (; rn; rn = route_next (rn))
|
||
if (rn->info)
|
||
break;
|
||
|
||
if (rn && rn->info)
|
||
{
|
||
find = rn->info;
|
||
route_unlock_node (rn);
|
||
return find;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
unsigned long
|
||
ospf_lsdb_count_all (struct ospf_lsdb *lsdb)
|
||
{
|
||
return lsdb->total;
|
||
}
|
||
|
||
unsigned long
|
||
ospf_lsdb_count (struct ospf_lsdb *lsdb, int type)
|
||
{
|
||
return lsdb->type[type].count;
|
||
}
|
||
|
||
unsigned long
|
||
ospf_lsdb_count_self (struct ospf_lsdb *lsdb, int type)
|
||
{
|
||
return lsdb->type[type].count_self;
|
||
}
|
||
|
||
unsigned int
|
||
ospf_lsdb_checksum (struct ospf_lsdb *lsdb, int type)
|
||
{
|
||
return lsdb->type[type].checksum;
|
||
}
|
||
|
||
unsigned long
|
||
ospf_lsdb_isempty (struct ospf_lsdb *lsdb)
|
||
{
|
||
return (lsdb->total == 0);
|
||
}
|