]> git.puffer.fish Git - mirror/frr.git/commitdiff
ospfd: TI-LFA basic infrastructure and algorithms
authorGalaxyGorilla <sascha@netdef.org>
Wed, 5 Aug 2020 08:44:21 +0000 (08:44 +0000)
committerGalaxyGorilla <sascha@netdef.org>
Tue, 19 Jan 2021 15:32:13 +0000 (15:32 +0000)
Signed-off-by: GalaxyGorilla <sascha@netdef.org>
26 files changed:
doc/user/ospfd.rst
ospfd/ospf_lsa.c
ospfd/ospf_lsa.h
ospfd/ospf_memory.c
ospfd/ospf_memory.h
ospfd/ospf_route.c
ospfd/ospf_route.h
ospfd/ospf_spf.c
ospfd/ospf_spf.h
ospfd/ospf_sr.c
ospfd/ospf_sr.h
ospfd/ospf_ti_lfa.c [new file with mode: 0644]
ospfd/ospf_ti_lfa.h [new file with mode: 0644]
ospfd/ospf_vty.c
ospfd/ospf_vty.h
ospfd/ospf_zebra.c
ospfd/ospfd.c
ospfd/ospfd.h
ospfd/subdir.am
tests/ospfd/.gitignore [new file with mode: 0644]
tests/ospfd/common.c [new file with mode: 0644]
tests/ospfd/common.h [new file with mode: 0644]
tests/ospfd/test_ospf_spf.c [new file with mode: 0644]
tests/ospfd/test_ospf_spf.py [new file with mode: 0644]
tests/ospfd/topologies.c [new file with mode: 0644]
tests/subdir.am

index 7184a0e19757fc6ccd68c084618a4c5d9c563922..b45427d1282876bb7f8ce8389bff016c5fc47d4a 100644 (file)
@@ -1233,6 +1233,20 @@ Summary Route will be originated on-behalf of all matched external LSAs.
    Show configuration for display all configured summary routes with
    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
 ==============
 
