diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 7977a2a9f4..af801da8d7 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -995,7 +995,8 @@ void ospf_vl_delete(struct ospf *ospf, struct ospf_vl_data *vl_data) ospf_vl_data_free(vl_data); } -static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) +static int ospf_vl_set_params(struct ospf_area *area, + struct ospf_vl_data *vl_data, struct vertex *v) { int changed = 0; struct ospf_interface *voi; @@ -1003,6 +1004,7 @@ static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) struct vertex_parent *vp = NULL; unsigned int i; struct router_lsa *rl; + struct ospf_interface *oi; voi = vl_data->vl_oi; @@ -1013,17 +1015,24 @@ static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) } for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { - vl_data->nexthop.oi = vp->nexthop->oi; + vl_data->nexthop.lsa_pos = vp->nexthop->lsa_pos; vl_data->nexthop.router = vp->nexthop->router; - if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, - &vl_data->nexthop.oi->address->u.prefix4)) - changed = 1; + /* + * Only deal with interface data when the local + * (calculating) node is the SPF root node + */ + if (!area->spf_dry_run) { + oi = ospf_if_lookup_by_lsa_pos( + area, vl_data->nexthop.lsa_pos); - voi->address->u.prefix4 = - vl_data->nexthop.oi->address->u.prefix4; - voi->address->prefixlen = - vl_data->nexthop.oi->address->prefixlen; + if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, + &oi->address->u.prefix4)) + changed = 1; + + voi->address->u.prefix4 = oi->address->u.prefix4; + voi->address->prefixlen = oi->address->prefixlen; + } break; /* We take the first interface. */ } @@ -1114,7 +1123,7 @@ void ospf_vl_up_check(struct ospf_area *area, struct in_addr rid, OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); } - if (ospf_vl_set_params(vl_data, v)) { + if (ospf_vl_set_params(area, vl_data, v)) { if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) zlog_debug( "ospf_vl_up_check: VL cost change, scheduling router lsa refresh"); diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 776f50b33a..487275beec 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -375,7 +375,7 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, else route_unlock_node(rn); - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); listnode_add(rn->info, or); @@ -438,7 +438,7 @@ void ospf_intra_add_transit(struct route_table *rt, struct vertex *v, or->type = OSPF_DESTINATION_NETWORK; or->u.std.origin = (struct lsa_header *)lsa; - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); rn->info = or ; } @@ -453,7 +453,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, struct ospf_route * or ; struct prefix_ipv4 p; struct router_lsa *lsa; - struct ospf_interface *oi; + struct ospf_interface *oi = NULL; struct ospf_path *path; if (IS_DEBUG_OSPF_EVENT) @@ -538,7 +538,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, zlog_debug( "ospf_intra_add_stub(): routes are equal, merge"); - ospf_route_copy_nexthops_from_vertex(cur_or, v); + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); if (IPV4_ADDR_CMP(&cur_or->u.std.origin->id, &lsa->header.id) @@ -563,7 +563,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, list_delete_all_node(cur_or->paths); - ospf_route_copy_nexthops_from_vertex(cur_or, v); + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); cur_or->u.std.origin = (struct lsa_header *)lsa; return; @@ -588,24 +588,35 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_intra_add_stub(): this network is on remote router"); - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); } else { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_intra_add_stub(): this network is on this router"); - if ((oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos))) { + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); + + if (oi || area->spf_dry_run) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_intra_add_stub(): the interface is %s", - IF_NAME(oi)); + "ospf_intra_add_stub(): the lsa pos is %d", + lsa_pos); path = ospf_path_new(); path->nexthop.s_addr = INADDR_ANY; - path->ifindex = oi->ifp->ifindex; - if (CHECK_FLAG(oi->connected->flags, - ZEBRA_IFA_UNNUMBERED)) - path->unnumbered = 1; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, + ZEBRA_IFA_UNNUMBERED)) + path->unnumbered = 1; + } + listnode_add(or->paths, path); } else { if (IS_DEBUG_OSPF_EVENT) @@ -734,30 +745,41 @@ static int ospf_path_exist(struct list *plist, struct in_addr nexthop, return 0; } -void ospf_route_copy_nexthops_from_vertex(struct ospf_route *to, +void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *to, struct vertex *v) { struct listnode *node; struct ospf_path *path; struct vertex_nexthop *nexthop; struct vertex_parent *vp; + struct ospf_interface *oi = NULL; assert(to->paths); for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { nexthop = vp->nexthop; - if (nexthop->oi != NULL) { - if (!ospf_path_exist(to->paths, nexthop->router, - nexthop->oi)) { - path = ospf_path_new(); - path->nexthop = nexthop->router; - path->ifindex = nexthop->oi->ifp->ifindex; - if (CHECK_FLAG(nexthop->oi->connected->flags, + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, nexthop->lsa_pos); + + if ((oi && !ospf_path_exist(to->paths, nexthop->router, oi)) + || area->spf_dry_run) { + path = ospf_path_new(); + path->nexthop = nexthop->router; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) path->unnumbered = 1; - listnode_add(to->paths, path); } + + listnode_add(to->paths, path); } } } diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 20cdc75fe8..a0aa7a0c6f 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -146,7 +146,8 @@ extern void ospf_intra_add_stub(struct route_table *, struct router_lsa_link *, extern int ospf_route_cmp(struct ospf *, struct ospf_route *, struct ospf_route *); extern void ospf_route_copy_nexthops(struct ospf_route *, struct list *); -extern void ospf_route_copy_nexthops_from_vertex(struct ospf_route *, +extern void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *, struct vertex *); extern void ospf_route_subst(struct route_node *, struct ospf_route *, diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 7e293d7edc..dfeb80bd17 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -252,14 +252,12 @@ static void ospf_vertex_dump(const char *msg, struct vertex *v, if (vp) { zlog_debug( - "parent %s backlink %d nexthop %s interface %s", + "parent %s backlink %d nexthop %s lsa pos %d", inet_ntoa(vp->parent->lsa->id), vp->backlink, inet_ntop(AF_INET, &vp->nexthop->router, buf1, BUFSIZ), - vp->nexthop->oi - ? IF_NAME(vp->nexthop->oi) - : "NULL"); + vp->nexthop->lsa_pos); } } } @@ -291,7 +289,8 @@ static void ospf_vertex_add_parent(struct vertex *v) } } -static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa) +static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa, + bool is_dry_run) { struct vertex *v; @@ -299,6 +298,7 @@ static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa) v = ospf_vertex_new(root_lsa); area->spf = v; + area->spf_dry_run = is_dry_run; /* Reset ABR and ASBR router counts. */ area->abr_count = 0; @@ -486,6 +486,41 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, return; } +static int match_stub_prefix(struct lsa_header *lsa, struct in_addr v_link_addr, + struct in_addr w_link_addr) +{ + uint8_t *p, *lim; + struct router_lsa_link *l = NULL; + struct in_addr masked_lsa_addr; + + if (lsa->type != OSPF_ROUTER_LSA) + return 0; + + p = ((uint8_t *)lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)lsa) + ntohs(lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + if (l->m[0].type != LSA_LINK_TYPE_STUB) + continue; + + masked_lsa_addr.s_addr = + (l->link_id.s_addr & l->link_data.s_addr); + + /* check that both links belong to the same stub subnet */ + if ((masked_lsa_addr.s_addr + == (v_link_addr.s_addr & l->link_data.s_addr)) + && (masked_lsa_addr.s_addr + == (w_link_addr.s_addr & l->link_data.s_addr))) + return 1; + } + + return 0; +} + /* * 16.1.1. Calculate nexthop from root through V (parent) to * vertex W (destination), with given distance from root->W. @@ -506,7 +541,6 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, 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]; @@ -530,21 +564,11 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, /* 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, + "%s: considering link type:%d link_id:%s link_data:%s", + __func__, l->m[0].type, inet_ntop(AF_INET, &l->link_id, buf1, BUFSIZ), inet_ntop(AF_INET, &l->link_data, buf2, BUFSIZ)); @@ -557,8 +581,6 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, struct router_lsa_link *l2 = NULL; 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 @@ -580,55 +602,30 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, * then that link is a constituent of the PtMP * link, and its address is a nexthop address * for V. + * + * Note for point-to-point interfaces: + * + * Having nexthop = 0 (as proposed in the RFC) + * 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. + * + * Due to these reasons p2p and p2mp + * interfaces are handled the same way here, + * in the style of p2mp as described above. */ - if (oi->type == OSPF_IFTYPE_POINTOPOINT) { - /* - * Having nexthop = 0 (as proposed in - * the RFC) 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; - 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; + struct in_addr nexthop = {.s_addr = 0}; - la.family = AF_INET; - la.prefixlen = oi->address->prefixlen; - - /* - * 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 (prefix_cmp((struct prefix - *)&la, - oi->address) - != 0) - continue; - - /* - * link_data is on our PtMP - * network - */ + while ((l2 = ospf_get_next_link(w, v, l2))) { + if (match_stub_prefix(v->lsa, + l->link_data, + l2->link_data)) { added = 1; nexthop = l2->link_data; break; @@ -636,23 +633,17 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, } if (added) { - /* - * found all necessary info to build - * nexthop - */ nh = vertex_nexthop_new(); - nh->oi = oi; nh->router = nexthop; + nh->lsa_pos = lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } else zlog_info( - "%s: could not determine nexthop for link %s", - __func__, oi->ifp->name); + "%s: could not determine nexthop for link", + __func__); } /* end point-to-point link from V to W */ else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { - struct ospf_vl_data *vl_data; - /* * VLink implementation limitations: * a) vl_data can only reference one nexthop, @@ -663,6 +654,9 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, * multiple ones exist this router through * multiple transit-areas. */ + + struct ospf_vl_data *vl_data; + vl_data = ospf_vl_lookup(area->ospf, NULL, l->link_id); @@ -670,8 +664,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, && CHECK_FLAG(vl_data->flags, OSPF_VL_FLAG_APPROVED)) { nh = vertex_nexthop_new(); - nh->oi = vl_data->nexthop.oi; nh->router = vl_data->nexthop.router; + nh->lsa_pos = vl_data->nexthop.lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } else @@ -684,8 +678,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, assert(w->type == OSPF_VERTEX_NETWORK); nh = vertex_nexthop_new(); - nh->oi = oi; nh->router.s_addr = 0; /* Nexthop not required */ + nh->lsa_pos = lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } @@ -717,8 +711,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, * inherited from the parent network). */ nh = vertex_nexthop_new(); - nh->oi = vp->nexthop->oi; nh->router = l->link_data; + nh->lsa_pos = vp->nexthop->lsa_pos; added = 1; ospf_spf_add_parent(v, w, nh, distance); } @@ -817,6 +811,7 @@ static void ospf_spf_next(struct vertex *v, struct ospf_area *area, 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)); @@ -961,7 +956,7 @@ static void ospf_spf_next(struct vertex *v, struct ospf_area *area, */ vertex_pqueue_del(candidate, w); ospf_nexthop_calculation(area, v, w, l, - distance, lsa_pos); + distance, lsa_pos); vertex_pqueue_add(candidate, w); } } /* end W is already on the candidate list */ @@ -988,11 +983,9 @@ static void ospf_spf_dump(struct vertex *v, int i) if (IS_DEBUG_OSPF_EVENT) for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { - zlog_debug(" nexthop %p %s %s", (void *)parent->nexthop, + zlog_debug(" nexthop %p %s %d", (void *)parent->nexthop, inet_ntoa(parent->nexthop->router), - parent->nexthop->oi - ? IF_NAME(parent->nexthop->oi) - : "NULL"); + parent->nexthop->lsa_pos); } i++; @@ -1158,10 +1151,9 @@ ospf_rtrs_print (struct route_table *rtrs) #endif /* Calculating the shortest-path tree for an area, see RFC2328 16.1. */ -static void ospf_spf_calculate(struct ospf_area *area, - struct ospf_lsa *root_lsa, - struct route_table *new_table, - struct route_table *new_rtrs) +void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run) { struct vertex_pqueue_head candidate; struct vertex *v; @@ -1200,7 +1192,7 @@ static void ospf_spf_calculate(struct ospf_area *area, * Initialize the shortest-path tree to only the root (which is usually * the router doing the calculation). */ - ospf_spf_init(area, root_lsa); + ospf_spf_init(area, root_lsa, is_dry_run); /* Set Area A's TransitCapability to false. */ area->transit = OSPF_TRANSIT_FALSE; @@ -1248,12 +1240,6 @@ static void ospf_spf_calculate(struct ospf_area *area, ospf_spf_process_stubs(area, area->spf, new_table, 0); 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); /* Increment SPF Calculation Counter. */ area->spf_calculation++; @@ -1265,16 +1251,25 @@ static void ospf_spf_calculate(struct ospf_area *area, zlog_debug("ospf_spf_calculate: Stop. %zd 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); + /* If this is a dry run then keep the SPF data in place */ + if (!area->spf_dry_run) { + /* + * 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); + } } -static int ospf_spf_calculate_areas(struct ospf *ospf, - struct route_table *new_table, - struct route_table *new_rtrs) +int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run) { struct ospf_area *area; struct listnode *node, *nnode; @@ -1288,7 +1283,7 @@ static int ospf_spf_calculate_areas(struct ospf *ospf, continue; ospf_spf_calculate(area, area->router_lsa_self, new_table, - new_rtrs); + new_rtrs, is_dry_run); areas_processed++; } @@ -1296,7 +1291,7 @@ static int ospf_spf_calculate_areas(struct ospf *ospf, if (ospf->backbone) { area = ospf->backbone; ospf_spf_calculate(area, area->router_lsa_self, new_table, - new_rtrs); + new_rtrs, is_dry_run); areas_processed++; } @@ -1325,7 +1320,8 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread) monotime(&spf_start_time); new_table = route_table_init(); /* routing table */ new_rtrs = route_table_init(); /* ABR/ASBR routing table */ - areas_processed = ospf_spf_calculate_areas(ospf, new_table, new_rtrs); + areas_processed = + ospf_spf_calculate_areas(ospf, new_table, new_rtrs, false); spf_time = monotime_since(&spf_start_time, NULL); ospf_vl_shut_unapproved(ospf); diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 09a0b6f1b7..9bf3dc645e 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -49,15 +49,14 @@ struct vertex { /* A nexthop taken on the root node to get to this (parent) vertex */ struct vertex_nexthop { - struct ospf_interface *oi; /* output intf on root node */ struct in_addr router; /* router address to send to */ + int lsa_pos; /* LSA position for resolving the interface */ }; struct vertex_parent { - struct vertex_nexthop - *nexthop; /* link to nexthop info for this parent */ - struct vertex *parent; /* parent vertex */ - int backlink; /* index back to parent for router-lsa's */ + struct vertex_nexthop *nexthop; /* nexthop address for this parent */ + struct vertex *parent; /* parent vertex */ + int backlink; /* index back to parent for router-lsa's */ }; /* What triggered the SPF ? */ @@ -73,6 +72,14 @@ typedef enum { } ospf_spf_reason_t; extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t); +extern void ospf_spf_calculate(struct ospf_area *area, + struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run); +extern int ospf_spf_calculate_areas(struct ospf *ospf, + struct route_table *new_table, + struct route_table *new_rtrs, + bool is_dry_run); extern void ospf_rtrs_free(struct route_table *); /* void ospf_spf_calculate_timer_add (); */ diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index cdeaa38dc0..e3e63fea8f 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -410,6 +410,8 @@ struct ospf_area { /* Shortest Path Tree. */ struct vertex *spf; + bool spf_dry_run; /* flag for checking if the SPF calculation is + intended for the local RIB */ /* Threads. */ struct thread *t_stub_router; /* Stub-router timer */