blob: ad53eb4fd1d2840516f8315b3fce2b997400e429 [file] [log] [blame]
/*
* Copyright (C) 2002 Yasuhiro Ohara
*
* 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 "memory.h"
#include "log.h"
#include "command.h"
#include "if.h"
#include "ospf6_dump.h"
#include "ospf6_lsdb.h"
#include "ospf6_interface.h"
#include "ospf6_area.h"
#include "ospf6_top.h"
#define OSPF6_LSDB_MATCH_TYPE 0x01
#define OSPF6_LSDB_MATCH_ID 0x02
#define OSPF6_LSDB_MATCH_ADV_ROUTER 0x04
#define OSPF6_LSDB_SHOW_DUMP 0x08
#define OSPF6_LSDB_SHOW_DETAIL 0x10
struct ospf6_lsdb_hook_t hooks[0x2000];
struct ospf6_lsdb_hook_t *ospf6_lsdb_hook = hooks;
struct ospf6_lsdb *
ospf6_lsdb_create ()
{
struct ospf6_lsdb *lsdb;
lsdb = XCALLOC (MTYPE_OSPF6_LSDB, sizeof (struct ospf6_lsdb));
if (lsdb == NULL)
{
zlog_warn ("Can't malloc lsdb");
return NULL;
}
memset (lsdb, 0, sizeof (struct ospf6_lsdb));
lsdb->table = route_table_init ();
return lsdb;
}
void
ospf6_lsdb_delete (struct ospf6_lsdb *lsdb)
{
ospf6_lsdb_remove_all (lsdb);
route_table_finish (lsdb->table);
XFREE (MTYPE_OSPF6_LSDB, lsdb);
}
static void
ospf6_lsdb_set_key (struct prefix_ipv6 *key, int flag,
u_int16_t type, u_int32_t id, u_int32_t adv_router)
{
int len = 0;
memset (key, 0, sizeof (struct prefix_ipv6));
if (CHECK_FLAG (flag, OSPF6_LSDB_MATCH_TYPE))
{
len += 2;
if (CHECK_FLAG (flag, OSPF6_LSDB_MATCH_ADV_ROUTER))
{
len += 4;
if (CHECK_FLAG (flag, OSPF6_LSDB_MATCH_ID))
len += 4;
}
}
if (len > 0)
memcpy ((char *)&key->prefix, &type, 2);
if (len > 2)
memcpy ((char *)&key->prefix + 2, &adv_router, 4);
if (len > 6)
memcpy ((char *)&key->prefix + 6, &id, 4);
key->family = AF_INET6;
key->prefixlen = len * 8;
}
void
ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb)
{
int flag;
struct prefix_ipv6 key;
struct route_node *rn;
struct ospf6_lsa *old = NULL;
flag = OSPF6_LSDB_MATCH_TYPE | OSPF6_LSDB_MATCH_ID |
OSPF6_LSDB_MATCH_ADV_ROUTER;
ospf6_lsdb_set_key (&key, flag, lsa->header->type, lsa->header->id,
lsa->header->adv_router);
rn = route_node_get (lsdb->table, (struct prefix *) &key);
if (rn->info)
old = rn->info;
rn->info = lsa;
ospf6_lsa_lock (lsa);
if (old)
ospf6_lsa_unlock (old);
else
lsdb->count++;
}
void
ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb)
{
int flag;
struct prefix_ipv6 key;
struct route_node *rn;
struct ospf6_lsa *old;
flag = OSPF6_LSDB_MATCH_TYPE | OSPF6_LSDB_MATCH_ID |
OSPF6_LSDB_MATCH_ADV_ROUTER;
ospf6_lsdb_set_key (&key, flag, lsa->header->type, lsa->header->id,
lsa->header->adv_router);
rn = route_node_lookup (lsdb->table, (struct prefix *) &key);
if (! rn || ! rn->info)
{
zlog_warn ("LSDB: Can't remove: no such LSA: %s", lsa->str);
return;
}
old = rn->info;
if (old != lsa)
{
zlog_warn ("LSDB: Can't remove: different instance: %s (%p <-> %p) %s",
lsa->str, lsa, old, old->str);
return;
}
rn->info = NULL;
ospf6_lsa_unlock (old);
lsdb->count--;
}
static void
ospf6_lsdb_lookup_node (struct ospf6_lsdb_node *node,
u_int16_t type, u_int32_t id, u_int32_t adv_router,
struct ospf6_lsdb *lsdb)
{
int flag;
struct route_node *rn;
memset (node, 0, sizeof (struct ospf6_lsdb_node));
flag = OSPF6_LSDB_MATCH_TYPE | OSPF6_LSDB_MATCH_ID |
OSPF6_LSDB_MATCH_ADV_ROUTER;
ospf6_lsdb_set_key (&node->key, flag, type, id, adv_router);
rn = route_node_lookup (lsdb->table, (struct prefix *) &node->key);
if (! rn || ! rn->info)
return;
node->node = rn;
node->next = route_next (rn);
node->lsa = rn->info;
if (node->next != NULL)
route_unlock_node (node->next);
}
struct ospf6_lsa *
ospf6_lsdb_lookup_lsdb (u_int16_t type, u_int32_t id, u_int32_t adv_router,
struct ospf6_lsdb *lsdb)
{
struct ospf6_lsdb_node node;
ospf6_lsdb_lookup_node (&node, type, id, adv_router, lsdb);
return node.lsa;
}
/* Iteration function */
void
ospf6_lsdb_head (struct ospf6_lsdb_node *node, struct ospf6_lsdb *lsdb)
{
struct route_node *rn;
memset (node, 0, sizeof (struct ospf6_lsdb_node));
rn = route_top (lsdb->table);
if (rn == NULL)
return;
while (rn && rn->info == NULL)
rn = route_next (rn);
if (rn && rn->info)
{
node->node = rn;
node->next = route_next (rn);
node->lsa = rn->info;
if (node->next != NULL)
route_unlock_node (node->next);
}
}
void
ospf6_lsdb_type (struct ospf6_lsdb_node *node, u_int16_t type,
struct ospf6_lsdb *lsdb)
{
int flag;
struct route_node *rn;
memset (node, 0, sizeof (struct ospf6_lsdb_node));
flag = OSPF6_LSDB_MATCH_TYPE;
ospf6_lsdb_set_key (&node->key, flag, type, 0, 0);
/* get the closest radix node */
rn = route_node_get (lsdb->table, (struct prefix *) &node->key);
/* skip to the real existing lsdb entry */
while (rn && rn->info == NULL && rn->p.prefixlen >= node->key.prefixlen &&
prefix_match ((struct prefix *) &node->key, &rn->p))
rn = route_next (rn);
if (rn && rn->info)
{
node->node = rn;
node->next = route_next (rn);
node->lsa = rn->info;
if (node->next != NULL)
route_unlock_node (node->next);
}
}
void
ospf6_lsdb_type_router (struct ospf6_lsdb_node *node,
u_int16_t type, u_int32_t adv_router,
struct ospf6_lsdb *lsdb)
{
int flag;
struct route_node *rn;
memset (node, 0, sizeof (struct ospf6_lsdb_node));
flag = OSPF6_LSDB_MATCH_TYPE | OSPF6_LSDB_MATCH_ADV_ROUTER;
ospf6_lsdb_set_key (&node->key, flag, type, 0, adv_router);
/* get the closest radix node */
rn = route_node_get (lsdb->table, (struct prefix *) &node->key);
/* skip to the real existing lsdb entry */
while (rn && rn->info == NULL && rn->p.prefixlen >= node->key.prefixlen &&
prefix_match ((struct prefix *) &node->key, &rn->p))
rn = route_next (rn);
if (rn && rn->info)
{
node->node = rn;
node->next = route_next (rn);
node->lsa = rn->info;
if (node->next != NULL)
route_unlock_node (node->next);
}
}
void
ospf6_lsdb_next (struct ospf6_lsdb_node *node)
{
struct route_node *rn;
route_lock_node (node->node);
rn = route_next (node->node);
/* skip to the real existing lsdb entry */
while (rn && rn->info == NULL && rn->p.prefixlen >= node->key.prefixlen &&
prefix_match ((struct prefix *) &node->key, &rn->p))
rn = route_next (rn);
if (rn && rn->info && rn->p.prefixlen >= node->key.prefixlen &&
prefix_match ((struct prefix *) &node->key, &rn->p))
{
node->node = rn;
node->next = route_next (rn);
node->lsa = rn->info;
if (node->next != NULL)
route_unlock_node (node->next);
}
else
{
node->node = NULL;
node->next = NULL;
node->lsa = NULL;
}
}
struct ospf6_lsa *
ospf6_lsdb_lookup (u_int16_t type, u_int32_t id, u_int32_t adv_router,
void *scope)
{
struct ospf6_interface *o6i;
struct ospf6_area *o6a;
listnode i, j;
if (scope == (void *) ospf6)
return ospf6_lsdb_lookup_lsdb (type, id, adv_router, ospf6->lsdb);
for (i = listhead (ospf6->area_list); i; nextnode (i))
{
o6a = getdata (i);
if (scope == (void *) o6a)
return ospf6_lsdb_lookup_lsdb (type, id, adv_router, o6a->lsdb);
for (j = listhead (o6a->if_list); j; nextnode (j))
{
o6i = getdata (j);
if (scope == (void *) o6i)
return ospf6_lsdb_lookup_lsdb (type, id, adv_router, o6i->lsdb);
}
}
zlog_warn ("LSDB: Can't lookup: unknown scope, type %#hx", ntohs (type));
return NULL;
}
void
ospf6_lsdb_install (struct ospf6_lsa *new)
{
struct ospf6_lsdb *lsdb;
struct ospf6_lsa *old;
int need_hook = 0;
void (*hook) (struct ospf6_lsa *, struct ospf6_lsa *);
struct ospf6 *as = NULL;
struct ospf6_area *area = NULL;
struct ospf6_interface *linklocal = NULL;
hook = NULL;
switch (ntohs (new->header->type) & OSPF6_LSTYPE_SCOPE_MASK)
{
case OSPF6_LSA_SCOPE_LINKLOCAL:
linklocal = (struct ospf6_interface *) new->scope;
lsdb = linklocal->lsdb;
break;
case OSPF6_LSA_SCOPE_AREA:
area = (struct ospf6_area *) new->scope;
lsdb = area->lsdb;
break;
case OSPF6_LSA_SCOPE_AS:
as = (struct ospf6 *) new->scope;
lsdb = as->lsdb;
break;
default:
zlog_warn ("LSDB: Can't install: scope unknown: %s", new->str);
return;
}
/* whether schedule calculation or not */
old = ospf6_lsdb_lookup_lsdb (new->header->type, new->header->id,
new->header->adv_router, lsdb);
if (! old || ospf6_lsa_differ (old, new))
need_hook++;
/* log */
if (IS_OSPF6_DUMP_LSDB)
zlog_info ("LSDB: Install: %s %s", new->str,
((IS_LSA_MAXAGE (new)) ? "(MaxAge)" : ""));
if (old)
ospf6_lsa_lock (old);
ospf6_lsdb_add (new, lsdb);
gettimeofday (&new->installed, NULL);
hook = ospf6_lsdb_hook[ntohs (new->header->type) &
OSPF6_LSTYPE_CODE_MASK].hook;
if (need_hook && hook)
(*hook) (old, new);
/* old LSA should be freed here */
if (old)
ospf6_lsa_unlock (old);
}
void
ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb)
{
struct ospf6_lsdb_node node;
for (ospf6_lsdb_head (&node, lsdb); ! ospf6_lsdb_is_end (&node);
ospf6_lsdb_next (&node))
ospf6_lsdb_remove (node.lsa, lsdb);
}
void
ospf6_lsdb_remove_maxage (struct ospf6_lsdb *lsdb)
{
struct ospf6_lsdb_node node;
struct ospf6_lsa *lsa;
for (ospf6_lsdb_head (&node, lsdb); ! ospf6_lsdb_is_end (&node);
ospf6_lsdb_next (&node))
{
lsa = node.lsa;
/* contiue if it's not MaxAge */
if (! IS_LSA_MAXAGE (lsa))
continue;
/* continue if it's referenced by some retrans-lists */
if (lsa->lock != 1)
continue;
if (IS_OSPF6_DUMP_LSDB)
zlog_info ("Remove MaxAge LSA: %s", lsa->str);
ospf6_lsdb_remove (lsa, lsdb);
}
}
/* vty functions */
static int
ospf6_lsdb_match (int flag, u_int16_t type, u_int32_t id,
u_int32_t adv_router, struct ospf6_lsa *lsa)
{
if (CHECK_FLAG (flag, OSPF6_LSDB_MATCH_TYPE) &&
lsa->header->type != type)
return 0;
if (CHECK_FLAG (flag, OSPF6_LSDB_MATCH_ID) &&
lsa->header->id != id)
return 0;
if (CHECK_FLAG (flag, OSPF6_LSDB_MATCH_ADV_ROUTER) &&
lsa->header->adv_router != adv_router)
return 0;
return 1;
}
int
show_ipv6_ospf6_lsdb (struct vty *vty, int argc, char **argv,
struct ospf6_lsdb *lsdb)
{
u_int flag;
u_int16_t type = 0;
u_int32_t id, adv_router;
int ret;
struct ospf6_lsdb_node node;
char invalid[32], *invalidp;
int l_argc = argc;
char **l_argv = argv;
flag = 0;
memset (invalid, 0, sizeof (invalid));
invalidp = invalid;
/* chop tail if the words is 'dump' or 'summary' */
if (l_argc > 0 && ! strcmp (l_argv[l_argc - 1], "dump"))
{
SET_FLAG (flag, OSPF6_LSDB_SHOW_DUMP);
l_argc --;
}
else if (l_argc > 0 && ! strcmp (l_argv[l_argc - 1], "detail"))
{
SET_FLAG (flag, OSPF6_LSDB_SHOW_DETAIL);
l_argc --;
}
if (l_argc > 0)
{
SET_FLAG (flag, OSPF6_LSDB_MATCH_TYPE);
if (! strncmp (l_argv[0], "r", 1))
type = htons (OSPF6_LSA_TYPE_ROUTER);
if (! strncmp (l_argv[0], "n", 1))
type = htons (OSPF6_LSA_TYPE_NETWORK);
if (! strncmp (l_argv[0], "a", 1))
type = htons (OSPF6_LSA_TYPE_AS_EXTERNAL);
if (! strcmp (l_argv[0], "intra-prefix"))
type = htons (OSPF6_LSA_TYPE_INTRA_PREFIX);
if (! strcmp (l_argv[0], "inter-router"))
type = htons (OSPF6_LSA_TYPE_INTER_ROUTER);
if (! strcmp (l_argv[0], "inter-prefix"))
type = htons (OSPF6_LSA_TYPE_INTER_PREFIX);
if (! strncmp (l_argv[0], "l", 1))
type = htons (OSPF6_LSA_TYPE_LINK);
if (! strncmp (l_argv[0], "0x", 2) && strlen (l_argv[0]) == 6)
type = htons ((short) strtol (l_argv[0], (char **)NULL, 16));
if (! strncmp (l_argv[0], "*", 1))
UNSET_FLAG (flag, OSPF6_LSDB_MATCH_TYPE);
}
if (l_argc > 1)
{
SET_FLAG (flag, OSPF6_LSDB_MATCH_ID);
if (! strncmp (l_argv[1], "*", 1))
UNSET_FLAG (flag, OSPF6_LSDB_MATCH_ID);
else
{
ret = inet_pton (AF_INET, l_argv[1], &id);
if (ret != 1)
{
id = htonl (strtoul (l_argv[1], &invalidp, 10));
if (invalid[0] != '\0')
{
vty_out (vty, "Link State ID is not parsable: %s%s",
l_argv[1], VTY_NEWLINE);
return CMD_SUCCESS;
}
}
}
}
if (l_argc > 2)
{
SET_FLAG (flag, OSPF6_LSDB_MATCH_ADV_ROUTER);
if (! strncmp (l_argv[2], "*", 1))
UNSET_FLAG (flag, OSPF6_LSDB_MATCH_ADV_ROUTER);
else
{
ret = inet_pton (AF_INET, l_argv[2], &adv_router);
if (ret != 1)
{
adv_router = htonl (strtoul (l_argv[2], &invalidp, 10));
if (invalid[0] != '\0')
{
vty_out (vty, "Advertising Router is not parsable: %s%s",
l_argv[2], VTY_NEWLINE);
return CMD_SUCCESS;
}
}
}
}
if (! CHECK_FLAG (flag, OSPF6_LSDB_SHOW_DETAIL))
ospf6_lsa_show_summary_header (vty);
for (ospf6_lsdb_head (&node, lsdb); ! ospf6_lsdb_is_end (&node);
ospf6_lsdb_next (&node))
{
if (! ospf6_lsdb_match (flag, type, id, adv_router, node.lsa))
continue;
if (CHECK_FLAG (flag, OSPF6_LSDB_SHOW_DUMP))
ospf6_lsa_show_dump (vty, node.lsa);
else if (CHECK_FLAG (flag, OSPF6_LSDB_SHOW_DETAIL))
ospf6_lsa_show (vty, node.lsa);
else
ospf6_lsa_show_summary (vty, node.lsa);
}
return CMD_SUCCESS;
}
DEFUN (show_ipv6_ospf6_database,
show_ipv6_ospf6_database_cmd,
"show ipv6 ospf6 database",
SHOW_STR
IP6_STR
OSPF6_STR
"LSA Database\n"
)
{
struct ospf6_area *o6a;
struct ospf6_interface *o6i;
listnode i, j;
/* call show function for each of LSAs in the LSDBs */
for (i = listhead (ospf6->area_list); i; nextnode (i))
{
o6a = (struct ospf6_area *) getdata (i);
/* LinkLocal LSDBs */
for (j = listhead (o6a->if_list); j; nextnode (j))
{
o6i = (struct ospf6_interface *) getdata (j);
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, " Interface %s (Area: %s):%s",
o6i->interface->name, o6a->str, VTY_NEWLINE);
vty_out (vty, "%s", VTY_NEWLINE);
show_ipv6_ospf6_lsdb (vty, argc, argv, o6i->lsdb);
}
/* Area LSDBs */
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, " Area %s:%s", o6a->str, VTY_NEWLINE);
vty_out (vty, "%s", VTY_NEWLINE);
show_ipv6_ospf6_lsdb (vty, argc, argv, o6a->lsdb);
}
/* AS LSDBs */
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, " AS:%s", VTY_NEWLINE);
vty_out (vty, "%s", VTY_NEWLINE);
show_ipv6_ospf6_lsdb (vty, argc, argv, ospf6->lsdb);
return CMD_SUCCESS;
}
ALIAS (show_ipv6_ospf6_database,
show_ipv6_ospf6_database_type_cmd,
"show ipv6 ospf6 database (router|network|as-external|intra-prefix|inter-prefix|inter-router|link|*|HEX|dump|detail)",
SHOW_STR
IP6_STR
OSPF6_STR
"LSA Database\n"
"Router-LSA\n"
"Network-LSA\n"
"AS-External-LSA\n"
"Intra-Area-Prefix-LSA\n"
"Inter-Area-Router-LSA\n"
"Inter-Area-Prefix-LSA\n"
"Link-LSA\n"
"All LS Type\n"
"Specify LS Type by Hex\n"
"Dump raw LSA data in Hex\n"
"show detail of LSAs\n"
)
ALIAS (show_ipv6_ospf6_database,
show_ipv6_ospf6_database_type_id_cmd,
"show ipv6 ospf6 database (router|network|as-external|intra-prefix|inter-prefix|inter-router|link|*|HEX) (A.B.C.D|*|dump|detail)",
SHOW_STR
IP6_STR
OSPF6_STR
"LSA Database\n"
"Router-LSA\n"
"Network-LSA\n"
"AS-External-LSA\n"
"Intra-Area-Prefix-LSA\n"
"Inter-Area-Router-LSA\n"
"Inter-Area-Prefix-LSA\n"
"Link-LSA\n"
"All LS Type\n"
"Specify LS Type by Hex\n"
"Link State ID\n"
"All Link State ID\n"
"Dump raw LSA data in Hex\n"
"show detail of LSAs\n"
)
ALIAS (show_ipv6_ospf6_database,
show_ipv6_ospf6_database_type_id_adv_router_cmd,
"show ipv6 ospf6 database (router|network|as-external|intra-prefix|inter-prefix|inter-router|link|*|HEX) (A.B.C.D|*) (A.B.C.D|*|dump|detail)",
SHOW_STR
IP6_STR
OSPF6_STR
"LSA Database\n"
"Router-LSA\n"
"Network-LSA\n"
"AS-External-LSA\n"
"Intra-Area-Prefix-LSA\n"
"Inter-Area-Router-LSA\n"
"Inter-Area-Prefix-LSA\n"
"Link-LSA\n"
"All LS Type\n"
"Specify LS Type by Hex\n"
"Link State ID\n"
"All Link State ID\n"
"Advertising Router\n"
"All Advertising Router\n"
"Dump raw LSA data in Hex\n"
"show detail of LSAs\n"
)
ALIAS (show_ipv6_ospf6_database,
show_ipv6_ospf6_database_type_id_adv_router_dump_cmd,
"show ipv6 ospf6 database (router|network|as-external|intra-prefix|inter-prefix|inter-router|link|*|HEX) (A.B.C.D|*) (A.B.C.D|*) (dump|detail|)",
SHOW_STR
IP6_STR
OSPF6_STR
"LSA Database\n"
"Router-LSA\n"
"Network-LSA\n"
"AS-External-LSA\n"
"Intra-Area-Prefix-LSA\n"
"Inter-Area-Router-LSA\n"
"Inter-Area-Prefix-LSA\n"
"Link-LSA\n"
"All LS Type\n"
"Specify LS Type by Hex\n"
"Link State ID\n"
"All Link State ID\n"
"Advertising Router\n"
"All Advertising Router\n"
"Dump raw LSA data in Hex\n"
"show detail of LSAs\n"
)
void
ospf6_lsdb_init ()
{
install_element (VIEW_NODE, &show_ipv6_ospf6_database_cmd);
install_element (VIEW_NODE, &show_ipv6_ospf6_database_type_cmd);
install_element (VIEW_NODE, &show_ipv6_ospf6_database_type_id_cmd);
install_element (VIEW_NODE, &show_ipv6_ospf6_database_type_id_adv_router_cmd);
install_element (VIEW_NODE, &show_ipv6_ospf6_database_type_id_adv_router_dump_cmd);
install_element (ENABLE_NODE, &show_ipv6_ospf6_database_cmd);
install_element (ENABLE_NODE, &show_ipv6_ospf6_database_type_cmd);
install_element (ENABLE_NODE, &show_ipv6_ospf6_database_type_id_cmd);
install_element (ENABLE_NODE, &show_ipv6_ospf6_database_type_id_adv_router_cmd);
install_element (ENABLE_NODE, &show_ipv6_ospf6_database_type_id_adv_router_dump_cmd);
}