index 4c9db16c6b8ec46dea633ad8cf636d872a578002..dba52889b784c47affe877dfdbea68e237346908 100644 (file)
@@ -337,7 +337,7 @@ void lsa_header_set(struct stream *s, uint8_t options, uint8_t type,
 
 /* router-LSA related functions. */
 /* 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;
 
@@ -420,9 +420,8 @@ static uint16_t ospf_link_cost(struct ospf_interface *oi)
 }
 
 /* Set a link information. */
-static char link_info_set(struct stream **s, struct in_addr id,
-                         struct in_addr data, uint8_t type, uint8_t tos,
-                         uint16_t cost)
+char link_info_set(struct stream **s, struct in_addr id, struct in_addr data,
+                  uint8_t type, uint8_t tos, uint16_t cost)
 {
        /* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits
         * 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. */
-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;
        uint16_t cnt;
index c5de287948103b0bd61d8dec551a43d96f73dcd5..f2a0d36e7e04810fdd36cc8fac692cf089d0e62e 100644 (file)
@@ -260,6 +260,8 @@ extern struct lsa_header *ospf_lsa_data_dup(struct lsa_header *);
 extern void ospf_lsa_data_free(struct lsa_header *);
 
 /* 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_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_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 int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *);
 extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *,
index ae22cec414ad4f791135d32e79e87f14c3b98e6c..f4fb68cbdfb0a2438b77e08147a225e891148a92 100644 (file)
@@ -58,3 +58,5 @@ DEFINE_MTYPE(OSPFD, OSPF_EXT_PARAMS, "OSPF Extended parameters")
 DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters")
 DEFINE_MTYPE(OSPFD, OSPF_GR_HELPER, "OSPF Graceful Restart Helper")
 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")
index 624b1d33063c0d7adbc27ca79840a7d0259af784..42bc8d7b77751c6b3996adc13c6a02edc467c9c8 100644 (file)
@@ -57,5 +57,7 @@ DECLARE_MTYPE(OSPF_SR_PARAMS)
 DECLARE_MTYPE(OSPF_EXT_PARAMS)
 DECLARE_MTYPE(OSPF_GR_HELPER)
 DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR)
+DECLARE_MTYPE(OSPF_P_SPACE)
+DECLARE_MTYPE(OSPF_Q_SPACE)
 
 #endif /* _QUAGGA_OSPF_MEMORY_H */
index 590122e223a4634cd847c24ad0f974a9eee59ce9..8079eeefe9c17becf9884da6491b6915069f0c17 100644 (file)
@@ -140,6 +140,35 @@ static int ospf_route_exist_new_table(struct route_table *rt,
        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,
    then return 1, otherwise return 0. */
 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;
                                if (op->ifindex != newop->ifindex)
                                        return 0;
+
+                               /* check TI-LFA backup paths */
+                               if (!ospf_route_backup_path_same(&op->srni,
+                                                                &newop->srni))
+                                       return 0;
                        }
                        return 1;
                } else if (prefix_same(&rn->p, (struct prefix *)prefix))
@@ -664,38 +698,6 @@ void ospf_route_table_dump(struct route_table *rt)
        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.
    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,
@@ -802,6 +804,7 @@ void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area,
                    || area->spf_dry_run) {
                        path = ospf_path_new();
                        path->nexthop = nexthop->router;
+                       path->adv_router = v->id;
 
                        if (oi) {
                                path->ifindex = oi->ifp->ifindex;
index c3fa5954d57e9361af9227b819a8a493433c45e9..86a14352ba2bc4d689001b3f6a49543ef8e94f28 100644 (file)
@@ -42,6 +42,10 @@ struct sr_nexthop_info {
         * or NULL if next hop is the destination of the prefix
         */
        struct sr_node *nexthop;
+
+       /* TI-LFA */
+       struct mpls_label_stack *backup_label_stack;
+       struct in_addr backup_nexthop;
 };
 
 /* OSPF Path. */
index 4665f53edbdaf648a062676fc5827109289c9038..6023b85a572a5ec031cefa75c1b749e39f62c0ce 100644 (file)
@@ -46,6 +46,7 @@
 #include "ospfd/ospf_abr.h"
 #include "ospfd/ospf_dump.h"
 #include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ti_lfa.h"
 #include "ospfd/ospf_errors.h"
 
 /* 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) {
                                vertex_nexthop_free(vp->nexthop);
                                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.
  */
 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;
 
@@ -163,6 +169,7 @@ static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink,
        new->parent = v;
        new->backlink = backlink;
        new->nexthop = hop;
+       new->local_nexthop = lhop;
 
        return new;
 }
@@ -172,7 +179,7 @@ static void vertex_parent_free(void *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;
        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,
                          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,
                                struct vertex_nexthop *newhop,
+                               struct vertex_nexthop *newlhop,
                                unsigned int distance)
 {
        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);
 
        return;
@@ -541,7 +738,7 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
                                             unsigned int distance, int lsa_pos)
 {
        struct listnode *node, *nnode;
-       struct vertex_nexthop *nh;
+       struct vertex_nexthop *nh, *lnh;
        struct vertex_parent *vp;
        unsigned int added = 0;
        char buf1[BUFSIZ];
@@ -703,7 +900,17 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
                                        nh = vertex_nexthop_new();
                                        nh->router = nexthop;
                                        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;
                                } else
                                        zlog_info(
@@ -733,7 +940,17 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
                                        nh = vertex_nexthop_new();
                                        nh->router = vl_data->nexthop.router;
                                        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;
                                } else
                                        zlog_info(
@@ -747,7 +964,15 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
                        nh = vertex_nexthop_new();
                        nh->router.s_addr = 0; /* Nexthop not required */
                        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;
                }
        } /* end V is the root */
@@ -780,8 +1005,18 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
                                        nh = vertex_nexthop_new();
                                        nh->router = l->link_data;
                                        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;
-                                       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
@@ -829,12 +1064,41 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
 
        for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) {
                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;
 }
 
+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).
  * 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)
                                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
                         * 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;
 
        if (v->type == OSPF_VERTEX_ROUTER) {
-               vty_out(vty, "SPF Result: depth %d [R] %pI4\n", i,
-                       &v->lsa->id);
+               vty_out(vty, "SPF Result: depth %d [R] %pI4\n", i, &v->lsa->id);
        } else {
                struct network_lsa *lsa = (struct network_lsa *)v->lsa;
                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)) {
-               vty_out(vty, " nexthop %pI4 lsa pos %d\n",
-                       &parent->nexthop->router,
-                       parent->nexthop->lsa_pos);
+               vty_out(vty,
+                       " nexthop %pI4 lsa pos %d -- local nexthop %pI4 lsa pos %d\n",
+                       &parent->nexthop->router, parent->nexthop->lsa_pos,
+                       &parent->local_nexthop->router,
+                       parent->local_nexthop->lsa_pos);
        }
 
        i++;
@@ -1128,7 +1403,9 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v,
                        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)
+                       /* 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,
                                                    parent_is_root, lsa_pos);
                        lsa_pos++;
@@ -1185,15 +1462,18 @@ void ospf_rtrs_free(struct route_table *rtrs)
 
 void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list)
 {
+
        /*
         * Free nexthop information, canonical versions of which are
         * attached the first level of router vertices attached to the
         * root vertex, see ospf_nexthop_calculation.
         */
