frr/isisd/isis_circuit.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1722 lines
47 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
2003-12-23 09:09:43 +01:00
/*
* IS-IS Rout(e)ing protocol - isis_circuit.h
*
* Copyright (C) 2001,2002 Sampo Saaristo
* Tampere University of Technology
* Institute of Communications Engineering
*/
#include <zebra.h>
2004-05-19 13:38:40 +02:00
#ifdef GNU_LINUX
2003-12-23 09:09:43 +01:00
#include <net/ethernet.h>
2004-05-19 13:38:40 +02:00
#else
#include <netinet/if_ether.h>
#endif
2003-12-23 09:09:43 +01:00
#include "log.h"
#include "memory.h"
#include "vrf.h"
2003-12-23 09:09:43 +01:00
#include "if.h"
#include "linklist.h"
#include "command.h"
#include "frrevent.h"
#include "vty.h"
2003-12-23 09:09:43 +01:00
#include "hash.h"
#include "prefix.h"
#include "stream.h"
#include "qobj.h"
#include "lib/northbound_cli.h"
2003-12-23 09:09:43 +01:00
#include "isisd/isis_constants.h"
#include "isisd/isis_common.h"
2012-03-24 16:35:20 +01:00
#include "isisd/isis_flags.h"
2003-12-23 09:09:43 +01:00
#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_srv6.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"
2003-12-23 09:09:43 +01:00
DEFINE_MTYPE_STATIC(ISISD, ISIS_CIRCUIT, "ISIS circuit");
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 *);
DEFINE_HOOK(isis_circuit_new_hook, (struct isis_circuit *circuit), (circuit));
DEFINE_HOOK(isis_circuit_del_hook, (struct isis_circuit *circuit), (circuit));
static void isis_circuit_enable(struct isis_circuit *circuit)
{
struct isis_area *area = circuit->area;
struct interface *ifp = circuit->interface;
if (!area) {
area = isis_area_lookup(circuit->tag, ifp->vrf->vrf_id);
if (area)
isis_area_add_circuit(area, circuit);
}
if (if_is_operative(ifp))
isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp);
}
static void isis_circuit_disable(struct isis_circuit *circuit)
{
struct isis_area *area = circuit->area;
struct interface *ifp = circuit->interface;
if (if_is_operative(ifp))
isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp);
if (area)
isis_area_del_circuit(area, circuit);
}
struct isis_circuit *isis_circuit_new(struct interface *ifp, const char *tag)
2003-12-23 09:09:43 +01:00
{
struct isis_circuit *circuit;
int i;
circuit = XCALLOC(MTYPE_ISIS_CIRCUIT, sizeof(struct isis_circuit));
circuit->tag = XSTRDUP(MTYPE_ISIS_CIRCUIT, tag);
2012-03-24 16:35:20 +01:00
/*
* Default values
*/
#ifndef FABRICD
circuit->is_type_config = yang_get_default_enum(
"/frr-interface:lib/interface/frr-isisd:isis/circuit-type");
circuit->flags = 0;
circuit->pad_hellos = yang_get_default_enum(
"/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_config = IS_LEVEL_1_AND_2;
2012-03-24 16:35:20 +01:00
circuit->flags = 0;
circuit->pad_hellos = ISIS_HELLO_PADDING_ALWAYS;
2012-03-24 16:35:20 +01:00
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;
2012-03-24 16:35:20 +01:00
circuit->te_metric[i] = DEFAULT_CIRCUIT_METRIC;
circuit->level_arg[i].level = i + 1;
circuit->level_arg[i].circuit = circuit;
2012-03-24 16:35:20 +01:00
}
#endif /* ifndef FABRICD */
circuit->is_type = circuit->is_type_config;
circuit_mt_init(circuit);
isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL1);
isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL2);
circuit->ldp_sync_info = ldp_sync_info_create();
circuit->ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED;
QOBJ_REG(circuit, isis_circuit);
isis_circuit_if_bind(circuit, ifp);
circuit->ip_addrs = list_new();
circuit->ipv6_link = list_new();
circuit->ipv6_non_link = list_new();
if (ifp->ifindex != IFINDEX_INTERNAL)
isis_circuit_enable(circuit);
2003-12-23 09:09:43 +01:00
return circuit;
}
2012-03-24 16:35:20 +01:00
void isis_circuit_del(struct isis_circuit *circuit)
{
if (!circuit)
return;
if (circuit->interface->ifindex != IFINDEX_INTERNAL)
isis_circuit_disable(circuit);
2012-03-24 16:35:20 +01:00
isis_circuit_if_unbind(circuit, circuit->interface);
QOBJ_UNREG(circuit);
ldp_sync_info_free(&circuit->ldp_sync_info);
circuit_mt_finish(circuit);
isis_lfa_excluded_ifaces_delete(circuit, ISIS_LEVEL1);
isis_lfa_excluded_ifaces_delete(circuit, ISIS_LEVEL2);
list_delete(&circuit->ip_addrs);
list_delete(&circuit->ipv6_link);
list_delete(&circuit->ipv6_non_link);
if (circuit->ext) {
isis_del_ext_subtlvs(circuit->ext);
circuit->ext = NULL;
}
XFREE(MTYPE_TMP, circuit->bfd_config.profile);
XFREE(MTYPE_ISIS_CIRCUIT, circuit->tag);
2012-03-24 16:35:20 +01:00
/* and lastly the circuit itself */
XFREE(MTYPE_ISIS_CIRCUIT, circuit);
return;
}
2003-12-23 09:09:43 +01:00
void isis_circuit_configure(struct isis_circuit *circuit,
struct isis_area *area)
{
2012-03-24 16:35:20 +01:00
assert(area);
circuit->isis = area->isis;
2003-12-23 09:09:43 +01:00
circuit->area = area;
2003-12-23 09:09:43 +01:00
/*
* 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.
2003-12-23 09:09:43 +01:00
*/
if (area->is_type != IS_LEVEL_1_AND_2)
circuit->is_type = area->is_type;
2003-12-23 09:09:43 +01:00
/*
* Add the circuit into area
*/
listnode_add(area->circuit_list, circuit);
2003-12-23 09:09:43 +01:00
circuit->idx = flags_get_index(&area->flags);
hook_call(isis_circuit_new_hook, circuit);
2003-12-23 09:09:43 +01:00
return;
}
2012-03-24 16:35:20 +01:00
void isis_circuit_deconfigure(struct isis_circuit *circuit,
struct isis_area *area)
2003-12-23 09:09:43 +01:00
{
hook_call(isis_circuit_del_hook, circuit);
2003-12-23 09:09:43 +01:00
/* Free the index of SRM and SSN flags */
flags_free_index(&area->flags, circuit->idx);
2012-03-24 16:35:20 +01:00
circuit->idx = 0;
/* Reset IS type to configured */
circuit->is_type = circuit->is_type_config;
2012-03-24 16:35:20 +01:00
/* Remove circuit from area */
assert(circuit->area == area);
listnode_delete(area->circuit_list, circuit);
circuit->area = NULL;
circuit->isis = NULL;
2003-12-23 09:09:43 +01:00
return;
}
struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp)
{
return (struct isis_circuit *)ifp->info;
2003-12-23 09:09:43 +01:00
}
DEFINE_HOOK(isis_circuit_add_addr_hook, (struct isis_circuit *circuit),
(circuit));
void isis_circuit_add_addr(struct isis_circuit *circuit,
struct connected *connected)
2003-12-23 09:09:43 +01:00
{
2012-03-24 16:35:20 +01:00
struct listnode *node;
2003-12-23 09:09:43 +01:00
struct prefix_ipv4 *ipv4;
struct prefix_ipv6 *ipv6;
if (connected->address->family == AF_INET) {
uint32_t addr = connected->address->u.prefix4.s_addr;
2012-03-24 16:35:20 +01:00
addr = ntohl(addr);
if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr))
2012-03-24 16:35:20 +01:00
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)
2012-03-24 16:35:20 +01:00
lsp_regenerate_schedule(circuit->area, circuit->is_type,
0);
2003-12-23 09:09:43 +01:00
#ifdef EXTREME_DEBUG
if (IS_DEBUG_EVENTS)
zlog_debug("Added IP address %pFX to circuit %s",
connected->address,
circuit->interface->name);
#endif /* EXTREME_DEBUG */
}
if (connected->address->family == AF_INET6) {
2012-03-24 16:35:20 +01:00
if (IN6_IS_ADDR_LOOPBACK(&connected->address->u.prefix6))
return;
2012-03-24 16:35:20 +01:00
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);
/* Update Local IPv6 address param. if MPLS TE is on */
if (circuit->ext && circuit->area
&& IS_MPLS_TE(circuit->area->mta)) {
IPV6_ADDR_COPY(&circuit->ext->local_addr6,
&ipv6->prefix);
SET_SUBTLV(circuit->ext, EXT_LOCAL_ADDR6);
}
}
if (circuit->area)
2012-03-24 16:35:20 +01:00
lsp_regenerate_schedule(circuit->area, circuit->is_type,
0);
2003-12-23 09:09:43 +01:00
#ifdef EXTREME_DEBUG
if (IS_DEBUG_EVENTS)
zlog_debug("Added IPv6 address %pFX to circuit %s",
connected->address,
circuit->interface->name);
#endif /* EXTREME_DEBUG */
}
hook_call(isis_circuit_add_addr_hook, circuit);
2003-12-23 09:09:43 +01:00
return;
}
void isis_circuit_del_addr(struct isis_circuit *circuit,
struct connected *connected)
2003-12-23 09:09:43 +01:00
{
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))
2012-03-24 16:35:20 +01:00
if (prefix_same((struct prefix *)ip,
(struct prefix *)ipv4))
break;
if (ip) {
listnode_delete(circuit->ip_addrs, ip);
prefix_ipv4_free(&ip);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
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) {
2017-02-13 19:27:54 +01:00
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");
2012-03-24 16:35:20 +01:00
} 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;
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
return id;
}
2003-12-23 09:09:43 +01:00
void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp)
{
struct connected *conn;
if (if_is_loopback(ifp) || (isis_option_check(ISIS_OPT_DUMMY_AS_LOOPBACK) &&
CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_DUMMY))) {
circuit->circ_type = CIRCUIT_T_LOOPBACK;
circuit->is_passive = 1;
} else if (if_is_broadcast(ifp)) {
if (fabricd || circuit->circ_type_config == CIRCUIT_T_P2P)
2012-03-24 16:35:20 +01:00
circuit->circ_type = CIRCUIT_T_P2P;
else
2012-03-24 16:35:20 +01:00
circuit->circ_type = CIRCUIT_T_BROADCAST;
} else if (if_is_pointopoint(ifp)) {
circuit->circ_type = CIRCUIT_T_P2P;
} else {
/* It's normal in case of loopback etc. */
if (IS_DEBUG_EVENTS)
zlog_debug("%s: unsupported media", __func__);
2012-03-24 16:35:20 +01:00
circuit->circ_type = CIRCUIT_T_UNKNOWN;
}
frr_each (if_connected, ifp->connected, conn)
isis_circuit_add_addr(circuit, conn);
2003-12-23 09:09:43 +01:00
}
2012-03-24 16:35:20 +01:00
void isis_circuit_if_del(struct isis_circuit *circuit, struct interface *ifp)
2003-12-23 09:09:43 +01:00
{
2012-03-24 16:35:20 +01:00
struct connected *conn;
2012-03-24 16:35:20 +01:00
assert(circuit->interface == ifp);
2012-03-24 16:35:20 +01:00
/* destroy addresses */
frr_each_safe (if_connected, ifp->connected, conn)
2012-03-24 16:35:20 +01:00
isis_circuit_del_addr(circuit, conn);
2012-03-24 16:35:20 +01:00
circuit->circ_type = CIRCUIT_T_UNKNOWN;
2003-12-23 09:09:43 +01:00
}
2012-03-24 16:35:20 +01:00
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)
2003-12-23 09:09:43 +01:00
{
2012-03-24 16:35:20 +01:00
assert(circuit != NULL);
assert(ifp != NULL);
assert(circuit->interface == ifp);
assert(ifp->info == circuit);
2003-12-23 09:09:43 +01:00
circuit->interface = NULL;
2012-03-24 16:35:20 +01:00
ifp->info = NULL;
}
2012-03-24 16:35:20 +01:00
static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit,
int is_set)
{
struct isis_area *area;
struct isis_lsp *lsp;
int level;
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
}
}
}
2003-12-23 09:09:43 +01:00
}
size_t isis_circuit_pdu_size(struct isis_circuit *circuit)
{
return ISO_MTU(circuit);
}
static bool isis_circuit_lfa_enabled(struct isis_circuit *circuit, int level)
{
return (circuit->lfa_protection[level - 1] ||
circuit->rlfa_protection[level - 1] ||
circuit->tilfa_protection[level - 1]);
}
void isis_circuit_switchover_routes(struct isis_circuit *circuit, int family,
union g_addr *nexthop_ip, ifindex_t ifindex)
{
char is_type;
if (!circuit->area)
return;
is_type = circuit->area->is_type;
if ((is_type == IS_LEVEL_1 || is_type == IS_LEVEL_1_AND_2) &&
isis_circuit_lfa_enabled(circuit, IS_LEVEL_1))
isis_area_switchover_routes(circuit->area, family, nexthop_ip,
ifindex, IS_LEVEL_1);
if ((is_type == IS_LEVEL_2 || is_type == IS_LEVEL_1_AND_2) &&
isis_circuit_lfa_enabled(circuit, IS_LEVEL_2))
isis_area_switchover_routes(circuit->area, family, nexthop_ip,
ifindex, IS_LEVEL_2);
}
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
event_add_read(master, isis_receive, circuit, circuit->fd,
&circuit->t_read);
#else
event_add_timer_msec(master, isis_receive, circuit,
listcount(circuit->area->circuit_list) * 100,
&circuit->t_read);
#endif
}
2003-12-23 09:09:43 +01:00
int isis_circuit_up(struct isis_circuit *circuit)
{
2012-03-24 16:35:20 +01:00
int retv;
2012-03-24 16:35:20 +01:00
/* Set the flags for all the lsps of the circuit. */
isis_circuit_update_all_srmflags(circuit, 1);
2012-03-24 16:35:20 +01:00
if (circuit->state == C_STATE_UP)
return ISIS_OK;
if (circuit->is_passive) {
circuit->last_uptime = time(NULL);
/* 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;
}
2012-03-24 16:35:20 +01:00
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);
isisd: Fix crash in ISIS when mtu mismatch occurs When lsp-mtu is configured larger than interface mtu and the interface is brought up, the ISIS code would crash. When other vendors have this misconfiguration they just continue ISIS running and allow the LSP packets to be created but not sent. When the misconfiguration is corrected the LSP packets start being sent. This change creates that same behavior in FRR. The startup issue I am hitting is when the isis lsp-mtu is larger that the interfaces mtu. We run into this case when we are in the process of changing the mtu on a tunnel. I issue a shutdown/no shutdown on the interface, because the tunnel MTU is smaller than the lsp-mtu, it is considered an error and calls circuit_if_del. This deletes part of the circuit information, which includes the circuit->ip_addr list. Later on we get an address update from zebra and try to add the interface address to this list and crash. 2022/04/07 20:19:52.032 ISIS: [GTRPJ-X68CG] CSM_EVENT for tun_gw2: IF_UP_FROM_Z calls isis_circuit_if_add this initialize the circuit->ip_addrs isis_circuit_up has the mtu check circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit) and fails returns ISIS_ERROR on failure call isis_circuit_if_del this deletes the circiut->ip_addrs list <---- 2022/04/07 20:19:52.032 ZEBRA: [NXYHN-ZKW2V] zebra_if_addr_update_ctx: INTF_ADDR_ADD: ifindex 3, addr 192.168.0.1/24 message to isisd to add address isis_zebra_if_address_add isis_circuit_add_addr circuit->ip_addr we try to add the ip address to the list, but it was deleted above and isisd crashes Signed-off-by: Lynne Morrison <lynne.morrison@ibm.com>
2022-04-13 22:07:44 +02:00
/* Allow ISIS to continue configuration. With this
* configuration failure ISIS will attempt to send lsp
* packets but will fail until the mtu is configured properly
*/
}
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;
}
2012-03-24 16:35:20 +01:00
/*
* 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
if (IS_DEBUG_EVENTS)
zlog_debug("%s: if_id %d, isomtu %d snpa %pSY",
__func__, circuit->interface->ifindex,
ISO_MTU(circuit), circuit->u.bc.snpa);
2012-03-24 16:35:20 +01:00
#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();
event_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]);
2012-03-24 16:35:20 +01:00
}
/* 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
*/
2012-03-24 16:35:20 +01:00
circuit->u.p2p.neighbor = NULL;
send_hello_sched(circuit, 0, TRIGGERED_IIH_DELAY);
}
2003-12-23 09:09:43 +01:00
/* initializing PSNP timers */
2012-03-24 16:35:20 +01:00
if (circuit->is_type & IS_LEVEL_1)
event_add_timer(
master, send_l1_psnp, circuit,
isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
&circuit->t_send_psnp[0]);
2012-03-24 16:35:20 +01:00
if (circuit->is_type & IS_LEVEL_2)
event_add_timer(
master, send_l2_psnp, circuit,
isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
&circuit->t_send_psnp[1]);
2012-03-24 16:35:20 +01:00
/* unified init for circuits; ignore warnings below this level */
retv = isis_sock_init(circuit);
if (retv != ISIS_OK) {
isis_circuit_down(circuit);
return retv;
}
2012-03-24 16:35:20 +01:00
/* 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);
circuit->last_uptime = time(NULL);
if (circuit->area->mta && circuit->area->mta->status)
isis_link_params_update(circuit, circuit->interface);
isis_if_ldp_sync_enable(circuit);
#ifndef FABRICD
/* send northbound notification */
isis_notif_if_state_change(circuit, false);
#endif /* ifndef FABRICD */
2012-03-24 16:35:20 +01:00
return ISIS_OK;
2003-12-23 09:09:43 +01:00
}
void isis_circuit_down(struct isis_circuit *circuit)
{
#ifndef FABRICD
/* send northbound notification */
isis_notif_if_state_change(circuit, true);
#endif /* ifndef FABRICD */
isis_if_ldp_sync_disable(circuit);
/* 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);
}
}
}
2012-03-24 16:35:20 +01:00
/* Clear the flags for all the lsps of the circuit. */
isis_circuit_update_all_srmflags(circuit, 0);
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2012-03-24 16:35:20 +01:00
/* destroy neighbour lists */
if (circuit->u.bc.lan_neighs[0]) {
list_delete(&circuit->u.bc.lan_neighs[0]);
2012-03-24 16:35:20 +01:00
circuit->u.bc.lan_neighs[0] = NULL;
}
if (circuit->u.bc.lan_neighs[1]) {
list_delete(&circuit->u.bc.lan_neighs[1]);
2012-03-24 16:35:20 +01:00
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]);
2012-03-24 16:35:20 +01:00
}
if (circuit->u.bc.adjdb[1]) {
circuit->u.bc.adjdb[1]->del = isis_delete_adj;
list_delete(&circuit->u.bc.adjdb[1]);
2012-03-24 16:35:20 +01:00
}
if (circuit->u.bc.is_dr[0]) {
isis_dr_resign(circuit, 1);
circuit->u.bc.is_dr[0] = 0;
}
isisd: fix crash when switching P2P after shutdowning LAN circuit When shutdown a LAN-type circuit, and if the current device is not the DIS (assuming it is a level-1 device), the isis_circuit_down() function will not call the isis_dr_resign() function to clear the circuit->u.bc.run_dr_elect[0] bit (this bit is set on interfaces in the isis_run_dr() function). After switching the link to a P2P type, since u.p2p and u.bc form a union, and circuit->u.bc.snpa = "\000\000\000\000\000" and circuit->u.bc.run_dr_elect = "\001", this results in circuit->u.p2p.neighbor = 0x1000000000000. Consequently, the value of adj->sysid accesses a wild pointer, causing the current crash. The backtrace is as follows: (gdb) bt #0 0x00007fbd30e55fe1 in raise () from /lib/x86_64-linux-gnu/libpthread.so.0 #1 0x00007fbd30f76b29 in core_handler (signo=11, siginfo=0x7ffc60b7a270, context=0x7ffc60b7a140) at ../lib/sigevent.c:261 #2 <signal handler called> #3 0x00007fbd30dddba4 in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #4 0x0000562aec46617f in isis_spf_run_lfa (area=0x562aee3a56d0, spftree=0x562aee3a51a0) at ../isisd/isis_lfa.c:2403 #5 0x0000562aec483854 in isis_run_spf_with_protection (area=0x562aee3a56d0, spftree=0x562aee3a51a0) at ../isisd/isis_spf.c:1891 #6 0x0000562aec483b05 in isis_run_spf_cb (thread=0x7ffc60b7b000) at ../isisd/isis_spf.c:1953 #7 0x00007fbd30f900bb in thread_call (thread=0x7ffc60b7b000) at ../lib/thread.c:1990 #8 0x00007fbd30f2897b in frr_run (master=0x562aee0833c0) at ../lib/libfrr.c:1198 #9 0x0000562aec454d6d in main (argc=5, argv=0x7ffc60b7b228, envp=0x7ffc60b7b258) at ../isisd/isis_main.c:273 (gdb) f 4 #4 0x0000562aec46617f in isis_spf_run_lfa (area=0x562aee3a56d0, spftree=0x562aee3a51a0) at ../isisd/isis_lfa.c:2403 2403 ../isisd/isis_lfa.c: No such file or directory. (gdb) p circuit->u.p2p.neighbor $1 = (struct isis_adjacency *) 0x1000000000000 (gdb) p adj->sysid Cannot access memory at address 0x1000000000006 (gdb) p circuit->u.bc $2 = {snpa = "\000\000\000\000\000", run_dr_elect = "\001", t_run_dr = {0x0, 0x0}, t_send_lan_hello = {0x0, 0x0}, adjdb = {0x0, 0x0}, lan_neighs = {0x0, 0x0}, is_dr = "\000", l1_desig_is = "\000\000\000\000\000\000", l2_desig_is = "\000\000\000\000\000\000", t_refresh_pseudo_lsp = {0x0, 0x0}} (gdb) The backtrace provided above pertains to version 8.5.4, but it seems that the same issue exists in the code of the master branch as well. Signed-off-by: baozhen-H3C <bao.zhen@h3c.com>
2024-11-06 13:17:03 +01:00
circuit->u.bc.run_dr_elect[0] = 0;
2012-03-24 16:35:20 +01:00
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;
}
isisd: fix crash when switching P2P after shutdowning LAN circuit When shutdown a LAN-type circuit, and if the current device is not the DIS (assuming it is a level-1 device), the isis_circuit_down() function will not call the isis_dr_resign() function to clear the circuit->u.bc.run_dr_elect[0] bit (this bit is set on interfaces in the isis_run_dr() function). After switching the link to a P2P type, since u.p2p and u.bc form a union, and circuit->u.bc.snpa = "\000\000\000\000\000" and circuit->u.bc.run_dr_elect = "\001", this results in circuit->u.p2p.neighbor = 0x1000000000000. Consequently, the value of adj->sysid accesses a wild pointer, causing the current crash. The backtrace is as follows: (gdb) bt #0 0x00007fbd30e55fe1 in raise () from /lib/x86_64-linux-gnu/libpthread.so.0 #1 0x00007fbd30f76b29 in core_handler (signo=11, siginfo=0x7ffc60b7a270, context=0x7ffc60b7a140) at ../lib/sigevent.c:261 #2 <signal handler called> #3 0x00007fbd30dddba4 in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #4 0x0000562aec46617f in isis_spf_run_lfa (area=0x562aee3a56d0, spftree=0x562aee3a51a0) at ../isisd/isis_lfa.c:2403 #5 0x0000562aec483854 in isis_run_spf_with_protection (area=0x562aee3a56d0, spftree=0x562aee3a51a0) at ../isisd/isis_spf.c:1891 #6 0x0000562aec483b05 in isis_run_spf_cb (thread=0x7ffc60b7b000) at ../isisd/isis_spf.c:1953 #7 0x00007fbd30f900bb in thread_call (thread=0x7ffc60b7b000) at ../lib/thread.c:1990 #8 0x00007fbd30f2897b in frr_run (master=0x562aee0833c0) at ../lib/libfrr.c:1198 #9 0x0000562aec454d6d in main (argc=5, argv=0x7ffc60b7b228, envp=0x7ffc60b7b258) at ../isisd/isis_main.c:273 (gdb) f 4 #4 0x0000562aec46617f in isis_spf_run_lfa (area=0x562aee3a56d0, spftree=0x562aee3a51a0) at ../isisd/isis_lfa.c:2403 2403 ../isisd/isis_lfa.c: No such file or directory. (gdb) p circuit->u.p2p.neighbor $1 = (struct isis_adjacency *) 0x1000000000000 (gdb) p adj->sysid Cannot access memory at address 0x1000000000006 (gdb) p circuit->u.bc $2 = {snpa = "\000\000\000\000\000", run_dr_elect = "\001", t_run_dr = {0x0, 0x0}, t_send_lan_hello = {0x0, 0x0}, adjdb = {0x0, 0x0}, lan_neighs = {0x0, 0x0}, is_dr = "\000", l1_desig_is = "\000\000\000\000\000\000", l2_desig_is = "\000\000\000\000\000\000", t_refresh_pseudo_lsp = {0x0, 0x0}} (gdb) The backtrace provided above pertains to version 8.5.4, but it seems that the same issue exists in the code of the master branch as well. Signed-off-by: baozhen-H3C <bao.zhen@h3c.com>
2024-11-06 13:17:03 +01:00
circuit->u.bc.run_dr_elect[1] = 0;
2012-03-24 16:35:20 +01:00
memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
memset(circuit->u.bc.snpa, 0, ETH_ALEN);
EVENT_OFF(circuit->u.bc.t_send_lan_hello[0]);
EVENT_OFF(circuit->u.bc.t_send_lan_hello[1]);
EVENT_OFF(circuit->u.bc.t_run_dr[0]);
EVENT_OFF(circuit->u.bc.t_run_dr[1]);
EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[0]);
EVENT_OFF(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) {
2012-03-24 16:35:20 +01:00
isis_delete_adj(circuit->u.p2p.neighbor);
circuit->u.p2p.neighbor = NULL;
EVENT_OFF(circuit->u.p2p.t_send_p2p_hello);
}
/*
* All adjacencies have to be gone, delete snmp list
* and reset snmpd idx generator
*/
if (circuit->snmp_adj_list != NULL)
list_delete(&circuit->snmp_adj_list);
circuit->snmp_adj_idx_gen = 0;
2012-03-24 16:35:20 +01:00
/* Cancel all active threads */
EVENT_OFF(circuit->t_send_csnp[0]);
EVENT_OFF(circuit->t_send_csnp[1]);
EVENT_OFF(circuit->t_send_psnp[0]);
EVENT_OFF(circuit->t_send_psnp[1]);
EVENT_OFF(circuit->t_read);
if (circuit->tx_queue) {
isis_tx_queue_free(circuit->tx_queue);
circuit->tx_queue = NULL;
}
2012-03-24 16:35:20 +01:00
/* 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);
}
2012-03-24 16:35:20 +01:00
circuit->upadjcount[0] = 0;
circuit->upadjcount[1] = 0;
2003-12-23 09:09:43 +01:00
/* close the socket */
2012-03-24 16:35:20 +01:00
if (circuit->fd) {
close(circuit->fd);
circuit->fd = 0;
}
2012-03-24 16:35:20 +01:00
if (circuit->rcv_stream != NULL) {
stream_free(circuit->rcv_stream);
circuit->rcv_stream = NULL;
}
2012-03-24 16:35:20 +01:00
if (circuit->snd_stream != NULL) {
stream_free(circuit->snd_stream);
circuit->snd_stream = NULL;
}
event_cancel_event(master, circuit);
2003-12-23 09:09:43 +01:00
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++;
}
2003-12-23 09:09:43 +01:00
return;
}
void isis_circuit_print_json(struct isis_circuit *circuit,
struct json_object *json, char detail)
{
int level;
json_object *iface_json, *ipv4_addr_json, *ipv6_link_json,
*ipv6_non_link_json, *hold_json, *lan_prio_json, *levels_json,
*level_json;
char buf_prx[INET6_BUFSIZ];
char buf[255];
snprintfrr(buf, sizeof(buf), "0x%x", circuit->circuit_id);
if (detail == ISIS_UI_LEVEL_BRIEF) {
iface_json = json_object_new_object();
json_object_object_add(json, "interface", iface_json);
json_object_string_add(iface_json, "name",
circuit->interface->name);
json_object_string_add(iface_json, "circuit-id", buf);
json_object_string_add(iface_json, "state",
circuit_state2string(circuit->state));
json_object_string_add(iface_json, "type",
circuit_type2string(circuit->circ_type));
json_object_string_add(iface_json, "level",
circuit_t2string(circuit->is_type));
}
if (detail == ISIS_UI_LEVEL_DETAIL) {
struct listnode *node;
struct prefix *ip_addr;
iface_json = json_object_new_object();
json_object_object_add(json, "interface", iface_json);
json_object_string_add(iface_json, "name",
circuit->interface->name);
json_object_string_add(iface_json, "state",
circuit_state2string(circuit->state));
if (circuit->is_passive)
json_object_string_add(iface_json, "is-passive",
"passive");
else
json_object_string_add(iface_json, "is-passive",
"active");
json_object_string_add(iface_json, "circuit-id", buf);
json_object_string_add(iface_json, "type",
circuit_type2string(circuit->circ_type));
json_object_string_add(iface_json, "level",
circuit_t2string(circuit->is_type));
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
json_object_string_addf(iface_json, "snpa", "%pSY",
circuit->u.bc.snpa);
levels_json = json_object_new_array();
json_object_object_add(iface_json, "levels", levels_json);
for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
if ((circuit->is_type & level) == 0)
continue;
level_json = json_object_new_object();
json_object_string_add(level_json, "level",
circuit_t2string(level));
if (circuit->area->newmetric)
json_object_int_add(level_json, "metric",
circuit->te_metric[level - 1]);
else
json_object_int_add(level_json, "metric",
circuit->metric[level - 1]);
if (!circuit->is_passive) {
json_object_int_add(level_json, "active-neighbors",
circuit->upadjcount[level - 1]);
json_object_int_add(level_json, "hello-interval",
circuit->hello_interval[level - 1]);
hold_json = json_object_new_object();
json_object_object_add(level_json, "holddown",
hold_json);
json_object_int_add(hold_json, "count",
circuit->hello_multiplier[level - 1]);
json_object_string_add(
hold_json, "pad",
isis_hello_padding2string(
circuit->pad_hellos));
json_object_int_add(level_json, "cnsp-interval",
circuit->csnp_interval[level - 1]);
json_object_int_add(level_json, "psnp-interval",
circuit->psnp_interval[level - 1]);
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
lan_prio_json =
json_object_new_object();
json_object_object_add(level_json,
"lan",
lan_prio_json);
json_object_int_add(lan_prio_json, "priority",
circuit->priority[level - 1]);
json_object_string_add(lan_prio_json, "is-dis",
(circuit->u.bc.is_dr[level - 1]
? "yes"
: "no"));
}
}
json_object_array_add(levels_json, level_json);
}
if (listcount(circuit->ip_addrs) > 0) {
ipv4_addr_json = json_object_new_object();
json_object_object_add(iface_json, "ip-prefix",
ipv4_addr_json);
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
ip_addr)) {
snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX",
ip_addr);
json_object_string_add(ipv4_addr_json, "ip",
buf_prx);
}
}
if (listcount(circuit->ipv6_link) > 0) {
ipv6_link_json = json_object_new_object();
json_object_object_add(iface_json, "ipv6-link-locals",
ipv6_link_json);
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
ip_addr)) {
snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX",
ip_addr);
json_object_string_add(ipv6_link_json, "ipv6",
buf_prx);
}
}
if (listcount(circuit->ipv6_non_link) > 0) {
ipv6_non_link_json = json_object_new_object();
json_object_object_add(iface_json, "ipv6-prefixes",
ipv6_non_link_json);
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
ip_addr)) {
snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX",
ip_addr);
json_object_string_add(ipv6_non_link_json,
"ipv6", buf_prx);
}
}
}
return;
}
2012-03-24 16:35:20 +01:00
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");
2012-03-24 16:35:20 +01:00
}
2012-03-24 16:35:20 +01:00
if (detail == ISIS_UI_LEVEL_DETAIL) {
struct listnode *node;
struct prefix *ip_addr;
2012-03-24 16:35:20 +01:00
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");
2012-03-24 16:35:20 +01:00
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: %-10pSY", circuit->u.bc.snpa);
vty_out(vty, "\n");
2012-03-24 16:35:20 +01:00
if (circuit->is_type & IS_LEVEL_1) {
vty_out(vty, " Level-1 Information:\n");
2012-03-24 16:35:20 +01:00
if (circuit->area->newmetric)
vty_out(vty, " Metric: %d",
circuit->te_metric[0]);
else
vty_out(vty, " Metric: %d",
circuit->metric[0]);
2012-03-24 16:35:20 +01:00
if (!circuit->is_passive) {
vty_out(vty, ", Active neighbors: %u\n",
circuit->upadjcount[0]);
vty_out(vty,
" Hello interval: %u, Holddown count: %u, Padding: %s\n",
2012-03-24 16:35:20 +01:00
circuit->hello_interval[0],
circuit->hello_multiplier[0],
isis_hello_padding2string(
circuit->pad_hellos));
vty_out(vty,
" CNSP interval: %u, PSNP interval: %u\n",
2012-03-24 16:35:20 +01:00
circuit->csnp_interval[0],
circuit->psnp_interval[0]);
2012-03-24 16:35:20 +01:00
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
vty_out(vty,
" LAN Priority: %u, %s\n",
2012-03-24 16:35:20 +01:00
circuit->priority[0],
(circuit->u.bc.is_dr[0]
? "is DIS"
: "is not DIS"));
2012-03-24 16:35:20 +01:00
} else {
vty_out(vty, "\n");
2012-03-24 16:35:20 +01:00
}
}
2012-03-24 16:35:20 +01:00
if (circuit->is_type & IS_LEVEL_2) {
vty_out(vty, " Level-2 Information:\n");
2012-03-24 16:35:20 +01:00
if (circuit->area->newmetric)
vty_out(vty, " Metric: %d",
circuit->te_metric[1]);
else
vty_out(vty, " Metric: %d",
circuit->metric[1]);
2012-03-24 16:35:20 +01:00
if (!circuit->is_passive) {
vty_out(vty, ", Active neighbors: %u\n",
circuit->upadjcount[1]);
vty_out(vty,
" Hello interval: %u, Holddown count: %u, Padding: %s\n",
2012-03-24 16:35:20 +01:00
circuit->hello_interval[1],
circuit->hello_multiplier[1],
isis_hello_padding2string(
circuit->pad_hellos));
vty_out(vty,
" CNSP interval: %u, PSNP interval: %u\n",
2012-03-24 16:35:20 +01:00
circuit->csnp_interval[1],
circuit->psnp_interval[1]);
2012-03-24 16:35:20 +01:00
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
vty_out(vty,
" LAN Priority: %u, %s\n",
2012-03-24 16:35:20 +01:00
circuit->priority[1],
(circuit->u.bc.is_dr[1]
? "is DIS"
: "is not DIS"));
2012-03-24 16:35:20 +01:00
} else {
vty_out(vty, "\n");
2012-03-24 16:35:20 +01:00
}
}
if (listcount(circuit->ip_addrs) > 0) {
vty_out(vty, " IP Prefix(es):\n");
2012-03-24 16:35:20 +01:00
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
ip_addr))
vty_out(vty, " %pFX\n", ip_addr);
}
if (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 (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");
2012-03-24 16:35:20 +01:00
}
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)
2003-12-23 09:09:43 +01:00
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
2003-12-23 09:09:43 +01:00
int write = 0;
struct interface *ifp;
2012-03-24 16:35:20 +01:00
struct isis_circuit *circuit;
2003-12-23 09:09:43 +01:00
int i;
FOR_ALL_INTERFACES (vrf, ifp) {
2012-03-24 16:35:20 +01:00
/* IF name */
if_vty_config_start(vty, ifp);
2012-03-24 16:35:20 +01:00
write++;
/* IF desc */
if (ifp->desc) {
vty_out(vty, " description %s\n", ifp->desc);
2012-03-24 16:35:20 +01:00
write++;
}
/* ISIS Circuit */
do {
circuit = circuit_scan_by_ifp(ifp);
2012-03-24 16:35:20 +01:00
if (circuit == NULL)
break;
2012-03-24 16:35:20 +01:00
if (circuit->ip_router) {
vty_out(vty, " ip router " PROTO_NAME " %s\n",
circuit->tag);
2012-03-24 16:35:20 +01:00
write++;
}
if (circuit->is_passive) {
vty_out(vty, " " PROTO_NAME " passive\n");
2012-03-24 16:35:20 +01:00
write++;
}
if (circuit->circ_type_config == CIRCUIT_T_P2P) {
vty_out(vty, " " PROTO_NAME " network point-to-point\n");
2012-03-24 16:35:20 +01:00
write++;
}
if (circuit->ipv6_router) {
vty_out(vty, " ipv6 router " PROTO_NAME " %s\n",
circuit->tag);
2012-03-24 16:35:20 +01:00
write++;
}
2012-03-24 16:35:20 +01:00
/* ISIS - circuit type */
if (!fabricd) {
if (circuit->is_type == IS_LEVEL_1) {
vty_out(vty, " " PROTO_NAME " circuit-type level-1\n");
2012-03-24 16:35:20 +01:00
write++;
} else {
if (circuit->is_type == IS_LEVEL_2) {
vty_out(vty,
" " PROTO_NAME " circuit-type level-2-only\n");
write++;
}
}
2012-03-24 16:35:20 +01:00
}
2012-03-24 16:35:20 +01:00
/* 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]);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
write++;
}
}
2012-03-24 16:35:20 +01:00
}
2012-03-24 16:35:20 +01:00
/* 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]);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
write++;
}
}
2012-03-24 16:35:20 +01:00
}
/* ISIS - Hello padding - Defaults to always so only
* display if not always */
switch (circuit->pad_hellos) {
case ISIS_HELLO_PADDING_DISABLED:
vty_out(vty, " no " PROTO_NAME " hello padding\n");
2012-03-24 16:35:20 +01:00
write++;
break;
case ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION:
vty_out(vty, PROTO_NAME
" hello padding during-adjacency-formation\n");
write++;
break;
case ISIS_HELLO_PADDING_ALWAYS:
break;
2012-03-24 16:35:20 +01:00
}
if (circuit->disable_threeway_adj) {
vty_out(vty, " no isis three-way-handshake\n");
write++;
}
2012-03-24 16:35:20 +01:00
/* 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]);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
write++;
}
}
}
2012-03-24 16:35:20 +01:00
/* 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]);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
write++;
}
}
}
2012-03-24 16:35:20 +01:00
/* 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]);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
write++;
}
}
}
2012-03-24 16:35:20 +01:00
/* 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]);
2012-03-24 16:35:20 +01:00
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);
2012-03-24 16:35:20 +01:00
write++;
}
}
}
2012-03-24 16:35:20 +01:00
if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) {
vty_out(vty, " " PROTO_NAME " password md5 %s\n",
circuit->passwd.passwd);
2012-03-24 16:35:20 +01:00
write++;
} else if (circuit->passwd.type
== ISIS_PASSWD_TYPE_CLEARTXT) {
vty_out(vty, " " PROTO_NAME " password clear %s\n",
circuit->passwd.passwd);
2012-03-24 16:35:20 +01:00
write++;
}
if (circuit->bfd_config.enabled) {
vty_out(vty, " " PROTO_NAME " bfd\n");
write++;
}
2018-10-12 16:45:32 +02:00
write += hook_call(isis_circuit_config_write,
circuit, vty);
} while (0);
if_vty_config_end(vty);
2003-12-23 09:09:43 +01:00
}
2003-12-23 09:09:43 +01:00
return write;
}
#endif /* ifdef FABRICD */
2003-12-23 09:09:43 +01:00
void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
bool ipv6_router)
2003-12-23 09:09:43 +01:00
{
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);
if (area) {
area->ip_circuits += ip_router - old_ipr;
area->ipv6_circuits += ipv6_router - old_ipv6r;
if (ip_router || ipv6_router)
lsp_regenerate_schedule(area, circuit->is_type, 0);
}
2012-03-24 16:35:20 +01:00
}
2003-12-23 09:09:43 +01:00
ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
2012-03-24 16:35:20 +01:00
{
if (circuit->is_passive == passive)
return ferr_ok();
if (if_is_loopback(circuit->interface) && !passive)
return ferr_cfg_invalid("loopback is always passive");
2012-03-24 16:35:20 +01:00
if (circuit->state != C_STATE_UP) {
circuit->is_passive = passive;
} else {
2012-03-24 16:35:20 +01:00
struct isis_area *area = circuit->area;
isis_csm_state_change(ISIS_DISABLE, circuit, area);
circuit->is_passive = passive;
2012-03-24 16:35:20 +01:00
isis_csm_state_change(ISIS_ENABLE, circuit, area);
}
return ferr_ok();
2003-12-23 09:09:43 +01:00
}
ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level,
int metric)
2012-03-24 16:35:20 +01:00
{
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);
/* Don't modify metric if advertise high metrics is configured */
if (circuit->area && circuit->area->advertise_high_metrics)
return ferr_ok();
/* 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();
2003-12-23 09:09:43 +01:00
}
ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit)
2003-12-23 09:09:43 +01:00
{
memset(&circuit->passwd, 0, sizeof(circuit->passwd));
return ferr_ok();
2012-03-24 16:35:20 +01:00
}
ferr_r isis_circuit_passwd_set(struct isis_circuit *circuit,
uint8_t passwd_type, const char *passwd)
2012-03-24 16:35:20 +01:00
{
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();
2003-12-23 09:09:43 +01:00
}
ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
const char *passwd)
2003-12-23 09:09:43 +01:00
{
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);
2003-12-23 09:09:43 +01:00
}
void isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
2012-03-24 16:35:20 +01:00
{
if (circuit->circ_type == circ_type)
return;
2012-03-24 16:35:20 +01:00
if (circuit->state != C_STATE_UP) {
circuit->circ_type = circ_type;
circuit->circ_type_config = circ_type;
2012-03-24 16:35:20 +01:00
} 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);
2012-03-24 16:35:20 +01:00
}
}
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;
if (circuit->area)
lsp_regenerate_schedule(circuit->area,
IS_LEVEL_1 | IS_LEVEL_2, 0);
}
return CMD_SUCCESS;
}
2003-12-23 09:09:43 +01:00
int isis_if_new_hook(struct interface *ifp)
{
return 0;
}
int isis_if_delete_hook(struct interface *ifp)
{
if (ifp->info)
isis_circuit_del(ifp->info);
2003-12-23 09:09:43 +01:00
return 0;
}
static int isis_ifp_create(struct interface *ifp)
{
struct isis_circuit *circuit = ifp->info;
if (circuit)
isis_circuit_enable(circuit);
hook_call(isis_if_new_hook, ifp);
return 0;
}
static int isis_ifp_up(struct interface *ifp)
{
struct isis_circuit *circuit = ifp->info;
if (circuit) {
UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z);
isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp);
}
/* Notify SRv6 that the interface went up */
isis_srv6_ifp_up_notify(ifp);
return 0;
}
static int isis_ifp_down(struct interface *ifp)
{
afi_t afi;
struct isis_circuit *circuit = ifp->info;
if (circuit &&
!CHECK_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z)) {
SET_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z);
for (afi = AFI_IP; afi <= AFI_IP6; afi++)
isis_circuit_switchover_routes(
circuit, afi == AFI_IP ? AF_INET : AF_INET6,
NULL, ifp->ifindex);
isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp);
SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
}
return 0;
}
static int isis_ifp_destroy(struct interface *ifp)
{
struct isis_circuit *circuit = ifp->info;
if (circuit)
isis_circuit_disable(circuit);
return 0;
}
/* Reset IS hello timer after interval change */
void isis_reset_hello_timer(struct isis_circuit *circuit)
{
/* First send an immediate hello to prevent adjacency loss
* during longer hello interval transitions
*/
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
/* For broadcast circuits - need to handle both levels */
if (circuit->is_type & IS_LEVEL_1) {
/* send hello immediately */
send_hello(circuit, IS_LEVEL_1);
/* reset level-1 hello timer */
EVENT_OFF(circuit->u.bc.t_send_lan_hello[0]);
if (circuit->area && (circuit->area->is_type & IS_LEVEL_1))
send_hello_sched(circuit, IS_LEVEL_1,
isis_jitter(circuit->hello_interval[0],
IIH_JITTER));
}
if (circuit->is_type & IS_LEVEL_2) {
/* send hello immediately */
send_hello(circuit, IS_LEVEL_2);
/* reset level-2 hello timer */
EVENT_OFF(circuit->u.bc.t_send_lan_hello[1]);
if (circuit->area && (circuit->area->is_type & IS_LEVEL_2))
send_hello_sched(circuit, IS_LEVEL_2,
isis_jitter(circuit->hello_interval[1],
IIH_JITTER));
}
} else if (circuit->circ_type == CIRCUIT_T_P2P) {
/* For point-to-point circuits */
send_hello(circuit, IS_LEVEL_1);
/* reset hello timer */
EVENT_OFF(circuit->u.p2p.t_send_p2p_hello);
send_hello_sched(circuit, 0, isis_jitter(circuit->hello_interval[0], IIH_JITTER));
}
}
void isis_circuit_init(void)
2003-12-23 09:09:43 +01:00
{
/* 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);
2003-12-23 09:09:43 +01:00
/* Install interface node */
#ifdef FABRICD
if_cmd_init(isis_interface_config_write);
#else
if_cmd_init_default();
#endif
hook_register_prio(if_real, 0, isis_ifp_create);
hook_register_prio(if_up, 0, isis_ifp_up);
hook_register_prio(if_down, 0, isis_ifp_down);
hook_register_prio(if_unreal, 0, isis_ifp_destroy);
2003-12-23 09:09:43 +01:00
}