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