-       ospf_canonical_nexthops_free(spf);
+       if (spf)
+               ospf_canonical_nexthops_free(spf);
 
        /* Free SPF vertices list with deconstructor ospf_vertex_free. */
-       list_delete(&vertex_list);
+       if (vertex_list)
+               list_delete(&vertex_list);
 }
 
 #if 0
@@ -1359,19 +1639,26 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("ospf_spf_calculate: Stop. %zd vertices",
                           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 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);
+       if (ospf->ti_lfa_enabled)
+               ospf_ti_lfa_compute(area, new_table);
+
+       ospf_spf_cleanup(area->spf, area->spf_vertex_list);
 }
 
-int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
-                            struct route_table *new_rtrs, bool is_dry_run,
-                            bool is_root_node)
+void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
+                             struct route_table *new_rtrs)
 {
        struct ospf_area *area;
        struct listnode *node, *nnode;
-       int areas_processed = 0;
 
        /* Calculate SPF for each area. */
        for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
@@ -1380,20 +1667,13 @@ int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
                if (ospf->backbone && ospf->backbone == area)
                        continue;
 
-               ospf_spf_calculate(area, area->router_lsa_self, new_table,
-                                  new_rtrs, is_dry_run, is_root_node);
-               areas_processed++;
+               ospf_spf_calculate_area(ospf, area, new_table, new_rtrs);
        }
 
        /* SPF for backbone, if required */
-       if (ospf->backbone) {
-               area = ospf->backbone;
-               ospf_spf_calculate(area, area->router_lsa_self, new_table,
-                                  new_rtrs, is_dry_run, is_root_node);
-               areas_processed++;
-       }
-
-       return areas_processed;
+       if (ospf->backbone)
+               ospf_spf_calculate_area(ospf, ospf->backbone, new_table,
+                                       new_rtrs);
 }
 
 /* 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 route_table *new_table, *new_rtrs;
        struct timeval start_time, spf_start_time;
-       int areas_processed;
        unsigned long ia_time, prune_time, rt_time;
        unsigned long abr_time, total_spf_time, spf_time;
        char rbuf[32]; /* reason_buf */
@@ -1418,8 +1697,7 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread)
        monotime(&spf_start_time);
        new_table = route_table_init(); /* routing table */
        new_rtrs = route_table_init();  /* ABR/ASBR routing table */
-       areas_processed = ospf_spf_calculate_areas(ospf, new_table, new_rtrs,
-                                                  false, true);
+       ospf_spf_calculate_areas(ospf, new_table, new_rtrs);
        spf_time = monotime_since(&spf_start_time, NULL);
 
        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);
                if (IS_OSPF_ABR(ospf))
                        zlog_info("                 ABR: %ld (%d areas)",
-                                 abr_time, areas_processed);
+                                 abr_time, ospf->areas->count);
                zlog_info("Reason(s) for SPF: %s", rbuf);
        }
 
index 2dc0f8b88663281acd6a302cb4104472da8317fa..c679e5e71da94ec3ba2fe43251085d027a6d625d 100644 (file)
@@ -47,15 +47,15 @@ struct vertex {
        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 in_addr router;     /* router address to send to */
        int lsa_pos; /* LSA position for resolving the interface */
 };
 
 struct vertex_parent {
-       struct vertex_nexthop *nexthop; /* nexthop address for this parent */
-       struct vertex *parent;          /* parent vertex */
+       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 */
        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_rtrs, bool is_dry_run,
                               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_rtrs,
-                                   bool is_dry_run, bool is_root_node);
+                                   struct route_table *new_rtrs);
+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_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);
 
index e2218957d2c8093bcd5bbde6f11597a782e03c68..f3bb4cef836fe90ed0b28e39e019a06a1ec296fe 100644 (file)
@@ -159,6 +159,16 @@ static struct sr_node *sr_node_new(struct in_addr *rid)
        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 */
 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;
 }
 
+/* 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 */
 static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top,
                                                  struct in_addr addr)
index 222675944d4dcf88907216c84b76a16d3b077399..13649ad4aaa48584f4d23d74df54bd0f52a75ef8 100644 (file)
@@ -361,4 +361,9 @@ extern void ospf_sr_update_local_prefix(struct interface *ifp,
                                        struct prefix *p);
 /* Segment Routing re-routing function */
 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 */
diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c
new file mode 100644 (file)
index 0000000..61c2785
--- /dev/null
@@ -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);
+}
diff --git a/ospfd/ospf_ti_lfa.h b/ospfd/ospf_ti_lfa.h
new file mode 100644 (file)
index 0000000..8f2effa
--- /dev/null
@@ -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 */
index 68ad62cda41d91b35f9d9090d3fdc44c673719f1..4c5c5234ad9714ee2186696b591af8a68f1b66be 100644 (file)
@@ -2602,6 +2602,33 @@ ALIAS(no_ospf_write_multiplier, no_write_multiplier_cmd,
       "Write multiplier\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[] = {
        "Unknown", "Standard (RFC2328)", "Alternative IBM",
        "Alternative Cisco", "Alternative Shortcut"
@@ -6681,8 +6708,8 @@ static void show_lsa_detail_adv_router(struct vty *vty, struct ospf *ospf,
                                       json_lstype);
 }
 
-static void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf,
-                                         int self, json_object *json)
+void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, int self,
+                                  json_object *json)
 {
        struct ospf_lsa *lsa;
        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);
        }
 
+       /* TI-LFA print. */
+       if (ospf->ti_lfa_enabled)
+               vty_out(vty, " fast-reroute ti-lfa\n");
+
        /* Network area print. */
        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, &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. */
        ospf_vty_if_init();
 
index 79aabe7b4ea4397b718b9e6240dbcba276ce779f..bf9c971710204b59b46e1e46361b2119cd594010 100644 (file)
@@ -54,4 +54,8 @@ extern void ospf_vty_show_init(void);
 extern void ospf_vty_clear_init(void);
 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 */
index 2d02619ae395263a911a36e80a2dbae61e851ca6..84a2b63fbf4686bd9acf1c2977cc5b4052c39c9a 100644 (file)
@@ -198,15 +198,70 @@ static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS)
        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,
                    struct ospf_route * or)
 {
        struct zapi_route api;
-       struct zapi_nexthop *api_nh;
        uint8_t distance;
        struct ospf_path *path;
        struct listnode *node;
-       int count = 0;
 
        memset(&api, 0, sizeof(api));
        api.vrf_id = ospf->vrf_id;
@@ -241,29 +296,11 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
                api.distance = distance;
        }
 
-       /* Nexthop, ifindex, distance and metric information. */
        for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) {
-               if (count >= MULTIPATH_NUM)
+               if (api.nexthop_num >= MULTIPATH_NUM)
                        break;
-               api_nh = &api.nexthops[count];
-#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;
-               count++;
+
+               ospf_zebra_add_nexthop(ospf, path, &api);
 
                if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) {
                        struct interface *ifp;
@@ -276,7 +313,6 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
                                ifp ? ifp->name : " ");
                }
        }
-       api.nexthop_num = count;
 
        zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);
 }
index bab75995b7d0dca36e2f864074f7c73d6c6647a7..a0a746488cd6d4eed569f43f1c5da0866bad0f35 100644 (file)
@@ -87,6 +87,20 @@ static void ospf_finish_final(struct ospf *);
 
 #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)
 {
        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;
 }
 
-/* Allocate new ospf structure. */
-static struct ospf *ospf_new(unsigned short instance, const char *name)
+struct ospf *ospf_new_alloc(unsigned short instance, const char *name)
 {
        int i;
        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_lsa = route_table_init();
        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. */
        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_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT;
        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->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);
 
        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 (new->vrf_id != VRF_UNKNOWN)
                        flog_err(
@@ -376,6 +396,12 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
                                __func__);
                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);
 
        return new;
@@ -887,8 +913,7 @@ static void ospf_finish_final(struct ospf *ospf)
 
 
 /* allocate new OSPF Area object */
-static struct ospf_area *ospf_area_new(struct ospf *ospf,
-                                      struct in_addr area_id)
+struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id)
 {
        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;
 
@@ -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_is_operative(co->ifp))
                ospf_if_up(oi);
+
+       return oi;
 }
 
 static void update_redistributed(struct ospf *ospf, int add_to_ospf)
index 6960d151c2213fd4a7011657eddf4845095eb979..420e6f22eb2977dc4ab305d8eb21aec46a0323a9 100644 (file)
@@ -23,6 +23,7 @@
 #define _ZEBRA_OSPFD_H
 
 #include <zebra.h>
+#include "typesafe.h"
 #include "qobj.h"
 #include "libospf.h"
 #include "ldp_sync.h"
