diff options
author | Renato Westphal <renato@opensourcerouting.org> | 2021-03-15 02:14:27 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-15 02:14:27 +0100 |
commit | e1908ceb4242dffc97a4204da4ffa57a52c5512d (patch) | |
tree | 1ce6d9d02aeed10d72896a816fe028028e829c88 /isisd | |
parent | Merge pull request #8160 from idryzhov/fix-show-yang-oper (diff) | |
parent | tests: Adding test for ISIS SNMP (diff) | |
download | frr-e1908ceb4242dffc97a4204da4ffa57a52c5512d.tar.xz frr-e1908ceb4242dffc97a4204da4ffa57a52c5512d.zip |
Merge pull request #7945 from volta-networks/feat_isis_snmp
isisd: add support for read-only snmp mibs objects
Diffstat (limited to 'isisd')
-rw-r--r-- | isisd/isis_adjacency.c | 17 | ||||
-rw-r--r-- | isisd/isis_adjacency.h | 2 | ||||
-rw-r--r-- | isisd/isis_circuit.c | 74 | ||||
-rw-r--r-- | isisd/isis_circuit.h | 9 | ||||
-rw-r--r-- | isisd/isis_dr.c | 1 | ||||
-rw-r--r-- | isisd/isis_dynhn.c | 35 | ||||
-rw-r--r-- | isisd/isis_dynhn.h | 3 | ||||
-rw-r--r-- | isisd/isis_lsp.c | 8 | ||||
-rw-r--r-- | isisd/isis_nb.h | 89 | ||||
-rw-r--r-- | isisd/isis_nb_notifications.c | 136 | ||||
-rw-r--r-- | isisd/isis_pdu.c | 180 | ||||
-rw-r--r-- | isisd/isis_snmp.c | 3457 | ||||
-rw-r--r-- | isisd/isis_spf.c | 16 | ||||
-rw-r--r-- | isisd/isisd.c | 13 | ||||
-rw-r--r-- | isisd/isisd.h | 18 | ||||
-rw-r--r-- | isisd/subdir.am | 10 |
16 files changed, 3964 insertions, 104 deletions
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 71d475816..3c3a68764 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -49,13 +49,21 @@ #include "isisd/fabricd.h" #include "isisd/isis_nb.h" -static struct isis_adjacency *adj_alloc(const uint8_t *id) +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; } @@ -65,7 +73,7 @@ struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, struct isis_adjacency *adj; int i; - adj = adj_alloc(id); /* P2P kludge */ + adj = adj_alloc(circuit, id); /* P2P kludge */ if (snpa) { memcpy(adj->snpa, snpa, ETH_ALEN); @@ -146,6 +154,8 @@ void isis_delete_adj(void *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); thread_cancel(&adj->t_expire); if (adj->adj_state != ISIS_ADJ_DOWN) @@ -292,7 +302,6 @@ void isis_adj_state_change(struct isis_adjacency **padj, 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); @@ -303,12 +312,14 @@ void isis_adj_state_change(struct isis_adjacency **padj, 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++; listnode_delete(circuit->u.bc.adjdb[level - 1], adj); diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 2780d826f..3afb7209f 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -105,6 +105,8 @@ struct isis_adjacency { unsigned int mt_count; /* Number of entries in mt_set */ struct bfd_session *bfd_session; struct list *adj_sids; /* Segment Routing Adj-SIDs. */ + uint32_t snmp_idx; + struct listnode *snmp_list_node; }; struct isis_threeway_adj; diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 4aac3f888..62822cbf8 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -71,6 +71,48 @@ DEFINE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp)) int isis_if_new_hook(struct interface *); int isis_if_delete_hook(struct interface *); +static int isis_circuit_smmp_id_gen(struct isis_circuit *circuit) +{ + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = NULL; + uint32_t id; + uint32_t i; + + isis = isis_lookup_by_vrfid(vrf->vrf_id); + if (isis == NULL) + return 0; + + id = isis->snmp_circuit_id_last; + id++; + + /* find next unused entry */ + for (i = 0; i < SNMP_CIRCUITS_MAX; i++) { + if (id >= SNMP_CIRCUITS_MAX) { + id = 0; + continue; + } + + if (id == 0) + continue; + + if (isis->snmp_circuits[id] == NULL) + break; + + id++; + } + + if (i == SNMP_CIRCUITS_MAX) { + zlog_warn("Could not allocate a smmp-circuit-id"); + return 0; + } + + isis->snmp_circuits[id] = circuit; + isis->snmp_circuit_id_last = id; + circuit->snmp_id = id; + + return 1; +} + struct isis_circuit *isis_circuit_new(struct isis *isis) { struct isis_circuit *circuit; @@ -80,6 +122,12 @@ struct isis_circuit *isis_circuit_new(struct isis *isis) circuit->isis = isis; /* + * Note: if snmp-id generation failed circuit will fail + * up operation + */ + isis_circuit_smmp_id_gen(circuit); + + /* * Default values */ #ifndef FABRICD @@ -150,11 +198,18 @@ struct isis_circuit *isis_circuit_new(struct isis *isis) void isis_circuit_del(struct isis_circuit *circuit) { + struct isis *isis = NULL; + if (!circuit) return; QOBJ_UNREG(circuit); + if (circuit->interface) { + isis = isis_lookup_by_vrfid(circuit->interface->vrf_id); + isis->snmp_circuits[circuit->snmp_id] = NULL; + } + isis_circuit_if_unbind(circuit, circuit->interface); circuit_mt_finish(circuit); @@ -609,6 +664,7 @@ int isis_circuit_up(struct isis_circuit *circuit) 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 @@ -623,6 +679,13 @@ int isis_circuit_up(struct isis_circuit *circuit) return ISIS_OK; } + if (circuit->snmp_id == 0) { + /* We cannot bring circuit up if does not have snmp-id */ + flog_err(EC_ISIS_CONFIG, + "No snnmp-id: there are too many circuits:"); + return ISIS_ERROR; + } + if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit)) { flog_err( EC_ISIS_CONFIG, @@ -722,6 +785,8 @@ int isis_circuit_up(struct isis_circuit *circuit) circuit->tx_queue = isis_tx_queue_new(circuit, send_lsp); + circuit->last_uptime = time(NULL); + #ifndef FABRICD /* send northbound notification */ isis_notif_if_state_change(circuit, false); @@ -828,6 +893,15 @@ void isis_circuit_down(struct isis_circuit *circuit) thread_cancel(&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 */ thread_cancel(&circuit->t_send_csnp[0]); thread_cancel(&circuit->t_send_csnp[1]); diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 3387232da..15d58bd73 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -79,6 +79,7 @@ struct isis_circuit_arg { struct isis_circuit { int state; uint8_t circuit_id; /* l1/l2 bcast CircuitID */ + time_t last_uptime; struct isis *isis; struct isis_area *area; /* back pointer to the area */ struct interface *interface; /* interface info from z */ @@ -115,6 +116,8 @@ struct isis_circuit { int pad_hellos; /* add padding to Hello PDUs ? */ char ext_domain; /* externalDomain (boolean) */ int lsp_regenerate_pending[ISIS_LEVELS]; + uint64_t lsp_error_counter; + /* * Configurables */ @@ -165,6 +168,12 @@ struct isis_circuit { uint32_t auth_type_failures; /*authentication-type-fails */ uint32_t auth_failures; /* authentication-fails */ + uint32_t snmp_id; /* Circuit id in snmp */ + + uint32_t snmp_adj_idx_gen; /* Create unique id for adjacency on creation + */ + struct list *snmp_adj_list; /* List in id order */ + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(isis_circuit) diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index f6175fe9a..e09e23aae 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -97,6 +97,7 @@ static int isis_check_dr_change(struct isis_adjacency *adj, int level) /* was there a DIS state transition ? */ { adj->dischanges[level - 1]++; + adj->circuit->desig_changes[level - 1]++; /* ok rotate the history list through */ for (i = DIS_RECORDS - 1; i > 0; i--) { adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 244f388c2..d2c5d93e2 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -166,3 +166,38 @@ void dynhn_print_all(struct vty *vty, struct isis *isis) cmd_hostname_get()); return; } + +struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level) +{ + struct listnode *node = NULL; + struct isis_dynhn *dyn = NULL; + struct isis_dynhn *found_dyn = NULL; + int res; + + for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) { + res = memcmp(dyn->id, id, ISIS_SYS_ID_LEN); + + if (res < 0) + continue; + + if (res == 0 && dyn->level <= level) + continue; + + if (res == 0) { + /* + * This is the best match, we can stop + * searching + */ + + found_dyn = dyn; + break; + } + + if (found_dyn == NULL + || memcmp(dyn->id, found_dyn->id, ISIS_SYS_ID_LEN) < 0) { + found_dyn = dyn; + } + } + + return found_dyn; +} diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h index 973fde830..8d25582e4 100644 --- a/isisd/isis_dynhn.h +++ b/isisd/isis_dynhn.h @@ -38,4 +38,7 @@ struct isis_dynhn *dynhn_find_by_id(const uint8_t *id); struct isis_dynhn *dynhn_find_by_name(const char *hostname); void dynhn_print_all(struct vty *vty, struct isis *isis); +/* Snmp support */ +struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level); + #endif /* _ZEBRA_ISIS_DYNHN_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index a17d9a6ae..06a5a69e3 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -324,8 +324,8 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno) /* check for overflow */ if (newseq < lsp->hdr.seqno) { /* send northbound notification */ - isis_notif_lsp_exceed_max(lsp->area, - rawlspid_print(lsp->hdr.lsp_id)); + lsp->area->lsp_exceeded_max_counter++; + isis_notif_lsp_exceed_max(lsp->area, lsp->hdr.lsp_id); } #endif /* ifndef FABRICD */ @@ -1357,8 +1357,8 @@ int lsp_generate(struct isis_area *area, int level) #ifndef FABRICD /* send northbound notification */ - isis_notif_lsp_gen(area, rawlspid_print(newlsp->hdr.lsp_id), - newlsp->hdr.seqno, newlsp->last_generated); + isis_notif_lsp_gen(area, newlsp->hdr.lsp_id, newlsp->hdr.seqno, + newlsp->last_generated); #endif /* ifndef FABRICD */ return ISIS_OK; diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index dfa77fbac..a6841b9fd 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -549,40 +549,97 @@ void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, /* Notifications. */ void isis_notif_db_overload(const struct isis_area *area, bool overload); void isis_notif_lsp_too_large(const struct isis_circuit *circuit, - uint32_t pdu_size, const char *lsp_id); + uint32_t pdu_size, const uint8_t *lsp_id); void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down); void isis_notif_corrupted_lsp(const struct isis_area *area, - const char *lsp_id); /* currently unused */ + const uint8_t *lsp_id); /* currently unused */ void isis_notif_lsp_exceed_max(const struct isis_area *area, - const char *lsp_id); + const uint8_t *lsp_id); void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, uint8_t max_area_addrs, - const char *raw_pdu); + const char *raw_pdu, size_t raw_pdu_len); void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, - const char *raw_pdu); + const char *raw_pdu, + size_t raw_pdu_len); void isis_notif_authentication_failure(const struct isis_circuit *circuit, - const char *raw_pdu); + const char *raw_pdu, size_t raw_pdu_len); void isis_notif_adj_state_change(const struct isis_adjacency *adj, int new_state, const char *reason); void isis_notif_reject_adjacency(const struct isis_circuit *circuit, - const char *reason, const char *raw_pdu); + const char *reason, const char *raw_pdu, + size_t raw_pdu_len); void isis_notif_area_mismatch(const struct isis_circuit *circuit, - const char *raw_pdu); + const char *raw_pdu, size_t raw_pdu_len); void isis_notif_lsp_received(const struct isis_circuit *circuit, - const char *lsp_id, uint32_t seqno, + const uint8_t *lsp_id, uint32_t seqno, uint32_t timestamp, const char *sys_id); -void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, +void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id, uint32_t seqno, uint32_t timestamp); void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, - uint8_t rcv_id_len, const char *raw_pdu); + uint8_t rcv_id_len, const char *raw_pdu, + size_t raw_pdu_len); void isis_notif_version_skew(const struct isis_circuit *circuit, - uint8_t version, const char *raw_pdu); + uint8_t version, const char *raw_pdu, + size_t raw_pdu_len); void isis_notif_lsp_error(const struct isis_circuit *circuit, - const char *lsp_id, const char *raw_pdu, - uint32_t offset, uint8_t tlv_type); + const uint8_t *lsp_id, const char *raw_pdu, + size_t raw_pdu_len, uint32_t offset, + uint8_t tlv_type); void isis_notif_seqno_skipped(const struct isis_circuit *circuit, - const char *lsp_id); + const uint8_t *lsp_id); void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, - const char *lsp_id); + const uint8_t *lsp_id); + +/* We also declare hook for every notification */ + +DECLARE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area)); +DECLARE_HOOK(isis_hook_lsp_too_large, + (const struct isis_circuit *circuit, uint32_t pdu_size, + const uint8_t *lsp_id), + (circuit, pdu_size, lsp_id)); +/* Note: no isis_hook_corrupted_lsp - because this notificaiton is not used */ +DECLARE_HOOK(isis_hook_lsp_exceed_max, + (const struct isis_area *area, const uint8_t *lsp_id), + (area, lsp_id)); +DECLARE_HOOK(isis_hook_max_area_addr_mismatch, + (const struct isis_circuit *circuit, uint8_t max_addrs, + const char *raw_pdu, size_t raw_pdu_len), + (circuit, max_addrs, raw_pdu, raw_pdu_len)); +DECLARE_HOOK(isis_hook_authentication_type_failure, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit, raw_pdu, raw_pdu_len)); +DECLARE_HOOK(isis_hook_authentication_failure, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit, raw_pdu, raw_pdu_len)); +DECLARE_HOOK(isis_hook_adj_state_change, (const struct isis_adjacency *adj), + (adj)); +DECLARE_HOOK(isis_hook_reject_adjacency, + (const struct isis_circuit *circuit, const char *pdu, + size_t pdu_len), + (circuit, pdu, pdu_len)); +DECLARE_HOOK(isis_hook_area_mismatch, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit)); +DECLARE_HOOK(isis_hook_id_len_mismatch, + (const struct isis_circuit *circuit, uint8_t rcv_id_len, + const char *raw_pdu, size_t raw_pdu_len), + (circuit, rcv_id_len, raw_pdu, raw_pdu_len)); +DECLARE_HOOK(isis_hook_version_skew, + (const struct isis_circuit *circuit, uint8_t version, + const char *raw_pdu, size_t raw_pdu_len), + (circuit)); +DECLARE_HOOK(isis_hook_lsp_error, + (const struct isis_circuit *circuit, const uint8_t *lsp_id, + const char *raw_pdu, size_t raw_pdu_len), + (circuit)); +DECLARE_HOOK(isis_hook_seqno_skipped, + (const struct isis_circuit *circuit, const uint8_t *lsp_id), + (circuit, lsp_id)); +DECLARE_HOOK(isis_hook_own_lsp_purge, + (const struct isis_circuit *circuit, const uint8_t *lsp_id), + (circuit, lsp_id)); #endif /* ISISD_ISIS_NB_H_ */ diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c index ea33ec10e..755378a9b 100644 --- a/isisd/isis_nb_notifications.c +++ b/isisd/isis_nb_notifications.c @@ -28,6 +28,56 @@ #include "isisd/isis_dynhn.h" #include "isisd/isis_misc.h" +DEFINE_HOOK(isis_hook_lsp_too_large, + (const struct isis_circuit *circuit, uint32_t pdu_size, + const uint8_t *lsp_id), + (circuit, pdu_size, lsp_id)); +DEFINE_HOOK(isis_hook_corrupted_lsp, (const struct isis_area *area), (area)); +DEFINE_HOOK(isis_hook_lsp_exceed_max, + (const struct isis_area *area, const uint8_t *lsp_id), + (area, lsp_id)); +DEFINE_HOOK(isis_hook_max_area_addr_mismatch, + (const struct isis_circuit *circuit, uint8_t max_addrs, + const char *raw_pdu, size_t raw_pdu_len), + (circuit, max_addrs, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_authentication_type_failure, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_authentication_failure, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_adj_state_change, (const struct isis_adjacency *adj), + (adj)); +DEFINE_HOOK(isis_hook_reject_adjacency, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_area_mismatch, + (const struct isis_circuit *circuit, const char *raw_pdu, + size_t raw_pdu_len), + (circuit, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_id_len_mismatch, + (const struct isis_circuit *circuit, uint8_t rcv_id_len, + const char *raw_pdu, size_t raw_pdu_len), + (circuit, rcv_id_len, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_version_skew, + (const struct isis_circuit *circuit, uint8_t version, + const char *raw_pdu, size_t raw_pdu_len), + (circuit, version, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_lsp_error, + (const struct isis_circuit *circuit, const uint8_t *lsp_id, + const char *raw_pdu, size_t raw_pdu_len), + (circuit, lsp_id, raw_pdu, raw_pdu_len)); +DEFINE_HOOK(isis_hook_seqno_skipped, + (const struct isis_circuit *circuit, const uint8_t *lsp_id), + (circuit, lsp_id)); +DEFINE_HOOK(isis_hook_own_lsp_purge, + (const struct isis_circuit *circuit, const uint8_t *lsp_id), + (circuit, lsp_id)); + + /* * Helper functions. */ @@ -92,7 +142,7 @@ void isis_notif_db_overload(const struct isis_area *area, bool overload) * XPath: /frr-isisd:lsp-too-large */ void isis_notif_lsp_too_large(const struct isis_circuit *circuit, - uint32_t pdu_size, const char *lsp_id) + uint32_t pdu_size, const uint8_t *lsp_id) { const char *xpath = "/frr-isisd:lsp-too-large"; struct list *arguments = yang_data_list_new(); @@ -106,9 +156,11 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit, data = yang_data_new_uint32(xpath_arg, pdu_size); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); + hook_call(isis_hook_lsp_too_large, circuit, pdu_size, lsp_id); + nb_notification_send(xpath, arguments); } @@ -135,7 +187,8 @@ void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down) /* * XPath: /frr-isisd:corrupted-lsp-detected */ -void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id) +void isis_notif_corrupted_lsp(const struct isis_area *area, + const uint8_t *lsp_id) { const char *xpath = "/frr-isisd:corrupted-lsp-detected"; struct list *arguments = yang_data_list_new(); @@ -144,16 +197,19 @@ void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id) notif_prep_instance_hdr(xpath, area, "default", arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); + hook_call(isis_hook_corrupted_lsp, area); + nb_notification_send(xpath, arguments); } /* * XPath: /frr-isisd:attempt-to-exceed-max-sequence */ -void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id) +void isis_notif_lsp_exceed_max(const struct isis_area *area, + const uint8_t *lsp_id) { const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence"; struct list *arguments = yang_data_list_new(); @@ -162,9 +218,11 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id) notif_prep_instance_hdr(xpath, area, "default", arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); + hook_call(isis_hook_lsp_exceed_max, area, lsp_id); + nb_notification_send(xpath, arguments); } @@ -173,7 +231,7 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id) */ void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, uint8_t max_area_addrs, - const char *raw_pdu) + const char *raw_pdu, size_t raw_pdu_len) { const char *xpath = "/frr-isisd:max-area-addresses-mismatch"; struct list *arguments = yang_data_list_new(); @@ -190,6 +248,9 @@ void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_max_area_addr_mismatch, circuit, max_area_addrs, + raw_pdu, raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -197,7 +258,8 @@ void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, * XPath: /frr-isisd:authentication-type-failure */ void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, - const char *raw_pdu) + const char *raw_pdu, + size_t raw_pdu_len) { const char *xpath = "/frr-isisd:authentication-type-failure"; struct list *arguments = yang_data_list_new(); @@ -211,6 +273,9 @@ void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_authentication_type_failure, circuit, raw_pdu, + raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -218,7 +283,7 @@ void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, * XPath: /frr-isisd:authentication-failure */ void isis_notif_authentication_failure(const struct isis_circuit *circuit, - const char *raw_pdu) + const char *raw_pdu, size_t raw_pdu_len) { const char *xpath = "/frr-isisd:authentication-failure"; struct list *arguments = yang_data_list_new(); @@ -232,6 +297,9 @@ void isis_notif_authentication_failure(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_authentication_failure, circuit, raw_pdu, + raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -269,6 +337,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, listnode_add(arguments, data); } + hook_call(isis_hook_adj_state_change, adj); + nb_notification_send(xpath, arguments); } @@ -276,7 +346,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, * XPath: /frr-isisd:rejected-adjacency */ void isis_notif_reject_adjacency(const struct isis_circuit *circuit, - const char *reason, const char *raw_pdu) + const char *reason, const char *raw_pdu, + size_t raw_pdu_len) { const char *xpath = "/frr-isisd:rejected-adjacency"; struct list *arguments = yang_data_list_new(); @@ -293,6 +364,8 @@ void isis_notif_reject_adjacency(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_reject_adjacency, circuit, raw_pdu, raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -300,7 +373,7 @@ void isis_notif_reject_adjacency(const struct isis_circuit *circuit, * XPath: /frr-isisd:area-mismatch */ void isis_notif_area_mismatch(const struct isis_circuit *circuit, - const char *raw_pdu) + const char *raw_pdu, size_t raw_pdu_len) { const char *xpath = "/frr-isisd:area-mismatch"; struct list *arguments = yang_data_list_new(); @@ -314,6 +387,8 @@ void isis_notif_area_mismatch(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_area_mismatch, circuit, raw_pdu, raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -321,7 +396,7 @@ void isis_notif_area_mismatch(const struct isis_circuit *circuit, * XPath: /frr-isisd:lsp-received */ void isis_notif_lsp_received(const struct isis_circuit *circuit, - const char *lsp_id, uint32_t seqno, + const uint8_t *lsp_id, uint32_t seqno, uint32_t timestamp, const char *sys_id) { const char *xpath = "/frr-isisd:lsp-received"; @@ -333,7 +408,7 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit, notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); data = yang_data_new_uint32(xpath_arg, seqno); @@ -351,7 +426,7 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit, /* * XPath: /frr-isisd:lsp-generation */ -void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, +void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id, uint32_t seqno, uint32_t timestamp) { const char *xpath = "/frr-isisd:lsp-generation"; @@ -361,7 +436,7 @@ void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, notif_prep_instance_hdr(xpath, area, "default", arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); data = yang_data_new_uint32(xpath_arg, seqno); @@ -377,7 +452,8 @@ void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, * XPath: /frr-isisd:id-len-mismatch */ void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, - uint8_t rcv_id_len, const char *raw_pdu) + uint8_t rcv_id_len, const char *raw_pdu, + size_t raw_pdu_len) { const char *xpath = "/frr-isisd:id-len-mismatch"; struct list *arguments = yang_data_list_new(); @@ -394,6 +470,9 @@ void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_id_len_mismatch, circuit, rcv_id_len, raw_pdu, + raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -401,7 +480,8 @@ void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, * XPath: /frr-isisd:version-skew */ void isis_notif_version_skew(const struct isis_circuit *circuit, - uint8_t version, const char *raw_pdu) + uint8_t version, const char *raw_pdu, + size_t raw_pdu_len) { const char *xpath = "/frr-isisd:version-skew"; struct list *arguments = yang_data_list_new(); @@ -418,6 +498,9 @@ void isis_notif_version_skew(const struct isis_circuit *circuit, data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); + hook_call(isis_hook_version_skew, circuit, version, raw_pdu, + raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -425,7 +508,8 @@ void isis_notif_version_skew(const struct isis_circuit *circuit, * XPath: /frr-isisd:lsp-error-detected */ void isis_notif_lsp_error(const struct isis_circuit *circuit, - const char *lsp_id, const char *raw_pdu, + const uint8_t *lsp_id, const char *raw_pdu, + size_t raw_pdu_len, __attribute__((unused)) uint32_t offset, __attribute__((unused)) uint8_t tlv_type) { @@ -438,13 +522,15 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit, notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); data = yang_data_new(xpath_arg, raw_pdu); listnode_add(arguments, data); /* ignore offset and tlv_type which cannot be set properly */ + hook_call(isis_hook_lsp_error, circuit, lsp_id, raw_pdu, raw_pdu_len); + nb_notification_send(xpath, arguments); } @@ -452,7 +538,7 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit, * XPath: /frr-isisd:sequence-number-skipped */ void isis_notif_seqno_skipped(const struct isis_circuit *circuit, - const char *lsp_id) + const uint8_t *lsp_id) { const char *xpath = "/frr-isisd:sequence-number-skipped"; struct list *arguments = yang_data_list_new(); @@ -463,9 +549,11 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit, notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); + hook_call(isis_hook_seqno_skipped, circuit, lsp_id); + nb_notification_send(xpath, arguments); } @@ -473,7 +561,7 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit, * XPath: /frr-isisd:own-lsp-purge */ void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, - const char *lsp_id) + const uint8_t *lsp_id) { const char *xpath = "/frr-isisd:own-lsp-purge"; struct list *arguments = yang_data_list_new(); @@ -484,8 +572,10 @@ void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); + data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); listnode_add(arguments, data); + hook_call(isis_hook_own_lsp_purge, circuit, lsp_id); + nb_notification_send(xpath, arguments); } diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index a02b48157..7256fcbbc 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -549,6 +549,19 @@ static int pdu_len_validate(uint16_t pdu_len, struct isis_circuit *circuit) return 0; } +static void update_rej_adj_count(struct isis_circuit *circuit) +{ + circuit->rej_adjacencies++; + if (circuit->is_type == IS_LEVEL_1) + circuit->area->rej_adjacencies[0]++; + else if (circuit->is_type == IS_LEVEL_2) + circuit->area->rej_adjacencies[1]++; + else { + circuit->area->rej_adjacencies[0]++; + circuit->area->rej_adjacencies[1]++; + } +} + static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, uint8_t *ssnpa) { @@ -581,22 +594,22 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, if (p2p_hello) { if (circuit->circ_type != CIRCUIT_T_P2P) { zlog_warn("p2p hello on non p2p circuit"); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD isis_notif_reject_adjacency( circuit, "p2p hello on non p2p circuit", - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } } else { if (circuit->circ_type != CIRCUIT_T_BROADCAST) { zlog_warn("lan hello on non broadcast circuit"); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD isis_notif_reject_adjacency( circuit, "lan hello on non broadcast circuit", - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } @@ -605,12 +618,12 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, zlog_debug( "level %d LAN Hello received over circuit with externalDomain = true", level); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD isis_notif_reject_adjacency( circuit, "LAN Hello received over circuit with externalDomain = true", - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } @@ -622,10 +635,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, circuit->area->area_tag, circuit->interface->name); } - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD - isis_notif_reject_adjacency( - circuit, "Interface level mismatch", raw_pdu); + isis_notif_reject_adjacency(circuit, + "Interface level mismatch", + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } @@ -652,10 +666,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %hu", circuit->area->area_tag, pdu_name, circuit->interface->name, iih.pdu_len); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD isis_notif_reject_adjacency(circuit, "Invalid PDU length", - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } @@ -664,10 +678,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, flog_err(EC_ISIS_PACKET, "Level %d LAN Hello with Circuit Type %d", level, iih.circ_type); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD - isis_notif_reject_adjacency( - circuit, "LAN Hello with wrong IS-level", raw_pdu); + isis_notif_reject_adjacency(circuit, + "LAN Hello with wrong IS-level", + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_ERROR; } @@ -678,10 +693,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream), circuit->rcv_stream, &iih.tlvs, &error_log)) { zlog_warn("isis_unpack_tlvs() failed: %s", error_log); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD isis_notif_reject_adjacency(circuit, "Failed to unpack TLVs", - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ goto out; } @@ -690,17 +705,18 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, zlog_warn("No Area addresses TLV in %s", pdu_name); #ifndef FABRICD /* send northbound notification */ - isis_notif_area_mismatch(circuit, raw_pdu); + isis_notif_area_mismatch(circuit, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ goto out; } if (!iih.tlvs->protocols_supported.count) { zlog_warn("No supported protocols TLV in %s", pdu_name); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD - isis_notif_reject_adjacency( - circuit, "No supported protocols TLV", raw_pdu); + isis_notif_reject_adjacency(circuit, + "No supported protocols TLV", + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ goto out; } @@ -716,12 +732,13 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start, pdu_end - pdu_start); if (auth_code == ISIS_AUTH_FAILURE) { - circuit->auth_failures++; - isis_notif_authentication_failure(circuit, raw_pdu); + update_rej_adj_count(circuit); + isis_notif_authentication_failure(circuit, raw_pdu, + sizeof(raw_pdu)); } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ - circuit->auth_type_failures++; - isis_notif_authentication_type_failure(circuit, - raw_pdu); + update_rej_adj_count(circuit); + isis_notif_authentication_type_failure(circuit, raw_pdu, + sizeof(raw_pdu)); } #endif /* ifndef FABRICD */ goto out; @@ -731,10 +748,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, zlog_warn( "ISIS-Adj (%s): Received IIH with own sysid on %s - discard", circuit->area->area_tag, circuit->interface->name); - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD - isis_notif_reject_adjacency( - circuit, "Received IIH with our own sysid", raw_pdu); + isis_notif_reject_adjacency(circuit, + "Received IIH with our own sysid", + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ goto out; } @@ -752,7 +770,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, } #ifndef FABRICD /* send northbound notification */ - isis_notif_area_mismatch(circuit, raw_pdu); + isis_notif_area_mismatch(circuit, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ goto out; } @@ -769,11 +787,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, "ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH", circuit->area->area_tag); } - circuit->rej_adjacencies++; + update_rej_adj_count(circuit); #ifndef FABRICD isis_notif_reject_adjacency( circuit, "Neither IPv4 not IPv6 considered usable", - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ goto out; } @@ -857,8 +875,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, #ifndef FABRICD /* send northbound notification */ - isis_notif_lsp_received(circuit, rawlspid_print(hdr.lsp_id), hdr.seqno, - time(NULL), sysid_print(hdr.lsp_id)); + isis_notif_lsp_received(circuit, hdr.lsp_id, hdr.seqno, time(NULL), + sysid_print(hdr.lsp_id)); #endif /* ifndef FABRICD */ if (pdu_len_validate(hdr.pdu_len, circuit)) { @@ -931,8 +949,18 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, * we change the code above to return those extra fields, we * will send dummy values which are ignored in the callback */ - isis_notif_lsp_error(circuit, rawlspid_print(hdr.lsp_id), - raw_pdu, 0, 0); + circuit->lsp_error_counter++; + if (circuit->is_type == IS_LEVEL_1) { + circuit->area->lsp_error_counter[0]++; + } else if (circuit->is_type == IS_LEVEL_2) { + circuit->area->lsp_error_counter[1]++; + } else { + circuit->area->lsp_error_counter[0]++; + circuit->area->lsp_error_counter[1]++; + } + + isis_notif_lsp_error(circuit, hdr.lsp_id, raw_pdu, + sizeof(raw_pdu), 0, 0); #endif /* ifndef FABRICD */ goto out; } @@ -956,11 +984,28 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, /* send northbound notification */ if (auth_code == ISIS_AUTH_FAILURE) { circuit->auth_failures++; - isis_notif_authentication_failure(circuit, raw_pdu); + if (circuit->is_type == IS_LEVEL_1) { + circuit->area->auth_failures[0]++; + } else if (circuit->is_type == IS_LEVEL_2) { + circuit->area->auth_failures[1]++; + } else { + circuit->area->auth_failures[0]++; + circuit->area->auth_failures[1]++; + } + isis_notif_authentication_failure(circuit, raw_pdu, + sizeof(raw_pdu)); } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ circuit->auth_type_failures++; - isis_notif_authentication_type_failure(circuit, - raw_pdu); + if (circuit->is_type == IS_LEVEL_1) { + circuit->area->auth_type_failures[0]++; + } else if (circuit->is_type == IS_LEVEL_2) { + circuit->area->auth_type_failures[1]++; + } else { + circuit->area->auth_type_failures[0]++; + circuit->area->auth_type_failures[1]++; + } + isis_notif_authentication_type_failure(circuit, raw_pdu, + sizeof(raw_pdu)); } #endif /* ifndef FABRICD */ goto out; @@ -1105,10 +1150,10 @@ dontcheckadj: if (lsp->hdr.seqno < hdr.seqno) { /* send northbound * notification */ + circuit->area + ->lsp_seqno_skipped_counter++; isis_notif_seqno_skipped( - circuit, - rawlspid_print( - hdr.lsp_id)); + circuit, hdr.lsp_id); } #endif /* ifndef FABRICD */ lsp_inc_seqno(lsp, hdr.seqno); @@ -1129,8 +1174,7 @@ dontcheckadj: /* our own LSP with 0 remaining life time */ #ifndef FABRICD /* send northbound notification */ - isis_notif_own_lsp_purge( - circuit, rawlspid_print(hdr.lsp_id)); + isis_notif_own_lsp_purge(circuit, hdr.lsp_id); #endif /* ifndef FABRICD */ } } @@ -1158,8 +1202,8 @@ dontcheckadj: lsp_inc_seqno(lsp, hdr.seqno); #ifndef FABRICD /* send northbound notification */ - isis_notif_seqno_skipped(circuit, - rawlspid_print(hdr.lsp_id)); + circuit->area->lsp_seqno_skipped_counter++; + isis_notif_seqno_skipped(circuit, hdr.lsp_id); #endif /* ifndef FABRICD */ if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( @@ -1388,12 +1432,28 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, pdu_end - pdu_start); if (auth_code == ISIS_AUTH_FAILURE) { circuit->auth_failures++; - isis_notif_authentication_failure(circuit, - raw_pdu); + if (circuit->is_type == IS_LEVEL_1) { + circuit->area->auth_failures[0]++; + } else if (circuit->is_type == IS_LEVEL_2) { + circuit->area->auth_failures[1]++; + } else { + circuit->area->auth_failures[0]++; + circuit->area->auth_failures[1]++; + } + isis_notif_authentication_failure( + circuit, raw_pdu, sizeof(raw_pdu)); } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ circuit->auth_type_failures++; - isis_notif_authentication_type_failure(circuit, - raw_pdu); + if (circuit->is_type == IS_LEVEL_1) { + circuit->area->auth_type_failures[0]++; + } else if (circuit->is_type == IS_LEVEL_2) { + circuit->area->auth_type_failures[1]++; + } else { + circuit->area->auth_type_failures[0]++; + circuit->area->auth_type_failures[1]++; + } + isis_notif_authentication_type_failure( + circuit, raw_pdu, sizeof(raw_pdu)); } #endif /* ifndef FABRICD */ goto out; @@ -1620,7 +1680,8 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) zlog_warn("Unsupported ISIS version %hhu", version1); #ifndef FABRICD /* send northbound notification */ - isis_notif_version_skew(circuit, version1, raw_pdu); + isis_notif_version_skew(circuit, version1, raw_pdu, + sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } @@ -1631,9 +1692,19 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) "IDFieldLengthMismatch: ID Length field in a received PDU %hhu, while the parameter for this IS is %u", id_len, ISIS_SYS_ID_LEN); circuit->id_len_mismatches++; + if (circuit->is_type == IS_LEVEL_1) { + circuit->area->id_len_mismatches[0]++; + } else if (circuit->is_type == IS_LEVEL_2) { + circuit->area->id_len_mismatches[1]++; + } else { + circuit->area->id_len_mismatches[0]++; + circuit->area->id_len_mismatches[1]++; + } + #ifndef FABRICD /* send northbound notification */ - isis_notif_id_len_mismatch(circuit, id_len, raw_pdu); + isis_notif_id_len_mismatch(circuit, id_len, raw_pdu, + sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_ERROR; } @@ -1662,7 +1733,8 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) zlog_warn("Unsupported ISIS PDU version %hhu", version2); #ifndef FABRICD /* send northbound notification */ - isis_notif_version_skew(circuit, version2, raw_pdu); + isis_notif_version_skew(circuit, version2, raw_pdu, + sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_WARNING; } @@ -1686,7 +1758,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) #ifndef FABRICD /* send northbound notification */ isis_notif_max_area_addr_mismatch(circuit, max_area_addrs, - raw_pdu); + raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ return ISIS_ERROR; } @@ -2409,7 +2481,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, #ifndef FABRICD /* send a northbound notification */ isis_notif_lsp_too_large(circuit, stream_get_endp(lsp->pdu), - rawlspid_print(lsp->hdr.lsp_id)); + lsp->hdr.lsp_id); #endif /* ifndef FABRICD */ if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(lsp->pdu), diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c new file mode 100644 index 000000000..cab919973 --- /dev/null +++ b/isisd/isis_snmp.c @@ -0,0 +1,3457 @@ +/* + * ISIS SNMP support + * Copyright (C) 2020 Volta Networks, Inc. + * Aleksey Romanov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as 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 + */ + +/* + * This is minimal read-only implementations providing isisReadOnlyCompliance + */ + +#include <zebra.h> + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> + +#include "vrf.h" +#include "if.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "memory.h" +#include "smux.h" +#include "libfrr.h" +#include "version.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_dynhn.h" +#include "isisd/isis_te.h" +#include "isisd/isis_dr.h" +#include "isisd/isis_nb.h" +#include "isisd/isisd.h" + +/* ISIS-MIB. */ +#define ISIS_MIB 1, 3, 6, 1, 2, 1, 138 + +#define ISIS_OBJECTS 1 +#define ISIS_SYSTEM 1, 1 +#define ISIS_SYSLEVEL 1, 2 +#define ISIS_CIRC 1, 3 +#define ISIS_CIRC_LEVEL_VALUES 1, 4 +#define ISIS_COUNTERS 1, 5 +#define ISIS_ISADJ 1, 6 + +/************************ isisSystemGroup ************************/ + +/* isisSysObject */ +#define ISIS_SYS_OBJECT 1, 1, 1 +#define ISIS_SYS_VERSION 1 +#define ISIS_SYS_LEVELTYPE 2 +#define ISIS_SYS_ID 3 +#define ISIS_SYS_MAXPATHSPLITS 4 +#define ISIS_SYS_MAXLSPGENINT 5 +#define ISIS_SYS_POLLESHELLORATE 6 +#define ISIS_SYS_WAITTIME 7 +#define ISIS_SYS_ADMINSTATE 8 +#define ISIS_SYS_L2TOL1LEAKING 9 +#define ISIS_SYS_MAXAGE 10 +#define ISIS_SYS_RECEIVELSPBUFFERSIZE 11 +#define ISIS_SYS_PROTSUPPORTED 12 +#define ISIS_SYS_NOTIFICATIONENABLE 13 + +/* isisManAreaAddrEntry */ +#define ISIS_MANAREA_ADDRENTRY 1, 1, 2, 1 +#define ISIS_MANAREA_ADDREXISTSTATE 2 + +/* isisAreaAddrEntry */ +#define ISIS_AREA_ADDRENTRY 1, 1, 3, 1 +#define ISIS_AREA_ADDR 1 + +/* isisSummAddrEntry */ +#define ISIS_SUMM_ADDRENTRY 1, 1, 4, 1 +#define ISIS_SUMM_ADDREXISTSTATE 4 +#define ISIS_SUMM_ADDRMETRIC 5 +#define ISIS_SUMM_ADDRFULLMETRIC 6 + +/* isisRedistributeAddrEntry */ +#define ISIS_REDISTRIBUTE_ADDRENTRY 1, 1, 5, 1 +#define ISIS_REDISTRIBUTE_ADDREXISTSTATE 3 + +/* isisRouterEntry */ +#define ISIS_ROUTER_ENTRY 1, 1, 6, 1 +#define ISIS_ROUTER_HOSTNAME 3 +#define ISIS_ROUTER_ID 4 + +/* isisSysLevelTable */ +#define ISIS_SYSLEVEL_ENTRY 1, 2, 1, 1 +#define ISIS_SYSLEVEL_ORIGLSPBUFFSIZE 2 +#define ISIS_SYSLEVEL_MINLSPGENINT 3 +#define ISIS_SYSLEVEL_STATE 4 +#define ISIS_SYSLEVEL_SETOVERLOAD 5 +#define ISIS_SYSLEVEL_SETOVERLOADUNTIL 6 +#define ISIS_SYSLEVEL_METRICSTYLE 7 +#define ISIS_SYSLEVEL_SPFCONSIDERS 8 +#define ISIS_SYSLEVEL_TEENABLED 9 + + +/* isisSystemCounterEntry */ +#define ISIS_SYSTEM_COUNTER_ENTRY 1, 5, 1, 1 +#define ISIS_SYSSTAT_CORRLSPS 2 +#define ISIS_SYSSTAT_AUTHTYPEFAILS 3 +#define ISIS_SYSSTAT_AUTHFAILS 4 +#define ISIS_SYSSTAT_LSPDBASEOLOADS 5 +#define ISIS_SYSSTAT_MANADDRDROPFROMAREAS 6 +#define ISIS_SYSSTAT_ATTMPTTOEXMAXSEQNUMS 7 +#define ISIS_SYSSTAT_SEQNUMSKIPS 8 +#define ISIS_SYSSTAT_OWNLSPPURGES 9 +#define ISIS_SYSSTAT_IDFIELDLENMISMATCHES 10 +#define ISIS_SYSSTAT_PARTCHANGES 11 +#define ISIS_SYSSTAT_SPFRUNS 12 +#define ISIS_SYSSTAT_LSPERRORS 13 + + +/************************ isisCircuitGroup ************************/ + +/* Scalar directly under isisCirc */ +#define ISIS_NEXTCIRC_INDEX 1 + +/* isisCircEntry */ +#define ISIS_CIRC_ENTRY 1, 3, 2, 1 +#define ISIS_CIRC_IFINDEX 2 +#define ISIS_CIRC_ADMINSTATE 3 +#define ISIS_CIRC_EXISTSTATE 4 +#define ISIS_CIRC_TYPE 5 +#define ISIS_CIRC_EXTDOMAIN 6 +#define ISIS_CIRC_LEVELTYPE 7 +#define ISIS_CIRC_PASSIVECIRCUIT 8 +#define ISIS_CIRC_MESHGROUPENABLED 9 +#define ISIS_CIRC_MESHGROUP 10 +#define ISIS_CIRC_SMALLHELLOS 11 +#define ISIS_CIRC_LASTUPTIME 12 +#define ISIS_CIRC_3WAYENABLED 13 +#define ISIS_CIRC_EXTENDEDCIRCID 14 + +/* isisCircLevelEntry */ +#define ISIS_CIRCLEVEL_ENTRY 1, 4, 1, 1 +#define ISIS_CIRCLEVEL_METRIC 2 +#define ISIS_CIRCLEVEL_WIDEMETRIC 3 +#define ISIS_CIRCLEVEL_ISPRIORITY 4 +#define ISIS_CIRCLEVEL_IDOCTET 5 +#define ISIS_CIRCLEVEL_ID 6 +#define ISIS_CIRCLEVEL_DESIS 7 +#define ISIS_CIRCLEVEL_HELLOMULTIPLIER 8 +#define ISIS_CIRCLEVEL_HELLOTIMER 9 +#define ISIS_CIRCLEVEL_DRHELLOTIMER 10 +#define ISIS_CIRCLEVEL_LSPTHROTTLE 11 +#define ISIS_CIRCLEVEL_MINLSPRETRANSINT 12 +#define ISIS_CIRCLEVEL_CSNPINTERVAL 13 +#define ISIS_CIRCLEVEL_PARTSNPINTERVAL 14 + +/* isisCircuitCounterEntry */ +#define ISIS_CIRC_COUNTER_ENTRY 1, 5, 2, 1 +#define ISIS_CIRC_ADJCHANGES 2 +#define ISIS_CIRC_NUMADJ 3 +#define ISIS_CIRC_INITFAILS 4 +#define ISIS_CIRC_REJADJS 5 +#define ISIS_CIRC_IDFIELDLENMISMATCHES 6 +#define ISIS_CIRC_MAXAREAADDRMISMATCHES 7 +#define ISIS_CIRC_AUTHTYPEFAILS 8 +#define ISIS_CIRC_AUTHFAILS 9 +#define ISIS_CIRC_LANDESISCHANGES 10 + + +/************************ isisISAdjGroup ************************/ + +/* isisISAdjEntry */ +#define ISIS_ISADJ_ENTRY 1, 6, 1, 1 +#define ISIS_ISADJ_STATE 2 +#define ISIS_ISADJ_3WAYSTATE 3 +#define ISIS_ISADJ_NEIGHSNPAADDRESS 4 +#define ISIS_ISADJ_NEIGHSYSTYPE 5 +#define ISIS_ISADJ_NEIGHSYSID 6 +#define ISIS_ISADJ_NBREXTENDEDCIRCID 7 +#define ISIS_ISADJ_USAGE 8 +#define ISIS_ISADJ_HOLDTIMER 9 +#define ISIS_ISADJ_NEIGHPRIORITY 10 +#define ISIS_ISADJ_LASTUPTIME 11 + +/* isisISAdjAreadAddrEntry */ +#define ISIS_ISADJAREA_ADDRENTRY 1, 6, 2, 1 +#define ISIS_ISADJAREA_ADDRESS 2 + +/* isisISAdjIPAddrEntry*/ +#define ISIS_ISADJIPADDR_ENTRY 1, 6, 3, 1 +#define ISIS_ISADJIPADDR_TYPE 2 +#define ISIS_ISADJIPADDR_ADDRESS 3 + + +/* isisISAdjProtSuppEntty */ + +#define ISIS_ISADJPROTSUPP_ENTRY 1, 6, 4, 1 +#define ISIS_ISADJPROTSUPP_PROTOCOL 1 + + +/************************ Trap data variables ************************/ +#define ISIS_NOTIFICATION_ENTRY 1, 10, 1 +#define ISIS_NOTIF_SYLELVELINDEX 1 +#define ISIS_NOTIF_CIRCIFINDEX 2 +#define ISIS_PDU_LSPID 3 +#define ISIS_PDU_FRAGMENT 4 +#define ISIS_PDU_FIELDLEN 5 +#define ISIS_PDU_MAXAREAADDR 6 +#define ISIS_PDU_PROTOVER 7 +#define ISIS_PDU_LSPSIZE 8 +#define ISIS_PDU_ORIGBUFFERSIZE 9 +#define ISIS_PDU_BUFFERSIZE 10 +#define ISIS_PDU_PROTSUPP 11 +#define ISIS_ADJ_STATE 12 +#define ISIS_ERROR_OFFSET 13 +#define ISIS_ERROR_TLVTYPE 14 +#define ISIS_NOTIF_AREAADDR 15 + +/************************ Traps ************************/ +#define ISIS_NOTIFICATIONS ISIS_MIB, 0 +#define ISIS_TRAP_DB_OVERLOAD 1 +#define ISIS_TRAP_MAN_ADDR_DROP 2 +#define ISIS_TRAP_CORRUPTED_LSP 3 +#define ISIS_TRAP_LSP_EXCEED_MAX 4 +#define ISIS_TRAP_ID_LEN_MISMATCH 5 +#define ISIS_TRAP_MAX_AREA_ADDR_MISMATCH 6 +#define ISIS_TRAP_OWN_LSP_PURGE 7 +#define ISIS_TRAP_SEQNO_SKIPPED 8 +#define ISIS_TRAP_AUTHEN_TYPE_FAILURE 9 +#define ISIS_TRAP_AUTHEN_FAILURE 10 +#define ISIS_TRAP_VERSION_SKEW 11 +#define ISIS_TRAP_AREA_MISMATCH 12 +#define ISIS_TRAP_REJ_ADJACENCY 13 +#define ISIS_TRAP_LSP_TOO_LARGE 14 +#define ISIS_TRAP_LSP_BUFFSIZE_MISMATCH 15 +#define ISIS_TRAP_PROTSUPP_MISMATCH 16 +#define ISIS_TRAP_ADJ_STATE_CHANGE 17 +#define ISIS_TRAP_LSP_ERROR 18 + +/* Change this definition if number of traps changes */ +#define ISIS_TRAP_LAST_TRAP ISIS_TRAP_LSP_ERROR + +#define ISIS_SNMP_TRAP_VAR 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 + + +/* SNMP value hack. */ +#define COUNTER32 ASN_COUNTER +#define INTEGER ASN_INTEGER +#define UNSIGNED32 ASN_GAUGE +#define TIMESTAMP ASN_TIMETICKS +#define TIMETICKS ASN_TIMETICKS +#define STRING ASN_OCTET_STR + +/* Declare static local variables for convenience. */ +SNMP_LOCAL_VARIABLES + +/* If ARRAY_SIZE is not available use a primitive substitution */ +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +/* + * Define time function, it serves two purposes + * 1. Uses unint32_t for unix time and encapsulates + * sing extension issues in conversion from time_t + * + * 2. I could be replaced in unit test environment + */ +#ifndef ISIS_SNMP_HAVE_TIME_FUNC +static uint32_t isis_snmp_time(void) +{ + return (uint32_t)time(NULL); +} + +#endif + +/* ISIS-MIB instances. */ +static oid isis_oid[] = {ISIS_MIB}; + +/* SNMP trap variable */ +static oid isis_snmp_trap_var[] = {ISIS_SNMP_TRAP_VAR}; + +/* SNMP trap values (others are calculated on the fly */ +static oid isis_snmp_notifications[] = {ISIS_NOTIFICATIONS}; +static oid isis_snmp_trap_val_db_overload[] = {ISIS_NOTIFICATIONS, + ISIS_TRAP_DB_OVERLOAD}; +static oid isis_snmp_trap_val_lsp_exceed_max[] = {ISIS_NOTIFICATIONS, + ISIS_TRAP_LSP_EXCEED_MAX}; +static oid isis_snmp_trap_val_area_mismatch[] = {ISIS_NOTIFICATIONS, + ISIS_TRAP_AREA_MISMATCH}; +static oid isis_snmp_trap_val_lsp_error[] = {ISIS_NOTIFICATIONS, + ISIS_TRAP_LSP_ERROR}; + +/* + * Trap vars under 'isisNotifications': note: we use full names of variables + * scalar index + */ +static oid isis_snmp_trap_data_var_sys_level_index[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_NOTIF_SYLELVELINDEX, 0}; +static oid isis_snmp_trap_data_var_circ_if_index[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_NOTIF_CIRCIFINDEX, 0}; +static oid isis_snmp_trap_data_var_pdu_lsp_id[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_LSPID, 0}; +static oid isis_snmp_trap_data_var_pdu_fragment[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_FRAGMENT, 0}; +static oid isis_snmp_trap_data_var_pdu_field_len[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_FIELDLEN, 0}; +static oid isis_snmp_trap_data_var_pdu_max_area_addr[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_MAXAREAADDR, 0}; +static oid isis_snmp_trap_data_var_pdu_proto_ver[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_PROTOVER, 0}; +static oid isis_snmp_trap_data_var_pdu_lsp_size[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_LSPSIZE, 0}; +static oid isis_snmp_trap_data_var_adj_state[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_ADJ_STATE, 0}; +static oid isis_snmp_trap_data_var_error_offset[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_ERROR_OFFSET, 0}; +static oid isis_snmp_trap_data_var_error_tlv_type[] = { + ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_ERROR_TLVTYPE, 0}; + +/* + * Other variables used by traps: note we use full names of variables and + * reserve space for index + */ +static oid isis_snmp_trap_data_var_sys_level_state[] = { + ISIS_MIB, ISIS_SYSLEVEL_ENTRY, ISIS_SYSLEVEL_STATE, 0}; + +/* Throttle time values for traps */ +static time_t isis_snmp_trap_timestamp[ISIS_TRAP_LAST_TRAP]; /* ?? 1 */ + +/* Max len of raw-pdu in traps */ +#define ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN (64) + +/* + * Just to save on typing we have a shortcut structure + * to specify mib layout as prefix/leaf combination + */ +#define ISIS_SNMP_PREF_LEN_MAX 10 +struct isis_var_prefix { + FindVarMethod *findVar; + uint8_t ivd_pref_len; + oid ivd_pref[ISIS_SNMP_PREF_LEN_MAX]; +}; + + +/* Find-val functions */ +static uint8_t *isis_snmp_find_sys_object(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_man_area(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_area_addr(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_summ_addr(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_redistribute_addr(struct variable *, oid *, + size_t *, int, size_t *, + WriteMethod **); + +static uint8_t *isis_snmp_find_router(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_sys_level(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_system_counter(struct variable *, oid *, + size_t *, int, size_t *, + WriteMethod **); + +static uint8_t *isis_snmp_find_next_circ_index(struct variable *, oid *, + size_t *, int, size_t *, + WriteMethod **); + +static uint8_t *isis_snmp_find_circ(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_circ_level(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_circ_counter(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_isadj(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_isadj_area(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_isadj_ipaddr(struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +static uint8_t *isis_snmp_find_isadj_prot_supp(struct variable *, oid *, + size_t *, int, size_t *, + WriteMethod **); + +/* + * Just to save on typing we have a shortcut structure + * to specify mib layout, we populate the rest of the data + * during initialization + */ +#define ISIS_PREF_LEN_MAX (6) + +struct isis_func_to_prefix { + FindVarMethod *ihtp_func; + oid ihtp_pref_oid[ISIS_PREF_LEN_MAX]; + uint8_t ihtp_pref_len; +}; + +static struct isis_func_to_prefix isis_func_to_prefix_arr[] = { + {isis_snmp_find_sys_object, {ISIS_SYS_OBJECT}, 3}, + {isis_snmp_find_man_area, {ISIS_MANAREA_ADDRENTRY}, 4}, + {isis_snmp_find_area_addr, {ISIS_AREA_ADDRENTRY}, 4}, + {isis_snmp_find_summ_addr, {ISIS_SUMM_ADDRENTRY}, 4}, + {isis_snmp_find_redistribute_addr, {ISIS_REDISTRIBUTE_ADDRENTRY}, 4}, + {isis_snmp_find_router, {ISIS_ROUTER_ENTRY}, 4}, + {isis_snmp_find_sys_level, {ISIS_SYSLEVEL_ENTRY}, 4}, + {isis_snmp_find_system_counter, {ISIS_SYSTEM_COUNTER_ENTRY}, 4}, + {isis_snmp_find_next_circ_index, {ISIS_CIRC}, 2}, + {isis_snmp_find_circ, {ISIS_CIRC_ENTRY}, 4}, + {isis_snmp_find_circ_level, {ISIS_CIRCLEVEL_ENTRY}, 4}, + {isis_snmp_find_circ_counter, {ISIS_CIRC_COUNTER_ENTRY}, 4}, + {isis_snmp_find_isadj, {ISIS_ISADJ_ENTRY}, 4}, + {isis_snmp_find_isadj_area, {ISIS_ISADJAREA_ADDRENTRY}, 4}, + {isis_snmp_find_isadj_ipaddr, {ISIS_ISADJIPADDR_ENTRY}, 4}, + {isis_snmp_find_isadj_prot_supp, {ISIS_ISADJPROTSUPP_ENTRY}, 4}, +}; +static size_t isis_func_to_prefix_count = ARRAY_SIZE(isis_func_to_prefix_arr); + +static struct variable isis_var_arr[] = { + {ISIS_SYS_VERSION, INTEGER, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_LEVELTYPE, INTEGER, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_ID, STRING, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_MAXPATHSPLITS, UNSIGNED32, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_MAXLSPGENINT, UNSIGNED32, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_POLLESHELLORATE, UNSIGNED32, RONLY, + isis_snmp_find_sys_object}, + {ISIS_SYS_WAITTIME, UNSIGNED32, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_ADMINSTATE, INTEGER, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_L2TOL1LEAKING, INTEGER, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_MAXAGE, UNSIGNED32, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_RECEIVELSPBUFFERSIZE, UNSIGNED32, RONLY, + isis_snmp_find_sys_object}, + {ISIS_SYS_PROTSUPPORTED, STRING, RONLY, isis_snmp_find_sys_object}, + {ISIS_SYS_NOTIFICATIONENABLE, INTEGER, RONLY, + isis_snmp_find_sys_object}, + {ISIS_MANAREA_ADDREXISTSTATE, INTEGER, RONLY, isis_snmp_find_man_area}, + {ISIS_AREA_ADDR, STRING, RONLY, isis_snmp_find_area_addr}, + {ISIS_SUMM_ADDREXISTSTATE, INTEGER, RONLY, isis_snmp_find_summ_addr}, + {ISIS_SUMM_ADDRMETRIC, UNSIGNED32, RONLY, isis_snmp_find_summ_addr}, + {ISIS_SUMM_ADDRFULLMETRIC, UNSIGNED32, RONLY, isis_snmp_find_summ_addr}, + {ISIS_REDISTRIBUTE_ADDREXISTSTATE, INTEGER, RONLY, + isis_snmp_find_redistribute_addr}, + {ISIS_ROUTER_HOSTNAME, STRING, RONLY, isis_snmp_find_router}, + {ISIS_ROUTER_ID, UNSIGNED32, RONLY, isis_snmp_find_router}, + {ISIS_SYSLEVEL_ORIGLSPBUFFSIZE, UNSIGNED32, RONLY, + isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_MINLSPGENINT, UNSIGNED32, RONLY, + isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_STATE, INTEGER, RONLY, isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_SETOVERLOAD, INTEGER, RONLY, isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_SETOVERLOADUNTIL, UNSIGNED32, RONLY, + isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_METRICSTYLE, INTEGER, RONLY, isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_SPFCONSIDERS, INTEGER, RONLY, isis_snmp_find_sys_level}, + {ISIS_SYSLEVEL_TEENABLED, INTEGER, RONLY, isis_snmp_find_sys_level}, + {ISIS_SYSSTAT_CORRLSPS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_AUTHTYPEFAILS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_AUTHFAILS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_LSPDBASEOLOADS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_MANADDRDROPFROMAREAS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_ATTMPTTOEXMAXSEQNUMS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_SEQNUMSKIPS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_OWNLSPPURGES, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_IDFIELDLENMISMATCHES, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_PARTCHANGES, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_SPFRUNS, COUNTER32, RONLY, isis_snmp_find_system_counter}, + {ISIS_SYSSTAT_LSPERRORS, COUNTER32, RONLY, + isis_snmp_find_system_counter}, + {ISIS_NEXTCIRC_INDEX, UNSIGNED32, RONLY, + isis_snmp_find_next_circ_index}, + {ISIS_CIRC_IFINDEX, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_ADMINSTATE, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_EXISTSTATE, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_TYPE, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_EXTDOMAIN, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_LEVELTYPE, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_PASSIVECIRCUIT, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_MESHGROUPENABLED, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_MESHGROUP, UNSIGNED32, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_SMALLHELLOS, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_LASTUPTIME, TIMESTAMP, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_3WAYENABLED, INTEGER, RONLY, isis_snmp_find_circ}, + {ISIS_CIRC_EXTENDEDCIRCID, UNSIGNED32, RONLY, isis_snmp_find_circ}, + {ISIS_CIRCLEVEL_METRIC, UNSIGNED32, RONLY, isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_WIDEMETRIC, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_ISPRIORITY, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_IDOCTET, UNSIGNED32, RONLY, isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_ID, STRING, RONLY, isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_DESIS, STRING, RONLY, isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_HELLOMULTIPLIER, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_HELLOTIMER, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_DRHELLOTIMER, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_LSPTHROTTLE, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_MINLSPRETRANSINT, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_CSNPINTERVAL, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRCLEVEL_PARTSNPINTERVAL, UNSIGNED32, RONLY, + isis_snmp_find_circ_level}, + {ISIS_CIRC_ADJCHANGES, COUNTER32, RONLY, isis_snmp_find_circ_counter}, + {ISIS_CIRC_NUMADJ, UNSIGNED32, RONLY, isis_snmp_find_circ_counter}, + {ISIS_CIRC_INITFAILS, COUNTER32, RONLY, isis_snmp_find_circ_counter}, + {ISIS_CIRC_REJADJS, COUNTER32, RONLY, isis_snmp_find_circ_counter}, + {ISIS_CIRC_IDFIELDLENMISMATCHES, COUNTER32, RONLY, + isis_snmp_find_circ_counter}, + {ISIS_CIRC_MAXAREAADDRMISMATCHES, COUNTER32, RONLY, + isis_snmp_find_circ_counter}, + {ISIS_CIRC_AUTHTYPEFAILS, COUNTER32, RONLY, + isis_snmp_find_circ_counter}, + {ISIS_CIRC_AUTHFAILS, COUNTER32, RONLY, isis_snmp_find_circ_counter}, + {ISIS_CIRC_LANDESISCHANGES, COUNTER32, RONLY, + isis_snmp_find_circ_counter}, + {ISIS_ISADJ_STATE, INTEGER, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_3WAYSTATE, INTEGER, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_NEIGHSNPAADDRESS, STRING, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_NEIGHSYSTYPE, INTEGER, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_NEIGHSYSID, STRING, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_NBREXTENDEDCIRCID, UNSIGNED32, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_USAGE, INTEGER, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_HOLDTIMER, UNSIGNED32, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_NEIGHPRIORITY, UNSIGNED32, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJ_LASTUPTIME, TIMESTAMP, RONLY, isis_snmp_find_isadj}, + {ISIS_ISADJAREA_ADDRESS, STRING, RONLY, isis_snmp_find_isadj_area}, + {ISIS_ISADJIPADDR_TYPE, INTEGER, RONLY, isis_snmp_find_isadj_ipaddr}, + {ISIS_ISADJIPADDR_ADDRESS, STRING, RONLY, isis_snmp_find_isadj_ipaddr}, + {ISIS_ISADJPROTSUPP_PROTOCOL, INTEGER, RONLY, + isis_snmp_find_isadj_prot_supp}, +}; + +static const size_t isis_var_count = ARRAY_SIZE(isis_var_arr); + +/* Minimal set of hard-coded data */ +#define ISIS_VERSION (1) + +/* If sys-id is not set use this value */ +static uint8_t isis_null_sysid[ISIS_SYS_ID_LEN]; + +/* OSI addr-len */ +#define ISIS_SNMP_OSI_ADDR_LEN_MAX (20) + +/* + * The implementation has a fixed max-path splits value + * of 64 (see ISIS_MAX_PATH_SPLITS), the max mib value + * is 32. + * + * FIXME(aromanov): should we return 32 or 64? + */ +#define ISIS_SNMP_MAX_PATH_SPLITS (32) + +#define ISIS_SNMP_ADMIN_STATE_ON (1) + +#define ISIS_SNMP_ROW_STATUS_ACTIVE (1) + +#define ISIS_SNMP_LEVEL_STATE_OFF (1) +#define ISIS_SNMP_LEVEL_STATE_ON (2) +#define ISIS_SNMP_LEVEL_STATE_WAITING (3) +#define ISIS_SNMP_LEVEL_STATE_OVERLOADED (4) + +#define ISIS_SNMP_TRUTH_VALUE_TRUE (1) +#define ISIS_SNMP_TRUTH_VALUE_FALSE (2) + +#define ISIS_SNMP_METRIC_STYLE_NARROW (1) +#define ISIS_SNMP_METRIC_STYLE_WIDE (2) +#define ISIS_SNMP_METRIC_STYLE_BOTH (3) + +#define ISIS_SNMP_MESH_GROUP_INACTIVE (1) + +#define ISIS_SNMP_ADJ_STATE_DOWN (1) +#define ISIS_SNMP_ADJ_STATE_INITIALIZING (2) +#define ISIS_SNMP_ADJ_STATE_UP (3) +#define ISIS_SNMP_ADJ_STATE_FAILED (4) + +#define ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1 (1) +#define ISIS_SNMP_ADJ_NEIGHTYPE_IS_L2 (2) +#define ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1_L2 (3) +#define ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN (4) + +#define ISIS_SNMP_INET_TYPE_V4 (1) +#define ISIS_SNMP_INET_TYPE_V6 (2) + +#define ISIS_SNMP_P2P_CIRCUIT (3) + +/* Protocols supported value */ +static uint8_t isis_snmp_protocols_supported = 0x7; /* All: iso, ipv4, ipv6 */ + +/* + * Convenience function to move to the next circuit, + */ +static struct isis_circuit *isis_snmp_circuit_next(struct isis_circuit *circuit) +{ + uint32_t start; + uint32_t off; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return NULL; + + start = 1; + + if (circuit != NULL) + start = circuit->snmp_id + 1; + + for (off = start; off < SNMP_CIRCUITS_MAX; off++) { + circuit = isis->snmp_circuits[off]; + + if (circuit != NULL) + return circuit; + } + + return NULL; +} + +/* + * Convenience function to get the first matching level + */ +static int isis_snmp_circuit_get_level_lo(struct isis_circuit *circuit) +{ + if (circuit->is_type == IS_LEVEL_2) + return IS_LEVEL_2; + + return IS_LEVEL_1; +} + +/* Check level match */ +static int isis_snmp_get_level_match(int is_type, int level) +{ + if (is_type != IS_LEVEL_1 && is_type != IS_LEVEL_2 + && is_type != IS_LEVEL_1_AND_2) + return 0; + + if (level != IS_LEVEL_1 && level != IS_LEVEL_2) + return 0; + + + if (is_type == IS_LEVEL_1) { + if (level == IS_LEVEL_1) + return 1; + + return 0; + } + + if (is_type == IS_LEVEL_2) { + if (level == IS_LEVEL_2) + return 1; + + return 0; + } + + return 1; +} +/* + * Helper function to convert oid index representing + * octet-string index (e.g. isis-sys-id) to byte string + * representing the same index. + * + * Also we do not fail if idx is longer than max_len, + * so we can use the same function to check compound + * indexes. + */ +static int isis_snmp_conv_exact(uint8_t *buf, size_t max_len, size_t *out_len, + const oid *idx, size_t idx_len) +{ + size_t off; + size_t len; + + /* Oid representation: length followed by bytes */ + if (idx == NULL || idx_len == 0) + return 0; + + len = idx[0]; + + if (len > max_len) + return 0; + + if (idx_len < len + 1) + return 0; + + for (off = 0; off < len; off++) { + if (idx[off + 1] > 0xff) + return 0; + + buf[off] = (uint8_t)(idx[off + 1] & 0xff); + } + + *out_len = len; + + return 1; +} + +static int isis_snmp_conv_next(uint8_t *buf, size_t max_len, size_t *out_len, + int *try_exact, const oid *idx, size_t idx_len) +{ + size_t off; + size_t len; + size_t cmp_len; + + if (idx == NULL || idx_len == 0) { + *out_len = 0; + *try_exact = 1; + return 1; + } + + len = idx[0]; + + if (len > max_len) + return 0; + + cmp_len = len; + + if ((idx_len - 1) < cmp_len) + cmp_len = idx_len - 1; + + for (off = 0; off < cmp_len; off++) { + if (idx[off + 1] > 0xff) { + memset(buf + off, 0xff, len - off); + *out_len = len; + *try_exact = 1; + return 1; + } + + buf[off] = (uint8_t)(idx[off + 1] & 0xff); + } + + if (cmp_len < len) + memset(buf + cmp_len, 0, len - cmp_len); + + *out_len = len; + *try_exact = cmp_len < len ? 1 : 0; + return 1; +} + +/* + * Helper functions to find area address from snmp index + */ +static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len, + struct isis_area **ret_area, + struct area_addr **ret_addr) +{ + uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX]; + size_t addr_len; + struct isis_area *area = NULL; + struct area_addr *addr = NULL; + struct listnode *addr_node; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return 0; + + if (list_isempty(isis->area_list)) { + /* Area is not configured yet */ + return 0; + } + + area = listgetdata(listhead(isis->area_list)); + + int res = isis_snmp_conv_exact(cmp_buf, sizeof(cmp_buf), &addr_len, + oid_idx, oid_idx_len); + + + if (!res || addr_len == 0 || oid_idx_len != (addr_len + 1)) { + /* Bad conversion, empty address or extra oids at the end */ + return 0; + } + + for (ALL_LIST_ELEMENTS_RO(area->area_addrs, addr_node, addr)) { + if (addr->addr_len != addr_len) + continue; + + if (memcmp(addr->area_addr, cmp_buf, addr_len) == 0) { + if (ret_area != 0) + *ret_area = area; + + if (ret_addr != 0) + *ret_addr = addr; + + return 1; + } + } + return 0; +} + +static int isis_snmp_area_addr_lookup_next(oid *oid_idx, size_t oid_idx_len, + struct isis_area **ret_area, + struct area_addr **ret_addr) +{ + uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX]; + size_t addr_len; + int try_exact = 0; + struct isis_area *found_area = NULL; + struct isis_area *area = NULL; + struct area_addr *found_addr = NULL; + struct area_addr *addr = NULL; + struct listnode *addr_node; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return 0; + + if (list_isempty(isis->area_list)) { + /* Area is not configured yet */ + return 0; + } + + area = listgetdata(listhead(isis->area_list)); + + int res = isis_snmp_conv_next(cmp_buf, sizeof(cmp_buf), &addr_len, + &try_exact, oid_idx, oid_idx_len); + + if (!res) + return 0; + + for (ALL_LIST_ELEMENTS_RO(area->area_addrs, addr_node, addr)) { + if (addr->addr_len < addr_len) + continue; + + if (addr->addr_len == addr_len) { + if (addr_len == 0) + continue; + + res = memcmp(addr->area_addr, cmp_buf, addr_len); + + if (res < 0) + continue; + + if (res == 0 && addr->addr_len == addr_len) { + if (try_exact) { + /* + * This is the best match no point + * to look further + */ + found_area = area; + found_addr = addr; + break; + } + continue; + } + } + + if (found_addr == NULL || addr->addr_len < found_addr->addr_len + || (addr->addr_len == found_addr->addr_len + && memcmp(addr->area_addr, found_addr->area_addr, + addr->addr_len) + < 0)) { + found_area = area; + found_addr = addr; + } + } + + if (found_area == NULL) + return 0; + + if (ret_area != 0) + *ret_area = found_area; + + if (ret_addr != 0) + *ret_addr = found_addr; + + return 1; +} + +/* + * Helper functions to find circuit from + * snmp index + */ +static int isis_snmp_circuit_lookup_exact(oid *oid_idx, size_t oid_idx_len, + struct isis_circuit **ret_circuit) +{ + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return 0; + + if (oid_idx == NULL || oid_idx_len < 1 + || oid_idx[0] > SNMP_CIRCUITS_MAX) + return 0; + + circuit = isis->snmp_circuits[oid_idx[0]]; + if (circuit == NULL) + return 0; + + if (ret_circuit != NULL) + *ret_circuit = circuit; + + return 1; +} + +static int isis_snmp_circuit_lookup_next(oid *oid_idx, size_t oid_idx_len, + struct isis_circuit **ret_circuit) +{ + oid off; + oid start; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return 0; + + start = 0; + + if (oid_idx != NULL || oid_idx_len != 0) { + if (oid_idx[0] > SNMP_CIRCUITS_MAX) + return 0; + + start = oid_idx[0]; + } + + for (off = start; off < SNMP_CIRCUITS_MAX; ++off) { + circuit = isis->snmp_circuits[off]; + + if (circuit != NULL && off > start) { + if (ret_circuit != NULL) + *ret_circuit = circuit; + + return 1; + } + } + + return 0; +} + +/* + * Helper functions to find circuit level + * combination from snmp index + */ +static int isis_snmp_circuit_level_lookup_exact( + oid *oid_idx, size_t oid_idx_len, int check_match, + struct isis_circuit **ret_circuit, int *ret_level) +{ + int level; + int res; + struct isis_circuit *circuit; + + /* Minor optimization: check level first */ + if (oid_idx == NULL || oid_idx_len < 2) + return 0; + + if (oid_idx[1] < IS_LEVEL_1 || oid_idx[1] > IS_LEVEL_2) + return 0; + + level = (int)oid_idx[1]; + + res = isis_snmp_circuit_lookup_exact(oid_idx, oid_idx_len, &circuit); + + if (!res) + return 0; + + if (check_match && !isis_snmp_get_level_match(circuit->is_type, level)) + return 0; + + if (ret_circuit != NULL) + *ret_circuit = circuit; + + if (ret_level != NULL) + *ret_level = level; + + return 1; +} + +static int isis_snmp_circuit_level_lookup_next( + oid *oid_idx, size_t oid_idx_len, int check_match, + struct isis_circuit **ret_circuit, int *ret_level) +{ + oid off; + oid start; + struct isis_circuit *circuit; + int level; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return 0; + + start = 0; + + if (oid_idx != NULL || oid_idx_len != 0) { + if (oid_idx[0] > SNMP_CIRCUITS_MAX) + return 0; + + start = oid_idx[0]; + } + + for (off = start; off < SNMP_CIRCUITS_MAX; off++) { + circuit = isis->snmp_circuits[off]; + + if (circuit == NULL) + continue; + + if (off > start || oid_idx_len < 2) { + /* Found and can use level 1 */ + level = IS_LEVEL_1; + break; + } + + /* We have to check level specified by index */ + if (oid_idx[1] < IS_LEVEL_1) { + level = IS_LEVEL_1; + break; + } + + if (oid_idx[1] < IS_LEVEL_2) { + level = IS_LEVEL_2; + break; + } + + /* Try next */ + circuit = NULL; + } + + if (circuit == NULL) + return 0; + + if (check_match + && !isis_snmp_get_level_match(circuit->is_type, level)) { + if (level == IS_LEVEL_1) { + /* + * We can simply advance level because + * at least one level should match + */ + level = IS_LEVEL_2; + } else { + /* We have to move to the next circuit */ + circuit = isis_snmp_circuit_next(circuit); + if (circuit == NULL) + return 0; + + level = isis_snmp_circuit_get_level_lo(circuit); + } + } + + if (ret_circuit != NULL) + *ret_circuit = circuit; + + if (ret_level != NULL) + *ret_level = level; + + return 1; +} + +/* + * Helper functions to find adjacency + * from snmp index. + * + * We have 4 tables related to adjacency + * looking up adjacency is quite expensive + * in case of bcast interfaces. + * + * It is pain to have 4 very similar functions + * hence we pass in and out additional data + * we are looking for. + * + * Note: we use data-len value to distinguish + * between ipv4 and ipv6 addresses + */ +#define ISIS_SNMP_ADJ_DATA_NONE (1) +#define ISIS_SNMP_ADJ_DATA_AREA_ADDR (2) +#define ISIS_SNMP_ADJ_DATA_IP_ADDR (3) +#define ISIS_SNMP_ADJ_DATA_PROTO (4) + +/* + * Helper function to process data associated + * with adjacency + */ +static int isis_snmp_adj_helper(struct isis_adjacency *adj, int data_id, + oid data_off, uint8_t **ret_data, + size_t *ret_data_len) +{ + uint8_t *data = NULL; + size_t data_len = 0; + + switch (data_id) { + case ISIS_SNMP_ADJ_DATA_NONE: + break; + + case ISIS_SNMP_ADJ_DATA_AREA_ADDR: + if (data_off >= adj->area_address_count) + return 0; + + data = adj->area_addresses[data_off].area_addr; + data_len = adj->area_addresses[data_off].addr_len; + break; + + case ISIS_SNMP_ADJ_DATA_IP_ADDR: + if (data_off + >= (adj->ipv4_address_count + adj->ipv6_address_count)) + return 0; + + if (data_off >= adj->ipv4_address_count) { + data = (uint8_t *)&adj->ipv6_addresses + [data_off - adj->ipv4_address_count]; + data_len = sizeof(adj->ipv6_addresses[0]); + } else { + data = (uint8_t *)&adj->ipv4_addresses[data_off]; + data_len = sizeof(adj->ipv4_addresses[0]); + } + + break; + + + case ISIS_SNMP_ADJ_DATA_PROTO: + if (data_off >= adj->nlpids.count) + return 0; + + data = &adj->nlpids.nlpids[data_off]; + data_len = sizeof(adj->nlpids.nlpids[0]); + break; + + default: + assert(0); + return 0; + } + + if (ret_data != NULL) + *ret_data = data; + + if (ret_data_len != NULL) + *ret_data_len = data_len; + + return 1; +} + +static int isis_snmp_adj_lookup_exact(oid *oid_idx, size_t oid_idx_len, + int data_id, + struct isis_adjacency **ret_adj, + oid *ret_data_idx, uint8_t **ret_data, + size_t *ret_data_len) +{ + int res; + struct listnode *node; + struct isis_circuit *circuit; + struct isis_adjacency *adj; + struct isis_adjacency *tmp_adj; + oid adj_idx; + oid data_off; + uint8_t *data; + size_t data_len; + + res = isis_snmp_circuit_lookup_exact(oid_idx, oid_idx_len, &circuit); + + if (!res) + return 0; + + if (oid_idx == NULL || oid_idx_len < 2 + || (data_id != ISIS_SNMP_ADJ_DATA_NONE && oid_idx_len < 3)) + return 0; + + adj_idx = oid_idx[1]; + + if (data_id != ISIS_SNMP_ADJ_DATA_NONE) { + if (oid_idx[2] == 0) + return 0; + + data_off = oid_idx[2] - 1; + } else { + /* + * Data-off is not used if data-id is none + * but we set it just for consistency + */ + data_off = 0; + } + + adj = NULL; + data = NULL; + data_len = 0; + + for (ALL_LIST_ELEMENTS_RO(circuit->snmp_adj_list, node, tmp_adj)) { + if (tmp_adj->snmp_idx > adj_idx) { + /* + * Adjacencies are ordered in the list + * no point to look further + */ + break; + } + + if (tmp_adj->snmp_idx == adj_idx) { + res = isis_snmp_adj_helper(tmp_adj, data_id, data_off, + &data, &data_len); + if (res) + adj = tmp_adj; + + break; + } + } + + if (adj == NULL) + return 0; + + if (ret_adj != NULL) + *ret_adj = adj; + + if (ret_data_idx != NULL) + *ret_data_idx = data_off + 1; + + if (ret_data) + *ret_data = data; + + if (ret_data_len) + *ret_data_len = data_len; + + return 1; +} + +static int isis_snmp_adj_lookup_next(oid *oid_idx, size_t oid_idx_len, + int data_id, + struct isis_adjacency **ret_adj, + oid *ret_data_idx, uint8_t **ret_data, + size_t *ret_data_len) +{ + struct listnode *node; + struct isis_circuit *circuit; + struct isis_adjacency *adj; + struct isis_adjacency *tmp_adj; + oid circ_idx; + oid adj_idx; + oid data_idx; + uint8_t *data; + size_t data_len; + + adj = NULL; + data = NULL; + data_len = 0; + + /* + * Note: we rely on the fact that data indexes are consequtive + * starting from 1 + */ + + if (oid_idx == 0 || oid_idx_len == 0) { + circ_idx = 0; + adj_idx = 0; + data_idx = 0; + } else if (oid_idx_len == 1) { + circ_idx = oid_idx[0]; + adj_idx = 0; + data_idx = 0; + } else if (oid_idx_len == 2) { + circ_idx = oid_idx[0]; + adj_idx = oid_idx[1]; + data_idx = 0; + } else { + circ_idx = oid_idx[0]; + adj_idx = oid_idx[1]; + + if (data_id == ISIS_SNMP_ADJ_DATA_NONE) + data_idx = 0; + else + data_idx = oid_idx[2]; + } + + if (!isis_snmp_circuit_lookup_exact(&circ_idx, 1, &circuit) + && !isis_snmp_circuit_lookup_next(&circ_idx, 1, &circuit)) + /* No circuit */ + return 0; + + if (circuit->snmp_id != circ_idx) { + /* Match is not exact */ + circ_idx = 0; + adj_idx = 0; + data_idx = 0; + } + + /* + * Note: the simple loop below will work in all cases + */ + while (circuit != NULL) { + for (ALL_LIST_ELEMENTS_RO(circuit->snmp_adj_list, node, + tmp_adj)) { + if (tmp_adj->snmp_idx < adj_idx) + continue; + + if (tmp_adj->snmp_idx == adj_idx + && data_id == ISIS_SNMP_ADJ_DATA_NONE) + continue; + + if (adj_idx != 0 && tmp_adj->snmp_idx > adj_idx) + data_idx = 0; + + if (isis_snmp_adj_helper(tmp_adj, data_id, data_idx, + &data, &data_len)) { + adj = tmp_adj; + break; + } + } + + if (adj != NULL) + break; + + circuit = isis_snmp_circuit_next(circuit); + circ_idx = 0; + adj_idx = 0; + data_idx = 0; + } + + if (adj == NULL) + return 0; + + if (ret_adj != NULL) + *ret_adj = adj; + + if (ret_data_idx != 0) { + if (data_id == ISIS_SNMP_ADJ_DATA_NONE) + /* + * Value does not matter but let us set + * it to zero for consistency + */ + *ret_data_idx = 0; + else + *ret_data_idx = data_idx + 1; + } + + if (ret_data != 0) + *ret_data = data; + + if (ret_data_len != 0) + *ret_data_len = data_len; + + return 1; +} + +static uint8_t *isis_snmp_find_sys_object(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + struct isis_area *area = NULL; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return NULL; + + if (!list_isempty(isis->area_list)) + area = listgetdata(listhead(isis->area_list)); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + switch (v->magic) { + case ISIS_SYS_VERSION: + return SNMP_INTEGER(ISIS_VERSION); + + case ISIS_SYS_LEVELTYPE: + /* + * If we do not have areas use 1&2 otherwise use settings + * from the first area in the list + */ + if (area == NULL) + return SNMP_INTEGER(IS_LEVEL_1_AND_2); + + return SNMP_INTEGER(area->is_type); + + case ISIS_SYS_ID: + if (!isis->sysid_set) { + *var_len = ISIS_SYS_ID_LEN; + return isis_null_sysid; + } + + *var_len = ISIS_SYS_ID_LEN; + return isis->sysid; + + case ISIS_SYS_MAXPATHSPLITS: + return SNMP_INTEGER(ISIS_SNMP_MAX_PATH_SPLITS); + + case ISIS_SYS_MAXLSPGENINT: + return SNMP_INTEGER(DEFAULT_MAX_LSP_GEN_INTERVAL); + + case ISIS_SYS_POLLESHELLORATE: + return SNMP_INTEGER(DEFAULT_HELLO_INTERVAL); + + case ISIS_SYS_WAITTIME: + /* Note: it seems that we have same fixed delay time */ + return SNMP_INTEGER(DEFAULT_MIN_LSP_GEN_INTERVAL); + + case ISIS_SYS_ADMINSTATE: + /* If daemon is running it admin state is on */ + return SNMP_INTEGER(ISIS_SNMP_ADMIN_STATE_ON); + + + case ISIS_SYS_L2TOL1LEAKING: + /* We do not allow l2-to-l1 leaking */ + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + case ISIS_SYS_MAXAGE: + return SNMP_INTEGER(MAX_AGE); + + case ISIS_SYS_RECEIVELSPBUFFERSIZE: + if (area == NULL) + return SNMP_INTEGER(DEFAULT_LSP_MTU); + + return SNMP_INTEGER(area->lsp_mtu); + + case ISIS_SYS_PROTSUPPORTED: + *var_len = 1; + return &isis_snmp_protocols_supported; + + case ISIS_SYS_NOTIFICATIONENABLE: + if (isis->snmp_notifications) + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); + + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + default: + break; + } + + return NULL; +} + + +static uint8_t *isis_snmp_find_man_area(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + int res; + struct area_addr *area_addr = NULL; + oid *oid_idx; + size_t oid_idx_len; + size_t off = 0; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + + if (exact) { + res = isis_snmp_area_addr_lookup_exact(oid_idx, oid_idx_len, + NULL, &area_addr); + + if (!res) + return NULL; + + } else { + res = isis_snmp_area_addr_lookup_next(oid_idx, oid_idx_len, + NULL, &area_addr); + + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = area_addr->addr_len; + + for (off = 0; off < area_addr->addr_len; off++) + name[v->namelen + 1 + off] = area_addr->area_addr[off]; + + *length = v->namelen + 1 + area_addr->addr_len; + } + + switch (v->magic) { + case ISIS_MANAREA_ADDREXISTSTATE: + return SNMP_INTEGER(ISIS_SNMP_ROW_STATUS_ACTIVE); + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_area_addr(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* + * Area addresses in sense of addresses reported by L1 lsps + * are not supported yet. + */ + (void)v; + (void)name; + (void)length; + (void)exact; + (void)var_len; + + + *write_method = NULL; + + return NULL; +} + +static uint8_t *isis_snmp_find_summ_addr(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* + * So far there is no way to set summary table values through cli + * and snmp operations are read-only, hence there are no entries + */ + (void)v; + (void)name; + (void)length; + (void)exact; + (void)var_len; + *write_method = NULL; + + return NULL; +} + +static uint8_t *isis_snmp_find_redistribute_addr(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* + * It is not clear at the point whether redist code in isis is actually + * used for now we will consider that entries are not present + */ + (void)v; + (void)name; + (void)length; + (void)exact; + (void)var_len; + *write_method = NULL; + + return NULL; +} + +static uint8_t *isis_snmp_find_router(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + uint8_t cmp_buf[ISIS_SYS_ID_LEN]; + size_t cmp_len; + int try_exact; + int cmp_level; + int res; + struct isis_dynhn *dyn = NULL; + oid *oid_idx; + size_t oid_idx_len; + size_t off = 0; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + + if (exact) { + res = isis_snmp_conv_exact(cmp_buf, sizeof(cmp_buf), &cmp_len, + oid_idx, oid_idx_len); + + if (!res || cmp_len != ISIS_SYS_ID_LEN + || oid_idx_len != (cmp_len + 2)) + /* + * Bad conversion, or bad length, + * or extra oids at the end + */ + return NULL; + + if (oid_idx[ISIS_SYS_ID_LEN + 1] < IS_LEVEL_1 + || oid_idx[ISIS_SYS_ID_LEN + 1] > IS_LEVEL_2) + /* Level part of the index is out of range */ + return NULL; + + cmp_level = (int)oid_idx[ISIS_SYS_ID_LEN + 1]; + + dyn = dynhn_find_by_id(cmp_buf); + + if (dyn == NULL || dyn->level != cmp_level) + return NULL; + + switch (v->magic) { + case ISIS_ROUTER_HOSTNAME: + *var_len = strlen(dyn->hostname); + return (uint8_t *)dyn->hostname; + + case ISIS_ROUTER_ID: + /* It seems that we do no know router-id in lsps */ + return SNMP_INTEGER(0); + + default: + break; + } + + return NULL; + } + + res = isis_snmp_conv_next(cmp_buf, sizeof(cmp_buf), &cmp_len, + &try_exact, oid_idx, oid_idx_len); + + + if (!res) + /* Bad conversion */ + return NULL; + + if (cmp_len != ISIS_SYS_ID_LEN) { + /* We do not have valid index oids */ + memset(cmp_buf, 0, sizeof(cmp_buf)); + cmp_level = 0; + } else if (try_exact) + /* + * We have no valid level index. + * Let start from non-existing level 0 and + * hence not need to do exact match + */ + cmp_level = 0; + else if (oid_idx_len < (ISIS_SYS_ID_LEN + 2)) + cmp_level = 0; + else if (oid_idx[ISIS_SYS_ID_LEN + 1] <= IS_LEVEL_2) + cmp_level = (int)oid_idx[ISIS_SYS_ID_LEN + 1]; + else + /* + * Any value greater than 2 will have the same result + * but we can have integer overflows, hence 3 is a reasonable + * choice + */ + cmp_level = (int)(IS_LEVEL_2 + 1); + + dyn = dynhn_snmp_next(cmp_buf, cmp_level); + + if (dyn == NULL) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = ISIS_SYS_ID_LEN; + + for (off = 0; off < ISIS_SYS_ID_LEN; off++) + name[v->namelen + 1 + off] = dyn->id[off]; + + name[v->namelen + 1 + ISIS_SYS_ID_LEN] = (oid)dyn->level; + + /* Set length */ + *length = v->namelen + 1 + ISIS_SYS_ID_LEN + 1; + + switch (v->magic) { + case ISIS_ROUTER_HOSTNAME: + *var_len = strlen(dyn->hostname); + return (uint8_t *)dyn->hostname; + + case ISIS_ROUTER_ID: + /* It seems that we do no know router-id in lsps */ + return SNMP_INTEGER(0); + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_sys_level(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + oid *oid_idx; + size_t oid_idx_len; + int level; + int level_match; + struct isis_area *area = NULL; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return NULL; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + + if (exact) { + if (oid_idx == NULL || oid_idx_len != 1) + return NULL; + + if (oid_idx[0] == IS_LEVEL_1) + level = IS_LEVEL_1; + else if (oid_idx[0] == IS_LEVEL_2) + level = IS_LEVEL_2; + else + return NULL; + + } else { + if (oid_idx == NULL) + level = IS_LEVEL_1; + else if (oid_idx_len == 0) + level = IS_LEVEL_1; + else if (oid_idx[0] < IS_LEVEL_1) + level = IS_LEVEL_1; + else if (oid_idx[0] < IS_LEVEL_2) + level = IS_LEVEL_2; + else + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = level; + + /* Set length */ + *length = v->namelen + 1; + } + + area = NULL; + + if (!list_isempty(isis->area_list)) + area = listgetdata(listhead(isis->area_list)); + + level_match = 0; + + if (area != NULL) + level_match = isis_snmp_get_level_match(area->is_type, level); + + switch (v->magic) { + case ISIS_SYSLEVEL_ORIGLSPBUFFSIZE: + if (level_match) + return SNMP_INTEGER(area->lsp_mtu); + + return SNMP_INTEGER(DEFAULT_LSP_MTU); + + case ISIS_SYSLEVEL_MINLSPGENINT: + if (level_match) + return SNMP_INTEGER(area->lsp_gen_interval[level - 1]); + else + return SNMP_INTEGER(DEFAULT_MIN_LSP_GEN_INTERVAL); + + case ISIS_SYSLEVEL_STATE: + if (level_match) { + if (area->overload_bit) + return SNMP_INTEGER( + ISIS_SNMP_LEVEL_STATE_OVERLOADED); + + return SNMP_INTEGER(ISIS_SNMP_LEVEL_STATE_ON); + } + return SNMP_INTEGER(ISIS_SNMP_LEVEL_STATE_OFF); + + case ISIS_SYSLEVEL_SETOVERLOAD: + if (level_match && area->overload_bit) + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); + + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + case ISIS_SYSLEVEL_SETOVERLOADUNTIL: + /* We do not have automatic cleanup of overload bit */ + return SNMP_INTEGER(0); + + case ISIS_SYSLEVEL_METRICSTYLE: + if (level_match) { + if (area->newmetric && area->oldmetric) + return SNMP_INTEGER( + ISIS_SNMP_METRIC_STYLE_BOTH); + + if (area->newmetric) + return SNMP_INTEGER( + ISIS_SNMP_METRIC_STYLE_WIDE); + + return SNMP_INTEGER(ISIS_SNMP_METRIC_STYLE_NARROW); + } + return SNMP_INTEGER(ISIS_SNMP_METRIC_STYLE_NARROW); + + case ISIS_SYSLEVEL_SPFCONSIDERS: + return SNMP_INTEGER(ISIS_SNMP_METRIC_STYLE_BOTH); + + case ISIS_SYSLEVEL_TEENABLED: + if (level_match && IS_MPLS_TE(area->mta)) + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); + + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_system_counter(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + oid *oid_idx; + size_t oid_idx_len; + int level; + int level_match; + struct isis_area *area = NULL; + uint32_t val; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return NULL; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + + if (exact) { + if (oid_idx == NULL || oid_idx_len != 1) + return 0; + + if (oid_idx[0] == IS_LEVEL_1) + level = IS_LEVEL_1; + else if (oid_idx[0] == IS_LEVEL_2) + level = IS_LEVEL_2; + else + return NULL; + + } else { + if (oid_idx == NULL) + level = IS_LEVEL_1; + else if (oid_idx_len == 0) + level = IS_LEVEL_1; + else if (oid_idx[0] < IS_LEVEL_1) + level = IS_LEVEL_1; + else if (oid_idx[0] < IS_LEVEL_2) + level = IS_LEVEL_2; + else + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = level; + + /* Set length */ + *length = v->namelen + 1; + } + + area = NULL; + + if (!list_isempty(isis->area_list)) + area = listgetdata(listhead(isis->area_list)); + + level_match = 0; + + if (area != NULL) + level_match = isis_snmp_get_level_match(area->is_type, level); + + if (!level_match) + /* If level does not match all counters are zeros */ + return SNMP_INTEGER(0); + + val = 0; + + switch (v->magic) { + case ISIS_SYSSTAT_CORRLSPS: + val = 0; + break; + + case ISIS_SYSSTAT_AUTHTYPEFAILS: + val = (uint32_t)area->auth_type_failures[level - 1]; + break; + + case ISIS_SYSSTAT_AUTHFAILS: + val = (uint32_t)area->auth_failures[level - 1]; + break; + + case ISIS_SYSSTAT_LSPDBASEOLOADS: + val = area->overload_counter; + break; + + case ISIS_SYSSTAT_MANADDRDROPFROMAREAS: + /* We do not support manual addresses */ + val = 0; + break; + + case ISIS_SYSSTAT_ATTMPTTOEXMAXSEQNUMS: + val = area->lsp_exceeded_max_counter; + break; + + case ISIS_SYSSTAT_SEQNUMSKIPS: + val = area->lsp_seqno_skipped_counter; + break; + + case ISIS_SYSSTAT_OWNLSPPURGES: + if (!area->purge_originator) + val = 0; + else + val = area->lsp_purge_count[level - 1]; + break; + + case ISIS_SYSSTAT_IDFIELDLENMISMATCHES: + val = (uint32_t)area->id_len_mismatches[level - 1]; + break; + + case ISIS_SYSSTAT_PARTCHANGES: + /* Not supported */ + val = 0; + break; + + case ISIS_SYSSTAT_SPFRUNS: + val = (uint32_t)area->spf_run_count[level - 1]; + break; + + case ISIS_SYSSTAT_LSPERRORS: + val = (uint32_t)area->lsp_error_counter[level - 1]; + break; + + default: + return NULL; + } + + return SNMP_INTEGER(val); +} + +static uint8_t *isis_snmp_find_next_circ_index(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + switch (v->magic) { + case ISIS_NEXTCIRC_INDEX: + /* + * We do not support circuit creation through snmp + */ + return SNMP_INTEGER(0); + + default: + break; + } + + return 0; +} + +static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + /* Index is circuit-id: 1-255 */ + oid *oid_idx; + size_t oid_idx_len; + struct isis_circuit *circuit; + uint64_t up_ticks; + uint64_t delta_ticks; + uint32_t now_time; + int res; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_circuit_lookup_exact(oid_idx, oid_idx_len, + &circuit); + + if (!res || oid_idx_len != 1) + return NULL; + + } else { + res = isis_snmp_circuit_lookup_next(oid_idx, oid_idx_len, + &circuit); + + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = circuit->snmp_id; + + /* Set length */ + *length = v->namelen + 1; + } + + switch (v->magic) { + case ISIS_CIRC_IFINDEX: + if (circuit->interface == 0) + return SNMP_INTEGER(0); + + return SNMP_INTEGER(circuit->interface->ifindex); + + case ISIS_CIRC_ADMINSTATE: + return SNMP_INTEGER(ISIS_SNMP_ADMIN_STATE_ON); + + case ISIS_CIRC_EXISTSTATE: + return SNMP_INTEGER(ISIS_SNMP_ROW_STATUS_ACTIVE); + + case ISIS_CIRC_TYPE: + /* + * Note: values do not match 100%: + * + * 1. From isis_circuit.h: + * CIRCUIT_T_UNKNOWN 0 + * CIRCUIT_T_BROADCAST 1 + * CIRCUIT_T_P2P 2 + * CIRCUIT_T_LOOPBACK 3 + * + * 2. From rfc: + * broadcast(1), + * ptToPt(2), + * staticIn(3), + * staticOut(4), + */ + + return SNMP_INTEGER(circuit->circ_type); + + case ISIS_CIRC_EXTDOMAIN: + if (circuit->ext_domain) + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); + + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + case ISIS_CIRC_LEVELTYPE: + return SNMP_INTEGER(circuit->is_type); + + case ISIS_CIRC_PASSIVECIRCUIT: + if (circuit->is_passive) + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); + + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + case ISIS_CIRC_MESHGROUPENABLED: + /* Not supported */ + return SNMP_INTEGER(ISIS_SNMP_MESH_GROUP_INACTIVE); + + case ISIS_CIRC_MESHGROUP: + /* Not supported */ + return SNMP_INTEGER(0); + + case ISIS_CIRC_SMALLHELLOS: + /* + * return false if lan hellos must be padded + */ + if (circuit->pad_hellos) + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); + + case ISIS_CIRC_LASTUPTIME: + if (circuit->last_uptime == 0) + return SNMP_INTEGER(0); + + up_ticks = netsnmp_get_agent_uptime(); + now_time = isis_snmp_time(); + + if (circuit->last_uptime >= now_time) + return SNMP_INTEGER(up_ticks); + + delta_ticks = (now_time - circuit->last_uptime) * 10; + + if (up_ticks < delta_ticks) + return SNMP_INTEGER(up_ticks); + + return SNMP_INTEGER((uint32_t)(up_ticks - delta_ticks)); + + case ISIS_CIRC_3WAYENABLED: + /* Not supported */ + return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); + + case ISIS_CIRC_EXTENDEDCIRCID: + /* Used for 3-way hand shake only */ + return SNMP_INTEGER(0); + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_circ_level(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + static uint8_t circuit_id_val[ISIS_SYS_ID_LEN + 1]; + /* Index is circuit-id: 1-255 + level: 1-2 */ + oid *oid_idx; + size_t oid_idx_len; + int res; + struct isis_circuit *circuit; + int level; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return NULL; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_circuit_level_lookup_exact(oid_idx, oid_idx_len, + 1, &circuit, &level); + + if (!res || oid_idx_len != 2) + return NULL; + + } else { + res = isis_snmp_circuit_level_lookup_next(oid_idx, oid_idx_len, + 1, &circuit, &level); + + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = circuit->snmp_id; + name[v->namelen + 1] = level; + + /* Set length */ + *length = v->namelen + 2; + } + + switch (v->magic) { + case ISIS_CIRCLEVEL_METRIC: + return SNMP_INTEGER(circuit->metric[level - 1]); + + case ISIS_CIRCLEVEL_WIDEMETRIC: + if (circuit->area == NULL || !circuit->area->newmetric) { + /* What should we do if wide metric is not supported? */ + return SNMP_INTEGER(0); + } + return SNMP_INTEGER(circuit->te_metric[level - 1]); + + case ISIS_CIRCLEVEL_ISPRIORITY: + return SNMP_INTEGER(circuit->priority[level - 1]); + + case ISIS_CIRCLEVEL_IDOCTET: + return SNMP_INTEGER(circuit->circuit_id); + + case ISIS_CIRCLEVEL_ID: + if (circuit->circ_type != CIRCUIT_T_P2P) { + /* + * Unless it is point-to-point circuit, the value is and + * empty octet string + */ + *var_len = 0; + return circuit_id_val; + } + + /* !!!!!! Circuit-id is zero for p2p links */ + if (circuit->u.p2p.neighbor == NULL + || circuit->u.p2p.neighbor->adj_state != ISIS_ADJ_UP) { + /* No adjacency or adjacency not fully up yet */ + memcpy(circuit_id_val, isis->sysid, ISIS_SYS_ID_LEN); + circuit_id_val[ISIS_SYS_ID_LEN] = circuit->circuit_id; + *var_len = ISIS_SYS_ID_LEN + 1; + return circuit_id_val; + } + + /* Adjacency fully-up */ + memcpy(circuit_id_val, circuit->u.p2p.neighbor->sysid, + ISIS_SYS_ID_LEN); + circuit_id_val[ISIS_SYS_ID_LEN] = 0; + *var_len = ISIS_SYS_ID_LEN + 1; + return circuit_id_val; + + case ISIS_CIRCLEVEL_DESIS: + if (circuit->circ_type != CIRCUIT_T_BROADCAST + || !circuit->u.bc.is_dr[level - 1]) { + /* + * Unless it is lan circuit participating in dis process + * the value is an empty octet string + */ + *var_len = 0; + return circuit_id_val; + } + + *var_len = ISIS_SYS_ID_LEN + 1; + + if (level == IS_LEVEL_1) + return circuit->u.bc.l1_desig_is; + + return circuit->u.bc.l2_desig_is; + + case ISIS_CIRCLEVEL_HELLOMULTIPLIER: + return SNMP_INTEGER(circuit->hello_multiplier[level - 1]); + + case ISIS_CIRCLEVEL_HELLOTIMER: + return SNMP_INTEGER(circuit->hello_interval[level - 1] * 1000); + + case ISIS_CIRCLEVEL_DRHELLOTIMER: + return SNMP_INTEGER(circuit->hello_interval[level - 1] * 1000); + + case ISIS_CIRCLEVEL_LSPTHROTTLE: + if (circuit->area) + return SNMP_INTEGER( + circuit->area->min_spf_interval[level - 1] + * 1000); + else + return SNMP_INTEGER(0); + + case ISIS_CIRCLEVEL_MINLSPRETRANSINT: + if (circuit->area) + return SNMP_INTEGER( + circuit->area->min_spf_interval[level - 1]); + else + return SNMP_INTEGER(0); + + case ISIS_CIRCLEVEL_CSNPINTERVAL: + return SNMP_INTEGER(circuit->csnp_interval[level - 1]); + + case ISIS_CIRCLEVEL_PARTSNPINTERVAL: + return SNMP_INTEGER(circuit->psnp_interval[level - 1]); + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_circ_counter(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* Index circuit-id 1-255 + level */ + oid *oid_idx; + size_t oid_idx_len; + int res; + struct isis_circuit *circuit; + int level; + uint32_t val = 0; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_circuit_level_lookup_exact(oid_idx, oid_idx_len, + 1, &circuit, &level); + + if (!res || oid_idx_len != 2) + return NULL; + + } else { + res = isis_snmp_circuit_level_lookup_next(oid_idx, oid_idx_len, + 1, &circuit, &level); + + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = circuit->snmp_id; + if (circuit->circ_type == CIRCUIT_T_P2P) + name[v->namelen + 1] = ISIS_SNMP_P2P_CIRCUIT; + else + name[v->namelen + 1] = level; + + /* Set length */ + *length = v->namelen + 2; + } + + switch (v->magic) { + case ISIS_CIRC_ADJCHANGES: + val = circuit->adj_state_changes; + break; + + case ISIS_CIRC_NUMADJ: + if (circuit->circ_type == CIRCUIT_T_P2P) { + val = circuit->u.p2p.neighbor == NULL ? 0 : 1; + break; + } + + if (circuit->circ_type != CIRCUIT_T_BROADCAST) { + val = 0; + break; + } + + if (level == IS_LEVEL_1) { + if (circuit->u.bc.adjdb[0] == NULL) + val = 0; + else + val = listcount(circuit->u.bc.adjdb[0]); + break; + } + + if (circuit->u.bc.adjdb[1] == NULL) + val = 0; + else + val = listcount(circuit->u.bc.adjdb[1]); + + break; + + case ISIS_CIRC_INITFAILS: + val = circuit->init_failures; /* counter never incremented */ + break; + + case ISIS_CIRC_REJADJS: + val = circuit->rej_adjacencies; + break; + + case ISIS_CIRC_IDFIELDLENMISMATCHES: + val = circuit->id_len_mismatches; + break; + + case ISIS_CIRC_MAXAREAADDRMISMATCHES: + val = circuit->max_area_addr_mismatches; + break; + + case ISIS_CIRC_AUTHTYPEFAILS: + val = circuit->auth_type_failures; + break; + + case ISIS_CIRC_AUTHFAILS: + val = circuit->auth_failures; + break; + + case ISIS_CIRC_LANDESISCHANGES: + if (circuit->circ_type == CIRCUIT_T_P2P) + val = 0; + else + val = circuit->desig_changes[level - 1]; + break; + + default: + return NULL; + } + + return SNMP_INTEGER(val); +} + +static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + /* Index is circuit-id: 1-255 + adj-id: 1-... */ + oid *oid_idx; + size_t oid_idx_len; + int res; + uint32_t val; + struct isis_adjacency *adj; + uint64_t up_ticks; + uint64_t delta_ticks; + uint32_t now_time; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len, + ISIS_SNMP_ADJ_DATA_NONE, &adj, + NULL, NULL, NULL); + + if (!res || oid_idx_len != 2) + return NULL; + + } else { + res = isis_snmp_adj_lookup_next(oid_idx, oid_idx_len, + ISIS_SNMP_ADJ_DATA_NONE, &adj, + NULL, NULL, NULL); + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = adj->circuit->snmp_id; + name[v->namelen + 1] = adj->snmp_idx; + + /* Set length */ + *length = v->namelen + 2; + } + + switch (v->magic) { + case ISIS_ISADJ_STATE: + val = ISIS_SNMP_ADJ_STATE_DOWN; + + switch (adj->adj_state) { + case ISIS_ADJ_UNKNOWN: + case ISIS_ADJ_DOWN: + val = ISIS_SNMP_ADJ_STATE_DOWN; + break; + + case ISIS_ADJ_INITIALIZING: + val = ISIS_SNMP_ADJ_STATE_INITIALIZING; + break; + + case ISIS_ADJ_UP: + val = ISIS_SNMP_ADJ_STATE_UP; + break; + } + + return SNMP_INTEGER(val); + + case ISIS_ISADJ_3WAYSTATE: + return SNMP_INTEGER(adj->threeway_state); + + case ISIS_ISADJ_NEIGHSNPAADDRESS: { + const char *snpa = (char *)snpa_print(adj->snpa); + *var_len = strlen(snpa); + return (uint8_t *)snpa; + } + + case ISIS_ISADJ_NEIGHSYSTYPE: + val = ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN; + + switch (adj->sys_type) { + case ISIS_SYSTYPE_UNKNOWN: + case ISIS_SYSTYPE_ES: + val = ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN; + break; + + case ISIS_SYSTYPE_IS: + val = ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1_L2; + break; + + case ISIS_SYSTYPE_L1_IS: + val = ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1; + break; + + case ISIS_SYSTYPE_L2_IS: + val = ISIS_SNMP_ADJ_NEIGHTYPE_IS_L2; + break; + } + + return SNMP_INTEGER(val); + + case ISIS_ISADJ_NEIGHSYSID: + *var_len = sizeof(adj->sysid); + return adj->sysid; + + case ISIS_ISADJ_NBREXTENDEDCIRCID: + return SNMP_INTEGER(adj->ext_circuit_id != 0 ? 1 : 0); + + case ISIS_ISADJ_USAGE: + /* It seems that no value conversion is required */ + return SNMP_INTEGER(adj->adj_usage); + + case ISIS_ISADJ_HOLDTIMER: + /* + * It seems that we want remaining timer + */ + if (adj->last_upd != 0) { + val = isis_snmp_time(); + if (val < (adj->last_upd + adj->hold_time)) + return SNMP_INTEGER(adj->last_upd + + adj->hold_time - val); + } + /* Not running or just expired */ + return SNMP_INTEGER(0); + + case ISIS_ISADJ_NEIGHPRIORITY: + return SNMP_INTEGER(adj->prio[adj->level - 1]); + + case ISIS_ISADJ_LASTUPTIME: + if (adj->flaps == 0) + return SNMP_INTEGER(0); + + up_ticks = netsnmp_get_agent_uptime(); + + now_time = isis_snmp_time(); + + if (adj->last_flap >= now_time) + return SNMP_INTEGER(up_ticks); + + delta_ticks = (now_time - adj->last_flap) * 10; + + if (up_ticks < delta_ticks) + return SNMP_INTEGER(up_ticks); + + return SNMP_INTEGER((uint32_t)(up_ticks - delta_ticks)); + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_isadj_area(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* Index circuit-id: 1-255 + adj-id: 1-... */ + oid *oid_idx; + size_t oid_idx_len; + int res; + struct isis_adjacency *adj; + oid data_idx; + uint8_t *data; + size_t data_len; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len, + ISIS_SNMP_ADJ_DATA_AREA_ADDR, + &adj, NULL, &data, &data_len); + + if (!res || oid_idx_len != 3) + return NULL; + + } else { + res = isis_snmp_adj_lookup_next( + oid_idx, oid_idx_len, ISIS_SNMP_ADJ_DATA_AREA_ADDR, + &adj, &data_idx, &data, &data_len); + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = adj->circuit->snmp_id; + name[v->namelen + 1] = adj->snmp_idx; + name[v->namelen + 2] = data_idx; + + /* Set length */ + *length = v->namelen + 3; + } + + switch (v->magic) { + case ISIS_ISADJAREA_ADDRESS: + *var_len = data_len; + return data; + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_isadj_ipaddr(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* Index circuit-id 1-255 + adj-id 1-... */ + oid *oid_idx; + size_t oid_idx_len; + int res; + struct isis_adjacency *adj; + oid data_idx; + uint8_t *data; + size_t data_len; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len, + ISIS_SNMP_ADJ_DATA_IP_ADDR, + &adj, NULL, &data, &data_len); + + if (!res || oid_idx_len != 3) + return NULL; + } else { + res = isis_snmp_adj_lookup_next( + oid_idx, oid_idx_len, ISIS_SNMP_ADJ_DATA_IP_ADDR, &adj, + &data_idx, &data, &data_len); + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = adj->circuit->snmp_id; + name[v->namelen + 1] = adj->snmp_idx; + name[v->namelen + 2] = data_idx; + + /* Set length */ + *length = v->namelen + 3; + } + + switch (v->magic) { + case ISIS_ISADJIPADDR_TYPE: + if (data_len == 4) + return SNMP_INTEGER(ISIS_SNMP_INET_TYPE_V4); + + return SNMP_INTEGER(ISIS_SNMP_INET_TYPE_V6); + + case ISIS_ISADJIPADDR_ADDRESS: + *var_len = data_len; + return data; + + default: + break; + } + + return NULL; +} + +static uint8_t *isis_snmp_find_isadj_prot_supp(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* Index circuit-id 1-255 + adj-id 1-... */ + oid *oid_idx; + size_t oid_idx_len; + int res; + struct isis_adjacency *adj; + oid data_idx; + uint8_t *data; + size_t data_len; + + *write_method = NULL; + + if (*length <= v->namelen) { + oid_idx = NULL; + oid_idx_len = 0; + } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) { + oid_idx = NULL; + oid_idx_len = 0; + } else { + oid_idx = name + v->namelen; + oid_idx_len = *length - v->namelen; + } + if (exact) { + res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len, + ISIS_SNMP_ADJ_DATA_PROTO, &adj, + NULL, &data, &data_len); + + if (!res || oid_idx_len != 3) + return NULL; + + } else { + res = isis_snmp_adj_lookup_next(oid_idx, oid_idx_len, + ISIS_SNMP_ADJ_DATA_PROTO, &adj, + &data_idx, &data, &data_len); + if (!res) + return NULL; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + name[v->namelen] = adj->circuit->snmp_id; + name[v->namelen + 1] = adj->snmp_idx; + name[v->namelen + 2] = data_idx; + + /* Set length */ + *length = v->namelen + 3; + } + + switch (v->magic) { + case ISIS_ISADJPROTSUPP_PROTOCOL: + return SNMP_INTEGER(*data); + + default: + break; + } + + return NULL; +} + + +/* Register ISIS-MIB. */ +static int isis_snmp_init(struct thread_master *tm) +{ + struct isis_func_to_prefix *h2f = isis_func_to_prefix_arr; + struct variable *v; + + for (size_t off = 0; off < isis_var_count; off++) { + v = &isis_var_arr[off]; + + if (v->findVar != h2f->ihtp_func) { + /* Next table */ + h2f++; + assert(h2f < (isis_func_to_prefix_arr + + isis_func_to_prefix_count)); + assert(v->findVar == h2f->ihtp_func); + } + + v->namelen = h2f->ihtp_pref_len + 1; + memcpy(v->name, h2f->ihtp_pref_oid, + h2f->ihtp_pref_len * sizeof(oid)); + v->name[h2f->ihtp_pref_len] = v->magic; + } + + + smux_init(tm); + REGISTER_MIB("mibII/isis", isis_var_arr, variable, isis_oid); + return 0; +} + +/* + * ISIS notification functions: we have one function per notification + */ +static int isis_snmp_trap_throttle(oid trap_id) +{ + time_t time_now; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL || !isis->snmp_notifications || !smux_enabled()) + return 0; + + time_now = isis_snmp_time(); + + if ((isis_snmp_trap_timestamp[trap_id] + 5) > time_now) + /* Throttle trap rate at 1 in 5 secs */ + return 0; + + isis_snmp_trap_timestamp[trap_id] = time_now; + return 1; +} + +static int isis_snmp_db_overload_update(const struct isis_area *area) +{ + netsnmp_variable_list *notification_vars; + long val; + uint32_t off; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_DB_OVERLOAD)) + return 0; + + notification_vars = NULL; + + /* Put in trap value */ + snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, + ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + (uint8_t *)&isis_snmp_trap_val_db_overload, + sizeof(isis_snmp_trap_val_db_overload)); + + /* Prepare data */ + val = area->is_type; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, + ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + (uint8_t *)&val, sizeof(val)); + + /* Patch sys_level_state with proper index */ + off = ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state) - 1; + isis_snmp_trap_data_var_sys_level_state[off] = val; + + /* Prepare data */ + if (area->overload_bit) + val = ISIS_SNMP_LEVEL_STATE_OVERLOADED; + else + val = ISIS_SNMP_LEVEL_STATE_ON; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_sys_level_state, + ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state), INTEGER, + (uint8_t *)&val, sizeof(val)); + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + smux_events_update(); + return 0; +} + +static int isis_snmp_lsp_exceed_max_update(const struct isis_area *area, + const uint8_t *lsp_id) +{ + netsnmp_variable_list *notification_vars; + long val; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_LSP_EXCEED_MAX)) + return 0; + + notification_vars = NULL; + + /* Put in trap value */ + snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, + ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + (uint8_t *)&isis_snmp_trap_val_lsp_exceed_max, + sizeof(isis_snmp_trap_val_lsp_exceed_max)); + + /* Prepare data */ + val = area->is_type; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, + ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + (uint8_t *)&val, sizeof(val)); + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_pdu_lsp_id, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + ISIS_SYS_ID_LEN + 2); + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + smux_events_update(); + return 0; +} + + +/* + * A common function to handle popular combination of trap objects + * isisNotificationSysLevelIndex, + * optional-object-a + * isisNotificationCircIfIndex, + * optional-object-b + */ +static void isis_snmp_update_worker_a(const struct isis_circuit *circuit, + oid trap_id, const oid *oid_a, + size_t oid_a_len, uint8_t type_a, + const void *data_a, size_t data_a_len, + const oid *oid_b, size_t oid_b_len, + uint8_t type_b, const void *data_b, + size_t data_b_len) +{ + netsnmp_variable_list *notification_vars = NULL; + oid var_name[MAX_OID_LEN]; + size_t var_count; + long val; + + /* Sanity */ + if (trap_id != ISIS_TRAP_ID_LEN_MISMATCH + && trap_id != ISIS_TRAP_MAX_AREA_ADDR_MISMATCH + && trap_id != ISIS_TRAP_OWN_LSP_PURGE + && trap_id != ISIS_TRAP_SEQNO_SKIPPED + && trap_id != ISIS_TRAP_AUTHEN_TYPE_FAILURE + && trap_id != ISIS_TRAP_AUTHEN_FAILURE + && trap_id != ISIS_TRAP_REJ_ADJACENCY) + return; + + /* Put in trap value */ + memcpy(var_name, isis_snmp_notifications, + sizeof(isis_snmp_notifications)); + var_count = ARRAY_SIZE(isis_snmp_notifications); + var_name[var_count++] = trap_id; + + /* Put in trap value */ + snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, + ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + (uint8_t *)var_name, var_count * sizeof(oid)); + + val = circuit->is_type; + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, + ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + (uint8_t *)&val, sizeof(val)); + + if (oid_a_len != 0) { + if (oid_a == NULL || data_a == NULL || data_a_len == 0) + return; + + snmp_varlist_add_variable(¬ification_vars, oid_a, oid_a_len, + type_a, (uint8_t *)data_a, + data_a_len); + } + + if (circuit->interface == NULL) + val = 0; + else + val = circuit->interface->ifindex; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, + ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + (uint8_t *)&val, sizeof(val)); + + + if (oid_b_len != 0) { + if (oid_b == NULL || data_b == NULL || data_b_len == 0) + return; + + snmp_varlist_add_variable(¬ification_vars, oid_b, oid_b_len, + type_b, (uint8_t *)data_b, + data_b_len); + } + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + smux_events_update(); +} + +/* + * A common function to handle popular combination of trap objects + * isisNotificationSysLevelIndex, + * isisNotificationCircIfIndex, + * optional-var-a + * optional-var-b + * + * Note: the only difference with worker_a is order of circ-if-index vs + * optional-var-a + */ +static void isis_snmp_update_worker_b(const struct isis_circuit *circuit, + oid trap_id, const oid *oid_a, + size_t oid_a_len, uint8_t type_a, + const void *data_a, size_t data_a_len, + const oid *oid_b, size_t oid_b_len, + uint8_t type_b, const void *data_b, + size_t data_b_len) +{ + netsnmp_variable_list *notification_vars = NULL; + oid var_name[MAX_OID_LEN]; + size_t var_count; + long val; + + /* Sanity */ + if (trap_id != ISIS_TRAP_VERSION_SKEW + && trap_id != ISIS_TRAP_LSP_TOO_LARGE + && trap_id != ISIS_TRAP_ADJ_STATE_CHANGE) + return; + + /* Put in trap value */ + memcpy(var_name, isis_snmp_notifications, + sizeof(isis_snmp_notifications)); + var_count = ARRAY_SIZE(isis_snmp_notifications); + var_name[var_count++] = trap_id; + + /* Put in trap value */ + snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, + ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + (uint8_t *)var_name, var_count * sizeof(oid)); + + val = circuit->is_type; + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, + ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + (uint8_t *)&val, sizeof(val)); + + if (circuit->interface == NULL) + val = 0; + else + val = circuit->interface->ifindex; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, + ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + (uint8_t *)&val, sizeof(val)); + + + if (oid_a_len != 0) { + if (oid_a == NULL || data_a == NULL || data_a_len == 0) + return; + + snmp_varlist_add_variable(¬ification_vars, oid_a, oid_a_len, + type_a, (uint8_t *)data_a, + data_a_len); + } + + if (oid_b_len != 0) { + if (oid_b == NULL || data_b == NULL || data_b_len == 0) + return; + + snmp_varlist_add_variable(¬ification_vars, oid_b, oid_b_len, + type_b, (uint8_t *)data_b, + data_b_len); + } + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + smux_events_update(); +} + + +static int isis_snmp_id_len_mismatch_update(const struct isis_circuit *circuit, + uint8_t rcv_id, const char *raw_pdu, + size_t raw_pdu_len) +{ + long val; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_ID_LEN_MISMATCH)) + return 0; + + val = rcv_id; + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_ID_LEN_MISMATCH, + isis_snmp_trap_data_var_pdu_field_len, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32, + &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + return 0; +} + +static int +isis_snmp_max_area_addr_mismatch_update(const struct isis_circuit *circuit, + uint8_t max_addrs, const char *raw_pdu, + size_t raw_pdu_len) +{ + long val; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_MAX_AREA_ADDR_MISMATCH)) + return 0; + + val = max_addrs; + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_MAX_AREA_ADDR_MISMATCH, + isis_snmp_trap_data_var_pdu_max_area_addr, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_max_area_addr), + UNSIGNED32, &val, sizeof(val), + isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + return 0; +} + +static int isis_snmp_own_lsp_purge_update(const struct isis_circuit *circuit, + const uint8_t *lsp_id) +{ + if (!isis_snmp_trap_throttle(ISIS_TRAP_OWN_LSP_PURGE)) + return 0; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_OWN_LSP_PURGE, NULL, 0, STRING, NULL, 0, + isis_snmp_trap_data_var_pdu_lsp_id, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + ISIS_SYS_ID_LEN + 2); + return 0; +} + +static int isis_snmp_seqno_skipped_update(const struct isis_circuit *circuit, + const uint8_t *lsp_id) +{ + if (!isis_snmp_trap_throttle(ISIS_TRAP_SEQNO_SKIPPED)) + return 0; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_SEQNO_SKIPPED, NULL, 0, STRING, NULL, 0, + isis_snmp_trap_data_var_pdu_lsp_id, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + ISIS_SYS_ID_LEN + 2); + return 0; +} + +static int +isis_snmp_authentication_type_failure_update(const struct isis_circuit *circuit, + const char *raw_pdu, + size_t raw_pdu_len) +{ + if (!isis_snmp_trap_throttle(ISIS_TRAP_AUTHEN_TYPE_FAILURE)) + return 0; + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_AUTHEN_TYPE_FAILURE, NULL, 0, STRING, NULL, + 0, isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + return 0; +} + +static int +isis_snmp_authentication_failure_update(const struct isis_circuit *circuit, + char const *raw_pdu, size_t raw_pdu_len) +{ + if (!isis_snmp_trap_throttle(ISIS_TRAP_AUTHEN_FAILURE)) + return 0; + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_AUTHEN_FAILURE, NULL, 0, STRING, NULL, 0, + isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + return 0; +} + +static int isis_snmp_version_skew_update(const struct isis_circuit *circuit, + uint8_t version, const char *raw_pdu, + size_t raw_pdu_len) +{ + long val; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_VERSION_SKEW)) + return 0; + + val = version; + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + isis_snmp_update_worker_b( + circuit, ISIS_TRAP_VERSION_SKEW, + isis_snmp_trap_data_var_pdu_proto_ver, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32, + &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + return 0; +} + +static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit, + const char *raw_pdu, + size_t raw_pdu_len) +{ + /* + * This is a special case because + * it does not include isisNotificationSysLevelIndex + */ + netsnmp_variable_list *notification_vars; + long val; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_AREA_MISMATCH)) + return 0; + + notification_vars = NULL; + + /* Put in trap value */ + snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, + ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + (uint8_t *)&isis_snmp_trap_val_area_mismatch, + sizeof(isis_snmp_trap_val_area_mismatch)); + + + if (circuit->interface == NULL) + val = 0; + else + val = circuit->interface->ifindex; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, + ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + (uint8_t *)&val, sizeof(val)); + + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + smux_events_update(); + + return 0; +} + +static int isis_snmp_reject_adjacency_update(const struct isis_circuit *circuit, + const char *raw_pdu, + size_t raw_pdu_len) +{ + if (!isis_snmp_trap_throttle(ISIS_TRAP_REJ_ADJACENCY)) + return 0; + + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + isis_snmp_update_worker_a( + circuit, ISIS_TRAP_REJ_ADJACENCY, NULL, 0, STRING, NULL, 0, + isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + return 0; +} + +static int isis_snmp_lsp_too_large_update(const struct isis_circuit *circuit, + uint32_t pdu_size, + const uint8_t *lsp_id) +{ + if (!isis_snmp_trap_throttle(ISIS_TRAP_LSP_TOO_LARGE)) + return 0; + + isis_snmp_update_worker_b( + circuit, ISIS_TRAP_LSP_TOO_LARGE, + isis_snmp_trap_data_var_pdu_lsp_size, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32, + &pdu_size, sizeof(pdu_size), isis_snmp_trap_data_var_pdu_lsp_id, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + ISIS_SYS_ID_LEN + 2); + return 0; +} + + +static int isis_snmp_adj_state_change_update(const struct isis_adjacency *adj) +{ + uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; + long val; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL || !isis->snmp_notifications || !smux_enabled()) + return 0; + + /* Prepare data */ + memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN); + lsp_id[ISIS_SYS_ID_LEN] = 0; + lsp_id[ISIS_SYS_ID_LEN + 1] = 0; + + val = ISIS_SNMP_ADJ_STATE_DOWN; + + switch (adj->adj_state) { + case ISIS_ADJ_UNKNOWN: + val = ISIS_SNMP_ADJ_STATE_DOWN; + break; + + case ISIS_ADJ_INITIALIZING: + val = ISIS_SNMP_ADJ_STATE_INITIALIZING; + break; + + case ISIS_ADJ_UP: + val = ISIS_SNMP_ADJ_STATE_UP; + break; + + case ISIS_ADJ_DOWN: + val = ISIS_SNMP_ADJ_STATE_FAILED; + break; + } + + isis_snmp_update_worker_b( + adj->circuit, ISIS_TRAP_ADJ_STATE_CHANGE, + isis_snmp_trap_data_var_pdu_lsp_id, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + ISIS_SYS_ID_LEN + 2, isis_snmp_trap_data_var_adj_state, + ARRAY_SIZE(isis_snmp_trap_data_var_adj_state), INTEGER, &val, + sizeof(val)); + return 0; +} + +static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, + const uint8_t *lsp_id, + char const *raw_pdu, size_t raw_pdu_len) +{ + /* + * This is a special case because + * it have more variables + */ + netsnmp_variable_list *notification_vars; + long val; + + if (!isis_snmp_trap_throttle(ISIS_TRAP_LSP_ERROR)) + return 0; + + notification_vars = NULL; + + /* Put in trap value */ + snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, + ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + (uint8_t *)&isis_snmp_trap_val_lsp_error, + sizeof(isis_snmp_trap_val_lsp_error)); + + /* Prepare data */ + val = circuit->is_type; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, + ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + (uint8_t *)&val, sizeof(val)); + + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_pdu_lsp_id, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + ISIS_SYS_ID_LEN + 2); + + /* Prepare data */ + if (circuit->interface == NULL) + val = 0; + else + val = circuit->interface->ifindex; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, + ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + (uint8_t *)&val, sizeof(val)); + + /* Prepare data */ + if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN) + raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_pdu_fragment, + ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + raw_pdu, raw_pdu_len); + + /* Prepare data */ + val = 0; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_error_offset, + ARRAY_SIZE(isis_snmp_trap_data_var_error_offset), UNSIGNED32, + (uint8_t *)&val, sizeof(val)); + + /* Prepare data */ + val = 0; + + snmp_varlist_add_variable( + ¬ification_vars, isis_snmp_trap_data_var_error_tlv_type, + ARRAY_SIZE(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32, + (uint8_t *)&val, sizeof(val)); + + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); + smux_events_update(); + return 0; +} + + +static int isis_snmp_module_init(void) +{ + hook_register(isis_hook_db_overload, isis_snmp_db_overload_update); + hook_register(isis_hook_lsp_exceed_max, + isis_snmp_lsp_exceed_max_update); + hook_register(isis_hook_id_len_mismatch, + isis_snmp_id_len_mismatch_update); + hook_register(isis_hook_max_area_addr_mismatch, + isis_snmp_max_area_addr_mismatch_update); + hook_register(isis_hook_own_lsp_purge, isis_snmp_own_lsp_purge_update); + hook_register(isis_hook_seqno_skipped, isis_snmp_seqno_skipped_update); + hook_register(isis_hook_authentication_type_failure, + isis_snmp_authentication_type_failure_update); + hook_register(isis_hook_authentication_failure, + isis_snmp_authentication_failure_update); + hook_register(isis_hook_version_skew, isis_snmp_version_skew_update); + hook_register(isis_hook_area_mismatch, isis_snmp_area_mismatch_update); + hook_register(isis_hook_reject_adjacency, + isis_snmp_reject_adjacency_update); + hook_register(isis_hook_lsp_too_large, isis_snmp_lsp_too_large_update); + hook_register(isis_hook_adj_state_change, + isis_snmp_adj_state_change_update); + hook_register(isis_hook_lsp_error, isis_snmp_lsp_error_update); + + hook_register(frr_late_init, isis_snmp_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "isis_snmp", .version = FRR_VERSION, + .description = "isis AgentX SNMP module", + .init = isis_snmp_module_init, ) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 22dfee994..7bcc6fea9 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1821,6 +1821,7 @@ static int isis_run_spf_cb(struct thread *thread) struct isis_spf_run *run = THREAD_ARG(thread); struct isis_area *area = run->area; int level = run->level; + int have_run = 0; XFREE(MTYPE_ISIS_SPF_RUN, run); area->spf_timer[level - 1] = NULL; @@ -1839,15 +1840,24 @@ static int isis_run_spf_cb(struct thread *thread) zlog_debug("ISIS-SPF (%s) L%d SPF needed, periodic SPF", area->area_tag, level); - if (area->ip_circuits) + if (area->ip_circuits) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_IPV4][level - 1]); - if (area->ipv6_circuits) + have_run = 1; + } + if (area->ipv6_circuits) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_IPV6][level - 1]); - if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) + have_run = 1; + } + if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_DSTSRC][level - 1]); + have_run = 1; + } + + if (have_run) + area->spf_run_count[level]++; isis_area_verify_routes(area); diff --git a/isisd/isisd.c b/isisd/isisd.c index a802bac13..487a902c0 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -89,6 +89,10 @@ static struct isis_master isis_master; /* ISIS process wide configuration pointer to export. */ struct isis_master *im; +#ifndef FABRICD +DEFINE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area)); +#endif /* ifndef FABRICD */ + /* * Prototypes. */ @@ -214,6 +218,7 @@ struct isis *isis_new(const char *vrf_name) isis->area_list = list_new(); isis->init_circ_list = list_new(); isis->uptime = time(NULL); + isis->snmp_notifications = 1; dyn_cache_init(isis); return isis; @@ -2563,6 +2568,14 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) if (new_overload_bit != area->overload_bit) { area->overload_bit = new_overload_bit; + + if (new_overload_bit) + area->overload_counter++; + +#ifndef FABRICD + hook_call(isis_hook_db_overload, area); +#endif /* ifndef FABRICD */ + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1); } #ifndef FABRICD diff --git a/isisd/isisd.h b/isisd/isisd.h index 22d9c6236..1b0ec2b4f 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -63,6 +63,8 @@ extern void isis_cli_init(void); all_vrf = strmatch(vrf_name, "all"); \ } +#define SNMP_CIRCUITS_MAX (512) + extern struct zebra_privs_t isisd_privs; /* uncomment if you are a developer in bug hunt */ @@ -93,6 +95,9 @@ struct isis { time_t uptime; /* when did we start */ struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ + struct isis_circuit *snmp_circuits[SNMP_CIRCUITS_MAX]; + uint32_t snmp_circuit_id_last; + int snmp_notifications; struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; struct ldp_sync_info_cmd ldp_sync_cmd; /* MPLS LDP-IGP Sync */ @@ -168,6 +173,7 @@ struct isis_area { char is_type; /* level-1 level-1-2 or level-2-only */ /* are we overloaded? */ char overload_bit; + uint32_t overload_counter; /* L1/L2 router identifier for inter-area traffic */ char attached_bit_send; char attached_bit_rcv_ignore; @@ -180,6 +186,9 @@ struct isis_area { int lsp_frag_threshold; uint64_t lsp_gen_count[ISIS_LEVELS]; uint64_t lsp_purge_count[ISIS_LEVELS]; + uint32_t lsp_exceeded_max_counter; + uint32_t lsp_seqno_skipped_counter; + uint64_t spf_run_count[ISIS_LEVELS]; int ip_circuits; /* logging adjacency changes? */ uint8_t log_adj_changes; @@ -220,10 +229,19 @@ struct isis_area { pdu_counter_t pdu_rx_counters; uint64_t lsp_rxmt_count; + /* Area counters */ + uint64_t rej_adjacencies[2]; + uint64_t auth_type_failures[2]; + uint64_t auth_failures[2]; + uint64_t id_len_mismatches[2]; + uint64_t lsp_error_counter[2]; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(isis_area) +DECLARE_HOOK(isis_area_overload_bit_update, (struct isis_area * area), (area)) + void isis_terminate(void); void isis_finish(struct isis *isis); void isis_master_init(struct thread_master *master); diff --git a/isisd/subdir.am b/isisd/subdir.am index 4be4efc11..98674a688 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -17,6 +17,9 @@ vtysh_scan += \ isisd/isisd.c \ # end vtysh_daemons += isisd +if SNMP +module_LTLIBRARIES += isisd/isisd_snmp.la +endif man8 += $(MANBUILD)/frr-isisd.8 endif @@ -137,7 +140,12 @@ isisd_isisd_SOURCES = $(ISIS_SOURCES) nodist_isisd_isisd_SOURCES = \ yang/frr-isisd.yang.c \ # end - + +isisd_isisd_snmp_la_SOURCES = isisd/isis_snmp.c +isisd_isisd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99 +isisd_isisd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +isisd_isisd_snmp_la_LIBADD = lib/libfrrsnmp.la + # Building fabricd FABRICD_CPPFLAGS = -DFABRICD=1 $(AM_CPPFLAGS) |