diff options
Diffstat (limited to 'isisd/isis_circuit.c')
-rw-r--r-- | isisd/isis_circuit.c | 1685 |
1 files changed, 1685 insertions, 0 deletions
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c new file mode 100644 index 0000000..ffa6ad3 --- /dev/null +++ b/isisd/isis_circuit.c @@ -0,0 +1,1685 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> +#ifdef GNU_LINUX +#include <net/ethernet.h> +#else +#include <netinet/if_ether.h> +#endif + +#include "log.h" +#include "memory.h" +#include "vrf.h" +#include "if.h" +#include "linklist.h" +#include "command.h" +#include "frrevent.h" +#include "vty.h" +#include "hash.h" +#include "prefix.h" +#include "stream.h" +#include "qobj.h" +#include "lib/northbound_cli.h" + +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_network.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dr.h" +#include "isisd/isisd.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" +#include "isisd/isis_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" + +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) +{ + struct isis_circuit *circuit; + int i; + + circuit = XCALLOC(MTYPE_ISIS_CIRCUIT, sizeof(struct isis_circuit)); + + circuit->tag = XSTRDUP(MTYPE_ISIS_CIRCUIT, tag); + + /* + * 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; + circuit->flags = 0; + circuit->pad_hellos = ISIS_HELLO_PADDING_ALWAYS; + for (i = 0; i < 2; i++) { + circuit->hello_interval[i] = DEFAULT_HELLO_INTERVAL; + circuit->hello_multiplier[i] = DEFAULT_HELLO_MULTIPLIER; + circuit->csnp_interval[i] = DEFAULT_CSNP_INTERVAL; + circuit->psnp_interval[i] = DEFAULT_PSNP_INTERVAL; + circuit->priority[i] = DEFAULT_PRIORITY; + circuit->metric[i] = DEFAULT_CIRCUIT_METRIC; + circuit->te_metric[i] = DEFAULT_CIRCUIT_METRIC; + circuit->level_arg[i].level = i + 1; + circuit->level_arg[i].circuit = circuit; + } +#endif /* ifndef FABRICD */ + + circuit->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); + + return circuit; +} + +void isis_circuit_del(struct isis_circuit *circuit) +{ + if (!circuit) + return; + + if (circuit->interface->ifindex != IFINDEX_INTERNAL) + isis_circuit_disable(circuit); + + 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_clear(circuit, ISIS_LEVEL1); + isis_lfa_excluded_ifaces_clear(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); + + /* and lastly the circuit itself */ + XFREE(MTYPE_ISIS_CIRCUIT, circuit); + + return; +} + +void isis_circuit_configure(struct isis_circuit *circuit, + struct isis_area *area) +{ + assert(area); + circuit->isis = area->isis; + circuit->area = area; + + /* + * Whenever the is-type of an area is changed, the is-type of each + * circuit + * in that area is updated to a non-empty subset of the area is-type. + * Inversely, when configuring a new circuit, this property should be + * ensured as well. + */ + if (area->is_type != IS_LEVEL_1_AND_2) + circuit->is_type = area->is_type; + + /* + * Add the circuit into area + */ + listnode_add(area->circuit_list, circuit); + + circuit->idx = flags_get_index(&area->flags); + + hook_call(isis_circuit_new_hook, circuit); + + return; +} + +void isis_circuit_deconfigure(struct isis_circuit *circuit, + struct isis_area *area) +{ + hook_call(isis_circuit_del_hook, circuit); + + /* Free the index of SRM and SSN flags */ + flags_free_index(&area->flags, circuit->idx); + circuit->idx = 0; + + /* Reset IS type to configured */ + circuit->is_type = circuit->is_type_config; + + /* Remove circuit from area */ + assert(circuit->area == area); + listnode_delete(area->circuit_list, circuit); + circuit->area = NULL; + circuit->isis = NULL; + + return; +} + +struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp) +{ + return (struct isis_circuit *)ifp->info; +} + +DEFINE_HOOK(isis_circuit_add_addr_hook, (struct isis_circuit *circuit), + (circuit)); + +void isis_circuit_add_addr(struct isis_circuit *circuit, + struct connected *connected) +{ + struct listnode *node; + struct prefix_ipv4 *ipv4; + struct prefix_ipv6 *ipv6; + + if (connected->address->family == AF_INET) { + uint32_t addr = connected->address->u.prefix4.s_addr; + addr = ntohl(addr); + if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr)) + return; + + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ipv4)) + if (prefix_same((struct prefix *)ipv4, + connected->address)) + return; + + ipv4 = prefix_ipv4_new(); + ipv4->prefixlen = connected->address->prefixlen; + ipv4->prefix = connected->address->u.prefix4; + listnode_add(circuit->ip_addrs, ipv4); + + /* Update Local IP address parameter if MPLS TE is enable */ + if (circuit->ext && circuit->area + && IS_MPLS_TE(circuit->area->mta)) { + circuit->ext->local_addr.s_addr = ipv4->prefix.s_addr; + SET_SUBTLV(circuit->ext, EXT_LOCAL_ADDR); + } + + if (circuit->area) + lsp_regenerate_schedule(circuit->area, circuit->is_type, + 0); + +#ifdef EXTREME_DEBUG + 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) { + if (IN6_IS_ADDR_LOOPBACK(&connected->address->u.prefix6)) + return; + + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ipv6)) + if (prefix_same((struct prefix *)ipv6, + connected->address)) + return; + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ipv6)) + if (prefix_same((struct prefix *)ipv6, + connected->address)) + return; + + ipv6 = prefix_ipv6_new(); + ipv6->prefixlen = connected->address->prefixlen; + ipv6->prefix = connected->address->u.prefix6; + + if (IN6_IS_ADDR_LINKLOCAL(&ipv6->prefix)) + listnode_add(circuit->ipv6_link, ipv6); + else { + listnode_add(circuit->ipv6_non_link, ipv6); + /* 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) + lsp_regenerate_schedule(circuit->area, circuit->is_type, + 0); + +#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); + + return; +} + +void isis_circuit_del_addr(struct isis_circuit *circuit, + struct connected *connected) +{ + struct prefix_ipv4 *ipv4, *ip = NULL; + struct listnode *node; + struct prefix_ipv6 *ipv6, *ip6 = NULL; + int found = 0; + + if (connected->address->family == AF_INET) { + ipv4 = prefix_ipv4_new(); + ipv4->prefixlen = connected->address->prefixlen; + ipv4->prefix = connected->address->u.prefix4; + + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ip)) + if (prefix_same((struct prefix *)ip, + (struct prefix *)ipv4)) + break; + + if (ip) { + listnode_delete(circuit->ip_addrs, ip); + prefix_ipv4_free(&ip); + if (circuit->area) + lsp_regenerate_schedule(circuit->area, + circuit->is_type, 0); + } else { + zlog_warn( + "Nonexistent ip address %pFX removal attempt from circuit %s", + connected->address, circuit->interface->name); + zlog_warn("Current ip addresses on %s:", + circuit->interface->name); + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, + ip)) { + zlog_warn(" %pFX", ip); + } + zlog_warn("End of addresses"); + } + + prefix_ipv4_free(&ipv4); + } + if (connected->address->family == AF_INET6) { + ipv6 = prefix_ipv6_new(); + ipv6->prefixlen = connected->address->prefixlen; + ipv6->prefix = connected->address->u.prefix6; + + if (IN6_IS_ADDR_LINKLOCAL(&ipv6->prefix)) { + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, + ip6)) { + if (prefix_same((struct prefix *)ip6, + (struct prefix *)ipv6)) + break; + } + if (ip6) { + listnode_delete(circuit->ipv6_link, ip6); + prefix_ipv6_free(&ip6); + found = 1; + } + } else { + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, + ip6)) { + if (prefix_same((struct prefix *)ip6, + (struct prefix *)ipv6)) + break; + } + if (ip6) { + listnode_delete(circuit->ipv6_non_link, ip6); + prefix_ipv6_free(&ip6); + found = 1; + } + } + + if (!found) { + zlog_warn( + "Nonexistent ip address %pFX removal attempt from circuit %s", + connected->address, circuit->interface->name); + zlog_warn("Current ip addresses on %s:", + circuit->interface->name); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, + ip6)) + zlog_warn(" %pFX", (struct prefix *)ip6); + zlog_warn(" -----"); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, + ip6)) + zlog_warn(" %pFX", (struct prefix *)ip6); + zlog_warn("End of addresses"); + } else if (circuit->area) + lsp_regenerate_schedule(circuit->area, circuit->is_type, + 0); + + prefix_ipv6_free(&ipv6); + } + return; +} + +static uint8_t isis_circuit_id_gen(struct isis *isis, struct interface *ifp) +{ + /* Circuit ids MUST be unique for any broadcast circuits. Otherwise, + * Pseudo-Node LSPs cannot be generated correctly. + * + * Currently, allocate one circuit ID for any circuit, limiting the total + * numer of circuits IS-IS can run on to 255. + * + * We should revisit this when implementing 3-way adjacencies for p2p, since + * we then have extended interface IDs available. + */ + uint8_t id = ifp->ifindex; + unsigned int i; + + for (i = 0; i < 256; i++) { + if (id && !_ISIS_CHECK_FLAG(isis->circuit_ids_used, id)) + break; + id++; + } + + if (i == 256) { + zlog_warn("Could not allocate a circuit id for '%s'", + ifp->name); + return 0; + } + + _ISIS_SET_FLAG(isis->circuit_ids_used, id); + return id; +} + +void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp) +{ + struct listnode *node, *nnode; + struct connected *conn; + + if (if_is_broadcast(ifp)) { + if (fabricd || circuit->circ_type_config == CIRCUIT_T_P2P) + circuit->circ_type = CIRCUIT_T_P2P; + else + circuit->circ_type = CIRCUIT_T_BROADCAST; + } else if (if_is_pointopoint(ifp)) { + circuit->circ_type = CIRCUIT_T_P2P; + } else if (if_is_loopback(ifp)) { + circuit->circ_type = CIRCUIT_T_LOOPBACK; + circuit->is_passive = 1; + } else { + /* It's normal in case of loopback etc. */ + if (IS_DEBUG_EVENTS) + zlog_debug("%s: unsupported media", __func__); + circuit->circ_type = CIRCUIT_T_UNKNOWN; + } + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn)) + isis_circuit_add_addr(circuit, conn); + +} + +void isis_circuit_if_del(struct isis_circuit *circuit, struct interface *ifp) +{ + struct listnode *node, *nnode; + struct connected *conn; + + assert(circuit->interface == ifp); + + /* destroy addresses */ + for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn)) + isis_circuit_del_addr(circuit, conn); + + circuit->circ_type = CIRCUIT_T_UNKNOWN; +} + +void isis_circuit_if_bind(struct isis_circuit *circuit, struct interface *ifp) +{ + assert(circuit != NULL); + assert(ifp != NULL); + if (circuit->interface) + assert(circuit->interface == ifp); + else + circuit->interface = ifp; + if (ifp->info) + assert(ifp->info == circuit); + else + ifp->info = circuit; +} + +void isis_circuit_if_unbind(struct isis_circuit *circuit, struct interface *ifp) +{ + assert(circuit != NULL); + assert(ifp != NULL); + assert(circuit->interface == ifp); + assert(ifp->info == circuit); + circuit->interface = NULL; + ifp->info = NULL; +} + +static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, + int is_set) +{ + struct isis_area *area; + struct isis_lsp *lsp; + int level; + + assert(circuit); + area = circuit->area; + assert(area); + for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { + if (!(level & circuit->is_type)) + continue; + + if (!lspdb_count(&area->lspdb[level - 1])) + continue; + + frr_each (lspdb, &area->lspdb[level - 1], lsp) { + if (is_set) { + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); + } else { + isis_tx_queue_del(circuit->tx_queue, lsp); + } + } + } +} + +size_t isis_circuit_pdu_size(struct isis_circuit *circuit) +{ + return ISO_MTU(circuit); +} + +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 +} + +int isis_circuit_up(struct isis_circuit *circuit) +{ + int retv; + + /* Set the flags for all the lsps of the circuit. */ + isis_circuit_update_all_srmflags(circuit, 1); + + if (circuit->state == C_STATE_UP) + return ISIS_OK; + + if (circuit->is_passive) { + 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; + } + 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); + + /* 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; + } + + /* + * 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); +#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]); + } + + /* 8.4.1 b) FIXME: solicit ES - 8.4.6 */ + /* 8.4.1 c) FIXME: listen for ESH PDUs */ + } else if (circuit->circ_type == CIRCUIT_T_P2P) { + /* initializing the hello send threads + * for a ptp IF + */ + circuit->u.p2p.neighbor = NULL; + send_hello_sched(circuit, 0, TRIGGERED_IIH_DELAY); + } + + /* initializing PSNP timers */ + if (circuit->is_type & IS_LEVEL_1) + event_add_timer( + master, send_l1_psnp, circuit, + isis_jitter(circuit->psnp_interval[0], PSNP_JITTER), + &circuit->t_send_psnp[0]); + + if (circuit->is_type & IS_LEVEL_2) + event_add_timer( + master, send_l2_psnp, circuit, + isis_jitter(circuit->psnp_interval[1], PSNP_JITTER), + &circuit->t_send_psnp[1]); + + /* unified init for circuits; ignore warnings below this level */ + retv = isis_sock_init(circuit); + if (retv != ISIS_OK) { + isis_circuit_down(circuit); + return retv; + } + + /* initialize the circuit streams after opening connection */ + isis_circuit_stream(circuit, &circuit->rcv_stream); + isis_circuit_stream(circuit, &circuit->snd_stream); + + isis_circuit_prepare(circuit); + + circuit->tx_queue = isis_tx_queue_new(circuit, send_lsp); + + 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 */ + + return ISIS_OK; +} + +void isis_circuit_down(struct isis_circuit *circuit) +{ +#ifndef FABRICD + /* send northbound notification */ + isis_notif_if_state_change(circuit, true); +#endif /* ifndef FABRICD */ + + 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); + } + } + } + + /* Clear the flags for all the lsps of the circuit. */ + isis_circuit_update_all_srmflags(circuit, 0); + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + /* destroy neighbour lists */ + if (circuit->u.bc.lan_neighs[0]) { + list_delete(&circuit->u.bc.lan_neighs[0]); + circuit->u.bc.lan_neighs[0] = NULL; + } + if (circuit->u.bc.lan_neighs[1]) { + list_delete(&circuit->u.bc.lan_neighs[1]); + circuit->u.bc.lan_neighs[1] = NULL; + } + /* destroy adjacency databases */ + if (circuit->u.bc.adjdb[0]) { + circuit->u.bc.adjdb[0]->del = isis_delete_adj; + list_delete(&circuit->u.bc.adjdb[0]); + circuit->u.bc.adjdb[0] = NULL; + } + if (circuit->u.bc.adjdb[1]) { + circuit->u.bc.adjdb[1]->del = isis_delete_adj; + list_delete(&circuit->u.bc.adjdb[1]); + circuit->u.bc.adjdb[1] = NULL; + } + if (circuit->u.bc.is_dr[0]) { + isis_dr_resign(circuit, 1); + circuit->u.bc.is_dr[0] = 0; + } + memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); + if (circuit->u.bc.is_dr[1]) { + isis_dr_resign(circuit, 2); + circuit->u.bc.is_dr[1] = 0; + } + memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); + memset(circuit->u.bc.snpa, 0, ETH_ALEN); + + 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) { + 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; + + /* 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; + } + + /* send one gratuitous hello to spead up convergence */ + if (circuit->state == C_STATE_UP) { + if (circuit->is_type & IS_LEVEL_1) + send_hello(circuit, IS_LEVEL_1); + if (circuit->is_type & IS_LEVEL_2) + send_hello(circuit, IS_LEVEL_2); + } + + circuit->upadjcount[0] = 0; + circuit->upadjcount[1] = 0; + + /* close the socket */ + if (circuit->fd) { + close(circuit->fd); + circuit->fd = 0; + } + + if (circuit->rcv_stream != NULL) { + stream_free(circuit->rcv_stream); + circuit->rcv_stream = NULL; + } + + if (circuit->snd_stream != NULL) { + stream_free(circuit->snd_stream); + circuit->snd_stream = NULL; + } + + event_cancel_event(master, circuit); + + return; +} + +void circuit_update_nlpids(struct isis_circuit *circuit) +{ + circuit->nlpids.count = 0; + + if (circuit->ip_router) { + circuit->nlpids.nlpids[0] = NLPID_IP; + circuit->nlpids.count++; + } + if (circuit->ipv6_router) { + circuit->nlpids.nlpids[circuit->nlpids.count] = NLPID_IPV6; + circuit->nlpids.count++; + } + return; +} + +void isis_circuit_print_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[0]); + else + json_object_int_add(level_json, "metric", + circuit->metric[0]); + if (!circuit->is_passive) { + json_object_int_add(level_json, + "active-neighbors", + circuit->upadjcount[0]); + json_object_int_add(level_json, + "hello-interval", + circuit->hello_interval[0]); + 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[0]); + json_object_string_add( + hold_json, "pad", + isis_hello_padding2string( + circuit->pad_hellos)); + json_object_int_add(level_json, "cnsp-interval", + circuit->csnp_interval[0]); + json_object_int_add(level_json, "psnp-interval", + circuit->psnp_interval[0]); + 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[0]); + json_object_string_add( + lan_prio_json, "is-dis", + (circuit->u.bc.is_dr[0] + ? "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; +} + +void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, + char detail) +{ + if (detail == ISIS_UI_LEVEL_BRIEF) { + vty_out(vty, " %-12s", circuit->interface->name); + vty_out(vty, "0x%-7x", circuit->circuit_id); + vty_out(vty, "%-9s", circuit_state2string(circuit->state)); + vty_out(vty, "%-9s", circuit_type2string(circuit->circ_type)); + vty_out(vty, "%-9s", circuit_t2string(circuit->is_type)); + vty_out(vty, "\n"); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) { + struct listnode *node; + struct prefix *ip_addr; + + vty_out(vty, " Interface: %s", circuit->interface->name); + vty_out(vty, ", State: %s", + circuit_state2string(circuit->state)); + if (circuit->is_passive) + vty_out(vty, ", Passive"); + else + vty_out(vty, ", Active"); + vty_out(vty, ", Circuit Id: 0x%x", circuit->circuit_id); + vty_out(vty, "\n"); + vty_out(vty, " Type: %s", + circuit_type2string(circuit->circ_type)); + vty_out(vty, ", Level: %s", circuit_t2string(circuit->is_type)); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + vty_out(vty, ", SNPA: %-10pSY", circuit->u.bc.snpa); + vty_out(vty, "\n"); + if (circuit->is_type & IS_LEVEL_1) { + vty_out(vty, " Level-1 Information:\n"); + if (circuit->area->newmetric) + vty_out(vty, " Metric: %d", + circuit->te_metric[0]); + else + vty_out(vty, " Metric: %d", + circuit->metric[0]); + if (!circuit->is_passive) { + vty_out(vty, ", Active neighbors: %u\n", + circuit->upadjcount[0]); + vty_out(vty, + " Hello interval: %u, Holddown count: %u, Padding: %s\n", + circuit->hello_interval[0], + circuit->hello_multiplier[0], + isis_hello_padding2string( + circuit->pad_hellos)); + vty_out(vty, + " CNSP interval: %u, PSNP interval: %u\n", + circuit->csnp_interval[0], + circuit->psnp_interval[0]); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + vty_out(vty, + " LAN Priority: %u, %s\n", + circuit->priority[0], + (circuit->u.bc.is_dr[0] + ? "is DIS" + : "is not DIS")); + } else { + vty_out(vty, "\n"); + } + } + if (circuit->is_type & IS_LEVEL_2) { + vty_out(vty, " Level-2 Information:\n"); + if (circuit->area->newmetric) + vty_out(vty, " Metric: %d", + circuit->te_metric[1]); + else + vty_out(vty, " Metric: %d", + circuit->metric[1]); + if (!circuit->is_passive) { + vty_out(vty, ", Active neighbors: %u\n", + circuit->upadjcount[1]); + vty_out(vty, + " Hello interval: %u, Holddown count: %u, Padding: %s\n", + circuit->hello_interval[1], + circuit->hello_multiplier[1], + isis_hello_padding2string( + circuit->pad_hellos)); + vty_out(vty, + " CNSP interval: %u, PSNP interval: %u\n", + circuit->csnp_interval[1], + circuit->psnp_interval[1]); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + vty_out(vty, + " LAN Priority: %u, %s\n", + circuit->priority[1], + (circuit->u.bc.is_dr[1] + ? "is DIS" + : "is not DIS")); + } else { + vty_out(vty, "\n"); + } + } + if (listcount(circuit->ip_addrs) > 0) { + vty_out(vty, " IP Prefix(es):\n"); + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, + ip_addr)) + vty_out(vty, " %pFX\n", ip_addr); + } + if (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"); + } + return; +} + +#ifdef FABRICD +DEFINE_HOOK(isis_circuit_config_write, + (struct isis_circuit *circuit, struct vty *vty), + (circuit, vty)); + +static int isis_interface_config_write(struct vty *vty) +{ + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + int write = 0; + struct interface *ifp; + struct isis_circuit *circuit; + int i; + + FOR_ALL_INTERFACES (vrf, ifp) { + /* IF name */ + if_vty_config_start(vty, ifp); + write++; + /* IF desc */ + if (ifp->desc) { + vty_out(vty, " description %s\n", ifp->desc); + write++; + } + /* ISIS Circuit */ + do { + circuit = circuit_scan_by_ifp(ifp); + if (circuit == NULL) + break; + if (circuit->ip_router) { + vty_out(vty, " ip router " PROTO_NAME " %s\n", + circuit->tag); + write++; + } + if (circuit->is_passive) { + vty_out(vty, " " PROTO_NAME " passive\n"); + write++; + } + if (circuit->circ_type_config == CIRCUIT_T_P2P) { + vty_out(vty, " " PROTO_NAME " network point-to-point\n"); + write++; + } + if (circuit->ipv6_router) { + vty_out(vty, " ipv6 router " PROTO_NAME " %s\n", + circuit->tag); + write++; + } + + /* ISIS - circuit type */ + if (!fabricd) { + if (circuit->is_type == IS_LEVEL_1) { + vty_out(vty, " " PROTO_NAME " circuit-type level-1\n"); + write++; + } else { + if (circuit->is_type == IS_LEVEL_2) { + vty_out(vty, + " " PROTO_NAME " circuit-type level-2-only\n"); + write++; + } + } + } + + /* ISIS - CSNP interval */ + if (circuit->csnp_interval[0] + == circuit->csnp_interval[1]) { + if (circuit->csnp_interval[0] + != DEFAULT_CSNP_INTERVAL) { + vty_out(vty, " " PROTO_NAME " csnp-interval %d\n", + circuit->csnp_interval[0]); + write++; + } + } else { + for (i = 0; i < 2; i++) { + if (circuit->csnp_interval[i] + != DEFAULT_CSNP_INTERVAL) { + vty_out(vty, + " " PROTO_NAME " csnp-interval %d level-%d\n", + circuit->csnp_interval + [i], + i + 1); + write++; + } + } + } + + /* ISIS - PSNP interval */ + if (circuit->psnp_interval[0] + == circuit->psnp_interval[1]) { + if (circuit->psnp_interval[0] + != DEFAULT_PSNP_INTERVAL) { + vty_out(vty, " " PROTO_NAME " psnp-interval %d\n", + circuit->psnp_interval[0]); + write++; + } + } else { + for (i = 0; i < 2; i++) { + if (circuit->psnp_interval[i] + != DEFAULT_PSNP_INTERVAL) { + vty_out(vty, + " " PROTO_NAME " psnp-interval %d level-%d\n", + circuit->psnp_interval + [i], + i + 1); + write++; + } + } + } + + /* ISIS - Hello padding - Defaults to 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"); + 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; + } + + if (circuit->disable_threeway_adj) { + vty_out(vty, " no isis three-way-handshake\n"); + write++; + } + + /* ISIS - Hello interval */ + if (circuit->hello_interval[0] + == circuit->hello_interval[1]) { + if (circuit->hello_interval[0] + != DEFAULT_HELLO_INTERVAL) { + vty_out(vty, + " " PROTO_NAME " hello-interval %d\n", + circuit->hello_interval[0]); + write++; + } + } else { + for (i = 0; i < 2; i++) { + if (circuit->hello_interval[i] + != DEFAULT_HELLO_INTERVAL) { + vty_out(vty, + " " PROTO_NAME " hello-interval %d level-%d\n", + circuit->hello_interval + [i], + i + 1); + write++; + } + } + } + + /* ISIS - Hello Multiplier */ + if (circuit->hello_multiplier[0] + == circuit->hello_multiplier[1]) { + if (circuit->hello_multiplier[0] + != DEFAULT_HELLO_MULTIPLIER) { + vty_out(vty, + " " PROTO_NAME " hello-multiplier %d\n", + circuit->hello_multiplier[0]); + write++; + } + } else { + for (i = 0; i < 2; i++) { + if (circuit->hello_multiplier[i] + != DEFAULT_HELLO_MULTIPLIER) { + vty_out(vty, + " " PROTO_NAME " hello-multiplier %d level-%d\n", + circuit->hello_multiplier + [i], + i + 1); + write++; + } + } + } + + /* ISIS - Priority */ + if (circuit->priority[0] == circuit->priority[1]) { + if (circuit->priority[0] != DEFAULT_PRIORITY) { + vty_out(vty, " " PROTO_NAME " priority %d\n", + circuit->priority[0]); + write++; + } + } else { + for (i = 0; i < 2; i++) { + if (circuit->priority[i] + != DEFAULT_PRIORITY) { + vty_out(vty, + " " PROTO_NAME " priority %d level-%d\n", + circuit->priority[i], + i + 1); + write++; + } + } + } + + /* ISIS - Metric */ + if (circuit->te_metric[0] == circuit->te_metric[1]) { + if (circuit->te_metric[0] + != DEFAULT_CIRCUIT_METRIC) { + vty_out(vty, " " PROTO_NAME " metric %d\n", + circuit->te_metric[0]); + write++; + } + } else { + for (i = 0; i < 2; i++) { + if (circuit->te_metric[i] + != DEFAULT_CIRCUIT_METRIC) { + vty_out(vty, + " " PROTO_NAME " metric %d level-%d\n", + circuit->te_metric[i], + i + 1); + write++; + } + } + } + if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) { + vty_out(vty, " " PROTO_NAME " password md5 %s\n", + circuit->passwd.passwd); + write++; + } else if (circuit->passwd.type + == ISIS_PASSWD_TYPE_CLEARTXT) { + vty_out(vty, " " PROTO_NAME " password clear %s\n", + circuit->passwd.passwd); + write++; + } + if (circuit->bfd_config.enabled) { + vty_out(vty, " " PROTO_NAME " bfd\n"); + write++; + } + write += hook_call(isis_circuit_config_write, + circuit, vty); + } while (0); + if_vty_config_end(vty); + } + + return write; +} +#endif /* ifdef FABRICD */ + +void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, + bool ipv6_router) +{ + struct isis_area *area = circuit->area; + int old_ipr = circuit->ip_router; + int old_ipv6r = circuit->ipv6_router; + + /* is there something to do? */ + if (old_ipr == ip_router && old_ipv6r == ipv6_router) + return; + + circuit->ip_router = ip_router; + circuit->ipv6_router = ipv6_router; + circuit_update_nlpids(circuit); + + 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); + } +} + +ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive) +{ + if (circuit->is_passive == passive) + return ferr_ok(); + + if (if_is_loopback(circuit->interface) && !passive) + return ferr_cfg_invalid("loopback is always passive"); + + if (circuit->state != C_STATE_UP) { + circuit->is_passive = passive; + } else { + struct isis_area *area = circuit->area; + isis_csm_state_change(ISIS_DISABLE, circuit, area); + circuit->is_passive = passive; + isis_csm_state_change(ISIS_ENABLE, circuit, area); + } + + return ferr_ok(); +} + +ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level, + int metric) +{ + assert(level == IS_LEVEL_1 || level == IS_LEVEL_2); + if (metric > MAX_WIDE_LINK_METRIC) + return ferr_cfg_invalid("metric %d too large for wide metric", + metric); + if (circuit->area && circuit->area->oldmetric + && metric > MAX_NARROW_LINK_METRIC) + return ferr_cfg_invalid("metric %d too large for narrow metric", + metric); + + /* 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(); +} + +ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit) +{ + memset(&circuit->passwd, 0, sizeof(circuit->passwd)); + return ferr_ok(); +} + +ferr_r isis_circuit_passwd_set(struct isis_circuit *circuit, + uint8_t passwd_type, const char *passwd) +{ + int len; + + if (!passwd) + return ferr_code_bug("no circuit password given"); + + len = strlen(passwd); + if (len > 254) + return ferr_code_bug( + "circuit password too long (max 254 chars)"); + + circuit->passwd.len = len; + strlcpy((char *)circuit->passwd.passwd, passwd, + sizeof(circuit->passwd.passwd)); + circuit->passwd.type = passwd_type; + return ferr_ok(); +} + +ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit, + const char *passwd) +{ + return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_CLEARTXT, + passwd); +} + +ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, + const char *passwd) +{ + return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_HMAC_MD5, + passwd); +} + +void isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) +{ + if (circuit->circ_type == circ_type) + return; + + if (circuit->state != C_STATE_UP) { + circuit->circ_type = circ_type; + circuit->circ_type_config = circ_type; + } else { + struct isis_area *area = circuit->area; + + isis_csm_state_change(ISIS_DISABLE, circuit, area); + circuit->circ_type = circ_type; + circuit->circ_type_config = circ_type; + isis_csm_state_change(ISIS_ENABLE, circuit, area); + } +} + +int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid, + bool enabled) +{ + struct isis_circuit_mt_setting *setting; + + setting = circuit_get_mt_setting(circuit, mtid); + if (setting->enabled != enabled) { + setting->enabled = enabled; + if (circuit->area) + lsp_regenerate_schedule(circuit->area, + IS_LEVEL_1 | IS_LEVEL_2, 0); + } + + return CMD_SUCCESS; +} + +int isis_if_new_hook(struct interface *ifp) +{ + return 0; +} + +int isis_if_delete_hook(struct interface *ifp) +{ + if (ifp->info) + isis_circuit_del(ifp->info); + + 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; +} + +void isis_circuit_init(void) +{ + /* Initialize Zebra interface data structure */ + hook_register_prio(if_add, 0, isis_if_new_hook); + hook_register_prio(if_del, 0, isis_if_delete_hook); + + /* Install interface node */ +#ifdef FABRICD + if_cmd_init(isis_interface_config_write); +#else + if_cmd_init_default(); +#endif + if_zapi_callbacks(isis_ifp_create, isis_ifp_up, + isis_ifp_down, isis_ifp_destroy); +} |