ospfd: TI-LFA basic infrastructure and algorithms

Signed-off-by: GalaxyGorilla <sascha@netdef.org>
This commit is contained in:
GalaxyGorilla 2020-08-05 08:44:21 +00:00
parent 6351ea6ebf
commit 7fd0729f76
26 changed files with 2008 additions and 118 deletions

View file

@ -1233,6 +1233,20 @@ Summary Route will be originated on-behalf of all matched external LSAs.
Show configuration for display all configured summary routes with Show configuration for display all configured summary routes with
matching external LSA information. matching external LSA information.
======
TI-LFA
======
Experimental support for Topology Independent LFA (Loop-Free Alternate), see
for example 'draft-bashandy-rtgwg-segment-routing-ti-lfa-05'. Note that
TI-LFA requires a proper Segment Routing configuration.
.. index:: fast-reroute ti-lfa
.. clicmd:: fast-reroute ti-lfa
Configured on the router level. Activates TI-LFA for all interfaces.
Currently just link protection for P2P interfaces is supported.
Debugging OSPF Debugging OSPF
============== ==============

View file

@ -337,7 +337,7 @@ void lsa_header_set(struct stream *s, uint8_t options, uint8_t type,
/* router-LSA related functions. */ /* router-LSA related functions. */
/* Get router-LSA flags. */ /* Get router-LSA flags. */
static uint8_t router_lsa_flags(struct ospf_area *area) uint8_t router_lsa_flags(struct ospf_area *area)
{ {
uint8_t flags; uint8_t flags;
@ -420,9 +420,8 @@ static uint16_t ospf_link_cost(struct ospf_interface *oi)
} }
/* Set a link information. */ /* Set a link information. */
static char link_info_set(struct stream **s, struct in_addr id, char link_info_set(struct stream **s, struct in_addr id, struct in_addr data,
struct in_addr data, uint8_t type, uint8_t tos, uint8_t type, uint8_t tos, uint16_t cost)
uint16_t cost)
{ {
/* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits /* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits
* vast majority of cases. Some rare routers with lots of links need * vast majority of cases. Some rare routers with lots of links need
@ -679,7 +678,7 @@ static int router_lsa_link_set(struct stream **s, struct ospf_area *area)
} }
/* Set router-LSA body. */ /* Set router-LSA body. */
static void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area) void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area)
{ {
unsigned long putp; unsigned long putp;
uint16_t cnt; uint16_t cnt;

View file

@ -260,6 +260,8 @@ extern struct lsa_header *ospf_lsa_data_dup(struct lsa_header *);
extern void ospf_lsa_data_free(struct lsa_header *); extern void ospf_lsa_data_free(struct lsa_header *);
/* Prototype for various LSAs */ /* Prototype for various LSAs */
extern void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area);
extern uint8_t router_lsa_flags(struct ospf_area *area);
extern int ospf_router_lsa_update(struct ospf *); extern int ospf_router_lsa_update(struct ospf *);
extern int ospf_router_lsa_update_area(struct ospf_area *); extern int ospf_router_lsa_update_area(struct ospf_area *);
@ -333,6 +335,10 @@ extern int is_prefix_default(struct prefix_ipv4 *);
extern int metric_type(struct ospf *, uint8_t, unsigned short); extern int metric_type(struct ospf *, uint8_t, unsigned short);
extern int metric_value(struct ospf *, uint8_t, unsigned short); extern int metric_value(struct ospf *, uint8_t, unsigned short);
extern char link_info_set(struct stream **s, struct in_addr id,
struct in_addr data, uint8_t type, uint8_t tos,
uint16_t cost);
extern struct in_addr ospf_get_nssa_ip(struct ospf_area *); extern struct in_addr ospf_get_nssa_ip(struct ospf_area *);
extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *); extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *);
extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *, extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *,

View file

@ -58,3 +58,5 @@ DEFINE_MTYPE(OSPFD, OSPF_EXT_PARAMS, "OSPF Extended parameters")
DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters") DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters")
DEFINE_MTYPE(OSPFD, OSPF_GR_HELPER, "OSPF Graceful Restart Helper") DEFINE_MTYPE(OSPFD, OSPF_GR_HELPER, "OSPF Graceful Restart Helper")
DEFINE_MTYPE(OSPFD, OSPF_EXTERNAL_RT_AGGR, "OSPF External Route Summarisation") DEFINE_MTYPE(OSPFD, OSPF_EXTERNAL_RT_AGGR, "OSPF External Route Summarisation")
DEFINE_MTYPE(OSPFD, OSPF_P_SPACE, "OSPF TI-LFA P-Space")
DEFINE_MTYPE(OSPFD, OSPF_Q_SPACE, "OSPF TI-LFA Q-Space")

View file

@ -57,5 +57,7 @@ DECLARE_MTYPE(OSPF_SR_PARAMS)
DECLARE_MTYPE(OSPF_EXT_PARAMS) DECLARE_MTYPE(OSPF_EXT_PARAMS)
DECLARE_MTYPE(OSPF_GR_HELPER) DECLARE_MTYPE(OSPF_GR_HELPER)
DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR) DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR)
DECLARE_MTYPE(OSPF_P_SPACE)
DECLARE_MTYPE(OSPF_Q_SPACE)
#endif /* _QUAGGA_OSPF_MEMORY_H */ #endif /* _QUAGGA_OSPF_MEMORY_H */

View file

@ -140,6 +140,35 @@ static int ospf_route_exist_new_table(struct route_table *rt,
return 1; return 1;
} }
static int ospf_route_backup_path_same(struct sr_nexthop_info *srni1,
struct sr_nexthop_info *srni2)
{
struct mpls_label_stack *ls1, *ls2;
uint8_t label_count;
ls1 = srni1->backup_label_stack;
ls2 = srni2->backup_label_stack;
if (!ls1 && !ls2)
return 1;
if ((ls1 && !ls2) || (!ls1 && ls2))
return 0;
if (ls1->num_labels != ls2->num_labels)
return 0;
for (label_count = 0; label_count < ls1->num_labels; label_count++) {
if (ls1->label[label_count] != ls2->label[label_count])
return 0;
}
if (!IPV4_ADDR_SAME(&srni1->backup_nexthop, &srni2->backup_nexthop))
return 0;
return 1;
}
/* If a prefix and a nexthop match any route in the routing table, /* If a prefix and a nexthop match any route in the routing table,
then return 1, otherwise return 0. */ then return 1, otherwise return 0. */
int ospf_route_match_same(struct route_table *rt, struct prefix_ipv4 *prefix, int ospf_route_match_same(struct route_table *rt, struct prefix_ipv4 *prefix,
@ -180,6 +209,11 @@ int ospf_route_match_same(struct route_table *rt, struct prefix_ipv4 *prefix,
return 0; return 0;
if (op->ifindex != newop->ifindex) if (op->ifindex != newop->ifindex)
return 0; return 0;
/* check TI-LFA backup paths */
if (!ospf_route_backup_path_same(&op->srni,
&newop->srni))
return 0;
} }
return 1; return 1;
} else if (prefix_same(&rn->p, (struct prefix *)prefix)) } else if (prefix_same(&rn->p, (struct prefix *)prefix))
@ -664,38 +698,6 @@ void ospf_route_table_dump(struct route_table *rt)
zlog_debug("========================================"); zlog_debug("========================================");
} }
void ospf_route_table_print(struct vty *vty, struct route_table *rt)
{
struct route_node *rn;
struct ospf_route * or ;
struct listnode *pnode;
struct ospf_path *path;
vty_out(vty, "========== OSPF routing table ==========\n");
for (rn = route_top(rt); rn; rn = route_next(rn))
if ((or = rn->info) != NULL) {
if (or->type == OSPF_DESTINATION_NETWORK) {
vty_out(vty, "N %-18pFX %-15pI4 %s %d\n",
&rn->p, & or->u.std.area_id,
ospf_path_type_str[or->path_type],
or->cost);
for (ALL_LIST_ELEMENTS_RO(or->paths, pnode,
path))
if (path->nexthop.s_addr != INADDR_ANY)
vty_out(vty, " -> %pI4\n",
&path->nexthop);
else
vty_out(vty, " -> %s\n",
"directly connected");
} else
vty_out(vty, "R %-18pI4 %-15pI4 %s %d\n",
&rn->p.u.prefix4, & or->u.std.area_id,
ospf_path_type_str[or->path_type],
or->cost);
}
vty_out(vty, "========================================\n");
}
/* This is 16.4.1 implementation. /* This is 16.4.1 implementation.
o Intra-area paths using non-backbone areas are always the most preferred. o Intra-area paths using non-backbone areas are always the most preferred.
o The other paths, intra-area backbone paths and inter-area paths, o The other paths, intra-area backbone paths and inter-area paths,
@ -802,6 +804,7 @@ void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area,
|| area->spf_dry_run) { || area->spf_dry_run) {
path = ospf_path_new(); path = ospf_path_new();
path->nexthop = nexthop->router; path->nexthop = nexthop->router;
path->adv_router = v->id;
if (oi) { if (oi) {
path->ifindex = oi->ifp->ifindex; path->ifindex = oi->ifp->ifindex;

View file

@ -42,6 +42,10 @@ struct sr_nexthop_info {
* or NULL if next hop is the destination of the prefix * or NULL if next hop is the destination of the prefix
*/ */
struct sr_node *nexthop; struct sr_node *nexthop;
/* TI-LFA */
struct mpls_label_stack *backup_label_stack;
struct in_addr backup_nexthop;
}; };
/* OSPF Path. */ /* OSPF Path. */

View file

