summaryrefslogtreecommitdiffstats
path: root/isisd/isis_snmp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--isisd/isis_snmp.c3468
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(&notification_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(
+ &notification_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(
+ &notification_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(&notification_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(
+ &notification_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(
+ &notification_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(&notification_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(
+ &notification_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(&notification_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(
+ &notification_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(&notification_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(&notification_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(
+ &notification_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(
+ &notification_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(&notification_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(&notification_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(&notification_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(
+ &notification_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(
+ &notification_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(&notification_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(
+ &notification_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(
+ &notification_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(
+ &notification_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(
+ &notification_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(
+ &notification_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(
+ &notification_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,
+);