]> git.puffer.fish Git - matthieu/frr.git/commitdiff
isisd: support for snmp
authorlynne <lynne@voltanet.io>
Tue, 15 Sep 2020 20:46:15 +0000 (16:46 -0400)
committerlynne <lynne@voltanet.io>
Tue, 2 Mar 2021 15:06:31 +0000 (10:06 -0500)
Add support for read only mib objects from RFC4444.

Signed-off-by: Lynne Morrison <lynne@voltanet.io>
Signed-off-by: Karen Schoener <karen@voltanet.io>
19 files changed:
isisd/isis_adjacency.c
isisd/isis_adjacency.h
isisd/isis_circuit.c
isisd/isis_circuit.h
isisd/isis_dr.c
isisd/isis_dynhn.c
isisd/isis_dynhn.h
isisd/isis_lsp.c
isisd/isis_nb.h
isisd/isis_nb_notifications.c
isisd/isis_pdu.c
isisd/isis_snmp.c [new file with mode: 0644]
isisd/isis_spf.c
isisd/isisd.c
isisd/isisd.h
isisd/subdir.am
lib/agentx.c
lib/smux.h
vtysh/extract.pl.in

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