@ -46,6 +46,7 @@
#include "ospfd/ospf_abr.h" #include "ospfd/ospf_abr.h"
#include "ospfd/ospf_dump.h" #include "ospfd/ospf_dump.h"
#include "ospfd/ospf_sr.h" #include "ospfd/ospf_sr.h"
#include "ospfd/ospf_ti_lfa.h"
#include "ospfd/ospf_errors.h" #include "ospfd/ospf_errors.h"
/* Variables to ensure a SPF scheduled log message is printed only once */ /* Variables to ensure a SPF scheduled log message is printed only once */
@ -145,6 +146,10 @@ static void ospf_canonical_nexthops_free(struct vertex *root)
if (vp->parent == root && vp->nexthop) { if (vp->parent == root && vp->nexthop) {
vertex_nexthop_free(vp->nexthop); vertex_nexthop_free(vp->nexthop);
vp->nexthop = NULL; vp->nexthop = NULL;
if (vp->local_nexthop) {
vertex_nexthop_free(vp->local_nexthop);
vp->local_nexthop = NULL;
}
} }
} }
} }
@ -154,7 +159,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root)
* 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,
struct vertex_nexthop *hop) struct vertex_nexthop *hop,
struct vertex_nexthop *lhop)
{ {
struct vertex_parent *new; struct vertex_parent *new;
@ -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;
new->local_nexthop = lhop;
return new; return new;
} }
@ -172,7 +179,7 @@ static void vertex_parent_free(void *p)
XFREE(MTYPE_OSPF_VERTEX_PARENT, p); XFREE(MTYPE_OSPF_VERTEX_PARENT, p);
} }
static int vertex_parent_cmp(void *aa, void *bb) int vertex_parent_cmp(void *aa, void *bb)
{ {
struct vertex_parent *a = aa, *b = bb; struct vertex_parent *a = aa, *b = bb;
return IPV4_ADDR_CMP(&a->nexthop->router, &b->nexthop->router); return IPV4_ADDR_CMP(&a->nexthop->router, &b->nexthop->router);
@ -284,6 +291,194 @@ static void ospf_vertex_add_parent(struct vertex *v)
} }
} }
/* Find a vertex according to its router id */
struct vertex *ospf_spf_vertex_find(struct in_addr id, struct list *vertex_list)
{
struct listnode *node;
struct vertex *found;
for (ALL_LIST_ELEMENTS_RO(vertex_list, node, found)) {
if (found->id.s_addr == id.s_addr)
return found;
}
return NULL;
}
/* Create a deep copy of a SPF vertex without children and parents */
static struct vertex *ospf_spf_vertex_copy(struct vertex *vertex)
{
struct vertex *copy;
copy = XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex));
memcpy(copy, vertex, sizeof(struct vertex));
copy->parents = list_new();
copy->parents->del = vertex_parent_free;
copy->parents->cmp = vertex_parent_cmp;
copy->children = list_new();
return copy;
}
/* Create a deep copy of a SPF vertex_parent */
static struct vertex_parent *
ospf_spf_vertex_parent_copy(struct vertex_parent *vertex_parent)
{
struct vertex_parent *vertex_parent_copy;
struct vertex_nexthop *nexthop_copy, *local_nexthop_copy;
vertex_parent_copy =
XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex_parent));
nexthop_copy =
XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex_nexthop));
local_nexthop_copy =
XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex_nexthop));
memcpy(vertex_parent_copy, vertex_parent, sizeof(struct vertex_parent));
memcpy(nexthop_copy, vertex_parent->nexthop,
sizeof(struct vertex_nexthop));
memcpy(local_nexthop_copy, vertex_parent->local_nexthop,
sizeof(struct vertex_nexthop));
vertex_parent_copy->nexthop = nexthop_copy;
vertex_parent_copy->local_nexthop = local_nexthop_copy;
return vertex_parent_copy;
}
/* Create a deep copy of a SPF tree */
void ospf_spf_copy(struct vertex *vertex, struct list *vertex_list)
{
struct listnode *node;
struct vertex *vertex_copy, *child, *child_copy, *parent_copy;
struct vertex_parent *vertex_parent, *vertex_parent_copy;
/* First check if the node is already in the vertex list */
vertex_copy = ospf_spf_vertex_find(vertex->id, vertex_list);
if (!vertex_copy) {
vertex_copy = ospf_spf_vertex_copy(vertex);
listnode_add(vertex_list, vertex_copy);
}
/* Copy all parents, create parent nodes if necessary */
for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, vertex_parent)) {
parent_copy = ospf_spf_vertex_find(vertex_parent->parent->id,
vertex_list);
if (!parent_copy) {
parent_copy =
ospf_spf_vertex_copy(vertex_parent->parent);
listnode_add(vertex_list, parent_copy);
}
vertex_parent_copy = ospf_spf_vertex_parent_copy(vertex_parent);
vertex_parent_copy->parent = parent_copy;
listnode_add(vertex_copy->parents, vertex_parent_copy);
}
/* Copy all children, create child nodes if necessary */
for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) {
child_copy = ospf_spf_vertex_find(child->id, vertex_list);
if (!child_copy) {
child_copy = ospf_spf_vertex_copy(child);
listnode_add(vertex_list, child_copy);
}
listnode_add(vertex_copy->children, child_copy);
}
/* Finally continue copying with child nodes */
for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child))
ospf_spf_copy(child, vertex_list);
}
static void ospf_spf_remove_branch(struct vertex_parent *vertex_parent,
struct vertex *child,
struct list *vertex_list)
{
struct listnode *node, *nnode, *inner_node, *inner_nnode;
struct vertex *grandchild;
struct vertex_parent *vertex_parent_found;
bool has_more_links = false;
/*
* First check if there are more nexthops for that parent to that child
*/
for (ALL_LIST_ELEMENTS_RO(child->parents, node, vertex_parent_found)) {
if (vertex_parent_found->parent->id.s_addr
== vertex_parent->parent->id.s_addr
&& vertex_parent_found->nexthop->router.s_addr
!= vertex_parent->nexthop->router.s_addr)
has_more_links = true;
}
/*
* No more links from that parent? Then delete the child from its
* children list.
*/
if (!has_more_links)
listnode_delete(vertex_parent->parent->children, child);
/*
* Delete the vertex_parent from the child parents list, this needs to
* be done anyway.
*/
listnode_delete(child->parents, vertex_parent);
/*
* Are there actually more parents left? If not, then delete the child!
* This is done by recursively removing the links to the grandchildren,
* such that finally the child can be removed without leaving unused
* partial branches.
*/
if (child->parents->count == 0) {
for (ALL_LIST_ELEMENTS(child->children, node, nnode,
grandchild)) {
for (ALL_LIST_ELEMENTS(grandchild->parents, inner_node,
inner_nnode,
vertex_parent_found)) {
ospf_spf_remove_branch(vertex_parent_found,
grandchild, vertex_list);
}
}
listnode_delete(vertex_list, child);
ospf_vertex_free(child);
}
}
int ospf_spf_remove_link(struct vertex *vertex, struct list *vertex_list,
struct router_lsa_link *link)
{
struct listnode *node, *inner_node;
struct vertex *child;
struct vertex_parent *vertex_parent;
/*
* Identify the node who shares a subnet (given by the link) with a
* child and remove the branch of this particular child.
*/
for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) {
for (ALL_LIST_ELEMENTS_RO(child->parents, inner_node,
vertex_parent)) {
if ((vertex_parent->local_nexthop->router.s_addr
& link->link_data.s_addr)
== (link->link_id.s_addr
& link->link_data.s_addr)) {
ospf_spf_remove_branch(vertex_parent, child,
vertex_list);
return 0;
}
}
}
/* No link found yet, move on recursively */
for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) {
if (ospf_spf_remove_link(child, vertex_list, link) == 0)
return 0;
}
/* link was not removed yet */
return 1;
}
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, bool is_root_node) bool is_dry_run, bool is_root_node)
{ {
@ -427,6 +622,7 @@ static void ospf_spf_flush_parents(struct vertex *w)
*/ */
static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, static void ospf_spf_add_parent(struct vertex *v, struct vertex *w,
struct vertex_nexthop *newhop, struct vertex_nexthop *newhop,
struct vertex_nexthop *newlhop,
unsigned int distance) unsigned int distance)
{ {
struct vertex_parent *vp, *wp; struct vertex_parent *vp, *wp;
@ -482,7 +678,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w,
} }
} }
vp = vertex_parent_new(v, ospf_lsa_has_link(w->lsa, v->lsa), newhop); vp = vertex_parent_new(v, ospf_lsa_has_link(w->lsa, v->lsa), newhop,
newlhop);
listnode_add_sort(w->parents, vp); listnode_add_sort(w->parents, vp);
return; return;
@ -541,7 +738,7 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
unsigned int distance, int lsa_pos) unsigned int distance, int lsa_pos)
{ {
struct listnode *node, *nnode; struct listnode *node, *nnode;
struct vertex_nexthop *nh; struct vertex_nexthop *nh, *lnh;
struct vertex_parent *vp; struct vertex_parent *vp;
unsigned int added = 0; unsigned int added = 0;
char buf1[BUFSIZ]; char buf1[BUFSIZ];
@ -703,7 +900,17 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
nh = vertex_nexthop_new(); nh = vertex_nexthop_new();
nh->router = nexthop; nh->router = nexthop;
nh->lsa_pos = lsa_pos; nh->lsa_pos = lsa_pos;
ospf_spf_add_parent(v, w, nh, distance);
/*
* Since v is the root the nexthop and
* local nexthop are the same.
*/
lnh = vertex_nexthop_new();
memcpy(lnh, nh,
sizeof(struct vertex_nexthop));
ospf_spf_add_parent(v, w, nh, lnh,
distance);
return 1; return 1;
} else } else
zlog_info( zlog_info(
@ -733,7 +940,17 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
nh = vertex_nexthop_new(); nh = vertex_nexthop_new();
nh->router = vl_data->nexthop.router; nh->router = vl_data->nexthop.router;
nh->lsa_pos = vl_data->nexthop.lsa_pos; nh->lsa_pos = vl_data->nexthop.lsa_pos;
ospf_spf_add_parent(v, w, nh, distance);
/*
* Since v is the root the nexthop and
* local nexthop are the same.
*/
lnh = vertex_nexthop_new();
memcpy(lnh, nh,
sizeof(struct vertex_nexthop));
ospf_spf_add_parent(v, w, nh, lnh,
distance);
return 1; return 1;
} else } else
zlog_info( zlog_info(
@ -747,7 +964,15 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
nh = vertex_nexthop_new(); nh = vertex_nexthop_new();
nh->router.s_addr = 0; /* Nexthop not required */ nh->router.s_addr = 0; /* Nexthop not required */
nh->lsa_pos = lsa_pos; nh->lsa_pos = lsa_pos;
ospf_spf_add_parent(v, w, nh, distance);
/*
* Since v is the root the nexthop and
* local nexthop are the same.
*/
lnh = vertex_nexthop_new();
memcpy(lnh, nh, sizeof(struct vertex_nexthop));
ospf_spf_add_parent(v, w, nh, lnh, distance);
return 1; return 1;
} }
} /* end V is the root */ } /* end V is the root */
@ -780,8 +1005,18 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
nh = vertex_nexthop_new(); nh = vertex_nexthop_new();
nh->router = l->link_data; nh->router = l->link_data;
nh->lsa_pos = vp->nexthop->lsa_pos; nh->lsa_pos = vp->nexthop->lsa_pos;
/*
* Since v is the root the nexthop and
* local nexthop are the same.
*/
lnh = vertex_nexthop_new();
memcpy(lnh, nh,
sizeof(struct vertex_nexthop));
added = 1; added = 1;
ospf_spf_add_parent(v, w, nh, distance); ospf_spf_add_parent(v, w, nh, lnh,
distance);
} }
/* /*
* Note lack of return is deliberate. See next * Note lack of return is deliberate. See next
@ -829,12 +1064,41 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) { for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) {
added = 1; added = 1;
ospf_spf_add_parent(v, w, vp->nexthop, distance);
/*
* The nexthop is inherited, but the local nexthop still needs
* to be created.
*/
if (l) {
lnh = vertex_nexthop_new();
lnh->router = l->link_data;
lnh->lsa_pos = lsa_pos;
} else {
lnh = NULL;
}
ospf_spf_add_parent(v, w, vp->nexthop, lnh, distance);
} }
return added; return added;
} }
static int ospf_spf_is_protected_link(struct ospf_area *area,
struct router_lsa_link *link)
{
struct router_lsa_link *p_link;
p_link = area->spf_protected_link;
if (!p_link)
return 0;
if ((p_link->link_id.s_addr & p_link->link_data.s_addr)
== (link->link_data.s_addr & p_link->link_data.s_addr))
return 1;
return 0;
}
/* /*
* RFC2328 16.1 (2). * RFC2328 16.1 (2).
* v is on the SPF tree. Examine the links in v's LSA. Update the list of * v is on the SPF tree. Examine the links in v's LSA. Update the list of
@ -891,6 +1155,16 @@ static void ospf_spf_next(struct vertex *v, struct ospf_area *area,
if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB) if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB)
continue; continue;
/*
* Don't process TI-LFA protected links.
*
* TODO: Replace this by a proper solution, e.g. remove
* corresponding links from the LSDB and run the SPF
* algo with the stripped-down LSDB.
*/
if (ospf_spf_is_protected_link(area, l))
continue;
/* /*
* (b) Otherwise, W is a transit vertex (router or * (b) Otherwise, W is a transit vertex (router or
* transit network). Look up the vertex W's LSA * transit network). Look up the vertex W's LSA
@ -1069,8 +1343,7 @@ void ospf_spf_print(struct vty *vty, struct vertex *v, int i)
struct vertex_parent *parent; struct vertex_parent *parent;
if (v->type == OSPF_VERTEX_ROUTER) { if (v->type == OSPF_VERTEX_ROUTER) {
vty_out(vty, "SPF Result: depth %d [R] %pI4\n", i, vty_out(vty, "SPF Result: depth %d [R] %pI4\n", i, &v->lsa->id);
&v->lsa->id);
} else { } else {
struct network_lsa *lsa = (struct network_lsa *)v->lsa; struct network_lsa *lsa = (struct network_lsa *)v->lsa;
vty_out(vty, "SPF Result: depth %d [N] %pI4/%d\n", i, vty_out(vty, "SPF Result: depth %d [N] %pI4/%d\n", i,
@ -1078,9 +1351,11 @@ void ospf_spf_print(struct vty *vty, struct vertex *v, int i)
} }
for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) {
vty_out(vty, " nexthop %pI4 lsa pos %d\n", vty_out(vty,
&parent->nexthop->router, " nexthop %pI4 lsa pos %d -- local nexthop %pI4 lsa pos %d\n",
parent->nexthop->lsa_pos); &parent->nexthop->router, parent->nexthop->lsa_pos,
&parent->local_nexthop->router,
parent->local_nexthop->lsa_pos);
} }
i++; i++;
@ -1128,7 +1403,9 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v,
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));
if (l->m[0].type == LSA_LINK_TYPE_STUB) /* Don't process TI-LFA protected links */
if (l->m[0].type == LSA_LINK_TYPE_STUB
&& !ospf_spf_is_protected_link(area, l))
ospf_intra_add_stub(rt, l, v, area, ospf_intra_add_stub(rt, l, v, area,
parent_is_root, lsa_pos); parent_is_root, lsa_pos);
lsa_pos++; lsa_pos++;
@ -1185,15 +1462,18 @@ void ospf_rtrs_free(struct route_table *rtrs)
void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list) void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list)
{ {
/* /*
* Free nexthop information, canonical versions of which are * Free nexthop information, canonical versions of which are
* attached the first level of router vertices attached to the * attached the first level of router vertices attached to the
* root vertex, see ospf_nexthop_calculation. * root vertex, see ospf_nexthop_calculation.
*/ */
ospf_canonical_nexthops_free(spf); if (spf)
ospf_canonical_nexthops_free(spf);
/* Free SPF vertices list with deconstructor ospf_vertex_free. */ /* Free SPF vertices list with deconstructor ospf_vertex_free. */
list_delete(&vertex_list); if (vertex_list)
list_delete(&vertex_list);
} }
#if 0 #if 0
@ -1359,19 +1639,26 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
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));
/* If this is a dry run then keep the SPF data in place */
if (!area->spf_dry_run)
ospf_spf_cleanup(area->spf, area->spf_vertex_list);
} }
int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_rtrs, bool is_dry_run, struct route_table *new_table,
bool is_root_node) struct route_table *new_rtrs)
{
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
false, true);
if (ospf->ti_lfa_enabled)
ospf_ti_lfa_compute(area, new_table);
ospf_spf_cleanup(area->spf, area->spf_vertex_list);
}
void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
struct route_table *new_rtrs)
{ {
struct ospf_area *area; struct ospf_area *area;
struct listnode *node, *nnode; struct listnode *node, *nnode;
int areas_processed = 0;
/* Calculate SPF for each area. */ /* Calculate SPF for each area. */
for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
@ -1380,20 +1667,13 @@ int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
if (ospf->backbone && ospf->backbone == area) if (ospf->backbone && ospf->backbone == area)
continue; continue;
ospf_spf_calculate(area, area->router_lsa_self, new_table, ospf_spf_calculate_area(ospf, area, new_table, new_rtrs);
new_rtrs, is_dry_run, is_root_node);
areas_processed++;
} }
/* SPF for backbone, if required */ /* SPF for backbone, if required */
if (ospf->backbone) { if (ospf->backbone)
area = ospf->backbone; ospf_spf_calculate_area(ospf, ospf->backbone, new_table,
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs);
new_rtrs, is_dry_run, is_root_node);
areas_processed++;
}
return areas_processed;
} }
/* Worker for SPF calculation scheduler. */ /* Worker for SPF calculation scheduler. */
@ -1402,7 +1682,6 @@ 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 timeval start_time, spf_start_time; struct timeval start_time, spf_start_time;
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 */
@ -1418,8 +1697,7 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread)
monotime(&spf_start_time); monotime(&spf_start_time);
new_table = route_table_init(); /* routing table */ new_table = route_table_init(); /* routing table */
new_rtrs = route_table_init(); /* ABR/ASBR routing table */ new_rtrs = route_table_init(); /* ABR/ASBR routing table */
areas_processed = ospf_spf_calculate_areas(ospf, new_table, new_rtrs, ospf_spf_calculate_areas(ospf, new_table, new_rtrs);
false, true);
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);
@ -1512,7 +1790,7 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread)
zlog_info(" RouteInstall: %ld", rt_time); zlog_info(" RouteInstall: %ld", rt_time);
if (IS_OSPF_ABR(ospf)) if (IS_OSPF_ABR(ospf))
zlog_info(" ABR: %ld (%d areas)", zlog_info(" ABR: %ld (%d areas)",
abr_time, areas_processed); abr_time, ospf->areas->count);
zlog_info("Reason(s) for SPF: %s", rbuf); zlog_info("Reason(s) for SPF: %s", rbuf);
} }

