mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-03 07:03:19 +00:00

An adjacency should be removed when the holdtimer expires, but if the system is overloaded we may end up doing it late. In the meanwhile vtysh will display an incorrect value in the show isis neighbor output, due to an overflow of the unsigned variable used to display the Holdtime, e.g.: pe1# show isis neighbor Area test: System Id Interface L state Holdtime SNPA Spirent-1 2.201 1 Down 26 2020.2020.2020 Spirent-1 2.203 1 Up 21 2020.2020.2020 Spirent-1 2.204 1 Up 18446744073709551615 2020.2020.2020 Spirent-1 2.207 1 Up 18446744073709551615 2020.2020.2020 Spirent-1 2.208 1 Up 18446744073709551615 2020.2020.2020 Spirent-1 2.209 1 Up 0 2020.2020.2020 Spirent-1 2.210 1 Up 18446744073709551615 2020.2020.2020 pe2 12.200 1 Up 30 2020.2020.2020 Guard against that by printing an "Expiring" message instead. Signed-off-by: Emanuele Di Pascale <emanuele@voltanet.io>
673 lines
17 KiB
C
673 lines
17 KiB
C
/*
|
|
* IS-IS Rout(e)ing protocol - isis_adjacency.c
|
|
* handling of IS-IS adjacencies
|
|
*
|
|
* 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>
|
|
|
|
#include "log.h"
|
|
#include "memory.h"
|
|
#include "hash.h"
|
|
#include "vty.h"
|
|
#include "linklist.h"
|
|
#include "thread.h"
|
|
#include "if.h"
|
|
#include "stream.h"
|
|
|
|
#include "isisd/isis_constants.h"
|
|
#include "isisd/isis_common.h"
|
|
#include "isisd/isis_flags.h"
|
|
#include "isisd/isisd.h"
|
|
#include "isisd/isis_circuit.h"
|
|
#include "isisd/isis_adjacency.h"
|
|
#include "isisd/isis_misc.h"
|
|
#include "isisd/isis_dr.h"
|
|
#include "isisd/isis_dynhn.h"
|
|
#include "isisd/isis_pdu.h"
|
|
#include "isisd/isis_lsp.h"
|
|
#include "isisd/isis_events.h"
|
|
#include "isisd/isis_mt.h"
|
|
#include "isisd/isis_tlvs.h"
|
|
#include "isisd/fabricd.h"
|
|
#include "isisd/isis_nb.h"
|
|
|
|
static struct isis_adjacency *adj_alloc(const uint8_t *id)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
|
|
adj = XCALLOC(MTYPE_ISIS_ADJACENCY, sizeof(struct isis_adjacency));
|
|
memcpy(adj->sysid, id, ISIS_SYS_ID_LEN);
|
|
|
|
return adj;
|
|
}
|
|
|
|
struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
|
|
int level, struct isis_circuit *circuit)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
int i;
|
|
|
|
adj = adj_alloc(id); /* P2P kludge */
|
|
|
|
if (snpa) {
|
|
memcpy(adj->snpa, snpa, ETH_ALEN);
|
|
} else {
|
|
memset(adj->snpa, ' ', ETH_ALEN);
|
|
}
|
|
|
|
adj->circuit = circuit;
|
|
adj->level = level;
|
|
adj->flaps = 0;
|
|
adj->last_flap = time(NULL);
|
|
adj->threeway_state = ISIS_THREEWAY_DOWN;
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
|
|
listnode_add(circuit->u.bc.adjdb[level - 1], adj);
|
|
adj->dischanges[level - 1] = 0;
|
|
for (i = 0; i < DIS_RECORDS;
|
|
i++) /* clear N DIS state change records */
|
|
{
|
|
adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
|
|
ISIS_UNKNOWN_DIS;
|
|
adj->dis_record[(i * ISIS_LEVELS) + level - 1]
|
|
.last_dis_change = time(NULL);
|
|
}
|
|
}
|
|
adj->adj_sids = list_new();
|
|
listnode_add(circuit->area->adjacency_list, adj);
|
|
|
|
return adj;
|
|
}
|
|
|
|
struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid, struct list *adjdb)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
struct listnode *node;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
|
|
if (memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0)
|
|
return adj;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa,
|
|
struct list *adjdb)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_adjacency *adj;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
|
|
if (memcmp(adj->snpa, ssnpa, ETH_ALEN) == 0)
|
|
return adj;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level,
|
|
const uint8_t *sysid)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
struct listnode *node;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
|
|
if (!(adj->level & level))
|
|
continue;
|
|
|
|
if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN))
|
|
return adj;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj))
|
|
|
|
void isis_delete_adj(void *arg)
|
|
{
|
|
struct isis_adjacency *adj = arg;
|
|
|
|
if (!adj)
|
|
return;
|
|
|
|
THREAD_TIMER_OFF(adj->t_expire);
|
|
if (adj->adj_state != ISIS_ADJ_DOWN)
|
|
adj->adj_state = ISIS_ADJ_DOWN;
|
|
|
|
hook_call(isis_adj_state_change_hook, adj);
|
|
|
|
XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses);
|
|
XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses);
|
|
XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses);
|
|
|
|
adj_mt_finish(adj);
|
|
list_delete(&adj->adj_sids);
|
|
|
|
listnode_delete(adj->circuit->area->adjacency_list, adj);
|
|
XFREE(MTYPE_ISIS_ADJACENCY, adj);
|
|
return;
|
|
}
|
|
|
|
static const char *adj_state2string(int state)
|
|
{
|
|
|
|
switch (state) {
|
|
case ISIS_ADJ_INITIALIZING:
|
|
return "Initializing";
|
|
case ISIS_ADJ_UP:
|
|
return "Up";
|
|
case ISIS_ADJ_DOWN:
|
|
return "Down";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
|
|
return NULL; /* not reached */
|
|
}
|
|
|
|
static const char *adj_level2string(int level)
|
|
{
|
|
switch (level) {
|
|
case IS_LEVEL_1:
|
|
return "level-1";
|
|
case IS_LEVEL_2:
|
|
return "level-2";
|
|
case IS_LEVEL_1_AND_2:
|
|
return "level-1-2";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
|
|
return NULL; /* not reached */
|
|
}
|
|
|
|
void isis_adj_process_threeway(struct isis_adjacency *adj,
|
|
struct isis_threeway_adj *tw_adj,
|
|
enum isis_adj_usage adj_usage)
|
|
{
|
|
enum isis_threeway_state next_tw_state = ISIS_THREEWAY_DOWN;
|
|
|
|
if (tw_adj && !adj->circuit->disable_threeway_adj) {
|
|
if (tw_adj->state == ISIS_THREEWAY_DOWN) {
|
|
next_tw_state = ISIS_THREEWAY_INITIALIZING;
|
|
} else if (tw_adj->state == ISIS_THREEWAY_INITIALIZING) {
|
|
next_tw_state = ISIS_THREEWAY_UP;
|
|
} else if (tw_adj->state == ISIS_THREEWAY_UP) {
|
|
if (adj->threeway_state == ISIS_THREEWAY_DOWN)
|
|
next_tw_state = ISIS_THREEWAY_DOWN;
|
|
else
|
|
next_tw_state = ISIS_THREEWAY_UP;
|
|
}
|
|
} else {
|
|
next_tw_state = ISIS_THREEWAY_UP;
|
|
}
|
|
|
|
if (next_tw_state != adj->threeway_state) {
|
|
if (IS_DEBUG_ADJ_PACKETS) {
|
|
zlog_info("ISIS-Adj (%s): Threeway state change %s to %s",
|
|
adj->circuit->area->area_tag,
|
|
isis_threeway_state_name(adj->threeway_state),
|
|
isis_threeway_state_name(next_tw_state));
|
|
}
|
|
}
|
|
|
|
if (next_tw_state != ISIS_THREEWAY_DOWN)
|
|
fabricd_initial_sync_hello(adj->circuit);
|
|
|
|
if (next_tw_state == ISIS_THREEWAY_DOWN) {
|
|
isis_adj_state_change(&adj, ISIS_ADJ_DOWN,
|
|
"Neighbor restarted");
|
|
return;
|
|
}
|
|
|
|
if (next_tw_state == ISIS_THREEWAY_UP) {
|
|
if (adj->adj_state != ISIS_ADJ_UP) {
|
|
isis_adj_state_change(&adj, ISIS_ADJ_UP, NULL);
|
|
adj->adj_usage = adj_usage;
|
|
}
|
|
}
|
|
|
|
if (adj->threeway_state != next_tw_state) {
|
|
send_hello_sched(adj->circuit, 0, TRIGGERED_IIH_DELAY);
|
|
}
|
|
|
|
adj->threeway_state = next_tw_state;
|
|
}
|
|
void isis_log_adj_change(struct isis_adjacency *adj,
|
|
enum isis_adj_state old_state,
|
|
enum isis_adj_state new_state, const char *reason)
|
|
{
|
|
const char *adj_name;
|
|
struct isis_dynhn *dyn;
|
|
|
|
dyn = dynhn_find_by_id(adj->sysid);
|
|
if (dyn)
|
|
adj_name = dyn->hostname;
|
|
else
|
|
adj_name = sysid_print(adj->sysid);
|
|
|
|
zlog_info(
|
|
"%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s",
|
|
adj_name, adj->circuit->interface->name,
|
|
adj_level2string(adj->level), adj_state2string(old_state),
|
|
adj_state2string(new_state), reason ? reason : "unspecified");
|
|
}
|
|
void isis_adj_state_change(struct isis_adjacency **padj,
|
|
enum isis_adj_state new_state, const char *reason)
|
|
{
|
|
struct isis_adjacency *adj = *padj;
|
|
enum isis_adj_state old_state = adj->adj_state;
|
|
struct isis_circuit *circuit = adj->circuit;
|
|
bool del = false;
|
|
|
|
if (new_state == old_state)
|
|
return;
|
|
|
|
adj->adj_state = new_state;
|
|
send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY);
|
|
|
|
if (IS_DEBUG_ADJ_PACKETS) {
|
|
zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
|
|
circuit->area->area_tag, old_state, new_state,
|
|
reason ? reason : "unspecified");
|
|
}
|
|
|
|
if (circuit->area->log_adj_changes)
|
|
isis_log_adj_change(adj, old_state, new_state, reason);
|
|
|
|
circuit->adj_state_changes++;
|
|
#ifndef FABRICD
|
|
/* send northbound notification */
|
|
isis_notif_adj_state_change(adj, new_state, reason);
|
|
#endif /* ifndef FABRICD */
|
|
|
|
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
|
|
for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
|
|
if ((adj->level & level) == 0)
|
|
continue;
|
|
if (new_state == ISIS_ADJ_UP) {
|
|
circuit->upadjcount[level - 1]++;
|
|
/* update counter & timers for debugging
|
|
* purposes */
|
|
adj->last_flap = time(NULL);
|
|
adj->flaps++;
|
|
} else if (old_state == ISIS_ADJ_UP) {
|
|
listnode_delete(circuit->u.bc.adjdb[level - 1],
|
|
adj);
|
|
|
|
circuit->upadjcount[level - 1]--;
|
|
if (circuit->upadjcount[level - 1] == 0)
|
|
isis_tx_queue_clean(circuit->tx_queue);
|
|
|
|
if (new_state == ISIS_ADJ_DOWN)
|
|
del = true;
|
|
}
|
|
|
|
if (circuit->u.bc.lan_neighs[level - 1]) {
|
|
list_delete_all_node(
|
|
circuit->u.bc.lan_neighs[level - 1]);
|
|
isis_adj_build_neigh_list(
|
|
circuit->u.bc.adjdb[level - 1],
|
|
circuit->u.bc.lan_neighs[level - 1]);
|
|
}
|
|
|
|
/* On adjacency state change send new pseudo LSP if we
|
|
* are the DR */
|
|
if (circuit->u.bc.is_dr[level - 1])
|
|
lsp_regenerate_schedule_pseudo(circuit, level);
|
|
}
|
|
|
|
} else if (circuit->circ_type == CIRCUIT_T_P2P) {
|
|
for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
|
|
if ((adj->level & level) == 0)
|
|
continue;
|
|
if (new_state == ISIS_ADJ_UP) {
|
|
circuit->upadjcount[level - 1]++;
|
|
|
|
/* update counter & timers for debugging
|
|
* purposes */
|
|
adj->last_flap = time(NULL);
|
|
adj->flaps++;
|
|
|
|
if (level == IS_LEVEL_1) {
|
|
thread_add_timer(master, send_l1_csnp,
|
|
circuit, 0,
|
|
&circuit->t_send_csnp[0]);
|
|
} else {
|
|
thread_add_timer(master, send_l2_csnp,
|
|
circuit, 0,
|
|
&circuit->t_send_csnp[1]);
|
|
}
|
|
} else if (old_state == ISIS_ADJ_UP) {
|
|
if (adj->circuit->u.p2p.neighbor == adj)
|
|
adj->circuit->u.p2p.neighbor = NULL;
|
|
circuit->upadjcount[level - 1]--;
|
|
if (circuit->upadjcount[level - 1] == 0)
|
|
isis_tx_queue_clean(circuit->tx_queue);
|
|
|
|
if (new_state == ISIS_ADJ_DOWN)
|
|
del = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
hook_call(isis_adj_state_change_hook, adj);
|
|
|
|
if (del) {
|
|
isis_delete_adj(adj);
|
|
*padj = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void isis_adj_print(struct isis_adjacency *adj)
|
|
{
|
|
struct isis_dynhn *dyn;
|
|
|
|
if (!adj)
|
|
return;
|
|
dyn = dynhn_find_by_id(adj->sysid);
|
|
if (dyn)
|
|
zlog_debug("%s", dyn->hostname);
|
|
|
|
zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d",
|
|
sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level,
|
|
adj->hold_time);
|
|
if (adj->ipv4_address_count) {
|
|
zlog_debug("IPv4 Address(es):");
|
|
for (unsigned int i = 0; i < adj->ipv4_address_count; i++)
|
|
zlog_debug("%s", inet_ntoa(adj->ipv4_addresses[i]));
|
|
}
|
|
|
|
if (adj->ipv6_address_count) {
|
|
zlog_debug("IPv6 Address(es):");
|
|
for (unsigned int i = 0; i < adj->ipv6_address_count; i++) {
|
|
char buf[INET6_ADDRSTRLEN];
|
|
inet_ntop(AF_INET6, &adj->ipv6_addresses[i], buf,
|
|
sizeof(buf));
|
|
zlog_debug("%s", buf);
|
|
}
|
|
}
|
|
zlog_debug("Speaks: %s", nlpid2string(&adj->nlpids));
|
|
|
|
return;
|
|
}
|
|
|
|
const char *isis_adj_yang_state(enum isis_adj_state state)
|
|
{
|
|
switch (state) {
|
|
case ISIS_ADJ_DOWN:
|
|
return "down";
|
|
case ISIS_ADJ_UP:
|
|
return "up";
|
|
case ISIS_ADJ_INITIALIZING:
|
|
return "init";
|
|
default:
|
|
return "failed";
|
|
}
|
|
}
|
|
|
|
int isis_adj_expire(struct thread *thread)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
|
|
/*
|
|
* Get the adjacency
|
|
*/
|
|
adj = THREAD_ARG(thread);
|
|
assert(adj);
|
|
adj->t_expire = NULL;
|
|
|
|
/* trigger the adj expire event */
|
|
isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "holding time expired");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* show isis neighbor [detail]
|
|
*/
|
|
void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
|
|
char detail)
|
|
{
|
|
time_t now;
|
|
struct isis_dynhn *dyn;
|
|
int level;
|
|
|
|
dyn = dynhn_find_by_id(adj->sysid);
|
|
if (dyn)
|
|
vty_out(vty, " %-20s", dyn->hostname);
|
|
else
|
|
vty_out(vty, " %-20s", sysid_print(adj->sysid));
|
|
|
|
if (detail == ISIS_UI_LEVEL_BRIEF) {
|
|
if (adj->circuit)
|
|
vty_out(vty, "%-12s", adj->circuit->interface->name);
|
|
else
|
|
vty_out(vty, "NULL circuit!");
|
|
vty_out(vty, "%-3u", adj->level); /* level */
|
|
vty_out(vty, "%-13s", adj_state2string(adj->adj_state));
|
|
now = time(NULL);
|
|
if (adj->last_upd) {
|
|
if (adj->last_upd + adj->hold_time
|
|
< (unsigned long long)now)
|
|
vty_out(vty, " Expiring");
|
|
else
|
|
vty_out(vty, " %-9llu",
|
|
(unsigned long long)adj->last_upd
|
|
+ adj->hold_time - now);
|
|
} else
|
|
vty_out(vty, "- ");
|
|
vty_out(vty, "%-10s", snpa_print(adj->snpa));
|
|
vty_out(vty, "\n");
|
|
}
|
|
|
|
if (detail == ISIS_UI_LEVEL_DETAIL) {
|
|
struct sr_adjacency *sra;
|
|
struct listnode *anode;
|
|
|
|
level = adj->level;
|
|
vty_out(vty, "\n");
|
|
if (adj->circuit)
|
|
vty_out(vty, " Interface: %s",
|
|
adj->circuit->interface->name);
|
|
else
|
|
vty_out(vty, " Interface: NULL circuit");
|
|
vty_out(vty, ", Level: %u", adj->level); /* level */
|
|
vty_out(vty, ", State: %s", adj_state2string(adj->adj_state));
|
|
now = time(NULL);
|
|
if (adj->last_upd) {
|
|
if (adj->last_upd + adj->hold_time
|
|
< (unsigned long long)now)
|
|
vty_out(vty, " Expiring");
|
|
else
|
|
vty_out(vty, ", Expires in %s",
|
|
time2string(adj->last_upd
|
|
+ adj->hold_time - now));
|
|
} else
|
|
vty_out(vty, ", Expires in %s",
|
|
time2string(adj->hold_time));
|
|
vty_out(vty, "\n");
|
|
vty_out(vty, " Adjacency flaps: %u", adj->flaps);
|
|
vty_out(vty, ", Last: %s ago",
|
|
time2string(now - adj->last_flap));
|
|
vty_out(vty, "\n");
|
|
vty_out(vty, " Circuit type: %s",
|
|
circuit_t2string(adj->circuit_t));
|
|
vty_out(vty, ", Speaks: %s", nlpid2string(&adj->nlpids));
|
|
vty_out(vty, "\n");
|
|
if (adj->mt_count != 1
|
|
|| adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) {
|
|
vty_out(vty, " Topologies:\n");
|
|
for (unsigned int i = 0; i < adj->mt_count; i++)
|
|
vty_out(vty, " %s\n",
|
|
isis_mtid2str(adj->mt_set[i]));
|
|
}
|
|
vty_out(vty, " SNPA: %s", snpa_print(adj->snpa));
|
|
if (adj->circuit
|
|
&& (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) {
|
|
dyn = dynhn_find_by_id(adj->lanid);
|
|
if (dyn)
|
|
vty_out(vty, ", LAN id: %s.%02x", dyn->hostname,
|
|
adj->lanid[ISIS_SYS_ID_LEN]);
|
|
else
|
|
vty_out(vty, ", LAN id: %s.%02x",
|
|
sysid_print(adj->lanid),
|
|
adj->lanid[ISIS_SYS_ID_LEN]);
|
|
|
|
vty_out(vty, "\n");
|
|
vty_out(vty, " LAN Priority: %u",
|
|
adj->prio[adj->level - 1]);
|
|
|
|
vty_out(vty, ", %s, DIS flaps: %u, Last: %s ago",
|
|
isis_disflag2string(
|
|
adj->dis_record[ISIS_LEVELS + level - 1]
|
|
.dis),
|
|
adj->dischanges[level - 1],
|
|
time2string(now - (adj->dis_record[ISIS_LEVELS
|
|
+ level - 1]
|
|
.last_dis_change)));
|
|
}
|
|
vty_out(vty, "\n");
|
|
|
|
if (adj->area_address_count) {
|
|
vty_out(vty, " Area Address(es):\n");
|
|
for (unsigned int i = 0; i < adj->area_address_count;
|
|
i++) {
|
|
vty_out(vty, " %s\n",
|
|
isonet_print(adj->area_addresses[i]
|
|
.area_addr,
|
|
adj->area_addresses[i]
|
|
.addr_len));
|
|
}
|
|
}
|
|
if (adj->ipv4_address_count) {
|
|
vty_out(vty, " IPv4 Address(es):\n");
|
|
for (unsigned int i = 0; i < adj->ipv4_address_count;
|
|
i++)
|
|
vty_out(vty, " %s\n",
|
|
inet_ntoa(adj->ipv4_addresses[i]));
|
|
}
|
|
if (adj->ipv6_address_count) {
|
|
vty_out(vty, " IPv6 Address(es):\n");
|
|
for (unsigned int i = 0; i < adj->ipv6_address_count;
|
|
i++) {
|
|
char buf[INET6_ADDRSTRLEN];
|
|
inet_ntop(AF_INET6, &adj->ipv6_addresses[i],
|
|
buf, sizeof(buf));
|
|
vty_out(vty, " %s\n", buf);
|
|
}
|
|
}
|
|
for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) {
|
|
const char *adj_type;
|
|
const char *backup;
|
|
uint32_t sid;
|
|
|
|
switch (sra->adj->circuit->circ_type) {
|
|
case CIRCUIT_T_BROADCAST:
|
|
adj_type = "LAN Adjacency-SID";
|
|
sid = sra->u.ladj_sid->sid;
|
|
break;
|
|
case CIRCUIT_T_P2P:
|
|
adj_type = "Adjacency-SID";
|
|
sid = sra->u.adj_sid->sid;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)"
|
|
: "";
|
|
|
|
vty_out(vty, " %s %s%s: %u\n",
|
|
(sra->nexthop.family == AF_INET) ? "IPv4"
|
|
: "IPv6",
|
|
adj_type, backup, sid);
|
|
}
|
|
vty_out(vty, "\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
void isis_adj_build_neigh_list(struct list *adjdb, struct list *list)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
struct listnode *node;
|
|
|
|
if (!list) {
|
|
zlog_warn("isis_adj_build_neigh_list(): NULL list");
|
|
return;
|
|
}
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) {
|
|
if (!adj) {
|
|
zlog_warn("isis_adj_build_neigh_list(): NULL adj");
|
|
return;
|
|
}
|
|
|
|
if ((adj->adj_state == ISIS_ADJ_UP
|
|
|| adj->adj_state == ISIS_ADJ_INITIALIZING))
|
|
listnode_add(list, adj->snpa);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void isis_adj_build_up_list(struct list *adjdb, struct list *list)
|
|
{
|
|
struct isis_adjacency *adj;
|
|
struct listnode *node;
|
|
|
|
if (adjdb == NULL) {
|
|
zlog_warn("isis_adj_build_up_list(): adjacency DB is empty");
|
|
return;
|
|
}
|
|
|
|
if (!list) {
|
|
zlog_warn("isis_adj_build_up_list(): NULL list");
|
|
return;
|
|
}
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) {
|
|
if (!adj) {
|
|
zlog_warn("isis_adj_build_up_list(): NULL adj");
|
|
return;
|
|
}
|
|
|
|
if (adj->adj_state == ISIS_ADJ_UP)
|
|
listnode_add(list, adj);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int isis_adj_usage2levels(enum isis_adj_usage usage)
|
|
{
|
|
switch (usage) {
|
|
case ISIS_ADJ_LEVEL1:
|
|
return IS_LEVEL_1;
|
|
case ISIS_ADJ_LEVEL2:
|
|
return IS_LEVEL_2;
|
|
case ISIS_ADJ_LEVEL1AND2:
|
|
return IS_LEVEL_1 | IS_LEVEL_2;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|