mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-30 12:01:43 +00:00

The fields in the broadcast/p2p union struct in an isis circuit are initialized when the circuit goes up, but currently this step is skipped if the interface is passive. This can create problems if the circuit type (referred to as network type in the config) changes from broadcast to point-to-point. We can end up with the p2p neighbor pointer pointing at some garbage left by the broadcast struct in the union, which would then cause a segfault the first time we would dereference it - for example when building the lsp, or computing the SPF tree. compressed backtrace of a possible crash: #0 0x0000555555579a9c in lsp_build at frr/isisd/isis_lsp.c:1114 #1 0x000055555557a516 in lsp_regenerate at frr/isisd/isis_lsp.c:1301 #2 0x000055555557aa25 in lsp_refresh at frr/isisd/isis_lsp.c:1381 #3 0x00007ffff7b2622c in thread_call at frr/lib/thread.c:1549 #4 0x00007ffff7ad6df4 in frr_run at frr/lib/libfrr.c:1098 #5 0x000055555556b67f in main at frr/isisd/isis_main.c:272 isis_lsp.c: 1112 case CIRCUIT_T_P2P: { 1113 struct isis_adjacency *nei = circuit->u.p2p.neighbor; 1114 if (nei && nei->adj_state == ISIS_ADJ_UP Signed-off-by: Emanuele Di Pascale <emanuele@voltanet.io>
1519 lines
40 KiB
C
1519 lines
40 KiB
C
/*
|
|
* IS-IS Rout(e)ing protocol - isis_circuit.h
|
|
*
|
|
* Copyright (C) 2001,2002 Sampo Saaristo
|
|
* Tampere University of Technology
|
|
* Institute of Communications Engineering
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public Licenseas published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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>
|
|
#ifdef GNU_LINUX
|
|
#include <net/ethernet.h>
|
|
#else
|
|
#include <netinet/if_ether.h>
|
|
#endif
|
|
|
|
#include "log.h"
|
|
#include "memory.h"
|
|
#include "vrf.h"
|
|
#include "if.h"
|
|
#include "linklist.h"
|
|
#include "command.h"
|
|
#include "thread.h"
|
|
#include "vty.h"
|
|
#include "hash.h"
|
|
#include "prefix.h"
|
|
#include "stream.h"
|
|
#include "qobj.h"
|
|
#include "lib/northbound_cli.h"
|
|
|
|
#include "isisd/isis_constants.h"
|
|
#include "isisd/isis_common.h"
|
|
#include "isisd/isis_flags.h"
|
|
#include "isisd/isis_circuit.h"
|
|
#include "isisd/isis_lsp.h"
|
|
#include "isisd/isis_pdu.h"
|
|
#include "isisd/isis_network.h"
|
|
#include "isisd/isis_misc.h"
|
|
#include "isisd/isis_constants.h"
|
|
#include "isisd/isis_adjacency.h"
|
|
#include "isisd/isis_dr.h"
|
|
#include "isisd/isisd.h"
|
|
#include "isisd/isis_csm.h"
|
|
#include "isisd/isis_events.h"
|
|
#include "isisd/isis_te.h"
|
|
#include "isisd/isis_mt.h"
|
|
#include "isisd/isis_errors.h"
|
|
#include "isisd/isis_tx_queue.h"
|
|
#include "isisd/isis_nb.h"
|
|
#include "isisd/isis_ldp_sync.h"
|
|
|
|
DEFINE_QOBJ_TYPE(isis_circuit)
|
|
|
|
DEFINE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp))
|
|
|
|
/*
|
|
* Prototypes.
|
|
*/
|
|
int isis_if_new_hook(struct interface *);
|
|
int isis_if_delete_hook(struct interface *);
|
|
|
|
struct isis_circuit *isis_circuit_new(struct isis *isis)
|
|
{
|
|
struct isis_circuit *circuit;
|
|
int i;
|
|
|
|
circuit = XCALLOC(MTYPE_ISIS_CIRCUIT, sizeof(struct isis_circuit));
|
|
|
|
circuit->isis = isis;
|
|
/*
|
|
* Default values
|
|
*/
|
|
#ifndef FABRICD
|
|
circuit->is_type = yang_get_default_enum(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/circuit-type");
|
|
circuit->flags = 0;
|
|
|
|
circuit->pad_hellos = yang_get_default_bool(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/hello/padding");
|
|
circuit->hello_interval[0] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1");
|
|
circuit->hello_interval[1] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2");
|
|
circuit->hello_multiplier[0] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1");
|
|
circuit->hello_multiplier[1] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2");
|
|
circuit->csnp_interval[0] = yang_get_default_uint16(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1");
|
|
circuit->csnp_interval[1] = yang_get_default_uint16(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2");
|
|
circuit->psnp_interval[0] = yang_get_default_uint16(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1");
|
|
circuit->psnp_interval[1] = yang_get_default_uint16(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2");
|
|
circuit->priority[0] = yang_get_default_uint8(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/priority/level-1");
|
|
circuit->priority[1] = yang_get_default_uint8(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/priority/level-2");
|
|
circuit->metric[0] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/metric/level-1");
|
|
circuit->metric[1] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/metric/level-2");
|
|
circuit->te_metric[0] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/metric/level-1");
|
|
circuit->te_metric[1] = yang_get_default_uint32(
|
|
"/frr-interface:lib/interface/frr-isisd:isis/metric/level-2");
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
circuit->level_arg[i].level = i + 1;
|
|
circuit->level_arg[i].circuit = circuit;
|
|
}
|
|
#else
|
|
circuit->is_type = IS_LEVEL_1_AND_2;
|
|
circuit->flags = 0;
|
|
circuit->pad_hellos = 1;
|
|
for (i = 0; i < 2; i++) {
|
|
circuit->hello_interval[i] = DEFAULT_HELLO_INTERVAL;
|
|
circuit->hello_multiplier[i] = DEFAULT_HELLO_MULTIPLIER;
|
|
circuit->csnp_interval[i] = DEFAULT_CSNP_INTERVAL;
|
|
circuit->psnp_interval[i] = DEFAULT_PSNP_INTERVAL;
|
|
circuit->priority[i] = DEFAULT_PRIORITY;
|
|
circuit->metric[i] = DEFAULT_CIRCUIT_METRIC;
|
|
circuit->te_metric[i] = DEFAULT_CIRCUIT_METRIC;
|
|
circuit->level_arg[i].level = i + 1;
|
|
circuit->level_arg[i].circuit = circuit;
|
|
}
|
|
#endif /* ifndef FABRICD */
|
|
|
|
circuit_mt_init(circuit);
|
|
|
|
QOBJ_REG(circuit, isis_circuit);
|
|
|
|
return circuit;
|
|
}
|
|
|
|
void isis_circuit_del(struct isis_circuit *circuit)
|
|
{
|
|
if (!circuit)
|
|
return;
|
|
|
|
QOBJ_UNREG(circuit);
|
|
|
|
isis_circuit_if_unbind(circuit, circuit->interface);
|
|
|
|
circuit_mt_finish(circuit);
|
|
|
|
/* and lastly the circuit itself */
|
|
XFREE(MTYPE_ISIS_CIRCUIT, circuit);
|
|
|
|
return;
|
|
}
|
|
|
|
void isis_circuit_configure(struct isis_circuit *circuit,
|
|
struct isis_area *area)
|
|
{
|
|
assert(area);
|
|
circuit->area = area;
|
|
|
|
/*
|
|
* Whenever the is-type of an area is changed, the is-type of each
|
|
* circuit
|
|
* in that area is updated to a non-empty subset of the area is-type.
|
|
* Inversely, when configuring a new circuit, this property should be
|
|
* ensured as well.
|
|
*/
|
|
if (area->is_type != IS_LEVEL_1_AND_2)
|
|
circuit->is_type = area->is_type;
|
|
|
|
/*
|
|
* Add the circuit into area
|
|
*/
|
|
listnode_add(area->circuit_list, circuit);
|
|
|
|
circuit->idx = flags_get_index(&area->flags);
|
|
|
|
return;
|
|
}
|
|
|
|
void isis_circuit_deconfigure(struct isis_circuit *circuit,
|
|
struct isis_area *area)
|
|
{
|
|
/* Free the index of SRM and SSN flags */
|
|
flags_free_index(&area->flags, circuit->idx);
|
|
circuit->idx = 0;
|
|
/* Remove circuit from area */
|
|
assert(circuit->area == area);
|
|
listnode_delete(area->circuit_list, circuit);
|
|
circuit->area = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
struct isis_circuit *circuit_lookup_by_ifp(struct interface *ifp,
|
|
struct list *list)
|
|
{
|
|
struct isis_circuit *circuit = NULL;
|
|
struct listnode *node;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(list, node, circuit))
|
|
if (circuit->interface == ifp) {
|
|
assert(ifp->info == circuit);
|
|
return circuit;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp)
|
|
{
|
|
struct isis_area *area;
|
|
struct listnode *node;
|
|
struct isis_circuit *circuit;
|
|
struct isis *isis = NULL;
|
|
|
|
if (ifp->info)
|
|
return (struct isis_circuit *)ifp->info;
|
|
|
|
isis = isis_lookup_by_vrfid(ifp->vrf_id);
|
|
if (isis == NULL) {
|
|
zlog_warn(" %s : ISIS routing instance not found", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if (isis->area_list) {
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
|
|
circuit =
|
|
circuit_lookup_by_ifp(ifp, area->circuit_list);
|
|
if (circuit)
|
|
return circuit;
|
|
}
|
|
}
|
|
return circuit_lookup_by_ifp(ifp, isis->init_circ_list);
|
|
}
|
|
|
|
void isis_circuit_add_addr(struct isis_circuit *circuit,
|
|
struct connected *connected)
|
|
{
|
|
struct listnode *node;
|
|
struct prefix_ipv4 *ipv4;
|
|
struct prefix_ipv6 *ipv6;
|
|
|
|
if (connected->address->family == AF_INET) {
|
|
uint32_t addr = connected->address->u.prefix4.s_addr;
|
|
addr = ntohl(addr);
|
|
if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr)
|
|
|| IPV4_LINKLOCAL(addr))
|
|
return;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ipv4))
|
|
if (prefix_same((struct prefix *)ipv4,
|
|
connected->address))
|
|
return;
|
|
|
|
ipv4 = prefix_ipv4_new();
|
|
ipv4->prefixlen = connected->address->prefixlen;
|
|
ipv4->prefix = connected->address->u.prefix4;
|
|
listnode_add(circuit->ip_addrs, ipv4);
|
|
|
|
/* Update Local IP address parameter if MPLS TE is enable */
|
|
if (circuit->ext && circuit->area
|
|
&& IS_MPLS_TE(circuit->area->mta)) {
|
|
circuit->ext->local_addr.s_addr = ipv4->prefix.s_addr;
|
|
SET_SUBTLV(circuit->ext, EXT_LOCAL_ADDR);
|
|
}
|
|
|
|
if (circuit->area)
|
|
lsp_regenerate_schedule(circuit->area, circuit->is_type,
|
|
0);
|
|
|
|
#ifdef EXTREME_DEBUG
|
|
zlog_debug("Added IP address %pFX to circuit %s",
|
|
connected->address, circuit->interface->name);
|
|
#endif /* EXTREME_DEBUG */
|
|
}
|
|
if (connected->address->family == AF_INET6) {
|
|
if (IN6_IS_ADDR_LOOPBACK(&connected->address->u.prefix6))
|
|
return;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ipv6))
|
|
if (prefix_same((struct prefix *)ipv6,
|
|
connected->address))
|
|
return;
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ipv6))
|
|
if (prefix_same((struct prefix *)ipv6,
|
|
connected->address))
|
|
return;
|
|
|
|
ipv6 = prefix_ipv6_new();
|
|
ipv6->prefixlen = connected->address->prefixlen;
|
|
ipv6->prefix = connected->address->u.prefix6;
|
|
|
|
if (IN6_IS_ADDR_LINKLOCAL(&ipv6->prefix))
|
|
listnode_add(circuit->ipv6_link, ipv6);
|
|
else
|
|
listnode_add(circuit->ipv6_non_link, ipv6);
|
|
if (circuit->area)
|
|
lsp_regenerate_schedule(circuit->area, circuit->is_type,
|
|
0);
|
|
|
|
#ifdef EXTREME_DEBUG
|
|
zlog_debug("Added IPv6 address %pFX to circuit %s",
|
|
connected->address, circuit->interface->name);
|
|
#endif /* EXTREME_DEBUG */
|
|
}
|
|
return;
|
|
}
|
|
|
|
void isis_circuit_del_addr(struct isis_circuit *circuit,
|
|
struct connected *connected)
|
|
{
|
|
struct prefix_ipv4 *ipv4, *ip = NULL;
|
|
struct listnode *node;
|
|
struct prefix_ipv6 *ipv6, *ip6 = NULL;
|
|
int found = 0;
|
|
|
|
if (connected->address->family == AF_INET) {
|
|
ipv4 = prefix_ipv4_new();
|
|
ipv4->prefixlen = connected->address->prefixlen;
|
|
ipv4->prefix = connected->address->u.prefix4;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ip))
|
|
if (prefix_same((struct prefix *)ip,
|
|
(struct prefix *)ipv4))
|
|
break;
|
|
|
|
if (ip) {
|
|
listnode_delete(circuit->ip_addrs, ip);
|
|
prefix_ipv4_free(&ip);
|
|
if (circuit->area)
|
|
lsp_regenerate_schedule(circuit->area,
|
|
circuit->is_type, 0);
|
|
} else {
|
|
zlog_warn(
|
|
"Nonexistent ip address %pFX removal attempt from circuit %s",
|
|
connected->address, circuit->interface->name);
|
|
zlog_warn("Current ip addresses on %s:",
|
|
circuit->interface->name);
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
|
|
ip)) {
|
|
zlog_warn(" %pFX", ip);
|
|
}
|
|
zlog_warn("End of addresses");
|
|
}
|
|
|
|
prefix_ipv4_free(&ipv4);
|
|
}
|
|
if (connected->address->family == AF_INET6) {
|
|
ipv6 = prefix_ipv6_new();
|
|
ipv6->prefixlen = connected->address->prefixlen;
|
|
ipv6->prefix = connected->address->u.prefix6;
|
|
|
|
if (IN6_IS_ADDR_LINKLOCAL(&ipv6->prefix)) {
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
|
|
ip6)) {
|
|
if (prefix_same((struct prefix *)ip6,
|
|
(struct prefix *)ipv6))
|
|
break;
|
|
}
|
|
if (ip6) {
|
|
listnode_delete(circuit->ipv6_link, ip6);
|
|
prefix_ipv6_free(&ip6);
|
|
found = 1;
|
|
}
|
|
} else {
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
|
|
ip6)) {
|
|
if (prefix_same((struct prefix *)ip6,
|
|
(struct prefix *)ipv6))
|
|
break;
|
|
}
|
|
if (ip6) {
|
|
listnode_delete(circuit->ipv6_non_link, ip6);
|
|
prefix_ipv6_free(&ip6);
|
|
found = 1;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
zlog_warn(
|
|
"Nonexistent ip address %pFX removal attempt from circuit %s",
|
|
connected->address, circuit->interface->name);
|
|
zlog_warn("Current ip addresses on %s:",
|
|
circuit->interface->name);
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
|
|
ip6))
|
|
zlog_warn(" %pFX", (struct prefix *)ip6);
|
|
zlog_warn(" -----");
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
|
|
ip6))
|
|
zlog_warn(" %pFX", (struct prefix *)ip6);
|
|
zlog_warn("End of addresses");
|
|
} else if (circuit->area)
|
|
lsp_regenerate_schedule(circuit->area, circuit->is_type,
|
|
0);
|
|
|
|
prefix_ipv6_free(&ipv6);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static uint8_t isis_circuit_id_gen(struct isis *isis, struct interface *ifp)
|
|
{
|
|
/* Circuit ids MUST be unique for any broadcast circuits. Otherwise,
|
|
* Pseudo-Node LSPs cannot be generated correctly.
|
|
*
|
|
* Currently, allocate one circuit ID for any circuit, limiting the total
|
|
* numer of circuits IS-IS can run on to 255.
|
|
*
|
|
* We should revisit this when implementing 3-way adjacencies for p2p, since
|
|
* we then have extended interface IDs available.
|
|
*/
|
|
uint8_t id = ifp->ifindex;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
if (id && !_ISIS_CHECK_FLAG(isis->circuit_ids_used, id))
|
|
break;
|
|
id++;
|
|
}
|
|
|
|
if (i == 256) {
|
|
zlog_warn("Could not allocate a circuit id for '%s'",
|
|
ifp->name);
|
|
return 0;
|
|
}
|
|
|
|
_ISIS_SET_FLAG(isis->circuit_ids_used, id);
|
|
return id;
|
|
}
|
|
|
|
void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp)
|
|
{
|
|
struct listnode *node, *nnode;
|
|
struct connected *conn;
|
|
|
|
isis_circuit_if_bind(circuit, ifp);
|
|
|
|
if (if_is_broadcast(ifp)) {
|
|
if (fabricd || circuit->circ_type_config == CIRCUIT_T_P2P)
|
|
circuit->circ_type = CIRCUIT_T_P2P;
|
|
else
|
|
circuit->circ_type = CIRCUIT_T_BROADCAST;
|
|
} else if (if_is_pointopoint(ifp)) {
|
|
circuit->circ_type = CIRCUIT_T_P2P;
|
|
} else if (if_is_loopback(ifp)) {
|
|
circuit->circ_type = CIRCUIT_T_LOOPBACK;
|
|
circuit->is_passive = 1;
|
|
} else {
|
|
/* It's normal in case of loopback etc. */
|
|
if (IS_DEBUG_EVENTS)
|
|
zlog_debug("isis_circuit_if_add: unsupported media");
|
|
circuit->circ_type = CIRCUIT_T_UNKNOWN;
|
|
}
|
|
|
|
circuit->ip_addrs = list_new();
|
|
circuit->ipv6_link = list_new();
|
|
circuit->ipv6_non_link = list_new();
|
|
|
|
for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn))
|
|
isis_circuit_add_addr(circuit, conn);
|
|
|
|
}
|
|
|
|
void isis_circuit_if_del(struct isis_circuit *circuit, struct interface *ifp)
|
|
{
|
|
struct listnode *node, *nnode;
|
|
struct connected *conn;
|
|
|
|
assert(circuit->interface == ifp);
|
|
|
|
/* destroy addresses */
|
|
for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn))
|
|
isis_circuit_del_addr(circuit, conn);
|
|
|
|
if (circuit->ip_addrs) {
|
|
assert(listcount(circuit->ip_addrs) == 0);
|
|
list_delete(&circuit->ip_addrs);
|
|
}
|
|
|
|
if (circuit->ipv6_link) {
|
|
assert(listcount(circuit->ipv6_link) == 0);
|
|
list_delete(&circuit->ipv6_link);
|
|
}
|
|
|
|
if (circuit->ipv6_non_link) {
|
|
assert(listcount(circuit->ipv6_non_link) == 0);
|
|
list_delete(&circuit->ipv6_non_link);
|
|
}
|
|
|
|
circuit->circ_type = CIRCUIT_T_UNKNOWN;
|
|
}
|
|
|
|
void isis_circuit_if_bind(struct isis_circuit *circuit, struct interface *ifp)
|
|
{
|
|
assert(circuit != NULL);
|
|
assert(ifp != NULL);
|
|
if (circuit->interface)
|
|
assert(circuit->interface == ifp);
|
|
else
|
|
circuit->interface = ifp;
|
|
if (ifp->info)
|
|
assert(ifp->info == circuit);
|
|
else
|
|
ifp->info = circuit;
|
|
}
|
|
|
|
void isis_circuit_if_unbind(struct isis_circuit *circuit, struct interface *ifp)
|
|
{
|
|
assert(circuit != NULL);
|
|
assert(ifp != NULL);
|
|
assert(circuit->interface == ifp);
|
|
assert(ifp->info == circuit);
|
|
circuit->interface = NULL;
|
|
ifp->info = NULL;
|
|
}
|
|
|
|
static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit,
|
|
int is_set)
|
|
{
|
|
struct isis_area *area;
|
|
struct isis_lsp *lsp;
|
|
int level;
|
|
|
|
assert(circuit);
|
|
area = circuit->area;
|
|
assert(area);
|
|
for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
|
|
if (!(level & circuit->is_type))
|
|
continue;
|
|
|
|
if (!lspdb_count(&area->lspdb[level - 1]))
|
|
continue;
|
|
|
|
frr_each (lspdb, &area->lspdb[level - 1], lsp) {
|
|
if (is_set) {
|
|
isis_tx_queue_add(circuit->tx_queue, lsp,
|
|
TX_LSP_NORMAL);
|
|
} else {
|
|
isis_tx_queue_del(circuit->tx_queue, lsp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t isis_circuit_pdu_size(struct isis_circuit *circuit)
|
|
{
|
|
return ISO_MTU(circuit);
|
|
}
|
|
|
|
void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream)
|
|
{
|
|
size_t stream_size = isis_circuit_pdu_size(circuit);
|
|
|
|
if (!*stream) {
|
|
*stream = stream_new(stream_size);
|
|
} else {
|
|
if (STREAM_SIZE(*stream) != stream_size)
|
|
stream_resize_inplace(stream, stream_size);
|
|
stream_reset(*stream);
|
|
}
|
|
}
|
|
|
|
void isis_circuit_prepare(struct isis_circuit *circuit)
|
|
{
|
|
#if ISIS_METHOD != ISIS_METHOD_DLPI
|
|
thread_add_read(master, isis_receive, circuit, circuit->fd,
|
|
&circuit->t_read);
|
|
#else
|
|
thread_add_timer_msec(master, isis_receive, circuit,
|
|
listcount(circuit->area->circuit_list) * 100,
|
|
&circuit->t_read);
|
|
#endif
|
|
}
|
|
|
|
int isis_circuit_up(struct isis_circuit *circuit)
|
|
{
|
|
int retv;
|
|
|
|
/* Set the flags for all the lsps of the circuit. */
|
|
isis_circuit_update_all_srmflags(circuit, 1);
|
|
|
|
if (circuit->state == C_STATE_UP)
|
|
return ISIS_OK;
|
|
|
|
if (circuit->is_passive) {
|
|
/* make sure the union fields are initialized, else we
|
|
* could end with garbage values from a previous circuit
|
|
* type, which would then cause a segfault when building
|
|
* LSPs or computing the SPF tree
|
|
*/
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
|
|
circuit->u.bc.adjdb[0] = list_new();
|
|
circuit->u.bc.adjdb[1] = list_new();
|
|
} else if (circuit->circ_type == CIRCUIT_T_P2P) {
|
|
circuit->u.p2p.neighbor = NULL;
|
|
}
|
|
return ISIS_OK;
|
|
}
|
|
|
|
if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit)) {
|
|
flog_err(
|
|
EC_ISIS_CONFIG,
|
|
"Interface MTU %zu on %s is too low to support area lsp mtu %u!",
|
|
isis_circuit_pdu_size(circuit),
|
|
circuit->interface->name, circuit->area->lsp_mtu);
|
|
isis_circuit_update_all_srmflags(circuit, 0);
|
|
return ISIS_ERROR;
|
|
}
|
|
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
|
|
circuit->circuit_id =
|
|
isis_circuit_id_gen(circuit->isis, circuit->interface);
|
|
if (!circuit->circuit_id) {
|
|
flog_err(
|
|
EC_ISIS_CONFIG,
|
|
"There are already 255 broadcast circuits active!");
|
|
return ISIS_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Get the Hardware Address
|
|
*/
|
|
if (circuit->interface->hw_addr_len != ETH_ALEN) {
|
|
zlog_warn("unsupported link layer");
|
|
} else {
|
|
memcpy(circuit->u.bc.snpa, circuit->interface->hw_addr,
|
|
ETH_ALEN);
|
|
}
|
|
#ifdef EXTREME_DEGUG
|
|
zlog_debug("isis_circuit_if_add: if_id %d, isomtu %d snpa %s",
|
|
circuit->interface->ifindex, ISO_MTU(circuit),
|
|
snpa_print(circuit->u.bc.snpa));
|
|
#endif /* EXTREME_DEBUG */
|
|
|
|
circuit->u.bc.adjdb[0] = list_new();
|
|
circuit->u.bc.adjdb[1] = list_new();
|
|
|
|
/*
|
|
* ISO 10589 - 8.4.1 Enabling of broadcast circuits
|
|
*/
|
|
|
|
/* initilizing the hello sending threads
|
|
* for a broadcast IF
|
|
*/
|
|
|
|
/* 8.4.1 a) commence sending of IIH PDUs */
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
|
|
if (!(circuit->is_type & level))
|
|
continue;
|
|
|
|
send_hello_sched(circuit, level, TRIGGERED_IIH_DELAY);
|
|
circuit->u.bc.lan_neighs[level - 1] = list_new();
|
|
|
|
thread_add_timer(master, isis_run_dr,
|
|
&circuit->level_arg[level - 1],
|
|
2 * circuit->hello_interval[level - 1],
|
|
&circuit->u.bc.t_run_dr[level - 1]);
|
|
}
|
|
|
|
/* 8.4.1 b) FIXME: solicit ES - 8.4.6 */
|
|
/* 8.4.1 c) FIXME: listen for ESH PDUs */
|
|
} else if (circuit->circ_type == CIRCUIT_T_P2P) {
|
|
/* initializing the hello send threads
|
|
* for a ptp IF
|
|
*/
|
|
circuit->u.p2p.neighbor = NULL;
|
|
send_hello_sched(circuit, 0, TRIGGERED_IIH_DELAY);
|
|
}
|
|
|
|
/* initializing PSNP timers */
|
|
if (circuit->is_type & IS_LEVEL_1)
|
|
thread_add_timer(
|
|
master, send_l1_psnp, circuit,
|
|
isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
|
|
&circuit->t_send_psnp[0]);
|
|
|
|
if (circuit->is_type & IS_LEVEL_2)
|
|
thread_add_timer(
|
|
master, send_l2_psnp, circuit,
|
|
isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
|
|
&circuit->t_send_psnp[1]);
|
|
|
|
/* unified init for circuits; ignore warnings below this level */
|
|
retv = isis_sock_init(circuit);
|
|
if (retv != ISIS_OK) {
|
|
isis_circuit_down(circuit);
|
|
return retv;
|
|
}
|
|
|
|
/* initialize the circuit streams after opening connection */
|
|
isis_circuit_stream(circuit, &circuit->rcv_stream);
|
|
isis_circuit_stream(circuit, &circuit->snd_stream);
|
|
|
|
isis_circuit_prepare(circuit);
|
|
|
|
circuit->tx_queue = isis_tx_queue_new(circuit, send_lsp);
|
|
|
|
#ifndef FABRICD
|
|
/* send northbound notification */
|
|
isis_notif_if_state_change(circuit, false);
|
|
#endif /* ifndef FABRICD */
|
|
|
|
return ISIS_OK;
|
|
}
|
|
|
|
void isis_circuit_down(struct isis_circuit *circuit)
|
|
{
|
|
#ifndef FABRICD
|
|
/* send northbound notification */
|
|
isis_notif_if_state_change(circuit, true);
|
|
#endif /* ifndef FABRICD */
|
|
|
|
/* log adjacency changes if configured to do so */
|
|
if (circuit->area->log_adj_changes) {
|
|
struct isis_adjacency *adj = NULL;
|
|
if (circuit->circ_type == CIRCUIT_T_P2P) {
|
|
adj = circuit->u.p2p.neighbor;
|
|
if (adj)
|
|
isis_log_adj_change(
|
|
adj, adj->adj_state, ISIS_ADJ_DOWN,
|
|
"circuit is being brought down");
|
|
} else if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
|
|
struct list *adj_list;
|
|
struct listnode *node;
|
|
if (circuit->u.bc.adjdb[0]) {
|
|
adj_list = list_new();
|
|
isis_adj_build_up_list(circuit->u.bc.adjdb[0],
|
|
adj_list);
|
|
for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj))
|
|
isis_log_adj_change(
|
|
adj, adj->adj_state,
|
|
ISIS_ADJ_DOWN,
|
|
"circuit is being brought down");
|
|
list_delete(&adj_list);
|
|
}
|
|
if (circuit->u.bc.adjdb[1]) {
|
|
adj_list = list_new();
|
|
isis_adj_build_up_list(circuit->u.bc.adjdb[1],
|
|
adj_list);
|
|
for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj))
|
|
isis_log_adj_change(
|
|
adj, adj->adj_state,
|
|
ISIS_ADJ_DOWN,
|
|
"circuit is being brought down");
|
|
list_delete(&adj_list);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear the flags for all the lsps of the circuit. */
|
|
isis_circuit_update_all_srmflags(circuit, 0);
|
|
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
|
|
/* destroy neighbour lists */
|
|
if (circuit->u.bc.lan_neighs[0]) {
|
|
list_delete(&circuit->u.bc.lan_neighs[0]);
|
|
circuit->u.bc.lan_neighs[0] = NULL;
|
|
}
|
|
if (circuit->u.bc.lan_neighs[1]) {
|
|
list_delete(&circuit->u.bc.lan_neighs[1]);
|
|
circuit->u.bc.lan_neighs[1] = NULL;
|
|
}
|
|
/* destroy adjacency databases */
|
|
if (circuit->u.bc.adjdb[0]) {
|
|
circuit->u.bc.adjdb[0]->del = isis_delete_adj;
|
|
list_delete(&circuit->u.bc.adjdb[0]);
|
|
circuit->u.bc.adjdb[0] = NULL;
|
|
}
|
|
if (circuit->u.bc.adjdb[1]) {
|
|
circuit->u.bc.adjdb[1]->del = isis_delete_adj;
|
|
list_delete(&circuit->u.bc.adjdb[1]);
|
|
circuit->u.bc.adjdb[1] = NULL;
|
|
}
|
|
if (circuit->u.bc.is_dr[0]) {
|
|
isis_dr_resign(circuit, 1);
|
|
circuit->u.bc.is_dr[0] = 0;
|
|
}
|
|
memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
|
|
if (circuit->u.bc.is_dr[1]) {
|
|
isis_dr_resign(circuit, 2);
|
|
circuit->u.bc.is_dr[1] = 0;
|
|
}
|
|
memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
|
|
memset(circuit->u.bc.snpa, 0, ETH_ALEN);
|
|
|
|
thread_cancel(&circuit->u.bc.t_send_lan_hello[0]);
|
|
thread_cancel(&circuit->u.bc.t_send_lan_hello[1]);
|
|
thread_cancel(&circuit->u.bc.t_run_dr[0]);
|
|
thread_cancel(&circuit->u.bc.t_run_dr[1]);
|
|
thread_cancel(&circuit->u.bc.t_refresh_pseudo_lsp[0]);
|
|
thread_cancel(&circuit->u.bc.t_refresh_pseudo_lsp[1]);
|
|
circuit->lsp_regenerate_pending[0] = 0;
|
|
circuit->lsp_regenerate_pending[1] = 0;
|
|
|
|
_ISIS_CLEAR_FLAG(circuit->isis->circuit_ids_used,
|
|
circuit->circuit_id);
|
|
circuit->circuit_id = 0;
|
|
} else if (circuit->circ_type == CIRCUIT_T_P2P) {
|
|
isis_delete_adj(circuit->u.p2p.neighbor);
|
|
circuit->u.p2p.neighbor = NULL;
|
|
thread_cancel(&circuit->u.p2p.t_send_p2p_hello);
|
|
}
|
|
|
|
/* Cancel all active threads */
|
|
thread_cancel(&circuit->t_send_csnp[0]);
|
|
thread_cancel(&circuit->t_send_csnp[1]);
|
|
thread_cancel(&circuit->t_send_psnp[0]);
|
|
thread_cancel(&circuit->t_send_psnp[1]);
|
|
thread_cancel(&circuit->t_read);
|
|
|
|
if (circuit->tx_queue) {
|
|
isis_tx_queue_free(circuit->tx_queue);
|
|
circuit->tx_queue = NULL;
|
|
}
|
|
|
|
/* send one gratuitous hello to spead up convergence */
|
|
if (circuit->state == C_STATE_UP) {
|
|
if (circuit->is_type & IS_LEVEL_1)
|
|
send_hello(circuit, IS_LEVEL_1);
|
|
if (circuit->is_type & IS_LEVEL_2)
|
|
send_hello(circuit, IS_LEVEL_2);
|
|
}
|
|
|
|
circuit->upadjcount[0] = 0;
|
|
circuit->upadjcount[1] = 0;
|
|
|
|
/* close the socket */
|
|
if (circuit->fd) {
|
|
close(circuit->fd);
|
|
circuit->fd = 0;
|
|
}
|
|
|
|
if (circuit->rcv_stream != NULL) {
|
|
stream_free(circuit->rcv_stream);
|
|
circuit->rcv_stream = NULL;
|
|
}
|
|
|
|
if (circuit->snd_stream != NULL) {
|
|
stream_free(circuit->snd_stream);
|
|
circuit->snd_stream = NULL;
|
|
}
|
|
|
|
thread_cancel_event(master, circuit);
|
|
|
|
return;
|
|
}
|
|
|
|
void circuit_update_nlpids(struct isis_circuit *circuit)
|
|
{
|
|
circuit->nlpids.count = 0;
|
|
|
|
if (circuit->ip_router) {
|
|
circuit->nlpids.nlpids[0] = NLPID_IP;
|
|
circuit->nlpids.count++;
|
|
}
|
|
if (circuit->ipv6_router) {
|
|
circuit->nlpids.nlpids[circuit->nlpids.count] = NLPID_IPV6;
|
|
circuit->nlpids.count++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty,
|
|
char detail)
|
|
{
|
|
if (detail == ISIS_UI_LEVEL_BRIEF) {
|
|
vty_out(vty, " %-12s", circuit->interface->name);
|
|
vty_out(vty, "0x%-7x", circuit->circuit_id);
|
|
vty_out(vty, "%-9s", circuit_state2string(circuit->state));
|
|
vty_out(vty, "%-9s", circuit_type2string(circuit->circ_type));
|
|
vty_out(vty, "%-9s", circuit_t2string(circuit->is_type));
|
|
vty_out(vty, "\n");
|
|
}
|
|
|
|
if (detail == ISIS_UI_LEVEL_DETAIL) {
|
|
struct listnode *node;
|
|
struct prefix *ip_addr;
|
|
|
|
vty_out(vty, " Interface: %s", circuit->interface->name);
|
|
vty_out(vty, ", State: %s",
|
|
circuit_state2string(circuit->state));
|
|
if (circuit->is_passive)
|
|
vty_out(vty, ", Passive");
|
|
else
|
|
vty_out(vty, ", Active");
|
|
vty_out(vty, ", Circuit Id: 0x%x", circuit->circuit_id);
|
|
vty_out(vty, "\n");
|
|
vty_out(vty, " Type: %s",
|
|
circuit_type2string(circuit->circ_type));
|
|
vty_out(vty, ", Level: %s", circuit_t2string(circuit->is_type));
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
|
|
vty_out(vty, ", SNPA: %-10s",
|
|
snpa_print(circuit->u.bc.snpa));
|
|
vty_out(vty, "\n");
|
|
if (circuit->is_type & IS_LEVEL_1) {
|
|
vty_out(vty, " Level-1 Information:\n");
|
|
if (circuit->area->newmetric)
|
|
vty_out(vty, " Metric: %d",
|
|
circuit->te_metric[0]);
|
|
else
|
|
vty_out(vty, " Metric: %d",
|
|
circuit->metric[0]);
|
|
if (!circuit->is_passive) {
|
|
vty_out(vty, ", Active neighbors: %u\n",
|
|
circuit->upadjcount[0]);
|
|
vty_out(vty,
|
|
" Hello interval: %u, Holddown count: %u %s\n",
|
|
circuit->hello_interval[0],
|
|
circuit->hello_multiplier[0],
|
|
(circuit->pad_hellos ? "(pad)"
|
|
: "(no-pad)"));
|
|
vty_out(vty,
|
|
" CNSP interval: %u, PSNP interval: %u\n",
|
|
circuit->csnp_interval[0],
|
|
circuit->psnp_interval[0]);
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
|
|
vty_out(vty,
|
|
" LAN Priority: %u, %s\n",
|
|
circuit->priority[0],
|
|
(circuit->u.bc.is_dr[0]
|
|
? "is DIS"
|
|
: "is not DIS"));
|
|
} else {
|
|
vty_out(vty, "\n");
|
|
}
|
|
}
|
|
if (circuit->is_type & IS_LEVEL_2) {
|
|
vty_out(vty, " Level-2 Information:\n");
|
|
if (circuit->area->newmetric)
|
|
vty_out(vty, " Metric: %d",
|
|
circuit->te_metric[1]);
|
|
else
|
|
vty_out(vty, " Metric: %d",
|
|
circuit->metric[1]);
|
|
if (!circuit->is_passive) {
|
|
vty_out(vty, ", Active neighbors: %u\n",
|
|
circuit->upadjcount[1]);
|
|
vty_out(vty,
|
|
" Hello interval: %u, Holddown count: %u %s\n",
|
|
circuit->hello_interval[1],
|
|
circuit->hello_multiplier[1],
|
|
(circuit->pad_hellos ? "(pad)"
|
|
: "(no-pad)"));
|
|
vty_out(vty,
|
|
" CNSP interval: %u, PSNP interval: %u\n",
|
|
circuit->csnp_interval[1],
|
|
circuit->psnp_interval[1]);
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
|
|
vty_out(vty,
|
|
" LAN Priority: %u, %s\n",
|
|
circuit->priority[1],
|
|
(circuit->u.bc.is_dr[1]
|
|
? "is DIS"
|
|
: "is not DIS"));
|
|
} else {
|
|
vty_out(vty, "\n");
|
|
}
|
|
}
|
|
if (circuit->ip_addrs && listcount(circuit->ip_addrs) > 0) {
|
|
vty_out(vty, " IP Prefix(es):\n");
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
|
|
ip_addr))
|
|
vty_out(vty, " %pFX\n", ip_addr);
|
|
}
|
|
if (circuit->ipv6_link && listcount(circuit->ipv6_link) > 0) {
|
|
vty_out(vty, " IPv6 Link-Locals:\n");
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
|
|
ip_addr))
|
|
vty_out(vty, " %pFX\n", ip_addr);
|
|
}
|
|
if (circuit->ipv6_non_link
|
|
&& listcount(circuit->ipv6_non_link) > 0) {
|
|
vty_out(vty, " IPv6 Prefixes:\n");
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
|
|
ip_addr))
|
|
vty_out(vty, " %pFX\n", ip_addr);
|
|
}
|
|
|
|
vty_out(vty, "\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
#ifdef FABRICD
|
|
DEFINE_HOOK(isis_circuit_config_write,
|
|
(struct isis_circuit *circuit, struct vty *vty),
|
|
(circuit, vty))
|
|
|
|
static int isis_interface_config_write(struct vty *vty)
|
|
{
|
|
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
|
int write = 0;
|
|
struct listnode *node;
|
|
struct interface *ifp;
|
|
struct isis_area *area;
|
|
struct isis_circuit *circuit;
|
|
int i;
|
|
struct isis *isis = NULL;
|
|
|
|
isis = isis_lookup_by_vrfid(vrf->vrf_id);
|
|
|
|
if (isis == NULL) {
|
|
vty_out(vty, "ISIS routing instance not found");
|
|
return 0;
|
|
}
|
|
|
|
FOR_ALL_INTERFACES (vrf, ifp) {
|
|
/* IF name */
|
|
vty_frame(vty, "interface %s\n", ifp->name);
|
|
write++;
|
|
/* IF desc */
|
|
if (ifp->desc) {
|
|
vty_out(vty, " description %s\n", ifp->desc);
|
|
write++;
|
|
}
|
|
/* ISIS Circuit */
|
|
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
|
|
circuit =
|
|
circuit_lookup_by_ifp(ifp, area->circuit_list);
|
|
if (circuit == NULL)
|
|
continue;
|
|
if (circuit->ip_router) {
|
|
vty_out(vty, " ip router " PROTO_NAME " %s\n",
|
|
area->area_tag);
|
|
write++;
|
|
}
|
|
if (circuit->is_passive) {
|
|
vty_out(vty, " " PROTO_NAME " passive\n");
|
|
write++;
|
|
}
|
|
if (circuit->circ_type_config == CIRCUIT_T_P2P) {
|
|
vty_out(vty, " " PROTO_NAME " network point-to-point\n");
|
|
write++;
|
|
}
|
|
if (circuit->ipv6_router) {
|
|
vty_out(vty, " ipv6 router " PROTO_NAME " %s\n",
|
|
area->area_tag);
|
|
write++;
|
|
}
|
|
|
|
/* ISIS - circuit type */
|
|
if (!fabricd) {
|
|
if (circuit->is_type == IS_LEVEL_1) {
|
|
vty_out(vty, " " PROTO_NAME " circuit-type level-1\n");
|
|
write++;
|
|
} else {
|
|
if (circuit->is_type == IS_LEVEL_2) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " circuit-type level-2-only\n");
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ISIS - CSNP interval */
|
|
if (circuit->csnp_interval[0]
|
|
== circuit->csnp_interval[1]) {
|
|
if (circuit->csnp_interval[0]
|
|
!= DEFAULT_CSNP_INTERVAL) {
|
|
vty_out(vty, " " PROTO_NAME " csnp-interval %d\n",
|
|
circuit->csnp_interval[0]);
|
|
write++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 2; i++) {
|
|
if (circuit->csnp_interval[i]
|
|
!= DEFAULT_CSNP_INTERVAL) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " csnp-interval %d level-%d\n",
|
|
circuit->csnp_interval
|
|
[i],
|
|
i + 1);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ISIS - PSNP interval */
|
|
if (circuit->psnp_interval[0]
|
|
== circuit->psnp_interval[1]) {
|
|
if (circuit->psnp_interval[0]
|
|
!= DEFAULT_PSNP_INTERVAL) {
|
|
vty_out(vty, " " PROTO_NAME " psnp-interval %d\n",
|
|
circuit->psnp_interval[0]);
|
|
write++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 2; i++) {
|
|
if (circuit->psnp_interval[i]
|
|
!= DEFAULT_PSNP_INTERVAL) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " psnp-interval %d level-%d\n",
|
|
circuit->psnp_interval
|
|
[i],
|
|
i + 1);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ISIS - Hello padding - Defaults to true so only
|
|
* display if false */
|
|
if (circuit->pad_hellos == 0) {
|
|
vty_out(vty, " no " PROTO_NAME " hello padding\n");
|
|
write++;
|
|
}
|
|
|
|
if (circuit->disable_threeway_adj) {
|
|
vty_out(vty, " no isis three-way-handshake\n");
|
|
write++;
|
|
}
|
|
|
|
/* ISIS - Hello interval */
|
|
if (circuit->hello_interval[0]
|
|
== circuit->hello_interval[1]) {
|
|
if (circuit->hello_interval[0]
|
|
!= DEFAULT_HELLO_INTERVAL) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " hello-interval %d\n",
|
|
circuit->hello_interval[0]);
|
|
write++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 2; i++) {
|
|
if (circuit->hello_interval[i]
|
|
!= DEFAULT_HELLO_INTERVAL) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " hello-interval %d level-%d\n",
|
|
circuit->hello_interval
|
|
[i],
|
|
i + 1);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ISIS - Hello Multiplier */
|
|
if (circuit->hello_multiplier[0]
|
|
== circuit->hello_multiplier[1]) {
|
|
if (circuit->hello_multiplier[0]
|
|
!= DEFAULT_HELLO_MULTIPLIER) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " hello-multiplier %d\n",
|
|
circuit->hello_multiplier[0]);
|
|
write++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 2; i++) {
|
|
if (circuit->hello_multiplier[i]
|
|
!= DEFAULT_HELLO_MULTIPLIER) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " hello-multiplier %d level-%d\n",
|
|
circuit->hello_multiplier
|
|
[i],
|
|
i + 1);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ISIS - Priority */
|
|
if (circuit->priority[0] == circuit->priority[1]) {
|
|
if (circuit->priority[0] != DEFAULT_PRIORITY) {
|
|
vty_out(vty, " " PROTO_NAME " priority %d\n",
|
|
circuit->priority[0]);
|
|
write++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 2; i++) {
|
|
if (circuit->priority[i]
|
|
!= DEFAULT_PRIORITY) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " priority %d level-%d\n",
|
|
circuit->priority[i],
|
|
i + 1);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ISIS - Metric */
|
|
if (circuit->te_metric[0] == circuit->te_metric[1]) {
|
|
if (circuit->te_metric[0]
|
|
!= DEFAULT_CIRCUIT_METRIC) {
|
|
vty_out(vty, " " PROTO_NAME " metric %d\n",
|
|
circuit->te_metric[0]);
|
|
write++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 2; i++) {
|
|
if (circuit->te_metric[i]
|
|
!= DEFAULT_CIRCUIT_METRIC) {
|
|
vty_out(vty,
|
|
" " PROTO_NAME " metric %d level-%d\n",
|
|
circuit->te_metric[i],
|
|
i + 1);
|
|
write++;
|
|
}
|
|
}
|
|
}
|
|
if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) {
|
|
vty_out(vty, " " PROTO_NAME " password md5 %s\n",
|
|
circuit->passwd.passwd);
|
|
write++;
|
|
} else if (circuit->passwd.type
|
|
== ISIS_PASSWD_TYPE_CLEARTXT) {
|
|
vty_out(vty, " " PROTO_NAME " password clear %s\n",
|
|
circuit->passwd.passwd);
|
|
write++;
|
|
}
|
|
write += hook_call(isis_circuit_config_write,
|
|
circuit, vty);
|
|
}
|
|
vty_endframe(vty, "!\n");
|
|
}
|
|
|
|
return write;
|
|
}
|
|
#else
|
|
static int isis_interface_config_write(struct vty *vty)
|
|
{
|
|
struct vrf *vrf = NULL;
|
|
int write = 0;
|
|
|
|
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
|
|
struct interface *ifp;
|
|
|
|
FOR_ALL_INTERFACES (vrf, ifp) {
|
|
struct lyd_node *dnode;
|
|
dnode = yang_dnode_get(
|
|
running_config->dnode,
|
|
"/frr-interface:lib/interface[name='%s'][vrf='%s']",
|
|
ifp->name, vrf->name);
|
|
if (dnode == NULL)
|
|
continue;
|
|
|
|
write++;
|
|
nb_cli_show_dnode_cmds(vty, dnode, false);
|
|
}
|
|
}
|
|
return write;
|
|
}
|
|
#endif /* ifdef FABRICD */
|
|
|
|
struct isis_circuit *isis_circuit_create(struct isis_area *area,
|
|
struct interface *ifp)
|
|
{
|
|
struct isis_circuit *circuit = circuit_scan_by_ifp(ifp);
|
|
|
|
if (circuit && circuit->area)
|
|
return NULL;
|
|
circuit = isis_csm_state_change(ISIS_ENABLE, circuit, area);
|
|
if (circuit->state != C_STATE_CONF && circuit->state != C_STATE_UP)
|
|
return circuit;
|
|
isis_circuit_if_bind(circuit, ifp);
|
|
if (circuit->area->mta && circuit->area->mta->status)
|
|
isis_link_params_update(circuit, ifp);
|
|
|
|
return circuit;
|
|
}
|
|
|
|
void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
|
|
bool ipv6_router)
|
|
{
|
|
struct isis_area *area = circuit->area;
|
|
int old_ipr = circuit->ip_router;
|
|
int old_ipv6r = circuit->ipv6_router;
|
|
|
|
/* is there something to do? */
|
|
if (old_ipr == ip_router && old_ipv6r == ipv6_router)
|
|
return;
|
|
|
|
circuit->ip_router = ip_router;
|
|
circuit->ipv6_router = ipv6_router;
|
|
circuit_update_nlpids(circuit);
|
|
|
|
/* the area should always be there if we get here, but in the past
|
|
* there were corner cases where the area was NULL (e.g. because the
|
|
* circuit was deconfigured following a validation error). Do not
|
|
* segfault if this happens again.
|
|
*/
|
|
if (!area) {
|
|
zlog_err("%s: NULL area for circuit %u", __func__,
|
|
circuit->circuit_id);
|
|
return;
|
|
}
|
|
|
|
area->ip_circuits += ip_router - old_ipr;
|
|
area->ipv6_circuits += ipv6_router - old_ipv6r;
|
|
|
|
if (!ip_router && !ipv6_router)
|
|
isis_csm_state_change(ISIS_DISABLE, circuit, area);
|
|
else
|
|
lsp_regenerate_schedule(area, circuit->is_type, 0);
|
|
}
|
|
|
|
ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
|
|
{
|
|
if (circuit->is_passive == passive)
|
|
return ferr_ok();
|
|
|
|
if (if_is_loopback(circuit->interface) && !passive)
|
|
return ferr_cfg_invalid("loopback is always passive");
|
|
|
|
if (circuit->state != C_STATE_UP) {
|
|
circuit->is_passive = passive;
|
|
} else {
|
|
struct isis_area *area = circuit->area;
|
|
isis_csm_state_change(ISIS_DISABLE, circuit, area);
|
|
circuit->is_passive = passive;
|
|
isis_csm_state_change(ISIS_ENABLE, circuit, area);
|
|
}
|
|
|
|
return ferr_ok();
|
|
}
|
|
|
|
ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level,
|
|
int metric)
|
|
{
|
|
assert(level == IS_LEVEL_1 || level == IS_LEVEL_2);
|
|
if (metric > MAX_WIDE_LINK_METRIC)
|
|
return ferr_cfg_invalid("metric %d too large for wide metric",
|
|
metric);
|
|
if (circuit->area && circuit->area->oldmetric
|
|
&& metric > MAX_NARROW_LINK_METRIC)
|
|
return ferr_cfg_invalid("metric %d too large for narrow metric",
|
|
metric);
|
|
|
|
/* inform ldp-sync of metric change
|
|
* if ldp-sync is running need to save metric
|
|
* and restore new values after ldp-sync completion.
|
|
*/
|
|
if (isis_ldp_sync_if_metric_config(circuit, level, metric)) {
|
|
circuit->te_metric[level - 1] = metric;
|
|
circuit->metric[level - 1] = metric;
|
|
if (circuit->area)
|
|
lsp_regenerate_schedule(circuit->area, level, 0);
|
|
}
|
|
return ferr_ok();
|
|
}
|
|
|
|
ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit)
|
|
{
|
|
memset(&circuit->passwd, 0, sizeof(circuit->passwd));
|
|
return ferr_ok();
|
|
}
|
|
|
|
ferr_r isis_circuit_passwd_set(struct isis_circuit *circuit,
|
|
uint8_t passwd_type, const char *passwd)
|
|
{
|
|
int len;
|
|
|
|
if (!passwd)
|
|
return ferr_code_bug("no circuit password given");
|
|
|
|
len = strlen(passwd);
|
|
if (len > 254)
|
|
return ferr_code_bug(
|
|
"circuit password too long (max 254 chars)");
|
|
|
|
circuit->passwd.len = len;
|
|
strlcpy((char *)circuit->passwd.passwd, passwd,
|
|
sizeof(circuit->passwd.passwd));
|
|
circuit->passwd.type = passwd_type;
|
|
return ferr_ok();
|
|
}
|
|
|
|
ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
|
|
const char *passwd)
|
|
{
|
|
return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_CLEARTXT,
|
|
passwd);
|
|
}
|
|
|
|
ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit,
|
|
const char *passwd)
|
|
{
|
|
return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_HMAC_MD5,
|
|
passwd);
|
|
}
|
|
|
|
struct cmd_node interface_node = {
|
|
.name = "interface",
|
|
.node = INTERFACE_NODE,
|
|
.parent_node = CONFIG_NODE,
|
|
.prompt = "%s(config-if)# ",
|
|
.config_write = isis_interface_config_write,
|
|
};
|
|
|
|
void isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
|
|
{
|
|
if (circuit->circ_type == circ_type)
|
|
return;
|
|
|
|
if (circuit->state != C_STATE_UP) {
|
|
circuit->circ_type = circ_type;
|
|
circuit->circ_type_config = circ_type;
|
|
} else {
|
|
struct isis_area *area = circuit->area;
|
|
|
|
isis_csm_state_change(ISIS_DISABLE, circuit, area);
|
|
circuit->circ_type = circ_type;
|
|
circuit->circ_type_config = circ_type;
|
|
isis_csm_state_change(ISIS_ENABLE, circuit, area);
|
|
}
|
|
}
|
|
|
|
int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid,
|
|
bool enabled)
|
|
{
|
|
struct isis_circuit_mt_setting *setting;
|
|
|
|
setting = circuit_get_mt_setting(circuit, mtid);
|
|
if (setting->enabled != enabled) {
|
|
setting->enabled = enabled;
|
|
lsp_regenerate_schedule(circuit->area, IS_LEVEL_1 | IS_LEVEL_2,
|
|
0);
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
int isis_if_new_hook(struct interface *ifp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int isis_if_delete_hook(struct interface *ifp)
|
|
{
|
|
struct isis_circuit *circuit;
|
|
/* Clean up the circuit data */
|
|
if (ifp && ifp->info) {
|
|
circuit = ifp->info;
|
|
isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isis_ifp_create(struct interface *ifp)
|
|
{
|
|
struct vrf *vrf = NULL;
|
|
|
|
if (if_is_operative(ifp)) {
|
|
vrf = vrf_lookup_by_id(ifp->vrf_id);
|
|
if (vrf)
|
|
isis_global_instance_create(vrf->name);
|
|
isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp),
|
|
ifp);
|
|
}
|
|
hook_call(isis_if_new_hook, ifp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isis_ifp_up(struct interface *ifp)
|
|
{
|
|
isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp), ifp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isis_ifp_down(struct interface *ifp)
|
|
{
|
|
struct isis_circuit *circuit;
|
|
|
|
circuit = isis_csm_state_change(IF_DOWN_FROM_Z,
|
|
circuit_scan_by_ifp(ifp), ifp);
|
|
if (circuit)
|
|
SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isis_ifp_destroy(struct interface *ifp)
|
|
{
|
|
if (if_is_operative(ifp))
|
|
zlog_warn("Zebra: got delete of %s, but interface is still up",
|
|
ifp->name);
|
|
|
|
isis_csm_state_change(IF_DOWN_FROM_Z, circuit_scan_by_ifp(ifp), ifp);
|
|
|
|
/* Cannot call if_delete because we should retain the pseudo interface
|
|
in case there is configuration info attached to it. */
|
|
if_delete_retain(ifp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void isis_circuit_init(void)
|
|
{
|
|
/* Initialize Zebra interface data structure */
|
|
hook_register_prio(if_add, 0, isis_if_new_hook);
|
|
hook_register_prio(if_del, 0, isis_if_delete_hook);
|
|
|
|
/* Install interface node */
|
|
install_node(&interface_node);
|
|
if_cmd_init();
|
|
if_zapi_callbacks(isis_ifp_create, isis_ifp_up,
|
|
isis_ifp_down, isis_ifp_destroy);
|
|
}
|