View file

@ -47,15 +47,15 @@ struct vertex {
struct list *children; /* list of children in SPF tree*/ struct list *children; /* list of children in SPF tree*/
}; };
/* A nexthop taken on the root node to get to this (parent) vertex */
struct vertex_nexthop { struct vertex_nexthop {
struct in_addr router; /* router address to send to */ struct in_addr router; /* router address to send to */
int lsa_pos; /* LSA position for resolving the interface */ int lsa_pos; /* LSA position for resolving the interface */
}; };
struct vertex_parent { struct vertex_parent {
struct vertex_nexthop *nexthop; /* nexthop address for this parent */ struct vertex_nexthop *nexthop; /* nexthop taken on the root node */
struct vertex *parent; /* parent vertex */ struct vertex_nexthop *local_nexthop; /* local nexthop of the parent */
struct vertex *parent; /* parent vertex */
int backlink; /* index back to parent for router-lsa's */ int backlink; /* index back to parent for router-lsa's */
}; };
@ -77,12 +77,20 @@ extern void ospf_spf_calculate(struct ospf_area *area,
struct route_table *new_table, struct route_table *new_table,
struct route_table *new_rtrs, bool is_dry_run, struct route_table *new_rtrs, bool is_dry_run,
bool is_root_node); bool is_root_node);
extern int ospf_spf_calculate_areas(struct ospf *ospf, extern void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_table, struct route_table *new_table,
struct route_table *new_rtrs, struct route_table *new_rtrs);
bool is_dry_run, bool is_root_node); extern void ospf_spf_calculate_areas(struct ospf *ospf,
struct route_table *new_table,
struct route_table *new_rtrs);
extern void ospf_rtrs_free(struct route_table *); extern void ospf_rtrs_free(struct route_table *);
extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list); extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list);
extern void ospf_spf_copy(struct vertex *vertex, struct list *vertex_list);
extern int ospf_spf_remove_link(struct vertex *vertex, struct list *vertex_list,
struct router_lsa_link *link);
extern struct vertex *ospf_spf_vertex_find(struct in_addr id,
struct list *vertex_list);
extern int vertex_parent_cmp(void *aa, void *bb);
extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i); extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i);

View file

@ -159,6 +159,16 @@ static struct sr_node *sr_node_new(struct in_addr *rid)
return new; return new;
} }
/* Supposed to be used for testing */
struct sr_node *ospf_sr_node_create(struct in_addr *rid)
{
struct sr_node *srn;
srn = hash_get(OspfSR.neighbors, (void *)rid, (void *)sr_node_new);
return srn;
}
/* Delete Segment Routing node */ /* Delete Segment Routing node */
static void sr_node_del(struct sr_node *srn) static void sr_node_del(struct sr_node *srn)
{ {
@ -653,6 +663,30 @@ static mpls_label_t index2label(uint32_t index, struct sr_block srgb)
return label; return label;
} }
/* Get the prefix sid for a specific router id */
mpls_label_t ospf_sr_get_prefix_sid_by_id(struct in_addr *id)
{
struct sr_node *srn;
struct sr_prefix *srp;
mpls_label_t label;
srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, id);
if (srn) {
/*
* TODO: Here we assume that the SRGBs are the same,
* and that the node's prefix SID is at the head of
* the list, probably needs tweaking.
*/
srp = listnode_head(srn->ext_prefix);
label = index2label(srp->sid, srn->srgb);
} else {
label = MPLS_INVALID_LABEL;
}
return label;
}
/* Get neighbor full structure from address */ /* Get neighbor full structure from address */
static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top, static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top,
struct in_addr addr) struct in_addr addr)

View file

@ -361,4 +361,9 @@ extern void ospf_sr_update_local_prefix(struct interface *ifp,
struct prefix *p); struct prefix *p);
/* Segment Routing re-routing function */ /* Segment Routing re-routing function */
extern void ospf_sr_update_task(struct ospf *ospf); extern void ospf_sr_update_task(struct ospf *ospf);
/* Support for TI-LFA */
extern mpls_label_t ospf_sr_get_prefix_sid_by_id(struct in_addr *id);
extern struct sr_node *ospf_sr_node_create(struct in_addr *rid);
#endif /* _FRR_OSPF_SR_H */ #endif /* _FRR_OSPF_SR_H */

548
ospfd/ospf_ti_lfa.c Normal file
View file

