| /* |
| * 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; |
| } |