@@ -374,10 +375,46 @@ struct ospf {
        /* MPLS LDP-IGP Sync */
        struct ldp_sync_info_cmd ldp_sync_cmd;
 
+       /* TI-LFA support for all interfaces. */
+       bool ti_lfa_enabled;
+
        QOBJ_FIELDS
 };
 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. */
 struct ospf_area {
        /* OSPF instance. */
@@ -475,6 +512,12 @@ struct ospf_area {
        bool spf_root_node; /* flag for checking if the calculating node is the
                               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. */
        struct thread *t_stub_router;     /* Stub-router timer */
        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_get(unsigned short instance, const char *name,
                             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_lookup_by_inst_name(unsigned short instance,
                                             const char *name);
@@ -619,6 +663,8 @@ extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup_next(struct ospf *,
                                                       struct in_addr *, int);
 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 void ospf_area_check_free(struct ospf *, struct in_addr);
 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);
 
 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 */
index 1a807ea12b9a5f255e1fbd9ace8cdcdbed93ca54..28d58452df547602a0417d9570ea8d4c07241e23 100644 (file)
@@ -52,6 +52,7 @@ ospfd_libfrrospf_a_SOURCES = \
        ospfd/ospf_route.c \
        ospfd/ospf_routemap.c \
        ospfd/ospf_spf.c \
+       ospfd/ospf_ti_lfa.c \
        ospfd/ospf_sr.c \
        ospfd/ospf_te.c \
        ospfd/ospf_vty.c \
@@ -100,6 +101,7 @@ noinst_HEADERS += \
        ospfd/ospf_ri.h \
        ospfd/ospf_route.h \
        ospfd/ospf_spf.h \
+       ospfd/ospf_ti_lfa.h \
        ospfd/ospf_sr.h \
        ospfd/ospf_te.h \
        ospfd/ospf_vty.h \
diff --git a/tests/ospfd/.gitignore b/tests/ospfd/.gitignore
new file mode 100644 (file)
index 0000000..c659b64
--- /dev/null
@@ -0,0 +1,3 @@
+/*_afl/*
+test_ospf_spf
+core
diff --git a/tests/ospfd/common.c b/tests/ospfd/common.c
new file mode 100644 (file)
index 0000000..1614565
--- /dev/null
@@ -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;
+}
diff --git a/tests/ospfd/common.h b/tests/ospfd/common.h
new file mode 100644 (file)
index 0000000..ce2bb31
--- /dev/null
@@ -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 */
diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c
new file mode 100644 (file)
index 0000000..b55951d
--- /dev/null
@@ -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);
+}
diff --git a/tests/ospfd/test_ospf_spf.py b/tests/ospfd/test_ospf_spf.py
new file mode 100644 (file)
index 0000000..3ae1c00
--- /dev/null
@@ -0,0 +1,6 @@
+import frrtest
+
+class TestOspfSPF(frrtest.TestMultiOut):
+    program = './test_ospf_spf'
+
+TestOspfSPF.exit_cleanly()
diff --git a/tests/ospfd/topologies.c b/tests/ospfd/topologies.c
new file mode 100644 (file)
index 0000000..37fd89e
--- /dev/null
@@ -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,
+                                               },
+                                       },
+                       },
+               },
+};
index 1f173d7f1a5ff2c9d30449a2dfe4ad549881a0ce..5bbe1a20c6d5b05f2fd6c4f1e8f3ff39e2b5f7a1 100644 (file)
@@ -31,6 +31,14 @@ TESTS_ISISD =
 IGNORE_ISISD = --ignore=isisd/
 endif
 
+if OSPFD
+TESTS_OSPFD = \
+       tests/ospfd/test_ospf_spf \
+       # end
+else
+TESTS_OSPFD =
+endif
+
 if OSPF6D
 TESTS_OSPF6D = \
        tests/ospf6d/test_lsdb \
@@ -90,6 +98,7 @@ check_PROGRAMS = \
        tests/lib/northbound/test_oper_data \
        $(TESTS_BGPD) \
        $(TESTS_ISISD) \
+       $(TESTS_OSPFD) \
        $(TESTS_OSPF6D) \
        $(TESTS_ZEBRA) \
        # end
@@ -126,6 +135,7 @@ noinst_HEADERS += \
        tests/lib/cli/common_cli.h \
        tests/lib/test_typelist.h \
        tests/isisd/test_common.h \
+       tests/ospfd/common.h \
        # end
 
 #
@@ -145,6 +155,7 @@ TESTS_CFLAGS = \
 ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
 BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) -lm
 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)
 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_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_CPPFLAGS = $(TESTS_CPPFLAGS)
 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.refout \
        tests/isisd/test_isis_vertex_queue.py \
+       tests/ospfd/test_ospf_spf.py \
        tests/lib/cli/test_commands.in \
        tests/lib/cli/test_commands.py \
        tests/lib/cli/test_commands.refout \