ospfd: Add support Router Capabilities support to OSPF
This is an implementation of RFC4970 (Router Information) and
RFC5088 (PCE Capabilities announcement)
* ospfd/Makefile.am: Add new file ospf_ri.c and ospf_ri.h
* ospfd/ospf_opaque.c: Add new Router Capabilities code point
* ospfd/ospf_ri.[c,h]: Implementation of RFC4970 & RFC5088
Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
diff --git a/ospfd/Makefile.am b/ospfd/Makefile.am
index f21f507..f586d73 100644
--- a/ospfd/Makefile.am
+++ b/ospfd/Makefile.am
@@ -16,7 +16,7 @@
ospf_nsm.c ospf_dump.c ospf_network.c ospf_packet.c ospf_lsa.c \
ospf_spf.c ospf_route.c ospf_ase.c ospf_abr.c ospf_ia.c ospf_flood.c \
ospf_lsdb.c ospf_asbr.c ospf_routemap.c ospf_snmp.c \
- ospf_opaque.c ospf_te.c ospf_vty.c ospf_api.c ospf_apiserver.c
+ ospf_opaque.c ospf_te.c ospf_ri.c ospf_vty.c ospf_api.c ospf_apiserver.c
ospfdheaderdir = $(pkgincludedir)/ospfd
@@ -27,11 +27,11 @@
noinst_HEADERS = \
ospf_interface.h ospf_neighbor.h ospf_network.h ospf_packet.h \
ospf_zebra.h ospf_spf.h ospf_route.h ospf_ase.h ospf_abr.h ospf_ia.h \
- ospf_flood.h ospf_snmp.h ospf_te.h ospf_vty.h ospf_apiserver.h
+ ospf_flood.h ospf_snmp.h ospf_te.h ospf_ri.h ospf_vty.h ospf_apiserver.h
ospfd_SOURCES = ospf_main.c
-ospfd_LDADD = libospf.la ../lib/libzebra.la @LIBCAP@
+ospfd_LDADD = libospf.la ../lib/libzebra.la @LIBCAP@ @LIBM@
EXTRA_DIST = OSPF-MIB.txt OSPF-TRAP-MIB.txt ChangeLog.opaque.txt
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 6b069c8..39465c1 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -62,6 +62,7 @@
*------------------------------------------------------------------------*/
#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_ri.h"
#ifdef SUPPORT_OSPF_API
int ospf_apiserver_init (void);
@@ -87,6 +88,9 @@
if (ospf_mpls_te_init () != 0)
exit (1);
+ if (ospf_router_info_init () != 0)
+ exit (1);
+
#ifdef SUPPORT_OSPF_API
if ((ospf_apiserver_enable) && (ospf_apiserver_init () != 0))
exit (1);
@@ -100,6 +104,8 @@
{
ospf_mpls_te_term ();
+ ospf_router_info_term ();
+
#ifdef SUPPORT_OSPF_API
ospf_apiserver_term ();
#endif /* SUPPORT_OSPF_API */
@@ -216,6 +222,9 @@
case OPAQUE_TYPE_INTER_AS_LSA:
name = "Inter-AS TE-v2 LSA";
break;
+ case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+ name = "Router Information LSA";
+ break;
default:
if (OPAQUE_TYPE_RANGE_UNASSIGNED (opaque_type))
name = "Unassigned";
diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c
new file mode 100644
index 0000000..5b1032b
--- /dev/null
+++ b/ospfd/ospf_ri.c
@@ -0,0 +1,1639 @@
+/*
+ * This is an implementation of RFC4970 Router Information
+ * 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/
+ *
+ * This file is part of GNU Quagga.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Quagga 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 GNU Quagga; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "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 "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_ri.h"
+#include "ospfd/ospf_te.h"
+
+struct ospf_pce_info
+{
+
+ /* Store Router Information PCE TLV and SubTLV in network byte order. */
+ struct ri_tlv_pce pce_header;
+ struct ri_pce_subtlv_address pce_address;
+ struct ri_pce_subtlv_path_scope pce_scope;
+ struct list *pce_domain;
+ struct list *pce_neighbor;
+ struct ri_pce_subtlv_cap_flag pce_cap_flag;
+};
+
+/* Following structure are internal use only. */
+struct ospf_router_info
+{
+ status_t status;
+
+ u_int8_t registered;
+ u_int8_t scope;
+
+ /* Flags to manage this router information. */
+#define RIFLG_LOOKUP_DONE 0x1
+#define RIFLG_LSA_ENGAGED 0x2
+#define RIFLG_LSA_FORCED_REFRESH 0x4
+ u_int32_t flags;
+
+ /* area pointer if flooding is Type 10 Null if flooding is AS scope */
+ struct ospf_area *area;
+ struct in_addr area_id;
+
+ /* Store Router Information Capabilities LSA */
+ struct ri_tlv_router_cap router_cap;
+
+ /* Store PCE capability LSA */
+ struct ospf_pce_info pce_info;
+};
+
+/*
+ * Global variable to manage Opaque-LSA/Router Information on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_router_info OspfRI;
+
+/*------------------------------------------------------------------------------*
+ * Followings are initialize/terminate functions for Router Information handling.
+ *------------------------------------------------------------------------------*/
+
+static void ospf_router_info_ism_change (struct ospf_interface *oi,
+ int old_status);
+static void ospf_router_info_nsm_change (struct ospf_neighbor *nbr,
+ int old_status);
+static void ospf_router_info_config_write_router (struct vty *vty);
+static void ospf_router_info_show_info (struct vty *vty,
+ struct ospf_lsa *lsa);
+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 (opcode_t opcode);
+static void ospf_router_info_register_vty (void);
+static void del_pce_info (void *val);
+
+int
+ospf_router_info_init (void)
+{
+
+ memset (&OspfRI, 0, sizeof (struct ospf_router_info));
+ OspfRI.status = disabled;
+ OspfRI.registered = 0;
+ OspfRI.scope = OSPF_OPAQUE_AS_LSA;
+ OspfRI.flags = 0;
+
+ /* Initialize pce domain and neighbor list */
+ OspfRI.pce_info.pce_domain = list_new ();
+ OspfRI.pce_info.pce_domain->del = del_pce_info;
+ OspfRI.pce_info.pce_neighbor = list_new ();
+ OspfRI.pce_info.pce_neighbor->del = del_pce_info;
+
+ ospf_router_info_register_vty ();
+
+ return 0;
+}
+
+static int
+ospf_router_info_register (u_int8_t scope)
+{
+ int rc = 0;
+
+ if (OspfRI.registered)
+ return 0;
+
+ zlog_info ("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_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 */
+
+ if (rc != 0)
+ {
+ zlog_warn ("ospf_router_info_init: Failed to register functions");
+ return rc;
+ }
+
+ OspfRI.registered = 1;
+ OspfRI.scope = scope;
+ return 0;
+}
+
+static int
+ospf_router_info_unregister ()
+{
+
+ if ((OspfRI.scope != OSPF_OPAQUE_AS_LSA)
+ && (OspfRI.scope != OSPF_OPAQUE_AREA_LSA))
+ {
+ zlog_warn ("Unable to unregister Router Info functions: Wrong scope!");
+ return -1;
+ }
+
+ ospf_delete_opaque_functab (OspfRI.scope,
+ OPAQUE_TYPE_ROUTER_INFORMATION_LSA);
+
+ OspfRI.registered = 0;
+ return 0;
+
+}
+
+void
+ospf_router_info_term (void)
+{
+
+ list_delete (OspfRI.pce_info.pce_domain);
+ list_delete (OspfRI.pce_info.pce_neighbor);
+
+ OspfRI.pce_info.pce_domain = NULL;
+ OspfRI.pce_info.pce_neighbor = NULL;
+ OspfRI.status = disabled;
+
+ ospf_router_info_unregister (OspfRI.scope);
+
+ return;
+}
+
+static void
+del_pce_info (void *val)
+{
+ XFREE (MTYPE_OSPF_PCE_PARAMS, val);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are control functions for ROUTER INFORMATION parameters management.
+ *------------------------------------------------------------------------*/
+
+static void
+set_router_info_capabilities (struct ri_tlv_router_cap *ric, u_int32_t cap)
+{
+ ric->header.type = htons (RI_TLV_CAPABILITIES);
+ ric->header.length = htons (RI_TLV_LENGTH);
+ ric->value = htonl (cap);
+ return;
+}
+
+static int
+set_pce_header (struct ospf_pce_info *pce)
+{
+ u_int16_t length = 0;
+ struct listnode *node;
+ struct ri_pce_subtlv_domain *domain;
+ struct ri_pce_subtlv_neighbor *neighbor;
+
+ /* PCE Address */
+ if (ntohs (pce->pce_address.header.type) != 0)
+ length += RI_TLV_SIZE (&pce->pce_address.header);
+
+ /* PCE Path Scope */
+ if (ntohs (pce->pce_scope.header.type) != 0)
+ length += RI_TLV_SIZE (&pce->pce_scope.header);
+
+ /* PCE Domain */
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+ {
+ if (ntohs (domain->header.type) != 0)
+ length += RI_TLV_SIZE (&domain->header);
+ }
+
+ /* PCE Neighbor */
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+ {
+ if (ntohs (neighbor->header.type) != 0)
+ length += RI_TLV_SIZE (&neighbor->header);
+ }
+
+ /* PCE Capabilities */
+ if (ntohs (pce->pce_cap_flag.header.type) != 0)
+ length += RI_TLV_SIZE (&pce->pce_cap_flag.header);
+
+ if (length != 0)
+ {
+ pce->pce_header.header.type = htons (RI_TLV_PCE);
+ pce->pce_header.header.length = htons (length);
+ }
+ else
+ {
+ pce->pce_header.header.type = 0;
+ pce->pce_header.header.length = 0;
+ }
+
+ return length;
+}
+
+static void
+set_pce_address (struct in_addr ipv4, struct ospf_pce_info *pce)
+{
+
+ /* Enable PCE Info */
+ pce->pce_header.header.type = htons (RI_TLV_PCE);
+ /* Set PCE Address */
+ pce->pce_address.header.type = htons (RI_PCE_SUBTLV_ADDRESS);
+ pce->pce_address.header.length = htons (PCE_ADDRESS_LENGTH_IPV4);
+ pce->pce_address.address.type = htons (PCE_ADDRESS_TYPE_IPV4);
+ pce->pce_address.address.value = ipv4;
+
+ return;
+}
+
+static void
+set_pce_path_scope (u_int32_t scope, struct ospf_pce_info *pce)
+{
+
+ /* Enable PCE Info */
+ pce->pce_header.header.type = htons (RI_TLV_PCE);
+ /* Set PCE Scope */
+ pce->pce_scope.header.type = htons (RI_PCE_SUBTLV_PATH_SCOPE);
+ pce->pce_scope.header.length = htons (RI_TLV_LENGTH);
+ pce->pce_scope.value = htonl (scope);
+
+ return;
+}
+
+static void
+set_pce_domain (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce)
+{
+
+ struct ri_pce_subtlv_domain *new;
+
+ /* Enable PCE Info */
+ pce->pce_header.header.type = htons (RI_TLV_PCE);
+
+ /* Create new domain info */
+ new =
+ XCALLOC (MTYPE_OSPF_PCE_PARAMS,
+ sizeof (struct ri_pce_subtlv_domain));
+
+ new->header.type = htons (RI_PCE_SUBTLV_DOMAIN);
+ new->header.length = htons (PCE_ADDRESS_LENGTH_IPV4);
+ new->type = htons (type);
+ new->value = htonl (domain);
+
+ /* Add new domain to the list */
+ listnode_add (pce->pce_domain, new);
+
+ return;
+}
+
+static void
+unset_pce_domain (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce)
+{
+ struct listnode *node;
+ struct ri_pce_subtlv_domain *old = NULL;
+ int found = 0;
+
+ /* Search the corresponding node */
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, old))
+ {
+ if ((old->type == htons (type)) && (old->value == htonl (domain)))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ /* if found remove it */
+ if (found)
+ {
+ listnode_delete (pce->pce_domain, old);
+
+ /* Avoid misjudgement in the next lookup. */
+ if (listcount (pce->pce_domain) == 0)
+ pce->pce_domain->head = pce->pce_domain->tail = NULL;
+
+ /* Finally free the old domain */
+ XFREE (MTYPE_OSPF_PCE_PARAMS, old);
+ }
+}
+
+static void
+set_pce_neighbor (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce)
+{
+
+ struct ri_pce_subtlv_neighbor *new;
+
+ /* Enable PCE Info */
+ pce->pce_header.header.type = htons (RI_TLV_PCE);
+
+ /* Create new neighbor info */
+ new =
+ XCALLOC (MTYPE_OSPF_PCE_PARAMS,
+ sizeof (struct ri_pce_subtlv_neighbor));
+
+ new->header.type = htons (RI_PCE_SUBTLV_NEIGHBOR);
+ new->header.length = htons (PCE_ADDRESS_LENGTH_IPV4);
+ new->type = htons (type);
+ new->value = htonl (domain);
+
+ /* Add new domain to the list */
+ listnode_add (pce->pce_neighbor, new);
+
+ return;
+}
+
+static void
+unset_pce_neighbor (u_int16_t type, u_int32_t domain,
+ struct ospf_pce_info *pce)
+{
+ struct listnode *node;
+ struct ri_pce_subtlv_neighbor *old = NULL;
+ int found = 0;
+
+ /* Search the corresponding node */
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, old))
+ {
+ if ((old->type == htons (type)) && (old->value == htonl (domain)))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ /* if found remove it */
+ if (found)
+ {
+ listnode_delete (pce->pce_neighbor, old);
+
+ /* Avoid misjudgement in the next lookup. */
+ if (listcount (pce->pce_neighbor) == 0)
+ pce->pce_neighbor->head = pce->pce_neighbor->tail = NULL;
+
+ /* Finally free the old domain */
+ XFREE (MTYPE_OSPF_PCE_PARAMS, old);
+ }
+}
+
+static void
+set_pce_cap_flag (u_int32_t cap, struct ospf_pce_info *pce)
+{
+
+ /* Enable PCE Info */
+ pce->pce_header.header.type = htons (RI_TLV_PCE);
+ /* Set PCE Capabilities flag */
+ pce->pce_cap_flag.header.type = htons (RI_PCE_SUBTLV_CAP_FLAG);
+ pce->pce_cap_flag.header.length = htons (RI_TLV_LENGTH);
+ pce->pce_cap_flag.value = htonl (cap);
+
+ return;
+}
+
+
+static void
+unset_param (struct ri_tlv_header *tlv)
+{
+
+ tlv->type = 0;
+ /* Fill the Value to 0 */
+ memset ((tlv + RI_TLV_HDR_SIZE), 0, RI_TLV_BODY_SIZE (tlv));
+ tlv->length = 0;
+
+ return;
+}
+
+static void
+initialize_params (struct ospf_router_info *ori)
+{
+ u_int32_t cap;
+ struct ospf *top;
+
+ /*
+ * Initialize default Router Information Capabilities.
+ */
+ cap = 0;
+ cap = cap | RI_TE_SUPPORT;
+
+ set_router_info_capabilities (&ori->router_cap, cap);
+
+ /* If Area address is not null and exist, retrieve corresponding structure */
+ top = ospf_lookup ();
+ zlog_info ("RI-> Initialize Router Info for %s scope within area %s",
+ OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS",
+ inet_ntoa (OspfRI.area_id));
+
+ /* Try to get the Area context at this step. Do it latter if not available */
+ if ((OspfRI.scope == OSPF_OPAQUE_AREA_LSA) && (OspfRI.area == NULL))
+ OspfRI.area = ospf_area_lookup_by_area_id (top, OspfRI.area_id);
+
+ /*
+ * Initialize default PCE Information values
+ */
+ /* PCE address == OSPF Router ID */
+ set_pce_address (top->router_id, &ori->pce_info);
+
+ /* PCE scope */
+ cap = 7; /* Set L, R and Rd bits to one = intra & inter-area path computation */
+ set_pce_path_scope (cap, &ori->pce_info);
+
+ /* PCE Capabilities */
+ cap =
+ PCE_CAP_BIDIRECTIONAL | PCE_CAP_DIVERSE_PATH | PCE_CAP_OBJECTIVES |
+ PCE_CAP_ADDITIVE | PCE_CAP_MULTIPLE_REQ;
+ set_pce_cap_flag (cap, &ori->pce_info);
+
+ /* Finally compute PCE header */
+ set_pce_header (&ori->pce_info);
+
+ return;
+}
+
+static int
+is_mandated_params_set (struct ospf_router_info ori)
+{
+ int rc = 0;
+
+ if (ntohs (ori.router_cap.header.type) == 0)
+ goto out;
+
+ if ((ntohs (ori.pce_info.pce_header.header.type) == RI_TLV_PCE)
+ && (ntohs (ori.pce_info.pce_address.header.type) == 0)
+ && (ntohs (ori.pce_info.pce_cap_flag.header.type) == 0))
+ goto out;
+
+ rc = 1;
+
+out:
+ return rc;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are callback functions against generic Opaque-LSAs handling.
+ *------------------------------------------------------------------------*/
+static void
+ospf_router_info_ism_change (struct ospf_interface *oi, int old_state)
+{
+ /* So far, nothing to do here. */
+ return;
+
+}
+
+static void
+ospf_router_info_nsm_change (struct ospf_neighbor *nbr, int old_state)
+{
+
+ /* So far, nothing to do here. */
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are OSPF protocol processing functions for ROUTER INFORMATION
+ *------------------------------------------------------------------------*/
+
+static void
+build_tlv_header (struct stream *s, struct ri_tlv_header *tlvh)
+{
+
+ stream_put (s, tlvh, sizeof (struct ri_tlv_header));
+ return;
+}
+
+static void
+build_tlv (struct stream *s, struct ri_tlv_header *tlvh)
+{
+
+ if (ntohs (tlvh->type) != 0)
+ {
+ build_tlv_header (s, tlvh);
+ stream_put (s, tlvh + 1, RI_TLV_BODY_SIZE (tlvh));
+ }
+ return;
+}
+
+static void
+ospf_router_info_lsa_body_set (struct stream *s)
+{
+
+ struct listnode *node;
+ struct ri_pce_subtlv_domain *domain;
+ struct ri_pce_subtlv_neighbor *neighbor;
+
+ /* Build Router Information TLV */
+ build_tlv (s, &OspfRI.router_cap.header);
+
+ /* Add RI PCE TLV if it is set */
+ /* Compute PCE Info header first */
+ if ((set_pce_header (&OspfRI.pce_info)) != 0)
+ {
+
+ /* Build PCE TLV */
+ build_tlv_header (s, &OspfRI.pce_info.pce_header.header);
+
+ /* Build PCE address sub-tlv */
+ build_tlv (s, &OspfRI.pce_info.pce_address.header);
+
+ /* Build PCE path scope sub-tlv */
+ build_tlv (s, &OspfRI.pce_info.pce_scope.header);
+
+ /* Build PCE domain sub-tlv */
+ for (ALL_LIST_ELEMENTS_RO (OspfRI.pce_info.pce_domain, node, domain))
+ build_tlv (s, &domain->header);
+
+ /* Build PCE neighbor sub-tlv */
+ for (ALL_LIST_ELEMENTS_RO
+ (OspfRI.pce_info.pce_neighbor, node, neighbor))
+ build_tlv (s, &neighbor->header);
+
+ /* Build PCE cap flag sub-tlv */
+ build_tlv (s, &OspfRI.pce_info.pce_cap_flag.header);
+ }
+
+ return;
+}
+
+/* Create new opaque-LSA. */
+static struct ospf_lsa *
+ospf_router_info_lsa_new ()
+{
+ struct ospf *top;
+ 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 ("ospf_router_info_lsa_new: stream_new() ?");
+ goto out;
+ }
+ lsah = (struct lsa_header *) STREAM_DATA (s);
+
+ options = OSPF_OPTION_E; /* Enable AS external as we flood RI with Opaque Type 11 */
+ options |= OSPF_OPTION_O; /* Don't forget this :-) */
+
+ lsa_type = OspfRI.scope;
+ /* LSA ID == 0 for Router Information see RFC 4970 */
+ tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0);
+ lsa_id.s_addr = htonl (tmp);
+
+ if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+ zlog_debug
+ ("LSA[Type%d:%s]: Create an Opaque-LSA/ROUTER INFORMATION instance",
+ lsa_type, inet_ntoa (lsa_id));
+
+ top = ospf_lookup ();
+
+ /* Set opaque-LSA header fields. */
+ lsa_header_set (s, options, lsa_type, lsa_id, top->router_id);
+
+ /* Set opaque-LSA body fields. */
+ ospf_router_info_lsa_body_set (s);
+
+ /* 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 ("ospf_router_info_lsa_new: ospf_lsa_new() ?");
+ stream_free (s);
+ goto out;
+ }
+ if ((new->data = ospf_lsa_data_new (length)) == NULL)
+ {
+ zlog_warn ("ospf_router_info_lsa_new: ospf_lsa_data_new() ?");
+ ospf_lsa_unlock (&new);
+ new = NULL;
+ stream_free (s);
+ goto out;
+ }
+
+ new->area = OspfRI.area; /* Area must be null if the Opaque type is AS scope, fulfill otherwise */
+
+ SET_FLAG (new->flags, OSPF_LSA_SELF);
+ memcpy (new->data, lsah, length);
+ stream_free (s);
+
+out:return new;
+}
+
+static int
+ospf_router_info_lsa_originate1 (void *arg)
+{
+ struct ospf_lsa *new;
+ struct ospf *top;
+ struct ospf_area *area;
+ int rc = -1;
+
+ /* First check if the area is known if flooding scope is Area */
+ if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA)
+ {
+ area = (struct ospf_area *) arg;
+ if (area->area_id.s_addr != OspfRI.area_id.s_addr)
+ {
+ zlog_debug
+ ("RI -> This is not the Router Information Area. Stop processing");
+ goto out;
+ }
+ OspfRI.area = area;
+ }
+
+ /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
+ if ((new = ospf_router_info_lsa_new ()) == NULL)
+ {
+ zlog_warn
+ ("ospf_router_info_lsa_originate1: ospf_router_info_lsa_new() ?");
+ goto out;
+ }
+
+ /* Get ospf info */
+ top = ospf_lookup ();
+
+ /* Install this LSA into LSDB. */
+ if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL)
+ {
+ zlog_warn ("ospf_router_info_lsa_originate1: ospf_lsa_install() ?");
+ ospf_lsa_unlock (&new);
+ goto out;
+ }
+
+ /* Now this Router Info parameter entry has associated LSA. */
+ SET_FLAG (OspfRI.flags, RIFLG_LSA_ENGAGED);
+
+ /* Update new LSA origination count. */
+ top->lsa_originate_count++;
+
+ /* Flood new LSA through AS. */
+ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+ ospf_flood_through_as (top, NULL /*nbr */ , new);
+ else
+ ospf_flood_through_area (OspfRI.area, NULL /*nbr */ , new);
+
+ if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+ {
+ zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/ROUTER INFORMATION",
+ new->data->type, inet_ntoa (new->data->id));
+ ospf_lsa_header_dump (new->data);
+ }
+
+ rc = 0;
+out:return rc;
+}
+
+static int
+ospf_router_info_lsa_originate (void *arg)
+{
+
+ int rc = -1;
+
+ if (OspfRI.status == disabled)
+ {
+ zlog_info
+ ("ospf_router_info_lsa_originate: ROUTER INFORMATION is disabled now.");
+ rc = 0; /* This is not an error case. */
+ goto out;
+ }
+
+ /* Check if Router Information LSA is already engaged */
+ if (OspfRI.flags & RIFLG_LSA_ENGAGED)
+ {
+ if (OspfRI.flags & RIFLG_LSA_FORCED_REFRESH)
+ {
+ OspfRI.flags &= ~RIFLG_LSA_FORCED_REFRESH;
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+ }
+ }
+ else
+ {
+ if (!is_mandated_params_set (OspfRI))
+ zlog_warn
+ ("ospf_router_info_lsa_originate: lacks mandated ROUTER INFORMATION parameters");
+
+ /* Ok, let's try to originate an LSA */
+ if (ospf_router_info_lsa_originate1 (arg) != 0)
+ goto out;
+ }
+
+ rc = 0;
+out:return rc;
+}
+
+static struct ospf_lsa *
+ospf_router_info_lsa_refresh (struct ospf_lsa *lsa)
+{
+ struct ospf_lsa *new = NULL;
+ struct ospf *top;
+
+ if (OspfRI.status == disabled)
+ {
+ /*
+ * This LSA must have flushed before due to ROUTER INFORMATION status change.
+ * It seems a slip among routers in the routing domain.
+ */
+ zlog_info
+ ("ospf_router_info_lsa_refresh: ROUTER INFORMATION is disabled now.");
+ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */
+ }
+
+ /* Verify that the Router Information ID is supported */
+ if (GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)) != 0)
+ {
+ zlog_warn
+ ("ospf_router_info_lsa_refresh: Unsupported Router Information ID");
+ goto out;
+ }
+
+ /* If the lsa's age reached to MaxAge, start flushing procedure. */
+ if (IS_LSA_MAXAGE (lsa))
+ {
+ OspfRI.flags &= ~RIFLG_LSA_ENGAGED;
+ ospf_opaque_lsa_flush_schedule (lsa);
+ goto out;
+ }
+
+ /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
+ if ((new = ospf_router_info_lsa_new ()) == NULL)
+ {
+ zlog_warn
+ ("ospf_router_info_lsa_refresh: ospf_router_info_lsa_new() ?");
+ goto out;
+ }
+ new->data->ls_seqnum = lsa_seqnum_increment (lsa);
+
+ /* Install this LSA into LSDB. */
+ /* Given "lsa" will be freed in the next function. */
+ top = ospf_lookup ();
+ if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL)
+ {
+ zlog_warn ("ospf_router_info_lsa_refresh: ospf_lsa_install() ?");
+ ospf_lsa_unlock (&new);
+ goto out;
+ }
+
+ /* Flood updated LSA through AS or AREA depending of OspfRI.scope. */
+ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+ ospf_flood_through_as (top, NULL /*nbr */ , new);
+ else
+ ospf_flood_through_area (OspfRI.area, NULL /*nbr */ , new);
+
+ /* Debug logging. */
+ if (IS_DEBUG_OSPF (lsa, LSA_GENERATE))
+ {
+ zlog_debug ("LSA[Type%d:%s]: Refresh Opaque-LSA/ROUTER INFORMATION",
+ new->data->type, inet_ntoa (new->data->id));
+ ospf_lsa_header_dump (new->data);
+ }
+
+out:return new;
+}
+
+static void
+ospf_router_info_lsa_schedule (opcode_t 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));
+
+ zlog_debug ("RI-> LSA schedule %s%s%s",
+ opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+ opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+ opcode == FLUSH_THIS_LSA ? "Flush" : "");
+
+ top = ospf_lookup ();
+ if ((OspfRI.scope == OSPF_OPAQUE_AREA_LSA) && (OspfRI.area == NULL))
+ {
+ zlog_warn
+ ("ospf_router_info_lsa_schedule(): Router Info is Area scope flooding but area is not set");
+ OspfRI.area = ospf_area_lookup_by_area_id (top, OspfRI.area_id);
+ }
+ lsa.area = OspfRI.area;
+ lsa.data = &lsah;
+ lsah.type = OspfRI.scope;
+
+ /* LSA ID is set to 0 for the Router Information. See RFC 4970 */
+ tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0);
+ lsah.id.s_addr = htonl (tmp);
+
+ switch (opcode)
+ {
+ case REORIGINATE_THIS_LSA:
+ if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA)
+ ospf_opaque_lsa_reoriginate_schedule ((void *) OspfRI.area,
+ OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_ROUTER_INFORMATION_LSA);
+ else
+ ospf_opaque_lsa_reoriginate_schedule ((void *) top,
+ OSPF_OPAQUE_AS_LSA,
+ OPAQUE_TYPE_ROUTER_INFORMATION_LSA);
+ break;
+ case REFRESH_THIS_LSA:
+ ospf_opaque_lsa_refresh_schedule (&lsa);
+ break;
+ case FLUSH_THIS_LSA:
+ OspfRI.flags &= ~RIFLG_LSA_ENGAGED;
+ ospf_opaque_lsa_flush_schedule (&lsa);
+ break;
+ default:
+ zlog_warn ("ospf_router_info_lsa_schedule: Unknown opcode (%u)",
+ opcode);
+ break;
+ }
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty session control functions.
+ *------------------------------------------------------------------------*/
+
+static u_int16_t
+show_vty_router_cap (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+ struct ri_tlv_router_cap *top = (struct ri_tlv_router_cap *) tlvh;
+
+ if (vty != NULL)
+ vty_out (vty, " Router Capabilities: 0x%x%s", ntohl (top->value),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" Router Capabilities: 0x%x", ntohl (top->value));
+
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_address (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+ struct ri_pce_subtlv_address *top = (struct ri_pce_subtlv_address *) tlvh;
+
+ if (ntohs (top->address.type) == PCE_ADDRESS_TYPE_IPV4)
+ {
+ if (vty != NULL)
+ vty_out (vty, " PCE Address: %s%s", inet_ntoa (top->address.value),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE Address: %s", inet_ntoa (top->address.value));
+ }
+ else
+ {
+ /* TODO: Add support to IPv6 with inet_ntop() */
+ if (vty != NULL)
+ vty_out (vty, " PCE Address: 0x%x%s",
+ ntohl (top->address.value.s_addr), VTY_NEWLINE);
+ else
+ zlog_debug (" PCE Address: 0x%x",
+ ntohl (top->address.value.s_addr));
+ }
+
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_path_scope (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+ struct ri_pce_subtlv_path_scope *top =
+ (struct ri_pce_subtlv_path_scope *) tlvh;
+
+ if (vty != NULL)
+ vty_out (vty, " PCE Path Scope: 0x%x%s", ntohl (top->value),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE Path Scope: 0x%x", ntohl (top->value));
+
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_domain (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+ struct ri_pce_subtlv_domain *top = (struct ri_pce_subtlv_domain *) tlvh;
+ struct in_addr tmp;
+
+ if (ntohs (top->type) == PCE_DOMAIN_TYPE_AREA)
+ {
+ tmp.s_addr = top->value;
+ if (vty != NULL)
+ vty_out (vty, " PCE domain Area: %s%s", inet_ntoa (tmp),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE domain Area: %s", inet_ntoa (tmp));
+ }
+ else
+ {
+ if (vty != NULL)
+ vty_out (vty, " PCE domain AS: %d%s", ntohl (top->value),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE domain AS: %d", ntohl (top->value));
+ }
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_neighbor (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+
+ struct ri_pce_subtlv_neighbor *top = (struct ri_pce_subtlv_neighbor *) tlvh;
+ struct in_addr tmp;
+
+ if (ntohs (top->type) == PCE_DOMAIN_TYPE_AREA)
+ {
+ tmp.s_addr = top->value;
+ if (vty != NULL)
+ vty_out (vty, " PCE neighbor Area: %s%s", inet_ntoa (tmp),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE neighbor Area: %s", inet_ntoa (tmp));
+ }
+ else
+ {
+ if (vty != NULL)
+ vty_out (vty, " PCE neighbor AS: %d%s", ntohl (top->value),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE neighbor AS: %d", ntohl (top->value));
+ }
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_subtlv_cap_flag (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+ struct ri_pce_subtlv_cap_flag *top = (struct ri_pce_subtlv_cap_flag *) tlvh;
+
+ if (vty != NULL)
+ vty_out (vty, " PCE Capabilities Flag: 0x%x%s", ntohl (top->value),
+ VTY_NEWLINE);
+ else
+ zlog_debug (" PCE Capabilities Flag: 0x%x", ntohl (top->value));
+
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_unknown_tlv (struct vty *vty, struct ri_tlv_header *tlvh)
+{
+ if (vty != NULL)
+ vty_out (vty, " Unknown TLV: [type(0x%x), length(0x%x)]%s",
+ ntohs (tlvh->type), ntohs (tlvh->length), VTY_NEWLINE);
+ else
+ zlog_debug (" Unknown TLV: [type(0x%x), length(0x%x)]",
+ ntohs (tlvh->type), ntohs (tlvh->length));
+
+ return RI_TLV_SIZE (tlvh);
+}
+
+static u_int16_t
+show_vty_pce_info (struct vty *vty, struct ri_tlv_header *ri, uint32_t total)
+{
+ struct ri_tlv_header *tlvh;
+ u_int16_t sum = 0;
+
+ for (tlvh = ri; sum < total; tlvh = RI_TLV_HDR_NEXT (tlvh))
+ {
+ switch (ntohs (tlvh->type))
+ {
+ case RI_PCE_SUBTLV_ADDRESS:
+ sum += show_vty_pce_subtlv_address (vty, tlvh);
+ break;
+ case RI_PCE_SUBTLV_PATH_SCOPE:
+ sum += show_vty_pce_subtlv_path_scope (vty, tlvh);
+ break;
+ case RI_PCE_SUBTLV_DOMAIN:
+ sum += show_vty_pce_subtlv_domain (vty, tlvh);
+ break;
+ case RI_PCE_SUBTLV_NEIGHBOR:
+ sum += show_vty_pce_subtlv_neighbor (vty, tlvh);
+ break;
+ case RI_PCE_SUBTLV_CAP_FLAG:
+ sum += show_vty_pce_subtlv_cap_flag (vty, tlvh);
+ break;
+ default:
+ sum += show_vty_unknown_tlv (vty, tlvh);
+ break;
+ }
+ }
+ return sum;
+}
+
+static void
+ospf_router_info_show_info (struct vty *vty, struct ospf_lsa *lsa)
+{
+ struct lsa_header *lsah = (struct lsa_header *) lsa->data;
+ struct ri_tlv_header *tlvh;
+ u_int16_t length = 0, sum = 0;
+
+ /* Initialize TLV browsing */
+ length = ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+ for (tlvh = RI_TLV_HDR_TOP (lsah); sum < length;
+ tlvh = RI_TLV_HDR_NEXT (tlvh))
+ {
+ switch (ntohs (tlvh->type))
+ {
+ case RI_TLV_CAPABILITIES:
+ sum += show_vty_router_cap (vty, tlvh);
+ break;
+ case RI_TLV_PCE:
+ tlvh++;
+ sum += RI_TLV_HDR_SIZE;
+ sum += show_vty_pce_info (vty, tlvh, length - sum);
+ break;
+ default:
+ sum += show_vty_unknown_tlv (vty, tlvh);
+ break;
+ }
+ }
+
+ return;
+}
+
+static void
+ospf_router_info_config_write_router (struct vty *vty)
+{
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+ struct listnode *node;
+ struct ri_pce_subtlv_domain *domain;
+ struct ri_pce_subtlv_neighbor *neighbor;
+ struct in_addr tmp;
+
+ if (OspfRI.status == enabled)
+ {
+ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+ vty_out (vty, " router-info as%s", VTY_NEWLINE);
+ else
+ vty_out (vty, " router-info area %s%s", inet_ntoa (OspfRI.area_id),
+ VTY_NEWLINE);
+
+ if (pce->pce_address.header.type != 0)
+ vty_out (vty, " pce address %s%s",
+ inet_ntoa (pce->pce_address.address.value), VTY_NEWLINE);
+
+ if (pce->pce_cap_flag.header.type != 0)
+ vty_out (vty, " pce flag 0x%x%s", ntohl (pce->pce_cap_flag.value),
+ VTY_NEWLINE);
+
+ 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%s", inet_ntoa (tmp),
+ VTY_NEWLINE);
+ }
+ else
+ {
+ vty_out (vty, " pce domain as %d%s", ntohl (domain->value),
+ VTY_NEWLINE);
+ }
+ }
+ }
+
+ 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%s", inet_ntoa (tmp),
+ VTY_NEWLINE);
+ }
+ else
+ {
+ vty_out (vty, " pce neighbor as %d%s",
+ ntohl (neighbor->value), VTY_NEWLINE);
+ }
+ }
+ }
+
+ if (pce->pce_scope.header.type != 0)
+ vty_out (vty, " pce scope 0x%x%s",
+ ntohl (OspfRI.pce_info.pce_scope.value), VTY_NEWLINE);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * Followings are vty command functions.
+ *------------------------------------------------------------------------*/
+
+DEFUN (router_info,
+ router_info_area_cmd,
+ "router-info area A.B.C.D",
+ OSPF_RI_STR
+ "Enable the Router Information functionality with Area flooding scope\n"
+ "OSPF area ID in IP format")
+{
+
+ u_int8_t scope;
+
+ if (OspfRI.status == enabled)
+ return CMD_SUCCESS;
+
+ /* Check and get Area value if present */
+ if (argc == 1)
+ {
+ if (!inet_aton (argv[0], &OspfRI.area_id))
+ {
+ vty_out (vty, "Please specify Router Info Area by A.B.C.D%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ scope = OSPF_OPAQUE_AREA_LSA;
+ }
+ else
+ {
+ OspfRI.area_id.s_addr = 0;
+ scope = OSPF_OPAQUE_AS_LSA;
+ }
+
+ /* First start to register Router Information callbacks */
+ if ((ospf_router_info_register (scope)) != 0)
+ {
+ zlog_warn ("Enable to register Router Information callbacks. Abort!");
+ return CMD_WARNING;
+ }
+
+ OspfRI.status = enabled;
+
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug ("RI-> Router Information (%s flooding): OFF -> ON",
+ OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS");
+
+ /*
+ * Following code is intended to handle two cases;
+ *
+ * 1) Router Information was disabled at startup time, but now become enabled.
+ * 2) Router Information was once enabled then disabled, and now enabled again.
+ */
+
+ initialize_params (&OspfRI);
+
+ /* Refresh RI LSA if already engaged */
+ if (OspfRI.flags & RIFLG_LSA_ENGAGED)
+ {
+ zlog_debug ("RI-> Initial origination following configuration");
+ ospf_router_info_lsa_schedule (REORIGINATE_THIS_LSA);
+ }
+ return CMD_SUCCESS;
+
+}
+
+ALIAS (router_info,
+ router_info_as_cmd,
+ "router-info as",
+ OSPF_RI_STR
+ "Enable the Router Information functionality with AS flooding scope\n")
+
+DEFUN (no_router_info,
+ no_router_info_cmd,
+ "no router-info",
+ NO_STR
+ "Disable the Router Information functionality\n")
+{
+
+ if (OspfRI.status == disabled)
+ return CMD_SUCCESS;
+
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug ("RI-> Router Information: ON -> OFF");
+
+ if (OspfRI.flags & RIFLG_LSA_ENGAGED)
+ ospf_router_info_lsa_schedule (FLUSH_THIS_LSA);
+
+ /* Unregister the callbacks */
+ ospf_router_info_unregister ();
+
+ OspfRI.status = disabled;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pce_address,
+ pce_address_cmd,
+ "pce address A.B.C.D",
+ PCE_STR
+ "Stable IP address of the PCE\n"
+ "PCE address in IPv4 address format\n")
+{
+ struct in_addr value;
+ struct ospf_pce_info *pi = &OspfRI.pce_info;
+
+ if (!inet_aton (argv[0], &value))
+ {
+ vty_out (vty, "Please specify PCE Address by A.B.C.D%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (ntohs (pi->pce_address.header.type) == 0
+ || ntohl (pi->pce_address.address.value.s_addr) != ntohl (value.s_addr))
+ {
+
+ set_pce_address (value, pi);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_address,
+ no_pce_address_cmd,
+ "no pce address",
+ NO_STR
+ PCE_STR
+ "Disable PCE address\n")
+{
+
+ unset_param (&OspfRI.pce_info.pce_address.header);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pce_path_scope,
+ pce_path_scope_cmd,
+ "pce scope BITPATTERN",
+ PCE_STR
+ "Path scope visibilities of the PCE for path computation\n"
+ "32-bit Hexadecimal value\n")
+{
+ uint32_t scope;
+ struct ospf_pce_info *pi = &OspfRI.pce_info;
+
+ if (sscanf (argv[0], "0x%x", &scope) != 1)
+ {
+ vty_out (vty, "pce_path_scope: fscanf: %s%s", safe_strerror (errno),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (ntohl (pi->pce_scope.header.type) == 0 || scope != pi->pce_scope.value)
+ {
+ set_pce_path_scope (scope, pi);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_path_scope,
+ no_pce_path_scope_cmd,
+ "no pce scope",
+ NO_STR
+ PCE_STR
+ "Disable PCE path scope\n")
+{
+
+ unset_param (&OspfRI.pce_info.pce_address.header);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pce_domain,
+ pce_domain_cmd,
+ "pce domain as <0-65535>",
+ PCE_STR
+ "Configure PCE domain AS number\n"
+ "AS number where the PCE as visibilities for path computation\n"
+ "AS number in decimal <0-65535>\n")
+{
+
+ uint32_t as;
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+ struct listnode *node;
+ struct ri_pce_subtlv_domain *domain;
+
+ if (sscanf (argv[0], "%d", &as) != 1)
+ {
+ vty_out (vty, "pce_domain: fscanf: %s%s", safe_strerror (errno),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Check if the domain is not already in the domain list */
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+ {
+ if (ntohl (domain->header.type) == 0 && as == domain->value)
+ goto out;
+ }
+
+ /* Create new domain if not found */
+ set_pce_domain (PCE_DOMAIN_TYPE_AS, as, pce);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+out:return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_domain,
+ no_pce_domain_cmd,
+ "no pce domain as <0-65535>",
+ NO_STR
+ PCE_STR
+ "Disable PCE domain AS number\n"
+ "AS number where the PCE as visibilities for path computation\n"
+ "AS number in decimal <0-65535>\n")
+{
+
+ uint32_t as;
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+
+ if (sscanf (argv[0], "%d", &as) != 1)
+ {
+ vty_out (vty, "no_pce_domain: fscanf: %s%s", safe_strerror (errno),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Unset corresponding PCE domain */
+ unset_pce_domain (PCE_DOMAIN_TYPE_AS, as, pce);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pce_neigbhor,
+ pce_neighbor_cmd,
+ "pce neighbor as <0-65535>",
+ PCE_STR
+ "Configure PCE neighbor domain AS number\n"
+ "AS number of PCE neighbors\n"
+ "AS number in decimal <0-65535>\n")
+{
+
+ uint32_t as;
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+ struct listnode *node;
+ struct ri_pce_subtlv_neighbor *neighbor;
+
+ if (sscanf (argv[0], "%d", &as) != 1)
+ {
+ vty_out (vty, "pce_neighbor: fscanf: %s%s", safe_strerror (errno),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Check if the domain is not already in the domain list */
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+ {
+ if (ntohl (neighbor->header.type) == 0 && as == neighbor->value)
+ goto out;
+ }
+
+ /* Create new domain if not found */
+ set_pce_neighbor (PCE_DOMAIN_TYPE_AS, as, pce);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+out:return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_neighbor,
+ no_pce_neighbor_cmd,
+ "no pce neighbor as <0-65535>",
+ NO_STR
+ PCE_STR
+ "Disable PCE neighbor AS number\n"
+ "AS number of PCE neighbor\n"
+ "AS number in decimal <0-65535>\n")
+{
+
+ uint32_t as;
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+
+ if (sscanf (argv[0], "%d", &as) != 1)
+ {
+ vty_out (vty, "no_pce_neighbor: fscanf: %s%s", safe_strerror (errno),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Unset corresponding PCE domain */
+ unset_pce_neighbor (PCE_DOMAIN_TYPE_AS, as, pce);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (pce_cap_flag,
+ pce_cap_flag_cmd,
+ "pce flag BITPATTERN",
+ PCE_STR
+ "Capabilities of the PCE for path computation\n"
+ "32-bit Hexadecimal value\n")
+{
+
+ uint32_t cap;
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+
+ if (sscanf (argv[0], "0x%x", &cap) != 1)
+ {
+ vty_out (vty, "pce_cap_flag: fscanf: %s%s", safe_strerror (errno),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (ntohl (pce->pce_cap_flag.header.type) == 0
+ || cap != pce->pce_cap_flag.value)
+ {
+ set_pce_cap_flag (cap, pce);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_pce_cap_flag,
+ no_pce_cap_flag_cmd,
+ "no pce flag",
+ NO_STR
+ PCE_STR
+ "Disable PCE capabilities\n")
+{
+
+ unset_param (&OspfRI.pce_info.pce_cap_flag.header);
+
+ /* Refresh RI LSA if already engaged */
+ if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule (REFRESH_THIS_LSA);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_router_info,
+ show_ip_ospf_router_info_cmd,
+ "show ip ospf router-info",
+ SHOW_STR
+ IP_STR
+ OSPF_STR
+ "Router Information\n")
+{
+
+ if (OspfRI.status == enabled)
+ {
+ vty_out (vty, "--- Router Information parameters ---%s", VTY_NEWLINE);
+ show_vty_router_cap (vty, &OspfRI.router_cap.header);
+ }
+ else
+ {
+ if (vty != NULL)
+ vty_out (vty, " Router Information is disabled on this router%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_opsf_router_info_pce,
+ show_ip_ospf_router_info_pce_cmd,
+ "show ip ospf router-info pce",
+ SHOW_STR
+ IP_STR
+ OSPF_STR
+ "Router Information\n"
+ "PCE information\n")
+{
+
+ struct ospf_pce_info *pce = &OspfRI.pce_info;
+ struct listnode *node;
+ struct ri_pce_subtlv_domain *domain;
+ struct ri_pce_subtlv_neighbor *neighbor;
+
+ if (OspfRI.status == enabled)
+ {
+ vty_out (vty, "--- PCE parameters ---%s", VTY_NEWLINE);
+
+ if (pce->pce_address.header.type != 0)
+ show_vty_pce_subtlv_address (vty, &pce->pce_address.header);
+
+ if (pce->pce_scope.header.type != 0)
+ show_vty_pce_subtlv_path_scope (vty, &pce->pce_scope.header);
+
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain))
+ {
+ if (domain->header.type != 0)
+ show_vty_pce_subtlv_domain (vty, &domain->header);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor))
+ {
+ if (neighbor->header.type != 0)
+ show_vty_pce_subtlv_neighbor (vty, &neighbor->header);
+ }
+
+ if (pce->pce_cap_flag.header.type != 0)
+ show_vty_pce_subtlv_cap_flag (vty, &pce->pce_cap_flag.header);
+
+ }
+ else
+ {
+ vty_out (vty, " Router Information is disabled on this router%s",
+ VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Install new CLI commands */
+static void
+ospf_router_info_register_vty (void)
+{
+ install_element (VIEW_NODE, &show_ip_ospf_router_info_cmd);
+ install_element (VIEW_NODE, &show_ip_ospf_router_info_pce_cmd);
+ install_element (ENABLE_NODE, &show_ip_ospf_router_info_cmd);
+ install_element (ENABLE_NODE, &show_ip_ospf_router_info_pce_cmd);
+
+ install_element (OSPF_NODE, &router_info_area_cmd);
+ install_element (OSPF_NODE, &router_info_as_cmd);
+ install_element (OSPF_NODE, &no_router_info_cmd);
+ install_element (OSPF_NODE, &pce_address_cmd);
+ install_element (OSPF_NODE, &pce_path_scope_cmd);
+ install_element (OSPF_NODE, &pce_domain_cmd);
+ install_element (OSPF_NODE, &no_pce_domain_cmd);
+ install_element (OSPF_NODE, &pce_neighbor_cmd);
+ install_element (OSPF_NODE, &no_pce_neighbor_cmd);
+ install_element (OSPF_NODE, &pce_cap_flag_cmd);
+
+ return;
+}
diff --git a/ospfd/ospf_ri.h b/ospfd/ospf_ri.h
new file mode 100644
index 0000000..c507434
--- /dev/null
+++ b/ospfd/ospf_ri.h
@@ -0,0 +1,191 @@
+/*
+ * This is an implementation of RFC4970 Router Information
+ * 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/
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_OSPF_ROUTER_INFO_H
+#define _ZEBRA_OSPF_ROUTER_INFO_H
+
+/*
+ * Opaque LSA's link state ID for Router Information is
+ * structured as follows.
+ *
+ * 24 16 8 0
+ * +--------+--------+--------+--------+
+ * | 1 | MBZ |........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<Resv'd>|<-- Instance --->|
+ *
+ *
+ * Type: IANA has assigned '4' for Router Information.
+ * MBZ: Reserved, must be set to zero.
+ * Instance: User may select an arbitrary 16-bit value.
+ *
+ */
+
+/*
+ * 24 16 8 0
+ * +--------+--------+--------+--------+ ---
+ * | LS age |Options | 9,10,11| A
+ * +--------+--------+--------+--------+ |
+ * | 4 | 0 | Instance | |
+ * +--------+--------+--------+--------+ |
+ * | Advertising router | | Standard (Opaque) LSA header;
+ * +--------+--------+--------+--------+ | Type 9,10 or 11 are used.
+ * | LS sequence number | |
+ * +--------+--------+--------+--------+ |
+ * | LS checksum | Length | V
+ * +--------+--------+--------+--------+ ---
+ * | Type | Length | A
+ * +--------+--------+--------+--------+ | TLV part for Router Information; Values might be
+ * | Values ... | V structured as a set of sub-TLVs.
+ * +--------+--------+--------+--------+ ---
+ */
+
+/*
+ * Following section defines TLV (tag, length, value) structures,
+ * used for Router Information.
+ */
+struct ri_tlv_header
+{
+ u_int16_t type; /* RI_TLV_XXX (see below) */
+ u_int16_t length; /* Value portion only, in byte */
+};
+
+#define RI_TLV_HDR_SIZE (sizeof (struct ri_tlv_header))
+#define RI_TLV_BODY_SIZE(tlvh) (ROUNDUP (ntohs ((tlvh)->length), sizeof (u_int32_t)))
+#define RI_TLV_SIZE(tlvh) (RI_TLV_HDR_SIZE + RI_TLV_BODY_SIZE(tlvh))
+#define RI_TLV_HDR_TOP(lsah) (struct ri_tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE)
+#define RI_TLV_HDR_NEXT(tlvh) (struct ri_tlv_header *)((char *)(tlvh) + RI_TLV_SIZE(tlvh))
+
+/*
+ * Following section defines TLV body parts.
+ */
+
+/* Up to now, 8 code point 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
+
+/* RFC4970: Router Information Capabilities TLV */ /* Mandatory */
+#define RI_TLV_CAPABILITIES 1
+
+struct ri_tlv_router_cap
+{
+ struct ri_tlv_header header; /* Value length is 4 bytes. */
+ 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
+
+#define RI_TLV_LENGTH 4
+
+/* RFC5088: PCE Capabilities TLV */ /* Optional */
+/* RI PCE TLV */
+#define RI_TLV_PCE 6
+
+struct ri_tlv_pce
+{
+ struct ri_tlv_header header;
+/* A set of PCE-sub-TLVs will follow. */
+};
+
+/* PCE Address Sub-TLV */ /* Mandatory */
+#define RI_PCE_SUBTLV_ADDRESS 1
+struct ri_pce_subtlv_address
+{
+ struct ri_tlv_header header; /* Type = 1; Length is 8 (IPv4) or 20 (IPv6) bytes. */
+#define PCE_ADDRESS_LENGTH_IPV4 8
+#define PCE_ADDRESS_LENGTH_IPV6 20
+ struct
+ {
+ u_int16_t type; /* Address type: 1 = IPv4, 2 = IPv6 */
+#define PCE_ADDRESS_TYPE_IPV4 1
+#define PCE_ADDRESS_TYPE_IPV6 2
+ u_int16_t reserved;
+ struct in_addr value; /* PCE address */
+ } address;
+};
+
+/* PCE Path-Scope Sub-TLV */ /* Mandatory */
+#define RI_PCE_SUBTLV_PATH_SCOPE 2
+struct ri_pce_subtlv_path_scope
+{
+ struct ri_tlv_header header; /* Type = 2; Length = 4 bytes. */
+ u_int32_t value; /* L, R, Rd, S, Sd, Y, PrefL, PrefR, PrefS and PrefY bits see RFC5088 page 9 */
+};
+
+/* PCE Domain Sub-TLV */ /* Optional */
+#define RI_PCE_SUBTLV_DOMAIN 3
+
+#define PCE_DOMAIN_TYPE_AREA 1
+#define PCE_DOMAIN_TYPE_AS 2
+
+struct ri_pce_subtlv_domain
+{
+ struct ri_tlv_header header; /* Type = 3; Length = 8 bytes. */
+ u_int16_t type; /* Domain type: 1 = OSPF Area ID, 2 = AS Number */
+ u_int16_t reserved;
+ u_int32_t value;
+};
+
+/* PCE Neighbor Sub-TLV */ /* Mandatory if R or S bit is set */
+#define RI_PCE_SUBTLV_NEIGHBOR 4
+struct ri_pce_subtlv_neighbor
+{
+ struct ri_tlv_header header; /* Type = 4; Length = 8 bytes. */
+ u_int16_t type; /* Domain type: 1 = OSPF Area ID, 2 = AS Number */
+ u_int16_t reserved;
+ u_int32_t value;
+};
+
+/* PCE Capabilities Flags Sub-TLV */ /* Optional */
+#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_OBJECTIVES 0x0020
+#define PCE_CAP_ADDITIVE 0x0040
+#define PCE_CAP_PRIORIZATION 0x0080
+#define PCE_CAP_MULTIPLE_REQ 0x0100
+
+struct ri_pce_subtlv_cap_flag
+{
+ struct ri_tlv_header header; /* Type = 5; Length = n x 4 bytes. */
+ u_int32_t value;
+};
+
+/* Prototypes. */
+extern int ospf_router_info_init (void);
+extern void ospf_router_info_term (void);
+
+#endif /* _ZEBRA_OSPF_ROUTER_INFO_H */