]> git.proxmox.com Git - mirror_frr.git/commitdiff
OSPFD: Add Experimental Segment Routing support
authorOlivier Dugeon <olivier.dugeon@orange.com>
Thu, 18 Jan 2018 18:11:11 +0000 (19:11 +0100)
committerOlivier Dugeon <olivier.dugeon@orange.com>
Thu, 18 Jan 2018 18:11:11 +0000 (19:11 +0100)
This is an implementation of draft-ietf-ospf-segment-routing-extensions-24
and RFC7684 for Extended Link & Prefix Opaque LSA.
Look to doc/OSPF_SR.rst for implementation details & known limitations.

New files:

 - ospfd/ospf_sr.h: Segment Routing structure definition (SubTLVs + SRDB)
 - ospfd/ospf_sr.c: Main functions for Segment Routing support
 - ospfd/ospf_ext.h: TLVs and SubTLVs definition for RFC7684
 - ospfd/ospf_ext.c: RFC7684 Extended Link / Prefix implementation
 - doc/OSPF-SRr.rst: Documentation

Modified Files:

 - doc/ospfd.texi: Add new Segment Routing CLI command definition
 - lib/command.h: Add new string command for Segment Routing CLI
 - lib/mpls.h: Add default value for SRGB
 - lib/route_types.txt: Add new OSPF Segment Routing route type
 - ospfd/ospf_dump.[c,h]: Add OSPF SR debug
 - ospfd/ospf_memory.[c,h]: Add new Segment Routing memory type
 - ospfd/ospf_opaque.[c,h]: Add ospf_sr_init() starting function
 - ospfd/ospf_ri.c: Add new functions to Set/Get Segment Routing TLVs
Add new ospf_router_info_lsa_upadte() to send Opaque LSA to ospf_sr.c()
 - ospfd/ospf_ri.h: Add new Router Information SR SubTLVs
 - ospfd/ospf_spf.c: Add new scheduler when running SPF to trigger
update of NHLFE
 - ospfd/ospfd.h: Add new thread for Segment Routing scheduler
 - ospfd/subdir.am: Add new files
 - vtysh/Makefile.am: Add new ospf_sr.c file for vtysh
 - zebra/kernel_netlink.c: Add new OSPF_SR route type
 - zebra/rt_netlink.[c,h]: Add new OSPF_SR route type
 - zebra/zebra_mpls.h: Add new OSPF_SR route type

Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
25 files changed:
doc/OSPF-SR.rst [new file with mode: 0644]
doc/ospfd.texi
lib/command.h
lib/mpls.h
lib/route_types.txt
ospfd/ospf_dump.c
ospfd/ospf_dump.h
ospfd/ospf_ext.c [new file with mode: 0644]
ospfd/ospf_ext.h [new file with mode: 0644]
ospfd/ospf_memory.c
ospfd/ospf_memory.h
ospfd/ospf_opaque.c
ospfd/ospf_opaque.h
ospfd/ospf_ri.c
ospfd/ospf_ri.h
ospfd/ospf_spf.c
ospfd/ospf_sr.c [new file with mode: 0644]
ospfd/ospf_sr.h [new file with mode: 0644]
ospfd/ospfd.h
ospfd/subdir.am
vtysh/Makefile.am
zebra/kernel_netlink.c
zebra/rt_netlink.c
zebra/rt_netlink.h
zebra/zebra_mpls.h

diff --git a/doc/OSPF-SR.rst b/doc/OSPF-SR.rst
new file mode 100644 (file)
index 0000000..8dcd885
--- /dev/null
@@ -0,0 +1,82 @@
+OSPF Segment Routing
+====================
+
+This is an EXPERIMENTAL support of draft draft-ietf-ospf-segment-routing-extensions-24.
+DON'T use it for production network.
+
+Implementation details
+----------------------
+
+Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various information:
+ - Router Information: flood the Segment Routing capabilities of the node. This include
+ the supported algorithms, the Segment Routing Global Block (SRGB) and the Maximum Stack
+ Depth.
+ - Extended Link: flood the Adjaceny and Lan Adjacency Segment Identifier
+ - Extended Prefix: flood the Prefix Segment Identifier
+
+The implementation follow previous TE and Router Information code. It used the OPAQUE LSA
+functions define in ospf_opaque.[c,h] as well as the OSPF API. This latter is mandatory
+for the implementation as it provides the Callback to Segment Routing functions (see below)
+when an Extended Link / Prefix or Router Information is received.
+
+Following files where modified or added:
+ - ospd_ri.[c,h] have been modified to add the new TLVs for Segment Routing.
+ - ospf_ext.[c,h] implement RFC7684 as base support of Extended Link and Prefix Opaque LSA.
+ - ospf_sr.[c,h] implement the earth of Segment Routing. It adds a new Segment Routing database
+ to manage Segment Identifiers per Link and Prefix and Segment Routing enable node, Callback
+ functions to process incoming LSA and install MPLS FIB entry through Zebra.
+
+the figure below shows the relation between the various files:
+
+ - ospf_sr.c centralized all the Segment Routing processing. It receives Opaque LSA
+ Router Information (4.0.0.0) from ospf_ri.c and Extended Prefix (7.0.0.X) Link (8.0.0.X)
+ from ospf_ext.c. Once received, it parse TLVs and SubTLVs and store information in SRDB
+ (which is defined in ospf_sr.h). For each received LSA, NHLFE is computed and send to
+ Zebra to add/remove new MPLS labels entries and FEC. New CLI configurations are also
+ centralized in ospf_sr.c. This CLI will trigger the flooding os new LSA Router Information
+ (4.0.0.0), Extended Prefix (7.0.0.X) and Link (8.0.0.X) by ospf_ri.c, respectively ospf_ext.c.
+ - ospf_ri.c send back to ospf_sr.c received Router Information LSA and update self Router
+ Information LSA with paramters provided by ospf_sr.c i.e. SRGB and MSD. It use ospf_opaque.c
+ functions to send / received these Opaque LSAs.
+ - ospf_ext.c send bacl to ospf_sr.c received Extended Prefix and Link Opaque LSA and send
+ self Extended Prefix and Link Opaque LSA through ospf_opaque.c functions.
+
+                    +-----------+     +-------+
+                    |           |     |       |
+                    | ospf_sr.c +-----+  SRDB |
+        +-----------+           +--+  |       |
+        |           +-^-------^-+  |  +-------+
+        |             |   |   |    |
+        |             |   |   |    |
+        |             |   |   |    +--------+
+        |             |   |   |             |
+    +---v----------+  |   |   |       +-----v-------+
+    |              |  |   |   |       |             |
+    | ospf_ri.c    +--+   |   +-------+ ospf_ext.c  |
+    | LSA 4.0.0.0  |      |           | LSA 7.0.0.X |
+    |              |      |           | LSA 8.0.0.X |
+    +---^----------+      |           |             |
+        |                 |           +-----^-------+
+        |                 |                 |
+        |                 |                 |
+        |        +--------v------------+    |
+        |        |                     |    |
+        |        | ZEBRA: Labels + FEC |    |
+        |        |                     |    |
+        |        +---------------------+    |
+        |                                   |
+        |                                   |
+        |         +---------------+         |
+        |         |               |         |
+        +---------> ospf_opaque.c <---------+
+                  |               |
+                  +---------------+
+
+
+Known limitations
+-----------------
+
+ - Only single Area is supported. ABR is not yet supported
+ - Only SPF algorithm is supported
+ - Extended Prefix Range is not supported
+
index cc33211510378103ea51473b8510ea47cdd68311..c013412b94c0c540a02a45cd0523fa46ff80e436 100644 (file)
@@ -22,6 +22,7 @@ networks.
 * Opaque LSA::
 * OSPF Traffic Engineering::
 * Router Information::
+* Segment Routing::
 * Debugging OSPF::              
 * OSPF Configuration Examples::
 @end menu
@@ -724,6 +725,44 @@ Show Router Capabilities flag.
 Show Router Capabilities PCE parameters.
 @end deffn
 
+@node Segment Routing
+@section Segment Routing
+
+This is an EXPERIMENTAL support of Segment Routing as per draft
+ draft-ietf-ospf-segment-routing-extensions-24i for MPLS dataplane.
+
+@deffn {OSPF Command} {segment-routing on} {}
+@deffnx {OSPF Command} {no segment-routing} {}
+Enable Segment Routing. Even if this also activate routing information support,
+it is preferable to also activate routing information, and set accordingly the
+Area or AS flooding.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing global-block (0-1048575) (0-1048575)} {}
+@deffnx {OSPF Command} {no segment-routing global-block} {}
+Fix the Segment Routing Global Block i.e. the label range used by MPLS to store
+label in the MPLS FIB.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing node-msd (1-16)} {}
+@deffnx {OSPF Command} {no segment-routing node-msd} {}
+Fix the Maximum Stack Depth supported by the router. The value depend of the 
+MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing prefix A.B.C.D/M index (0-65535)} {}
+@deffnx {OSPF Command} {no segment-routing prefix A.B.C.D/M} {}
+Set the Segment Rounting index for the specifyed prefix. Note
+that, only prefix with /32 corresponding to a loopback interface are  
+currently supported.
+@end deffn
+
+@deffn {Command} {show ip ospf database segment-routing} {}
+@deffnx {Command} {show ip ospf database segment-routing adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database segment-routing self-originate} {}
+Show Segment Routing Data Base, all SR nodes, specific advertized router or self router.
+@end deffn
+
 @node Debugging OSPF
 @section Debugging OSPF
 
index fa8323bf2ddb5a49d8e0d8847589bd41c385849f..c998bbe1f64842eee884c4f11828cffd183baced 100644 (file)
@@ -358,6 +358,7 @@ struct cmd_node {
 #define OSPF_RI_STR "OSPF Router Information specific commands\n"
 #define PCE_STR "PCE Router Information specific commands\n"
 #define MPLS_STR "MPLS information\n"
+#define SR_STR "Segment-Routing specific commands\n"
 #define WATCHFRR_STR "watchfrr information\n"
 #define ZEBRA_STR "Zebra information\n"
 
index bf98eecd81615adafe8b22f4002579a3610ba18a..9e7eeed34eb18551f96a94fb39654afdd7b637a6 100644 (file)
 #define MPLS_MAX_UNRESERVED_LABEL          1048575
 
 /* Default min and max SRGB label range */
-#define MPLS_DEFAULT_MIN_SRGB_LABEL        16000
-#define MPLS_DEFAULT_MAX_SRGB_LABEL        23999
+#define MPLS_DEFAULT_MIN_SRGB_LABEL        10000
+#define MPLS_DEFAULT_MAX_SRGB_LABEL        50000
+#define MPLS_DEFAULT_MIN_SRGB_SIZE         5000
+#define MPLS_DEFAULT_MAX_SRGB_SIZE         20000
 
 /* Maximum # labels that can be pushed. */
 #define MPLS_MAX_LABELS                    16
@@ -94,7 +96,8 @@ enum lsp_types_t {
        ZEBRA_LSP_NONE = 0,   /* No LSP. */
        ZEBRA_LSP_STATIC = 1, /* Static LSP. */
        ZEBRA_LSP_LDP = 2,    /* LDP LSP. */
-       ZEBRA_LSP_BGP = 3     /* BGP LSP. */
+       ZEBRA_LSP_BGP = 3,    /* BGP LSP. */
+       ZEBRA_LSP_SR = 4      /* Segment Routing LSP. */
 };
 
 /* Functions for basic label operations. */
index 4e764a14c139ab898bbd9717542c99545662b30a..a72ee507b17f081dfa52d8613926a4ff1fa1187d 100644 (file)
@@ -78,6 +78,7 @@ ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL,  'b', 0, 0, "BGP-Direct"
 ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC"
 ZEBRA_ROUTE_BABEL,      babel,     babeld, 'A', 1, 1, "Babel"
 ZEBRA_ROUTE_SHARP,    sharp,     sharpd, 'D', 1, 1, "SHARP"
+ZEBRA_ROUTE_OSPF_SR,    ospf-sr,   ospfd,  's', 1, 0, "OSPF-SR" 
 ZEBRA_ROUTE_ALL,        wildcard,  none,   '-', 0, 0, "-"
 
 
@@ -103,3 +104,4 @@ ZEBRA_ROUTE_LDP,    "Label Distribution Protocol (LDP)"
 ZEBRA_ROUTE_VNC_DIRECT,    "VNC direct (not via zebra) routes"
 ZEBRA_ROUTE_BABEL,  "Babel routing protocol (Babel)"
 ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)"
+ZEBRA_ROUTE_OSPF_SR, "OSPF Segment Routing (OSPF-SR)"
index 6a410f4ed3fd4d831a37754587f5bdd5096a210d..a16722076523dcebf7f3b3d03998dc2078e18f32 100644 (file)
@@ -51,6 +51,8 @@ unsigned long conf_debug_ospf_lsa = 0;
 unsigned long conf_debug_ospf_zebra = 0;
 unsigned long conf_debug_ospf_nssa = 0;
 unsigned long conf_debug_ospf_te = 0;
+unsigned long conf_debug_ospf_ext = 0;
+unsigned long conf_debug_ospf_sr = 0;
 
 /* Enable debug option variables -- valid only session. */
 unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
@@ -61,7 +63,8 @@ unsigned long term_debug_ospf_lsa = 0;
 unsigned long term_debug_ospf_zebra = 0;
 unsigned long term_debug_ospf_nssa = 0;
 unsigned long term_debug_ospf_te = 0;
-
+unsigned long term_debug_ospf_ext = 0;
+unsigned long term_debug_ospf_sr = 0;
 
 const char *ospf_redist_string(u_int route_type)
 {
@@ -1441,6 +1444,33 @@ DEFUN (no_debug_ospf_te,
        return CMD_SUCCESS;
 }
 
+DEFUN (debug_ospf_sr,
+       debug_ospf_sr_cmd,
+       "debug ospf sr",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF-SR information\n")
+{
+       if (vty->node == CONFIG_NODE)
+               CONF_DEBUG_ON(sr, SR);
+       TERM_DEBUG_ON(sr, SR);
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_sr,
+       no_debug_ospf_sr_cmd,
+       "no debug ospf sr",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF-SR information\n")
+{
+       if (vty->node == CONFIG_NODE)
+               CONF_DEBUG_OFF(sr, SR);
+       TERM_DEBUG_OFF(sr, SR);
+       return CMD_SUCCESS;
+}
+
 DEFUN (no_debug_ospf,
        no_debug_ospf_cmd,
        "no debug ospf",
@@ -1774,6 +1804,7 @@ void debug_init()
        install_element(ENABLE_NODE, &debug_ospf_event_cmd);
        install_element(ENABLE_NODE, &debug_ospf_nssa_cmd);
        install_element(ENABLE_NODE, &debug_ospf_te_cmd);
+       install_element(ENABLE_NODE, &debug_ospf_sr_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
@@ -1781,6 +1812,7 @@ void debug_init()
        install_element(ENABLE_NODE, &no_debug_ospf_event_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_te_cmd);
+       install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd);
 
        install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd);
        install_element(ENABLE_NODE, &debug_ospf_packet_cmd);
@@ -1809,12 +1841,14 @@ void debug_init()
        install_element(CONFIG_NODE, &debug_ospf_event_cmd);
        install_element(CONFIG_NODE, &debug_ospf_nssa_cmd);
        install_element(CONFIG_NODE, &debug_ospf_te_cmd);
+       install_element(CONFIG_NODE, &debug_ospf_sr_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_event_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_te_cmd);
+       install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd);
 
        install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd);
        install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd);
index ead2f526ba0ee5de0eee1a6017c7ed12376875d9..6ec92767d8445a7048329079b36e7db2d030b496 100644 (file)
@@ -57,6 +57,8 @@
 #define OSPF_DEBUG_EVENT        0x01
 #define OSPF_DEBUG_NSSA                0x02
 #define OSPF_DEBUG_TE          0x04
+#define OSPF_DEBUG_EXT         0x08
+#define OSPF_DEBUG_SR          0x10
 
 /* Macro for setting debug option. */
 #define CONF_DEBUG_PACKET_ON(a, b)         conf_debug_ospf_packet[a] |= (b)
 
 #define IS_DEBUG_OSPF_TE  IS_DEBUG_OSPF(te,TE)
 
