--- /dev/null
+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
+
* Opaque LSA::
* OSPF Traffic Engineering::
* Router Information::
+* Segment Routing::
* Debugging OSPF::
* OSPF Configuration Examples::
@end menu
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
#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"
#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
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. */
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, "-"
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)"
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};
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)
{
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",
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);
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);
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);
#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)
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[];
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
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")
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 */
#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")
* 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);
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);
ospf_router_info_term();
+ ospf_ext_term();
+
+ ospf_sr_term();
+
#ifdef SUPPORT_OSPF_API
ospf_apiserver_term();
#endif /* SUPPORT_OSPF_API */
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";
#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
* 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.
*
#include "thread.h"
#include "hash.h"
#include "sockunion.h" /* for inet_aton() */
+#include "mpls.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
#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 {
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;
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;
/* Store PCE capability LSA */
struct ospf_pce_info pce_info;
+
+ /* Store SR capability LSA */
+ struct ospf_ri_sr_info sr_info;
};
/*
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 */
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;
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(
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.
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)
{
&& (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.
*------------------------------------------------------------------------*/
/* 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);
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.
*------------------------------------------------------------------------*/
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;
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;
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;
}
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)
} else {
vty_out(vty,
- " Router Information is disabled on this router\n");
+ " PCE info is disabled on this router\n");
}
return CMD_SUCCESS;
/*
* 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.
*
*
* 24 16 8 0
* +--------+--------+--------+--------+
- * | 1 | MBZ |........|........|
+ * | 4 | MBZ |........|........|
* +--------+--------+--------+--------+
* |<-Type->|<Resv'd>|<-- Instance --->|
*
* +--------+--------+--------+--------+ |
* | 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.
* +--------+--------+--------+--------+ ---
*/
* 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
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
#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 */
#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 */
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__,
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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
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. */
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 \
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 \
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 \
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 \
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
{RTPROT_MROUTED, "mroute"},
{RTPROT_BGP, "BGP"},
{RTPROT_OSPF, "OSPF"},
+ {RTPROT_OSPF_SR, "OSPF-SR"},
{RTPROT_ISIS, "IS-IS"},
{RTPROT_RIP, "RIP"},
{RTPROT_RIPNG, "RIPNG"},
|| (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;
}
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;
#define RTPROT_EIGRP 192
#define RTPROT_LDP 193
#define RTPROT_SHARP 194
+#define RTPROT_OSPF_SR 195
void rt_netlink_init(void);
*/
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
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;
return "LDP";
case ZEBRA_LSP_BGP:
return "BGP";
+ case ZEBRA_LSP_SR:
+ return "SR";
default:
return "Unknown";
}