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