ospfd: clean some bitrot in SPF code

Just non-functional changes, cosmetics, removal of eye
cancer. The intention here is to make the SPF code more
approachable.

Signed-off-by: GalaxyGorilla <sascha@netdef.org>
This commit is contained in:
GalaxyGorilla 2020-08-05 14:16:57 +00:00
parent 806e504063
commit 5ec5929c62

View File

@ -68,14 +68,17 @@ static void ospf_spf_set_reason(ospf_spf_reason_t reason)
} }
static void ospf_vertex_free(void *); static void ospf_vertex_free(void *);
/* List of allocated vertices, to simplify cleanup of SPF. /*
* List of allocated vertices, to simplify cleanup of SPF.
* Not thread-safe obviously. If it ever needs to be, it'd have to be * Not thread-safe obviously. If it ever needs to be, it'd have to be
* dynamically allocated at begin of ospf_spf_calculate * dynamically allocated at begin of ospf_spf_calculate
*/ */
static struct list vertex_list = {.del = ospf_vertex_free}; static struct list vertex_list = {.del = ospf_vertex_free};
/* Heap related functions, for the managment of the candidates, to /*
* be used with pqueue. */ * Heap related functions, for the managment of the candidates, to
* be used with pqueue.
*/
static int vertex_cmp(const struct vertex *v1, const struct vertex *v2) static int vertex_cmp(const struct vertex *v1, const struct vertex *v2)
{ {
if (v1->distance != v2->distance) if (v1->distance != v2->distance)
@ -118,7 +121,8 @@ static void vertex_nexthop_free(struct vertex_nexthop *nh)
XFREE(MTYPE_OSPF_NEXTHOP, nh); XFREE(MTYPE_OSPF_NEXTHOP, nh);
} }
/* Free the canonical nexthop objects for an area, ie the nexthop objects /*
* Free the canonical nexthop objects for an area, ie the nexthop objects
* attached to the first-hop router vertices, and any intervening network * attached to the first-hop router vertices, and any intervening network
* vertices. * vertices.
*/ */
@ -131,7 +135,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root)
struct listnode *n2, *nn2; struct listnode *n2, *nn2;
struct vertex_parent *vp; struct vertex_parent *vp;
/* router vertices through an attached network each /*
* router vertices through an attached network each
* have a distinct (canonical / not inherited) nexthop * have a distinct (canonical / not inherited) nexthop
* which must be freed. * which must be freed.
* *
@ -150,7 +155,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root)
} }
} }
/* TODO: Parent list should be excised, in favour of maintaining only /*
* TODO: Parent list should be excised, in favour of maintaining only
* vertex_nexthop, with refcounts. * vertex_nexthop, with refcounts.
*/ */
static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink, static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink,
@ -163,6 +169,7 @@ static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink,
new->parent = v; new->parent = v;
new->backlink = backlink; new->backlink = backlink;
new->nexthop = hop; new->nexthop = hop;
return new; return new;
} }
@ -202,6 +209,7 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa)
new->type == OSPF_VERTEX_ROUTER ? "Router" new->type == OSPF_VERTEX_ROUTER ? "Router"
: "Network", : "Network",
inet_ntoa(new->lsa->id)); inet_ntoa(new->lsa->id));
return new; return new;
} }
@ -214,14 +222,6 @@ static void ospf_vertex_free(void *data)
v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network",
inet_ntoa(v->lsa->id)); inet_ntoa(v->lsa->id));
/* There should be no parents potentially holding references to this
* vertex
* Children however may still be there, but presumably referenced by
* other
* vertices
*/
// assert (listcount (v->parents) == 0);
if (v->children) if (v->children)
list_delete(&v->children); list_delete(&v->children);
@ -291,12 +291,12 @@ static void ospf_vertex_add_parent(struct vertex *v)
} }
} }
static void ospf_spf_init(struct ospf_area *area) static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa)
{ {
struct vertex *v; struct vertex *v;
/* Create root node. */ /* Create root node. */
v = ospf_vertex_new(area->router_lsa_self); v = ospf_vertex_new(root_lsa);
area->spf = v; area->spf = v;
@ -364,7 +364,8 @@ static int ospf_lsa_has_link(struct lsa_header *w, struct lsa_header *v)
return -1; return -1;
} }
/* Find the next link after prev_link from v to w. If prev_link is /*
* Find the next link after prev_link from v to w. If prev_link is
* NULL, return the first link from v to w. Ignore stub and virtual links; * NULL, return the first link from v to w. Ignore stub and virtual links;
* these link types will never be returned. * these link types will never be returned.
*/ */
@ -433,10 +434,11 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w,
assert(v && w && newhop); assert(v && w && newhop);
assert(distance); assert(distance);
/* IFF w has already been assigned a distance, then we shouldn't get /*
* here * IFF w has already been assigned a distance, then we shouldn't get
* unless callers have determined V(l)->W is shortest / equal-shortest * here unless callers have determined V(l)->W is shortest /
* path (0 is a special case distance (no distance yet assigned)). * equal-shortest path (0 is a special case distance (no distance yet
* assigned)).
*/ */
if (w->distance) if (w->distance)
assert(distance <= w->distance); assert(distance <= w->distance);
@ -452,7 +454,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w,
sizeof(buf[1]))); sizeof(buf[1])));
} }
/* Adding parent for a new, better path: flush existing parents from W. /*
* Adding parent for a new, better path: flush existing parents from W.
*/ */
if (distance < w->distance) { if (distance < w->distance) {
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
@ -463,7 +466,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w,
w->distance = distance; w->distance = distance;
} }
/* new parent is <= existing parents, add it to parent list (if nexthop /*
* new parent is <= existing parents, add it to parent list (if nexthop
* not on parent list) * not on parent list)
*/ */
for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) { for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) {
@ -482,7 +486,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w,
return; return;
} }
/* 16.1.1. Calculate nexthop from root through V (parent) to /*
* 16.1.1. Calculate nexthop from root through V (parent) to
* vertex W (destination), with given distance from root->W. * vertex W (destination), with given distance from root->W.
* *
* The link must be supplied if V is the root vertex. In all other cases * The link must be supplied if V is the root vertex. In all other cases
@ -514,16 +519,14 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
} }
if (v == area->spf) { if (v == area->spf) {
/* 16.1.1 para 4. In the first case, the parent vertex (V) is /*
the * 16.1.1 para 4. In the first case, the parent vertex (V) is
root (the calculating router itself). This means that the * the root (the calculating router itself). This means that
destination is either a directly connected network or * the destination is either a directly connected network or
directly * directly connected router. The outgoing interface in this
connected router. The outgoing interface in this case is * case is simply the OSPF interface connecting to the
simply * destination network/router.
the OSPF interface connecting to the destination */
network/router.
*/
/* we *must* be supplied with the link data */ /* we *must* be supplied with the link data */
assert(l != NULL); assert(l != NULL);
@ -548,58 +551,51 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
} }
if (w->type == OSPF_VERTEX_ROUTER) { if (w->type == OSPF_VERTEX_ROUTER) {
/* l is a link from v to w /*
* l2 will be link from w to v * l is a link from v to w l2 will be link from w to v
*/ */
struct router_lsa_link *l2 = NULL; struct router_lsa_link *l2 = NULL;
if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) {
struct in_addr nexthop = {.s_addr = 0}; struct in_addr nexthop = {.s_addr = 0};
/* If the destination is a router which connects /*
to * If the destination is a router which connects
the calculating router via a * to the calculating router via a
Point-to-MultiPoint * Point-to-MultiPoint network, the
network, the destination's next hop IP * destination's next hop IP address(es) can be
address(es) * determined by examining the destination's
can be determined by examining the * router-LSA: each link pointing back to the
destination's * calculating router and having a Link Data
router-LSA: each link pointing back to the * field belonging to the Point-to-MultiPoint
calculating router and having a Link Data * network provides an IP address of the next
field * hop router.
belonging to the Point-to-MultiPoint network *
provides an IP address of the next hop * At this point l is a link from V to W, and V
router. * is the root ("us"). If it is a point-to-
* multipoint interface, then look through the
At this point l is a link from V to W, and V * links in the opposite direction (W to V).
is the * If any of them have an address that lands
root ("us"). If it is a point-to-multipoint * within the subnet declared by the PtMP link,
interface, * then that link is a constituent of the PtMP
then look through the links in the opposite * link, and its address is a nexthop address
direction (W to V). * for 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
a nexthop address for V.
*/
if (oi->type == OSPF_IFTYPE_POINTOPOINT) { if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
/* Having nexthop = 0 is tempting, but /*
NOT acceptable. * Having nexthop = 0 (as proposed in
It breaks AS-External routes with a * the RFC) is tempting, but NOT
forwarding address, * acceptable. It breaks AS-External
since * routes with a forwarding address,
ospf_ase_complete_direct_routes() * since
will mistakenly * ospf_ase_complete_direct_routes()
assume we've reached the last hop and * will mistakenly assume we've reached
should place the * the last hop and should place the
forwarding address as nexthop. * forwarding address as nexthop. Also,
Also, users may configure * users may configure multi-access
multi-access links in p2p mode, * links in p2p mode, so we need the IP
so we need the IP to ARP the nexthop. * to ARP the nexthop.
*/ */
struct ospf_neighbor *nbr_w; struct ospf_neighbor *nbr_w;
nbr_w = ospf_nbr_lookup_by_routerid( nbr_w = ospf_nbr_lookup_by_routerid(
@ -615,8 +611,10 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
la.family = AF_INET; la.family = AF_INET;
la.prefixlen = oi->address->prefixlen; la.prefixlen = oi->address->prefixlen;
/* V links to W on PtMP interface /*
- find the interface address on W */ * V links to W on PtMP interface;
* find the interface address on W
*/
while ((l2 = ospf_get_next_link(w, v, while ((l2 = ospf_get_next_link(w, v,
l2))) { l2))) {
la.prefix = l2->link_data; la.prefix = l2->link_data;
@ -626,8 +624,11 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
oi->address) oi->address)
!= 0) != 0)
continue; continue;
/* link_data is on our PtMP
* network */ /*
* link_data is on our PtMP
* network
*/
added = 1; added = 1;
nexthop = l2->link_data; nexthop = l2->link_data;
break; break;
@ -635,8 +636,10 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
} }
if (added) { if (added) {
/* found all necessary info to build /*
* nexthop */ * found all necessary info to build
* nexthop
*/
nh = vertex_nexthop_new(); nh = vertex_nexthop_new();
nh->oi = oi; nh->oi = oi;
nh->router = nexthop; nh->router = nexthop;
@ -650,17 +653,15 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) {
struct ospf_vl_data *vl_data; struct ospf_vl_data *vl_data;
/* VLink implementation limitations: /*
* a) vl_data can only reference one nexthop, so * VLink implementation limitations:
* no ECMP * a) vl_data can only reference one nexthop,
* to backbone through VLinks. Though * so no ECMP to backbone through VLinks.
* transit-area * Though transit-area summaries may be
* summaries may be considered, and those can * considered, and those can be ECMP.
* be ECMP.
* b) We can only use /one/ VLink, even if * b) We can only use /one/ VLink, even if
* multiple ones * multiple ones exist this router through
* exist this router through multiple * multiple transit-areas.
* transit-areas.
*/ */
vl_data = ospf_vl_lookup(area->ospf, NULL, vl_data = ospf_vl_lookup(area->ospf, NULL,
l->link_id); l->link_id);
@ -693,29 +694,27 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
else if (v->type == OSPF_VERTEX_NETWORK) { else if (v->type == OSPF_VERTEX_NETWORK) {
/* See if any of V's parents are the root. */ /* See if any of V's parents are the root. */
for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) { for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) {
if (vp->parent == area->spf) /* connects to root? */ if (vp->parent == area->spf) {
{ /*
/* 16.1.1 para 5. ...the parent vertex is a * 16.1.1 para 5. ...the parent vertex is a
* network that * network that directly connects the
* directly connects the calculating router to * calculating router to the destination
* the destination * router. The list of next hops is then
* router. The list of next hops is then * determined by examining the destination's
* determined by * router-LSA ...
* examining the destination's router-LSA...
*/ */
assert(w->type == OSPF_VERTEX_ROUTER); assert(w->type == OSPF_VERTEX_ROUTER);
while ((l = ospf_get_next_link(w, v, l))) { while ((l = ospf_get_next_link(w, v, l))) {
/* ...For each link in the router-LSA /*
* that points back to the * ... For each link in the router-LSA
* parent network, the link's Link Data * that points back to the parent
* field provides the IP * network, the link's Link Data field
* address of a next hop router. The * provides the IP address of a next hop
* outgoing interface to * router. The outgoing interface to use
* use can then be derived from the next * can then be derived from the next
* hop IP address (or * hop IP address (or it can be
* it can be inherited from the parent * inherited from the parent network).
* network).
*/ */
nh = vertex_nexthop_new(); nh = vertex_nexthop_new();
nh->oi = vp->nexthop->oi; nh->oi = vp->nexthop->oi;
@ -723,52 +722,42 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
added = 1; added = 1;
ospf_spf_add_parent(v, w, nh, distance); ospf_spf_add_parent(v, w, nh, distance);
} }
/* Note lack of return is deliberate. See next /*
* comment. */ * Note lack of return is deliberate. See next
* comment.
*/
} }
} }
/* NB: This code is non-trivial. /*
* NB: This code is non-trivial.
* *
* E.g. it is not enough to know that V connects to the root. It * E.g. it is not enough to know that V connects to the root. It
* is * is also important that the while above, looping through all
* also important that the while above, looping through all * links from W->V found at least one link, so that we know
* links from * there is bi-directional connectivity between V and W (which
* W->V found at least one link, so that we know there is * need not be the case, e.g. when OSPF has not yet converged
* bi-directional connectivity between V and W (which need not * fully). Otherwise, if we /always/ return here, without having
* be the * checked that root->V->-W actually resulted in a valid nexthop
* case, e.g. when OSPF has not yet converged fully). * being created, then we we will prevent SPF from finding/using
* Otherwise, if * higher cost paths.
* 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.
* *
* It is important, if root->V->W has not been added, that we * It is important, if root->V->W has not been added, that we
* continue * continue through to the intervening-router nexthop code
* through to the intervening-router nexthop code below. So as * below. So as to ensure other paths to V may be used. This
* to * avoids unnecessary blackholes while OSPF is converging.
* ensure other paths to V may be used. This avoids unnecessary
* blackholes while OSPF is convergening.
* *
* I.e. we may have arrived at this function, examining V -> W, * I.e. we may have arrived at this function, examining V -> W,
* via * via workable paths other than root -> V, and it's important
* workable paths other than root -> V, and it's important to * to avoid getting "confused" by non-working root->V->W path
* avoid * - it's important to *not* lose the working non-root paths,
* getting "confused" by non-working root->V->W path - it's * just because of a non-viable root->V->W.
* 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) if (added)
return added; return added;
} }
/* 16.1.1 para 4. If there is at least one intervening router in the /*
* 16.1.1 para 4. If there is at least one intervening router in the
* current shortest path between the destination and the root, the * current shortest path between the destination and the root, the
* destination simply inherits the set of next hops from the * destination simply inherits the set of next hops from the
* parent. * parent.
@ -785,13 +774,13 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
return added; return added;
} }
/* RFC2328 Section 16.1 (2). /*
* v is on the SPF tree. Examine the links in v's LSA. Update the list * RFC2328 16.1 (2).
* of candidates with any vertices not already on the list. If a lower-cost * v is on the SPF tree. Examine the links in v's LSA. Update the list of
* path is found to a vertex already on the candidate list, store the new cost. * candidates with any vertices not already on the list. If a lower-cost path
* is found to a vertex already on the candidate list, store the new cost.
*/ */
static void ospf_spf_next(struct vertex *v, struct ospf *ospf, static void ospf_spf_next(struct vertex *v, struct ospf_area *area,
struct ospf_area *area,
struct vertex_pqueue_head *candidate) struct vertex_pqueue_head *candidate)
{ {
struct ospf_lsa *w_lsa = NULL; struct ospf_lsa *w_lsa = NULL;
@ -801,8 +790,10 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
struct in_addr *r; struct in_addr *r;
int type = 0, lsa_pos = -1, lsa_pos_next = 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. */ * 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.
*/
if (v->type == OSPF_VERTEX_ROUTER) { if (v->type == OSPF_VERTEX_ROUTER) {
if (IS_ROUTER_LSA_VIRTUAL((struct router_lsa *)v->lsa)) if (IS_ROUTER_LSA_VIRTUAL((struct router_lsa *)v->lsa))
area->transit = OSPF_TRANSIT_TRUE; area->transit = OSPF_TRANSIT_TRUE;
@ -829,37 +820,35 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
p += (OSPF_ROUTER_LSA_LINK_SIZE p += (OSPF_ROUTER_LSA_LINK_SIZE
+ (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE));
/* (a) If this is a link to a stub network, examine the /*
next * (a) If this is a link to a stub network, examine the
link in V's LSA. Links to stub networks will be * next link in V's LSA. Links to stub networks will
considered in the second stage of the shortest path * be considered in the second stage of the shortest
calculation. */ * path calculation.
*/
if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB) if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB)
continue; continue;
/* (b) Otherwise, W is a transit vertex (router or /*
transit * (b) Otherwise, W is a transit vertex (router or
network). Look up the vertex W's LSA (router-LSA or * transit network). Look up the vertex W's LSA
network-LSA) in Area A's link state database. */ * (router-LSA or network-LSA) in Area A's link state
* database.
*/
switch (type) { switch (type) {
case LSA_LINK_TYPE_POINTOPOINT: case LSA_LINK_TYPE_POINTOPOINT:
case LSA_LINK_TYPE_VIRTUALLINK: case LSA_LINK_TYPE_VIRTUALLINK:
if (type == LSA_LINK_TYPE_VIRTUALLINK) { if (type == LSA_LINK_TYPE_VIRTUALLINK
if (IS_DEBUG_OSPF_EVENT) && IS_DEBUG_OSPF_EVENT)
zlog_debug( zlog_debug(
"looking up LSA through VL: %s", "looking up LSA through VL: %s",
inet_ntoa(l->link_id)); inet_ntoa(l->link_id));
} w_lsa = ospf_lsa_lookup(area->ospf, area,
w_lsa = ospf_lsa_lookup(ospf, area,
OSPF_ROUTER_LSA, OSPF_ROUTER_LSA,
l->link_id, l->link_id); l->link_id, l->link_id);
if (w_lsa) { if (w_lsa && IS_DEBUG_OSPF_EVENT)
if (IS_DEBUG_OSPF_EVENT) zlog_debug("found Router LSA %s",
zlog_debug( inet_ntoa(l->link_id));
"found Router LSA %s",
inet_ntoa(l->link_id));
}
break; break;
case LSA_LINK_TYPE_TRANSIT: case LSA_LINK_TYPE_TRANSIT:
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
@ -868,9 +857,8 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
inet_ntoa(l->link_id)); inet_ntoa(l->link_id));
w_lsa = ospf_lsa_lookup_by_id( w_lsa = ospf_lsa_lookup_by_id(
area, OSPF_NETWORK_LSA, l->link_id); area, OSPF_NETWORK_LSA, l->link_id);
if (w_lsa) if (w_lsa && IS_DEBUG_OSPF_EVENT)
if (IS_DEBUG_OSPF_EVENT) zlog_debug("found the LSA");
zlog_debug("found the LSA");
break; break;
default: default:
flog_warn(EC_OSPF_LSA, flog_warn(EC_OSPF_LSA,
@ -888,19 +876,19 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
/* Lookup the vertex W's LSA. */ /* Lookup the vertex W's LSA. */
w_lsa = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, w_lsa = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
*r); *r);
if (w_lsa) { if (w_lsa && IS_DEBUG_OSPF_EVENT)
if (IS_DEBUG_OSPF_EVENT) zlog_debug("found Router LSA %s",
zlog_debug("found Router LSA %s", inet_ntoa(w_lsa->data->id));
inet_ntoa(w_lsa->data->id));
}
/* step (d) below */ /* step (d) below */
distance = v->distance; distance = v->distance;
} }
/* (b cont.) If the LSA does not exist, or its LS age is equal /*
to MaxAge, or it does not have a link back to vertex V, * (b cont.) If the LSA does not exist, or its LS age is equal
examine the next link in V's LSA.[23] */ * to MaxAge, or it does not have a link back to vertex V,
* examine the next link in V's LSA.[23]
*/
if (w_lsa == NULL) { if (w_lsa == NULL) {
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug("No LSA found"); zlog_debug("No LSA found");
@ -919,19 +907,23 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
continue; continue;
} }
/* (c) If vertex W is already on the shortest-path tree, examine /*
the next link in the LSA. */ * (c) If vertex W is already on the shortest-path tree, examine
* the next link in the LSA.
*/
if (w_lsa->stat == LSA_SPF_IN_SPFTREE) { if (w_lsa->stat == LSA_SPF_IN_SPFTREE) {
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug("The LSA is already in SPF"); zlog_debug("The LSA is already in SPF");
continue; continue;
} }
/* (d) Calculate the link state cost D of the resulting path /*
from the root to vertex W. D is equal to the sum of the link * (d) Calculate the link state cost D of the resulting path
state cost of the (already calculated) shortest path to * from the root to vertex W. D is equal to the sum of the link
vertex V and the advertised cost of the link between vertices * state cost of the (already calculated) shortest path to
V and W. If D is: */ * vertex V and the advertised cost of the link between vertices
* V and W. If D is:
*/
/* calculate link cost D -- moved above */ /* calculate link cost D -- moved above */
@ -948,25 +940,24 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf,
zlog_debug("Nexthop Calc failed"); zlog_debug("Nexthop Calc failed");
} else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) { } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) {
w = w_lsa->stat; w = w_lsa->stat;
/* if D is greater than. */
if (w->distance < distance) { if (w->distance < distance) {
continue; continue;
} }
/* equal to. */
else if (w->distance == distance) { else if (w->distance == distance) {
/* Found an equal-cost path to W. /*
* Calculate nexthop of to W from V. */ * Found an equal-cost path to W.
* Calculate nexthop of to W from V.
*/
ospf_nexthop_calculation(area, v, w, l, ospf_nexthop_calculation(area, v, w, l,
distance, lsa_pos); distance, lsa_pos);
} }
/* less than. */
else { else {
/* Found a lower-cost path to W. /*
* Found a lower-cost path to W.
* nexthop_calculation is conditional, if it * nexthop_calculation is conditional, if it
* finds * finds valid nexthop it will call
* valid nexthop it will call spf_add_parents, * spf_add_parents, which will flush the old
* which * parents.
* will flush the old parents
*/ */
vertex_pqueue_del(candidate, w); vertex_pqueue_del(candidate, w);
ospf_nexthop_calculation(area, v, w, l, ospf_nexthop_calculation(area, v, w, l,
@ -1020,24 +1011,26 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v,
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug("ospf_process_stub():processing stubs for area %s", zlog_debug("ospf_process_stub():processing stubs for area %s",
inet_ntoa(area->area_id)); inet_ntoa(area->area_id));
if (v->type == OSPF_VERTEX_ROUTER) { if (v->type == OSPF_VERTEX_ROUTER) {
uint8_t *p; uint8_t *p;
uint8_t *lim; uint8_t *lim;
struct router_lsa_link *l; struct router_lsa_link *l;
struct router_lsa *rlsa; struct router_lsa *router_lsa;
int lsa_pos = 0; int lsa_pos = 0;
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug( zlog_debug(
"ospf_process_stubs():processing router LSA, id: %s", "ospf_process_stubs():processing router LSA, id: %s",
inet_ntoa(v->lsa->id)); inet_ntoa(v->lsa->id));
rlsa = (struct router_lsa *)v->lsa;
router_lsa = (struct router_lsa *)v->lsa;
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug( zlog_debug(
"ospf_process_stubs(): we have %d links to process", "ospf_process_stubs(): we have %d links to process",
ntohs(rlsa->links)); ntohs(router_lsa->links));
p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4; p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4;
lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length); lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length);
@ -1061,7 +1054,8 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v,
if (CHECK_FLAG(child->flags, OSPF_VERTEX_PROCESSED)) if (CHECK_FLAG(child->flags, OSPF_VERTEX_PROCESSED))
continue; continue;
/* the first level of routers connected to the root /*
* The first level of routers connected to the root
* should have 'parent_is_root' set, including those * should have 'parent_is_root' set, including those
* connected via a network vertex. * connected via a network vertex.
*/ */
@ -1097,6 +1091,7 @@ void ospf_rtrs_free(struct route_table *rtrs)
rn->info = NULL; rn->info = NULL;
route_unlock_node(rn); route_unlock_node(rn);
} }
route_table_finish(rtrs); route_table_finish(rtrs);
} }
@ -1162,8 +1157,9 @@ ospf_rtrs_print (struct route_table *rtrs)
} }
#endif #endif
/* Calculating the shortest-path tree for an area. */ /* Calculating the shortest-path tree for an area, see RFC2328 16.1. */
static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, static void ospf_spf_calculate(struct ospf_area *area,
struct ospf_lsa *root_lsa,
struct route_table *new_table, struct route_table *new_table,
struct route_table *new_rtrs) struct route_table *new_rtrs)
{ {
@ -1176,55 +1172,57 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
inet_ntoa(area->area_id)); inet_ntoa(area->area_id));
} }
/* Check router-lsa-self. If self-router-lsa is not yet allocated, /*
return this area's calculation. */ * If the router LSA of the root is not yet allocated, return this
if (!area->router_lsa_self) { * area's calculation. In the 'usual' case the root_lsa is the
* self-originated router LSA of the node itself.
*/
if (!root_lsa) {
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug( zlog_debug(
"ospf_spf_calculate: Skip area %s's calculation due to empty router_lsa_self", "ospf_spf_calculate: Skip area %s's calculation due to empty root LSA",
inet_ntoa(area->area_id)); inet_ntoa(area->area_id));
return; return;
} }
/* RFC2328 16.1. (1). */ /* Initialize the algorithm's data structures, see RFC2328 16.1. (1). */
/* Initialize the algorithm's data structures. */
/* This function scans all the LSA database and set the stat field to /*
* LSA_SPF_NOT_EXPLORED. */ * This function scans all the LSA database and set the stat field to
* LSA_SPF_NOT_EXPLORED.
*/
lsdb_clean_stat(area->lsdb); lsdb_clean_stat(area->lsdb);
/* Create a new heap for the candidates. */ /* Create a new heap for the candidates. */
vertex_pqueue_init(&candidate); vertex_pqueue_init(&candidate);
/* Initialize the shortest-path tree to only the root (which is the /*
router doing the calculation). */ * Initialize the shortest-path tree to only the root (which is usually
ospf_spf_init(area); * the router doing the calculation).
v = area->spf; */
/* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of ospf_spf_init(area, root_lsa);
* the
* spanning tree. */
v->lsa_p->stat = LSA_SPF_IN_SPFTREE;
/* Set Area A's TransitCapability to false. */ /* Set Area A's TransitCapability to false. */
area->transit = OSPF_TRANSIT_FALSE; area->transit = OSPF_TRANSIT_FALSE;
area->shortcut_capability = 1; area->shortcut_capability = 1;
/*
* Use the root vertex for the start of the SPF algorithm and make it
* part of the tree.
*/
v = area->spf;
v->lsa_p->stat = LSA_SPF_IN_SPFTREE;
for (;;) { for (;;) {
/* RFC2328 16.1. (2). */ /* RFC2328 16.1. (2). */
ospf_spf_next(v, ospf, area, &candidate); ospf_spf_next(v, area, &candidate);
/* RFC2328 16.1. (3). */ /* RFC2328 16.1. (3). */
/* If at this step the candidate list is empty, the shortest-
path tree (of transit vertices) has been completely built and
this stage of the procedure terminates. */
/* Otherwise, choose the vertex belonging to the candidate list
that is closest to the root, and add it to the shortest-path
tree (removing it from the candidate list in the
process). */
/* Extract from the candidates the node with the lower key. */
v = vertex_pqueue_pop(&candidate); v = vertex_pqueue_pop(&candidate);
if (!v) if (!v)
/* No more vertices left. */
break; break;
/* Update stat field in vertex. */
v->lsa_p->stat = LSA_SPF_IN_SPFTREE; v->lsa_p->stat = LSA_SPF_IN_SPFTREE;
ospf_vertex_add_parent(v); ospf_vertex_add_parent(v);
@ -1235,24 +1233,23 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
else else
ospf_intra_add_transit(new_table, v, area); ospf_intra_add_transit(new_table, v, area);
/* RFC2328 16.1. (5). */ /* Iterate back to (2), see RFC2328 16.1. (5). */
/* Iterate the algorithm by returning to Step 2. */ }
} /* end loop until no more candidate vertices */
if (IS_DEBUG_OSPF_EVENT) { if (IS_DEBUG_OSPF_EVENT) {
ospf_spf_dump(area->spf, 0); ospf_spf_dump(area->spf, 0);
ospf_route_table_dump(new_table); ospf_route_table_dump(new_table);
} }
/* Second stage of SPF calculation procedure's */ /*
* Second stage of SPF calculation procedure's, add leaves to the tree
* for stub networks.
*/
ospf_spf_process_stubs(area, area->spf, new_table, 0); ospf_spf_process_stubs(area, area->spf, new_table, 0);
/* Free candidate queue. */
//vertex_pqueue_fini(&candidate);
ospf_vertex_dump(__func__, area->spf, 0, 1); ospf_vertex_dump(__func__, area->spf, 0, 1);
/* Free nexthop information, canonical versions of which are attached /*
* Free nexthop information, canonical versions of which are attached
* the first level of router vertices attached to the root vertex, see * the first level of router vertices attached to the root vertex, see
* ospf_nexthop_calculation. * ospf_nexthop_calculation.
*/ */
@ -1268,21 +1265,51 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
zlog_debug("ospf_spf_calculate: Stop. %zd vertices", zlog_debug("ospf_spf_calculate: Stop. %zd vertices",
mtype_stats_alloc(MTYPE_OSPF_VERTEX)); mtype_stats_alloc(MTYPE_OSPF_VERTEX));
/* Free SPF vertices, but not the list. List has ospf_vertex_free /*
* Free SPF vertices, but not the list. List has ospf_vertex_free
* as deconstructor. * as deconstructor.
*/ */
list_delete_all_node(&vertex_list); list_delete_all_node(&vertex_list);
} }
/* Timer for SPF calculation. */ static int ospf_spf_calculate_areas(struct ospf *ospf,
static int ospf_spf_calculate_timer(struct thread *thread) struct route_table *new_table,
struct route_table *new_rtrs)
{
struct ospf_area *area;
struct listnode *node, *nnode;
int areas_processed = 0;
/* Calculate SPF for each area. */
for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
/* Do backbone last, so as to first discover intra-area paths
* for any back-bone virtual-links */
if (ospf->backbone && ospf->backbone == area)
continue;
ospf_spf_calculate(area, area->router_lsa_self, new_table,
new_rtrs);
areas_processed++;
}
/* SPF for backbone, if required */
if (ospf->backbone) {
area = ospf->backbone;
ospf_spf_calculate(area, area->router_lsa_self, new_table,
new_rtrs);
areas_processed++;
}
return areas_processed;
}
/* Worker for SPF calculation scheduler. */
static int ospf_spf_calculate_schedule_worker(struct thread *thread)
{ {
struct ospf *ospf = THREAD_ARG(thread); struct ospf *ospf = THREAD_ARG(thread);
struct route_table *new_table, *new_rtrs; struct route_table *new_table, *new_rtrs;
struct ospf_area *area;
struct listnode *node, *nnode;
struct timeval start_time, spf_start_time; struct timeval start_time, spf_start_time;
int areas_processed = 0; int areas_processed;
unsigned long ia_time, prune_time, rt_time; unsigned long ia_time, prune_time, rt_time;
unsigned long abr_time, total_spf_time, spf_time; unsigned long abr_time, total_spf_time, spf_time;
char rbuf[32]; /* reason_buf */ char rbuf[32]; /* reason_buf */
@ -1292,51 +1319,36 @@ static int ospf_spf_calculate_timer(struct thread *thread)
ospf->t_spf_calc = NULL; ospf->t_spf_calc = NULL;
monotime(&spf_start_time);
/* Allocate new table tree. */
new_table = route_table_init();
new_rtrs = route_table_init();
ospf_vl_unapprove(ospf); ospf_vl_unapprove(ospf);
/* Calculate SPF for each area. */ /* Execute SPF for each area including backbone, see RFC 2328 16.1. */
for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { monotime(&spf_start_time);
/* Do backbone last, so as to first discover intra-area paths new_table = route_table_init(); /* routing table */
* for any back-bone virtual-links new_rtrs = route_table_init(); /* ABR/ASBR routing table */
*/ areas_processed = ospf_spf_calculate_areas(ospf, new_table, new_rtrs);
if (ospf->backbone && ospf->backbone == area)
continue;
ospf_spf_calculate(ospf, area, new_table, new_rtrs);
areas_processed++;
}
/* SPF for backbone, if required */
if (ospf->backbone) {
ospf_spf_calculate(ospf, ospf->backbone, new_table, new_rtrs);
areas_processed++;
}
spf_time = monotime_since(&spf_start_time, NULL); spf_time = monotime_since(&spf_start_time, NULL);
ospf_vl_shut_unapproved(ospf); ospf_vl_shut_unapproved(ospf);
/* Calculate inter-area routes, see RFC 2328 16.2. */
monotime(&start_time); monotime(&start_time);
ospf_ia_routing(ospf, new_table, new_rtrs); ospf_ia_routing(ospf, new_table, new_rtrs);
ia_time = monotime_since(&start_time, NULL); ia_time = monotime_since(&start_time, NULL);
/* Get rid of transit networks and routers we cannot reach anyway. */
monotime(&start_time); monotime(&start_time);
ospf_prune_unreachable_networks(new_table); ospf_prune_unreachable_networks(new_table);
ospf_prune_unreachable_routers(new_rtrs); ospf_prune_unreachable_routers(new_rtrs);
prune_time = monotime_since(&start_time, NULL); prune_time = monotime_since(&start_time, NULL);
/* AS-external-LSA calculation should not be performed here. */ /* Note: RFC 2328 16.3. is apparently missing. */
/* If new Router Route is installed,
then schedule re-calculate External routes. */
if (1)
ospf_ase_calculate_schedule(ospf);
/*
* Calculate AS external routes, see RFC 2328 16.4.
* There is a dedicated routing table for external routes which is not
* handled here directly
*/
ospf_ase_calculate_schedule(ospf);
ospf_ase_calculate_timer_add(ospf); ospf_ase_calculate_timer_add(ospf);
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
@ -1344,22 +1356,22 @@ static int ospf_spf_calculate_timer(struct thread *thread)
"%s: ospf install new route, vrf %s id %u new_table count %lu", "%s: ospf install new route, vrf %s id %u new_table count %lu",
__func__, ospf_vrf_id_to_name(ospf->vrf_id), __func__, ospf_vrf_id_to_name(ospf->vrf_id),
ospf->vrf_id, new_table->count); ospf->vrf_id, new_table->count);
/* Update routing table. */ /* Update routing table. */
monotime(&start_time); monotime(&start_time);
ospf_route_install(ospf, new_table); ospf_route_install(ospf, new_table);
rt_time = monotime_since(&start_time, NULL); rt_time = monotime_since(&start_time, NULL);
/* Update ABR/ASBR routing table */ /* Free old ABR/ASBR routing table */
if (ospf->old_rtrs) { if (ospf->old_rtrs)
/* old_rtrs's node holds linked list of ospf_route. --kunihiro.
*/
/* ospf_route_delete (ospf->old_rtrs); */ /* ospf_route_delete (ospf->old_rtrs); */
ospf_rtrs_free(ospf->old_rtrs); ospf_rtrs_free(ospf->old_rtrs);
}
/* Update ABR/ASBR routing table */
ospf->old_rtrs = ospf->new_rtrs; ospf->old_rtrs = ospf->new_rtrs;
ospf->new_rtrs = new_rtrs; ospf->new_rtrs = new_rtrs;
/* ABRs may require additional changes, see RFC 2328 16.7. */
monotime(&start_time); monotime(&start_time);
if (IS_OSPF_ABR(ospf)) if (IS_OSPF_ABR(ospf))
ospf_abr_task(ospf); ospf_abr_task(ospf);
@ -1413,8 +1425,10 @@ static int ospf_spf_calculate_timer(struct thread *thread)
return 0; return 0;
} }
/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we /*
set timer for SPF calc. */ * 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_reason_t reason) void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason)
{ {
unsigned long delay, elapsed, ht; unsigned long delay, elapsed, ht;
@ -1446,9 +1460,10 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason)
/* Get SPF calculation delay time. */ /* Get SPF calculation delay time. */
if (elapsed < ht) { if (elapsed < ht) {
/* Got an event within the hold time of last SPF. We need to /*
* Got an event within the hold time of last SPF. We need to
* increase the hold_multiplier, if it's not already at/past * increase the hold_multiplier, if it's not already at/past
* maximum value, and wasn't already increased.. * maximum value, and wasn't already increased.
*/ */
if (ht < ospf->spf_max_holdtime) if (ht < ospf->spf_max_holdtime)
ospf->spf_hold_multiplier++; ospf->spf_hold_multiplier++;
@ -1468,6 +1483,6 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason)
zlog_debug("SPF: calculation timer delay = %ld msec", delay); zlog_debug("SPF: calculation timer delay = %ld msec", delay);
ospf->t_spf_calc = NULL; ospf->t_spf_calc = NULL;
thread_add_timer_msec(master, ospf_spf_calculate_timer, ospf, delay, thread_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf,
&ospf->t_spf_calc); delay, &ospf->t_spf_calc);
} }