Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2016 lines (1668 sloc) 51.5 KB
/*
* This is an implementation of RFC4970 Router Information
* with support of RFC5088 PCE Capabilites announcement
*
* Module name: Router Information
* Author: Olivier Dugeon <olivier.dugeon@orange.com>
* Copyright (C) 2012 - 2017 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 this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 "mpls.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
#include "ospfd/ospf_ism.h"
#include "ospfd/ospf_asbr.h"
#include "ospfd/ospf_lsa.h"
#include "ospfd/ospf_lsdb.h"
#include "ospfd/ospf_neighbor.h"
#include "ospfd/ospf_nsm.h"
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_packet.h"
#include "ospfd/ospf_spf.h"
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_route.h"
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_zebra.h"
#include "ospfd/ospf_sr.h"
#include "ospfd/ospf_ri.h"
#include "ospfd/ospf_errors.h"
/*
* 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_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(struct ospf_ri_area_info *ai,
enum lsa_opcode opcode);
static void ospf_router_info_register_vty(void);
static int ospf_router_info_lsa_update(struct ospf_lsa *lsa);
static void del_area_info(void *val);
static void del_pce_info(void *val);
int ospf_router_info_init(void)
{
zlog_info("RI (%s): Initialize Router Information", __func__);
memset(&OspfRI, 0, sizeof(struct ospf_router_info));
OspfRI.enabled = false;
OspfRI.registered = 0;
OspfRI.scope = OSPF_OPAQUE_AS_LSA;
OspfRI.as_flags = RIFLG_LSA_INACTIVE;
OspfRI.area_info = list_new();
OspfRI.area_info->del = del_area_info;
/* Initialize pce domain and neighbor list */
OspfRI.pce_info.enabled = false;
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;
/* Initialize Segment Routing information structure */
OspfRI.sr_info.enabled = false;
ospf_router_info_register_vty();
return 0;
}
static int ospf_router_info_register(uint8_t scope)
{
int rc = 0;
if (OspfRI.registered)
return rc;
zlog_info("RI (%s): Register Router Information with scope %s(%d)",
__func__,
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,
NULL, /* 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, ospf_router_info_lsa_update,
NULL); /* del_lsa_hook */
if (rc != 0) {
flog_warn(
EC_OSPF_OPAQUE_REGISTRATION,
"RI (%s): Failed to register functions", __func__);
return rc;
}
OspfRI.registered = 1;
OspfRI.scope = scope;
return rc;
}
static int ospf_router_info_unregister(void)
{
if ((OspfRI.scope != OSPF_OPAQUE_AS_LSA)
&& (OspfRI.scope != OSPF_OPAQUE_AREA_LSA)) {
assert("Unable to unregister Router Info functions: Wrong scope!"
== NULL);
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.enabled = false;
ospf_router_info_unregister();
return;
}
void ospf_router_info_finish(void)
{
list_delete_all_node(OspfRI.pce_info.pce_domain);
list_delete_all_node(OspfRI.pce_info.pce_neighbor);
OspfRI.enabled = false;
}
static void del_area_info(void *val)
{
XFREE(MTYPE_OSPF_ROUTER_INFO, val);
}
static void del_pce_info(void *val)
{
XFREE(MTYPE_OSPF_PCE_PARAMS, val);
}
/* Catch RI LSA flooding Scope for ospf_ext.[h,c] code */
struct scope_info ospf_router_info_get_flooding_scope(void)
{
struct scope_info flooding_scope;
if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
flooding_scope.scope = OSPF_OPAQUE_AS_LSA;
flooding_scope.areas = NULL;
return flooding_scope;
}
flooding_scope.scope = OSPF_OPAQUE_AREA_LSA;
flooding_scope.areas = OspfRI.area_info;
return flooding_scope;
}
static struct ospf_ri_area_info *lookup_by_area(struct ospf_area *area)
{
struct listnode *node, *nnode;
struct ospf_ri_area_info *ai;
for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai))
if (ai->area == area)
return ai;
return NULL;
}
/*------------------------------------------------------------------------*
* Followings are control functions for ROUTER INFORMATION parameters
*management.
*------------------------------------------------------------------------*/
static void set_router_info_capabilities(struct ri_tlv_router_cap *ric,
uint32_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)
{
uint16_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 += TLV_SIZE(&pce->pce_address.header);
/* PCE Path Scope */
if (ntohs(pce->pce_scope.header.type) != 0)
length += 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 += TLV_SIZE(&domain->header);
}
/* PCE Neighbor */
for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
if (ntohs(neighbor->header.type) != 0)
length += TLV_SIZE(&neighbor->header);
}
/* PCE Capabilities */
if (ntohs(pce->pce_cap_flag.header.type) != 0)
length += 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);
pce->enabled = true;
} else {
pce->pce_header.header.type = 0;
pce->pce_header.header.length = 0;
pce->enabled = false;
}
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(uint32_t scope, struct ospf_pce_info *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(uint16_t type, uint32_t domain,
struct ospf_pce_info *pce)
{
struct ri_pce_subtlv_domain *new;
/* 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(uint16_t type, uint32_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);
/* Finally free the old domain */
XFREE(MTYPE_OSPF_PCE_PARAMS, old);
}
}
static void set_pce_neighbor(uint16_t type, uint32_t domain,
struct ospf_pce_info *pce)
{
struct ri_pce_subtlv_neighbor *new;
/* 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(uint16_t type, uint32_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);
/* Finally free the old domain */
XFREE(MTYPE_OSPF_PCE_PARAMS, old);
}
}
static void set_pce_cap_flag(uint32_t cap, struct ospf_pce_info *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;
}
/* Segment Routing TLV setter */
/* Algorithm SubTLV - section 3.1 */
static void set_sr_algorithm(uint8_t algo)
{
OspfRI.sr_info.algo.value[0] = algo;
for (int i = 1; i < ALGORITHM_COUNT; i++)
OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
/* Set TLV type and length == only 1 Algorithm */
TLV_TYPE(OspfRI.sr_info.algo) = htons(RI_SR_TLV_SR_ALGORITHM);
TLV_LEN(OspfRI.sr_info.algo) = htons(sizeof(uint8_t));
}
/* unset Aglogithm SubTLV */
static void unset_sr_algorithm(uint8_t algo)
{
for (int i = 0; i < ALGORITHM_COUNT; i++)
OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
/* Unset TLV type and length */
TLV_TYPE(OspfRI.sr_info.algo) = htons(0);
TLV_LEN(OspfRI.sr_info.algo) = htons(0);
}
/* Segment Routing Global Block SubTLV - section 3.2 */
static void set_sr_sid_label_range(struct sr_srgb srgb)
{
/* Set Header */
TLV_TYPE(OspfRI.sr_info.range) = htons(RI_SR_TLV_SID_LABEL_RANGE);
TLV_LEN(OspfRI.sr_info.range) =
htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t));
/* Set Range Size */
OspfRI.sr_info.range.size = htonl(SET_RANGE_SIZE(srgb.range_size));
/* Set Lower bound label SubTLV */
TLV_TYPE(OspfRI.sr_info.range.lower) = htons(SUBTLV_SID_LABEL);
TLV_LEN(OspfRI.sr_info.range.lower) = htons(SID_RANGE_LABEL_LENGTH);
OspfRI.sr_info.range.lower.value = htonl(SET_LABEL(srgb.lower_bound));
}
/* Unset this SRGB SubTLV */
static void unset_sr_sid_label_range(void)
{
TLV_TYPE(OspfRI.sr_info.range) = htons(0);
TLV_LEN(OspfRI.sr_info.range) = htons(0);
TLV_TYPE(OspfRI.sr_info.range.lower) = htons(0);
TLV_LEN(OspfRI.sr_info.range.lower) = htons(0);
}
/* Set Maximum Stack Depth for this router */
static void set_sr_node_msd(uint8_t msd)
{
TLV_TYPE(OspfRI.sr_info.msd) = htons(RI_SR_TLV_NODE_MSD);
TLV_LEN(OspfRI.sr_info.msd) = htons(sizeof(uint32_t));
OspfRI.sr_info.msd.value = msd;
}
/* Unset this router MSD */
static void unset_sr_node_msd(void)
{
TLV_TYPE(OspfRI.sr_info.msd) = htons(0);
TLV_LEN(OspfRI.sr_info.msd) = htons(0);
}
static void unset_param(void *tlv_buffer)
{
struct tlv_header *tlv = (struct tlv_header *)tlv_buffer;
tlv->type = 0;
/* Fill the Value to 0 */
memset(TLV_DATA(tlv_buffer), 0, TLV_BODY_SIZE(tlv));
tlv->length = 0;
return;
}
static void initialize_params(struct ospf_router_info *ori)
{
uint32_t cap = 0;
struct ospf *top;
struct listnode *node, *nnode;
struct ospf_area *area;
struct ospf_ri_area_info *new;
/*
* Initialize default Router Information Capabilities.
*/
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_by_vrf_id(VRF_DEFAULT);
zlog_info("RI (%s): Initialize Router Info for %s scope", __func__,
OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS");
/* Try to get available Area's context from ospf at this step.
* Do it latter if not available */
if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) {
for (ALL_LIST_ELEMENTS(top->areas, node, nnode, area)) {
zlog_debug("RI (%s): Add area %s to Router Information",
__func__, inet_ntoa(area->area_id));
new = XCALLOC(MTYPE_OSPF_ROUTER_INFO,
sizeof(struct ospf_ri_area_info));
new->area = area;
new->flags = RIFLG_LSA_INACTIVE;
listnode_add(OspfRI.area_info, new);
}
}
/*
* 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);
return;
}
static int is_mandated_params_set(struct ospf_router_info ori)
{
int rc = 0;
if (ntohs(ori.router_cap.header.type) == 0)
return rc;
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))
return rc;
if ((ori.sr_info.enabled) && (ntohs(TLV_TYPE(ori.sr_info.algo)) == 0)
&& (ntohs(TLV_TYPE(ori.sr_info.range)) == 0))
return rc;
rc = 1;
return rc;
}
/*
* Used by Segment Routing to set new TLVs and Sub-TLVs values
*
* @param enable To activate or not Segment Routing router Information flooding
* @param size Size of Label Range i.e. SRGB size
* @param lower Lower bound of the Label Range i.e. SRGB first label
* @param msd Maximum label Stack Depth supported by the router
*
* @return none
*/
void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd)
{
struct listnode *node, *nnode;
struct ospf_ri_area_info *ai;
/* First, check if Router Information is registered or not */
if (!OspfRI.registered)
ospf_router_info_register(OSPF_OPAQUE_AREA_LSA);
/* Verify that scope is AREA */
if (OspfRI.scope != OSPF_OPAQUE_AREA_LSA) {
zlog_err(
"RI (%s): Router Info is %s flooding: Change scope to Area flooding for Segment Routing",
__func__,
OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS");
return;
}
/* Then, activate and initialize Router Information if necessary */
if (!OspfRI.enabled) {
OspfRI.enabled = true;
initialize_params(&OspfRI);
}
if (IS_DEBUG_OSPF_SR)
zlog_debug("RI (%s): %s Routing Information for Segment Routing",
__func__, enable ? "Enable" : "Disable");
/* Unset or Set SR parameters */
if (!enable) {
unset_sr_algorithm(SR_ALGORITHM_SPF);
unset_sr_sid_label_range();
unset_sr_node_msd();
OspfRI.sr_info.enabled = false;
} else {
// Only SR_ALGORITHM_SPF is supported
set_sr_algorithm(SR_ALGORITHM_SPF);
set_sr_sid_label_range(srgb);
if (msd != 0)
set_sr_node_msd(msd);
else
unset_sr_node_msd();
OspfRI.sr_info.enabled = true;
}
/* Refresh if already engaged or originate RI LSA */
for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) {
if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED))
ospf_router_info_lsa_schedule(ai, REFRESH_THIS_LSA);
else
ospf_router_info_lsa_schedule(ai,
REORIGINATE_THIS_LSA);
}
}
/*------------------------------------------------------------------------*
* Followings are callback functions against generic Opaque-LSAs handling.
*------------------------------------------------------------------------*/
static void ospf_router_info_ism_change(struct ospf_interface *oi,
int old_state)
{
struct ospf_ri_area_info *ai;
/* Collect area information */
ai = lookup_by_area(oi->area);
/* Check if area is not yet registered */
if (ai != NULL)
return;
/* Add this new area to the list */
ai = XCALLOC(MTYPE_OSPF_ROUTER_INFO, sizeof(struct ospf_ri_area_info));
ai->area = oi->area;
ai->flags = RIFLG_LSA_INACTIVE;
listnode_add(OspfRI.area_info, ai);
return;
}
/*------------------------------------------------------------------------*
* Followings are OSPF protocol processing functions for ROUTER INFORMATION
*------------------------------------------------------------------------*/
static void build_tlv_header(struct stream *s, struct tlv_header *tlvh)
{
stream_put(s, tlvh, sizeof(struct tlv_header));
return;
}
static void build_tlv(struct stream *s, struct tlv_header *tlvh)
{
if (ntohs(tlvh->type) != 0) {
build_tlv_header(s, tlvh);
stream_put(s, TLV_DATA(tlvh), 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);
/* Build Segment Routing TLVs if enabled */
if (OspfRI.sr_info.enabled) {
/* Build Algorithm TLV */
build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo));
/* Build SRGB TLV */
build_tlv(s, &TLV_HDR(OspfRI.sr_info.range));
/* Build MSD TLV */
build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd));
}
/* Add RI PCE TLV if it is set */
if (OspfRI.pce_info.enabled) {
/* Compute PCE Info header first */
set_pce_header(&OspfRI.pce_info);
/* Build PCE TLV */
build_tlv_header(s, &OspfRI.pce_info.pce_header.header);
/* 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_area *area)
{
struct ospf *top;
struct stream *s;
struct lsa_header *lsah;
struct ospf_lsa *new = NULL;
uint8_t options, lsa_type;
struct in_addr lsa_id;
uint32_t tmp;
uint16_t length;
/* Create a stream for LSA. */
s = stream_new(OSPF_MAX_LSA_SIZE);
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_by_vrf_id(VRF_DEFAULT);
/* 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. */
new = ospf_lsa_new_and_data(length);
new->area = area;
if (new->area && new->area->ospf)
new->vrf_id = new->area->ospf->vrf_id;
else
new->vrf_id = VRF_DEFAULT;
SET_FLAG(new->flags, OSPF_LSA_SELF);
memcpy(new->data, lsah, length);
stream_free(s);
return new;
}
static int ospf_router_info_lsa_originate_as(void *arg)
{
struct ospf_lsa *new;
struct ospf *top;
int rc = -1;
vrf_id_t vrf_id = VRF_DEFAULT;
/* Sanity Check */
if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) {
flog_warn(
EC_OSPF_LSA_INSTALL_FAILURE,
"RI (%s): wrong flooding scope AREA instead of AS ?",
__func__);
return rc;
}
/* Create new Opaque-LSA/ROUTER INFORMATION instance. */
new = ospf_router_info_lsa_new(NULL);
new->vrf_id = VRF_DEFAULT;
top = (struct ospf *)arg;
/* Check ospf info */
if (top == NULL) {
zlog_debug("RI (%s): ospf instance not found for vrf id %u",
__func__, vrf_id);
ospf_lsa_unlock(&new);
return rc;
}
/* Install this LSA into LSDB. */
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
flog_warn(
EC_OSPF_LSA_INSTALL_FAILURE,
"RI (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return rc;
}
/* Update new LSA origination count. */
top->lsa_originate_count++;
/* Flood new LSA through AREA or AS. */
SET_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED);
ospf_flood_through_as(top, 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;
return rc;
}
static int ospf_router_info_lsa_originate_area(void *arg)
{
struct ospf_lsa *new;
struct ospf *top;
struct ospf_ri_area_info *ai = NULL;
int rc = -1;
vrf_id_t vrf_id = VRF_DEFAULT;
/* Sanity Check */
if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
flog_warn(
EC_OSPF_LSA_INSTALL_FAILURE,
"RI (%s): wrong flooding scope AS instead of AREA ?",
__func__);
return rc;
}
/* Create new Opaque-LSA/ROUTER INFORMATION instance. */
ai = lookup_by_area((struct ospf_area *)arg);
if (ai == NULL) {
zlog_debug(
"RI (%s): There is no context for this Router Information. Stop processing",
__func__);
return rc;
}
if (ai->area->ospf) {
vrf_id = ai->area->ospf->vrf_id;
top = ai->area->ospf;
} else {
top = ospf_lookup_by_vrf_id(vrf_id);
}
new = ospf_router_info_lsa_new(ai->area);
new->vrf_id = vrf_id;
/* Check ospf info */
if (top == NULL) {
zlog_debug("RI (%s): ospf instance not found for vrf id %u",
__func__, vrf_id);
ospf_lsa_unlock(&new);
return rc;
}
/* Install this LSA into LSDB. */
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
flog_warn(
EC_OSPF_LSA_INSTALL_FAILURE,
"RI (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return rc;
}
/* Update new LSA origination count. */
top->lsa_originate_count++;
/* Flood new LSA through AREA or AS. */
SET_FLAG(ai->flags, RIFLG_LSA_ENGAGED);
ospf_flood_through_area(ai->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;
return rc;
}
static int ospf_router_info_lsa_originate(void *arg)
{
struct ospf_ri_area_info *ai;
int rc = -1;
if (!OspfRI.enabled) {
zlog_info("RI (%s): ROUTER INFORMATION is disabled now.",
__func__);
rc = 0; /* This is not an error case. */
return rc;
}
/* Check if Router Information LSA is already engaged */
if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
if ((CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED))
&& (CHECK_FLAG(OspfRI.as_flags,
RIFLG_LSA_FORCED_REFRESH))) {
UNSET_FLAG(OspfRI.as_flags, RIFLG_LSA_FORCED_REFRESH);
ospf_router_info_lsa_schedule(NULL, REFRESH_THIS_LSA);
rc = 0;
return rc;
}
} else {
ai = lookup_by_area((struct ospf_area *)arg);
if (ai == NULL) {
flog_warn(
EC_OSPF_LSA,
"RI (%s): Missing area information", __func__);
return rc;
}
if ((CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED))
&& (CHECK_FLAG(ai->flags, RIFLG_LSA_FORCED_REFRESH))) {
UNSET_FLAG(ai->flags, RIFLG_LSA_FORCED_REFRESH);
ospf_router_info_lsa_schedule(ai, REFRESH_THIS_LSA);
rc = 0;
return rc;
}
}
/* Router Information is not yet Engaged, check parameters */
if (!is_mandated_params_set(OspfRI))
flog_warn(
EC_OSPF_LSA,
"RI (%s): lacks mandated ROUTER INFORMATION parameters",
__func__);
/* Ok, let's try to originate an LSA */
if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
rc = ospf_router_info_lsa_originate_as(arg);
else
rc = ospf_router_info_lsa_originate_area(arg);
return rc;
}
static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa)
{
struct ospf_ri_area_info *ai = NULL;
struct ospf_lsa *new = NULL;
struct ospf *top;
if (!OspfRI.enabled) {
/*
* This LSA must have flushed before due to ROUTER INFORMATION
* status change.
* It seems a slip among routers in the routing domain.
*/
zlog_info("RI (%s): ROUTER INFORMATION is disabled now.",
__func__);
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) {
flog_warn(
EC_OSPF_LSA,
"RI (%s): Unsupported Router Information ID",
__func__);
return NULL;
}
/* Process LSA depending of the flooding scope */
if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) {
/* Get context AREA context */
ai = lookup_by_area(lsa->area);
if (ai == NULL) {
flog_warn(
EC_OSPF_LSA,
"RI (%s): No associated Area", __func__);
return NULL;
}
/* Flush LSA, if the lsa's age reached to MaxAge. */
if (IS_LSA_MAXAGE(lsa)) {
UNSET_FLAG(ai->flags, RIFLG_LSA_ENGAGED);
ospf_opaque_lsa_flush_schedule(lsa);
return NULL;
}
/* Create new Opaque-LSA/ROUTER INFORMATION instance. */
new = ospf_router_info_lsa_new(ai->area);
new->data->ls_seqnum = lsa_seqnum_increment(lsa);
new->vrf_id = lsa->vrf_id;
/* Install this LSA into LSDB. */
/* Given "lsa" will be freed in the next function. */
top = ospf_lookup_by_vrf_id(lsa->vrf_id);
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
"RI (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return new;
}
/* Flood updated LSA through AREA */
ospf_flood_through_area(ai->area, NULL /*nbr */, new);
} else { /* AS Flooding scope */
/* Flush LSA, if the lsa's age reached to MaxAge. */
if (IS_LSA_MAXAGE(lsa)) {
UNSET_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED);
ospf_opaque_lsa_flush_schedule(lsa);
return NULL;
}
/* Create new Opaque-LSA/ROUTER INFORMATION instance. */
new = ospf_router_info_lsa_new(NULL);
new->data->ls_seqnum = lsa_seqnum_increment(lsa);
new->vrf_id = lsa->vrf_id;
/* Install this LSA into LSDB. */
/* Given "lsa" will be freed in the next function. */
top = ospf_lookup_by_vrf_id(lsa->vrf_id);
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
"RI (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return new;
}
/* Flood updated LSA through AS */
ospf_flood_through_as(top, 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);
}
return new;
}
static void ospf_router_info_lsa_schedule(struct ospf_ri_area_info *ai,
enum lsa_opcode opcode)
{
struct ospf_lsa lsa;
struct lsa_header lsah;
struct ospf *top;
uint32_t tmp;
memset(&lsa, 0, sizeof(lsa));
memset(&lsah, 0, sizeof(lsah));
zlog_debug("RI (%s): LSA schedule %s%s%s", __func__,
opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
opcode == REFRESH_THIS_LSA ? "Refresh" : "",
opcode == FLUSH_THIS_LSA ? "Flush" : "");
/* Check LSA flags state coherence and collect area information */
if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) {
if ((ai == NULL) || (ai->area == NULL)) {
flog_warn(
EC_OSPF_LSA,
"RI (%s): Router Info is Area scope flooding but area is not set",
__func__);
return;
}
if (!CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)
&& (opcode != REORIGINATE_THIS_LSA))
return;
if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)
&& (opcode == REORIGINATE_THIS_LSA))
opcode = REFRESH_THIS_LSA;
lsa.area = ai->area;
top = ai->area->ospf;
} else {
if (!CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED)
&& (opcode != REORIGINATE_THIS_LSA))
return;
if (CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED)
&& (opcode == REORIGINATE_THIS_LSA))
opcode = REFRESH_THIS_LSA;
top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
lsa.area = NULL;
}
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 *)ai->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:
if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA)
UNSET_FLAG(ai->flags, RIFLG_LSA_ENGAGED);
else
UNSET_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED);
ospf_opaque_lsa_flush_schedule(&lsa);
break;
}
return;
}
/* Callback to handle Segment Routing information */
static int ospf_router_info_lsa_update(struct ospf_lsa *lsa)
{
/* Sanity Check */
if (lsa == NULL) {
flog_warn(EC_OSPF_LSA, "RI (%s): Abort! LSA is NULL",
__func__);
return -1;
}
/* Process only Opaque LSA */
if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
&& (lsa->data->type != OSPF_OPAQUE_AS_LSA))
return 0;
/* Process only Router Information LSA */
if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
!= OPAQUE_TYPE_ROUTER_INFORMATION_LSA)
return 0;
/* Check if it is not my LSA */
if (IS_LSA_SELF(lsa))
return 0;
/* Check if Router Info & Segment Routing are enable */
if (!OspfRI.enabled || !OspfRI.sr_info.enabled)
return 0;
/* Call Segment Routing LSA update or deletion */
if (!IS_LSA_MAXAGE(lsa))
ospf_sr_ri_lsa_update(lsa);
else
ospf_sr_ri_lsa_delete(lsa);
return 0;
}
/*------------------------------------------------------------------------*
* Followings are vty session control functions.
*------------------------------------------------------------------------*/
static uint16_t show_vty_router_cap(struct vty *vty, struct 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\n",
ntohl(top->value));
else
zlog_debug(" Router Capabilities: 0x%x", ntohl(top->value));
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_pce_subtlv_address(struct vty *vty,
struct 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\n",
inet_ntoa(top->address.value));
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\n",
ntohl(top->address.value.s_addr));
else
zlog_debug(" PCE Address: 0x%x",
ntohl(top->address.value.s_addr));
}
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_pce_subtlv_path_scope(struct vty *vty,
struct 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\n", ntohl(top->value));
else
zlog_debug(" PCE Path Scope: 0x%x", ntohl(top->value));
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_pce_subtlv_domain(struct vty *vty,
struct 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\n", inet_ntoa(tmp));
else
zlog_debug(" PCE domain Area: %s", inet_ntoa(tmp));
} else {
if (vty != NULL)
vty_out(vty, " PCE domain AS: %d\n",
ntohl(top->value));
else
zlog_debug(" PCE domain AS: %d", ntohl(top->value));
}
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_pce_subtlv_neighbor(struct vty *vty,
struct 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\n",
inet_ntoa(tmp));
else
zlog_debug(" PCE neighbor Area: %s", inet_ntoa(tmp));
} else {
if (vty != NULL)
vty_out(vty, " PCE neighbor AS: %d\n",
ntohl(top->value));
else
zlog_debug(" PCE neighbor AS: %d",
ntohl(top->value));
}
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_pce_subtlv_cap_flag(struct vty *vty,
struct 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\n",
ntohl(top->value));
else
zlog_debug(" PCE Capabilities Flag: 0x%x",
ntohl(top->value));
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh)
{
if (vty != NULL)
vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n",
ntohs(tlvh->type), ntohs(tlvh->length));
else
zlog_debug(" Unknown TLV: [type(0x%x), length(0x%x)]",
ntohs(tlvh->type), ntohs(tlvh->length));
return TLV_SIZE(tlvh);
}
static uint16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri,
uint32_t total)
{
struct tlv_header *tlvh;
uint16_t sum = 0;
for (tlvh = ri; sum < total; tlvh = 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;
}
/* Display Segment Routing Algorithm TLV information */
static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh)
{
struct ri_sr_tlv_sr_algorithm *algo =
(struct ri_sr_tlv_sr_algorithm *)tlvh;
int i;
if (vty != NULL) {
vty_out(vty, " Segment Routing Algorithm TLV:\n");
for (i = 0; i < ntohs(algo->header.length); i++) {
switch (algo->value[i]) {
case 0:
vty_out(vty, " Algorithm %d: SPF\n", i);
break;
case 1:
vty_out(vty, " Algorithm %d: Strict SPF\n",
i);
break;
default:
vty_out(vty,
" Algorithm %d: Unknown value %d\n", i,
algo->value[i]);
break;
}
}
}
else {
zlog_debug(" Segment Routing Algorithm TLV:");
for (i = 0; i < ntohs(algo->header.length); i++)
switch (algo->value[i]) {
case 0:
zlog_debug(" Algorithm %d: SPF", i);
break;
case 1:
zlog_debug(" Algorithm %d: Strict SPF", i);
break;
default:
zlog_debug(
" Algorithm %d: Unknown value %d\n",
i, algo->value[i]);
break;
}
}
return TLV_SIZE(tlvh);
}
/* Display Segment Routing SID/Label Range TLV information */
static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh)
{
struct ri_sr_tlv_sid_label_range *range =
(struct ri_sr_tlv_sid_label_range *)tlvh;
if (vty != NULL) {
vty_out(vty,
" Segment Routing Range TLV:\n"
" Range Size = %d\n"
" SID Label = %d\n\n",
GET_RANGE_SIZE(ntohl(range->size)),
GET_LABEL(ntohl(range->lower.value)));
} else {
zlog_debug(
" Segment Routing Range TLV:\n"
" Range Size = %d\n"
" SID Label = %d\n\n",
GET_RANGE_SIZE(ntohl(range->size)),
GET_LABEL(ntohl(range->lower.value)));
}
return TLV_SIZE(tlvh);
}
/* Display Segment Routing Maximum Stack Depth TLV information */
static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh)
{
struct ri_sr_tlv_node_msd *msd = (struct ri_sr_tlv_node_msd *)tlvh;
if (vty != NULL) {
vty_out(vty,
" Segment Routing MSD TLV:\n"
" Node Maximum Stack Depth = %d\n",
msd->value);
} else {
zlog_debug(
" Segment Routing MSD TLV:\n"
" Node Maximum Stack Depth = %d\n",
msd->value);
}
return TLV_SIZE(tlvh);
}
static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa)
{
struct lsa_header *lsah = (struct lsa_header *)lsa->data;
struct tlv_header *tlvh;
uint16_t length = 0, sum = 0;
/* Initialize TLV browsing */
length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
for (tlvh = TLV_HDR_TOP(lsah); sum < length;
tlvh = TLV_HDR_NEXT(tlvh)) {
switch (ntohs(tlvh->type)) {
case RI_TLV_CAPABILITIES:
sum += show_vty_router_cap(vty, tlvh);
break;
case RI_TLV_PCE:
tlvh++;
sum += TLV_HDR_SIZE;
sum += show_vty_pce_info(vty, tlvh, length - sum);
break;
case RI_SR_TLV_SR_ALGORITHM:
sum += show_vty_sr_algorithm(vty, tlvh);
break;
case RI_SR_TLV_SID_LABEL_RANGE:
sum += show_vty_sr_range(vty, tlvh);
break;
case RI_SR_TLV_NODE_MSD:
sum += show_vty_sr_msd(vty, tlvh);
break;
default:
sum += show_vty_unknown_tlv(vty, tlvh);
break;
}
}
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.enabled)
return;
if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
vty_out(vty, " router-info as\n");
else
vty_out(vty, " router-info area\n");
if (OspfRI.pce_info.enabled) {
if (pce->pce_address.header.type != 0)
vty_out(vty, " pce address %s\n",
inet_ntoa(pce->pce_address.address.value));
if (pce->pce_cap_flag.header.type != 0)
vty_out(vty, " pce flag 0x%x\n",
ntohl(pce->pce_cap_flag.value));
for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
if (domain->header.type != 0) {
if (domain->type == PCE_DOMAIN_TYPE_AREA) {
tmp.s_addr = domain->value;
vty_out(vty, " pce domain area %s\n",
inet_ntoa(tmp));
} else {
vty_out(vty, " pce domain as %d\n",
ntohl(domain->value));
}
}
}
for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
if (neighbor->header.type != 0) {
if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
tmp.s_addr = neighbor->value;
vty_out(vty, " pce neighbor area %s\n",
inet_ntoa(tmp));
} else {
vty_out(vty, " pce neighbor as %d\n",
ntohl(neighbor->value));
}
}
}
if (pce->pce_scope.header.type != 0)
vty_out(vty, " pce scope 0x%x\n",
ntohl(OspfRI.pce_info.pce_scope.value));
}
return;
}
/*------------------------------------------------------------------------*
* Followings are vty command functions.
*------------------------------------------------------------------------*/
/* Simple wrapper schedule RI LSA action in function of the scope */
static void ospf_router_info_schedule(enum lsa_opcode opcode)
{
struct listnode *node, *nnode;
struct ospf_ri_area_info *ai;
if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
if (CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED))
ospf_router_info_lsa_schedule(NULL, opcode);
else if (opcode == REORIGINATE_THIS_LSA)
ospf_router_info_lsa_schedule(NULL, opcode);
} else {
for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) {
if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED))
ospf_router_info_lsa_schedule(ai, opcode);
}
}
}
DEFUN (router_info,
router_info_area_cmd,
"router-info <as|area [A.B.C.D]>",
OSPF_RI_STR
"Enable the Router Information functionality with AS flooding scope\n"
"Enable the Router Information functionality with Area flooding scope\n"
"OSPF area ID in IP format (deprecated)\n")
{
int idx_mode = 1;
uint8_t scope;
if (OspfRI.enabled)
return CMD_SUCCESS;
/* Check and get Area value if present */
if (strncmp(argv[idx_mode]->arg, "as", 2) == 0)
scope = OSPF_OPAQUE_AS_LSA;
else
scope = OSPF_OPAQUE_AREA_LSA;
/* First start to register Router Information callbacks */
if (!OspfRI.registered && (ospf_router_info_register(scope)) != 0) {
vty_out(vty,
"%% Unable to register Router Information callbacks.");
flog_err(
EC_OSPF_INIT_FAIL,
"RI (%s): Unable to register Router Information callbacks. Abort!",
__func__);
return CMD_WARNING_CONFIG_FAILED;
}
OspfRI.enabled = true;
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);
/* Originate or Refresh RI LSA if already engaged */
ospf_router_info_schedule(REORIGINATE_THIS_LSA);
return CMD_SUCCESS;
}
DEFUN (no_router_info,
no_router_info_cmd,
"no router-info",
NO_STR
"Disable the Router Information functionality\n")
{
if (!OspfRI.enabled)
return CMD_SUCCESS;
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("RI-> Router Information: ON -> OFF");
ospf_router_info_schedule(FLUSH_THIS_LSA);
OspfRI.enabled = false;
return CMD_SUCCESS;
}
static int ospf_ri_enabled(struct vty *vty)
{
if (OspfRI.enabled)
return 1;
if (vty)
vty_out(vty, "%% OSPF RI is not turned on\n");
return 0;
}
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")
{
int idx_ipv4 = 2;
struct in_addr value;
struct ospf_pce_info *pi = &OspfRI.pce_info;
if (!ospf_ri_enabled(vty))
return CMD_WARNING_CONFIG_FAILED;
if (!inet_aton(argv[idx_ipv4]->arg, &value)) {
vty_out(vty, "Please specify PCE Address by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
}
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 */
ospf_router_info_schedule(REFRESH_THIS_LSA);
}
return CMD_SUCCESS;
}
DEFUN (no_pce_address,
no_pce_address_cmd,
"no pce address [A.B.C.D]",
NO_STR
PCE_STR
"Disable PCE address\n"
"PCE address in IPv4 address format\n")
{
unset_param(&OspfRI.pce_info.pce_address);
/* Refresh RI LSA if already engaged */
ospf_router_info_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")
{
int idx_bitpattern = 2;
uint32_t scope;
struct ospf_pce_info *pi = &OspfRI.pce_info;
if (!ospf_ri_enabled(vty))
return CMD_WARNING_CONFIG_FAILED;
if (sscanf(argv[idx_bitpattern]->arg, "0x%x", &scope) != 1) {
vty_out(vty, "pce_path_scope: fscanf: %s\n",
safe_strerror(errno));
return CMD_WARNING_CONFIG_FAILED;
}
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 */
ospf_router_info_schedule(REFRESH_THIS_LSA);
}
return CMD_SUCCESS;
}
DEFUN (no_pce_path_scope,
no_pce_path_scope_cmd,
"no pce scope [BITPATTERN]",
NO_STR
PCE_STR
"Disable PCE path scope\n"
"32-bit Hexadecimal value\n")
{
unset_param(&OspfRI.pce_info.pce_address);
/* Refresh RI LSA if already engaged */
ospf_router_info_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")
{
int idx_number = 3;
uint32_t as;
struct ospf_pce_info *pce = &OspfRI.pce_info;
struct listnode *node;
struct ri_pce_subtlv_domain *domain;
if (!ospf_ri_enabled(vty))
return CMD_WARNING_CONFIG_FAILED;
if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) {
vty_out(vty, "pce_domain: fscanf: %s\n", safe_strerror(errno));
return CMD_WARNING_CONFIG_FAILED;
}
/* 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)
return CMD_SUCCESS;
}
/* Create new domain if not found */
set_pce_domain(PCE_DOMAIN_TYPE_AS, as, pce);
/* Refresh RI LSA if already engaged */
ospf_router_info_schedule(REFRESH_THIS_LSA);
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")
{
int idx_number = 4;
uint32_t as;
struct ospf_pce_info *pce = &OspfRI.pce_info;
if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) {
vty_out(vty, "no_pce_domain: fscanf: %s\n",
safe_strerror(errno));
return CMD_WARNING_CONFIG_FAILED;
}
/* Unset corresponding PCE domain */
unset_pce_domain(PCE_DOMAIN_TYPE_AS, as, pce);
/* Refresh RI LSA if already engaged */
ospf_router_info_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")
{
int idx_number = 3;
uint32_t as;
struct ospf_pce_info *pce = &OspfRI.pce_info;
struct listnode *node;
struct ri_pce_subtlv_neighbor *neighbor;
if (!ospf_ri_enabled(vty))
return CMD_WARNING_CONFIG_FAILED;
if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) {
vty_out(vty, "pce_neighbor: fscanf: %s\n",
safe_strerror(errno));
return CMD_WARNING_CONFIG_FAILED;
}
/* 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)
return CMD_SUCCESS;
}
/* Create new domain if not found */
set_pce_neighbor(PCE_DOMAIN_TYPE_AS, as, pce);
/* Refresh RI LSA if already engaged */
ospf_router_info_schedule(REFRESH_THIS_LSA);
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")
{
int idx_number = 4;
uint32_t as;
struct ospf_pce_info *pce = &OspfRI.pce_info;
if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) {
vty_out(vty, "no_pce_neighbor: fscanf: %s\n",
safe_strerror(errno));
return CMD_WARNING_CONFIG_FAILED;
}
/* Unset corresponding PCE domain */
unset_pce_neighbor(PCE_DOMAIN_TYPE_AS, as, pce);
/* Refresh RI LSA if already engaged */
ospf_router_info_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")
{
int idx_bitpattern = 2;
uint32_t cap;
struct ospf_pce_info *pce = &OspfRI.pce_info;
if (!ospf_ri_enabled(vty))
return CMD_WARNING_CONFIG_FAILED;
if (sscanf(argv[idx_bitpattern]->arg, "0x%x", &cap) != 1) {
vty_out(vty, "pce_cap_flag: fscanf: %s\n",
safe_strerror(errno));
return CMD_WARNING_CONFIG_FAILED;
}
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 */
ospf_router_info_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);
/* Refresh RI LSA if already engaged */
ospf_router_info_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.enabled) {
vty_out(vty, "--- Router Information parameters ---\n");
show_vty_router_cap(vty, &OspfRI.router_cap.header);
} else {
if (vty != NULL)
vty_out(vty,
" Router Information is disabled on this router\n");
}
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.enabled) && (OspfRI.pce_info.enabled)) {
vty_out(vty, "--- PCE parameters ---\n");
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, " PCE info is disabled on this router\n");
}
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(OSPF_NODE, &router_info_area_cmd);
install_element(OSPF_NODE, &no_router_info_cmd);
install_element(OSPF_NODE, &pce_address_cmd);
install_element(OSPF_NODE, &no_pce_address_cmd);
install_element(OSPF_NODE, &pce_path_scope_cmd);
install_element(OSPF_NODE, &no_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);
install_element(OSPF_NODE, &no_pce_cap_flag_cmd);
return;
}
You can’t perform that action at this time.