]> git.proxmox.com Git - mirror_frr.git/commitdiff
Add support Router Capabilities support to OSPF
authorOlivier Dugeon <olivier.dugeon@orange.com>
Tue, 19 Apr 2016 17:21:17 +0000 (19:21 +0200)
committerDonald Sharp <sharpd@cumulusnetwroks.com>
Sat, 3 Sep 2016 15:05:51 +0000 (11:05 -0400)
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>
ospfd/Makefile.am
ospfd/ospf_opaque.c
ospfd/ospf_ri.c [new file with mode: 0644]
ospfd/ospf_ri.h [new file with mode: 0644]

index 9eb166a3d61b8d6e38e51e27366b7920a41ddfba..55f9bf2d8e042142a9772768ef9d486edded6b0b 100644 (file)
@@ -16,7 +16,7 @@ libospf_la_SOURCES = \
        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 \
         ospf_bfd.c
 
 ospfdheaderdir = $(pkgincludedir)/ospfd
@@ -28,12 +28,12 @@ ospfdheader_HEADERS = \
 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 \
         ospf_bfd.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
 
index 8c6ec892c498303dac3d11bd4356abfbca889c1f..9b395b3e8e824effc9c4db37ad34cd25780f2ce8 100644 (file)
@@ -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 @@ ospf_opaque_init (void)
   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_opaque_term (void)
 {
   ospf_mpls_te_term ();
 
+  ospf_router_info_term ();
+
 #ifdef SUPPORT_OSPF_API
   ospf_apiserver_term ();
 #endif /* SUPPORT_OSPF_API */
@@ -216,6 +222,9 @@ ospf_opaque_type_name (u_char opaque_type)
     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 (file)
index 0000000..8fefd2b
--- /dev/null
@@ -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 ();
+
+  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 (file)
index 0000000..3f898ac
--- /dev/null
@@ -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 */