From cf9b9f77f638923f5a44fdd14ce2725631ffa526 Mon Sep 17 00:00:00 2001 From: Olivier Dugeon Date: Thu, 18 Jan 2018 19:11:11 +0100 Subject: [PATCH] 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 --- doc/OSPF-SR.rst | 82 ++ doc/ospfd.texi | 39 + lib/command.h | 1 + lib/mpls.h | 9 +- lib/route_types.txt | 2 + ospfd/ospf_dump.c | 36 +- ospfd/ospf_dump.h | 8 + ospfd/ospf_ext.c | 1769 ++++++++++++++++++++++++++++++++ ospfd/ospf_ext.h | 196 ++++ ospfd/ospf_memory.c | 2 + ospfd/ospf_memory.h | 2 + ospfd/ospf_opaque.c | 25 +- ospfd/ospf_opaque.h | 4 +- ospfd/ospf_ri.c | 423 +++++++- ospfd/ospf_ri.h | 54 +- ospfd/ospf_spf.c | 5 +- ospfd/ospf_sr.c | 2186 ++++++++++++++++++++++++++++++++++++++++ ospfd/ospf_sr.h | 315 ++++++ ospfd/ospfd.h | 1 + ospfd/subdir.am | 4 + vtysh/Makefile.am | 1 + zebra/kernel_netlink.c | 1 + zebra/rt_netlink.c | 6 +- zebra/rt_netlink.h | 1 + zebra/zebra_mpls.h | 10 + 25 files changed, 5096 insertions(+), 86 deletions(-) create mode 100644 doc/OSPF-SR.rst create mode 100644 ospfd/ospf_ext.c create mode 100644 ospfd/ospf_ext.h create mode 100644 ospfd/ospf_sr.c create mode 100644 ospfd/ospf_sr.h diff --git a/doc/OSPF-SR.rst b/doc/OSPF-SR.rst new file mode 100644 index 0000000000..8dcd88592a --- /dev/null +++ b/doc/OSPF-SR.rst @@ -0,0 +1,82 @@ +OSPF Segment Routing +==================== + +This is an EXPERIMENTAL support of draft draft-ietf-ospf-segment-routing-extensions-24. +DON'T use it for production network. + +Implementation details +---------------------- + +Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various information: + - Router Information: flood the Segment Routing capabilities of the node. This include + the supported algorithms, the Segment Routing Global Block (SRGB) and the Maximum Stack + Depth. + - Extended Link: flood the Adjaceny and Lan Adjacency Segment Identifier + - Extended Prefix: flood the Prefix Segment Identifier + +The implementation follow previous TE and Router Information code. It used the OPAQUE LSA +functions define in ospf_opaque.[c,h] as well as the OSPF API. This latter is mandatory +for the implementation as it provides the Callback to Segment Routing functions (see below) +when an Extended Link / Prefix or Router Information is received. + +Following files where modified or added: + - ospd_ri.[c,h] have been modified to add the new TLVs for Segment Routing. + - ospf_ext.[c,h] implement RFC7684 as base support of Extended Link and Prefix Opaque LSA. + - ospf_sr.[c,h] implement the earth of Segment Routing. It adds a new Segment Routing database + to manage Segment Identifiers per Link and Prefix and Segment Routing enable node, Callback + functions to process incoming LSA and install MPLS FIB entry through Zebra. + +the figure below shows the relation between the various files: + + - ospf_sr.c centralized all the Segment Routing processing. It receives Opaque LSA + Router Information (4.0.0.0) from ospf_ri.c and Extended Prefix (7.0.0.X) Link (8.0.0.X) + from ospf_ext.c. Once received, it parse TLVs and SubTLVs and store information in SRDB + (which is defined in ospf_sr.h). For each received LSA, NHLFE is computed and send to + Zebra to add/remove new MPLS labels entries and FEC. New CLI configurations are also + centralized in ospf_sr.c. This CLI will trigger the flooding os new LSA Router Information + (4.0.0.0), Extended Prefix (7.0.0.X) and Link (8.0.0.X) by ospf_ri.c, respectively ospf_ext.c. + - ospf_ri.c send back to ospf_sr.c received Router Information LSA and update self Router + Information LSA with paramters provided by ospf_sr.c i.e. SRGB and MSD. It use ospf_opaque.c + functions to send / received these Opaque LSAs. + - ospf_ext.c send bacl to ospf_sr.c received Extended Prefix and Link Opaque LSA and send + self Extended Prefix and Link Opaque LSA through ospf_opaque.c functions. + + +-----------+ +-------+ + | | | | + | ospf_sr.c +-----+ SRDB | + +-----------+ +--+ | | + | +-^-------^-+ | +-------+ + | | | | | + | | | | | + | | | | +--------+ + | | | | | + +---v----------+ | | | +-----v-------+ + | | | | | | | + | ospf_ri.c +--+ | +-------+ ospf_ext.c | + | LSA 4.0.0.0 | | | LSA 7.0.0.X | + | | | | LSA 8.0.0.X | + +---^----------+ | | | + | | +-----^-------+ + | | | + | | | + | +--------v------------+ | + | | | | + | | ZEBRA: Labels + FEC | | + | | | | + | +---------------------+ | + | | + | | + | +---------------+ | + | | | | + +---------> ospf_opaque.c <---------+ + | | + +---------------+ + + +Known limitations +----------------- + + - Only single Area is supported. ABR is not yet supported + - Only SPF algorithm is supported + - Extended Prefix Range is not supported + diff --git a/doc/ospfd.texi b/doc/ospfd.texi index cc33211510..c013412b94 100644 --- a/doc/ospfd.texi +++ b/doc/ospfd.texi @@ -22,6 +22,7 @@ networks. * Opaque LSA:: * OSPF Traffic Engineering:: * Router Information:: +* Segment Routing:: * Debugging OSPF:: * OSPF Configuration Examples:: @end menu @@ -724,6 +725,44 @@ Show Router Capabilities flag. Show Router Capabilities PCE parameters. @end deffn +@node Segment Routing +@section Segment Routing + +This is an EXPERIMENTAL support of Segment Routing as per draft + draft-ietf-ospf-segment-routing-extensions-24i for MPLS dataplane. + +@deffn {OSPF Command} {segment-routing on} {} +@deffnx {OSPF Command} {no segment-routing} {} +Enable Segment Routing. Even if this also activate routing information support, +it is preferable to also activate routing information, and set accordingly the +Area or AS flooding. +@end deffn + +@deffn {OSPF Command} {segment-routing global-block (0-1048575) (0-1048575)} {} +@deffnx {OSPF Command} {no segment-routing global-block} {} +Fix the Segment Routing Global Block i.e. the label range used by MPLS to store +label in the MPLS FIB. +@end deffn + +@deffn {OSPF Command} {segment-routing node-msd (1-16)} {} +@deffnx {OSPF Command} {no segment-routing node-msd} {} +Fix the Maximum Stack Depth supported by the router. The value depend of the +MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32. +@end deffn + +@deffn {OSPF Command} {segment-routing prefix A.B.C.D/M index (0-65535)} {} +@deffnx {OSPF Command} {no segment-routing prefix A.B.C.D/M} {} +Set the Segment Rounting index for the specifyed prefix. Note +that, only prefix with /32 corresponding to a loopback interface are +currently supported. +@end deffn + +@deffn {Command} {show ip ospf database segment-routing} {} +@deffnx {Command} {show ip ospf database segment-routing adv-router @var{adv-router}} {} +@deffnx {Command} {show ip ospf database segment-routing self-originate} {} +Show Segment Routing Data Base, all SR nodes, specific advertized router or self router. +@end deffn + @node Debugging OSPF @section Debugging OSPF diff --git a/lib/command.h b/lib/command.h index fa8323bf2d..c998bbe1f6 100644 --- a/lib/command.h +++ b/lib/command.h @@ -358,6 +358,7 @@ struct cmd_node { #define OSPF_RI_STR "OSPF Router Information specific commands\n" #define PCE_STR "PCE Router Information specific commands\n" #define MPLS_STR "MPLS information\n" +#define SR_STR "Segment-Routing specific commands\n" #define WATCHFRR_STR "watchfrr information\n" #define ZEBRA_STR "Zebra information\n" diff --git a/lib/mpls.h b/lib/mpls.h index bf98eecd81..9e7eeed34e 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -41,8 +41,10 @@ #define MPLS_MAX_UNRESERVED_LABEL 1048575 /* Default min and max SRGB label range */ -#define MPLS_DEFAULT_MIN_SRGB_LABEL 16000 -#define MPLS_DEFAULT_MAX_SRGB_LABEL 23999 +#define MPLS_DEFAULT_MIN_SRGB_LABEL 10000 +#define MPLS_DEFAULT_MAX_SRGB_LABEL 50000 +#define MPLS_DEFAULT_MIN_SRGB_SIZE 5000 +#define MPLS_DEFAULT_MAX_SRGB_SIZE 20000 /* Maximum # labels that can be pushed. */ #define MPLS_MAX_LABELS 16 @@ -94,7 +96,8 @@ enum lsp_types_t { ZEBRA_LSP_NONE = 0, /* No LSP. */ ZEBRA_LSP_STATIC = 1, /* Static LSP. */ ZEBRA_LSP_LDP = 2, /* LDP LSP. */ - ZEBRA_LSP_BGP = 3 /* BGP LSP. */ + ZEBRA_LSP_BGP = 3, /* BGP LSP. */ + ZEBRA_LSP_SR = 4 /* Segment Routing LSP. */ }; /* Functions for basic label operations. */ diff --git a/lib/route_types.txt b/lib/route_types.txt index 4e764a14c1..a72ee507b1 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -78,6 +78,7 @@ ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct" ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP" +ZEBRA_ROUTE_OSPF_SR, ospf-sr, ospfd, 's', 1, 0, "OSPF-SR" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-" @@ -103,3 +104,4 @@ ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)" ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" +ZEBRA_ROUTE_OSPF_SR, "OSPF Segment Routing (OSPF-SR)" 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 + * Author: Olivier Dugeon + * + * 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 +#include +#include +#include + +#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 + * Author: Olivier Dugeon + * + * 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 + * 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.enabled) + return; - if (OspfRI.pce_info.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 (pce->pce_address.header.type != 0) - vty_out(vty, " pce address %s\n", - inet_ntoa(pce->pce_address.address.value)); + if (OspfRI.pce_info.enabled) { - if (pce->pce_cap_flag.header.type != 0) - vty_out(vty, " pce flag 0x%x\n", - ntohl(pce->pce_cap_flag.value)); + if (pce->pce_address.header.type != 0) + vty_out(vty, " pce address %s\n", + inet_ntoa(pce->pce_address.address.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 (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)); - } - } - } - - if (pce->pce_scope.header.type != 0) - vty_out(vty, " pce scope 0x%x\n", - ntohl(OspfRI.pce_info.pce_scope.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)); } 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 + * 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->||<-- 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 + * Author: Olivier Dugeon + * + * 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 +#include +#include +#include + +#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 + * Author: Anselme Sawadogo + * + * 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 \ diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 3ddb6aba54..c9b6f50160 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -75,6 +75,7 @@ vtysh_scan += $(top_srcdir)/ospfd/ospf_opaque.c vtysh_scan += $(top_srcdir)/ospfd/ospf_ri.c vtysh_scan += $(top_srcdir)/ospfd/ospf_routemap.c vtysh_scan += $(top_srcdir)/ospfd/ospf_te.c +vtysh_scan += $(top_srcdir)/ospfd/ospf_sr.c vtysh_scan += $(top_srcdir)/ospfd/ospf_vty.c endif diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 1be2cbcaf5..d794c4c64d 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -102,6 +102,7 @@ static const struct message rtproto_str[] = { {RTPROT_MROUTED, "mroute"}, {RTPROT_BGP, "BGP"}, {RTPROT_OSPF, "OSPF"}, + {RTPROT_OSPF_SR, "OSPF-SR"}, {RTPROT_ISIS, "IS-IS"}, {RTPROT_RIP, "RIP"}, {RTPROT_RIPNG, "RIPNG"}, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a77814668d..bb034b8939 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -98,7 +98,8 @@ static inline int is_selfroute(int proto) || (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) - || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)) { + || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) + || (proto == RTPROT_OSPF_SR)) { return 1; } @@ -118,6 +119,9 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_OSPF6: proto = RTPROT_OSPF; break; + case ZEBRA_ROUTE_OSPF_SR: + proto = RTPROT_OSPF_SR; + break; case ZEBRA_ROUTE_STATIC: proto = RTPROT_STATIC; break; diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 51350fd6fb..79d5e14a62 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -52,6 +52,7 @@ #define RTPROT_EIGRP 192 #define RTPROT_LDP 193 #define RTPROT_SHARP 194 +#define RTPROT_OSPF_SR 195 void rt_netlink_init(void); diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 22c771c348..93e1162b6f 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -316,6 +316,12 @@ void mpls_ldp_lsp_uninstall_all(struct hash_backet *backet, void *ctxt); */ void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi); +/* + * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry. + * If no other NHLFEs exist, the entry would be deleted. + */ +void mpls_sr_lsp_uninstall_all(struct hash_backet *backet, void *ctxt); + #if defined(HAVE_CUMULUS) /* * Check that the label values used in LSP creation are consistent. The @@ -448,6 +454,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type) return ZEBRA_ROUTE_LDP; case ZEBRA_LSP_BGP: return ZEBRA_ROUTE_BGP; + case ZEBRA_LSP_SR: + return ZEBRA_ROUTE_OSPF_SR; case ZEBRA_LSP_NONE: default: return ZEBRA_ROUTE_KERNEL; @@ -464,6 +472,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type) return "LDP"; case ZEBRA_LSP_BGP: return "BGP"; + case ZEBRA_LSP_SR: + return "SR"; default: return "Unknown"; }