mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-30 23:27:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1455 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1455 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | ||
|  * 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);
 | ||
| }
 | ||
| 
 | 