@ -0,0 +1,548 @@
/*
* OSPF TI-LFA
* Copyright (C) 2020 NetDEF, Inc.
* Sascha Kattelmann
*
* This file is part of FRR.
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "prefix.h"
#include "table.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_asbr.h"
#include "ospfd/ospf_lsa.h"
#include "ospfd/ospf_spf.h"
#include "ospfd/ospf_sr.h"
#include "ospfd/ospf_route.h"
#include "ospfd/ospf_ti_lfa.h"
DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item,
p_spaces_compare_func)
DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item,
q_spaces_compare_func)
static void ospf_ti_lfa_find_p_node(struct vertex *pc_node,
struct p_space *p_space,
struct q_space *q_space,
struct ospf_ti_lfa_node_info *node_info)
{
struct listnode *node;
struct vertex *p_node = NULL, *p_node_pc_parent;
struct vertex_parent *pc_vertex_parent;
node_info->type = OSPF_TI_LFA_UNDEFINED_NODE;
for (ALL_LIST_ELEMENTS_RO(pc_node->parents, node, pc_vertex_parent)) {
p_node = ospf_spf_vertex_find(pc_vertex_parent->parent->id,
p_space->vertex_list);
/* Just take the first discovered P node */
if (p_node)
break;
}
if (!p_node)
return;
node_info->node = p_node;
node_info->type = OSPF_TI_LFA_P_NODE;
/* For the nexthop we just use the first vertex parent */
p_node_pc_parent =
ospf_spf_vertex_find(p_node->id, p_space->pc_vertex_list);
pc_vertex_parent = listnode_head(p_node_pc_parent->parents);
/*
* It can happen that the P node is the root node itself (hence there
* can be no parents). In this case we don't need to set a nexthop.
*/
node_info->nexthop.s_addr = INADDR_ANY;
if (pc_vertex_parent)
node_info->nexthop = pc_vertex_parent->nexthop->router;
}
static void ospf_ti_lfa_find_q_node(struct vertex *pc_node,
struct p_space *p_space,
struct q_space *q_space,
struct ospf_ti_lfa_node_info *node_info)
{
struct listnode *node;
struct vertex *p_node, *q_node, *q_space_parent = NULL;
struct vertex_parent *pc_vertex_parent;
p_node = ospf_spf_vertex_find(pc_node->id, p_space->vertex_list);
q_node = ospf_spf_vertex_find(pc_node->id, q_space->vertex_list);
/*
* If we don't find the node in the Q space then there's really
* something wrong (since we check the parent, see below).
*/
assert(q_node);
node_info->type = OSPF_TI_LFA_UNDEFINED_NODE;
if (p_node && q_node) {
node_info->node = pc_node;
node_info->type = OSPF_TI_LFA_PQ_NODE;
/* For the nexthop we just use the first vertex parent */
pc_vertex_parent = listnode_head(pc_node->parents);
node_info->nexthop = pc_vertex_parent->nexthop->router;
return;
}
if (pc_node->parents->count == 0)
return;
/* First check if the same link also exists in the Q space */
for (ALL_LIST_ELEMENTS_RO(pc_node->parents, node, pc_vertex_parent)) {
/*
* Note that the Q space has the 'reverse' direction of the PC
* SPF. Hence compare PC SPF parents to Q space children.
*/
q_space_parent = ospf_spf_vertex_find(
pc_vertex_parent->parent->id, q_node->children);
if (q_space_parent)
break;
}
/*
* If the Q space parent doesn't exist we 'hit' the border to the P
* space and hence got our Q node.
*/
if (!q_space_parent) {
node_info->node = pc_node;
node_info->type = OSPF_TI_LFA_Q_NODE;
/* For the nexthop we just use the first vertex parent */
pc_vertex_parent = listnode_head(pc_node->parents);
node_info->nexthop = pc_vertex_parent->nexthop->router;
return;
}
return ospf_ti_lfa_find_q_node(pc_vertex_parent->parent, p_space,
q_space, node_info);
}
static struct mpls_label_stack *
ospf_ti_lfa_create_label_stack(mpls_label_t labels[], uint32_t num_labels)
{
struct mpls_label_stack *label_stack;
uint32_t i;
/* Sanity check */
for (i = 0; i < num_labels; i++) {
if (labels[i] == MPLS_INVALID_LABEL)
return NULL;
}
label_stack = XCALLOC(MTYPE_OSPF_Q_SPACE,
sizeof(struct mpls_label_stack)
+ num_labels * sizeof(mpls_label_t));
label_stack->num_labels = num_labels;
for (i = 0; i < num_labels; i++)
label_stack->label[i] = labels[i];
return label_stack;
}
static void ospf_ti_lfa_generate_label_stack(struct p_space *p_space,
struct q_space *q_space)
{
struct ospf_ti_lfa_node_info p_node_info, q_node_info;
mpls_label_t labels[2];
struct vertex *pc_node;
zlog_debug("%s: Generating Label stack for src %pI4 and dest %pI4.",
__func__, &p_space->root->id, &q_space->root->id);
pc_node = ospf_spf_vertex_find(q_space->root->id,
p_space->pc_vertex_list);
if (!pc_node) {
zlog_debug(
"%s: There seems to be no post convergence path (yet).",
__func__);
return;
}
ospf_ti_lfa_find_q_node(pc_node, p_space, q_space, &q_node_info);
if (q_node_info.type == OSPF_TI_LFA_UNDEFINED_NODE) {
zlog_debug("%s: Q node not found!", __func__);
return;
}
/* Found a PQ node? Then we are done here. */
if (q_node_info.type == OSPF_TI_LFA_PQ_NODE) {
labels[0] = ospf_sr_get_prefix_sid_by_id(&q_node_info.node->id);
q_space->label_stack =
ospf_ti_lfa_create_label_stack(labels, 1);
q_space->nexthop = q_node_info.nexthop;
return;
}
/* Otherwise find the adjacent P node. */
pc_node = ospf_spf_vertex_find(q_node_info.node->id,
p_space->pc_vertex_list);
ospf_ti_lfa_find_p_node(pc_node, p_space, q_space, &p_node_info);
if (p_node_info.type == OSPF_TI_LFA_UNDEFINED_NODE) {
zlog_debug("%s: P node not found!", __func__);
return;
}
/*
* It can happen that the P node is the root itself, therefore we don't
* need a label for it.
*/
if (p_node_info.node->id.s_addr == p_space->root->id.s_addr) {
labels[0] = ospf_sr_get_prefix_sid_by_id(&q_node_info.node->id);
q_space->label_stack =
ospf_ti_lfa_create_label_stack(labels, 1);
q_space->nexthop = q_node_info.nexthop;
return;
}
/* Otherwise we have a P and also a Q node which we need labels for. */
labels[0] = ospf_sr_get_prefix_sid_by_id(&p_node_info.node->id);
labels[1] = ospf_sr_get_prefix_sid_by_id(&q_node_info.node->id);
q_space->label_stack = ospf_ti_lfa_create_label_stack(labels, 2);
q_space->nexthop = p_node_info.nexthop;
}
static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
struct p_space *p_space,
struct vertex *dest)
{
struct listnode *node;
struct vertex *child;
struct route_table *new_table, *new_rtrs;
struct q_space *q_space, q_space_search;
char buf[MPLS_LABEL_STRLEN];
/* Check if we already have a Q space for this destination */
q_space_search.root = dest;
if (q_spaces_find(p_space->q_spaces, &q_space_search))
return;
q_space = XCALLOC(MTYPE_OSPF_Q_SPACE, sizeof(struct q_space));
new_table = route_table_init();
new_rtrs = route_table_init();
/*
* Generate a new SPF tree for this vertex,
* dry run true, root node false
*/
ospf_spf_calculate(area, dest->lsa_p, new_table, new_rtrs, true, false);
q_space->root = area->spf;
q_space->vertex_list = area->spf_vertex_list;
q_space->label_stack = NULL;
/* 'Cut' the branch of the protected link out of the new SPF tree */
ospf_spf_remove_link(q_space->root, q_space->vertex_list,
p_space->protected_link);
/*
* Generate the smallest possible label stack from the root of the P
* space to the root of the Q space.
*/
ospf_ti_lfa_generate_label_stack(p_space, q_space);
if (q_space->label_stack) {
mpls_label2str(q_space->label_stack->num_labels,
q_space->label_stack->label, buf,
MPLS_LABEL_STRLEN, true);
zlog_info(
"%s: Generated label stack %s for root %pI4 and destination %pI4 for protected link %pI4",
__func__, buf, &p_space->root->id, &q_space->root->id,
&p_space->protected_link->link_id);
} else {
zlog_info(
"%s: NO label stack generated for root %pI4 and destination %pI4 for protected link %pI4",
__func__, &p_space->root->id, &q_space->root->id,
&p_space->protected_link->link_id);
}
/* We are finished, store the new Q space in the P space struct */
q_spaces_add(p_space->q_spaces, q_space);
/* Recursively generate Q spaces for all children */
for (ALL_LIST_ELEMENTS_RO(dest->children, node, child))
ospf_ti_lfa_generate_q_spaces(area, p_space, child);
}
static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
struct p_space *p_space)
{
struct route_table *new_table, *new_rtrs;
new_table = route_table_init();
new_rtrs = route_table_init();
area->spf_protected_link = p_space->protected_link;
/*
* The 'post convergence' SPF tree is generated here
* dry run true, root node false
*
* So how does this work? During the SPF calculation the algorithm
* checks if a link belongs to a protected stub and then just ignores
* it. This is actually _NOT_ a good way to calculate the post
* convergence SPF tree. The preferred way would be to delete the
* relevant links from a copy of the LSDB and then just run the SPF
* algorithm on that as usual. However, removing links from router
* LSAs appears to be its own endeavour (because LSAs are stored as a
* 'raw' stream), so we go with this rather hacky way for now.
*/
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
true, false);
p_space->pc_spf = area->spf;
p_space->pc_vertex_list = area->spf_vertex_list;
area->spf_protected_link = NULL;
}
static void ospf_ti_lfa_generate_p_space(struct ospf_area *area,
struct vertex *child,
struct router_lsa_link *link)
{
struct vertex *spf_orig;
struct list *vertex_list, *vertex_list_orig;
struct p_space *p_space;
p_space = XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_space));
vertex_list = list_new();
/* The P-space will get its own SPF tree, so copy the old one */
ospf_spf_copy(area->spf, vertex_list);
p_space->root = listnode_head(vertex_list);
p_space->vertex_list = vertex_list;
p_space->protected_link = link;
/* Initialize the Q spaces for this P space and protected link */
p_space->q_spaces =
XCALLOC(MTYPE_OSPF_Q_SPACE, sizeof(struct q_spaces_head));
q_spaces_init(p_space->q_spaces);
/* 'Cut' the child branch out of the new SPF tree */
ospf_spf_remove_link(p_space->root, p_space->vertex_list,
p_space->protected_link);
/*
* Since we are going to calculate more SPF trees for Q spaces, keep the
* 'original' one here temporarily
*/
spf_orig = area->spf;
vertex_list_orig = area->spf_vertex_list;
/* Generate the post convergence SPF as a blueprint for backup paths */
ospf_ti_lfa_generate_post_convergence_spf(area, p_space);
/* Generate the relevant Q spaces for this particular P space */
ospf_ti_lfa_generate_q_spaces(area, p_space, child);
/* Put the 'original' SPF tree back in place */
area->spf = spf_orig;
area->spf_vertex_list = vertex_list_orig;
/* We are finished, store the new P space */
p_spaces_add(area->p_spaces, p_space);
}
void ospf_ti_lfa_generate_p_spaces(struct ospf_area *area)
{
struct listnode *node, *inner_node;
struct vertex *root, *child;
struct vertex_parent *vertex_parent;
uint8_t *p, *lim;
struct router_lsa_link *l = NULL;
struct prefix stub_prefix, child_prefix;
area->p_spaces =
XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head));
p_spaces_init(area->p_spaces);
root = area->spf;
/* Root or its router LSA was not created yet? */
if (!root || !root->lsa)
return;
stub_prefix.family = AF_INET;
child_prefix.family = AF_INET;
child_prefix.prefixlen = IPV4_MAX_PREFIXLEN;
p = ((uint8_t *)root->lsa) + OSPF_LSA_HEADER_SIZE + 4;
lim = ((uint8_t *)root->lsa) + ntohs(root->lsa->length);
zlog_info("%s: Generating P spaces for area %pI4", __func__,
&area->area_id);
/*
* Iterate over all stub networks which target other OSPF neighbors.
* Check the nexthop of the child vertex if a stub network is relevant.
*/
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;
stub_prefix.prefixlen = ip_masklen(l->link_data);
stub_prefix.u.prefix4 = l->link_id;
for (ALL_LIST_ELEMENTS_RO(root->children, node, child)) {
if (child->type != OSPF_VERTEX_ROUTER)
continue;
for (ALL_LIST_ELEMENTS_RO(child->parents, inner_node,
vertex_parent)) {
child_prefix.u.prefix4 =
vertex_parent->nexthop->router;
/*
* If there's a link for that stub network then
* we will protect it. Hence generate a P space
* for that particular link including the
* Q spaces so we can later on generate a
* backup path for the link.
*/
if (prefix_match(&stub_prefix, &child_prefix)) {
zlog_info(
"%s: Generating P space for %pI4",
__func__, &l->link_id);
ospf_ti_lfa_generate_p_space(area,
child, l);
}
}
}
}
}
static struct p_space *
ospf_ti_lfa_get_p_space_by_nexthop(struct ospf_area *area,
struct in_addr *nexthop)
{
struct p_space *p_space;
struct router_lsa_link *link;
frr_each(p_spaces, area->p_spaces, p_space) {
link = p_space->protected_link;
if ((nexthop->s_addr & link->link_data.s_addr)
== (link->link_id.s_addr & link->link_data.s_addr))
return p_space;
}
return NULL;
}
void ospf_ti_lfa_insert_backup_paths(struct ospf_area *area,
struct route_table *new_table)
{
struct route_node *rn;
struct ospf_route *or;
struct ospf_path *path;
struct listnode *node;
struct p_space *p_space;
struct q_space *q_space, q_space_search;
struct vertex root_search;
for (rn = route_top(new_table); rn; rn = route_next(rn)) {
or = rn->info;
if (or == NULL)
continue;
/* Insert a backup path for all OSPF paths */
for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) {
p_space = ospf_ti_lfa_get_p_space_by_nexthop(
area, &path->nexthop);
if (!p_space) {
zlog_debug(
"%s: P space not found for nexthop %pI4.",
__func__, &path->nexthop);
continue;
}
root_search.id = path->adv_router;
q_space_search.root = &root_search;
q_space = q_spaces_find(p_space->q_spaces,
&q_space_search);
if (!q_space) {
zlog_debug(
"%s: Q space not found for advertising router %pI4.",
__func__, &path->adv_router);
continue;
}
path->srni.backup_label_stack = q_space->label_stack;
path->srni.backup_nexthop = q_space->nexthop;
}
}
}
void ospf_ti_lfa_free_p_spaces(struct ospf_area *area)
{
struct p_space *p_space;
struct q_space *q_space;
while ((p_space = p_spaces_pop(area->p_spaces))) {
while ((q_space = q_spaces_pop(p_space->q_spaces))) {
ospf_spf_cleanup(q_space->root, q_space->vertex_list);
/*
* TODO: label stack is used for route installation
* XFREE(MTYPE_OSPF_Q_SPACE, q_space->label_stack);
*/
XFREE(MTYPE_OSPF_Q_SPACE, q_space);
}
ospf_spf_cleanup(p_space->root, p_space->vertex_list);
ospf_spf_cleanup(p_space->pc_spf, p_space->pc_vertex_list);
q_spaces_fini(p_space->q_spaces);
XFREE(MTYPE_OSPF_Q_SPACE, p_space->q_spaces);
}
p_spaces_fini(area->p_spaces);
XFREE(MTYPE_OSPF_P_SPACE, area->p_spaces);
}
void ospf_ti_lfa_compute(struct ospf_area *area, struct route_table *new_table)
{
/*
* Generate P spaces per protected link and their respective Q spaces,
* generate backup paths (MPLS label stacks) by finding P/Q nodes.
*/
ospf_ti_lfa_generate_p_spaces(area);
/* Insert the generated backup paths into the routing table. */
ospf_ti_lfa_insert_backup_paths(area, new_table);
/* Cleanup P spaces and related datastructures including Q spaces. */
ospf_ti_lfa_free_p_spaces(area);
}

