/* * Copyright (C) 1999 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. */ /* Shortest Path First calculation for OSPFv3 */ #include "ospf6d.h" #include "linklist.h" #include "prefix.h" #include "table.h" #include "ospf6_proto.h" #include "ospf6_lsa.h" #include "ospf6_lsdb.h" #include "ospf6_route.h" #include "ospf6_spf.h" #include "ospf6_neighbor.h" #include "ospf6_interface.h" #include "ospf6_area.h" #include "ospf6_bintree.h" #include "ospf6_linklist.h" struct bintree *_candidate_list; struct linklist *nexthop_list; struct ospf6_spf_candidate_node { u_int32_t cost; struct linklist *list; }; int ospf6_spf_candidate_node_cmp (void *a, void *b) { struct ospf6_spf_candidate_node *ca = a; struct ospf6_spf_candidate_node *cb = b; return ca->cost - cb->cost; } int ospf6_spf_vertex_cmp (void *a, void *b) { return 1; } void ospf6_spf_candidate_node_print (int indent_num, void *node) { struct ospf6_spf_candidate_node *cn = node; char format[256]; snprintf (format, sizeof (format), "%%%ds %%d (num: %%d)", indent_num * 2 + 1); zlog_info (format, " ", cn->cost, cn->list->count); } void ospf6_spf_candidate_init () { _candidate_list = bintree_create (); _candidate_list->cmp = ospf6_spf_candidate_node_cmp; } u_int32_t ospf6_spf_candidate_count () { u_int32_t count = 0; struct bintree_node node; struct ospf6_spf_candidate_node *cnode; for (bintree_head (_candidate_list, &node); ! bintree_end (&node); bintree_next (&node)) { cnode = node.data; count += cnode->list->count; } return count; } void ospf6_spf_candidate_print () { zlog_info ("---------------------------"); bintree_print (ospf6_spf_candidate_node_print, _candidate_list); zlog_info ("---------------------------"); } void ospf6_spf_candidate_enqueue (struct ospf6_vertex *v) { struct ospf6_spf_candidate_node req, *node; memset (&req, 0, sizeof (req)); req.cost = v->distance; node = bintree_lookup (&req, _candidate_list); if (node == NULL) { node = malloc (sizeof (struct ospf6_spf_candidate_node)); node->cost = v->distance; node->list = linklist_create (); node->list->cmp = ospf6_spf_vertex_cmp; bintree_add (node, _candidate_list); } linklist_add (v, node->list); #if 0 if (IS_OSPF6_DUMP_SPF) ospf6_spf_candidate_print (); #endif } struct ospf6_vertex * ospf6_spf_candidate_dequeue () { struct ospf6_spf_candidate_node *node; struct linklist_node lnode; struct ospf6_vertex *ret; node = bintree_lookup_min (_candidate_list); if (node == NULL) return NULL; linklist_head (node->list, &lnode); ret = lnode.data; linklist_remove (ret, node->list); if (node->list->count == 0) { linklist_delete (node->list); bintree_remove (node, _candidate_list); } #if 0 if (IS_OSPF6_DUMP_SPF) ospf6_spf_candidate_print (); #endif return ret; } void ospf6_spf_candidate_remove (struct ospf6_vertex *v) { struct bintree_node node; struct ospf6_spf_candidate_node *cnode = NULL; for (bintree_head (_candidate_list, &node); ! bintree_end (&node); bintree_next (&node)) { cnode = node.data; if (linklist_lookup (v, cnode->list)) { linklist_remove (v, cnode->list); break; } } if (cnode->list->count == 0) { linklist_delete (cnode->list); bintree_remove (cnode, _candidate_list); } } #define TIMER_SEC_MICRO 1000000 /* timeval calculation */ static void ospf6_timeval_add (const struct timeval *t1, const struct timeval *t2, struct timeval *result) { long moveup = 0; result->tv_usec = t1->tv_usec + t2->tv_usec; while (result->tv_usec > TIMER_SEC_MICRO) { result->tv_usec -= TIMER_SEC_MICRO; moveup ++; } result->tv_sec = t1->tv_sec + t2->tv_sec + moveup; } static void ospf6_timeval_add_equal (const struct timeval *t, struct timeval *result) { struct timeval tmp; ospf6_timeval_add (t, result, &tmp); result->tv_sec = tmp.tv_sec; result->tv_usec = tmp.tv_usec; } /* Compare timeval a and b. It returns an integer less than, equal to, or great than zero if a is found, respectively, to be less than, to match, or be greater than b. */ static int ospf6_timeval_cmp (const struct timeval t1, const struct timeval t2) { return (t1.tv_sec == t2.tv_sec ? t1.tv_usec - t2.tv_usec : t1.tv_sec - t2.tv_sec); } static int ospf6_spf_lsd_num (struct ospf6_vertex *V, struct ospf6_area *o6a) { u_int16_t type; u_int32_t id, adv_router; struct ospf6_lsa *lsa; if (V->vertex_id.id.s_addr) type = htons (OSPF6_LSA_TYPE_NETWORK); else type = htons (OSPF6_LSA_TYPE_ROUTER); id = V->vertex_id.id.s_addr; adv_router = V->vertex_id.adv_router.s_addr; lsa = ospf6_lsdb_lookup_lsdb (type, id, adv_router, o6a->lsdb); if (! lsa) { zlog_err ("SPF: Can't find associated LSA for %s", V->string); return 0; } return ospf6_lsa_lsd_num ((struct ospf6_lsa_header *) lsa->header); } /* RFC2328 section 16.1.1: Check if there is at least one router in the path from the root to this vertex. */ static int ospf6_spf_is_router_to_root (struct ospf6_vertex *c, struct ospf6_spftree *spf_tree) { listnode node; struct ospf6_vertex *p; if (spf_tree->root == c) return 0; for (node = listhead (c->parent_list); node; nextnode (node)) { p = (struct ospf6_vertex *) getdata (node); if (p == spf_tree->root) return 0; if (p->vertex_id.id.s_addr == 0) /* this is router */ continue; else if (ospf6_spf_is_router_to_root (p, spf_tree)) continue; return 0; } return 1; } static struct in6_addr * ospf6_spf_get_ipaddr (u_int32_t id, u_int32_t adv_router, u_int32_t ifindex) { char buf[64], nhbuf[64]; struct ospf6_interface *o6i; struct ospf6_neighbor *o6n; struct ospf6_lsa *lsa; struct ospf6_lsdb_node node; o6i = ospf6_interface_lookup_by_index (ifindex); if (! o6i) { zlog_err ("SPF: Can't find interface: index %d", ifindex); return (struct in6_addr *) NULL; } /* Find Link-LSA of the vertex in question */ lsa = NULL; for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_LINK), adv_router, o6i->lsdb); ! ospf6_lsdb_is_end (&node); ospf6_lsdb_next (&node)) lsa = node.lsa; /* return Linklocal Address field if the Link-LSA exists */ if (lsa && lsa->header->adv_router == adv_router) { struct ospf6_link_lsa *link_lsa; link_lsa = (struct ospf6_link_lsa *) (lsa->header + 1); return &link_lsa->llsa_linklocal; } zlog_warn ("SPF: Can't find Link-LSA for %s", inet_ntop (AF_INET, &adv_router, buf, sizeof (buf))); o6n = ospf6_neighbor_lookup (adv_router, o6i); if (! o6n) { inet_ntop (AF_INET, &adv_router, buf, sizeof (buf)); zlog_err ("SPF: Can't find neighbor %s in %s, " "unable to find his linklocal address", buf, o6i->interface->name); return (struct in6_addr *) NULL; } zlog_warn ("SPF: use packet's source address for %s's nexthop: %s", inet_ntop (AF_INET, &adv_router, buf, sizeof (buf)), inet_ntop (AF_INET6, &o6n->hisaddr, nhbuf, sizeof (nhbuf))); return &o6n->hisaddr; } static int ospf6_spf_nexthop_calculation (struct ospf6_vertex *W, u_int32_t ifindex, struct ospf6_vertex *V, struct ospf6_spftree *spf_tree) { struct ospf6_nexthop *nexthop, *n; u_int32_t adv_router, id; struct in6_addr nexthop_ipaddr, *ipaddr; unsigned int nexthop_ifindex; struct linklist_node node; /* until this, nexthop_list should be untouched */ assert (list_isempty (W->nexthop_list)); /* If ther is at least one intervening router from root to W */ if (ospf6_spf_is_router_to_root (W, spf_tree)) { /* Create no new nexthop, Inherit from the intervening router */ for (linklist_head (V->nexthop_list, &node); ! linklist_end (&node); linklist_next (&node)) linklist_add (node.data, W->nexthop_list); return 0; } /* Create new nexthop */ adv_router = W->vertex_id.adv_router.s_addr; id = W->vertex_id.id.s_addr; nexthop_ifindex = 0; memset (&nexthop_ipaddr, 0, sizeof (struct in6_addr)); if (spf_tree->root && V == spf_tree->root) { nexthop_ifindex = ifindex; if (! id) /* xxx, if V is router */ { ipaddr = ospf6_spf_get_ipaddr (id, adv_router, ifindex); if (! ipaddr) { /* xxx, should trigger error and quit SPF calculation... */ memset (&nexthop_ipaddr, 0xff, sizeof (struct in6_addr)); return -1; } else memcpy (&nexthop_ipaddr, ipaddr, sizeof (struct in6_addr)); } } else { /* V is broadcast network, W is router */ assert (V->vertex_id.id.s_addr != 0); assert (W->vertex_id.id.s_addr == 0); linklist_head (V->nexthop_list, &node); n = (struct ospf6_nexthop *) node.data; nexthop_ifindex = n->ifindex; ipaddr = ospf6_spf_get_ipaddr (id, adv_router, n->ifindex); if (! ipaddr) { /* xxx, should trigger error and quit SPF calculation... */ memset (&nexthop_ipaddr, 0xff, sizeof (struct in6_addr)); return -1; } else memcpy (&nexthop_ipaddr, ipaddr, sizeof (struct in6_addr)); } nexthop = XCALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_nexthop)); nexthop->ifindex = nexthop_ifindex; memcpy (&nexthop->address, &nexthop_ipaddr, sizeof (nexthop->address)); linklist_add (nexthop, W->nexthop_list); /* to hold malloced memory */ linklist_add (nexthop, nexthop_list); return 0; } static struct ospf6_vertex * ospf6_spf_vertex_create (int index, struct ospf6_vertex *V, struct ospf6_area *o6a) { struct ospf6_lsa *lsa; struct ospf6_router_lsa *router_lsa; struct ospf6_router_lsd *router_lsd; struct ospf6_network_lsa *network_lsa; struct ospf6_network_lsd *network_lsd; u_int32_t id, adv_router; u_int16_t type; void *lsd; struct ospf6_vertex *W; u_int16_t distance; u_int32_t ifindex; int backreference, lsdnum, i; char buf_router[16], buf_id[16]; type = id = adv_router = 0; /* Get Linkstate description */ lsd = ospf6_lsa_lsd_get (index, (struct ospf6_lsa_header *) V->lsa->header); if (! lsd) { zlog_err ("SPF: Can't find %dth Link description from %s", index, V->lsa->str); return (struct ospf6_vertex *) NULL; } /* Check Link state description */ distance = 0; ifindex = 0; if (V->lsa->header->type == htons (OSPF6_LSA_TYPE_ROUTER)) { router_lsd = lsd; if (router_lsd->type == OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT) { type = htons (OSPF6_LSA_TYPE_ROUTER); id = htonl (0); } else if (router_lsd->type == OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK) { type = htons (OSPF6_LSA_TYPE_NETWORK); id = router_lsd->neighbor_interface_id; } adv_router = router_lsd->neighbor_router_id; distance = ntohs (router_lsd->metric); ifindex = ntohl (router_lsd->interface_id); } else if (V->lsa->header->type == htons (OSPF6_LSA_TYPE_NETWORK)) { network_lsd = lsd; type = htons (OSPF6_LSA_TYPE_ROUTER); id = htonl (0); adv_router = network_lsd->adv_router; } /* Avoid creating candidate of myself */ if (adv_router == o6a->ospf6->router_id && type == htons (OSPF6_LSA_TYPE_ROUTER)) { return (struct ospf6_vertex *) NULL; } /* Find Associated LSA for W */ lsa = ospf6_lsdb_lookup_lsdb (type, id, adv_router, o6a->lsdb); if (! lsa) { inet_ntop (AF_INET, &adv_router, buf_router, sizeof (buf_router)); inet_ntop (AF_INET, &id, buf_id, sizeof (buf_id)); if (IS_OSPF6_DUMP_SPF) { if (type == htons (OSPF6_LSA_TYPE_ROUTER)) zlog_info ("SPF: Can't find LSA for W (%s *): not found", buf_router); else zlog_info ("SPF: Can't find LSA for W (%s %s): not found", buf_router, buf_id); } return (struct ospf6_vertex *) NULL; } if (IS_LSA_MAXAGE (lsa)) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: Associated LSA for W is MaxAge: %s", lsa->str); return (struct ospf6_vertex *) NULL; } /* Check back reference from W's lsa to V's lsa */ backreference = 0; lsdnum = ospf6_lsa_lsd_num ((struct ospf6_lsa_header *) lsa->header); for (i = 0; i < lsdnum; i++) { if (ospf6_lsa_lsd_is_refer_ok (i, (struct ospf6_lsa_header *) lsa->header, index, (struct ospf6_lsa_header *) V->lsa->header)) backreference++; } if (! backreference) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: Back reference failed: V: %s, W: %s", V->lsa->str, lsa->str); return (struct ospf6_vertex *) NULL; } /* Allocate new ospf6_vertex for W */ W = (struct ospf6_vertex *) XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex)); if (! W) { zlog_err ("SPF: Can't allocate memory for Vertex"); return (struct ospf6_vertex *) NULL; } memset (W, 0, sizeof (struct ospf6_vertex)); /* Initialize */ W->vertex_id.family = AF_UNSPEC; W->vertex_id.prefixlen = 64; /* xxx */ W->lsa = lsa; if (type == htons (OSPF6_LSA_TYPE_ROUTER)) W->vertex_id.id.s_addr = htonl (0); /* XXX */ else W->vertex_id.id.s_addr = W->lsa->header->id; W->vertex_id.adv_router.s_addr = W->lsa->header->adv_router; W->nexthop_list = linklist_create (); W->path_list = list_new (); W->parent_list = list_new (); W->distance = V->distance + distance; W->depth = V->depth + 1; inet_ntop (AF_INET, &W->vertex_id.adv_router.s_addr, buf_router, sizeof (buf_router)); inet_ntop (AF_INET, &W->vertex_id.id.s_addr, buf_id, sizeof (buf_id)); snprintf (W->string, sizeof (W->string), "[%s-%s (%d)]", buf_router, buf_id, W->distance); /* capability bits and optional capabilities */ if (W->vertex_id.id.s_addr == 0) { router_lsa = (struct ospf6_router_lsa *) (W->lsa->header + 1); W->capability_bits = router_lsa->bits; memcpy (W->opt_capability, router_lsa->options, sizeof (W->opt_capability)); } else { network_lsa = (struct ospf6_network_lsa *) (W->lsa->header + 1); W->capability_bits = network_lsa->reserved; memcpy (W->opt_capability, network_lsa->options, sizeof (W->opt_capability)); } /* Link to Parent node */ listnode_add (W->parent_list, V); /* Nexthop Calculation */ if (ospf6_spf_nexthop_calculation (W, ifindex, V, o6a->spf_tree) < 0) return NULL; return W; } static void ospf6_spf_vertex_delete (struct ospf6_vertex *v) { linklist_delete (v->nexthop_list); list_delete (v->path_list); list_delete (v->parent_list); XFREE (MTYPE_OSPF6_VERTEX, v); } static void ospf6_spf_vertex_merge (struct ospf6_vertex *w, struct ospf6_vertex *x) { listnode node; struct linklist_node lnode; /* merge should be done on two nodes which are almost the same */ /* these w and x should be both candidate. candidate should not have any children */ assert (list_isempty (w->path_list)); assert (list_isempty (x->path_list)); /* merge parent list */ for (node = listhead (w->parent_list); node; nextnode (node)) { if (listnode_lookup (x->parent_list, getdata (node))) continue; listnode_add (x->parent_list, getdata (node)); } /* merge nexthop list */ for (linklist_head (w->nexthop_list, &lnode); ! linklist_end (&lnode); linklist_next (&lnode)) linklist_add (lnode.data, x->nexthop_list); } static void ospf6_spf_initialize (list candidate_list, struct ospf6_area *o6a) { listnode node; struct ospf6_vertex *v; struct ospf6_lsa *lsa; u_int16_t type; u_int32_t id, adv_router; struct linklist_node lnode; struct ospf6_nexthop *nexthop; struct interface *ifp; char buf_router[64], buf_id[64]; /* delete topology routing table for this area */ ospf6_route_remove_all (o6a->table_topology); /* Delete previous spf tree */ for (node = listhead (o6a->spf_tree->list); node; nextnode (node)) { v = (struct ospf6_vertex *) getdata (node); ospf6_spf_vertex_delete (v); } list_delete_all_node (o6a->spf_tree->list); for (linklist_head (nexthop_list, &lnode); ! linklist_end (&lnode); linklist_next (&lnode)) XFREE (MTYPE_OSPF6_VERTEX, lnode.data); linklist_remove_all (nexthop_list); /* Find self originated Router-LSA */ type = htons (OSPF6_LSA_TYPE_ROUTER); id = htonl (0); adv_router = ospf6->router_id; lsa = ospf6_lsdb_lookup_lsdb (type, id, adv_router, o6a->lsdb); if (! lsa) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: Can't find self originated Router-LSA"); return; } if (IS_LSA_MAXAGE (lsa)) { zlog_err ("SPF: MaxAge self originated Router-LSA"); return; } /* Create root vertex */ v = (struct ospf6_vertex *) XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex)); if (! v) { zlog_err ("SPF: Can't allocate memory for root vertex"); return; } memset (v, 0, sizeof (struct ospf6_vertex)); v->vertex_id.family = AF_UNSPEC; /* XXX */ v->vertex_id.prefixlen = 64; /* XXX */ v->vertex_id.id.s_addr = htonl (0); v->vertex_id.adv_router.s_addr = ospf6->router_id; if (ospf6_is_asbr (ospf6)) OSPF6_OPT_SET (v->opt_capability, OSPF6_OPT_E); OSPF6_OPT_SET (v->opt_capability, OSPF6_OPT_V6); OSPF6_OPT_SET (v->opt_capability, OSPF6_OPT_R); v->nexthop_list = linklist_create (); v->path_list = list_new (); v->parent_list = list_new (); v->distance = 0; v->depth = 0; v->lsa = lsa; inet_ntop (AF_INET, &v->vertex_id.adv_router.s_addr, buf_router, sizeof (buf_router)); inet_ntop (AF_INET, &v->vertex_id.id.s_addr, buf_id, sizeof (buf_id)); snprintf (v->string, sizeof (v->string), "[%s-%s (%d)]", buf_router, buf_id, v->distance); nexthop = XCALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_nexthop)); ifp = if_lookup_by_name ("lo0"); if (ifp) nexthop->ifindex = ifp->ifindex; inet_pton (AF_INET6, "::1", &nexthop->address); linklist_add (nexthop, v->nexthop_list); linklist_add (nexthop, nexthop_list); o6a->spf_tree->root = v; listnode_add (candidate_list, v); ospf6_spf_candidate_enqueue (v); } static struct ospf6_vertex * ospf6_spf_get_closest_candidate (list candidate_list) { listnode node; struct ospf6_vertex *candidate, *closest; closest = (struct ospf6_vertex *) NULL; for (node = listhead (candidate_list); node; nextnode (node)) { candidate = (struct ospf6_vertex *) getdata (node); if (closest && candidate->distance > closest->distance) continue; /* always choose network vertices if those're the same cost */ if (closest && candidate->distance == closest->distance && closest->vertex_id.id.s_addr != 0) continue; closest = candidate; } return closest; } static struct ospf6_vertex * ospf6_spf_get_same_candidate (struct ospf6_vertex *w, list candidate_list) { listnode node; struct ospf6_vertex *c, *same; same = (struct ospf6_vertex *) NULL; for (node = listhead (candidate_list); node; nextnode (node)) { c = (struct ospf6_vertex *) getdata (node); if (w->vertex_id.adv_router.s_addr != c->vertex_id.adv_router.s_addr) continue; if (w->vertex_id.id.s_addr != c->vertex_id.id.s_addr) continue; if (same) zlog_warn ("SPF: duplicate candidates in candidate_list"); same = c; } return same; } static void ospf6_spf_install (struct ospf6_vertex *vertex, struct ospf6_area *o6a) { listnode node; struct ospf6_vertex *parent; struct ospf6_nexthop *nexthop; struct ospf6_route_req request; struct linklist_node lnode; struct ospf6_router_lsa *router_lsa; struct ospf6_network_lsa *network_lsa; router_lsa = OSPF6_LSA_HEADER_END (vertex->lsa->header); network_lsa = OSPF6_LSA_HEADER_END (vertex->lsa->header); if (IS_OSPF6_DUMP_SPF) { zlog_info ("SPF: Install: %s", vertex->string); } listnode_add (o6a->spf_tree->list, vertex); for (node = listhead (vertex->parent_list); node; nextnode (node)) { parent = (struct ospf6_vertex *) getdata (node); listnode_add (parent->path_list, vertex); vertex->depth = parent->depth + 1; } #if 0 if (vertex == o6a->spf_tree->root) return; #endif /*0*/ /* install route to topology table */ memset (&request, 0, sizeof (request)); if (vertex->vertex_id.id.s_addr) /* xxx */ request.route.type = OSPF6_DEST_TYPE_NETWORK; else request.route.type = OSPF6_DEST_TYPE_ROUTER; memcpy (&request.route.prefix, &vertex->vertex_id, sizeof (struct prefix)); request.path.area_id = o6a->area_id; request.path.type = OSPF6_PATH_TYPE_INTRA; request.path.cost = vertex->distance; request.path.cost_e2 = 0; request.path.origin.type = vertex->lsa->header->type; request.path.origin.id = vertex->lsa->header->id; request.path.origin.adv_router = vertex->lsa->header->adv_router; if (vertex->lsa->header->type == htons (OSPF6_LSA_TYPE_ROUTER)) request.path.router_bits = router_lsa->bits; memcpy (&request.path.capability, vertex->opt_capability, sizeof (request.path.capability)); #if 0 if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: install %d nexthops for %s", listcount (vertex->nexthop_list), vertex->string); #endif for (linklist_head (vertex->nexthop_list, &lnode); ! linklist_end (&lnode); linklist_next (&lnode)) { nexthop = lnode.data; request.nexthop.ifindex = nexthop->ifindex; memcpy (&request.nexthop.address, &nexthop->address, sizeof (request.nexthop.address)); ospf6_route_add (&request, o6a->table_topology); } } struct ospf6_vertex * ospf6_spf_lookup (struct ospf6_vertex *w, struct ospf6_area *o6a) { listnode node; struct ospf6_vertex *v; for (node = listhead (o6a->spf_tree->list); node; nextnode (node)) { v = (struct ospf6_vertex *) getdata (node); if (w->vertex_id.adv_router.s_addr != v->vertex_id.adv_router.s_addr) continue; if (w->vertex_id.id.s_addr != v->vertex_id.id.s_addr) continue; return v; } return (struct ospf6_vertex *) NULL; } u_int32_t stat_node = 0; u_int32_t stat_candidate = 0; u_int32_t stat_candidate_max = 0; u_int32_t stat_spf = 0; /* RFC2328 section 16.1 , RFC2740 section 3.8.1 */ static int ospf6_spf_calculation (struct ospf6_area *o6a) { list candidate_list; struct ospf6_vertex *V, *W, *X; int ldnum, i; if (! o6a || ! o6a->spf_tree) { zlog_err ("SPF: Can't calculate SPF tree: malformed area"); return -1; } stat_spf ++; stat_node = 0; stat_candidate = 0; stat_candidate_max = 0; if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: Calculation for area %s", o6a->str); ospf6_route_table_freeze (o6a->table_topology); ospf6_route_remove_all (o6a->table_topology); /* (1): Initialize the algorithm's data structures */ candidate_list = list_new (); ospf6_spf_initialize (candidate_list, o6a); stat_candidate ++; /* (3): Install closest from candidate list; if empty, break */ while (listcount (candidate_list)) { V = ospf6_spf_get_closest_candidate (candidate_list); listnode_delete (candidate_list, V); { struct ospf6_vertex *V_; if (stat_candidate_max < ospf6_spf_candidate_count ()) stat_candidate_max = ospf6_spf_candidate_count (); V_ = ospf6_spf_candidate_dequeue (); #if 0 if (IS_OSPF6_DUMP_SPF) { zlog_info ("Candidate list count: %lu", (u_long)ospf6_spf_candidate_count ()); zlog_info ("*** Candidate %s: %p <-> %p", (V == V_ ? "same" : "*** differ ***"), V, V_); zlog_info (" %p: %s", V, V->string); zlog_info (" %p: %s", V_, V_->string); } #endif } stat_node++; ospf6_spf_install (V, o6a); /* (2): Examin LSA of just added vertex */ ldnum = ospf6_spf_lsd_num (V, o6a); for (i = 0; i < ldnum; i++) { /* (b): If no LSA, or MaxAge, or LinkBack fail, examin next */ W = ospf6_spf_vertex_create (i, V, o6a); if (! W) continue; stat_candidate ++; /* (c) */ if (ospf6_spf_lookup (W, o6a)) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: %s: Already in SPF tree", W->string); ospf6_spf_vertex_delete (W); continue; } /* (d) */ X = ospf6_spf_get_same_candidate (W, candidate_list); if (X && X->distance < W->distance) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: %s: More closer found", W->string); ospf6_spf_vertex_delete (W); continue; } if (X && X->distance == W->distance) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: %s: new ECMP candidate", W->string); ospf6_spf_vertex_merge (W, X); ospf6_spf_vertex_delete (W); continue; } if (X) { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: %s: Swap with old candidate", W->string); listnode_delete (candidate_list, X); ospf6_spf_candidate_remove (X); ospf6_spf_vertex_delete (X); } else { if (IS_OSPF6_DUMP_SPF) zlog_info ("SPF: %s: New Candidate", W->string); } if (stat_candidate_max < ospf6_spf_candidate_count ()) stat_candidate_max = ospf6_spf_candidate_count (); listnode_add (candidate_list, W); ospf6_spf_candidate_enqueue (W); } } assert (listcount (candidate_list) == 0); list_free (candidate_list); assert (ospf6_spf_candidate_count () == 0); /* Clear thread timer */ o6a->spf_tree->t_spf_calculation = (struct thread *) NULL; if (IS_OSPF6_DUMP_SPF) { zlog_info ("SPF: Calculation for area %s done", o6a->str); zlog_info ("SPF: Statistics: %luth", (u_long)stat_spf); zlog_info ("SPF: Node Number: %lu", (u_long)stat_node); zlog_info ("SPF: Candidate Number: %lu Max: %lu", (u_long) stat_candidate, (u_long) stat_candidate_max); } ospf6_route_table_thaw (o6a->table_topology); return 0; } int ospf6_spf_calculation_thread (struct thread *t) { struct ospf6_area *o6a; struct timeval start, end, runtime, interval; o6a = (struct ospf6_area *) THREAD_ARG (t); if (! o6a) { zlog_err ("SPF: Thread error"); return -1; } if (! o6a->spf_tree) { zlog_err ("SPF: Can't find SPF Tree for area: %s", o6a->str); return -1; } /* execute SPF calculation */ gettimeofday (&start, (struct timezone *) NULL); ospf6_spf_calculation (o6a); gettimeofday (&end, (struct timezone *) NULL); /* update statistics */ o6a->spf_tree->timerun ++; ospf6_timeval_sub (&end, &start, &runtime); ospf6_timeval_add_equal (&runtime, &o6a->spf_tree->runtime_total); if (o6a->spf_tree->timerun == 1) { o6a->spf_tree->runtime_min.tv_sec = runtime.tv_sec; o6a->spf_tree->runtime_min.tv_usec = runtime.tv_usec; o6a->spf_tree->runtime_max.tv_sec = runtime.tv_sec; o6a->spf_tree->runtime_max.tv_usec = runtime.tv_usec; } if (ospf6_timeval_cmp (o6a->spf_tree->runtime_min, runtime) > 0) { o6a->spf_tree->runtime_min.tv_sec = runtime.tv_sec; o6a->spf_tree->runtime_min.tv_usec = runtime.tv_usec; } if (ospf6_timeval_cmp (runtime, o6a->spf_tree->runtime_max) > 0) { o6a->spf_tree->runtime_max.tv_sec = runtime.tv_sec; o6a->spf_tree->runtime_max.tv_usec = runtime.tv_usec; } if (o6a->spf_tree->timerun == 1) { ospf6_timeval_sub (&start, &ospf6->starttime, &interval); ospf6_timeval_add_equal (&interval, &o6a->spf_tree->interval_total); o6a->spf_tree->interval_min.tv_sec = interval.tv_sec; o6a->spf_tree->interval_min.tv_usec = interval.tv_usec; o6a->spf_tree->interval_max.tv_sec = interval.tv_sec; o6a->spf_tree->interval_max.tv_usec = interval.tv_usec; } else { ospf6_timeval_sub (&start, &o6a->spf_tree->updated_time, &interval); ospf6_timeval_add_equal (&interval, &o6a->spf_tree->interval_total); if (ospf6_timeval_cmp (o6a->spf_tree->interval_min, interval) > 0) { o6a->spf_tree->interval_min.tv_sec = interval.tv_sec; o6a->spf_tree->interval_min.tv_usec = interval.tv_usec; } if (ospf6_timeval_cmp (interval, o6a->spf_tree->interval_max) > 0) { o6a->spf_tree->interval_max.tv_sec = interval.tv_sec; o6a->spf_tree->interval_max.tv_usec = interval.tv_usec; } } o6a->spf_tree->updated_time.tv_sec = end.tv_sec; o6a->spf_tree->updated_time.tv_usec = end.tv_usec; /* clear thread */ o6a->spf_tree->t_spf_calculation = (struct thread *) NULL; return 0; } void ospf6_spf_database_hook (struct ospf6_lsa *old, struct ospf6_lsa *new) { struct ospf6_area *o6a = NULL; struct ospf6_interface *o6i = NULL; if (new->header->type == htons (OSPF6_LSA_TYPE_ROUTER) || new->header->type == htons (OSPF6_LSA_TYPE_NETWORK)) o6a = new->scope; else if (new->header->type == htons (OSPF6_LSA_TYPE_LINK)) { o6i = new->scope; o6a = o6i->area; } if (o6a) ospf6_spf_calculation_schedule (o6a->area_id); } void ospf6_spf_calculation_schedule (u_int32_t area_id) { struct ospf6_area *o6a; char buf[64]; o6a = ospf6_area_lookup (area_id, ospf6); if (! o6a) { inet_ntop (AF_INET, &area_id, buf, sizeof (buf)); zlog_err ("SPF: Can't find area: %s", buf); return; } if (! o6a->spf_tree) { zlog_err ("SPF: Can't find SPF Tree for area: %s", o6a->str); return; } if (o6a->spf_tree->t_spf_calculation) return; o6a->spf_tree->t_spf_calculation = thread_add_event (master, ospf6_spf_calculation_thread, o6a, 0); } struct ospf6_spftree * ospf6_spftree_create () { struct ospf6_spftree *spf_tree; spf_tree = (struct ospf6_spftree *) XMALLOC (MTYPE_OSPF6_SPFTREE, sizeof (struct ospf6_spftree)); if (! spf_tree) { zlog_err ("SPF: Can't allocate memory for SPF tree"); return (struct ospf6_spftree *) NULL; } memset (spf_tree, 0, sizeof (spf_tree)); spf_tree->list = list_new (); return spf_tree; } void ospf6_spftree_delete (struct ospf6_spftree *spf_tree) { listnode node; struct ospf6_vertex *v; /* Delete spf tree */ for (node = listhead (spf_tree->list); node; nextnode (node)) { v = (struct ospf6_vertex *) getdata (node); ospf6_spf_vertex_delete (v); } list_delete_all_node (spf_tree->list); XFREE (MTYPE_OSPF6_SPFTREE, spf_tree); } void ospf6_nexthop_show (struct vty *vty, struct ospf6_nexthop *nexthop) { char buf[128], *ifname; struct ospf6_interface *o6i; ifname = NULL; o6i = ospf6_interface_lookup_by_index (nexthop->ifindex); if (! o6i) { zlog_err ("Spf: invalid ifindex %d in nexthop", nexthop->ifindex); } else ifname = o6i->interface->name; inet_ntop (AF_INET6, &nexthop->address, buf, sizeof (buf)); vty_out (vty, " %s%%%s(%d)%s", buf, ifname, nexthop->ifindex, VTY_NEWLINE); } void ospf6_vertex_show (struct vty *vty, struct ospf6_vertex *vertex) { listnode node; struct ospf6_vertex *v; struct linklist_node lnode; vty_out (vty, "SPF node %s%s", vertex->string, VTY_NEWLINE); vty_out (vty, " cost to this node: %d%s", vertex->distance, VTY_NEWLINE); vty_out (vty, " hops to this node: %d%s", vertex->depth, VTY_NEWLINE); vty_out (vty, " nexthops reachable to this node:%s", VTY_NEWLINE); for (linklist_head (vertex->nexthop_list, &lnode); ! linklist_end (&lnode); linklist_next (&lnode)) ospf6_nexthop_show (vty, (struct ospf6_nexthop *) lnode.data); vty_out (vty, " parent nodes to this node:%s", VTY_NEWLINE); if (! list_isempty (vertex->parent_list)) vty_out (vty, " "); for (node = listhead (vertex->parent_list); node; nextnode (node)) { v = (struct ospf6_vertex *) getdata (node); vty_out (vty, "%s ", v->string); } if (! list_isempty (vertex->parent_list)) vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, " child nodes to this node:%s", VTY_NEWLINE); if (! list_isempty (vertex->path_list)) vty_out (vty, " "); for (node = listhead (vertex->path_list); node; nextnode (node)) { v = (struct ospf6_vertex *) getdata (node); vty_out (vty, "%s ", v->string); } if (! list_isempty (vertex->path_list)) vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "%s", VTY_NEWLINE); } void ospf6_spf_statistics_show (struct vty *vty, struct ospf6_spftree *spf_tree) { listnode node; struct ospf6_vertex *vertex; u_int router_count, network_count, maxdepth; struct timeval runtime_avg, interval_avg, last_updated, now; char rmin[64], rmax[64], ravg[64]; char imin[64], imax[64], iavg[64]; char last_updated_string[64]; maxdepth = router_count = network_count = 0; for (node = listhead (spf_tree->list); node; nextnode (node)) { vertex = (struct ospf6_vertex *) getdata (node); if (vertex->vertex_id.id.s_addr) network_count++; else router_count++; if (maxdepth < vertex->depth) maxdepth = vertex->depth; } ospf6_timeval_div (&spf_tree->runtime_total, spf_tree->timerun, &runtime_avg); ospf6_timeval_string (&spf_tree->runtime_min, rmin, sizeof (rmin)); ospf6_timeval_string (&spf_tree->runtime_max, rmax, sizeof (rmax)); ospf6_timeval_string (&runtime_avg, ravg, sizeof (ravg)); ospf6_timeval_div (&spf_tree->interval_total, spf_tree->timerun, &interval_avg); ospf6_timeval_string (&spf_tree->interval_min, imin, sizeof (imin)); ospf6_timeval_string (&spf_tree->interval_max, imax, sizeof (imax)); ospf6_timeval_string (&interval_avg, iavg, sizeof (iavg)); gettimeofday (&now, (struct timezone *) NULL); ospf6_timeval_sub (&now, &spf_tree->updated_time, &last_updated); ospf6_timeval_string (&last_updated, last_updated_string, sizeof (last_updated_string)); vty_out (vty, " SPF algorithm executed %d times%s", spf_tree->timerun, VTY_NEWLINE); vty_out (vty, " Average time to run SPF: %s%s", ravg, VTY_NEWLINE); vty_out (vty, " Maximum time to run SPF: %s%s", rmax, VTY_NEWLINE); vty_out (vty, " Average interval of SPF: %s%s", iavg, VTY_NEWLINE); vty_out (vty, " SPF last updated: %s ago%s", last_updated_string, VTY_NEWLINE); vty_out (vty, " Current SPF node count: %d%s", listcount (spf_tree->list), VTY_NEWLINE); vty_out (vty, " Router: %d Network: %d%s", router_count, network_count, VTY_NEWLINE); vty_out (vty, " Maximum of Hop count to nodes: %d%s", maxdepth, VTY_NEWLINE); } DEFUN (show_ipv6_ospf6_area_spf_node, show_ipv6_ospf6_area_spf_node_cmd, "show ipv6 ospf6 area A.B.C.D spf node", SHOW_STR IP6_STR OSPF6_STR OSPF6_AREA_STR OSPF6_AREA_ID_STR "Shortest Path First caculation\n" "vertex infomation\n" ) { listnode i; u_int32_t area_id; struct ospf6_area *o6a; struct ospf6_vertex *vertex; OSPF6_CMD_CHECK_RUNNING (); inet_pton (AF_INET, argv[0], &area_id); o6a = ospf6_area_lookup (area_id, ospf6); if (! o6a) return CMD_SUCCESS; for (i = listhead (o6a->spf_tree->list); i; nextnode (i)) { vertex = (struct ospf6_vertex *) getdata (i); ospf6_vertex_show (vty, vertex); } return CMD_SUCCESS; } static void ospf6_spftree_show (struct vty *vty, char *prefix, int current_rest, struct ospf6_vertex *v) { char *p; int psize; int restnum; listnode node; vty_out (vty, "%s+-%s%s", prefix, v->string, VTY_NEWLINE); if (listcount (v->path_list) == 0) return; psize = strlen (prefix) + 3; p = malloc (psize); if (!p) { vty_out (vty, "depth too long ...%s", VTY_NEWLINE); return; } restnum = listcount (v->path_list); for (node = listhead (v->path_list); node; nextnode (node)) { --restnum; snprintf (p, psize, "%s%s", prefix, (current_rest ? "| " : " ")); ospf6_spftree_show (vty, p, restnum, (struct ospf6_vertex *) getdata (node)); } free (p); } DEFUN (show_ipv6_ospf6_area_spf_tree, show_ipv6_ospf6_area_spf_tree_cmd, "show ipv6 ospf6 area A.B.C.D spf tree", SHOW_STR IP6_STR OSPF6_STR OSPF6_AREA_STR OSPF6_AREA_ID_STR "Shortest Path First caculation\n" "Displays spf tree\n") { u_int32_t area_id; struct ospf6_area *o6a; OSPF6_CMD_CHECK_RUNNING (); inet_pton (AF_INET, argv[0], &area_id); o6a = ospf6_area_lookup (area_id, ospf6); if (! o6a) return CMD_SUCCESS; vty_out (vty, "%s SPF tree for Area %s%s%s", VTY_NEWLINE, o6a->str, VTY_NEWLINE, VTY_NEWLINE); if (! o6a->spf_tree->root) return CMD_SUCCESS; ospf6_spftree_show (vty, "", 0, o6a->spf_tree->root); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_area_topology, show_ipv6_ospf6_area_topology_cmd, "show ipv6 ospf6 area A.B.C.D topology", SHOW_STR IP6_STR OSPF6_STR OSPF6_AREA_STR OSPF6_AREA_ID_STR OSPF6_SPF_STR "Displays SPF topology table\n") { struct ospf6_area *o6a; u_int32_t area_id; OSPF6_CMD_CHECK_RUNNING (); inet_pton (AF_INET, argv[0], &area_id); o6a = ospf6_area_lookup (area_id, ospf6); if (! o6a) return CMD_SUCCESS; argc -= 1; argv += 1; return ospf6_route_table_show (vty, argc, argv, o6a->table_topology); } ALIAS (show_ipv6_ospf6_area_topology, show_ipv6_ospf6_area_topology_router_cmd, "show ipv6 ospf6 area A.B.C.D topology (A.B.C.D|<0-4294967295>|detail)", SHOW_STR IP6_STR OSPF6_STR OSPF6_AREA_STR OSPF6_AREA_ID_STR OSPF6_SPF_STR "Displays SPF topology table\n" OSPF6_ROUTER_ID_STR OSPF6_ROUTER_ID_STR ) ALIAS (show_ipv6_ospf6_area_topology, show_ipv6_ospf6_area_topology_router_lsid_cmd, "show ipv6 ospf6 area A.B.C.D topology (A.B.C.D|<0-4294967295>) (A.B.C.D|<0-4294967295>)", SHOW_STR IP6_STR OSPF6_STR OSPF6_AREA_STR OSPF6_AREA_ID_STR OSPF6_SPF_STR "Displays SPF topology table\n" OSPF6_ROUTER_ID_STR OSPF6_ROUTER_ID_STR OSPF6_LS_ID_STR OSPF6_LS_ID_STR ) void ospf6_spf_init () { nexthop_list = linklist_create (); ospf6_spf_candidate_init (); install_element (VIEW_NODE, &show_ipv6_ospf6_area_spf_node_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_area_topology_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_area_topology_router_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_area_topology_router_lsid_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_area_spf_node_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_area_topology_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_area_topology_router_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_area_topology_router_lsid_cmd); }