ospfd: introduce a 'dry run' into SPF code

in OSPF interface data is used for the nexthop resolution
during the SPF algorithm, see RFC2328 16.1.1. However, for
certain technologies like TI-LFA it is desirable to be able
to calculate SPFs for arbitrary root nodes, not just the
calculating node. Since interface data is not available for
other nodes it is necessary to remove this dependency and
make its usage optional, depending on the intent of
changing the RIB with the generated tree (or not).

To signal that a SPF run is used without the intent to
change the RIB an additional flag `spf_dry_run` is
introduced to the ospf_area struct. This flag is currently
only used within the pure SPF code but will be extended
to the SPF postprocessing later on.

Signed-off-by: GalaxyGorilla <sascha@netdef.org>
This commit is contained in:
GalaxyGorilla 2020-08-07 12:13:07 +00:00
parent 5ec5929c62
commit 1d376ff539
6 changed files with 180 additions and 143 deletions

View File

@ -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");

View File

@ -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);
}
}
}

View File

@ -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 *,

View File

@ -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);

View File

@ -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 (); */

View File

@ -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 */