diff options
Diffstat (limited to '')
-rw-r--r-- | isisd/isis_snmp.c | 3468 |
1 files changed, 3468 insertions, 0 deletions
diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c new file mode 100644 index 0000000..a184b5b --- /dev/null +++ b/isisd/isis_snmp.c @@ -0,0 +1,3468 @@ +/* + * 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 "lib/version.h" +#include "lib/zclient.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 + 1 + +#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 + */ + +/* 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) + +static inline uint32_t isis_snmp_adj_state(enum isis_adj_state state) +{ + switch (state) { + case ISIS_ADJ_UNKNOWN: + return ISIS_SNMP_ADJ_STATE_DOWN; + case ISIS_ADJ_INITIALIZING: + return ISIS_SNMP_ADJ_STATE_INITIALIZING; + case ISIS_ADJ_UP: + return ISIS_SNMP_ADJ_STATE_UP; + case ISIS_ADJ_DOWN: + return ISIS_SNMP_ADJ_STATE_FAILED; + } + + return 0; /* not reached */ +} + +#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) + +static inline uint32_t isis_snmp_adj_neightype(enum isis_system_type type) +{ + switch (type) { + case ISIS_SYSTYPE_UNKNOWN: + case ISIS_SYSTYPE_ES: + return ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN; + case ISIS_SYSTYPE_IS: + return ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1_L2; + case ISIS_SYSTYPE_L1_IS: + return ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1; + case ISIS_SYSTYPE_L2_IS: + return ISIS_SNMP_ADJ_NEIGHTYPE_IS_L2; + } + + return 0; /* not reached */ +} + +#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 */ + +#define SNMP_CIRCUITS_MAX (512) + +static struct isis_circuit *snmp_circuits[SNMP_CIRCUITS_MAX]; +static uint32_t snmp_circuit_id_last; + +static int isis_circuit_snmp_id_gen(struct isis_circuit *circuit) +{ + uint32_t id; + uint32_t i; + + id = 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 (snmp_circuits[id] == NULL) + break; + + id++; + } + + if (i == SNMP_CIRCUITS_MAX) { + zlog_warn("Could not allocate a smmp-circuit-id"); + return 0; + } + + snmp_circuits[id] = circuit; + snmp_circuit_id_last = id; + circuit->snmp_id = id; + + return 0; +} + +static int isis_circuit_snmp_id_free(struct isis_circuit *circuit) +{ + snmp_circuits[circuit->snmp_id] = NULL; + circuit->snmp_id = 0; + return 0; +} + +/* + * 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; + + start = 1; + + if (circuit != NULL) + start = circuit->snmp_id + 1; + + for (off = start; off < SNMP_CIRCUITS_MAX; off++) { + circuit = 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; + + if (oid_idx == NULL || oid_idx_len < 1 + || oid_idx[0] > SNMP_CIRCUITS_MAX) + return 0; + + circuit = 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; + + 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 = 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 = NULL; + int level; + + 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 = 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; + } + + assert(oid_idx != NULL); + + /* 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->ll_ipv6_count)) + return 0; + + if (data_off >= adj->ipv4_address_count) { + data = (uint8_t *)&adj->ll_ipv6_addrs + [data_off - adj->ipv4_address_count]; + data_len = sizeof(adj->ll_ipv6_addrs[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; + 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_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(isis, 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(isis, 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); + + 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; + uint32_t up_ticks; + uint32_t delta_ticks; + time_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 == NULL) + 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 = (uint32_t)netsnmp_get_agent_uptime(); + now_time = time(NULL); + + 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(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; + time_t val; + struct isis_adjacency *adj; + uint32_t up_ticks; + uint32_t delta_ticks; + time_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: + return SNMP_INTEGER(isis_snmp_adj_state(adj->adj_state)); + + 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: + return SNMP_INTEGER(isis_snmp_adj_neightype(adj->sys_type)); + + 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 = time(NULL); + 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 = (uint32_t)netsnmp_get_agent_uptime(); + + now_time = time(NULL); + + 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(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 = time(NULL); + + 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(adj->adj_state); + + 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(isis_circuit_new_hook, isis_circuit_snmp_id_gen); + hook_register(isis_circuit_del_hook, isis_circuit_snmp_id_free); + + 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, +); |