forked from Mirror/frr
ospfd: TI-LFA basic infrastructure and algorithms
Signed-off-by: GalaxyGorilla <sascha@netdef.org>
This commit is contained in:
parent
6351ea6ebf
commit
7fd0729f76
|
@ -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
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 *,
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
350
ospfd/ospf_spf.c
350
ospfd/ospf_spf.c
|
@ -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,14 +1462,17 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
if (spf)
|
||||||
ospf_canonical_nexthops_free(spf);
|
ospf_canonical_nexthops_free(spf);
|
||||||
|
|
||||||
/* Free SPF vertices list with deconstructor ospf_vertex_free. */
|
/* Free SPF vertices list with deconstructor ospf_vertex_free. */
|
||||||
|
if (vertex_list)
|
||||||
list_delete(&vertex_list);
|
list_delete(&vertex_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
|
||||||
|
struct route_table *new_table,
|
||||||
|
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);
|
||||||
|
|
||||||
/* 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);
|
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_areas(struct ospf *ospf, struct route_table *new_table,
|
||||||
struct route_table *new_rtrs, bool is_dry_run,
|
struct route_table *new_rtrs)
|
||||||
bool is_root_node)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,14 +47,14 @@ 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_nexthop *local_nexthop; /* local nexthop of the parent */
|
||||||
struct vertex *parent; /* parent vertex */
|
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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
548
ospfd/ospf_ti_lfa.c
Normal 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
35
ospfd/ospf_ti_lfa.h
Normal 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 */
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
3
tests/ospfd/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/*_afl/*
|
||||||
|
test_ospf_spf
|
||||||
|
core
|
169
tests/ospfd/common.c
Normal file
169
tests/ospfd/common.c
Normal 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
38
tests/ospfd/common.h
Normal 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
263
tests/ospfd/test_ospf_spf.c
Normal 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);
|
||||||
|
}
|
6
tests/ospfd/test_ospf_spf.py
Normal file
6
tests/ospfd/test_ospf_spf.py
Normal 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
298
tests/ospfd/topologies.c
Normal 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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -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 \
|
||||||
|
|
Loading…
Reference in a new issue