// SPDX-License-Identifier: GPL-2.0-or-later /* * 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 */ #include #include "log.h" #include "memory.h" #include "hash.h" #include "vty.h" #include "linklist.h" #include "frrevent.h" #include "if.h" #include "stream.h" #include "bfd.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" DEFINE_MTYPE_STATIC(ISISD, ISIS_ADJACENCY, "ISIS adjacency"); DEFINE_MTYPE(ISISD, ISIS_ADJACENCY_INFO, "ISIS adjacency info"); static struct isis_adjacency *adj_alloc(struct isis_circuit *circuit, 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); adj->snmp_idx = ++circuit->snmp_adj_idx_gen; if (circuit->snmp_adj_list == NULL) circuit->snmp_adj_list = list_new(); adj->snmp_list_node = listnode_add(circuit->snmp_adj_list, adj); 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(circuit, 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(); adj->srv6_endx_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; /* Remove self from snmp list without walking the list*/ list_delete_node(adj->circuit->snmp_adj_list, adj->snmp_list_node); EVENT_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->ll_ipv6_addrs); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->global_ipv6_addrs); adj_mt_finish(adj); list_delete(&adj->adj_sids); list_delete(&adj->srv6_endx_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 */ } static void isis_adj_route_switchover(struct isis_adjacency *adj) { union g_addr ip = {}; ifindex_t ifindex; unsigned int i; if (!adj->circuit || !adj->circuit->interface) return; ifindex = adj->circuit->interface->ifindex; for (i = 0; i < adj->ipv4_address_count; i++) { ip.ipv4 = adj->ipv4_addresses[i]; isis_circuit_switchover_routes(adj->circuit, AF_INET, &ip, ifindex); } for (i = 0; i < adj->ll_ipv6_count; i++) { ip.ipv6 = adj->ll_ipv6_addrs[i]; isis_circuit_switchover_routes(adj->circuit, AF_INET6, &ip, ifindex); } for (i = 0; i < adj->global_ipv6_count; i++) { ip.ipv6 = adj->global_ipv6_addrs[i]; isis_circuit_switchover_routes(adj->circuit, AF_INET6, &ip, ifindex); } } 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; } const char *isis_adj_name(const struct isis_adjacency *adj) { static char buf[ISO_SYSID_STRLEN]; if (!adj) return "NONE"; struct isis_dynhn *dyn; dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); if (dyn) return dyn->hostname; snprintfrr(buf, sizeof(buf), "%pSY", adj->sysid); return buf; } void isis_log_adj_change(struct isis_adjacency *adj, enum isis_adj_state old_state, enum isis_adj_state new_state, const char *reason) { zlog_info( "%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s", isis_adj_name(adj), 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; if (old_state == ISIS_ADJ_UP && !CHECK_FLAG(adj->circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z)) { if (IS_DEBUG_EVENTS) zlog_debug( "ISIS-Adj (%s): Starting fast-reroute on state change %d->%d: %s", circuit->area->area_tag, old_state, new_state, reason ? reason : "unspecified"); isis_adj_route_switchover(adj); } 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); #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->adj_state_changes++; circuit->upadjcount[level - 1]++; /* update counter & timers for debugging * purposes */ adj->last_flap = time(NULL); adj->flaps++; } else if (old_state == ISIS_ADJ_UP) { circuit->adj_state_changes++; circuit->upadjcount[level - 1]--; if (circuit->upadjcount[level - 1] == 0) isis_tx_queue_clean(circuit->tx_queue); if (new_state == ISIS_ADJ_DOWN) { listnode_delete( circuit->u.bc.adjdb[level - 1], adj); 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) { event_add_timer( master, send_l1_csnp, circuit, 0, &circuit->t_send_csnp[0]); } else { event_add_timer( master, send_l2_csnp, circuit, 0, &circuit->t_send_csnp[1]); } } else if (old_state == ISIS_ADJ_UP) { circuit->upadjcount[level - 1]--; if (circuit->upadjcount[level - 1] == 0) isis_tx_queue_clean(circuit->tx_queue); if (new_state == ISIS_ADJ_DOWN) { if (adj->circuit->u.p2p.neighbor == adj) adj->circuit->u.p2p.neighbor = NULL; 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->circuit->isis, adj->sysid); if (dyn) zlog_debug("%s", dyn->hostname); zlog_debug("SystemId %20pSY SNPA %pSY, level %d; Holding Time %d", adj->sysid, 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("%pI4", &adj->ipv4_addresses[i]); } if (adj->ll_ipv6_count) { zlog_debug("IPv6 Address(es):"); for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[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"; case ISIS_ADJ_UNKNOWN: return "failed"; } assert(!"Reached end of function where we are not expecting to"); } void isis_adj_expire(struct event *thread) { struct isis_adjacency *adj; /* * Get the adjacency */ adj = EVENT_ARG(thread); assert(adj); adj->t_expire = NULL; /* trigger the adj expire event */ isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "holding time expired"); } /* * show isis neighbor [detail] json */ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, char detail) { json_object *iface_json, *ipv4_addr_json, *ipv6_link_json, *ipv6_non_link_json, *topo_json, *dis_flaps_json, *area_addr_json, *adj_sid_json; time_t now; struct isis_dynhn *dyn; int level; char buf[256]; json_object_string_add(json, "adj", isis_adj_name(adj)); if (detail == ISIS_UI_LEVEL_BRIEF) { if (adj->circuit) json_object_string_add(json, "interface", adj->circuit->interface->name); else json_object_string_add(json, "interface", "NULL circuit!"); json_object_int_add(json, "level", adj->level); json_object_string_add(json, "state", adj_state2string(adj->adj_state)); now = time(NULL); if (adj->last_upd) { if (adj->last_upd + adj->hold_time < now) json_object_string_add(json, "last-upd", "expiring"); else json_object_string_add( json, "expires-in", time2string(adj->last_upd + adj->hold_time - now)); } json_object_string_addf(json, "snpa", "%pSY", adj->snpa); } if (detail == ISIS_UI_LEVEL_DETAIL) { struct sr_adjacency *sra; struct listnode *anode; level = adj->level; iface_json = json_object_new_object(); json_object_object_add(json, "interface", iface_json); if (adj->circuit) json_object_string_add(iface_json, "name", adj->circuit->interface->name); else json_object_string_add(iface_json, "name", "null-circuit"); json_object_int_add(json, "level", adj->level); json_object_string_add(iface_json, "state", adj_state2string(adj->adj_state)); now = time(NULL); if (adj->last_upd) { if (adj->last_upd + adj->hold_time < now) json_object_string_add(iface_json, "last-upd", "expiring"); else json_object_string_add( json, "expires-in", time2string(adj->last_upd + adj->hold_time - now)); } else json_object_string_add(json, "expires-in", time2string(adj->hold_time)); json_object_int_add(iface_json, "adj-flaps", adj->flaps); json_object_string_add(iface_json, "last-ago", time2string(now - adj->last_flap)); json_object_string_add(iface_json, "circuit-type", circuit_t2string(adj->circuit_t)); json_object_string_add(iface_json, "speaks", nlpid2string(&adj->nlpids)); if (adj->mt_count != 1 || adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) { topo_json = json_object_new_object(); json_object_object_add(iface_json, "topologies", topo_json); for (unsigned int i = 0; i < adj->mt_count; i++) { snprintfrr(buf, sizeof(buf), "topo-%d", i); json_object_string_add( topo_json, buf, isis_mtid2str(adj->mt_set[i])); } } json_object_string_addf(iface_json, "snpa", "%pSY", adj->snpa); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); if (dyn) { snprintfrr(buf, sizeof(buf), "%s-%02x", dyn->hostname, adj->lanid[ISIS_SYS_ID_LEN]); json_object_string_add(iface_json, "lan-id", buf); } else { json_object_string_addf(iface_json, "lan-id", "%pSY", adj->lanid); } json_object_int_add(iface_json, "lan-prio", adj->prio[adj->level - 1]); dis_flaps_json = json_object_new_object(); json_object_object_add(iface_json, "dis-flaps", dis_flaps_json); json_object_string_add( dis_flaps_json, "dis-record", isis_disflag2string( adj->dis_record[ISIS_LEVELS + level - 1] .dis)); json_object_int_add(dis_flaps_json, "last", adj->dischanges[level - 1]); json_object_string_add( dis_flaps_json, "ago", time2string(now - (adj->dis_record[ISIS_LEVELS + level - 1] .last_dis_change))); } if (adj->area_address_count) { area_addr_json = json_object_new_object(); json_object_object_add(iface_json, "area-address", area_addr_json); for (unsigned int i = 0; i < adj->area_address_count; i++) { json_object_string_addf( area_addr_json, "isonet", "%pIS", &adj->area_addresses[i]); } } if (adj->ipv4_address_count) { ipv4_addr_json = json_object_new_object(); json_object_object_add(iface_json, "ipv4-address", ipv4_addr_json); for (unsigned int i = 0; i < adj->ipv4_address_count; i++){ inet_ntop(AF_INET, &adj->ipv4_addresses[i], buf, sizeof(buf)); json_object_string_add(ipv4_addr_json, "ipv4", buf); } } if (adj->ll_ipv6_count) { ipv6_link_json = json_object_new_object(); json_object_object_add(iface_json, "ipv6-link-local", ipv6_link_json); for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], buf, sizeof(buf)); json_object_string_add(ipv6_link_json, "ipv6", buf); } } if (adj->global_ipv6_count) { ipv6_non_link_json = json_object_new_object(); json_object_object_add(iface_json, "ipv6-global", ipv6_non_link_json); for (unsigned int i = 0; i < adj->global_ipv6_count; i++) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &adj->global_ipv6_addrs[i], buf, sizeof(buf)); json_object_string_add(ipv6_non_link_json, "ipv6", buf); } } adj_sid_json = json_object_new_object(); json_object_object_add(iface_json, "adj-sid", adj_sid_json); 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)" : ""; json_object_string_add(adj_sid_json, "nexthop", (sra->nexthop.family == AF_INET) ? "IPv4" : "IPv6"); json_object_string_add(adj_sid_json, "adj-type", adj_type); json_object_string_add(adj_sid_json, "is-backup", backup); json_object_int_add(adj_sid_json, "sid", sid); } } return; } /* * 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; vty_out(vty, " %-20s", isis_adj_name(adj)); 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 < 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, "%-10pSY", 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 < 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: %pSY", adj->snpa); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->circuit->isis, 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: %pPN", adj->lanid); 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, " %pIS\n", &adj->area_addresses[i]); } } 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, " %pI4\n", &adj->ipv4_addresses[i]); } if (adj->ll_ipv6_count) { vty_out(vty, " IPv6 Address(es):\n"); for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], buf, sizeof(buf)); vty_out(vty, " %s\n", buf); } } if (adj->global_ipv6_count) { vty_out(vty, " Global IPv6 Address(es):\n"); for (unsigned int i = 0; i < adj->global_ipv6_count; i++) { char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &adj->global_ipv6_addrs[i], buf, sizeof(buf)); vty_out(vty, " %s\n", buf); } } if (adj->circuit && adj->circuit->bfd_config.enabled) { vty_out(vty, " BFD is %s%s\n", adj->bfd_session ? "active, status " : "configured", !adj->bfd_session ? "" : bfd_get_status_str(bfd_sess_status( adj->bfd_session))); } 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("%s: NULL list", __func__); return; } for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) { if (!adj) { zlog_warn("%s: NULL adj", __func__); 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("%s: adjacency DB is empty", __func__); return; } if (!list) { zlog_warn("%s: NULL list", __func__); return; } for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) { if (!adj) { zlog_warn("%s: NULL adj", __func__); 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; case ISIS_ADJ_NONE: return 0; } assert(!"Reached end of function where we are not expecting to"); }