35
ospfd/ospf_ti_lfa.h Normal file
View file

@ -0,0 +1,35 @@
/*
* OSPF calculation.
* Copyright (C) 2020 NetDEF, Inc.
* Sascha Kattelmann
*
* This file is part of FRR.
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _OSPF_TI_LFA_H
#define _OSPF_TI_LFA_H
extern void ospf_ti_lfa_compute(struct ospf_area *area,
struct route_table *new_table);
/* unit testing */
extern void ospf_ti_lfa_generate_p_spaces(struct ospf_area *area);
extern void ospf_ti_lfa_insert_backup_paths(struct ospf_area *area,
struct route_table *new_table);
extern void ospf_ti_lfa_free_p_spaces(struct ospf_area *area);
#endif /* _OSPF_TI_LFA_H */

View file

@ -2602,6 +2602,33 @@ ALIAS(no_ospf_write_multiplier, no_write_multiplier_cmd,
"Write multiplier\n" "Write multiplier\n"
"Maximum number of interface serviced per write\n") "Maximum number of interface serviced per write\n")
DEFUN(ospf_ti_lfa, ospf_ti_lfa_cmd, "fast-reroute ti-lfa",
"Fast Reroute for MPLS and IP resilience\n"
"Topology Independent LFA (Loop-Free Alternate)\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
ospf->ti_lfa_enabled = true;
ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE);
return CMD_SUCCESS;
}
DEFUN(no_ospf_ti_lfa, no_ospf_ti_lfa_cmd, "no fast-reroute ti-lfa",
NO_STR
"Fast Reroute for MPLS and IP resilience\n"
"Topology Independent LFA (Loop-Free Alternate)\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
ospf->ti_lfa_enabled = false;
ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE);
return CMD_SUCCESS;
}
static const char *const ospf_abr_type_descr_str[] = { static const char *const ospf_abr_type_descr_str[] = {
"Unknown", "Standard (RFC2328)", "Alternative IBM", "Unknown", "Standard (RFC2328)", "Alternative IBM",
"Alternative Cisco", "Alternative Shortcut" "Alternative Cisco", "Alternative Shortcut"
@ -6681,8 +6708,8 @@ static void show_lsa_detail_adv_router(struct vty *vty, struct ospf *ospf,
json_lstype); json_lstype);
} }
static void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, int self,
int self, json_object *json) json_object *json)
{ {
struct ospf_lsa *lsa; struct ospf_lsa *lsa;
struct route_node *rn; struct route_node *rn;
@ -12361,6 +12388,10 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
oi->ifp->name, &oi->address->u.prefix4); oi->ifp->name, &oi->address->u.prefix4);
} }
/* TI-LFA print. */
if (ospf->ti_lfa_enabled)
vty_out(vty, " fast-reroute ti-lfa\n");
/* Network area print. */ /* Network area print. */
config_write_network_area(vty, ospf); config_write_network_area(vty, ospf);
@ -12825,6 +12856,10 @@ void ospf_vty_init(void)
install_element(OSPF_NODE, &ospf_proactive_arp_cmd); install_element(OSPF_NODE, &ospf_proactive_arp_cmd);
install_element(OSPF_NODE, &no_ospf_proactive_arp_cmd); install_element(OSPF_NODE, &no_ospf_proactive_arp_cmd);
/* TI-LFA commands */
install_element(OSPF_NODE, &ospf_ti_lfa_cmd);
install_element(OSPF_NODE, &no_ospf_ti_lfa_cmd);
/* Init interface related vty commands. */ /* Init interface related vty commands. */
ospf_vty_if_init(); ospf_vty_if_init();

View file

@ -54,4 +54,8 @@ extern void ospf_vty_show_init(void);
extern void ospf_vty_clear_init(void); extern void ospf_vty_clear_init(void);
extern int str2area_id(const char *, struct in_addr *, int *); extern int str2area_id(const char *, struct in_addr *, int *);
/* unit tests */
void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, int self,
json_object *json);
#endif /* _QUAGGA_OSPF_VTY_H */ #endif /* _QUAGGA_OSPF_VTY_H */

View file

@ -198,15 +198,70 @@ static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS)
return 0; return 0;
} }
/* Nexthop, ifindex, distance and metric information. */
static void ospf_zebra_add_nexthop(struct ospf *ospf, struct ospf_path *path,
struct zapi_route *api)
{
struct zapi_nexthop *api_nh;
struct zapi_nexthop *api_nh_backup;
/* TI-LFA backup path label stack comes first, if present */
if (path->srni.backup_label_stack) {
api_nh_backup = &api->backup_nexthops[api->backup_nexthop_num];
api_nh_backup->vrf_id = ospf->vrf_id;
api_nh_backup->type = NEXTHOP_TYPE_IPV4_IFINDEX;
api_nh_backup->gate.ipv4 = path->srni.backup_nexthop;
api_nh_backup->label_num =
path->srni.backup_label_stack->num_labels;
memcpy(api_nh_backup->labels,
path->srni.backup_label_stack->label,
sizeof(mpls_label_t) * api_nh_backup->label_num);
api->backup_nexthop_num++;
}
/* And here comes the primary nexthop */
api_nh = &api->nexthops[api->nexthop_num];
#ifdef HAVE_NETLINK
if (path->unnumbered
|| (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0)) {
#else /* HAVE_NETLINK */
if (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0) {
#endif /* HAVE_NETLINK */
api_nh->gate.ipv4 = path->nexthop;
api_nh->ifindex = path->ifindex;
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
} else if (path->nexthop.s_addr != INADDR_ANY) {
api_nh->gate.ipv4 = path->nexthop;
api_nh->type = NEXTHOP_TYPE_IPV4;
} else {
api_nh->ifindex = path->ifindex;
api_nh->type = NEXTHOP_TYPE_IFINDEX;
}
api_nh->vrf_id = ospf->vrf_id;
/* Set TI-LFA backup nexthop info if present */
if (path->srni.backup_label_stack) {
SET_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS);
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
/* Just care about a single TI-LFA backup path for now */
api_nh->backup_num = 1;
api_nh->backup_idx[0] = api->backup_nexthop_num - 1;
}
api->nexthop_num++;
}
void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
struct ospf_route * or) struct ospf_route * or)
{ {
struct zapi_route api; struct zapi_route api;
struct zapi_nexthop *api_nh;
uint8_t distance; uint8_t distance;
struct ospf_path *path; struct ospf_path *path;
struct listnode *node; struct listnode *node;
int count = 0;
memset(&api, 0, sizeof(api)); memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id; api.vrf_id = ospf->vrf_id;
@ -241,29 +296,11 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
api.distance = distance; api.distance = distance;
} }
/* Nexthop, ifindex, distance and metric information. */
for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) { for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) {
if (count >= MULTIPATH_NUM) if (api.nexthop_num >= MULTIPATH_NUM)
break; break;
api_nh = &api.nexthops[count];
#ifdef HAVE_NETLINK ospf_zebra_add_nexthop(ospf, path, &api);
if (path->unnumbered || (path->nexthop.s_addr != INADDR_ANY
&& path->ifindex != 0)) {
#else /* HAVE_NETLINK */
if (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0) {
#endif /* HAVE_NETLINK */
api_nh->gate.ipv4 = path->nexthop;
api_nh->ifindex = path->ifindex;
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
} else if (path->nexthop.s_addr != INADDR_ANY) {
api_nh->gate.ipv4 = path->nexthop;
api_nh->type = NEXTHOP_TYPE_IPV4;
} else {
api_nh->ifindex = path->ifindex;
api_nh->type = NEXTHOP_TYPE_IFINDEX;
}
api_nh->vrf_id = ospf->vrf_id;
count++;
if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) {
struct interface *ifp; struct interface *ifp;
@ -276,7 +313,6 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
ifp ? ifp->name : " "); ifp ? ifp->name : " ");
} }
} }
api.nexthop_num = count;
zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);
} }

