--- embedaddon/quagga/ospfd/ospf_spf.c 2012/10/09 09:22:29 1.1.1.2 +++ embedaddon/quagga/ospfd/ospf_spf.c 2016/11/02 10:09:12 1.1.1.4 @@ -46,13 +46,56 @@ Software Foundation, Inc., 59 Temple Place - Suite 330 #include "ospfd/ospf_abr.h" #include "ospfd/ospf_dump.h" +/* Variables to ensure a SPF scheduled log message is printed only once */ + +static unsigned int spf_reason_flags = 0; + +static void +ospf_clear_spf_reason_flags () +{ + spf_reason_flags = 0; +} + +static void +ospf_spf_set_reason (ospf_spf_reason_t reason) +{ + spf_reason_flags |= 1 << reason; +} + +static void +ospf_get_spf_reason_str (char *buf) +{ + if (!buf) + return; + + buf[0] = '\0'; + if (spf_reason_flags) + { + if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL) + strcat (buf, "R, "); + if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL) + strcat (buf, "N, "); + if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL) + strcat (buf, "S, "); + if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL) + strcat (buf, "AS, "); + if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE) + strcat (buf, "ABR, "); + if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE) + strcat (buf, "ASBR, "); + if (spf_reason_flags & SPF_FLAG_MAXAGE) + strcat (buf, "M, "); + buf[strlen(buf)-2] = '\0'; /* skip the last ", " */ + } +} + static void ospf_vertex_free (void *); /* List of allocated vertices, to simplify cleanup of SPF. * Not thread-safe obviously. If it ever needs to be, it'd have to be * dynamically allocated at begin of ospf_spf_calculate */ static struct list vertex_list = { .del = ospf_vertex_free }; - + /* Heap related functions, for the managment of the candidates, to * be used with pqueue. */ static int @@ -90,7 +133,7 @@ update_stat (void *node , int position) /* Set the status of the vertex, when its position changes. */ *(v->stat) = position; } - + static struct vertex_nexthop * vertex_nexthop_new (void) { @@ -134,7 +177,7 @@ ospf_canonical_nexthops_free (struct vertex *root) vertex_nexthop_free (vp->nexthop); } } - + /* TODO: Parent list should be excised, in favour of maintaining only * vertex_nexthop, with refcounts. */ @@ -159,7 +202,7 @@ vertex_parent_free (void *p) { XFREE (MTYPE_OSPF_VERTEX_PARENT, p); } - + static struct vertex * ospf_vertex_new (struct ospf_lsa *lsa) { @@ -276,7 +319,7 @@ ospf_vertex_add_parent (struct vertex *v) listnode_add (vp->parent->children, v); } } - + static void ospf_spf_init (struct ospf_area *area) { @@ -422,7 +465,8 @@ ospf_spf_add_parent (struct vertex *v, struct vertex * struct vertex_nexthop *newhop, unsigned int distance) { - struct vertex_parent *vp; + struct vertex_parent *vp, *wp; + struct listnode *node; /* we must have a newhop, and a distance */ assert (v && w && newhop); @@ -456,7 +500,19 @@ ospf_spf_add_parent (struct vertex *v, struct vertex * w->distance = distance; } - /* new parent is <= existing parents, add it to parent list */ + /* new parent is <= existing parents, add it to parent list (if nexthop + * not on parent list) + */ + for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) + { + if (memcmp(newhop, wp->nexthop, sizeof(*newhop)) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: ... nexthop already on parent list, skipping add", __func__); + return; + } + } + vp = vertex_parent_new (v, ospf_lsa_has_link (w->lsa, v->lsa), newhop); listnode_add (w->parents, vp); @@ -477,13 +533,15 @@ ospf_spf_add_parent (struct vertex *v, struct vertex * static unsigned int ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, struct vertex *w, struct router_lsa_link *l, - unsigned int distance) + unsigned int distance, int lsa_pos) { struct listnode *node, *nnode; struct vertex_nexthop *nh; struct vertex_parent *vp; struct ospf_interface *oi = NULL; unsigned int added = 0; + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; if (IS_DEBUG_OSPF_EVENT) { @@ -502,30 +560,38 @@ ospf_nexthop_calculation (struct ospf_area *area, stru the OSPF interface connecting to the destination network/router. */ + /* we *must* be supplied with the link data */ + assert (l != NULL); + oi = ospf_if_lookup_by_lsa_pos (area, lsa_pos); + if (!oi) + { + zlog_debug("%s: OI not found in LSA: lsa_pos:%d link_id:%s link_data:%s", + __func__, lsa_pos, + inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ), + inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ)); + return 0; + } + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug("%s: considering link:%s " + "type:%d link_id:%s link_data:%s", + __func__, oi->ifp->name, l->m[0].type, + inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ), + inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ)); + } + if (w->type == OSPF_VERTEX_ROUTER) { /* l is a link from v to w * l2 will be link from w to v */ struct router_lsa_link *l2 = NULL; - - /* we *must* be supplied with the link data */ - assert (l != NULL); - - if (IS_DEBUG_OSPF_EVENT) - { - char buf1[BUFSIZ]; - char buf2[BUFSIZ]; - - zlog_debug("ospf_nexthop_calculation(): considering link " - "type %d link_id %s link_data %s", - l->m[0].type, - inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ), - inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ)); - } if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { + struct in_addr nexthop = { .s_addr = 0 }; + /* If the destination is a router which connects to the calculating router via a Point-to-MultiPoint network, the destination's next hop IP address(es) @@ -536,68 +602,67 @@ ospf_nexthop_calculation (struct ospf_area *area, stru provides an IP address of the next hop router. At this point l is a link from V to W, and V is the - root ("us"). Find the local interface associated - with l (its address is in l->link_data). If it - is a point-to-multipoint interface, then look through - the links in the opposite direction (W to V). If - any of them have an address that lands within the + root ("us"). If it is a point-to-multipoint interface, + then look through the links in the opposite direction (W to V). + If any of them have an address that lands within the subnet declared by the PtMP link, then that link - is a constituent of the PtMP link, and its address is + is a constituent of the PtMP link, and its address is a nexthop address for V. */ - oi = ospf_if_is_configured (area->ospf, &l->link_data); - if (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) - { - struct prefix_ipv4 la; + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + { + /* Having nexthop = 0 is tempting, but NOT acceptable. + It breaks AS-External routes with a forwarding address, + since ospf_ase_complete_direct_routes() will mistakenly + assume we've reached the last hop and should place the + forwarding address as nexthop. + Also, users may configure multi-access links in p2p mode, + so we need the IP to ARP the nexthop. + */ + struct ospf_neighbor *nbr_w; - la.family = AF_INET; - la.prefixlen = oi->address->prefixlen; + nbr_w = ospf_nbr_lookup_by_routerid (oi->nbrs, &l->link_id); + if (nbr_w != NULL) + { + added = 1; + nexthop = nbr_w->src; + } + } + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + { + struct prefix_ipv4 la; - /* V links to W on PtMP interface - - find the interface address on W */ - while ((l2 = ospf_get_next_link (w, v, l2))) - { - la.prefix = l2->link_data; + la.family = AF_INET; + la.prefixlen = oi->address->prefixlen; - if (prefix_cmp ((struct prefix *) &la, - oi->address) == 0) - /* link_data is on our PtMP network */ - break; - } - } /* end l is on point-to-multipoint link */ - else - { - /* l is a regular point-to-point link. - Look for a link from W to V. - */ - while ((l2 = ospf_get_next_link (w, v, l2))) - { - oi = ospf_if_is_configured (area->ospf, - &(l2->link_data)); + /* V links to W on PtMP interface + - find the interface address on W */ + while ((l2 = ospf_get_next_link (w, v, l2))) + { + la.prefix = l2->link_data; - if (oi == NULL) - continue; + if (prefix_cmp ((struct prefix *) &la, + oi->address) != 0) + continue; + /* link_data is on our PtMP network */ + added = 1; + nexthop = l2->link_data; + break; + } + } - if (!IPV4_ADDR_SAME (&oi->address->u.prefix4, - &l->link_data)) - continue; - - break; - } - } - - if (oi && l2) + if (added) { /* found all necessary info to build nexthop */ nh = vertex_nexthop_new (); nh->oi = oi; - nh->router = l2->link_data; + nh->router = nexthop; ospf_spf_add_parent (v, w, nh, distance); return 1; } else - zlog_info("ospf_nexthop_calculation(): " - "could not determine nexthop for link"); + zlog_info("%s: could not determine nexthop for link %s", + __func__, oi->ifp->name); } /* end point-to-point link from V to W */ else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { @@ -630,19 +695,13 @@ ospf_nexthop_calculation (struct ospf_area *area, stru else { assert(w->type == OSPF_VERTEX_NETWORK); - oi = ospf_if_is_configured (area->ospf, &(l->link_data)); - if (oi) - { - nh = vertex_nexthop_new (); - nh->oi = oi; - nh->router.s_addr = 0; - ospf_spf_add_parent (v, w, nh, distance); - return 1; - } + + nh = vertex_nexthop_new (); + nh->oi = oi; + nh->router.s_addr = 0; /* Nexthop not required */ + ospf_spf_add_parent (v, w, nh, distance); + return 1; } - zlog_info("ospf_nexthop_calculation(): " - "Unknown attached link"); - return 0; } /* end V is the root */ /* Check if W's parent is a network connected to root. */ else if (v->type == OSPF_VERTEX_NETWORK) @@ -673,20 +732,34 @@ ospf_nexthop_calculation (struct ospf_area *area, stru added = 1; ospf_spf_add_parent (v, w, nh, distance); } - } + /* Note lack of return is deliberate. See next comment. */ + } } /* NB: This code is non-trivial. * * E.g. it is not enough to know that V connects to the root. It is * also important that the while above, looping through all links from * W->V found at least one link, so that we know there is - * bi-directional connectivity between V and W. Otherwise, if we - * /always/ return here, but don't check that W->V exists then we - * we will prevent SPF from finding/using higher cost paths.. + * bi-directional connectivity between V and W (which need not be the + * case, e.g. when OSPF has not yet converged fully). Otherwise, if + * we /always/ return here, without having checked that root->V->-W + * actually resulted in a valid nexthop being created, then we we will + * prevent SPF from finding/using higher cost paths. * - * See also bug #330, and also: + * It is important, if root->V->W has not been added, that we continue + * through to the intervening-router nexthop code below. So as to + * ensure other paths to V may be used. This avoids unnecessary + * blackholes while OSPF is convergening. * - * http://blogs.sun.com/paulj/entry/the_difference_a_line_makes + * I.e. we may have arrived at this function, examining V -> W, via + * workable paths other than root -> V, and it's important to avoid + * getting "confused" by non-working root->V->W path - it's important + * to *not* lose the working non-root paths, just because of a + * non-viable root->V->W. + * + * See also bug #330 (required reading!), and: + * + * http://blogs.oracle.com/paulj/entry/the_difference_a_line_makes */ if (added) return added; @@ -723,7 +796,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *are u_char *lim; struct router_lsa_link *l = NULL; struct in_addr *r; - int type = 0; + int type = 0, lsa_pos=-1, lsa_pos_next=0; /* If this is a router-LSA, and bit V of the router-LSA (see Section A.4.2:RFC2328) is set, set Area A's TransitCapability to TRUE. */ @@ -752,6 +825,8 @@ ospf_spf_next (struct vertex *v, struct ospf_area *are { l = (struct router_lsa_link *) p; + lsa_pos = lsa_pos_next; /* LSA link position */ + lsa_pos_next++; p += (OSPF_ROUTER_LSA_LINK_SIZE + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); @@ -873,7 +948,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *are w = ospf_vertex_new (w_lsa); /* Calculate nexthop to W. */ - if (ospf_nexthop_calculation (area, v, w, l, distance)) + if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos)) pqueue_enqueue (w, candidate); else if (IS_DEBUG_OSPF_EVENT) zlog_debug ("Nexthop Calc failed"); @@ -893,7 +968,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *are { /* Found an equal-cost path to W. * Calculate nexthop of to W from V. */ - ospf_nexthop_calculation (area, v, w, l, distance); + ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos); } /* less than. */ else @@ -903,7 +978,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *are * valid nexthop it will call spf_add_parents, which * will flush the old parents */ - if (ospf_nexthop_calculation (area, v, w, l, distance)) + if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos)) /* Decrease the key of the node in the heap. * trickle-sort it up towards root, just in case this * node should now be the new root due the cost change. @@ -939,7 +1014,7 @@ ospf_spf_dump (struct vertex *v, int i) for (ALL_LIST_ELEMENTS_RO (v->parents, nnode, parent)) { zlog_debug (" nexthop %p %s %s", - parent->nexthop, + (void *)parent->nexthop, inet_ntoa (parent->nexthop->router), parent->nexthop->oi ? IF_NAME(parent->nexthop->oi) : "NULL"); @@ -969,6 +1044,7 @@ ospf_spf_process_stubs (struct ospf_area *area, struct u_char *lim; struct router_lsa_link *l; struct router_lsa *rlsa; + int lsa_pos = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_process_stubs():processing router LSA, id: %s", @@ -990,7 +1066,8 @@ ospf_spf_process_stubs (struct ospf_area *area, struct (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); if (l->m[0].type == LSA_LINK_TYPE_STUB) - ospf_intra_add_stub (rt, l, v, area, parent_is_root); + ospf_intra_add_stub (rt, l, v, area, parent_is_root, lsa_pos); + lsa_pos++; } } @@ -1198,29 +1275,30 @@ ospf_spf_calculate (struct ospf_area *area, struct rou /* Free candidate queue. */ pqueue_delete (candidate); - + ospf_vertex_dump (__func__, area->spf, 0, 1); /* Free nexthop information, canonical versions of which are attached * the first level of router vertices attached to the root vertex, see * ospf_nexthop_calculation. */ ospf_canonical_nexthops_free (area->spf); - - /* Free SPF vertices, but not the list. List has ospf_vertex_free - * as deconstructor. - */ - list_delete_all_node (&vertex_list); - + /* Increment SPF Calculation Counter. */ area->spf_calculation++; quagga_gettime (QUAGGA_CLK_MONOTONIC, &area->ospf->ts_spf); + area->ts_spf = area->ospf->ts_spf; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_spf_calculate: Stop. %ld vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); + + /* Free SPF vertices, but not the list. List has ospf_vertex_free + * as deconstructor. + */ + list_delete_all_node (&vertex_list); } - + /* Timer for SPF calculation. */ static int ospf_spf_calculate_timer (struct thread *thread) @@ -1229,12 +1307,18 @@ ospf_spf_calculate_timer (struct thread *thread) struct route_table *new_table, *new_rtrs; struct ospf_area *area; struct listnode *node, *nnode; - + struct timeval start_time, stop_time, spf_start_time; + int areas_processed = 0; + unsigned long ia_time, prune_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + char rbuf[32]; /* reason_buf */ + if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: Timer (SPF calculation expire)"); ospf->t_spf_calc = NULL; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &spf_start_time); /* Allocate new table tree. */ new_table = route_table_init (); new_rtrs = route_table_init (); @@ -1249,21 +1333,36 @@ ospf_spf_calculate_timer (struct thread *thread) */ if (ospf->backbone && ospf->backbone == area) continue; - + ospf_spf_calculate (area, new_table, new_rtrs); + areas_processed++; } - + /* SPF for backbone, if required */ if (ospf->backbone) - ospf_spf_calculate (ospf->backbone, new_table, new_rtrs); - + { + ospf_spf_calculate (ospf->backbone, new_table, new_rtrs); + areas_processed++; + } + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + spf_time = timeval_elapsed (stop_time, spf_start_time); + ospf_vl_shut_unapproved (ospf); + start_time = stop_time; /* saving a call */ + ospf_ia_routing (ospf, new_table, new_rtrs); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + ia_time = timeval_elapsed (stop_time, start_time); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); ospf_prune_unreachable_networks (new_table); ospf_prune_unreachable_routers (new_rtrs); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + prune_time = timeval_elapsed (stop_time, start_time); /* AS-external-LSA calculation should not be performed here. */ /* If new Router Route is installed, @@ -1273,9 +1372,13 @@ ospf_spf_calculate_timer (struct thread *thread) ospf_ase_calculate_timer_add (ospf); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); + /* Update routing table. */ ospf_route_install (ospf, new_table); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + rt_time = timeval_elapsed (stop_time, start_time); /* Update ABR/ASBR routing table */ if (ospf->old_rtrs) { @@ -1287,19 +1390,42 @@ ospf_spf_calculate_timer (struct thread *thread) ospf->old_rtrs = ospf->new_rtrs; ospf->new_rtrs = new_rtrs; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); if (IS_OSPF_ABR (ospf)) ospf_abr_task (ospf); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + abr_time = timeval_elapsed (stop_time, start_time); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + total_spf_time = timeval_elapsed (stop_time, spf_start_time); + ospf->ts_spf_duration.tv_sec = total_spf_time/1000000; + ospf->ts_spf_duration.tv_usec = total_spf_time % 1000000; + + ospf_get_spf_reason_str (rbuf); + if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("SPF: calculation complete"); + { + zlog_info ("SPF Processing Time(usecs): %ld", total_spf_time); + zlog_info ("\t SPF Time: %ld", spf_time); + zlog_info ("\t InterArea: %ld", ia_time); + zlog_info ("\t Prune: %ld", prune_time); + zlog_info ("\tRouteInstall: %ld", rt_time); + if (IS_OSPF_ABR (ospf)) + zlog_info ("\t ABR: %ld (%d areas)", + abr_time, areas_processed); + zlog_info ("Reason(s) for SPF: %s", rbuf); + } + ospf_clear_spf_reason_flags (); + return 0; } /* Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer for SPF calc. */ void -ospf_spf_calculate_schedule (struct ospf *ospf) +ospf_spf_calculate_schedule (struct ospf *ospf, ospf_spf_reason_t reason) { unsigned long delay, elapsed, ht; struct timeval result; @@ -1311,12 +1437,14 @@ ospf_spf_calculate_schedule (struct ospf *ospf) if (ospf == NULL) return; + ospf_spf_set_reason (reason); + /* SPF calculation timer is already scheduled. */ if (ospf->t_spf_calc) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: calculation timer is already scheduled: %p", - ospf->t_spf_calc); + (void *)ospf->t_spf_calc); return; } @@ -1354,6 +1482,8 @@ ospf_spf_calculate_schedule (struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: calculation timer delay = %ld", delay); + + zlog_info ("SPF: Scheduled in %ld msec", delay); ospf->t_spf_calc = thread_add_timer_msec (master, ospf_spf_calculate_timer, ospf, delay);