blob: 5b1032b0c8ceb9d8ec371139d3da1194300544fe [file] [log] [blame]
/*
* 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;
}