View file

@ -87,6 +87,20 @@ static void ospf_finish_final(struct ospf *);
#define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1 #define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1
int p_spaces_compare_func(const struct p_space *a, const struct p_space *b)
{
return (a->protected_link->link_id.s_addr
- b->protected_link->link_id.s_addr);
}
int q_spaces_compare_func(const struct q_space *a, const struct q_space *b)
{
return (a->root->id.s_addr - b->root->id.s_addr);
}
DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item,
p_spaces_compare_func)
void ospf_process_refresh_data(struct ospf *ospf, bool reset) void ospf_process_refresh_data(struct ospf *ospf, bool reset)
{ {
struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
@ -264,8 +278,7 @@ static int ospf_area_id_cmp(struct ospf_area *a1, struct ospf_area *a2)
return 0; return 0;
} }
/* Allocate new ospf structure. */ struct ospf *ospf_new_alloc(unsigned short instance, const char *name)
static struct ospf *ospf_new(unsigned short instance, const char *name)
{ {
int i; int i;
struct vrf *vrf = NULL; struct vrf *vrf = NULL;
@ -340,8 +353,6 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT;
new->maxage_lsa = route_table_init(); new->maxage_lsa = route_table_init();
new->t_maxage_walker = NULL; new->t_maxage_walker = NULL;
thread_add_timer(master, ospf_lsa_maxage_walker, new,
OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker);
/* Distance table init. */ /* Distance table init. */
new->distance_table = route_table_init(); new->distance_table = route_table_init();
@ -349,8 +360,6 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
new->lsa_refresh_queue.index = 0; new->lsa_refresh_queue.index = 0;
new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT;
new->t_lsa_refresher = NULL; new->t_lsa_refresher = NULL;
thread_add_timer(master, ospf_lsa_refresh_walker, new,
new->lsa_refresh_interval, &new->t_lsa_refresher);
new->lsa_refresher_started = monotime(NULL); new->lsa_refresher_started = monotime(NULL);
new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1); new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1);
@ -368,6 +377,17 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
QOBJ_REG(new, ospf); QOBJ_REG(new, ospf);
new->fd = -1; new->fd = -1;
return new;
}
/* Allocate new ospf structure. */
static struct ospf *ospf_new(unsigned short instance, const char *name)
{
struct ospf *new;
new = ospf_new_alloc(instance, name);
if ((ospf_sock_init(new)) < 0) { if ((ospf_sock_init(new)) < 0) {
if (new->vrf_id != VRF_UNKNOWN) if (new->vrf_id != VRF_UNKNOWN)
flog_err( flog_err(
@ -376,6 +396,12 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
__func__); __func__);
return new; return new;
} }
thread_add_timer(master, ospf_lsa_maxage_walker, new,
OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker);
thread_add_timer(master, ospf_lsa_refresh_walker, new,
new->lsa_refresh_interval, &new->t_lsa_refresher);
thread_add_read(master, ospf_read, new, new->fd, &new->t_read); thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
return new; return new;
@ -887,8 +913,7 @@ static void ospf_finish_final(struct ospf *ospf)
/* allocate new OSPF Area object */ /* allocate new OSPF Area object */
static struct ospf_area *ospf_area_new(struct ospf *ospf, struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id)
struct in_addr area_id)
{ {
struct ospf_area *new; struct ospf_area *new;
@ -1035,7 +1060,8 @@ void ospf_area_del_if(struct ospf_area *area, struct ospf_interface *oi)
} }
static void add_ospf_interface(struct connected *co, struct ospf_area *area) struct ospf_interface *add_ospf_interface(struct connected *co,
struct ospf_area *area)
{ {
struct ospf_interface *oi; struct ospf_interface *oi;
@ -1072,6 +1098,8 @@ static void add_ospf_interface(struct connected *co, struct ospf_area *area)
if ((area->ospf->router_id.s_addr != INADDR_ANY) if ((area->ospf->router_id.s_addr != INADDR_ANY)
&& if_is_operative(co->ifp)) && if_is_operative(co->ifp))
ospf_if_up(oi); ospf_if_up(oi);
return oi;
} }
static void update_redistributed(struct ospf *ospf, int add_to_ospf) static void update_redistributed(struct ospf *ospf, int add_to_ospf)

View file

@ -23,6 +23,7 @@
#define _ZEBRA_OSPFD_H #define _ZEBRA_OSPFD_H
#include <zebra.h> #include <zebra.h>
#include "typesafe.h"
#include "qobj.h" #include "qobj.h"
#include "libospf.h" #include "libospf.h"
#include "ldp_sync.h" #include "ldp_sync.h"
@ -374,10 +375,46 @@ struct ospf {
/* MPLS LDP-IGP Sync */ /* MPLS LDP-IGP Sync */
struct ldp_sync_info_cmd ldp_sync_cmd; struct ldp_sync_info_cmd ldp_sync_cmd;
/* TI-LFA support for all interfaces. */
bool ti_lfa_enabled;
QOBJ_FIELDS QOBJ_FIELDS
}; };
DECLARE_QOBJ_TYPE(ospf) DECLARE_QOBJ_TYPE(ospf)
enum ospf_ti_lfa_node_type {
OSPF_TI_LFA_UNDEFINED_NODE,
OSPF_TI_LFA_PQ_NODE,
OSPF_TI_LFA_P_NODE,
OSPF_TI_LFA_Q_NODE,
};
struct ospf_ti_lfa_node_info {
struct vertex *node;
enum ospf_ti_lfa_node_type type;
struct in_addr nexthop;
};
PREDECL_RBTREE_UNIQ(q_spaces)
struct q_space {
struct vertex *root;
struct list *vertex_list;
struct mpls_label_stack *label_stack;
struct in_addr nexthop;
struct q_spaces_item q_spaces_item;
};
PREDECL_RBTREE_UNIQ(p_spaces)
struct p_space {
struct vertex *root;
struct router_lsa_link *protected_link;
struct q_spaces_head *q_spaces;
struct list *vertex_list;
struct vertex *pc_spf;
struct list *pc_vertex_list;
struct p_spaces_item p_spaces_item;
};
/* OSPF area structure. */ /* OSPF area structure. */
struct ospf_area { struct ospf_area {
/* OSPF instance. */ /* OSPF instance. */
@ -475,6 +512,12 @@ struct ospf_area {
bool spf_root_node; /* flag for checking if the calculating node is the bool spf_root_node; /* flag for checking if the calculating node is the
root node of the SPF tree */ root node of the SPF tree */
/* TI-LFA protected link for SPF calculations */
struct router_lsa_link *spf_protected_link;
/* P/Q spaces for TI-LFA */
struct p_spaces_head *p_spaces;
/* Threads. */ /* Threads. */
struct thread *t_stub_router; /* Stub-router timer */ struct thread *t_stub_router; /* Stub-router timer */
struct thread *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */ struct thread *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */
@ -566,6 +609,7 @@ extern const char *ospf_redist_string(unsigned int route_type);
extern struct ospf *ospf_lookup_instance(unsigned short); extern struct ospf *ospf_lookup_instance(unsigned short);
extern struct ospf *ospf_get(unsigned short instance, const char *name, extern struct ospf *ospf_get(unsigned short instance, const char *name,
bool *created); bool *created);
extern struct ospf *ospf_new_alloc(unsigned short instance, const char *name);
extern struct ospf *ospf_get_instance(unsigned short, bool *created); extern struct ospf *ospf_get_instance(unsigned short, bool *created);
extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance,
const char *name); const char *name);
@ -619,6 +663,8 @@ extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup_next(struct ospf *,
struct in_addr *, int); struct in_addr *, int);
extern int ospf_oi_count(struct interface *); extern int ospf_oi_count(struct interface *);
extern struct ospf_area *ospf_area_new(struct ospf *ospf,
struct in_addr area_id);
extern struct ospf_area *ospf_area_get(struct ospf *, struct in_addr); extern struct ospf_area *ospf_area_get(struct ospf *, struct in_addr);
extern void ospf_area_check_free(struct ospf *, struct in_addr); extern void ospf_area_check_free(struct ospf *, struct in_addr);
extern struct ospf_area *ospf_area_lookup_by_area_id(struct ospf *, extern struct ospf_area *ospf_area_lookup_by_area_id(struct ospf *,
@ -640,4 +686,11 @@ const char *ospf_vrf_id_to_name(vrf_id_t vrf_id);
int ospf_area_nssa_no_summary_set(struct ospf *, struct in_addr); int ospf_area_nssa_no_summary_set(struct ospf *, struct in_addr);
const char *ospf_get_name(const struct ospf *ospf); const char *ospf_get_name(const struct ospf *ospf);
extern struct ospf_interface *add_ospf_interface(struct connected *co,
struct ospf_area *area);
extern int p_spaces_compare_func(const struct p_space *a,
const struct p_space *b);
extern int q_spaces_compare_func(const struct q_space *a,
const struct q_space *b);
#endif /* _ZEBRA_OSPFD_H */ #endif /* _ZEBRA_OSPFD_H */

View file

@ -52,6 +52,7 @@ ospfd_libfrrospf_a_SOURCES = \
ospfd/ospf_route.c \ ospfd/ospf_route.c \
ospfd/ospf_routemap.c \ ospfd/ospf_routemap.c \
ospfd/ospf_spf.c \ ospfd/ospf_spf.c \
ospfd/ospf_ti_lfa.c \
ospfd/ospf_sr.c \ ospfd/ospf_sr.c \
ospfd/ospf_te.c \ ospfd/ospf_te.c \
ospfd/ospf_vty.c \ ospfd/ospf_vty.c \
@ -100,6 +101,7 @@ noinst_HEADERS += \
ospfd/ospf_ri.h \ ospfd/ospf_ri.h \
ospfd/ospf_route.h \ ospfd/ospf_route.h \
ospfd/ospf_spf.h \ ospfd/ospf_spf.h \
ospfd/ospf_ti_lfa.h \
ospfd/ospf_sr.h \ ospfd/ospf_sr.h \
ospfd/ospf_te.h \ ospfd/ospf_te.h \
ospfd/ospf_vty.h \ ospfd/ospf_vty.h \

3
tests/ospfd/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/*_afl/*
test_ospf_spf
core

169
tests/ospfd/common.c Normal file
View file

@ -0,0 +1,169 @@
#include <zebra.h>
#include "lib/stream.h"
#include "lib/vty.h"
#include "lib/mpls.h"
#include "lib/if.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_spf.h"
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_lsa.h"
#include "ospfd/ospf_lsdb.h"
#include "ospfd/ospf_interface.h"
#include "ospfd/ospf_sr.h"
#include "common.h"
struct thread_master *master;
struct zebra_privs_t ospfd_privs;
struct ospf_topology *test_find_topology(const char *name)
{
if (strmatch(name, "topo1"))
return &topo1;
else if (strmatch(name, "topo2"))
return &topo2;
else if (strmatch(name, "topo3"))
return &topo3;
return NULL;
}
struct ospf_test_node *test_find_node(struct ospf_topology *topology,
const char *hostname)
{
for (int i = 0; topology->nodes[i].hostname[0]; i++)
if (strmatch(hostname, topology->nodes[i].hostname))
return &topology->nodes[i];
return NULL;
}
static void inject_router_lsa(struct vty *vty, struct ospf *ospf,
struct ospf_topology *topology,
struct ospf_test_node *root,
struct ospf_test_node *tnode)
{
struct ospf_area *area;
struct in_addr router_id;
struct in_addr adj_router_id;
struct prefix_ipv4 prefix;
struct in_addr data;
struct stream *s;
struct lsa_header *lsah;
struct ospf_lsa *new;
int length;
unsigned long putp;
uint16_t link_count;
struct ospf_test_node *tfound_adj_node;
struct ospf_test_adj *tadj;
bool is_self_lsa = false;
area = ospf->backbone;
inet_aton(tnode->router_id, &router_id);
if (strncmp(root->router_id, tnode->router_id, 256) == 0)
is_self_lsa = true;
s = stream_new(OSPF_MAX_LSA_SIZE);
lsa_header_set(s, LSA_OPTIONS_GET(area) | LSA_OPTIONS_NSSA_GET(area),
OSPF_ROUTER_LSA, router_id, router_id);
stream_putc(s, router_lsa_flags(area));
stream_putc(s, 0);
putp = stream_get_endp(s);
stream_putw(s, 0);
for (link_count = 0; tnode->adjacencies[link_count].hostname[0];
link_count++) {
tadj = &tnode->adjacencies[link_count];
tfound_adj_node = test_find_node(topology, tadj->hostname);
str2prefix_ipv4(tnode->adjacencies[link_count].network,
&prefix);
inet_aton(tfound_adj_node->router_id, &adj_router_id);
data.s_addr = prefix.prefix.s_addr;
link_info_set(&s, adj_router_id, data,
LSA_LINK_TYPE_POINTOPOINT, 0, tadj->metric);
masklen2ip(prefix.prefixlen, &data);
link_info_set(&s, prefix.prefix, data, LSA_LINK_TYPE_STUB, 0,
tadj->metric);
}
/* Don't forget the node itself (just a stub) */
str2prefix_ipv4(tnode->router_id, &prefix);
data.s_addr = 0xffffffff;
link_info_set(&s, prefix.prefix, data, LSA_LINK_TYPE_STUB, 0, 0);
/* Take twice the link count (for P2P and stub) plus the local stub */
stream_putw_at(s, putp, (2 * link_count) + 1);
length = stream_get_endp(s);
lsah = (struct lsa_header *)STREAM_DATA(s);
lsah->length = htons(length);
new = ospf_lsa_new_and_data(length);
new->area = area;
new->vrf_id = area->ospf->vrf_id;
if (is_self_lsa)
SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED);
memcpy(new->data, lsah, length);
stream_free(s);
ospf_lsdb_add(area->lsdb, new);
if (is_self_lsa) {
ospf_lsa_unlock(&area->router_lsa_self);
area->router_lsa_self = ospf_lsa_lock(new);
}
}
static void inject_sr_db_entry(struct vty *vty, struct ospf_test_node *tnode)
{
struct in_addr router_id;
struct sr_node *srn;
struct sr_prefix *srp;
inet_aton(tnode->router_id, &router_id);
srn = ospf_sr_node_create(&router_id);
srn->srgb.range_size = 8000;
srn->srgb.lower_bound = 16000;
srn->msd = 16;
srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
srp->adv_router = router_id;
srp->sid = tnode->label;
srp->srn = srn;
listnode_add(srn->ext_prefix, srp);
}
int topology_load(struct vty *vty, struct ospf_topology *topology,
struct ospf_test_node *root, struct ospf *ospf)
{
struct ospf_test_node *tnode;
for (int i = 0; topology->nodes[i].hostname[0]; i++) {
tnode = &topology->nodes[i];
/* Inject a router LSA for each node, used for SPF */
inject_router_lsa(vty, ospf, topology, root, tnode);
/*
* SR information could also be inected via LSAs, but directly
* filling the SR DB with labels is just easier.
*/
inject_sr_db_entry(vty, tnode);
}
return 0;
}