+#define IS_DEBUG_OSPF_EXT  IS_DEBUG_OSPF(ext,EXT)
+
+#define IS_DEBUG_OSPF_SR  IS_DEBUG_OSPF(sr,SR)
+
 #define IS_CONF_DEBUG_OSPF_PACKET(a, b)                                        \
        (conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
 #define IS_CONF_DEBUG_OSPF(a, b) (conf_debug_ospf_##a & OSPF_DEBUG_##b)
@@ -119,6 +125,8 @@ extern unsigned long term_debug_ospf_lsa;
 extern unsigned long term_debug_ospf_zebra;
 extern unsigned long term_debug_ospf_nssa;
 extern unsigned long term_debug_ospf_te;
+extern unsigned long term_debug_ospf_ext;
+extern unsigned long term_debug_ospf_sr;
 
 /* Message Strings. */
 extern char *ospf_lsa_type_str[];
diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c
new file mode 100644 (file)
index 0000000..e4dad99
--- /dev/null
@@ -0,0 +1,1769 @@
+/*
+ * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute
+ * Advertisement
+ *
+ * Module name: Extended Prefix/Link Opaque LSA
+ *
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2016 - 2017 Orange Labs http://www.orange.com
+ *
+ * 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h" /* for inet_aton() */
+#include "network.h"
+#include "if.h"
+#include "libospf.h" /* for ospf interface types */
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ext.h"
+
+/* Following structure are internal use only. */
+
+/*
+ * Global variable to manage Extended Prefix/Link Opaque LSA on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_ext_lp OspfEXT;
+
+/*------------------------------------------------------------------------------*
+ * Followings are initialize/terminate functions for Extended Prefix/Link Opaque
+ * LSA handling.
+ *------------------------------------------------------------------------------*/
+
+/* Extended Prefix Opaque LSA related callback functions */
+static int ospf_ext_pref_del_if(struct interface *ifp);
+static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_ext_pref_lsa_originate(void *arg);
+static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa);
+static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode);
+/* Extended Link Opaque LSA related callback functions */
+static int ospf_ext_link_new_if(struct interface *ifp);
+static int ospf_ext_link_del_if(struct interface *ifp);
+static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status);
+static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status);
+static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_ext_link_lsa_originate(void *arg);
+static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa);
+static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode);
+static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op);
+static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa);
+static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa);
+static void del_ext_info(void *val);
+
+/*
+ * Extended Link/Prefix initialization
+ *
+ * @param - none
+ *
+ * @return - 0 if OK, <> 0 otherwise
+ */
+int ospf_ext_init(void)
+{
+       int rc = 0;
+
+       memset(&OspfEXT, 0, sizeof(struct ospf_ext_lp));
+       OspfEXT.enabled = false;
+       /* Only Area flooding is supported yet */
+       OspfEXT.scope = OSPF_OPAQUE_AREA_LSA;
+       /* Initialize interface list */
+       OspfEXT.iflist = list_new();
+       OspfEXT.iflist->del = del_ext_info;
+
+       zlog_info("EXT: Register Extended Link Opaque LSA");
+       rc = ospf_register_opaque_functab(
+               OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_EXTENDED_LINK_LSA,
+               ospf_ext_link_new_if,   /* new if */
+               ospf_ext_link_del_if,   /* del if */
+               ospf_ext_link_ism_change,    /* ism change */
+               ospf_ext_link_nsm_change,    /* nsm change */
+               NULL,                        /* Write router config. */
+               NULL,                        /* Write interface conf. */
+               NULL,                        /* Write debug config. */
+               ospf_ext_link_show_info,     /* Show LSA info */
+               ospf_ext_link_lsa_originate, /* Originate LSA */
+               ospf_ext_link_lsa_refresh,   /* Refresh LSA */
+               ospf_ext_link_lsa_update,    /* new_lsa_hook */
+               NULL);                       /* del_lsa_hook */
+
+       if (rc != 0) {
+               zlog_warn("EXT: Failed to register Extended Link LSA");
+               return rc;
+       }
+
+       zlog_info("EXT: Register Extended Prefix Opaque LSA");
+       rc = ospf_register_opaque_functab(
+               OspfEXT.scope, OPAQUE_TYPE_EXTENDED_PREFIX_LSA,
+               NULL,                        /* new interface */
+               ospf_ext_pref_del_if,   /* del interface */
+               NULL,                        /* ism change */
+               NULL,                        /* nsm change */
+               ospf_sr_config_write_router, /* Write router config. */
+               NULL,                        /* Write interface conf. */
+               NULL,                        /* Write debug config. */
+               ospf_ext_pref_show_info,     /* Show LSA info */
+               ospf_ext_pref_lsa_originate, /* Originate LSA */
+               ospf_ext_pref_lsa_refresh,   /* Refresh LSA */
+               ospf_ext_pref_lsa_update,    /* new_lsa_hook */
+               NULL);                       /* del_lsa_hook */
+       if (rc != 0) {
+               zlog_warn("EXT: Failed to register Extended Prefix LSA");
+               return rc;
+       }
+
+       return rc;
+}
+
+/*
+ * Extended Link/Prefix termination function
+ *
+ * @paam - node
+ *
+ * @return - none
+ */
+void ospf_ext_term(void)
+{
+
+       if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA)
+           || (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
+               zlog_warn(
+                       "EXT: Unable to unregister Extended Prefix "
+                       "Opaque LSA functions: Wrong scope!");
+       else
+               ospf_delete_opaque_functab(OspfEXT.scope,
+                                          OPAQUE_TYPE_EXTENDED_PREFIX_LSA);
+
+       ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
+                                  OPAQUE_TYPE_EXTENDED_LINK_LSA);
+
+       list_delete_and_null(&OspfEXT.iflist);
+       OspfEXT.scope = 0;
+       OspfEXT.enabled = false;
+
+       return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are control functions for Extended Prefix/Link Opaque LSA
+ * parameters management.
+ *------------------------------------------------------------------------*/
+/* Functions to free memory space */
+static void del_ext_info(void *val)
+{
+       XFREE(MTYPE_OSPF_EXT_PARAMS, val);
+       return;
+}
+
+/* Increment instance value for Extended Prefix Opaque LSAs Opaque ID field */
+static u_int32_t get_ext_pref_instance_value(void)
+{
+       static u_int32_t seqno = 0;
+
+       if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM)
+               seqno += 1;
+       else
+               seqno = 1; /* Avoid zero. */
+
+       return seqno;
+}
+
+/* Increment instance value for Extended Link Opaque LSAs Opaque ID field */
+static u_int32_t get_ext_link_instance_value(void)
+{
+       static u_int32_t seqno = 0;
+
+       if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM)
+               seqno += 1;
+       else
+               seqno = 1; /* Avoid zero. */
+
+       return seqno;
+}
+
+/* Lookup Extended Prefix/Links by ifp from OspfEXT struct iflist */
+static struct ext_itf *lookup_ext_by_ifp(struct interface *ifp)
+{
+       struct listnode *node, *nnode;
+       struct ext_itf *exti;
+
+       for (ALL_LIST_ELEMENTS(OspfEXT.iflist, node, nnode, exti))
+               if (exti->ifp == ifp)
+                       return exti;
+
+       return NULL;
+}
+
+/* Lookup Extended Prefix/Links by LSA ID from OspfEXT struct iflist */
+static struct ext_itf *lookup_ext_by_instance(struct ospf_lsa *lsa)
+{
+       struct listnode *node;
+       struct ext_itf *exti;
+       unsigned int key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr));
+
+       for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+               if (exti->instance == key)
+                       return exti;
+
+       zlog_warn("lookup_linkparams_by_instance: Entry not found: key(%x)",
+                 key);
+       return NULL;
+}
+
+/*------------------------------------------------------------------------*
+ * The underlying subsection defines setters and unsetters to create and
+ * delete tlvs and subtlvs
+ *------------------------------------------------------------------------*/
+
+/* Extended Prefix TLV - RFC7684 section 2.1 */
+static void set_ext_prefix(struct ext_itf *exti, u_int8_t route_type,
+                          u_int8_t flags, struct prefix_ipv4 p)
+{
+
+       TLV_TYPE(exti->prefix) = htons(EXT_TLV_PREFIX);
+       /* Warning: Size must be adjust depending of subTLV's */
+       TLV_LEN(exti->prefix) = htons(EXT_TLV_PREFIX_SIZE);
+       exti->prefix.route_type = route_type;
+       exti->prefix.flags = flags;
+       /* Only Address Family Ipv4 (0) is defined in RFC 7684 */
+       exti->prefix.af = 0;
+       exti->prefix.pref_length = p.prefixlen;
+       exti->prefix.address = p.prefix;
+}
+
+/* Extended Link TLV - RFC7684 section 3.1 */
+static void set_ext_link(struct ext_itf *exti, u_int8_t type, struct in_addr id,
+                        struct in_addr data)
+{
+
+       TLV_TYPE(exti->link) = htons(EXT_TLV_LINK);
+       /* Warning: Size must be adjust depending of subTLV's */
+       TLV_LEN(exti->link) = htons(EXT_TLV_LINK_SIZE);
+       exti->link.link_type = type;
+       exti->link.link_id = id;
+       exti->link.link_data = data;
+}
+
+/* Prefix SID SubTLV - section 5 */
+static void set_prefix_sid(struct ext_itf *exti, u_int8_t algorithm,
+                          u_int32_t value, int value_type)
+{
+
+       u_int8_t flags;
+
+       if ((algorithm != SR_ALGORITHM_SPF)
+           && (algorithm != SR_ALGORITHM_STRICT_SPF)) {
+               zlog_warn(
+                       "OSPF_SR: unrecognized "
+                       "algorithm, not spf or strict spf");
+               return;
+       }
+
+       /* Set the flags according to the type of value field: label or index
+        * other flags flags are cleared, in particular the No-PHP as the
+        * Linux Kernel only supports Penultimate Hop Popping (PHP)
+        */
+       if (value_type == SID_LABEL)
+               flags = EXT_SUBTLV_PREFIX_SID_VFLG;
+       else
+               flags = 0;
+
+       /* set prefix sid subtlv for an extended prefix tlv */
+       TLV_TYPE(exti->node_sid) = htons(EXT_SUBTLV_PREFIX_SID);
+       exti->node_sid.algorithm = algorithm;
+       exti->node_sid.flags = flags;
+       exti->node_sid.mtid = 0; /* Multi-Topology is not supported */
+
+       /* Set Label or Index value */
+       if (value_type == SID_LABEL) {
+               TLV_LEN(exti->node_sid) = htons(SID_LABEL_SIZE);
+               exti->node_sid.value = htonl(SET_LABEL(value));
+       } else {
+               TLV_LEN(exti->node_sid) = htons(SID_INDEX_SIZE);
+               exti->node_sid.value = htonl(value);
+       }
+
+       return;
+}
+
+/* Adjacency SID SubTLV - section 6.1 */
+static void set_adj_sid(struct ext_itf *exti, bool backup, u_int32_t value,
+                       int value_type)
+{
+       int index;
+       u_int8_t flags;
+
+       /* Determine which ADJ_SID must be set: nominal or backup */
+       if (backup) {
+               flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG;
+               index = 1;
+       } else {
+               index = 0;
+               flags = 0;
+       }
+
+       /* Set Header */
+       TLV_TYPE(exti->adj_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
+
+       /* Only Local ADJ-SID is supported for the moment */
+       SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+
+       exti->adj_sid[index].mtid = 0; /* Multi-Topology is not supported */
+
+       /* Adjust Length, Flags and Value depending on the type of Label */
+       if (value_type == SID_LABEL) {
+               SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->adj_sid[index]) = htons(SID_LABEL_SIZE);
+               exti->adj_sid[index].value = htonl(SET_LABEL(value));
+       } else {
+               UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->adj_sid[index]) = htons(SID_INDEX_SIZE);
+               exti->adj_sid[index].value = htonl(value);
+       }
+
+       exti->adj_sid[index].flags = flags; /* Set computed flags */
+       exti->adj_sid[index].mtid = 0;   /* Multi-Topology is not supported */
+       exti->adj_sid[index].weight = 0; /* Load-Balancing is not supported */
+       return;
+}
+
+/* LAN Adjacency SID SubTLV - section 6.2 */
+static void set_lan_adj_sid(struct ext_itf *exti, bool backup, u_int32_t value,
+                           int value_type, struct in_addr neighbor_id)
+{
+
+       int index;
+       u_int8_t flags;
+
+       /* Determine which ADJ_SID must be set: nominal or backup */
+       if (backup) {
+               flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG;
+               index = 1;
+       } else {
+               index = 0;
+               flags = 0;
+       }
+
+       /* Set Header */
+       TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
+
+       /* Only Local ADJ-SID is supported for the moment */
+       SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+
+       /* Adjust Length, Flags and Value depending on the type of Label */
+       if (value_type == SID_LABEL) {
+               SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->lan_sid[index]) = htons(SID_LABEL_SIZE);
+               exti->lan_sid[index].value = htonl(SET_LABEL(value));
+       } else {
+               UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->lan_sid[index]) = htons(SID_INDEX_SIZE);
+               exti->lan_sid[index].value = htonl(value);
+       }
+
+       exti->lan_sid[index].flags = flags; /* Set computed flags */
+       exti->lan_sid[index].mtid = 0;   /* Multi-Topology is not supported */
+       exti->lan_sid[index].weight = 0; /* Load-Balancing is not supported */
+       exti->lan_sid[index].neighbor_id = neighbor_id;
+       return;
+}
+
+/* Experimental SubTLV from Cisco */
+static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif)
+{
+
+       TLV_TYPE(exti->rmt_itf_addr) = htons(EXT_SUBTLV_RMT_ITF_ADDR);
+       TLV_LEN(exti->rmt_itf_addr) = htons(sizeof(struct in_addr));
+       exti->rmt_itf_addr.value = rmtif;
+       return;
+}
+
+/*
+ * Update Extended prefix SID index for Loopback interface type
+ *
+ * @param ifname - Loopback interface name
+ * @param index - new value for the prefix SID of this interface
+ * @param p - prefix for this interface or NULL if Extended Prefix
+ * should be remove
+ *
+ * @return instance number if update is OK, 0 otherwise
+ */
+int ospf_ext_schedule_prefix_index(struct interface *ifp, u_int32_t index,
+                                  struct prefix_ipv4 *p)
+{
+       int rc = 0;
+       struct ext_itf *exti;
+
+       /* Find Extended Prefix interface */
+       exti = lookup_ext_by_ifp(ifp);
+       if (exti == NULL)
+               return rc;
+
+       if (p != NULL) {
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT (ospf_ext_schedule_prefix_index) "
+                               "Schedule new prefix %s/%d with index %d "
+                               "on interface %s",
+                               inet_ntoa(p->prefix), p->prefixlen, index,
+                               ifp->name);
+
+               /* Set first Extended Prefix then the Prefix SID information */
+               set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG,
+                              *p);
+               set_prefix_sid(exti, SR_ALGORITHM_SPF, index, SID_INDEX);
+
+               /* Try to Schedule LSA */
+               SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                       ospf_ext_pref_lsa_schedule(exti, REFRESH_THIS_LSA);
+               else
+                       ospf_ext_pref_lsa_schedule(exti, REORIGINATE_THIS_LSA);
+       } else {
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT (ospf_ext_schedule_prefix_index) "
+                               "Remove prefix for interface %s",
+                               ifp->name);
+
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+               }
+       }
+
+       return exti->instance;
+}
+
+/*
+ * Used by Segment Routing to activate/deactivate Extended Link/Prefix flooding
+ *
+ * @param enable To activate or not Segment Routing Extended LSA flooding
+ *
+ * @return none
+ */
+void ospf_ext_update_sr(bool enable)
+{
+       struct listnode *node;
+       struct ext_itf *exti;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "EXT (ospf_ext_update_sr): %s Extended LSAs for "
+                       "Segment Routing",
+                       enable ? "Enable" : "Disable");
+
+       if (enable) {
+               OspfEXT.enabled = true;
+               /* Refresh LSAs if already engaged or originate */
+               for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+                       if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                               ospf_ext_lsa_schedule(exti, REFRESH_THIS_LSA);
+                       else
+                               ospf_ext_lsa_schedule(exti,
+                                                     REORIGINATE_THIS_LSA);
+       } else {
+               /* Start by Flushing engaged LSAs */
+               for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+                       if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                               ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
+               /* And then disable Extended Link/Prefix */
+               OspfEXT.enabled = false;
+       }
+}
+/*------------------------------------------------------------------------*
+ * Followings are callback functions against generic Opaque-LSAs handling.
+ *------------------------------------------------------------------------*/
+
+/* Add new Interface in Extended Interface List */
+static int ospf_ext_link_new_if(struct interface *ifp)
+{
+       struct ext_itf *new;
+       int rc = -1;
+
+       if (lookup_ext_by_ifp(ifp) != NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_new_if) interface %s"
+                       " is already in use",
+                       ifp ? ifp->name : "-");
+               rc = 0; /* Do nothing here. */
+               return rc;
+       }
+
+       new = XCALLOC(MTYPE_OSPF_EXT_PARAMS, sizeof(struct ext_itf));
+       if (new == NULL) {
+               zlog_warn("EXT: XCALLOC: %s", safe_strerror(errno));
+               return rc;
+       }
+
+       /* initialize new information and link back the interface */
+       new->ifp = ifp;
+       new->flags = EXT_LPFLG_LSA_INACTIVE;
+
+       listnode_add(OspfEXT.iflist, new);
+
+       rc = 0;
+       return rc;
+}
+
+/* Remove existing Interface from Extended Interface List */
+static int ospf_ext_link_del_if(struct interface *ifp)
+{
+       struct ext_itf *exti;
+       int rc = -1;
+
+       if ((exti = lookup_ext_by_ifp(ifp)) != NULL) {
+               struct list *iflist = OspfEXT.iflist;
+
+               /* Skip Extended Prefix interface */
+               if (exti->stype == PREF_SID)
+                       return 0;
+
+               /* Dequeue listnode entry from the list. */
+               listnode_delete(iflist, exti);
+
+               /* Avoid misjudgement in the next lookup. */
+               if (listcount(iflist) == 0)
+                       iflist->head = iflist->tail = NULL;
+
+               XFREE(MTYPE_OSPF_EXT_PARAMS, exti);
+
+               rc = 0;
+       } else {
+               zlog_warn(
+                       "EXT (ospf_ext_link_del_if) interface %s "
+                       "is not found",
+                       ifp ? ifp->name : "-");
+       }
+
+       return rc;
+}
+
+/* Remove existing Interface from Extended Interface List */
+static int ospf_ext_pref_del_if(struct interface *ifp)
+{
+       struct ext_itf *exti;
+       int rc = -1;
+
+       if ((exti = lookup_ext_by_ifp(ifp)) != NULL) {
+               struct list *iflist = OspfEXT.iflist;
+
+               /* Look only to Extended Prefix interface */
+               if (exti->stype != PREF_SID)
+                       return 0;
+
+               /* Dequeue listnode entry from the list. */
+               listnode_delete(iflist, exti);
+
+               /* Avoid misjudgement in the next lookup. */
+               if (listcount(iflist) == 0)
+                       iflist->head = iflist->tail = NULL;
+
+               XFREE(MTYPE_OSPF_EXT_PARAMS, exti);
+
+               rc = 0;
+       } else {
+               zlog_warn(
+                       "EXT (ospf_ext_pref_del_if) interface %s "
+                       "is not found",
+                       ifp ? ifp->name : "-");
+       }
+
+       return rc;
+}
+
+/*
+ * Determine if an Extended Interface is Link or Prefix type and
+ * allocate new instance value accordingly
+ */
+static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status)
+{
+       struct ext_itf *exti;
+
+       /* Get interface information for Segment Routing */
+       if ((exti = lookup_ext_by_ifp(oi->ifp)) == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_ism_change) Cannot get Extended "
+                       "information from OI(%s)",
+                       IF_NAME(oi));
+               return;
+       }
+
+       /* Determine if interface is related to Node SID or Adjacency/LAN SID */
+       if (oi->type == OSPF_IFTYPE_LOOPBACK) {
+               exti->stype = PREF_SID;
+               exti->instance = get_ext_pref_instance_value();
+       } else {
+               exti->stype = ADJ_SID;
+               exti->instance = get_ext_link_instance_value();
+       }
+       zlog_debug("EXT (ospf_ext_link_ism_change) Set %s SID to interface %s ",
+                  exti->stype == PREF_SID ? "Node" : "Adj.", oi->ifp->name);
+}
+
+/*
+ * Finish Extended Link configuration and flood corresponding LSA
+ * when OSPF adjacency on this link fire up
+ */
+static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
+{
+       struct ospf_interface *oi = nbr->oi;
+       struct ext_itf *exti;
+       u_int32_t label;
+
+       /* Process Neighbor only when its state is NSM Full */
+       if (nbr->state != NSM_Full)
+               return;
+
+       /* Get interface information for Segment Routing */
+       if ((exti = lookup_ext_by_ifp(oi->ifp)) == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_nsm_change) Cannot get Extended "
+                       "information from OI(%s)",
+                       IF_NAME(oi));
+               return;
+       }
+
+       if (oi->area == NULL || oi->area->ospf == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_nsm_change) Cannot refer to "
+                       "OSPF from OI(%s)",
+                       IF_NAME(oi));
+               return;
+       }
+
+       /* Keep Area information in combination with SR info. */
+       exti->area = oi->area;
+       OspfEXT.area = oi->area;
+
+       /* Process only Adjacency/LAN SID */
+       if (exti->stype == PREF_SID)
+               return;
+
+       switch (oi->state) {
+       case ISM_PointToPoint:
+               /* Segment ID is an Adjacency one */
+               exti->stype = ADJ_SID;
+
+               /* Set Extended Link TLV with link_id == Nbr Router ID */
+               set_ext_link(exti, OSPF_IFTYPE_POINTOPOINT, nbr->router_id,
+                            oi->address->u.prefix4);
+
+               /* Set Extended Link Adjacency SubTLVs, backup first */
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, true, label, SID_LABEL);
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, false, label, SID_LABEL);
+               /* And Remote Interface address */
+               set_rmt_itf_addr(exti, nbr->address.u.prefix4);
+
+               break;
+
+       case ISM_DR:
+               /* Segment ID is a LAN Adjacency for the DR only */
+               exti->stype = LAN_ADJ_SID;
+
+               /* Set Extended Link TLV with link_id == DR */
+               set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi),
+                            oi->address->u.prefix4);
+
+               /* Set Extended Link Adjacency SubTLVs, backup first */
+               label = get_ext_link_label_value();
+               set_lan_adj_sid(exti, true, label, SID_LABEL, nbr->router_id);
+               label = get_ext_link_label_value();
+               set_lan_adj_sid(exti, false, label, SID_LABEL, nbr->router_id);
+
+               break;
+
+       case ISM_DROther:
+       case ISM_Backup:
+               /* Segment ID is an Adjacency if not the DR */
+               exti->stype = ADJ_SID;
+
+               /* Set Extended Link TLV with link_id == DR */
+               set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi),
+                            oi->address->u.prefix4);
+
+               /* Set Extended Link Adjacency SubTLVs, backup first */
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, true, label, SID_LABEL);
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, false, label, SID_LABEL);
+
+               break;
+
+       default:
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+               }
+               return;
+       }
+
+       /* flood this links params if everything is ok */
+       SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+       if (OspfEXT.enabled) {
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                       ospf_ext_link_lsa_schedule(exti, REFRESH_THIS_LSA);
+               else
+                       ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA);
+       }
+
+       return;
+}
+
+/* Callbacks to handle Extended Link Segment Routing LSA information */
+static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa)
+{
+       /* Sanity Check */
+       if (lsa == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_lsa_update): Abort! LSA is "
+                       "NULL");
+               return -1;
+       }
+
+       /* Process only Extended Link LSA */
+       if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
+           != OPAQUE_TYPE_EXTENDED_LINK_LSA)
+               return 0;
+
+       /* Check if Extended is enable */
+       if (!OspfEXT.enabled)
+               return 0;
+
+       /* Call Segment Routing LSA update or deletion */
+       if (!IS_LSA_MAXAGE(lsa))
+               ospf_sr_ext_link_lsa_update(lsa);
+       else
+               ospf_sr_ext_link_lsa_delete(lsa);
+
+       return 0;
+}
+
+/* Callbacks to handle Extended Prefix Segment Routing LSA information */
+static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa)
+{
+
+       /* Sanity Check */
+       if (lsa == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_pref_lsa_update): Abort! LSA is "
+                       "NULL");
+               return -1;
+       }
+
+       /* Process only Extended Prefix LSA */
+       if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
+           != OPAQUE_TYPE_EXTENDED_PREFIX_LSA)
+               return 0;
+
+       /* Check if Extended is enable */
+       if (!OspfEXT.enabled)
+               return 0;
+
+       /* Call Segment Routing LSA update or deletion */
+       if (!IS_LSA_MAXAGE(lsa))
+               ospf_sr_ext_prefix_lsa_update(lsa);
+       else
+               ospf_sr_ext_prefix_lsa_delete(lsa);
+
+       return 0;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are OSPF protocol processing functions for
+ * Extended Prefix/Link Opaque LSA
+ *------------------------------------------------------------------------*/
+
+static void build_tlv_header(struct stream *s, struct tlv_header *tlvh)
+{
+       stream_put(s, tlvh, sizeof(struct tlv_header));
+       return;
+}
+
+static void build_tlv(struct stream *s, struct tlv_header *tlvh)
+{
+
+       if ((tlvh != NULL) && (ntohs(tlvh->type) != 0)) {
+               build_tlv_header(s, tlvh);
+               stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh));
+       }
+       return;
+}
+
+/* Build an Extended Prefix Opaque LSA body for extended prefix TLV */
+static void ospf_ext_pref_lsa_body_set(struct stream *s, struct ext_itf *exti)
+{
+
+       /* Sanity check */
+       if ((exti == NULL) || (exti->stype != PREF_SID))
+               return;
+
+       /* Adjust Extended Prefix TLV size */
+       TLV_LEN(exti->prefix) =
+               htons(ntohs(TLV_LEN(exti->node_sid)) + EXT_TLV_PREFIX_SIZE);
+
+       /* Build LSA body for an Extended Prefix TLV */
+       build_tlv_header(s, &exti->prefix.header);
+       stream_put(s, TLV_DATA(&exti->prefix.header), EXT_TLV_PREFIX_SIZE);
+       /* Then add Prefix SID SubTLV */
+       build_tlv(s, &exti->node_sid.header);
+
+       return;
+}
+
+/* Build an Extended Link Opaque LSA body for extended link TLV */
+static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti)
+{
+
+       /* Sanity check */
+       if ((exti == NULL)
+           || ((exti->stype != ADJ_SID) && (exti->stype != LAN_ADJ_SID)))
+               return;
+
+       if (exti->stype == ADJ_SID) {
+               /* Adjust Extended Link TLV size for Adj. SID */
+               TLV_LEN(exti->link) =
+                       htons(EXT_TLV_LINK_SIZE + 2 * EXT_SUBTLV_ADJ_SID_SIZE
+                             + 2 * TLV_HDR_SIZE + EXT_SUBTLV_RMT_ITF_ADDR_SIZE
+                             + TLV_HDR_SIZE);
+
+               /* Build LSA body for an Extended Link TLV with Adj. SID */
+               build_tlv_header(s, &exti->link.header);
+               stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
+               /* then add Ajacency SubTLVs */
+               build_tlv(s, &exti->adj_sid[1].header);
+               build_tlv(s, &exti->adj_sid[0].header);
+               /* Cisco experimental SubTLV */
+               build_tlv(s, &exti->rmt_itf_addr.header);
+       } else {
+               /* Adjust Extended Link TLV size for LAN SID */
+               TLV_LEN(exti->link) = htons(EXT_TLV_LINK_SIZE
+                                           + 2 * EXT_SUBTLV_LAN_ADJ_SID_SIZE
+                                           + 2 * TLV_HDR_SIZE);
+
+               /* Build LSA body for an Extended Link TLV with LAN SID */
+               build_tlv_header(s, &exti->link.header);
+               stream_put(s, &exti->link.header, EXT_TLV_LINK_SIZE);
+               /* then add LAN-Ajacency SubTLVs */
+               build_tlv(s, &exti->lan_sid[1].header);
+               build_tlv(s, &exti->lan_sid[0].header);
+       }
+
+       return;
+}
+
+/* Create new Extended Prefix opaque-LSA for every extended prefix */
+static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area,
+                                             struct ext_itf *exti)
+{
+       struct stream *s;
+       struct lsa_header *lsah;
+       struct ospf_lsa *new = NULL;
+       u_char options, lsa_type;
+       struct in_addr lsa_id;
+       struct in_addr router_id;
+       u_int32_t tmp;
+       u_int16_t length;
+
+       /* Create a stream for LSA. */
+       if ((s = stream_new(OSPF_MAX_LSA_SIZE)) == NULL) {
+               zlog_warn("EXT: stream_new() error");
+               return NULL;
+       }
+
+       /* Prepare LSA Header */
+       lsah = (struct lsa_header *)STREAM_DATA(s);
+
+       lsa_type = OspfEXT.scope;
+
+       /* LSA ID is a variable number identifying different instances of
+        * Extended Prefix Opaque LSA from the same router see RFC 7684 */
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance);
+       lsa_id.s_addr = htonl(tmp);
+
+       options = OSPF_OPTION_O; /* Don't forget this :-) */
+
+       /* Fix Options and Router ID depending of the flooding scope */
+       if ((OspfEXT.scope == OSPF_OPAQUE_AS_LSA) || (area == NULL)) {
+               options = OSPF_OPTION_E;
+               struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+               router_id = top->router_id;
+       } else {
+               options |= LSA_OPTIONS_GET(area); /* Get area default option */
+               options |= LSA_OPTIONS_NSSA_GET(area);
+               router_id = area->ospf->router_id;
+       }
+
+       /* Set opaque-LSA header fields. */
+       lsa_header_set(s, options, lsa_type, lsa_id, router_id);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+               zlog_debug(
+                       "EXT: LSA[Type%d:%s]: Create an Opaque-LSA/Extended "
+                       "Prefix Opaque LSA instance",
+                       lsa_type, inet_ntoa(lsa_id));
+
+
+       /* Set opaque-LSA body fields. */
+       ospf_ext_pref_lsa_body_set(s, exti);
+
+       /* Set length. */
+       length = stream_get_endp(s);
+       lsah->length = htons(length);
+
+       /* Now, create an OSPF LSA instance. */
+       if ((new = ospf_lsa_new()) == NULL) {
+               zlog_warn("EXT: ospf_lsa_new() error");
+               stream_free(s);
+               return NULL;
+       }
+       if ((new->data = ospf_lsa_data_new(length)) == NULL) {
+               zlog_warn("EXT: ospf_lsa_data_new() error");
+               ospf_lsa_unlock(&new);
+               new = NULL;
+               stream_free(s);
+               return NULL;
+       }
+
+       new->area = area;
+       SET_FLAG(new->flags, OSPF_LSA_SELF);
+       memcpy(new->data, lsah, length);
+       stream_free(s);
+
+       return new;
+}
+
+/* Create new Extended Link opaque-LSA for every extended link TLV */
+static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area,
+                                             struct ext_itf *exti)
+{
+       struct stream *s;
+       struct lsa_header *lsah;
+       struct ospf_lsa *new = NULL;
+       u_char options, lsa_type;
+       struct in_addr lsa_id;
+       u_int32_t tmp;
+       u_int16_t length;
+
+       /* Create a stream for LSA. */
+       if ((s = stream_new(OSPF_MAX_LSA_SIZE)) == NULL) {
+               zlog_warn("EXT: stream_new() error");
+               return NULL;
+       }
+       lsah = (struct lsa_header *)STREAM_DATA(s);
+
+       options = OSPF_OPTION_O;          /* Don't forget this :-) */
+       options |= LSA_OPTIONS_GET(area); /* Get area default option */
+       options |= LSA_OPTIONS_NSSA_GET(area);
+       /* Extended Link Opaque LSA are only flooded within an area */
+       lsa_type = OSPF_OPAQUE_AREA_LSA;
+
+       /* LSA ID is a variable number identifying different instances of
+        * Extended Link Opaque LSA from the same router see RFC 7684 */
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+       lsa_id.s_addr = htonl(tmp);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+               zlog_debug(
+                       "EXT: LSA[Type%d:%s]: Create an Opaque-LSA/Extended "
+                       "Link Opaque LSA instance",
+                       lsa_type, inet_ntoa(lsa_id));
+
+       /* Set opaque-LSA header fields. */
+       lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id);
+
+       /* Set opaque-LSA body fields. */
+       ospf_ext_link_lsa_body_set(s, exti);
+
+       /* Set length. */
+       length = stream_get_endp(s);
+       lsah->length = htons(length);
+
+       /* Now, create an OSPF LSA instance. */
+       if ((new = ospf_lsa_new()) == NULL) {
+               zlog_warn("EXT: ospf_lsa_new() error");
+               stream_free(s);
+               return NULL;
+       }
+       if ((new->data = ospf_lsa_data_new(length)) == NULL) {
+               zlog_warn("EXT: ospf_lsa_data_new() error");
+               ospf_lsa_unlock(&new);
+               new = NULL;
+               stream_free(s);
+               return NULL;
+       }
+
+       new->area = area;
+       SET_FLAG(new->flags, OSPF_LSA_SELF);
+       memcpy(new->data, lsah, length);
+       stream_free(s);
+
+       return new;
+}
+
+/* Process the origination of an Extended Prefix Opaque LSA
+ * for every extended prefix TLV */
+static int ospf_ext_pref_lsa_originate1(struct ospf_area *area,
+                                       struct ext_itf *exti)
+{
+       struct ospf_lsa *new;
+       int rc = -1;
+
+
+       /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */
+       if ((new = ospf_ext_pref_lsa_new(area, exti)) == NULL) {
+               zlog_warn("EXT: ospf_ext_pref_lsa_new() error");
+               return rc;
+       }
+
+       /* Install this LSA into LSDB. */
+       if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT: ospf_lsa_install() error");
+               ospf_lsa_unlock(&new);
+               return rc;
+       }
+
+       /* Now this Extended Prefix Opaque LSA info parameter entry has
+        * associated LSA. */
+       SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+
+       /* Update new LSA origination count. */
+       area->ospf->lsa_originate_count++;
+
+       /* Flood new LSA through area. */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               char area_id[INET_ADDRSTRLEN];
+               strncpy(area_id, inet_ntoa(area->area_id), INET_ADDRSTRLEN);
+               zlog_debug(
+                       "EXT: LSA[Type%d:%s]: Originate Opaque-LSA/Extended "
+                       "Prefix Opaque LSA: Area(%s), Link(%s)",
+                       new->data->type, inet_ntoa(new->data->id), area_id,
+                       exti->ifp->name);
+               ospf_lsa_header_dump(new->data);
+       }
+
+       rc = 0;
+
+       return rc;
+}
+
+/* Process the origination of an Extended Link Opaque LSA
+ * for every extended link TLV */
+static int ospf_ext_link_lsa_originate1(struct ospf_area *area,
+                                       struct ext_itf *exti)
+{
+       struct ospf_lsa *new;
+       int rc = -1;
+
+       /* Create new Opaque-LSA/Extended Link Opaque LSA instance. */
+       if ((new = ospf_ext_link_lsa_new(area, exti)) == NULL) {
+               zlog_warn("EXT: ospf_ext_link_lsa_new() error");
+               return rc;
+       }
+
+       /* Install this LSA into LSDB. */
+       if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT: ospf_lsa_install() error");
+               ospf_lsa_unlock(&new);
+               return rc;
+       }
+
+       /* Now this link-parameter entry has associated LSA. */
+       SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+
+       /* Update new LSA origination count. */
+       area->ospf->lsa_originate_count++;
+
+       /* Flood new LSA through area. */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               char area_id[INET_ADDRSTRLEN];
+               strncpy(area_id, inet_ntoa(area->area_id), INET_ADDRSTRLEN);
+               zlog_debug(
+                       "EXT: LSA[Type%d:%s]: Originate Opaque-LSA/Extended "
+                       "Link Opaque LSA: Area(%s), Link(%s)",
+                       new->data->type, inet_ntoa(new->data->id), area_id,
+                       exti->ifp->name);
+               ospf_lsa_header_dump(new->data);
+       }
+
+       rc = 0;
+
+       return rc;
+}
+
+/* Trigger the origination of Extended Prefix Opaque LSAs */
+static int ospf_ext_pref_lsa_originate(void *arg)
+{
+       struct ospf_area *area = (struct ospf_area *)arg;
+       struct listnode *node;
+       struct ext_itf *exti;
+       int rc = -1;
+
+       if (!OspfEXT.enabled) {
+               zlog_info(
+                       "EXT: Segment Routing "
+                       "functionality is Disabled now.");
+               rc = 0; /* This is not an error case. */
+               return rc;
+       }
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "EXT (ospf_ext_pref_lsa_originate) "
+                       "Start Originate Prefix LSA for area %s",
+                       inet_ntoa(area->area_id));
+
+       /* Check if Extended Prefix Opaque LSA is already engaged */
+       for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+
+               /* Process only Prefix SID */
+               if (exti->stype != PREF_SID)
+                       continue;
+
+               /* Process only Extended Prefix with valid Area ID */
+               if ((exti->area == NULL)
+                   || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
+                       continue;
+
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       if (CHECK_FLAG(exti->flags,
+                                      EXT_LPFLG_LSA_FORCED_REFRESH)) {
+                               zlog_warn("EXT: Refresh instead of Originate");
+                               UNSET_FLAG(exti->flags,
+                                          EXT_LPFLG_LSA_FORCED_REFRESH);
+                               ospf_ext_pref_lsa_schedule(exti,
+                                                          REFRESH_THIS_LSA);
+                       }
+                       continue;
+               }
+
+               /* Ok, let's try to originate an LSA */
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT: Let's finally reoriginate the "
+                               "LSA 7.0.0.%d for Itf %s",
+                               exti->instance,
+                               exti->ifp ? exti->ifp->name : "");
+               ospf_ext_pref_lsa_originate1(area, exti);
+       }
+
+       rc = 0;
+       return rc;
+}
+
+/* Trigger the origination of Extended Link Opaque LSAs */
+static int ospf_ext_link_lsa_originate(void *arg)
+{
+       struct ospf_area *area = (struct ospf_area *)arg;
+       struct listnode *node;
+       struct ext_itf *exti;
+       int rc = -1;
+
+       if (!OspfEXT.enabled) {
+               zlog_info(
+                       "EXT: Segment Routing "
+                       "functionality is Disabled now.");
+               rc = 0; /* This is not an error case. */
+               return rc;
+       }
+
+       /* Check if Extended Prefix Opaque LSA is already engaged */
+       for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+               /* Process only Adjacency or LAN SID */
+               if (exti->stype == PREF_SID)
+                       continue;
+
+               /* Process only Extended Link with valid Area ID */
+               if ((exti->area == NULL)
+                   || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
+                       continue;
+
+               /* Check if LSA not already engaged */
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       if (CHECK_FLAG(exti->flags,
+                                      EXT_LPFLG_LSA_FORCED_REFRESH)) {
+                               zlog_warn("EXT: Refresh instead of Originate");
+                               UNSET_FLAG(exti->flags,
+                                          EXT_LPFLG_LSA_FORCED_REFRESH);
+                               ospf_ext_link_lsa_schedule(exti,
+                                                          REFRESH_THIS_LSA);
+                       }
+                       continue;
+               }
+
+               /* Ok, let's try to originate an LSA */
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT Let's finally reoriginate the "
+                               "LSA 8.0.0.%d for Itf %s through the Area %s",
+                               exti->instance,
+                               exti->ifp ? exti->ifp->name : "-",
+                               inet_ntoa(area->area_id));
+               ospf_ext_link_lsa_originate1(area, exti);
+       }
+
+       rc = 0;
+       return rc;
+}
+
+/* Refresh an Extended Prefix Opaque LSA */
+static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
+{
+       struct ospf_lsa *new = NULL;
+       struct ospf_area *area = lsa->area;
+       struct ospf *top;
+       struct ext_itf *exti;
+
+       if (!OspfEXT.enabled) {
+               /*
+                * This LSA must have flushed before due to Extended Prefix
+                * Opaque LSA status change.
+                * It seems a slip among routers in the routing domain.
+                */
+               zlog_info("EXT: Segment Routing functionality is Disabled");
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Lookup this lsa corresponding Extended parameters */
+       if ((exti = lookup_ext_by_instance(lsa)) == NULL) {
+               zlog_warn("EXT: Invalid parameter LSA ID");
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Check if Interface was not disable in the interval */
+       if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) {
+               zlog_warn("EXT: Interface was Disabled: Flush it!");
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* If the lsa's age reached to MaxAge, start flushing procedure. */
+       if (IS_LSA_MAXAGE(lsa)) {
+               if (exti)
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(lsa);
+               return NULL;
+       }
+
+       /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */
+       if (exti)
+               new = ospf_ext_pref_lsa_new(area, exti);
+
+       if (new == NULL) {
+               zlog_warn("EXT: ospf_ext_pref_lsa_new() error");
+               return NULL;
+       }
+       new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+       /* Install this LSA into LSDB. */
+       /* Given "lsa" will be freed in the next function. */
+       /* As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use
+        * ospf_lookup() to get ospf instance */
+       if (area)
+               top = area->ospf;
+       else
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+
+       if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT: ospf_lsa_install() error");
+               ospf_lsa_unlock(&new);
+               return NULL;
+       }
+
+       /* Flood updated LSA through the Prefix Area according to the RFC7684 */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       /* Debug logging. */
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               zlog_debug("EXT: LSA[Type%d:%s]: Refresh Extended Prefix LSA",
+                          new->data->type, inet_ntoa(new->data->id));
+               ospf_lsa_header_dump(new->data);
+       }
+
+       return new;
+}
+
+/* Refresh an Extended Link Opaque LSA */
+static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa)
+{
+       struct ext_itf *exti;
+       struct ospf_area *area = lsa->area;
+       struct ospf *top = area->ospf;
+       struct ospf_lsa *new = NULL;
+
+       if (!OspfEXT.enabled) {
+               /*
+                * This LSA must have flushed before due to OSPF-SR status
+                * change. It seems a slip among routers in the routing domain.
+                */
+               zlog_info(
+                       "EXT (ospf_ext_link_lsa_refresh): Segment Routing "
+                       "functionality is Disabled");
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Lookup this lsa corresponding Extended parameters */
+       if ((exti = lookup_ext_by_instance(lsa)) == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_lsa_refresh): Invalid parameter "
+                       "LSA ID");
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Check if Interface was not disable in the interval */
+       if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_lsa_refresh): Interface was "
+                       "Disabled: Flush it!");
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* If the lsa's age reached to MaxAge, start flushing procedure. */
+       if (IS_LSA_MAXAGE(lsa)) {
+               if (exti)
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(lsa);
+               return NULL;
+       }
+
+       /* Create new Opaque-LSA/MPLS-TE instance. */
+       if ((new = ospf_ext_link_lsa_new(area, exti)) == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_lsa_refresh): Error creating "
+                       "new LSA");
+               return NULL;
+       }
+       new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+       /* Install this LSA into LSDB. */
+       /* Given "lsa" will be freed in the next function. */
+       if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_lsa_refresh): Error installing "
+                       "new LSA");
+               ospf_lsa_unlock(&new);
+               return NULL;
+       }
+
+       /* Flood updated LSA through the link Area according to the RFC7684 */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       /* Debug logging. */
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               zlog_debug(
+                       "EXT (ospf_ext_link_lsa_refresh): LSA[Type%d:%s]: "
+                       "Refresh Extended Link LSA",
+                       new->data->type, inet_ntoa(new->data->id));
+               ospf_lsa_header_dump(new->data);
+       }
+
+       return new;
+}
+
+/* Schedule Extended Prefix Opaque LSA origination/refreshment/flushing */
+static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode)
+{
+       struct ospf_lsa lsa;
+       struct lsa_header lsah;
+       struct ospf *top;
+       u_int32_t tmp;
+
+       memset(&lsa, 0, sizeof(lsa));
+       memset(&lsah, 0, sizeof(lsah));
+
+       /* Sanity Check */
+       if (exti == NULL)
+               return;
+
+       /* Check if the corresponding link is ready to be flooded */
+       if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
+               return;
+
+       zlog_debug(
+               "EXT (ospf_ext_pref_lsa_schedule): Schedule %s%s%s LSA "
+               "for interface %s",
+               opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+               opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+               opcode == FLUSH_THIS_LSA ? "Flush" : "",
+               exti->ifp ? exti->ifp->name : "-");
+
+       /* Set LSA header information */
+       if (exti->area == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_pref_lsa_schedule): Flooding is "
+                       "Area scope but area is not yet set");
+               if (OspfEXT.area == NULL) {
+                       top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+                       OspfEXT.area = ospf_area_lookup_by_area_id(
+                               top, OspfEXT.area_id);
+               }
+               exti->area = OspfEXT.area;
+       }
+       lsa.area = exti->area;
+       lsa.data = &lsah;
+       lsah.type = OSPF_OPAQUE_AREA_LSA;
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance);
+       lsah.id.s_addr = htonl(tmp);
+
+       switch (opcode) {
+       case REORIGINATE_THIS_LSA:
+               ospf_opaque_lsa_reoriginate_schedule(
+                       (void *)exti->area, OSPF_OPAQUE_AREA_LSA,
+                       OPAQUE_TYPE_EXTENDED_PREFIX_LSA);
+               break;
+       case REFRESH_THIS_LSA:
+               ospf_opaque_lsa_refresh_schedule(&lsa);
+               break;
+       case FLUSH_THIS_LSA:
+               UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(&lsa);
+               break;
+       default:
+               zlog_warn("EXT (ospf_ext_pref_lsa_schedule): Unknown opcode");
+               break;
+       }
+
+       return;
+}
+
+/* Schedule Extended Link Opaque LSA origination/refreshment/flushing */
+static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode)
+{
+       struct ospf_lsa lsa;
+       struct lsa_header lsah;
+       struct ospf *top;
+       u_int32_t tmp;
+
+       memset(&lsa, 0, sizeof(lsa));
+       memset(&lsah, 0, sizeof(lsah));
+
+       /* Sanity Check */
+       if (exti == NULL)
+               return;
+
+       /* Check if the corresponding link is ready to be flooded */
+       if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
+               return;
+
+       zlog_debug(
+               "EXT (ospf_ext_link_lsa_schedule): Schedule %s%s%s LSA "
+               "for interface %s",
+               opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+               opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+               opcode == FLUSH_THIS_LSA ? "Flush" : "",
+               exti->ifp ? exti->ifp->name : "-");
+
+       /* Set LSA header information */
+       if (exti->area == NULL) {
+               zlog_warn(
+                       "EXT (ospf_ext_link_lsa_schedule): Flooding is "
+                       "Area scope but area is not yet set");
+               if (OspfEXT.area == NULL) {
+                       top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+                       OspfEXT.area = ospf_area_lookup_by_area_id(
+                               top, OspfEXT.area_id);
+               }
+               exti->area = OspfEXT.area;
+       }
+       lsa.area = exti->area;
+       lsa.data = &lsah;
+       lsah.type = OSPF_OPAQUE_AREA_LSA;
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+       lsah.id.s_addr = htonl(tmp);
+
+       switch (opcode) {
+       case REORIGINATE_THIS_LSA:
+               ospf_opaque_lsa_reoriginate_schedule(
+                       (void *)exti->area, OSPF_OPAQUE_AREA_LSA,
+                       OPAQUE_TYPE_EXTENDED_LINK_LSA);
+               break;
+       case REFRESH_THIS_LSA:
+               ospf_opaque_lsa_refresh_schedule(&lsa);
+               break;
+       case FLUSH_THIS_LSA:
+               UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(&lsa);
+               break;
+       default:
+               zlog_warn("EXT (ospf_ext_link_lsa_schedule): Unknown opcode");
+               break;
+       }
+
+       return;
+}
+
+/* Schedule Extended Link or Prefix depending of the Type of LSA */
+static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op)
+{
+
+       if (exti->stype == PREF_SID)
+               ospf_ext_pref_lsa_schedule(exti, op);
+       else
+               ospf_ext_link_lsa_schedule(exti, op);
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty show functions.
+ *------------------------------------------------------------------------*/
+/* Cisco experimental SubTLV */
+static u_int16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty,
+                                               struct tlv_header *tlvh)
+{
+       struct ext_subtlv_rmt_itf_addr *top;
+       top = (struct ext_subtlv_rmt_itf_addr *)tlvh;
+
+       vty_out(vty,
+               "  Remote Interface Address Sub-TLV: Length %d\n        "
+               "Address: %s\n",
+               ntohs(top->header.length), inet_ntoa(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Adjacency SID SubTLV */
+static u_int16_t show_vty_ext_link_adj_sid(struct vty *vty,
+                                          struct tlv_header *tlvh)
+{
+       struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh;
+
+       vty_out(vty,
+               "  Adj-SID Sub-TLV: Length %d\n\tFlags: "
+               "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %d\n",
+               ntohs(top->header.length), top->flags, top->mtid, top->weight,
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
+                                                                    : "Index",
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+                       ? GET_LABEL(ntohl(top->value))
+                       : ntohl(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* LAN Adjacency SubTLV */
+static u_int16_t show_vty_ext_link_lan_adj_sid(struct vty *vty,
+                                              struct tlv_header *tlvh)
+{
+       struct ext_subtlv_lan_adj_sid *top =
+               (struct ext_subtlv_lan_adj_sid *)tlvh;
+
+       vty_out(vty,
+               "  LAN-Adj-SID Sub-TLV: Length %d\n\tFlags: "
+               "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: "
+               "%s\n\tLabel: %d\n",
+               ntohs(top->header.length), top->flags, top->mtid, top->weight,
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
+                                                                    : "Index",
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+                       ? GET_LABEL(ntohl(top->value))
+                       : ntohl(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+static u_int16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh)
+{
+       vty_out(vty, "    Unknown TLV: [type(0x%x), length(0x%x)]\n",
+               ntohs(tlvh->type), ntohs(tlvh->length));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Extended Link Sub TLVs */
+static u_int16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext)
+{
+       struct ext_tlv_link *top = (struct ext_tlv_link *)ext;
+       struct tlv_header *tlvh;
+       u_int16_t length = ntohs(top->header.length) - 3 * sizeof(u_int32_t);
+       u_int16_t sum = 0;
+
+       vty_out(vty,
+               "  Extended Link TLV: Length %d\n       Link Type: 0x%x\n"
+               "       Link ID: %s\n",
+               ntohs(top->header.length), top->link_type,
+               inet_ntoa(top->link_id));
+       vty_out(vty, "  Link data: %s\n", inet_ntoa(top->link_data));
+
+       tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+                                    + EXT_TLV_LINK_SIZE);
+       for (; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_SUBTLV_ADJ_SID:
+                       sum += show_vty_ext_link_adj_sid(vty, tlvh);
+                       break;
+               case EXT_SUBTLV_LAN_ADJ_SID:
+                       sum += show_vty_ext_link_lan_adj_sid(vty, tlvh);
+                       break;
+               case EXT_SUBTLV_RMT_ITF_ADDR:
+                       sum += show_vty_ext_link_rmt_itf_addr(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+       return sum + sizeof(struct ext_tlv_link);
+}
+
+/* Extended Link TLVs */
+static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa)
+{
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct tlv_header *tlvh;
+       u_int16_t length = 0, sum = 0;
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_TLV_LINK:
+                       sum += show_vty_link_info(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+       return;
+}
+
+/* Prefix SID SubTLV */
+static u_int16_t show_vty_ext_pref_pref_sid(struct vty *vty,
+                                           struct tlv_header *tlvh)
+{
+       struct ext_subtlv_prefix_sid *top =
+               (struct ext_subtlv_prefix_sid *)tlvh;
+
+       vty_out(vty,
+               "  Prefix SID Sub-TLV: Length %d\n\tAlgorithm: "
+               "%d\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %d\n",
+               ntohs(top->header.length), top->algorithm, top->flags,
+               top->mtid,
+               CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label"
+                                                                  : "Index",
+               CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG)
+                       ? GET_LABEL(ntohl(top->value))
+                       : ntohl(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Extended Prefix SubTLVs */
+static u_int16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext)
+{
+       struct ext_tlv_prefix *top = (struct ext_tlv_prefix *)ext;
+       struct tlv_header *tlvh;
+       u_int16_t length = ntohs(top->header.length) - 2 * sizeof(u_int32_t);
+       u_int16_t sum = 0;
+
+       vty_out(vty,
+               "  Extended Prefix TLV: Length %d\n\tRoute Type: %d\n"
+               "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %s/%d\n",
+               ntohs(top->header.length), top->route_type, top->af, top->flags,
+               inet_ntoa(top->address), top->pref_length);
+
+       tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+                                    + EXT_TLV_PREFIX_SIZE);
+       for (; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_SUBTLV_PREFIX_SID:
+                       sum += show_vty_ext_pref_pref_sid(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+       return sum + sizeof(struct ext_tlv_prefix);
+}
+
+/* Extended Prefix TLVs */
+static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa)
+{
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct tlv_header *tlvh;
+       u_int16_t length = 0, sum = 0;
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_TLV_PREFIX:
+                       sum += show_vty_pref_info(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+       return;
+}
diff --git a/ospfd/ospf_ext.h b/ospfd/ospf_ext.h
new file mode 100644 (file)
index 0000000..5e18554
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute
+ * Advertisement
+ *
+ * Module name: Extended Prefix/Link Opaque LSA header definition
+ *
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2016 - 2017 Orange Labs http://www.orange.com
+ *
+ * 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _FRR_OSPF_EXT_PREF_H_
+#define _FRR_OSPF_EXT_PREF_H_
+
+/*
+ * Opaque LSA's link state ID for Extended Prefix/Link is
+ * structured as follows.
+ *
+ *        24       16        8        0
+ * +--------+--------+--------+--------+
+ * |  7/8   |........|........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<------- Instance ------->|
+ *
+ *
+ * Type:      IANA has assigned '7' for Extended Prefix Opaque LSA
+ *                         and '8' for Extended Link Opaque LSA
+ * Instance:  User may select arbitrary 24-bit values to identify
+ *            different instances of Extended Prefix/Link Opaque LSA
+ *
+ */
+
+/*
+ *        24       16        8        0
+ * +--------+--------+--------+--------+ ---
+ * |   LS age        |Options |  10,11 |  A
+ * +--------+--------+--------+--------+  |  Standard (Opaque) LSA header;
+ * |   7/8  |        Instance          |  |
+ * +--------+--------+--------+--------+  |  Type 10 or 11 are used for Extended
+ * |        Advertising router         |  |  Prefix Opaque LSA
+ * +--------+--------+--------+--------+  |
+ * |        LS sequence number         |  |  Type 10 only is used for Extended
+ * +--------+--------+--------+--------+  |  Link Opaque LSA
+ * |   LS checksum   |     Length      |  V
+ * +--------+--------+--------+--------+ ---
+ * |      Type       |     Length      |  A
+ * +--------+--------+--------+--------+  |  TLV part for Extended Prefix/Link
+ * |                                   |  |  Opaque LSA;
+ * ~              Values ...           ~  |  Values might be structured as a set
+ * |                                   |  V  of sub-TLVs.
+ * +--------+--------+--------+--------+ ---
+ */
+
+/* Global use constant numbers */
+
+#define        MAX_LEGAL_EXT_INSTANCE_NUM      (0xffff)
+#define LEGAL_EXT_INSTANCE_RANGE(i)    (0 <= (i) && (i) <= 0xffff)
+
+/* Flags to manage Extended Link/Prefix Opaque LSA */
+#define EXT_LPFLG_LSA_INACTIVE          0x00
+#define EXT_LPFLG_LSA_ACTIVE            0x01
+#define EXT_LPFLG_LSA_ENGAGED           0x02
+#define EXT_LPFLG_LSA_LOOKUP_DONE       0x04
+#define EXT_LPFLG_LSA_FORCED_REFRESH    0x08
+#define EXT_LPFLG_FIB_ENTRY_SET         0x10
+
+/*
+ * Following section defines TLV (tag, length, value) structures,
+ * used in Extended Prefix/Link Opaque LSA.
+ */
+
+/* Extended Prefix TLV Route Types */
+#define EXT_TLV_PREF_ROUTE_UNSPEC      0
+#define EXT_TLV_PREF_ROUTE_INTRA_AREA  1
+#define EXT_TLV_PREF_ROUTE_INTER_AREA  3
+#define EXT_TLV_PREF_ROUTE_AS_EXT      5
+#define EXT_TLV_PREF_ROUTE_NSSA_EXT    7
+
+/* Extended Prefix and Extended Prefix Range TLVs'
+ * Address family flag for IPv4 */
+#define EXT_TLV_PREF_AF_IPV4           0
+
+/* Extended Prefix TLV Flags */
+#define EXT_TLV_PREF_AFLG              0x80
+#define EXT_TLV_PREF_NFLG              0x40
+
+/* Extended Prefix Range TLV Flags */
+#define EXT_TLV_PREF_RANGE_IAFLG       0x80
+
+/* ERO subtlvs Flags */
+#define EXT_SUBTLV_ERO_LFLG            0x80
+
+/* Extended Prefix TLV see RFC 7684 section 2.1 */
+#define EXT_TLV_PREFIX                 1
+#define EXT_TLV_PREFIX_SIZE            8
+struct ext_tlv_prefix {
+       struct tlv_header header;
+       u_int8_t route_type;
+       u_int8_t pref_length;
+       u_int8_t af;
+       u_int8_t flags;
+       struct in_addr address;
+};
+
+/* Extended Link TLV see RFC 7684 section 3.1 */
+#define EXT_TLV_LINK                   1
+#define EXT_TLV_LINK_SIZE              12
+struct ext_tlv_link {
+       struct tlv_header header;
+       u_int8_t link_type;
+       u_int8_t reserved[3];
+       struct in_addr link_id;
+       struct in_addr link_data;
+};
+
+/* Remote Interface Address Sub-TLV, Cisco experimental use Sub-TLV */
+#define EXT_SUBTLV_RMT_ITF_ADDR         32768
+#define EXT_SUBTLV_RMT_ITF_ADDR_SIZE   4
+struct ext_subtlv_rmt_itf_addr {
+       struct tlv_header header;
+       struct in_addr value;
+};
+
+/* Internal structure to manage Extended Link/Prefix Opaque LSA */
+struct ospf_ext_lp {
+       bool enabled;
+
+       /* Flags to manage this Extended Prefix/Link Opaque LSA */
+       u_int32_t flags;
+
+       /* Scope is area Opaque Type 10 or AS Opaque LSA Type 11 for
+        * Extended Prefix and area Opaque Type 10 for Extended Link */
+       u_int8_t scope;
+
+       /* area pointer if flooding is Type 10 Null if flooding is AS scope */
+       struct ospf_area *area;
+       struct in_addr area_id;
+
+       /* List of interface with Segment Routing enable */
+       struct list *iflist;
+};
+
+/* Structure to aggregate interfaces information for Extended Prefix/Link */
+struct ext_itf {
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       u_int32_t instance;
+       u_int8_t type; /* Extended Prefix (7) or Link (8) */
+
+       /* Reference pointer to a Zebra-interface. */
+       struct interface *ifp;
+
+       /* Area info in which this SR link belongs to. */
+       struct ospf_area *area;
+
+       /* Flags to manage this link parameters. */
+       u_int32_t flags;
+
+       /* SID type: Node, Adjacency or LAN Adjacency */
+       enum sid_type stype;
+
+       /* extended link/prefix TLV information */
+       struct ext_tlv_prefix prefix;
+       struct ext_subtlv_prefix_sid node_sid;
+       struct ext_tlv_link link;
+       struct ext_subtlv_adj_sid adj_sid[2];
+       struct ext_subtlv_lan_adj_sid lan_sid[2];
+
+       /* cisco experimental subtlv */
+       struct ext_subtlv_rmt_itf_addr rmt_itf_addr;
+};
+
+/* Prototypes. */
+extern int ospf_ext_init(void);
+extern void ospf_ext_term(void);
+extern void ospf_ext_update_sr(bool);
+extern int ospf_ext_schedule_prefix_index(struct interface *, u_int32_t,
+                                         struct prefix_ipv4 *);
+#endif /* _FRR_OSPF_EXT_PREF_H_ */
index cdc9b929fa1f3b3f179b82a842b532faac6a2b8c..1332104b0afdd6a384f2b1cf6ff14a4785107d7c 100644 (file)
@@ -53,3 +53,5 @@ DEFINE_MTYPE(OSPFD, OSPF_IF_PARAMS, "OSPF if params")
 DEFINE_MTYPE(OSPFD, OSPF_MESSAGE, "OSPF message")
 DEFINE_MTYPE(OSPFD, OSPF_MPLS_TE, "OSPF MPLS parameters")
 DEFINE_MTYPE(OSPFD, OSPF_PCE_PARAMS, "OSPF PCE parameters")
+DEFINE_MTYPE(OSPFD, OSPF_EXT_PARAMS, "OSPF Extended parameters")
+DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters")
index 5f5960eec7cc48c01ffdd9ba047a5f587bd24335..50c6f33ecfbc4c5d03ab4747d9c8c5461f96573b 100644 (file)
@@ -52,5 +52,7 @@ DECLARE_MTYPE(OSPF_IF_PARAMS)
 DECLARE_MTYPE(OSPF_MESSAGE)
 DECLARE_MTYPE(OSPF_MPLS_TE)
 DECLARE_MTYPE(OSPF_PCE_PARAMS)
+DECLARE_MTYPE(OSPF_SR_PARAMS)
+DECLARE_MTYPE(OSPF_EXT_PARAMS)
 
 #endif /* _QUAGGA_OSPF_MEMORY_H */
index 6f9da925428973e3e238f47e976bdb690e42230b..292d5e818609fa06fd57ca08f503ee62df187058 100644 (file)
 #include "ospfd/ospf_route.h"
 #include "ospfd/ospf_ase.h"
 #include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
 
 DEFINE_MTYPE_STATIC(OSPFD, OSPF_OPAQUE_FUNCTAB, "OSPF opaque function table")
 DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_TYPE, "OSPF opaque per-type info")
@@ -59,9 +63,6 @@ DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_ID, "OSPF opaque per-ID info")
  * Followings are initialize/terminate functions for Opaque-LSAs handling.
  *------------------------------------------------------------------------*/
 
-#include "ospfd/ospf_te.h"
-#include "ospfd/ospf_ri.h"
-
 #ifdef SUPPORT_OSPF_API
 int ospf_apiserver_init(void);
 void ospf_apiserver_term(void);
@@ -85,9 +86,17 @@ void ospf_opaque_init(void)
        if (ospf_mpls_te_init() != 0)
                exit(1);
 
+       /* Segment Routing init */
+       if (ospf_sr_init() != 0)
+               exit(1);
+
        if (ospf_router_info_init() != 0)
                exit(1);
 
+       /* Force Extended Prefix/Link to Type 10 */
+       if (ospf_ext_init() != 0)
+               exit(1);
+
 #ifdef SUPPORT_OSPF_API
        if ((ospf_apiserver_enable) && (ospf_apiserver_init() != 0))
                exit(1);
@@ -102,6 +111,10 @@ void ospf_opaque_term(void)
 
        ospf_router_info_term();
 
+       ospf_ext_term();
+
+       ospf_sr_term();
+
 #ifdef SUPPORT_OSPF_API
        ospf_apiserver_term();
 #endif /* SUPPORT_OSPF_API */
@@ -209,6 +222,12 @@ static const char *ospf_opaque_type_name(u_char opaque_type)
        case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
                name = "Router Information LSA";
                break;
+       case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+               name = "Extended Prefix Opaque LSA";
+               break;
+       case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+               name = "Extended Link Opaque LSA";
+               break;
        default:
                if (OPAQUE_TYPE_RANGE_UNASSIGNED(opaque_type))
                        name = "Unassigned";
index 9dc1f92f4dc16ab0decc992dd94c3fdb1405342d..323ad75b78623098fe0b2ad9e5651880986d48bf 100644 (file)
@@ -59,7 +59,9 @@
 #define OPAQUE_TYPE_L1VPN_LSA                          5
 #define OPAQUE_TYPE_ROUTER_INFORMATION_LSA             4
 #define OPAQUE_TYPE_INTER_AS_LSA                       6
-#define OPAQUE_TYPE_MAX                                6
+#define OPAQUE_TYPE_EXTENDED_PREFIX_LSA                7
+#define OPAQUE_TYPE_EXTENDED_LINK_LSA                  8
+#define OPAQUE_TYPE_MAX                                8
 
 /* Followings types are proposed in internet-draft documents. */
 #define OPAQUE_TYPE_8021_QOSPF                         129
index ead6923435399ac3018a97a108df0a0bdb6049a1..0a6917dce7f1420b3dfeedb80b08ebd394a0138d 100644 (file)
@@ -3,9 +3,8 @@
  * with support of RFC5088 PCE Capabilites announcement
  *
  * Module name: Router Information
- * Version:     0.99.22
- * Created:     2012-02-01 by Olivier Dugeon
- * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/
  *
  * This file is part of GNU Quagga.
  *
@@ -39,6 +38,7 @@
 #include "thread.h"
 #include "hash.h"
 #include "sockunion.h" /* for inet_aton() */
+#include "mpls.h"
 
 #include "ospfd/ospfd.h"
 #include "ospfd/ospf_interface.h"
@@ -55,8 +55,8 @@
 #include "ospfd/ospf_route.h"
 #include "ospfd/ospf_ase.h"
 #include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_sr.h"
 #include "ospfd/ospf_ri.h"
-#include "ospfd/ospf_te.h"
 
 /* Store Router Information PCE TLV and SubTLV in network byte order. */
 struct ospf_pce_info {
@@ -69,6 +69,20 @@ struct ospf_pce_info {
        struct ri_pce_subtlv_cap_flag pce_cap_flag;
 };
 
+/* Store Router Information Segment Routing TLV and SubTLV in network byte order. */
+struct ospf_ri_sr_info {
+       bool enabled;
+       /* Algorithms supported by the node */
+       struct ri_sr_tlv_sr_algorithm algo;
+       /*
+        * Segment Routing Global Block i.e. label range
+        * Only one range supported in this code
+        */
+       struct ri_sr_tlv_sid_label_range range;
+       /* Maximum SID Depth supported by the node */
+       struct ri_sr_tlv_node_msd msd;
+};
+
 /* Following structure are internal use only. */
 struct ospf_router_info {
        bool enabled;
@@ -77,7 +91,7 @@ struct ospf_router_info {
        u_int8_t scope;
 
 /* Flags to manage this router information. */
-#define RIFLG_LSA_ENGAGED                      0x1
+#define RIFLG_LSA_ENGAGED              0x1
 #define RIFLG_LSA_FORCED_REFRESH       0x2
        u_int32_t flags;
 
@@ -90,6 +104,9 @@ struct ospf_router_info {
 
        /* Store PCE capability LSA */
        struct ospf_pce_info pce_info;
+
+       /* Store SR capability LSA */
+       struct ospf_ri_sr_info sr_info;
 };
 
 /*
@@ -113,15 +130,19 @@ static int ospf_router_info_lsa_originate(void *arg);
 static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa);
 static void ospf_router_info_lsa_schedule(enum lsa_opcode opcode);
 static void ospf_router_info_register_vty(void);
+static int ospf_router_info_lsa_update(struct ospf_lsa *lsa);
 static void del_pce_info(void *val);
 
 int ospf_router_info_init(void)
 {
 
+       zlog_info("RI -> Initialize Router Information");
+
        memset(&OspfRI, 0, sizeof(struct ospf_router_info));
        OspfRI.enabled = false;
        OspfRI.registered = 0;
        OspfRI.scope = OSPF_OPAQUE_AS_LSA;
+       OspfRI.area_id.s_addr = 0;
        OspfRI.flags = 0;
 
        /* Initialize pce domain and neighbor list */
@@ -131,6 +152,9 @@ int ospf_router_info_init(void)
        OspfRI.pce_info.pce_neighbor = list_new();
        OspfRI.pce_info.pce_neighbor->del = del_pce_info;
 
+       /* Initialize Segment Routing information structure */
+       OspfRI.sr_info.enabled = false;
+
        ospf_router_info_register_vty();
 
        return 0;
@@ -143,19 +167,22 @@ static int ospf_router_info_register(u_int8_t scope)
        if (OspfRI.registered)
                return rc;
 
-       zlog_info("Register Router Information with scope %s(%d)",
+       zlog_info("RI -> Register Router Information with scope %s(%d)",
                  scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", scope);
        rc = ospf_register_opaque_functab(
                scope, OPAQUE_TYPE_ROUTER_INFORMATION_LSA,
                NULL, /* new interface */
                NULL, /* del interface */
-               ospf_router_info_ism_change, ospf_router_info_nsm_change,
+               ospf_router_info_ism_change,
+               ospf_router_info_nsm_change,
                ospf_router_info_config_write_router,
                NULL, /* Config. write interface */
                NULL, /* Config. write debug */
-               ospf_router_info_show_info, ospf_router_info_lsa_originate,
-               ospf_router_info_lsa_refresh, NULL, /* new_lsa_hook */
-               NULL);                              /* del_lsa_hook */
+               ospf_router_info_show_info,
+               ospf_router_info_lsa_originate,
+               ospf_router_info_lsa_refresh,
+               ospf_router_info_lsa_update,
+               NULL); /* del_lsa_hook */
 
        if (rc != 0) {
                zlog_warn(
@@ -204,6 +231,20 @@ static void del_pce_info(void *val)
        return;
 }
 
+/* Catch RI LSA flooding Scope for ospf_ext.[h,c] code */
+struct scope_info ospf_router_info_get_flooding_scope(void)
+{
+       struct scope_info flooding_scope;
+       if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
+               flooding_scope.scope = OSPF_OPAQUE_AS_LSA;
+               flooding_scope.area_id.s_addr = 0;
+               return flooding_scope;
+       }
+       flooding_scope.scope = OSPF_OPAQUE_AREA_LSA;
+       flooding_scope.area_id.s_addr = OspfRI.area_id.s_addr;
+       return flooding_scope;
+}
+
 /*------------------------------------------------------------------------*
  * Followings are control functions for ROUTER INFORMATION parameters
  *management.
@@ -399,6 +440,84 @@ static void set_pce_cap_flag(u_int32_t cap, struct ospf_pce_info *pce)
        return;
 }
 
+/* Segment Routing TLV setter */
+
+/* Algorithm SubTLV - section 3.1 */
+static void set_sr_algorithm(u_int8_t algo)
+{
+
+       OspfRI.sr_info.algo.value[0] = algo;
+       for (int i = 1; i < ALGORITHM_COUNT; i++)
+               OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
+
+       /* Set TLV type and length == only 1 Algorithm */
+       TLV_TYPE(OspfRI.sr_info.algo) = htons(RI_SR_TLV_SR_ALGORITHM);
+       TLV_LEN(OspfRI.sr_info.algo) = htons(sizeof(u_int8_t));
+
+       return;
+}
+
+/* unset Aglogithm SubTLV */
+static void unset_sr_algorithm(u_int8_t algo)
+{
+
+       for (int i = 0; i < ALGORITHM_COUNT; i++)
+               OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
+
+       /* Unset TLV type and length */
+       TLV_TYPE(OspfRI.sr_info.algo) = htons(0);
+       TLV_LEN(OspfRI.sr_info.algo) = htons(0);
+
+       return;
+}
+
+/* Segment Routing Global Block SubTLV - section 3.2 */
+static void set_sr_sid_label_range(struct sr_srgb srgb)
+{
+       /* Set Header */
+       TLV_TYPE(OspfRI.sr_info.range) = htons(RI_SR_TLV_SID_LABEL_RANGE);
+       TLV_LEN(OspfRI.sr_info.range) =
+               htons(SUBTLV_SID_LABEL_SIZE + sizeof(u_int32_t));
+       /* Set Range Size */
+       OspfRI.sr_info.range.size = htonl(SET_RANGE_SIZE(srgb.range_size));
+       /* Set Lower bound label SubTLV */
+       TLV_TYPE(OspfRI.sr_info.range.lower) = htons(SUBTLV_SID_LABEL);
+       TLV_LEN(OspfRI.sr_info.range.lower) = htons(SID_RANGE_LABEL_LENGTH);
+       OspfRI.sr_info.range.lower.value = htonl(SET_LABEL(srgb.lower_bound));
+
+       return;
+}
+
+/* Unset this SRGB SubTLV */
+static void unset_sr_sid_label_range()
+{
+
+       TLV_TYPE(OspfRI.sr_info.range) = htons(0);
+       TLV_LEN(OspfRI.sr_info.range) = htons(0);
+       TLV_TYPE(OspfRI.sr_info.range.lower) = htons(0);
+       TLV_LEN(OspfRI.sr_info.range.lower) = htons(0);
+
+       return;
+}
+
+/* Set Maximum Stack Depth for this router */
+static void set_sr_node_msd(u_int8_t msd)
+{
+       TLV_TYPE(OspfRI.sr_info.msd) = htons(RI_SR_TLV_NODE_MSD);
+       TLV_LEN(OspfRI.sr_info.msd) = htons(sizeof(u_int32_t));
+       OspfRI.sr_info.msd.value = msd;
+
+       return;
+}
+
+/* Unset this router MSD */
+static void unset_sr_node_msd()
+{
+       TLV_TYPE(OspfRI.sr_info.msd) = htons(0);
+       TLV_LEN(OspfRI.sr_info.msd) = htons(0);
+
+       return;
+}
 
 static void unset_param(struct tlv_header *tlv)
 {
@@ -466,11 +585,62 @@ static int is_mandated_params_set(struct ospf_router_info ori)
            && (ntohs(ori.pce_info.pce_cap_flag.header.type) == 0))
                return rc;
 
+       if ((ori.sr_info.enabled) && (ntohs(TLV_TYPE(ori.sr_info.algo)) == 0)
+           && (ntohs(TLV_TYPE(ori.sr_info.range)) == 0))
+               return rc;
+
        rc = 1;
 
        return rc;
 }
 
+/*
+ * Used by Segment Routing to set new TLVs and Sub-TLVs values
+ *
+ * @param enable To activate or not Segment Routing router Information flooding
+ * @param size   Size of Label Range i.e. SRGB size
+ * @param lower  Lower bound of the Label Range i.e. SRGB first label
+ * @param msd    Maximum label Stack Depth suported by the router
+ *
+ * @return none
+ */
+void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, u_int8_t msd)
+{
+
+       /* First activate and initialize Router Information is necessary */
+       if (!OspfRI.enabled) {
+               OspfRI.enabled = true;
+               initialize_params(&OspfRI);
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("RI-> %s Routing Information for Segment Routing",
+                       enable ? "Enable" : "Disable");
+
+       /* Unset or Set SR parameters */
+       if (!enable) {
+               unset_sr_algorithm(SR_ALGORITHM_SPF);
+               unset_sr_sid_label_range();
+               unset_sr_node_msd();
+               OspfRI.sr_info.enabled = false;
+       } else {
+               // Only SR_ALGORITHM_SPF is supported
+               set_sr_algorithm(SR_ALGORITHM_SPF);
+               set_sr_sid_label_range(srgb);
+               if (msd != 0)
+                       set_sr_node_msd(msd);
+               else
+                       unset_sr_node_msd();
+               OspfRI.sr_info.enabled = true;
+       }
+
+       /* Refresh if already engaged or originate RI LSA */
+       if (CHECK_FLAG(OspfRI.flags, RIFLG_LSA_ENGAGED))
+               ospf_router_info_lsa_schedule(REFRESH_THIS_LSA);
+       else
+               ospf_router_info_lsa_schedule(REORIGINATE_THIS_LSA);
+}
+
 /*------------------------------------------------------------------------*
  * Followings are callback functions against generic Opaque-LSAs handling.
  *------------------------------------------------------------------------*/
@@ -519,12 +689,22 @@ static void ospf_router_info_lsa_body_set(struct stream *s)
        /* Build Router Information TLV */
        build_tlv(s, &OspfRI.router_cap.header);
 
-       /* Compute PCE Info header first */
-       set_pce_header (&OspfRI.pce_info);
+       /* Build Segment Routing TLVs if enabled */
+       if (OspfRI.sr_info.enabled) {
+               /* Build Algorithm TLV */
+               build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo));
+               /* Build SRGB TLV */
+               build_tlv(s, &TLV_HDR(OspfRI.sr_info.range));
+               /* Build MSD TLV */
+               build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd));
+       }
 
        /* Add RI PCE TLV if it is set */
        if (OspfRI.pce_info.enabled) {
 
+               /* Compute PCE Info header first */
+               set_pce_header (&OspfRI.pce_info);
+
                /* Build PCE TLV */
                build_tlv_header(s, &OspfRI.pce_info.pce_header.header);
 
@@ -855,6 +1035,38 @@ static void ospf_router_info_lsa_schedule(enum lsa_opcode opcode)
        return;
 }
 
+/* Callback to handle Segment Routing information */
+static int ospf_router_info_lsa_update(struct ospf_lsa *lsa)
+{
+
+       /* Sanity Check */
+       if (lsa == NULL) {
+               zlog_warn("OSPF-RI (ospf_router_info_lsa_update): Abort! LSA is NULL");
+               return -1;
+       }
+
+       /* Check if it is not my LSA */
+       if (IS_LSA_SELF(lsa))
+               return 0;
+
+       /* Process only Router Information LSA */
+       if (GET_OPAQUE_TYPE(
+                       ntohl(lsa->data->id.s_addr)) != OPAQUE_TYPE_ROUTER_INFORMATION_LSA)
+               return 0;
+
+       /* Check if Router Info & Segment Routing are enable */
+       if (!OspfRI.enabled || !OspfRI.sr_info.enabled)
+               return 0;
+
+       /* Call Segment Routing LSA update or deletion */
+       if (!IS_LSA_MAXAGE(lsa))
+               ospf_sr_ri_lsa_update(lsa);
+       else
+               ospf_sr_ri_lsa_delete(lsa);
+
+       return 0;
+}
+
 /*------------------------------------------------------------------------*
  * Followings are vty session control functions.
  *------------------------------------------------------------------------*/
@@ -1021,6 +1233,98 @@ static u_int16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri,
        return sum;
 }
 
+/* Display Segment Routing Algorithm TLV information */
+static u_int16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh)
+{
+       struct ri_sr_tlv_sr_algorithm *algo =
+               (struct ri_sr_tlv_sr_algorithm *)tlvh;
+       int i;
+       if (vty != NULL) {
+               vty_out(vty, "  Segment Routing Algorithm TLV:\n");
+               for (i = 0; i < ntohs(algo->header.length); i++) {
+                       switch (algo->value[i]) {
+                       case 0:
+                               vty_out(vty, "    Algorithm %d: SPF\n", i);
+                               break;
+                       case 1:
+                               vty_out(vty, "    Algorithm %d: Strict SPF\n",
+                                       i);
+                               break;
+                       default:
+                               vty_out(vty,
+                                       "  Algorithm %d: Unknown value %d\n", i,
+                                       algo->value[i]);
+                               break;
+                       }
+               }
+       }
+
+       else {
+               zlog_debug("  Segment Routing Algorithm TLV:\n");
+               for (i = 0; i < ntohs(algo->header.length); i++)
+                       switch (algo->value[i]) {
+                       case 0:
+                               zlog_debug("    Algorithm %d: SPF\n", i);
+                               break;
+                       case 1:
+                               zlog_debug("    Algorithm %d: Strict SPF\n", i);
+                               break;
+                       default:
+                               zlog_debug(
+                                       "    Algorithm %d: Unknown value %d\n",
+                                       i, algo->value[i]);
+                               break;
+                       }
+       }
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Display Segment Routing SID/Label Range TLV information */
+static u_int16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh)
+{
+       struct ri_sr_tlv_sid_label_range *range =
+               (struct ri_sr_tlv_sid_label_range *)tlvh;
+
+       if (vty != NULL) {
+               vty_out(vty,
+                       "  Segment Routing Range TLV:\n"
+                       "    Range Size = %d\n"
+                       "    SID Label = %d\n\n",
+                       GET_RANGE_SIZE(ntohl(range->size)),
+                       GET_LABEL(ntohl(range->lower.value)));
+       } else {
+               zlog_debug(
+                       "  Segment Routing Range TLV:\n"
+                       "    Range Size = %d\n"
+                       "    SID Label = %d\n\n",
+                       GET_RANGE_SIZE(ntohl(range->size)),
+                       GET_LABEL(ntohl(range->lower.value)));
+       }
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Display Segment Routing Maximum Stack Depth TLV information */
+static u_int16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh)
+{
+       struct ri_sr_tlv_node_msd *msd = (struct ri_sr_tlv_node_msd *)tlvh;
+
+       if (vty != NULL) {
+               vty_out(vty,
+                       "  Segment Routing MSD TLV:\n"
+                       "    Node Maximum Stack Depth = %d\n",
+                       msd->value);
+       } else {
+               zlog_debug(
+                       "  Segment Routing MSD TLV:\n"
+                       "    Node Maximum Stack Depth = %d\n",
+                       msd->value);
+       }
+
+       return TLV_SIZE(tlvh);
+}
+
 static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa)
 {
        struct lsa_header *lsah = (struct lsa_header *)lsa->data;
@@ -1041,6 +1345,16 @@ static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa)
                        sum += TLV_HDR_SIZE;
                        sum += show_vty_pce_info(vty, tlvh, length - sum);
                        break;
+               case RI_SR_TLV_SR_ALGORITHM:
+                       sum += show_vty_sr_algorithm(vty, tlvh);
+                       break;
+               case RI_SR_TLV_SID_LABEL_RANGE:
+                       sum += show_vty_sr_range(vty, tlvh);
+                       break;
+               case RI_SR_TLV_NODE_MSD:
+                       sum += show_vty_sr_msd(vty, tlvh);
+                       break;
+
                default:
                        sum += show_vty_unknown_tlv(vty, tlvh);
                        break;
@@ -1058,53 +1372,54 @@ static void ospf_router_info_config_write_router(struct vty *vty)
        struct ri_pce_subtlv_neighbor *neighbor;
        struct in_addr tmp;
 
-       if (OspfRI.enabled) {
-               if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
-                       vty_out(vty, " router-info as\n");
-               else
-                       vty_out(vty, " router-info area %s\n",
-                               inet_ntoa(OspfRI.area_id));
-
-               if (OspfRI.pce_info.enabled) {
-
-                       if (pce->pce_address.header.type != 0)
-                               vty_out(vty, "  pce address %s\n",
-                                       inet_ntoa(pce->pce_address.address.value));
-
-                       if (pce->pce_cap_flag.header.type != 0)
-                               vty_out(vty, "  pce flag 0x%x\n",
-                                       ntohl(pce->pce_cap_flag.value));
-
-                       for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
-                               if (domain->header.type != 0) {
-                                       if (domain->type == PCE_DOMAIN_TYPE_AREA) {
-                                               tmp.s_addr = domain->value;
-                                               vty_out(vty, "  pce domain area %s\n",
-                                                       inet_ntoa(tmp));
-                                       } else {
-                                               vty_out(vty, "  pce domain as %d\n",
-                                                       ntohl(domain->value));
-                                       }
+       if (!OspfRI.enabled)
+               return;
+
+       if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+               vty_out(vty, " router-info as\n");
+       else
+               vty_out(vty, " router-info area %s\n",
+                       inet_ntoa(OspfRI.area_id));
+
+       if (OspfRI.pce_info.enabled) {
+
+               if (pce->pce_address.header.type != 0)
+                       vty_out(vty, "  pce address %s\n",
+                               inet_ntoa(pce->pce_address.address.value));
+
+               if (pce->pce_cap_flag.header.type != 0)
+                       vty_out(vty, "  pce flag 0x%x\n",
+                               ntohl(pce->pce_cap_flag.value));
+
+               for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
+                       if (domain->header.type != 0) {
+                               if (domain->type == PCE_DOMAIN_TYPE_AREA) {
+                                       tmp.s_addr = domain->value;
+                                       vty_out(vty, "  pce domain area %s\n",
+                                               inet_ntoa(tmp));
+                               } else {
+                                       vty_out(vty, "  pce domain as %d\n",
+                                               ntohl(domain->value));
                                }
                        }
+               }
 
-                       for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
-                               if (neighbor->header.type != 0) {
-                                       if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
-                                               tmp.s_addr = neighbor->value;
-                                               vty_out(vty, "  pce neighbor area %s\n",
-                                                       inet_ntoa(tmp));
-                                       } else {
-                                               vty_out(vty, "  pce neighbor as %d\n",
-                                                       ntohl(neighbor->value));
-                                       }
+               for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
+                       if (neighbor->header.type != 0) {
+                               if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
+                                       tmp.s_addr = neighbor->value;
+                                       vty_out(vty, "  pce neighbor area %s\n",
+                                               inet_ntoa(tmp));
+                               } else {
+                                       vty_out(vty, "  pce neighbor as %d\n",
+                                               ntohl(neighbor->value));
                                }
                        }
-
-                       if (pce->pce_scope.header.type != 0)
-                               vty_out(vty, "  pce scope 0x%x\n",
-                                       ntohl(OspfRI.pce_info.pce_scope.value));
                }
+
+               if (pce->pce_scope.header.type != 0)
+                       vty_out(vty, "  pce scope 0x%x\n",
+                               ntohl(OspfRI.pce_info.pce_scope.value));
        }
        return;
 }
@@ -1539,7 +1854,7 @@ DEFUN (show_ip_opsf_router_info_pce,
        struct ri_pce_subtlv_domain *domain;
        struct ri_pce_subtlv_neighbor *neighbor;
 
-       if (OspfRI.enabled) {
+       if ((OspfRI.enabled) && (OspfRI.pce_info.enabled)) {
                vty_out(vty, "--- PCE parameters ---\n");
 
                if (pce->pce_address.header.type != 0)
@@ -1568,7 +1883,7 @@ DEFUN (show_ip_opsf_router_info_pce,
 
        } else {
                vty_out(vty,
-                       "  Router Information is disabled on this router\n");
+                       "  PCE info is disabled on this router\n");
        }
 
        return CMD_SUCCESS;
index 2d90730d93ecfde5411de5c5283ddca990932a95..3fb83141b5e0be7065c0b2e7224e0a3f89e571a0 100644 (file)
@@ -1,11 +1,13 @@
 /*
  * This is an implementation of RFC4970 Router Information
  * with support of RFC5088 PCE Capabilites announcement
+ * and support of draft-ietf-ospf-segment-routing-extensions-18
+ * for Segment Routing Capabilities announcement
+ *
  *
  * Module name: Router Information
- * Version:     0.99.22
- * Created:     2012-02-01 by Olivier Dugeon
- * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/
  *
  * This file is part of GNU Zebra.
  *
@@ -33,7 +35,7 @@
  *
  *        24       16        8        0
  * +--------+--------+--------+--------+
- * |    1   |  MBZ   |........|........|
+ * |    4   |  MBZ   |........|........|
  * +--------+--------+--------+--------+
  * |<-Type->|<Resv'd>|<-- Instance --->|
  *
@@ -57,9 +59,8 @@
  * +--------+--------+--------+--------+  |
  * |   LS checksum   |     Length      |  V
  * +--------+--------+--------+--------+ ---
- * |      Type       |     Length      |  A
- * +--------+--------+--------+--------+  |  TLV part for Router Information;
- * Values might be
+ * |      Type       |     Length      |  A  TLV part for Router Information;
+ * +--------+--------+--------+--------+  |  Values might be
  * |              Values ...           |  V  structured as a set of sub-TLVs.
  * +--------+--------+--------+--------+ ---
  */
@@ -68,9 +69,9 @@
  * Following section defines TLV body parts.
  */
 
-/* Up to now, 8 code point have been assigned to Router Information */
+/* Up to now, 11 code points have been assigned to Router Information */
 /* Only type 1 Router Capabilities and 6 PCE are supported with this code */
-#define RI_IANA_MAX_TYPE               8
+#define RI_IANA_MAX_TYPE               11
 
 /* RFC4970: Router Information Capabilities TLV */ /* Mandatory */
 #define RI_TLV_CAPABILITIES            1
@@ -80,12 +81,13 @@ struct ri_tlv_router_cap {
        u_int32_t value;
 };
 
-#define RI_GRACE_RESTART       0x01
-#define RI_GRACE_HELPER                0x02
-#define RI_STUB_SUPPORT                0x04
-#define RI_TE_SUPPORT          0x08
-#define RI_P2P_OVER_LAN                0x10
-#define RI_TE_EXPERIMENTAL     0x20
+/* Capabilities bits are left align */
+#define RI_GRACE_RESTART       0x80000000
+#define RI_GRACE_HELPER                0x40000000
+#define RI_STUB_SUPPORT                0x20000000
+#define RI_TE_SUPPORT          0x10000000
+#define RI_P2P_OVER_LAN                0x08000000
+#define RI_TE_EXPERIMENTAL     0x04000000
 
 #define RI_TLV_LENGTH          4
 
@@ -151,22 +153,30 @@ struct ri_pce_subtlv_neighbor {
 #define RI_PCE_SUBTLV_CAP_FLAG         5
 
 #define PCE_CAP_GMPLS_LINK             0x0001
-#define PCE_CAP_BIDIRECTIONAL  0x0002
-#define PCE_CAP_DIVERSE_PATH   0x0004
-#define PCE_CAP_LOAD_BALANCE   0x0008
-#define PCE_CAP_SYNCHRONIZED   0x0010
+#define PCE_CAP_BIDIRECTIONAL          0x0002
+#define PCE_CAP_DIVERSE_PATH           0x0004
+#define PCE_CAP_LOAD_BALANCE           0x0008
+#define PCE_CAP_SYNCHRONIZED           0x0010
 #define PCE_CAP_OBJECTIVES             0x0020
 #define PCE_CAP_ADDITIVE               0x0040
-#define PCE_CAP_PRIORIZATION   0x0080
-#define PCE_CAP_MULTIPLE_REQ   0x0100
+#define PCE_CAP_PRIORIZATION           0x0080
+#define PCE_CAP_MULTIPLE_REQ           0x0100
 
 struct ri_pce_subtlv_cap_flag {
        struct tlv_header header; /* Type = 5; Length = n x 4 bytes. */
        u_int32_t value;
 };
 
+/* Structure to share flooding scope info for Segment Routing */
+struct scope_info {
+       u_int8_t scope;
+       struct in_addr area_id;
+};
+
 /* Prototypes. */
 extern int ospf_router_info_init(void);
 extern void ospf_router_info_term(void);
-
+extern int ospf_router_info_enable(void);
+extern void ospf_router_info_update_sr(bool, struct sr_srgb, u_int8_t);
+extern struct scope_info ospf_router_info_get_flooding_scope(void);
 #endif /* _ZEBRA_OSPF_ROUTER_INFO_H */
index 22fff1b53d50b0d603986de89a1ccf51c0e78081..9c747cd56562fad1e226da6758c12d5137e59299 100644 (file)
@@ -46,6 +46,7 @@
 #include "ospfd/ospf_ase.h"
 #include "ospfd/ospf_abr.h"
 #include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_sr.h"
 
 /* Variables to ensure a SPF scheduled log message is printed only once */
 
@@ -1339,7 +1340,6 @@ static int ospf_spf_calculate_timer(struct thread *thread)
 
        ospf_ase_calculate_timer_add(ospf);
 
-
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("%s: ospf install new route, vrf %s id %u new_table count %lu",
                           __PRETTY_FUNCTION__,
@@ -1366,6 +1366,9 @@ static int ospf_spf_calculate_timer(struct thread *thread)
                ospf_abr_task(ospf);
        abr_time = monotime_since(&start_time, NULL);
 
+       /* Schedule Segment Routing update */
+       ospf_sr_update_timer_add(ospf);
+
        total_spf_time =
                monotime_since(&spf_start_time, &ospf->ts_spf_duration);
 
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
new file mode 100644 (file)
index 0000000..e827ccc
--- /dev/null
@@ -0,0 +1,2186 @@
+/*
+ * This is an implementation of Segment Routing
+ * as per draft-ietf-ospf-segment-routing-extensions-24
+ *
+ * Module name: Segment Routing
+ *
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2016 - 2017 Orange Labs http://www.orange.com
+ *
+ * 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <zebra.h>
+
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "if.h"
+#include "jhash.h"
+#include "libospf.h" /* for ospf interface types */
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "monotime.h"
+#include "network.h"
+#include "prefix.h"
+#include "sockunion.h" /* for inet_aton() */
+#include "stream.h"
+#include "table.h"
+#include "thread.h"
+#include "vty.h"
+#include "zclient.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
+#include "ospfd/ospf_zebra.h"
+
+/*
+ * Global variable to manage Segment Routing on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_sr_db OspfSR;
+static void ospf_sr_register_vty(void);
+static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe);
+
+/*
+ * Segment Routing Data Base functions
+ */
+
+/* Hash function for Segment Routing entry */
+static unsigned int sr_hash(void *p)
+{
+       const struct in_addr *rid = p;
+
+       return (jhash_1word(rid->s_addr, 0));
+}
+
+/* Compare 2 Router ID hash entries based on SR Node */
+static int sr_cmp(const void *p1, const void *p2)
+{
+       const struct sr_node *srn = p1;
+       const struct in_addr *rid = p2;
+
+       return (IPV4_ADDR_SAME(&srn->adv_router, rid));
+}
+
+/* Functions to free memory space, segment routing */
+static void del_sr_info(void *val)
+{
+       XFREE(MTYPE_OSPF_SR_PARAMS, val);
+       return;
+}
+
+/* Allocate new Segment Routine node */
+static struct sr_node *sr_node_new(struct in_addr *rid)
+{
+
+       if (rid == NULL)
+               return NULL;
+
+       struct sr_node *new;
+
+       /* Allocate Segment Routing node memory */
+       new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_node));
+
+       /* Sanity Check */
+       if (new == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_node_new):"
+                       "Abort! can't create new SR node");
+               return NULL;
+       }
+
+       /* Default Algorithm, SRGB and MSD */
+       for (int i = 0; i < ALGORITHM_COUNT; i++)
+               OspfSR.algo[i] = SR_ALGORITHM_UNSET;
+
+       new->srgb.range_size = 0;
+       new->srgb.lower_bound = 0;
+       new->msd = 0;
+
+       /* Create Link, Prefix and Range TLVs list */
+       new->ext_link = list_new();
+       new->ext_prefix = list_new();
+       new->ext_link->del = del_sr_info;
+       new->ext_prefix->del = del_sr_info;
+
+       /* Check if list are correctly created */
+       if (new->ext_link == NULL || new->ext_prefix == NULL) {
+               list_delete_original(new->ext_link);
+               list_delete_original(new->ext_prefix);
+               XFREE(MTYPE_OSPF_SR_PARAMS, new);
+               return NULL;
+       }
+
+       IPV4_ADDR_COPY(&new->adv_router, rid);
+       new->neighbor = NULL;
+       new->instance = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Created new SR node for %s",
+                          inet_ntoa(new->adv_router));
+       return new;
+}
+
+/* Delete Segment Routing node */
+static void sr_node_del(struct sr_node *srn)
+{
+       struct listnode *node;
+       struct sr_link *srl;
+       struct sr_prefix *srp;
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       /* Clean Extended Link */
+       if (listcount(srn->ext_link) != 0) {
+               for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
+                       listnode_delete(srn->ext_link, srl);
+                       XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+               }
+       }
+       list_delete_original(srn->ext_link);
+
+       /* Clean Prefix List */
+       if (listcount(srn->ext_prefix) != 0) {
+               for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+                       listnode_delete(srn->ext_prefix, srp);
+                       XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+               }
+       }
+       list_delete_original(srn->ext_prefix);
+
+       XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+}
+
+/* Get SR Node for a given nexthop */
+static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf,
+                                             struct in_addr nexthop)
+{
+       struct ospf_interface *oi = NULL;
+       struct ospf_neighbor *nbr = NULL;
+       struct listnode *node;
+       struct route_node *rn;
+       struct sr_node *srn;
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL)
+               return NULL;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("      |-  Search SR-Node for nexthop %s",
+                          inet_ntoa(nexthop));
+
+       /* First, search neighbor Router ID for this nexthop */
+       for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
+               for (rn = route_top(oi->nbrs); rn; rn = route_next(rn))
+                       if ((nbr = rn->info))
+                               break;
+
+       if (nbr == NULL)
+               return NULL;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("      |-  Found nexthop Router ID %s",
+                          inet_ntoa(nbr->router_id));
+       /* Then, search SR Node */
+       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id);
+
+       return srn;
+}
+
+/*
+ * Segment Routing Initialization functions
+ */
+
+/* Segment Routing starter function */
+static int ospf_sr_start(struct ospf *ospf)
+{
+       struct route_node *rn;
+       struct ospf_lsa *lsa;
+       struct sr_node *srn;
+       int rc = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("SR (ospf_sr_start): Start Segment Routing");
+
+       /* Initialize self SR Node */
+       srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id),
+                      (void *)sr_node_new);
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return rc;
+
+       /* Complete & Store self SR Node */
+       srn->srgb.range_size = OspfSR.srgb.range_size;
+       srn->srgb.lower_bound = OspfSR.srgb.lower_bound;
+       srn->algo[0] = OspfSR.algo[0];
+       srn->msd = OspfSR.msd;
+       OspfSR.self = srn;
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR (ospf_sr_start): Update SR-DB from LSDB");
+
+       /* Start by looking to Router Info & Extended LSA in lsdb */
+       if ((ospf != NULL) && (ospf->backbone != NULL)) {
+               LSDB_LOOP(OPAQUE_AREA_LSDB(ospf->backbone), rn, lsa)
+               {
+                       if (IS_LSA_MAXAGE(lsa) || IS_LSA_SELF(lsa))
+                               continue;
+                       int lsa_id =
+                               GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+                       switch (lsa_id) {
+                       case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+                               ospf_sr_ri_lsa_update(lsa);
+                               break;
+                       case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+                               ospf_sr_ext_prefix_lsa_update(lsa);
+                               break;
+                       case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+                               ospf_sr_ext_link_lsa_update(lsa);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       rc = 1;
+       return rc;
+}
+
+/* Remove an SR Node in the SRDB */
+static void ospf_sr_node_nhlfe_del(struct hash_backet *backet, void *args)
+{
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct sr_link *srl;
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Delete all Prefix for SR Node %s",
+                          inet_ntoa(srn->adv_router));
+
+       /* Remove Extended Prefix */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp))
+               del_sid_nhlfe(srp->nhlfe);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Delete all Link for SR Node %s",
+                          inet_ntoa(srn->adv_router));
+
+       /* Remove Extended Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
+               /* Remove NHLFE entries for this Link */
+               del_sid_nhlfe(srl->nhlfe[0]);
+               del_sid_nhlfe(srl->nhlfe[1]);
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Remove SR Node %s",
+                          inet_ntoa(srn->adv_router));
+}
+
+/* Stop Segment Routing */
+static void ospf_sr_stop(void)
+{
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("SR (ospf_sr_stop): Stop Segment Routing");
+
+       /* Start by removing all Prefix and Link for each SR Node */
+       hash_iterate(OspfSR.neighbors, (void (*)(struct hash_backet *,
+                                                void *))ospf_sr_node_nhlfe_del,
+                    NULL);
+
+       /* Finish by cleaning the hash table */
+       hash_clean(OspfSR.neighbors, (void *)sr_node_del);
+}
+
+/*
+ * Segment Routing initialize function
+ *
+ * @param - nothing
+ *
+ * @return 0 if OK, -1 otherwise
+ */
+int ospf_sr_init(void)
+{
+       int rc = -1;
+
+       zlog_info("SR (ospf_sr_init): Initialize SR Data Base");
+
+       memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
+       OspfSR.enabled = false;
+       /* Only AREA flooding is supported in this release */
+       OspfSR.scope = OSPF_OPAQUE_AREA_LSA;
+
+       /* Initialize SRGB, Algorithms and MSD TLVs */
+       /* Only Algorithm SPF is supported */
+       OspfSR.algo[0] = SR_ALGORITHM_SPF;
+       for (int i = 1; i < ALGORITHM_COUNT; i++)
+               OspfSR.algo[i] = SR_ALGORITHM_UNSET;
+
+       OspfSR.srgb.range_size = MPLS_DEFAULT_MAX_SRGB_SIZE;
+       OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL;
+       OspfSR.msd = MPLS_MAX_LABELS;
+
+       /* Initialize Hash table for neighbor SR nodes */
+       OspfSR.neighbors = hash_create(sr_hash, sr_cmp, "OSPF_SR");
+       if (OspfSR.neighbors == NULL)
+               return rc;
+
+       /* Initialize Route Table for prefix */
+       OspfSR.prefix = route_table_init();
+       if (OspfSR.prefix == NULL)
+               return rc;
+
+       /* Register Segment Routing VTY command */
+       ospf_sr_register_vty();
+
+       rc = 0;
+       return rc;
+}
+
+/*
+ * Segment Routing termination function
+ *
+ * @param - nothing
+ *
+ * @return - nothing
+ */
+void ospf_sr_term(void)
+{
+
+       /* Stop Segment Routing */
+       ospf_sr_stop();
+
+       /* Clear SR Node Table */
+       if (OspfSR.neighbors)
+               hash_free(OspfSR.neighbors);
+
+       /* Clear Prefix Table */
+       if (OspfSR.prefix)
+               route_table_finish(OspfSR.prefix);
+
+       OspfSR.enabled = false;
+}
+
+/*
+ * Following functions are used to manipulate the
+ * Next Hop Label Forwarding entry (NHLFE)
+ */
+
+/* Compute label from index */
+static mpls_label_t index2label(u_int32_t index, struct sr_srgb srgb)
+{
+       mpls_label_t label;
+
+       label = srgb.lower_bound + index;
+       if (label > (srgb.lower_bound + srgb.range_size))
+               return MPLS_INVALID_LABEL;
+       else
+               return label;
+}
+
+/* Get neighbor full structure from address */
+static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top,
+                                                 struct in_addr addr)
+{
+       struct ospf_neighbor *nbr;
+       struct ospf_interface *oi;
+       struct listnode *node;
+       struct route_node *rn;
+
+       /* Sanity Check */
+       if (top == NULL)
+               return NULL;
+
+       for (ALL_LIST_ELEMENTS_RO(top->oiflist, node, oi))
+               for (rn = route_top(oi->nbrs); rn; rn = route_next(rn))
+                       if ((nbr = rn->info))
+                               if (IPV4_ADDR_SAME(&nbr->address.u.prefix4,
+                                                  &addr)
+                                   || IPV4_ADDR_SAME(&nbr->router_id, &addr)) {
+                                       route_unlock_node(rn);
+                                       return nbr;
+                               }
+
+       return NULL;
+}
+
+/* Get OSPF Path from address */
+static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
+                                            struct prefix_ipv4 p)
+{
+       struct ospf_route * or ;
+       struct ospf_path *path;
+       struct listnode *node;
+       struct route_node *rn;
+
+       /* Sanity Check */
+       if ((top == NULL) && (top->new_table))
+               return NULL;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("      |-  Search Nexthop for prefix %s/%d",
+                          inet_ntoa(p.prefix), p.prefixlen);
+
+       rn = route_node_lookup(top->new_table, (struct prefix *)&p);
+
+       /* Check if we found an OSPF route. May be NULL if SPF has not
+        * yet populate routing table for this prefix. */
+       if (rn == NULL)
+               return NULL;
+
+       route_unlock_node(rn);
+
+       if ((or = rn->info) == NULL)
+               return NULL;
+
+       /* Then search path from this route */
+       for (ALL_LIST_ELEMENTS_RO(or->paths, node, path))
+               if (path->nexthop.s_addr != INADDR_ANY || path->ifindex != 0)
+                       return path;
+
+       return NULL;
+}
+
+/* Compute NHLFE entry for Extended Link */
+static int compute_link_nhlfe(struct sr_link *srl)
+{
+       struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+       struct ospf_neighbor *nh;
+       int rc = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Compute NHLFE for link %s/%d",
+                          inet_ntoa(srl->nhlfe[0].prefv4.prefix),
+                          srl->nhlfe[0].prefv4.prefixlen);
+
+       /* First determine the OSPF Neighbor */
+       nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop);
+
+       /* Neighbor could be not found when OSPF Adjacency just fire up
+        * because SPF don't yet populate routing table. This NHLFE will
+        * be fixed later when SR SPF schedule will be called.
+        */
+       if (nh == NULL)
+               return rc;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Found nexthop NHLFE %s",
+                          inet_ntoa(nh->router_id));
+
+       /* Set ifindex for this neighbor */
+       srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex;
+       srl->nhlfe[1].ifindex = nh->oi->ifp->ifindex;
+
+       /* Set Input & Output Label */
+       if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+               srl->nhlfe[0].label_in = srl->sid[0];
+       else
+               srl->nhlfe[0].label_in =
+                       index2label(srl->sid[0], srl->srn->srgb);
+       if (CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+               srl->nhlfe[1].label_in = srl->sid[1];
+       else
+               srl->nhlfe[1].label_in =
+                       index2label(srl->sid[1], srl->srn->srgb);
+
+       srl->nhlfe[0].label_out = MPLS_IMP_NULL_LABEL;
+       srl->nhlfe[1].label_out = MPLS_IMP_NULL_LABEL;
+
+       rc = 1;
+       return rc;
+}
+
+/*
+ * Compute NHLFE entry for Extended Prefix
+ *
+ * @param srp - Segment Routing Prefix
+ *
+ * @return -1 if next hop is not found, 0 if nexthop has not changed
+ *         and 1 if success
+ */
+static int compute_prefix_nhlfe(struct sr_prefix *srp)
+{
+       struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+       struct ospf_path *nh = NULL;
+       struct sr_node *srnext;
+       int rc = -1;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Compute NHLFE for prefix %s/%d",
+                          inet_ntoa(srp->nhlfe.prefv4.prefix),
+                          srp->nhlfe.prefv4.prefixlen);
+
+       /* First determine the nexthop */
+       nh = get_nexthop_by_addr(top, srp->nhlfe.prefv4);
+
+       /* Nexthop could be not found when OSPF Adjacency just fire up
+        * because SPF don't yet populate routing table. This NHLFE will
+        * be fixed later when SR SPF schedule will be called.
+        */
+       if (nh == NULL)
+               return rc;
+
+       /* Check if NextHop has changed when call after running a new SPF */
+       if (IPV4_ADDR_SAME(&nh->nexthop, &srp->nhlfe.nexthop)
+           && (nh->ifindex == srp->nhlfe.ifindex))
+               return 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Found new next hop for this NHLFE: %s",
+                          inet_ntoa(nh->nexthop));
+
+       /* Get SR-Node for this nexthop */
+       srnext = get_sr_node_by_nexthop(top, nh->nexthop);
+       /* and store this information for later SRGB update */
+       srnext->neighbor = OspfSR.self;
+       if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router))
+               srp->nexthop = NULL;
+       else
+               srp->nexthop = srnext;
+
+       /*
+        * SR Node could be known, but SRGB could be not initialize
+        * This is due to the fact that Extended Link / Prefix could
+        * be received before corresponding Router Information LSA
+        */
+       if ((srnext == NULL) || (srnext->srgb.lower_bound == 0)
+           || (srnext->srgb.range_size == 0))
+               return rc;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Found SRGB %d/%d for next hop SR-Node %s",
+                          srnext->srgb.range_size, srnext->srgb.lower_bound,
+                          inet_ntoa(srnext->adv_router));
+
+       /* Set ip addr & ifindex for this neighbor */
+       IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &nh->nexthop);
+       srp->nhlfe.ifindex = nh->ifindex;
+
+       /* Compute Input Label with self SRGB */
+       srp->nhlfe.label_in = index2label(srp->sid, OspfSR.srgb);
+       /* and Output Label with Next hop SR Node SRGB or Implicit Null label
+        * if next hop is the destination and request PHP */
+       if ((srp->nexthop == NULL)
+           && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+               srp->nhlfe.label_out = MPLS_IMP_NULL_LABEL;
+       else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+               srp->nhlfe.label_out = srp->sid;
+       else
+               srp->nhlfe.label_out = index2label(srp->sid, srnext->srgb);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Computed new labels in: %d out: %d",
+                          srp->nhlfe.label_in, srp->nhlfe.label_out);
+
+       rc = 1;
+       return rc;
+}
+
+/* Send MPLS Label entry to Zebra for installation or deletion */
+static int ospf_zebra_send_mpls_labels(int cmd, struct sr_nhlfe nhlfe)
+{
+       struct stream *s;
+
+       /* Reset stream. */
+       s = zclient->obuf;
+       stream_reset(s);
+
+       zclient_create_header(s, cmd, VRF_DEFAULT);
+       stream_putc(s, ZEBRA_LSP_SR);
+       /* OSPF Segment Routing currently support only IPv4 */
+       stream_putl(s, nhlfe.prefv4.family);
+       stream_put_in_addr(s, &nhlfe.prefv4.prefix);
+       stream_putc(s, nhlfe.prefv4.prefixlen);
+       stream_put_in_addr(s, &nhlfe.nexthop);
+       stream_putl(s, nhlfe.ifindex);
+       stream_putc(s, OSPF_SR_PRIORITY_DEFAULT);
+       stream_putl(s, nhlfe.label_in);
+       stream_putl(s, nhlfe.label_out);
+
+       /* Put length at the first point of the stream. */
+       stream_putw_at(s, 0, stream_get_endp(s));
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  %s LSP %d/%d for %s/%d via %d",
+                          cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete",
+                          nhlfe.label_in, nhlfe.label_out,
+                          inet_ntoa(nhlfe.prefv4.prefix),
+                          nhlfe.prefv4.prefixlen, nhlfe.ifindex);
+
+       return (zclient_send_message(zclient));
+}
+
+/* Request zebra to install/remove FEC in FIB */
+static int ospf_zebra_send_mpls_ftn(int cmd, struct sr_nhlfe nhlfe)
+{
+       struct zapi_route api;
+       struct zapi_nexthop *api_nh;
+
+       /* Support only IPv4 */
+       if (nhlfe.prefv4.family != AF_INET)
+               return -1;
+
+       memset(&api, 0, sizeof(api));
+       api.vrf_id = VRF_DEFAULT;
+       api.type = ZEBRA_ROUTE_OSPF_SR;
+       api.safi = SAFI_UNICAST;
+       memcpy(&api.prefix, &nhlfe.prefv4, sizeof(struct prefix_ipv4));
+
+       if (cmd == ZEBRA_ROUTE_ADD) {
+               /* Metric value. */
+               SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+               api.metric = OSPF_SR_DEFAULT_METRIC;
+               /* Nexthop */
+               SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+               api_nh = &api.nexthops[0];
+               IPV4_ADDR_COPY(&api_nh->gate.ipv4, &nhlfe.nexthop);
+               api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+               api_nh->ifindex = nhlfe.ifindex;
+               /* MPLS labels */
+               SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
+               api_nh->labels[0] = nhlfe.label_out;
+               api_nh->label_num = 1;
+               api.nexthop_num = 1;
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  %s FEC %d for %s/%d via %d",
+                          cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete",
+                          nhlfe.label_out, inet_ntoa(nhlfe.prefv4.prefix),
+                          nhlfe.prefv4.prefixlen, nhlfe.ifindex);
+
+       return (zclient_route_send(cmd, zclient, &api));
+
+       return -1;
+}
+
+/* Add new NHLFE entry for SID */
+static inline void add_sid_nhlfe(struct sr_nhlfe nhlfe)
+{
+       if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) {
+               ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, nhlfe);
+               if (nhlfe.label_out != MPLS_IMP_NULL_LABEL)
+                       ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_ADD, nhlfe);
+       }
+}
+
+/* Remove NHLFE entry for SID */
+static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe)
+{
+       if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) {
+               ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, nhlfe);
+               if (nhlfe.label_out != MPLS_IMP_NULL_LABEL)
+                       ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_DELETE, nhlfe);
+       }
+}
+
+/* Update NHLFE entry for SID */
+static inline void update_sid_nhlfe(struct sr_nhlfe n1, struct sr_nhlfe n2)
+{
+
+       del_sid_nhlfe(n1);
+       add_sid_nhlfe(n2);
+}
+
+/*
+ * Functions to parse and get Extended Link / Prefix
+ * TLVs and SubTLVs
+ */
+
+/* Extended Link SubTLVs Getter */
+static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh)
+{
+
+       struct sr_link *srl;
+       struct ext_tlv_link *link = (struct ext_tlv_link *)tlvh;
+       struct ext_subtlv_adj_sid *adj_sid;
+       struct ext_subtlv_lan_adj_sid *lan_sid;
+       struct ext_subtlv_rmt_itf_addr *rmt_itf;
+
+       struct tlv_header *sub_tlvh;
+       u_int16_t length = 0, sum = 0, i = 0;
+
+       srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link));
+
+       if (srl == NULL)
+               return NULL;
+
+       /* Initialize TLV browsing */
+       length = ntohs(tlvh->length) - EXT_TLV_LINK_SIZE;
+       sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE
+                                        + EXT_TLV_LINK_SIZE);
+       for (; sum < length; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) {
+               switch (ntohs(sub_tlvh->type)) {
+               case EXT_SUBTLV_ADJ_SID:
+                       adj_sid = (struct ext_subtlv_adj_sid *)sub_tlvh;
+                       srl->type = ADJ_SID;
+                       i = CHECK_FLAG(adj_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_BFLG)
+                                   ? 1
+                                   : 0;
+                       srl->flags[i] = adj_sid->flags;
+                       if (CHECK_FLAG(adj_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+                               srl->sid[i] = GET_LABEL(ntohl(adj_sid->value));
+                       else
+                               srl->sid[i] = ntohl(adj_sid->value);
+                       IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop, &link->link_id);
+                       break;
+               case EXT_SUBTLV_LAN_ADJ_SID:
+                       lan_sid = (struct ext_subtlv_lan_adj_sid *)sub_tlvh;
+                       srl->type = LAN_ADJ_SID;
+                       i = CHECK_FLAG(lan_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_BFLG)
+                                   ? 1
+                                   : 0;
+                       srl->flags[i] = lan_sid->flags;
+                       if (CHECK_FLAG(lan_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+                               srl->sid[i] = GET_LABEL(ntohl(lan_sid->value));
+                       else
+                               srl->sid[i] = ntohl(lan_sid->value);
+                       IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop,
+                                      &lan_sid->neighbor_id);
+                       break;
+               case EXT_SUBTLV_RMT_ITF_ADDR:
+                       rmt_itf = (struct ext_subtlv_rmt_itf_addr *)sub_tlvh;
+                       IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &rmt_itf->value);
+                       IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &rmt_itf->value);
+                       break;
+               default:
+                       break;
+               }
+               sum += TLV_SIZE(sub_tlvh);
+       }
+
+       IPV4_ADDR_COPY(&srl->nhlfe[0].prefv4.prefix, &link->link_data);
+       srl->nhlfe[0].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
+       srl->nhlfe[0].prefv4.family = AF_INET;
+       apply_mask_ipv4(&srl->nhlfe[0].prefv4);
+       IPV4_ADDR_COPY(&srl->nhlfe[1].prefv4.prefix, &link->link_data);
+       srl->nhlfe[1].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
+       srl->nhlfe[1].prefv4.family = AF_INET;
+       apply_mask_ipv4(&srl->nhlfe[1].prefv4);
+
+       if (IS_DEBUG_OSPF_SR) {
+               zlog_debug("  |-  Found primary Adj/Lan Sid %d for %s/%d",
+                          srl->sid[0], inet_ntoa(srl->nhlfe[0].prefv4.prefix),
+                          srl->nhlfe[0].prefv4.prefixlen);
+               zlog_debug("  |-  Found backup Adj/Lan Sid %d for %s/%d",
+                          srl->sid[1], inet_ntoa(srl->nhlfe[1].prefv4.prefix),
+                          srl->nhlfe[1].prefv4.prefixlen);
+       }
+
+       return srl;
+}
+
+/* Extended Prefix SubTLVs Getter */
+static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh)
+{
+
+       struct sr_prefix *srp;
+       struct ext_tlv_prefix *pref = (struct ext_tlv_prefix *)tlvh;
+       struct ext_subtlv_prefix_sid *psid;
+
+       struct tlv_header *sub_tlvh;
+       u_int16_t length = 0, sum = 0;
+
+       srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+
+       if (srp == NULL)
+               return NULL;
+
+       /* Initialize TLV browsing */
+       length = ntohs(tlvh->length) - EXT_TLV_PREFIX_SIZE;
+       sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE
+                                        + EXT_TLV_PREFIX_SIZE);
+       for (; sum < length; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) {
+               switch (ntohs(sub_tlvh->type)) {
+               case EXT_SUBTLV_PREFIX_SID:
+                       psid = (struct ext_subtlv_prefix_sid *)sub_tlvh;
+                       if (psid->algorithm != SR_ALGORITHM_SPF) {
+                               zlog_err(
+                                       "SR (get_ext_prefix_sid): "
+                                       "Unsupported Algorithm");
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+                               return NULL;
+                       }
+                       srp->type = PREF_SID;
+                       srp->flags = psid->flags;
+                       if (CHECK_FLAG(psid->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+                               srp->sid = GET_LABEL(ntohl(psid->value));
+                       else
+                               srp->sid = ntohl(psid->value);
+                       IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
+                                      &pref->address);
+                       srp->nhlfe.prefv4.prefixlen = pref->pref_length;
+                       srp->nhlfe.prefv4.family = AF_INET;
+                       apply_mask_ipv4(&srp->nhlfe.prefv4);
+                       break;
+               default:
+                       break;
+               }
+               sum += TLV_SIZE(sub_tlvh);
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Found SID %d for prefix %s/%d", srp->sid,
+                          inet_ntoa(srp->nhlfe.prefv4.prefix),
+                          srp->nhlfe.prefv4.prefixlen);
+       return srp;
+}
+
+/*
+ * Functions to manipulate Segment Routing Link & Prefix structures
+ */
+
+/* Compare two Segment Link: return 0 if equal, 1 otherwise */
+static inline int sr_link_cmp(struct sr_link *srl1, struct sr_link *srl2)
+{
+       if ((srl1->sid[0] == srl2->sid[0]) && (srl1->sid[1] == srl2->sid[1])
+           && (srl1->type == srl2->type) && (srl1->flags[0] == srl2->flags[0])
+           && (srl1->flags[1] == srl2->flags[1]))
+               return 0;
+       else
+               return 1;
+}
+
+/* Compare two Segment Prefix: return 0 if equal, 1 otherwise */
+static inline int sr_prefix_cmp(struct sr_prefix *srp1, struct sr_prefix *srp2)
+{
+       if ((srp1->sid == srp2->sid) && (srp1->flags == srp2->flags))
+               return 0;
+       else
+               return 1;
+}
+
+/* Update Segment Link of given Segment Routing Node */
+static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl,
+                               u_char lsa_flags)
+{
+       struct listnode *node;
+       struct sr_link *lk;
+       bool found = false;
+
+       /* Sanity check */
+       if ((srn == NULL) || (srl == NULL))
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Process Extended Link Adj/Lan-SID");
+
+       /* Process only Local Adj/Lan_Adj SID coming from LSA SELF */
+       if (!CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+           || !CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+           || !CHECK_FLAG(lsa_flags, OSPF_LSA_SELF))
+               return;
+
+       /* Search for existing Segment Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, lk))
+               if (lk->instance == srl->instance) {
+                       found = true;
+                       break;
+               }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  %s SR Link 8.0.0.%d for SR node %s",
+                          found ? "Update" : "Add",
+                          GET_OPAQUE_ID(srl->instance),
+                          inet_ntoa(srn->adv_router));
+
+       /* if not found, add new Segment Link and install NHLFE */
+       if (!found) {
+               /* Complete SR-Link and add it to SR-Node list */
+               srl->srn = srn;
+               IPV4_ADDR_COPY(&srl->adv_router, &srn->adv_router);
+               listnode_add(srn->ext_link, srl);
+               /* Try to set MPLS table */
+               if (compute_link_nhlfe(srl)) {
+                       add_sid_nhlfe(srl->nhlfe[0]);
+                       add_sid_nhlfe(srl->nhlfe[1]);
+               }
+       } else {
+               if (sr_link_cmp(lk, srl)) {
+                       if (compute_link_nhlfe(srl)) {
+                               update_sid_nhlfe(lk->nhlfe[0], srl->nhlfe[0]);
+                               update_sid_nhlfe(lk->nhlfe[1], srl->nhlfe[1]);
+                               /* Replace Segment List */
+                               listnode_delete(srn->ext_link, lk);
+                               XFREE(MTYPE_OSPF_SR_PARAMS, lk);
+                               srl->srn = srn;
+                               IPV4_ADDR_COPY(&srl->adv_router,
+                                              &srn->adv_router);
+                               listnode_add(srn->ext_link, srl);
+                       } else {
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+                       }
+               } else {
+                       /* This is just an LSA refresh.
+                        * Stop processing and free SR Link */
+                       XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+               }
+       }
+}
+
+/* Update Segment Prefix of given Segment Routing Node */
+static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
+{
+
+       struct listnode *node;
+       struct sr_prefix *pref;
+       bool found = false;
+
+       /* Sanity check */
+       if (srn == NULL || srp == NULL)
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Process Extended Prefix SID %d", srp->sid);
+
+       /* Process only Global Prefix SID */
+       if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG))
+               return;
+
+       /* Search for existing Segment Prefix */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, pref))
+               if (pref->instance == srp->instance) {
+                       found = true;
+                       break;
+               }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  %s SR LSA ID 7.0.0.%d for SR node %s",
+                          found ? "Update" : "Add",
+                          GET_OPAQUE_ID(srp->instance),
+                          inet_ntoa(srn->adv_router));
+
+       /* if not found, add new Segment Prefix and install NHLFE */
+       if (!found) {
+               /* Complete SR-Prefix and add it to SR-Node list */
+               srp->srn = srn;
+               IPV4_ADDR_COPY(&srp->adv_router, &srn->adv_router);
+               listnode_add(srn->ext_prefix, srp);
+               /* Try to set MPLS table */
+               if (compute_prefix_nhlfe(srp) == 1) {
+                       add_sid_nhlfe(srp->nhlfe);
+               }
+       } else {
+               if (sr_prefix_cmp(pref, srp)) {
+                       if (compute_prefix_nhlfe(srp) == 1) {
+                               update_sid_nhlfe(pref->nhlfe, srp->nhlfe);
+                               /* Replace Segment Prefix */
+                               listnode_delete(srn->ext_prefix, pref);
+                               XFREE(MTYPE_OSPF_SR_PARAMS, pref);
+                               srp->srn = srn;
+                               IPV4_ADDR_COPY(&srp->adv_router,
+                                              &srn->adv_router);
+                               listnode_add(srn->ext_prefix, srp);
+                       } else {
+                               /* New NHLFE was not found.
+                                * Just free the SR Prefix */
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+                       }
+               } else {
+                       /* This is just an LSA refresh.
+                        * Stop processing and free SR Prefix */
+                       XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+               }
+       }
+}
+
+/*
+ * When change the FRR Self SRGB, update the NHLFE Input Label
+ * for all Extended Prefix with SID index through hash_iterate()
+ */
+static void update_in_nhlfe(struct hash_backet *backet, void *args)
+{
+       struct listnode *node;
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct sr_prefix *srp;
+       struct sr_nhlfe new;
+
+       /* Skip Self Node */
+       if (srn == OspfSR.self)
+               return;
+
+       /* Process Every Extended Prefix for this SR-Node */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+               /* Process only SID Index */
+               if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+                       continue;
+               /* Compute new NHLFE */
+               memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
+               new.label_in = index2label(srp->sid, OspfSR.srgb);
+               /* Update MPLS LFIB */
+               update_sid_nhlfe(srp->nhlfe, new);
+               /* Finally update Input Label */
+               srp->nhlfe.label_in = new.label_in;
+       }
+}
+
+/*
+ * When SRGB has changed, update NHLFE Output Label for all Extended Prefix
+ * with SID index which use the given SR-Node as nexthop though hash_iterate()
+ */
+static void update_out_nhlfe(struct hash_backet *backet, void *args)
+{
+       struct listnode *node;
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct sr_node *srnext = (struct sr_node *)args;
+       struct sr_prefix *srp;
+       struct sr_nhlfe new;
+
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+               /* Process only SID Index for next hop without PHP */
+               if ((srp->nexthop == NULL)
+                   && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+                       continue;
+               memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
+               new.label_out = index2label(srp->sid, srnext->srgb);
+               update_sid_nhlfe(srp->nhlfe, new);
+               srp->nhlfe.label_out = new.label_out;
+       }
+}
+
+/*
+ * Following functions are call when new Segment Routing LSA are received
+ *  - Router Information: ospf_sr_ri_lsa_update() & ospf_sr_ri_lsa_delete()
+ *  - Extended Link: ospf_sr_ext_link_update() & ospf_sr_ext_link_delete()
+ *  - Extended Prefix: ospf_ext_prefix_update() & ospf_sr_ext_prefix_delete()
+ */
+
+/* Update Segment Routing from Router Information LSA */
+void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct tlv_header *tlvh;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct ri_sr_tlv_sid_label_range *ri_srgb;
+       struct ri_sr_tlv_sr_algorithm *algo;
+       struct sr_srgb srgb;
+       u_int16_t length = 0, sum = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (ospf_sr_ri_lsa_update): Process Router "
+                       "Information LSA 4.0.0.%d from %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (IS_LSA_SELF(lsa))
+               return;
+
+       if (OspfSR.neighbors == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ri_lsa_update): Abort! no valid "
+                       "SR DataBase");
+               return;
+       }
+
+       /* Get SR Node in hash table from Router ID */
+       srn = hash_get(OspfSR.neighbors, (void *)&(lsah->adv_router),
+                      (void *)sr_node_new);
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ri_lsa_update): Abort! can't create "
+                       "SR node in hash table");
+               return;
+       }
+
+       if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
+               zlog_err(
+                       "SR (ospf_sr_ri_lsa_update): Abort! Wrong "
+                       "LSA ID 4.0.0.%d for SR node %s/%d",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router), srn->instance);
+               return;
+       }
+
+       /* Collect Router Information Sub TLVs */
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+       srgb.range_size = 0;
+       srgb.lower_bound = 0;
+
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case RI_SR_TLV_SR_ALGORITHM:
+                       algo = (struct ri_sr_tlv_sr_algorithm *)tlvh;
+                       int i;
+                       for (i = 0; i < ntohs(algo->header.length); i++)
+                               srn->algo[i] = algo->value[0];
+                       for (; i < ALGORITHM_COUNT; i++)
+                               srn->algo[i] = SR_ALGORITHM_UNSET;
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               case RI_SR_TLV_SID_LABEL_RANGE:
+                       ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh;
+                       srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size));
+                       srgb.lower_bound =
+                               GET_LABEL(ntohl(ri_srgb->lower.value));
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               case RI_SR_TLV_NODE_MSD:
+                       srn->msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value;
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               default:
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               }
+       }
+
+       /* Check that we collect mandatory parameters */
+       if (srn->algo[0] == SR_ALGORITHM_UNSET || srgb.range_size == 0
+           || srgb.lower_bound == 0) {
+               zlog_warn(
+                       "SR (ospf_sr_ri_lsa_update): Missing "
+                       "mandatory parameters. Abort!");
+               hash_release(OspfSR.neighbors, &(srn->adv_router));
+               XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+               return;
+       }
+
+       /* Check if it is a new SR Node or not */
+       if (srn->instance == 0) {
+               /* update LSA ID */
+               srn->instance = ntohl(lsah->id.s_addr);
+               /* Copy SRGB */
+               srn->srgb.range_size = srgb.range_size;
+               srn->srgb.lower_bound = srgb.lower_bound;
+       }
+
+       /* Check if SRGB has changed */
+       if ((srn->srgb.range_size != srgb.range_size)
+           || (srn->srgb.lower_bound != srgb.lower_bound)) {
+               srn->srgb.range_size = srgb.range_size;
+               srn->srgb.lower_bound = srgb.lower_bound;
+               /* Update NHLFE if it is a neighbor SR node */
+               if (srn->neighbor == OspfSR.self)
+                       hash_iterate(OspfSR.neighbors,
+                                    (void (*)(struct hash_backet *,
+                                              void *))update_out_nhlfe,
+                                    (void *)srn);
+       }
+
+       return;
+}
+
+/*
+ * Delete SR Node entry in hash table information corresponding to an expired
+ * Router Information LSA
+ */
+void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (ospf_sr_ri_lsa_delete): Remove SR node %s "
+                       "from lsa_id 4.0.0.%d",
+                       inet_ntoa(lsah->adv_router),
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ri_lsa_delete): Abort! no valid "
+                       "SR Data Base");
+               return;
+       }
+
+       /* Release Router ID entry in SRDB hash table */
+       srn = hash_release(OspfSR.neighbors, &(lsah->adv_router));
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ri_lsa_delete): Abort! no entry in SRDB "
+                       "for SR Node %s",
+                       inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
+               zlog_err(
+                       "SR (ospf_sr_ri_lsa_delete): Abort! Wrong "
+                       "LSA ID 4.0.0.%d for SR node %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       /* Remove SR node */
+       sr_node_del(srn);
+
+       return;
+}
+
+/* Update Segment Routing from Extended Link LSA */
+void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct tlv_header *tlvh;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct sr_link *srl;
+
+       u_int16_t length, sum;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (ospf_sr_ext_link_lsa_update): Process "
+                       "Extended Link LSA 8.0.0.%d from %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_link_lsa_update): Abort! no "
+                       "valid SR DataBase");
+               return;
+       }
+
+       /* Get SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+                                        (void *)&(lsah->adv_router),
+                                        (void *)sr_node_new);
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_link_lsa_update): Abort! can't "
+                       "create SR node in hash table");
+               return;
+       }
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+       sum = 0;
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               if (ntohs(tlvh->type) == EXT_TLV_LINK) {
+                       /* Got Extended Link information */
+                       srl = get_ext_link_sid(tlvh);
+                       /* Update SID if not null */
+                       if (srl != NULL) {
+                               srl->instance = ntohl(lsah->id.s_addr);
+                               update_ext_link_sid(srn, srl, lsa->flags);
+                       }
+               }
+               sum += TLV_SIZE(tlvh);
+       }
+}
+
+/* Delete Segment Routing from Extended Link LSA */
+void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa)
+{
+       struct listnode *node;
+       struct sr_link *srl;
+       struct sr_node *srn;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       u_int32_t instance = ntohl(lsah->id.s_addr);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (ospf_sr_ext_link_lsa_delete): Remove "
+                       "Extended Link LSA 8.0.0.%d from %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_link_lsa_delete): Abort! no "
+                       "valid SR DataBase");
+               return;
+       }
+
+       /* Search SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+                                           (void *)&(lsah->adv_router));
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_link_lsa_delete): Abort! "
+                       "no entry in SRDB for SR Node %s",
+                       inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       /* Search for corresponding Segment Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl))
+               if (srl->instance == instance)
+                       break;
+
+       /* Remove Segment Link if found */
+       if (srl->instance == instance) {
+               del_sid_nhlfe(srl->nhlfe[0]);
+               del_sid_nhlfe(srl->nhlfe[1]);
+               listnode_delete(srn->ext_link, srl);
+               XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+       } else {
+               zlog_warn(
+                       "SR (ospf_sr_ext_link_lsa_delete): Didn't "
+                       "found corresponding SR Link 8.0.0.%d for SR Node %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+       }
+
+       return;
+}
+
+/* Update Segment Routing from Extended Prefix LSA */
+void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct tlv_header *tlvh;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct sr_prefix *srp;
+
+       u_int16_t length, sum;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (ospf_sr_ext_prefix_lsa_update): Process "
+                       "Extended Prefix LSA 7.0.0.%d from %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_prefix_lsa_update): Abort! no "
+                       "valid SR DataBase");
+               return;
+       }
+
+       /* Get SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+                                        (void *)&(lsah->adv_router),
+                                        (void *)sr_node_new);
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_prefix_lsa_update): Abort! can't "
+                       "create SR node in hash table");
+               return;
+       }
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+       sum = 0;
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               if (ntohs(tlvh->type) == EXT_TLV_LINK) {
+                       /* Got Extended Link information */
+                       srp = get_ext_prefix_sid(tlvh);
+                       /* Update SID if not null */
+                       if (srp != NULL) {
+                               srp->instance = ntohl(lsah->id.s_addr);
+                               update_ext_prefix_sid(srn, srp);
+                       }
+               }
+               sum += TLV_SIZE(tlvh);
+       }
+}
+
+/* Delete Segment Routing from Extended Prefix LSA */
+void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa)
+{
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct sr_node *srn;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       u_int32_t instance = ntohl(lsah->id.s_addr);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (ospf_sr_ext_prefix_lsa_delete): Remove "
+                       "Extended Prefix LSA 7.0.0.%d from %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_prefix_lsa_delete): Abort! no "
+                       "valid SR DataBase");
+               return;
+       }
+
+       /* Search SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+                                           (void *)&(lsah->adv_router));
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (ospf_sr_ext_prefix_lsa_delete):  Abort! "
+                       "no entry in SRDB for SR Node %s",
+                       inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       /* Search for corresponding Segment Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp))
+               if (srp->instance == instance)
+                       break;
+
+       /* Remove Segment Link if found */
+       if (srp->instance == instance) {
+               del_sid_nhlfe(srp->nhlfe);
+               listnode_delete(srn->ext_link, srp);
+               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+       } else {
+               zlog_warn(
+                       "SR (ospf_sr_ext_prefix_lsa_delete): Didn't found"
+                       "corresponding SR Prefix 7.0.0.%d for SR Node %s",
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+       }
+
+       return;
+}
+
+/* Get Label for Extended Link SID */
+/* TODO: To be replace by Zebra Label Manager */
+u_int32_t get_ext_link_label_value(void)
+{
+       static u_int32_t label = ADJ_SID_MIN - 1;
+
+       if (label < ADJ_SID_MAX)
+               label += 1;
+
+       return label;
+}
+
+/*
+ * Following functions are used to update MPLS LFIB after a SPF run
+ */
+
+static void ospf_sr_nhlfe_update(struct hash_backet *backet, void *args)
+{
+
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct sr_nhlfe old;
+       struct interface *ifp;
+       struct prefix p;
+       int rc;
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Update Prefix for SR Node %s",
+                          inet_ntoa(srn->adv_router));
+
+       /* For FRR router check if there is no SR Prefix
+        * waiting to be communicated to Extended Prefix */
+       if (srn == OspfSR.self) {
+               for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+
+                       /* Skip Prefix already engaged */
+                       if (srp->instance != 0)
+                               continue;
+                       /* Get Interface and check if it is a Loopback */
+                       p.family = AF_INET;
+                       p.prefixlen = srp->nhlfe.prefv4.prefixlen;
+                       IPV4_ADDR_COPY(&p.u.prefix4, &srp->nhlfe.prefv4.prefix);
+                       ifp = if_lookup_prefix(&p, VRF_DEFAULT);
+                       if (ifp == NULL)
+                               continue;
+                       /* If interface is not a loopback, remove SR prefix */
+                       if (!if_is_loopback(ifp)) {
+                               zlog_warn(
+                                       "  |-  Interface %s is not a "
+                                       "Loopback. Remove prefix",
+                                       ifp->name);
+                               listnode_delete(srn->ext_prefix, srp);
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+                               continue;
+                       }
+                       /* OK. Let's update Extended Prefix LSA */
+                       rc = ospf_ext_schedule_prefix_index(ifp, srp->sid,
+                                                           &srp->nhlfe.prefv4);
+                       srp->instance = SET_OPAQUE_LSID(
+                               OPAQUE_TYPE_EXTENDED_PREFIX_LSA, rc);
+                       srp->nhlfe.ifindex = ifp->ifindex;
+               }
+               return;
+       }
+
+       /* Update Extended Prefix */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+
+               /* Backup current NHLFE */
+               memcpy(&old, &srp->nhlfe, sizeof(struct sr_nhlfe));
+
+               /* Compute the new NHLFE */
+               rc = compute_prefix_nhlfe(srp);
+
+               /* Check computation result */
+               switch (rc) {
+               /* next hop is not know, remove old NHLFE to avoid loop */
+               case -1:
+                       del_sid_nhlfe(srp->nhlfe);
+                       break;
+               /* next hop has not changed, skip it */
+               case 0:
+                       break;
+               /* there is a new next hop, update NHLFE */
+               case 1:
+                       update_sid_nhlfe(old, srp->nhlfe);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static int ospf_sr_update_schedule(struct thread *t)
+{
+
+       struct ospf *ospf;
+       struct timeval start_time, stop_time;
+
+       ospf = THREAD_ARG(t);
+       ospf->t_sr_update = NULL;
+
+       if (!OspfSR.update)
+               return 0;
+
+       monotime(&start_time);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("SR (ospf_sr_update_schedule): Start SPF update");
+
+       hash_iterate(OspfSR.neighbors, (void (*)(struct hash_backet *,
+                                                void *))ospf_sr_nhlfe_update,
+                    NULL);
+
+       monotime(&stop_time);
+
+       zlog_info(
+               "SR (ospf_sr_update_schedule): SPF Processing Time(usecs): "
+               "%lld\n",
+               (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+                       + (stop_time.tv_usec - start_time.tv_usec));
+
+       OspfSR.update = false;
+       return 1;
+}
+
+#define OSPF_SR_UPDATE_INTERVAL        1
+
+void ospf_sr_update_timer_add(struct ospf *ospf)
+{
+
+       if (ospf == NULL)
+               return;
+
+       /* Check if an update is not alreday engage */
+       if (OspfSR.update)
+               return;
+
+       OspfSR.update = true;
+
+       thread_add_timer(master, ospf_sr_update_schedule, ospf,
+                        OSPF_SR_UPDATE_INTERVAL, &ospf->t_sr_update);
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty command functions.
+ *------------------------------------------------------------------------*/
+
+/*
+ * Segment Routing Router configuration
+ *
+ * Must be centralize as it concerns both Extended Link/Prefix LSA
+ * and Router Information LSA. Choose to call it from Extended Prefix
+ * write_config() call back.
+ *
+ * @param vty VTY output
+ *
+ * @return none
+ */
+void ospf_sr_config_write_router(struct vty *vty)
+{
+       struct listnode *node;
+       struct sr_prefix *srp;
+
+       if (OspfSR.enabled) {
+               vty_out(vty, " segment-routing on\n");
+
+               vty_out(vty, " segment-routing global-block %d %d\n",
+                       OspfSR.srgb.lower_bound,
+                       OspfSR.srgb.lower_bound + OspfSR.srgb.range_size - 1);
+
+               if (OspfSR.msd != 0)
+                       vty_out(vty, " segment-routing node-msd %d\n",
+                               OspfSR.msd);
+
+               if (OspfSR.self != NULL) {
+                       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node,
+                                                 srp)) {
+                               vty_out(vty,
+                                       " segment-routing prefix %s/%d "
+                                       "index %d\n",
+                                       inet_ntoa(srp->nhlfe.prefv4.prefix),
+                                       srp->nhlfe.prefv4.prefixlen, srp->sid);
+                       }
+               }
+       }
+}
+
+DEFUN(ospf_sr_enable,
+       ospf_sr_enable_cmd,
+       "segment-routing on",
+       SR_STR
+       "Enable Segment Routing\n")
+{
+
+       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+       if (OspfSR.enabled)
+               return CMD_SUCCESS;
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Segment Routing: OFF -> ON");
+
+       /* Start Segment Routing */
+       OspfSR.enabled = true;
+       if (!ospf_sr_start(ospf)) {
+               zlog_warn("SR: Unable to start Segment Routing. Abort!");
+               return CMD_WARNING;
+       }
+
+       /* Set Router Information SR parameters */
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Activate SR for Router Information LSA");
+
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       /* Update Ext LSA */
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Activate SR for Extended Link/Prefix LSA");
+
+       ospf_ext_update_sr(true);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_sr_enable,
+       no_ospf_sr_enable_cmd,
+       "no segment-routing [on]",
+       NO_STR
+       SR_STR
+       "Disable Segment Routing\n")
+{
+
+       if (!OspfSR.enabled)
+               return CMD_SUCCESS;
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Segment Routing: ON -> OFF");
+
+       /* Start by Disabling Extended Link & Prefix LSA */
+       ospf_ext_update_sr(false);
+
+       /* then, disable Router Information SR parameters */
+       ospf_router_info_update_sr(false, OspfSR.srgb, OspfSR.msd);
+
+       /* Finally, stop Segment Routing */
+       ospf_sr_stop();
+       OspfSR.enabled = false;
+
+       return CMD_SUCCESS;
+}
+
+static int ospf_sr_enabled(struct vty *vty)
+{
+       if (OspfSR.enabled)
+               return 1;
+
+       if (vty)
+               vty_out(vty, "%% OSPF SR is not turned on\n");
+
+       return 0;
+}
+
+DEFUN (sr_sid_label_range,
+       sr_sid_label_range_cmd,
+       "segment-routing global-block (0-1048575) (0-1048575)",
+       SR_STR
+       "Segment Routing Global Block label range\n"
+       "Lower-bound range in decimal (0-1048575)\n"
+       "Upper-bound range in decimal (0-1048575)\n")
+{
+       u_int32_t upper;
+       u_int32_t lower;
+       u_int32_t size;
+       int idx_low = 2;
+       int idx_up = 3;
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       if (sscanf(argv[idx_low]->arg, "%d", &lower) != 1) {
+               vty_out(vty, "segment-routing: fscanf: %s\n",
+                       safe_strerror(errno));
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (sscanf(argv[idx_up]->arg, "%d", &upper) != 1) {
+               vty_out(vty, "segment-routing: fscanf: %s\n",
+                       safe_strerror(errno));
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       size = upper - lower + 1;
+
+       if (size > MPLS_DEFAULT_MAX_SRGB_SIZE || size <= 0) {
+               vty_out(vty,
+                       "Range size cannot be less than 0 or more than %d\n",
+                       MPLS_DEFAULT_MAX_SRGB_SIZE);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (upper > MPLS_DEFAULT_MAX_SRGB_LABEL) {
+               vty_out(vty, "Upper-bound cannot exceed %d\n",
+                       MPLS_DEFAULT_MAX_SRGB_LABEL);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (upper < MPLS_DEFAULT_MIN_SRGB_LABEL) {
+               vty_out(vty, "Upper-bound cannot be lower than %d\n",
+                       MPLS_DEFAULT_MIN_SRGB_LABEL);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Set SID/Label range SRGB */
+       OspfSR.srgb.range_size = size;
+       OspfSR.srgb.lower_bound = lower;
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       /* Update NHLFE entries */
+       hash_iterate(OspfSR.neighbors,
+                    (void (*)(struct hash_backet *, void *))update_in_nhlfe,
+                    NULL);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_sid_label_range,
+       no_sr_sid_label_range_cmd,
+       "no segment-routing global-block",
+       NO_STR
+       SR_STR
+       "Delete Segment Routing Global Block label range\n")
+{
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* Revert to default SRGB value */
+       OspfSR.srgb.range_size = MPLS_DEFAULT_MIN_SRGB_SIZE;
+       OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL;
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       /* Update NHLFE entries */
+       hash_iterate(OspfSR.neighbors,
+                    (void (*)(struct hash_backet *, void *))update_in_nhlfe,
+                    NULL);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (sr_node_msd,
+       sr_node_msd_cmd,
+       "segment-routing node-msd (1-16)",
+       SR_STR
+       "Maximum Stack Depth for this router\n"
+       "Maximum number of label that could be stack (1-16)\n")
+{
+       u_int32_t msd;
+       int idx_number = 2;
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       if (sscanf(argv[idx_number]->arg, "%d", &msd) != 1) {
+               vty_out(vty, "segment-routing: fscanf: %s\n",
+                       safe_strerror(errno));
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (msd < 1 || msd > MPLS_MAX_LABELS) {
+               vty_out(vty, "MSD must be comprise between 1 and %d\n",
+                       MPLS_MAX_LABELS);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Set this router MSD */
+       OspfSR.msd = msd;
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_node_msd,
+       no_sr_node_msd_cmd,
+       "no segment-routing node-msd",
+       NO_STR
+       SR_STR
+       "Disable Maximum Stack Depth for this router\n")
+{
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* unset this router MSD */
+       OspfSR.msd = 0;
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, 0);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (sr_prefix_sid,
+       sr_prefix_sid_cmd,
+       "segment-routing prefix A.B.C.D/M index (0-65535)",
+       SR_STR
+       "Prefix SID\n"
+       "IPv4 Prefix as A.B.C.D/M\n"
+       "SID index for this prefix in decimal (0-65535)\n"
+       "Index value inside SRGB (lower_bound < index < upper_bound)\n")
+{
+       int idx_prefix = 2;
+       int idx_index = 4;
+       struct prefix p;
+       uint32_t index;
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct interface *ifp;
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* Get network prefix */
+       str2prefix(argv[idx_prefix]->arg, &p);
+
+       /* Get & verify index value */
+       index = strtoul(argv[idx_index]->arg, NULL, 10);
+       if (index > OspfSR.srgb.range_size - 1) {
+               vty_out(vty, "Index %d must be lower than range size %d\n",
+                       index, OspfSR.srgb.range_size);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* check that the index is not already used */
+       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+               if (srp->sid == index) {
+                       vty_out(vty, "Index %d is already used\n", index);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       }
+
+       /* Get Interface and check if it is a Loopback */
+       ifp = if_lookup_prefix(&p, VRF_DEFAULT);
+       if (ifp == NULL) {
+               /* Interface could be not yet available i.e. when this
+                * command is in the configuration file, OSPF is not yet
+                * ready. In this case, store the prefix SID for latter
+                * (i.e. when SPF run) communication to Extended Prefix */
+               srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+               srp->instance = 0;
+               IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix, &p.u.prefix4);
+               srp->nhlfe.prefv4.prefixlen = p.prefixlen;
+               srp->nhlfe.prefv4.family = p.family;
+               srp->sid = index;
+               listnode_add(OspfSR.self->ext_prefix, srp);
+               vty_out(vty,
+                       "Interface for prefix %s not found. Deferred LSA "
+                       "flooding\n",
+                       argv[idx_prefix]->arg);
+               return CMD_SUCCESS;
+       }
+       if (!if_is_loopback(ifp)) {
+               vty_out(vty, "interface %s is not a Loopback\n", ifp->name);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Update Extended Prefix LSA */
+       if (!ospf_ext_schedule_prefix_index(ifp, index,
+                                           (struct prefix_ipv4 *)&p)) {
+               vty_out(vty, "Unable to set index %d for prefix %s\n", index,
+                       argv[idx_prefix]->arg);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_prefix_sid,
+       no_sr_prefix_sid_cmd,
+       "no segment-routing prefix A.B.C.D/M",
+       NO_STR
+       SR_STR
+       "Prefix SID\n"
+       "IPv4 Prefix as A.B.C.D/M\n")
+{
+       int idx_prefix = 2;
+       struct prefix p;
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct interface *ifp;
+       bool found = false;
+
+       /* Get network prefix */
+       str2prefix(argv[idx_prefix]->arg, &p);
+
+       /* check that the prefix is already set */
+       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp))
+               if (IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
+                   && (srp->nhlfe.prefv4.prefixlen == p.prefixlen))
+                       found = true;
+
+       if (!found) {
+               vty_out(vty, "Prefix %s is not found. Abort!\n",
+                       argv[idx_prefix]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Get Interface and check if it is a Loopback */
+       ifp = if_lookup_prefix(&p, VRF_DEFAULT);
+       if (ifp == NULL) {
+               vty_out(vty, "interface for prefix %s not found.\n",
+                       argv[idx_prefix]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (!if_is_loopback(ifp)) {
+               vty_out(vty, "interface %s is not a Loopback\n", ifp->name);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       /* Update Extended Prefix LSA */
+       if (!ospf_ext_schedule_prefix_index(ifp, 0, NULL)) {
+               vty_out(vty, "No corresponding loopback interface. Abort!\n");
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+static void show_vty_sr_node(struct vty *vty, struct sr_node *srn)
+{
+
+       struct listnode *node;
+       struct sr_link *srl;
+       struct sr_prefix *srp;
+       struct interface *itf;
+       char pref[16];
+       char sid[20];
+       char label[8];
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router));
+       vty_out(vty, "\tSRGB (Size/Label): %d/%d", srn->srgb.range_size,
+               srn->srgb.lower_bound);
+       vty_out(vty, "\tAlgorithm(s): %s",
+               srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+       for (int i = 1; i < ALGORITHM_COUNT; i++) {
+               if (srn->algo[i] == SR_ALGORITHM_UNSET)
+                       continue;
+               vty_out(vty, "/%s",
+                       srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+       }
+       if (srn->msd != 0)
+               vty_out(vty, "\tMSD: %d", srn->msd);
+
+       vty_out(vty,
+               "\n\n    Prefix or Link  Label In  Label Out      "
+               "Node or Adj. SID  Interface          Nexthop\n");
+       vty_out(vty,
+               "------------------  --------  ---------  "
+               "--------------------  ---------  ---------------\n");
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+               strncpy(pref, inet_ntoa(srp->nhlfe.prefv4.prefix), 16);
+               snprintf(sid, 20, "SR Pfx (idx %d)", srp->sid);
+               if (srp->nhlfe.label_out == MPLS_IMP_NULL_LABEL)
+                       sprintf(label, "pop");
+               else
+                       sprintf(label, "%d", srp->nhlfe.label_out);
+               itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
+               vty_out(vty, "%15s/%d  %8d  %9s  %20s  %9s  %15s\n", pref,
+                       srp->nhlfe.prefv4.prefixlen, srp->nhlfe.label_in, label,
+                       sid, itf ? itf->name : "-",
+                       inet_ntoa(srp->nhlfe.nexthop));
+       }
+
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
+               strncpy(pref, inet_ntoa(srl->nhlfe[0].prefv4.prefix), 16);
+               snprintf(sid, 20, "SR Adj. (lbl %d)", srl->sid[0]);
+               if (srl->nhlfe[0].label_out == MPLS_IMP_NULL_LABEL)
+                       sprintf(label, "pop");
+               else
+                       sprintf(label, "%d", srl->nhlfe[0].label_out);
+               itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT);
+               vty_out(vty, "%15s/%d  %8d  %9s  %20s  %9s  %15s\n", pref,
+                       srl->nhlfe[0].prefv4.prefixlen, srl->nhlfe[0].label_in,
+                       label, sid, itf ? itf->name : "-",
+                       inet_ntoa(srl->nhlfe[0].nexthop));
+               snprintf(sid, 20, "SR Adj. (lbl %d)", srl->sid[1]);
+               if (srl->nhlfe[1].label_out == MPLS_IMP_NULL_LABEL)
+                       sprintf(label, "pop");
+               else
+                       sprintf(label, "%d", srl->nhlfe[0].label_out);
+               vty_out(vty, "%15s/%d  %8d  %9s  %20s  %9s  %15s\n", pref,
+                       srl->nhlfe[1].prefv4.prefixlen, srl->nhlfe[1].label_in,
+                       label, sid, itf ? itf->name : "-",
+                       inet_ntoa(srl->nhlfe[1].nexthop));
+       }
+       vty_out(vty, "\n");
+}
+
+static void show_srdb_entry(struct hash_backet *backet, void *args)
+{
+       struct vty *vty = (struct vty *)args;
+       struct sr_node *srn = (struct sr_node *)backet->data;
+
+       show_vty_sr_node(vty, srn);
+}
+
+DEFUN (show_ip_opsf_srdb,
+       show_ip_ospf_srdb_cmd,
+       "show ip ospf database segment-routing [adv-router A.B.C.D|self-originate]",
+       SHOW_STR
+       IP_STR
+       OSPF_STR
+       "Database summary\n"
+       "Show Segment Routing Data Base\n"
+       "Advertising SR node\n"
+       "Advertising SR node ID (as an IP address)\n"
+       "Self-originated SR node\n")
+{
+       int idx_ip = 6;
+       struct in_addr rid;
+       struct sr_node *srn;
+
+       if (!OspfSR.enabled) {
+               vty_out(vty, "Segment Routing is disabled on this router\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       vty_out(vty, "\n          OSPF Segment Routing database for ID %s\n\n",
+               inet_ntoa(OspfSR.self->adv_router));
+
+       if (argc < idx_ip) {
+               /* Iterate through all the SRDB */
+               hash_iterate(
+                       OspfSR.neighbors,
+                       (void (*)(struct hash_backet *, void *))show_srdb_entry,
+                       (void *)vty);
+       } else {
+               /* or show only specified SR Node */
+               if (argc == idx_ip) {
+                       srn = OspfSR.self;
+               } else {
+                       if (!inet_aton(argv[idx_ip]->arg, &rid)) {
+                               vty_out(vty,
+                                       "Specified Router ID %s is "
+                                       "invalid\n",
+                                       argv[idx_ip]->arg);
+                               return CMD_WARNING_CONFIG_FAILED;
+                       }
+                       /* Get the SR Node from the SRDB */
+                       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+                                                           (void *)&rid);
+               }
+               show_vty_sr_node(vty, srn);
+       }
+       return CMD_SUCCESS;
+}
+
+/* Install new CLI commands */
+void ospf_sr_register_vty(void)
+{
+       install_element(VIEW_NODE, &show_ip_ospf_srdb_cmd);
+
+       install_element(OSPF_NODE, &ospf_sr_enable_cmd);
+       install_element(OSPF_NODE, &no_ospf_sr_enable_cmd);
+       install_element(OSPF_NODE, &sr_sid_label_range_cmd);
+       install_element(OSPF_NODE, &no_sr_sid_label_range_cmd);
+       install_element(OSPF_NODE, &sr_node_msd_cmd);
+       install_element(OSPF_NODE, &no_sr_node_msd_cmd);
+       install_element(OSPF_NODE, &sr_prefix_sid_cmd);
+       install_element(OSPF_NODE, &no_sr_prefix_sid_cmd);
+
+       return;
+}
diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h
new file mode 100644 (file)
index 0000000..3888164
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * This is an implementation of Segment Routing
+ * as per draft draft-ietf-ospf-segment-routing-extensions-24
+ *
+ * Module name: Segment Routing header definitions
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2017 Orange Labs http://www.orange.com
+ *
+ * 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _FRR_OSPF_SR_H
+#define _FRR_OSPF_SR_H
+
+/* Default Route priority for OSPF Segment Routing */
+#define OSPF_SR_PRIORITY_DEFAULT       10
+
+/* macros and constants for segment routing */
+#define SET_RANGE_SIZE_MASK             0xffffff00
+#define GET_RANGE_SIZE_MASK             0x00ffffff
+#define SET_LABEL_MASK                  0xffffff00
+#define GET_LABEL_MASK                  0x00ffffff
+#define SET_RANGE_SIZE(range_size) ((range_size << 8) & SET_RANGE_SIZE_MASK)
+#define GET_RANGE_SIZE(range_size) ((range_size >> 8) & GET_RANGE_SIZE_MASK)
+#define SET_LABEL(label) ((label << 8) & SET_LABEL_MASK)
+#define GET_LABEL(label) ((label >> 8) & GET_LABEL_MASK)
+
+/* Label range for Adj-SID attribution purpose. See ospf_ext.c */
+#define ADJ_SID_MIN                     50000
+#define ADJ_SID_MAX                     51000
+
+#define OSPF_SR_DEFAULT_METRIC         1
+
+/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */
+
+/* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */
+#define SID_BASE_SIZE  4
+#define SID_LABEL      3
+#define SID_LABEL_SIZE (SID_BASE_SIZE + SID_LABEL)
+#define SID_INDEX      4
+#define SID_INDEX_SIZE (SID_BASE_SIZE + SID_INDEX)
+
+/* SID/Label Sub TLV - section 2.1 */
+#define SUBTLV_SID_LABEL               1
+#define SUBTLV_SID_LABEL_SIZE          8
+struct subtlv_sid_label {
+       /* Length is 3 (20 rightmost bits MPLS label) or 4 (32 bits SID) */
+       struct tlv_header header;
+       u_int32_t value;
+};
+
+/*
+ * Following section defines Segment Routing TLV (tag, length, value)
+ * structures, used in Router Information Opaque LSA.
+ */
+
+/* RI SR-Algorithm TLV - section 3.1 */
+#define RI_SR_TLV_SR_ALGORITHM          8
+struct ri_sr_tlv_sr_algorithm {
+       struct tlv_header header;
+#define SR_ALGORITHM_SPF         0
+#define SR_ALGORITHM_STRICT_SPF  1
+#define SR_ALGORITHM_UNSET       255
+#define ALGORITHM_COUNT          4
+       /* Only 4 algorithms supported in this code */
+       u_int8_t value[ALGORITHM_COUNT];
+};
+
+/* RI SID/Label Range TLV - section 3.2 */
+#define RI_SR_TLV_SID_LABEL_RANGE      9
+struct ri_sr_tlv_sid_label_range {
+       struct tlv_header header;
+/* Only 24 upper most bits are significant */
+#define SID_RANGE_LABEL_LENGTH 3
+       u_int32_t size;
+       /* A SID/Label sub-TLV will follow. */
+       struct subtlv_sid_label lower;
+};
+
+/* RI Node/MSD TLV as per draft-ietf-ospf-segment-routing-msd-05 */
+#define RI_SR_TLV_NODE_MSD             12
+struct ri_sr_tlv_node_msd {
+       struct tlv_header header;
+       u_int8_t subtype; /* always = 1 */
+       u_int8_t value;
+       u_int16_t padding;
+};
+
+/*
+ * Following section defines Segment Routing TLV (tag, length, value)
+ * structures, used in Extended Prefix/Link Opaque LSA.
+ */
+
+/* Adj-SID and LAN-Ajd-SID subtlvs' flags */
+#define EXT_SUBTLV_LINK_ADJ_SID_BFLG   0x80
+#define EXT_SUBTLV_LINK_ADJ_SID_VFLG   0x40
+#define EXT_SUBTLV_LINK_ADJ_SID_LFLG   0x20
+#define EXT_SUBTLV_LINK_ADJ_SID_SFLG   0x10
+
+/* Prefix SID subtlv Flags */
+#define EXT_SUBTLV_PREFIX_SID_NPFLG    0x40
+#define EXT_SUBTLV_PREFIX_SID_MFLG     0x20
+#define EXT_SUBTLV_PREFIX_SID_EFLG     0x10
+#define EXT_SUBTLV_PREFIX_SID_VFLG     0x08
+#define EXT_SUBTLV_PREFIX_SID_LFLG     0x04
+
+/* SID/Label Binding subtlv Flags */
+#define EXT_SUBTLV_SID_BINDING_MFLG    0x80
+
+/* Extended Prefix Range TLV - section 4 */
+#define EXT_TLV_PREF_RANGE             2
+#define EXT_SUBTLV_PREFIX_RANGE_SIZE   12
+struct ext_tlv_prefix_range {
+       struct tlv_header header;
+       u_int8_t pref_length;
+       u_int8_t af;
+       u_int16_t range_size;
+       u_int8_t flags;
+       u_int8_t reserved[3];
+       struct in_addr address;
+};
+
+/* Prefix SID Sub-TLV - section 5 */
+#define EXT_SUBTLV_PREFIX_SID          2
+#define EXT_SUBTLV_PREFIX_SID_SIZE     8
+struct ext_subtlv_prefix_sid {
+       struct tlv_header header;
+       u_int8_t flags;
+       u_int8_t reserved;
+       u_int8_t mtid;
+       u_int8_t algorithm;
+       u_int32_t value;
+};
+
+/* Adj-SID Sub-TLV - section 6.1 */
+#define EXT_SUBTLV_ADJ_SID             2
+#define EXT_SUBTLV_ADJ_SID_SIZE                8
+struct ext_subtlv_adj_sid {
+       struct tlv_header header;
+       u_int8_t flags;
+       u_int8_t reserved;
+       u_int8_t mtid;
+       u_int8_t weight;
+       u_int32_t value;
+};
+
+/* LAN Adj-SID Sub-TLV - section 6.2 */
+#define EXT_SUBTLV_LAN_ADJ_SID         3
+#define EXT_SUBTLV_LAN_ADJ_SID_SIZE    12
+struct ext_subtlv_lan_adj_sid {
+       struct tlv_header header;
+       u_int8_t flags;
+       u_int8_t reserved;
+       u_int8_t mtid;
+       u_int8_t weight;
+       struct in_addr neighbor_id;
+       u_int32_t value;
+};
+
+/*
+ * Following section define structure used to manage Segment Routing
+ * information and TLVs / SubTLVs
+ */
+
+/* Structure aggregating SRGB info retrieved from an lsa */
+struct sr_srgb {
+       u_int32_t range_size;
+       u_int32_t lower_bound;
+};
+
+/* SID type to make difference between loopback interfaces and others */
+enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID };
+
+/* Structure aggregating all OSPF Segment Routing information for the node */
+struct ospf_sr_db {
+       /* Status of Segment Routing: enable or disable */
+       bool enabled;
+
+       /* Ongoing Update following an OSPF SPF */
+       bool update;
+
+       /* Flooding Scope: Area = 10 or AS = 11 */
+       u_int8_t scope;
+
+       /* FRR SR node */
+       struct sr_node *self;
+
+       /* List of neighbour SR nodes */
+       struct hash *neighbors;
+
+       /* List of SR prefix */
+       struct route_table *prefix;
+
+       /* Local SR info announced in Router Info LSA */
+
+       /* Algorithms supported by the node */
+       u_int8_t algo[ALGORITHM_COUNT];
+       /*
+        * Segment Routing Global Block i.e. label range
+        * Only one range supported in this code
+        */
+       struct sr_srgb srgb;
+       /* Maximum SID Depth supported by the node */
+       u_int8_t msd;
+};
+
+/* Structure aggregating all received SR info from LSAs by node */
+struct sr_node {
+       struct in_addr adv_router; /* used to identify sender of LSA */
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       u_int32_t instance;
+
+       u_int8_t algo[ALGORITHM_COUNT]; /* Algorithms supported by the node */
+       /* Segment Routing Global Block i.e. label range */
+       struct sr_srgb srgb;
+       u_int8_t msd; /* Maximum SID Depth */
+
+       /* List of Prefix & Link advertise by this node */
+       struct list *ext_prefix; /* For Node SID */
+       struct list *ext_link;   /* For Adj and LAN SID */
+
+       /* Pointer to FRR SR-Node or NULL if it is not a neighbor */
+       struct sr_node *neighbor;
+};
+
+
+/* Segment Routing - NHLFE info: support IPv4 Only */
+struct sr_nhlfe {
+       struct prefix_ipv4 prefv4;
+       struct in_addr nexthop;
+       ifindex_t ifindex;
+       mpls_label_t label_in;
+       mpls_label_t label_out;
+};
+
+/* Structure aggregating all Segment Routing Link information */
+/* Link are generally advertised by pair: primary + backup */
+struct sr_link {
+       struct in_addr adv_router; /* used to identify sender of LSA */
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       u_int32_t instance;
+
+       /* Flags to manage this link parameters. */
+       u_int32_t flags[2];
+
+       /* Segment Routing ID */
+       u_int32_t sid[2];
+       enum sid_type type;
+
+       /* SR NHLFE for this link */
+       struct sr_nhlfe nhlfe[2];
+
+       /* Back pointer to SR Node which advertise this Link */
+       struct sr_node *srn;
+};
+
+/* Structure aggregating all Segment Routing Prefix information */
+struct sr_prefix {
+       struct in_addr adv_router; /* used to identify sender of LSA */
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       u_int32_t instance;
+
+       /* Flags to manage this prefix parameters. */
+       u_int32_t flags;
+
+       /* Segment Routing ID */
+       u_int32_t sid;
+       enum sid_type type;
+
+       /* SR NHLFE for this prefix */
+       struct sr_nhlfe nhlfe;
+
+       /* Back pointer to SR Node which advertise this Prefix */
+       struct sr_node *srn;
+
+       /* Pointer to SR Node which is the next hop for this Prefix
+        * or NULL if next hop is the destination of the prefix */
+       struct sr_node *nexthop;
+};
+
+/* Prototypes definition */
+/* Segment Routing initialisation functions */
+extern int ospf_sr_init(void);
+extern void ospf_sr_term(void);
+/* Segment Routing LSA update & delete functions */
+extern void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *);
+extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *);
+extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *);
+extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *);
+/* Segment Routing configuration functions */
+extern u_int32_t get_ext_link_label_value(void);
+extern void ospf_sr_config_write_router(struct vty *);
+/* Segment Routing re-routing function */
+extern void ospf_sr_update_timer_add(struct ospf *);
+#endif /* _FRR_OSPF_SR_H */
index 5cb8ca85bc9acf93b0493fef9104e2700dc45aff..6954660e0247f11d589de44626396b2777103919 100644 (file)
@@ -240,6 +240,7 @@ struct ospf {
        struct thread *t_external_lsa;      /* AS-external-LSA origin timer. */
        struct thread
                *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */
+       struct thread *t_sr_update; /* Segment Routing update timer */
 
        unsigned int maxage_delay;      /* Delay on Maxage remover timer, sec */
        struct thread *t_maxage;        /* MaxAge LSA remover timer. */
index e063415fbdfa6f03ebf4f55ff9b1f7ba2ef3978f..9f04260366a1598d20b553e181e5f0b01272e50c 100644 (file)
@@ -20,6 +20,7 @@ ospfd_libfrrospf_a_SOURCES = \
        ospfd/ospf_bfd.c \
        ospfd/ospf_dump.c \
        ospfd/ospf_dump_api.c \
+       ospfd/ospf_ext.c \
        ospfd/ospf_flood.c \
        ospfd/ospf_ia.c \
        ospfd/ospf_interface.c \
@@ -36,6 +37,7 @@ ospfd_libfrrospf_a_SOURCES = \
        ospfd/ospf_route.c \
        ospfd/ospf_routemap.c \
        ospfd/ospf_spf.c \
+       ospfd/ospf_sr.c \
        ospfd/ospf_te.c \
        ospfd/ospf_vty.c \
        ospfd/ospf_zebra.c \
@@ -66,6 +68,7 @@ noinst_HEADERS += \
        ospfd/ospf_apiserver.h \
        ospfd/ospf_ase.h \
        ospfd/ospf_bfd.h \
+       ospfd/ospf_ext.h \
        ospfd/ospf_flood.h \
        ospfd/ospf_ia.h \
        ospfd/ospf_interface.h \
@@ -76,6 +79,7 @@ noinst_HEADERS += \
        ospfd/ospf_ri.h \
        ospfd/ospf_route.h \
        ospfd/ospf_spf.h \
+       ospfd/ospf_sr.h \
        ospfd/ospf_te.h \
        ospfd/ospf_vty.h \
        ospfd/ospf_zebra.h \
index 3ddb6aba54092765a4caea729beed5ad00d83047..c9b6f501606416c9c1207e6c8ef8ee190e45c9bc 100644 (file)
@@ -75,6 +75,7 @@ vtysh_scan += $(top_srcdir)/ospfd/ospf_opaque.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_ri.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_routemap.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_te.c
+vtysh_scan += $(top_srcdir)/ospfd/ospf_sr.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_vty.c
 endif
 
index 1be2cbcaf54ea27b974cacfee3a58f9cb9a2bd5b..d794c4c64d199faf8f8a72f8946bf4f852c420e9 100644 (file)
@@ -102,6 +102,7 @@ static const struct message rtproto_str[] = {
        {RTPROT_MROUTED, "mroute"},
        {RTPROT_BGP, "BGP"},
        {RTPROT_OSPF, "OSPF"},
+       {RTPROT_OSPF_SR, "OSPF-SR"},
        {RTPROT_ISIS, "IS-IS"},
        {RTPROT_RIP, "RIP"},
        {RTPROT_RIPNG, "RIPNG"},
index a77814668d4518d179abf30db4d5ea2b3081678d..bb034b89395580b26f794c9aa9ffa98f106eb228 100644 (file)
@@ -98,7 +98,8 @@ static inline int is_selfroute(int proto)
            || (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG)
            || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP)
            || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL)
-           || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)) {
+           || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)
+           || (proto == RTPROT_OSPF_SR)) {
                return 1;
        }
 
@@ -118,6 +119,9 @@ static inline int zebra2proto(int proto)
        case ZEBRA_ROUTE_OSPF6:
                proto = RTPROT_OSPF;
                break;
+       case ZEBRA_ROUTE_OSPF_SR:
+               proto = RTPROT_OSPF_SR;
+               break;
        case ZEBRA_ROUTE_STATIC:
                proto = RTPROT_STATIC;
                break;
index 51350fd6fb114d135a31d5f43f50e53d64df44b9..79d5e14a625d6a66ae2399a2740c9aebedcdc9f1 100644 (file)
@@ -52,6 +52,7 @@
 #define RTPROT_EIGRP       192
 #define RTPROT_LDP         193
 #define RTPROT_SHARP       194
+#define RTPROT_OSPF_SR     195
 
 void rt_netlink_init(void);
 
index 22c771c34809aa86706180a480c00056d40b4b1b..93e1162b6f166ce2f2dc31afcc47008d99f079bb 100644 (file)
@@ -316,6 +316,12 @@ void mpls_ldp_lsp_uninstall_all(struct hash_backet *backet, void *ctxt);
  */
 void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi);
 
+/*
+ * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry.
+ * If no other NHLFEs exist, the entry would be deleted.
+ */
+void mpls_sr_lsp_uninstall_all(struct hash_backet *backet, void *ctxt);
+
 #if defined(HAVE_CUMULUS)
 /*
  * Check that the label values used in LSP creation are consistent. The
@@ -448,6 +454,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type)
                return ZEBRA_ROUTE_LDP;
        case ZEBRA_LSP_BGP:
                return ZEBRA_ROUTE_BGP;
+       case ZEBRA_LSP_SR:
+               return ZEBRA_ROUTE_OSPF_SR;
        case ZEBRA_LSP_NONE:
        default:
                return ZEBRA_ROUTE_KERNEL;
@@ -464,6 +472,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type)
                return "LDP";
        case ZEBRA_LSP_BGP:
                return "BGP";
+       case ZEBRA_LSP_SR:
+               return "SR";
        default:
                return "Unknown";
        }