38
tests/ospfd/common.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef _COMMON_OSPF_H
#define _COMMON_OSPF_H
#define MAX_ADJACENCIES 8
#define MAX_NODES 12
struct ospf_test_adj {
char hostname[256];
char network[256];
uint32_t metric;
};
struct ospf_test_node {
char hostname[256];
const char *router_id;
mpls_label_t label;
struct ospf_test_adj adjacencies[MAX_ADJACENCIES + 1];
};
struct ospf_topology {
struct ospf_test_node nodes[MAX_NODES + 1];
};
/* Prototypes. */
extern struct ospf_topology *test_find_topology(const char *name);
extern struct ospf_test_node *test_find_node(struct ospf_topology *topology,
const char *hostname);
extern int topology_load(struct vty *vty, struct ospf_topology *topology,
struct ospf_test_node *root, struct ospf *ospf);
/* Global variables. */
extern struct thread_master *master;
extern struct ospf_topology topo1;
extern struct ospf_topology topo2;
extern struct ospf_topology topo3;
extern struct zebra_privs_t ospfd_privs;
#endif /* _COMMON_OSPF_H */

263
tests/ospfd/test_ospf_spf.c Normal file
View file

@ -0,0 +1,263 @@
#include <zebra.h>
#include "getopt.h"
#include "thread.h"
#include <lib/version.h>
#include "vty.h"
#include "command.h"
#include "log.h"
#include "vrf.h"
#include "table.h"
#include "mpls.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_asbr.h"
#include "ospfd/ospf_lsa.h"
#include "ospfd/ospf_route.h"
#include "ospfd/ospf_spf.h"
#include "ospfd/ospf_ti_lfa.h"
#include "ospfd/ospf_vty.h"
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_sr.h"
#include "common.h"
DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item,
p_spaces_compare_func)
DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item,
q_spaces_compare_func)
static struct ospf *test_init(struct ospf_test_node *root)
{
struct ospf *ospf;
struct ospf_area *area;
struct in_addr area_id;
struct in_addr router_id;
ospf = ospf_new_alloc(0, NULL);
area_id.s_addr = OSPF_AREA_BACKBONE;
area = ospf_area_new(ospf, area_id);
listnode_add_sort(ospf->areas, area);
inet_aton(root->router_id, &router_id);
ospf->router_id = router_id;
ospf->router_id_static = router_id;
return ospf;
}
static void test_run_spf(struct vty *vty, struct ospf *ospf)
{
struct route_table *new_table, *new_rtrs;
struct ospf_area *area;
struct p_space *p_space;
struct q_space *q_space;
char buf[MPLS_LABEL_STRLEN];
/* Just use the backbone for testing */
area = ospf->backbone;
new_table = route_table_init();
new_rtrs = route_table_init();
/* dryrun true, root_node false */
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
true, false);
ospf_spf_print(vty, area->spf, 0);
ospf_route_table_print(vty, new_table);
/* TI-LFA testrun */
ospf_ti_lfa_generate_p_spaces(area);
ospf_ti_lfa_insert_backup_paths(area, new_table);
/* Print P/Q space information */
frr_each(p_spaces, area->p_spaces, p_space) {
vty_out(vty, "\n\nP Space for root %pI4 and link %pI4:\n",
&p_space->root->id, &p_space->protected_link->link_id);
ospf_spf_print(vty, p_space->root, 0);
frr_each(q_spaces, p_space->q_spaces, q_space) {
vty_out(vty, "\nQ Space for destination %pI4:\n",
&q_space->root->id);
ospf_spf_print(vty, q_space->root, 0);
if (q_space->label_stack) {
mpls_label2str(q_space->label_stack->num_labels,
q_space->label_stack->label, buf,
MPLS_LABEL_STRLEN, true);
vty_out(vty, "\nLabel stack: %s\n", buf);
} else {
vty_out(vty, "\nLabel stack not generated!\n");
}
}
vty_out(vty,
"\nPost-convergence path for root %pI4 and link %pI4:\n",
&p_space->root->id, &p_space->protected_link->link_id);
ospf_spf_print(vty, p_space->pc_spf, 0);
}
/* Cleanup */
ospf_ti_lfa_free_p_spaces(area);
ospf_spf_cleanup(area->spf, area->spf_vertex_list);
/*
* Print the new routing table which is augmented with TI-LFA backup
* paths (label stacks).
*/
vty_out(vty, "\nNew route table:\n");
ospf_route_table_print(vty, new_table);
}
static int test_run(struct vty *vty, struct ospf_topology *topology,
struct ospf_test_node *root)
{
struct ospf *ospf;
ospf = test_init(root);
/* Inject LSAs into the OSPF backbone according to the topology */
if (topology_load(vty, topology, root, ospf)) {
vty_out(vty, "%% Failed to load topology\n");
return CMD_WARNING;
}
vty_out(vty, "\n");
show_ip_ospf_database_summary(vty, ospf, 0, NULL);
test_run_spf(vty, ospf);
return 0;
}
DEFUN(test_ospf, test_ospf_cmd, "test ospf topology WORD root HOSTNAME",
"Test mode\n"
"Choose OSPF for SPF testing\n"
"Network topology to choose\n"
"Name of the network topology to choose\n"
"Root node to choose\n"
"Hostname of the root node to choose\n")
{
struct ospf_topology *topology;
struct ospf_test_node *root;
int idx = 0;
/* Parse topology. */
argv_find(argv, argc, "topology", &idx);
topology = test_find_topology(argv[idx + 1]->arg);
if (!topology) {
vty_out(vty, "%% Topology not found\n");
return CMD_WARNING;
}
argv_find(argv, argc, "root", &idx);
root = test_find_node(topology, argv[idx + 1]->arg);
if (!root) {
vty_out(vty, "%% Root not found\n");
return CMD_WARNING;
}
return test_run(vty, topology, root);
}
static void vty_do_exit(int isexit)
{
printf("\nend.\n");
cmd_terminate();
vty_terminate();
thread_master_free(master);
if (!isexit)
exit(0);
}
struct option longopts[] = {{"help", no_argument, NULL, 'h'},
{"debug", no_argument, NULL, 'd'},
{0} };
/* Help information display. */
static void usage(char *progname, int status)
{
if (status != 0)
fprintf(stderr, "Try `%s --help' for more information.\n",
progname);
else {
printf("Usage : %s [OPTION...]\n\
ospfd SPF test program.\n\n\
-u, --debug Enable debugging\n\
-h, --help Display this help and exit\n\
\n\
Report bugs to %s\n",
progname, FRR_BUG_ADDRESS);
}
exit(status);
}
int main(int argc, char **argv)
{
char *p;
char *progname;
struct thread thread;
bool debug = false;
/* Set umask before anything for security */
umask(0027);
/* get program name */
progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
while (1) {
int opt;
opt = getopt_long(argc, argv, "hd", longopts, 0);
if (opt == EOF)
break;
switch (opt) {
case 0:
break;
case 'd':
debug = true;
break;
case 'h':
usage(progname, 0);
break;
default:
usage(progname, 1);
break;
}
}
/* master init. */
master = thread_master_create(NULL);
/* Library inits. */
cmd_init(1);
vty_init(master, false);
if (debug)
zlog_aux_init("NONE: ", LOG_DEBUG);
else
zlog_aux_init("NONE: ", ZLOG_DISABLED);
/* Install test command. */
install_element(VIEW_NODE, &test_ospf_cmd);
/* needed for SR DB init */
ospf_vty_init();
ospf_sr_init();
term_debug_ospf_event = 1;
/* Read input from .in file. */
vty_stdio(vty_do_exit);
/* Fetch next active thread. */
while (thread_fetch(master, &thread))
thread_call(&thread);
/* Not reached. */
exit(0);
}

View file

@ -0,0 +1,6 @@
import frrtest
class TestOspfSPF(frrtest.TestMultiOut):
program = './test_ospf_spf'
TestOspfSPF.exit_cleanly()

298
tests/ospfd/topologies.c Normal file
View file

@ -0,0 +1,298 @@
#include <zebra.h>
#include "mpls.h"
#include "if.h"
#include "ospfd/ospfd.h"
#include "common.h"
/*
* +---------+ +---------+
* | | | |
* | RT1 |eth-rt2 eth-rt1| RT2 |
* | 1.1.1.1 +---------------------+ 2.2.2.2 |
* | | 10.0.1.0/24 | |
* +---------+ +---------+
* |eth-rt3 eth-rt3|
* | |
* |10.0.3.0/24 |
* | |
* |eth-rt1 |
* +---------+ |
* | |eth-rt2 10.0.2.0/24|
* | RT3 +--------------------------+
* | 3.3.3.3 |
* | |
* +---------+
*
* P and Q spaces overlap here, hence just one P/Q node regardless of which
* link is protected. Hence the backup label stack just has one label.
*/
struct ospf_topology topo1 = {
.nodes =
{
{
.hostname = "rt1",
.router_id = "1.1.1.1",
.label = 10,
.adjacencies =
{
{
.hostname = "rt2",
.network =
"10.0.1.1/24",
.metric = 10,
},
{
.hostname = "rt3",
.network =
"10.0.3.1/24",
.metric = 10,
},
},
},
{
.hostname = "rt2",
.router_id = "2.2.2.2",
.label = 20,
.adjacencies =
{
{
.hostname = "rt1",
.network =
"10.0.1.2/24",
.metric = 10,
},
{
.hostname = "rt3",
.network =
"10.0.2.1/24",
.metric = 10,
},
},
},
{
.hostname = "rt3",
.router_id = "3.3.3.3",
.label = 30,
.adjacencies =
{
{
.hostname = "rt1",
.network =
"10.0.3.2/24",
.metric = 10,
},
{
.hostname = "rt2",
.network =
"10.0.2.2/24",
.metric = 10,
},
},
},
},
};
/*
* +---------+ +---------+
* | | | |
* | RT1 |eth-rt2 eth-rt1| RT2 |
* | 1.1.1.1 +---------------------+ 2.2.2.2 |
* | | 10.0.1.0/24 (10) | |
* +---------+ +---------+
* |eth-rt3 eth-rt3|
* | |
* |10.0.3.0/24 (30) |
* | |
* |eth-rt1 |
* +---------+ |
* | |eth-rt2 10.0.2.0/24|(10)
* | RT3 +--------------------------+
* | 3.3.3.3 |
* | |
* +---------+
*
* Regarding the subnet 10.0.1.0/24, the P space of RT1 is just RT1 itself
* while the Q space of RT3 consists of RT3 and RT2. Hence the P and Q
* nodes are disjunct (tricky: the root node is the P node here). For the
* backup label stack just one label is necessary.
*/
struct ospf_topology topo2 = {
.nodes =
{
{
.hostname = "rt1",
.router_id = "1.1.1.1",
.label = 10,
.adjacencies =
{
{
.hostname = "rt2",
.network =
"10.0.1.1/24",
.metric = 10,
},
{
.hostname = "rt3",
.network =
"10.0.3.1/24",
.metric = 30,
},
},
},
{
.hostname = "rt2",
.router_id = "2.2.2.2",
.label = 20,
.adjacencies =
{
{
.hostname = "rt1",
.network =
"10.0.1.2/24",
.metric = 10,
},
{
.hostname = "rt3",
.network =
"10.0.2.1/24",
.metric = 10,
},
},
},
{
.hostname = "rt3",
.router_id = "3.3.3.3",
.label = 30,
.adjacencies =
{
{
.hostname = "rt1",
.network =
"10.0.3.2/24",
.metric = 30,
},
{
.hostname = "rt2",
.network =
"10.0.2.2/24",
.metric = 10,
},
},
},
},
};
/*
* +---------+ +---------+
* | | | |
* | RT1 |eth-rt4 eth-rt1| RT4 |
* | 1.1.1.1 +---------------------+ 4.4.4.4 |
* | | 10.0.4.0/24 (10) | |
* +---------+ +---------+
* |eth-rt2 eth-rt3|
* | |
* |10.0.1.0/24 (10) |
* | 10.0.3.0/24 (10) |
* |eth-rt1 eth-rt4|
* +---------+ +---------+
* | |eth-rt3 eth-rt2| |
* | RT2 +---------------------+ RT3 |
* | 2.2.2.2 | 10.0.2.0/24 (20) | 3.3.3.3 |
* | | | |
* +---------+ +---------+
*
* Regarding the protected subnet 10.0.4.0/24, the P and Q spaces for root RT1
* and destination RT4 are disjunct and the P node is RT2 while RT3 is the Q
* node. Hence the backup label stack here is 16020/16030. Note that here the
* P and Q nodes are neither the root nor the destination nodes, so this is a
* case where you really need a label stack consisting of two labels.
*/
struct ospf_topology topo3 = {
.nodes =
{
{
.hostname = "rt1",
.router_id = "1.1.1.1",
.label = 10,
.adjacencies =
{
{
.hostname = "rt2",
.network =
"10.0.1.1/24",
.metric = 10,
},
{
.hostname = "rt4",
.network =
"10.0.4.1/24",
.metric = 10,
},
},
},
{
.hostname = "rt2",
.router_id = "2.2.2.2",
.label = 20,
.adjacencies =
{
{
.hostname = "rt1",
.network =
"10.0.1.2/24",
.metric = 10,
},
{
.hostname = "rt3",
.network =
"10.0.2.1/24",
.metric = 20,
},
},
},
{
.hostname = "rt3",
.router_id = "3.3.3.3",
.label = 30,
.adjacencies =
{
{
.hostname = "rt2",
.network =
"10.0.2.2/24",
.metric = 20,
},
{
.hostname = "rt4",
.network =
"10.0.3.1/24",
.metric = 10,
},
},
},
{
.hostname = "rt4",
.router_id = "4.4.4.4",
.label = 40,
.adjacencies =
{
{
.hostname = "rt1",
.network =
"10.0.4.2/24",
.metric = 10,
},
{
.hostname = "rt3",
.network =
"10.0.3.2/24",
.metric = 10,
},
},
},
},
};

View file

@ -31,6 +31,14 @@ TESTS_ISISD =
IGNORE_ISISD = --ignore=isisd/ IGNORE_ISISD = --ignore=isisd/
endif endif
if OSPFD
TESTS_OSPFD = \
tests/ospfd/test_ospf_spf \
# end
else
TESTS_OSPFD =
endif
if OSPF6D if OSPF6D
TESTS_OSPF6D = \ TESTS_OSPF6D = \
tests/ospf6d/test_lsdb \ tests/ospf6d/test_lsdb \
@ -90,6 +98,7 @@ check_PROGRAMS = \
tests/lib/northbound/test_oper_data \ tests/lib/northbound/test_oper_data \
$(TESTS_BGPD) \ $(TESTS_BGPD) \
$(TESTS_ISISD) \ $(TESTS_ISISD) \
$(TESTS_OSPFD) \
$(TESTS_OSPF6D) \ $(TESTS_OSPF6D) \
$(TESTS_ZEBRA) \ $(TESTS_ZEBRA) \
# end # end
@ -126,6 +135,7 @@ noinst_HEADERS += \
tests/lib/cli/common_cli.h \ tests/lib/cli/common_cli.h \
tests/lib/test_typelist.h \ tests/lib/test_typelist.h \
tests/isisd/test_common.h \ tests/isisd/test_common.h \
tests/ospfd/common.h \
# end # end
# #
@ -145,6 +155,7 @@ TESTS_CFLAGS = \
ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP) ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) -lm BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) -lm
ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD) ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD)
OSPFD_TEST_LDADD = ospfd/libfrrospf.a $(ALL_TESTS_LDADD)
OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD) OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD)
ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD) ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD)
@ -213,6 +224,11 @@ tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD)
tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c
tests_ospfd_test_ospf_spf_CFLAGS = $(TESTS_CFLAGS)
tests_ospfd_test_ospf_spf_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_ospfd_test_ospf_spf_LDADD = $(OSPFD_TEST_LDADD)
tests_ospfd_test_ospf_spf_SOURCES = tests/ospfd/test_ospf_spf.c tests/ospfd/common.c tests/ospfd/topologies.c
tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR) tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR)
tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_cxxcompat_SOURCES = tests/lib/cxxcompat.c tests_lib_cxxcompat_SOURCES = tests/lib/cxxcompat.c
@ -370,6 +386,7 @@ EXTRA_DIST += \
tests/isisd/test_isis_spf.in \ tests/isisd/test_isis_spf.in \
tests/isisd/test_isis_spf.refout \ tests/isisd/test_isis_spf.refout \
tests/isisd/test_isis_vertex_queue.py \ tests/isisd/test_isis_vertex_queue.py \
tests/ospfd/test_ospf_spf.py \
tests/lib/cli/test_commands.in \ tests/lib/cli/test_commands.in \
tests/lib/cli/test_commands.py \ tests/lib/cli/test_commands.py \
tests/lib/cli/test_commands.refout \ tests/lib/cli/test_commands.refout \