• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * SNMPv1 and SNMPv2 traps implementation.
4  */
5 
6 /*
7  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30  * OF SUCH DAMAGE.
31  *
32  * This file is part of the lwIP TCP/IP stack.
33  *
34  * Author: Martin Hentschel
35  *         Christiaan Simons <christiaan.simons@axon.tv>
36  *
37  */
38 
39 #include "lwip/apps/snmp_opts.h"
40 
41 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
42 
43 #include <string.h>
44 
45 #include "lwip/snmp.h"
46 #include "lwip/sys.h"
47 #include "lwip/apps/snmp.h"
48 #include "lwip/apps/snmp_core.h"
49 #include "lwip/prot/iana.h"
50 #include "snmp_msg.h"
51 #include "snmp_asn1.h"
52 #include "snmp_core_priv.h"
53 
54 #define SNMP_IS_INFORM                            1
55 #define SNMP_IS_TRAP                              0
56 
57 struct snmp_msg_trap
58 {
59   /* source enterprise ID (sysObjectID) */
60   const struct snmp_obj_id *enterprise;
61   /* source IP address, raw network order format */
62   ip_addr_t sip;
63   /* generic trap code */
64   u32_t gen_trap;
65   /* specific trap code */
66   u32_t spc_trap;
67   /* timestamp */
68   u32_t ts;
69   /* snmp_version */
70   u32_t snmp_version;
71 
72   /* output trap lengths used in ASN encoding */
73   /* encoding pdu length */
74   u16_t pdulen;
75   /* encoding community length */
76   u16_t comlen;
77   /* encoding sequence length */
78   u16_t seqlen;
79   /* encoding varbinds sequence length */
80   u16_t vbseqlen;
81 
82   /* error status */
83   s32_t error_status;
84   /* error index */
85   s32_t error_index;
86   /* trap or inform? */
87   u8_t trap_or_inform;
88 };
89 
90 static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
91 static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
92 static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
93 static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
94 static u16_t snmp_trap_header_sum_v1_specific(struct snmp_msg_trap *trap);
95 static u16_t snmp_trap_header_sum_v2c_specific(struct snmp_msg_trap *trap);
96 static err_t snmp_trap_header_enc_v1_specific(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
97 static err_t snmp_trap_header_enc_v2c_specific(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
98 static err_t snmp_prepare_trap_oid(struct snmp_obj_id *dest_snmp_trap_oid, const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap);
99 static void snmp_prepare_necessary_msg_fields(struct snmp_msg_trap *trap_msg, const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds);
100 static err_t snmp_send_msg(struct snmp_msg_trap *trap_msg, struct snmp_varbind *varbinds, u16_t tot_len, ip_addr_t *dip);
101 
102 #define BUILD_EXEC(code) \
103   if ((code) != ERR_OK) { \
104     LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!\n")); \
105     return ERR_ARG; \
106   }
107 
108 /** Agent community string for sending traps */
109 extern const char *snmp_community_trap;
110 
111 void *snmp_traps_handle;
112 
113 /**
114  * @ingroup snmp_traps
115  * @struct snmp_trap_dst
116  */
117 struct snmp_trap_dst
118 {
119   /* destination IP address in network order */
120   ip_addr_t dip;
121   /* set to 0 when disabled, >0 when enabled */
122   u8_t enable;
123 };
124 static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
125 
126 static u8_t snmp_auth_traps_enabled = 0;
127 
128 /* This is used in functions like snmp_coldstart_trap where user didn't specify which version of trap to use */
129 static u8_t snmp_default_trap_version = SNMP_VERSION_1;
130 
131 /* This is used in trap messages v2c */
132 static s32_t req_id = 1;
133 
134 /**
135  * @ingroup snmp_traps
136  * Sets enable switch for this trap destination.
137  * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
138  * @param enable switch if 0 destination is disabled >0 enabled.
139  *
140  */
141 void
snmp_trap_dst_enable(u8_t dst_idx,u8_t enable)142 snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
143 {
144   LWIP_ASSERT_SNMP_LOCKED();
145   if (dst_idx < SNMP_TRAP_DESTINATIONS) {
146     trap_dst[dst_idx].enable = enable;
147   }
148 }
149 
150 /**
151  * @ingroup snmp_traps
152  * Sets IPv4 address for this trap destination.
153  * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
154  * @param dst IPv4 address in host order.
155  *
156  */
157 void
snmp_trap_dst_ip_set(u8_t dst_idx,const ip_addr_t * dst)158 snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
159 {
160   LWIP_ASSERT_SNMP_LOCKED();
161   if (dst_idx < SNMP_TRAP_DESTINATIONS) {
162     ip_addr_set(&trap_dst[dst_idx].dip, dst);
163   }
164 }
165 
166 /**
167  * @ingroup snmp_traps
168  * Enable/disable authentication traps
169  *
170  * @param enable enable SNMP traps
171  *
172  */
173 void
snmp_set_auth_traps_enabled(u8_t enable)174 snmp_set_auth_traps_enabled(u8_t enable)
175 {
176   snmp_auth_traps_enabled = enable;
177 }
178 
179 /**
180  * @ingroup snmp_traps
181  * Get authentication traps enabled state
182  *
183  * @return TRUE if traps are enabled, FALSE if they aren't
184  */
185 u8_t
snmp_get_auth_traps_enabled(void)186 snmp_get_auth_traps_enabled(void)
187 {
188   return snmp_auth_traps_enabled;
189 }
190 
191 /**
192  * @ingroup snmp_traps
193  * Choose default SNMP version for sending traps (if not specified, default version is SNMP_VERSION_1)
194  * SNMP_VERSION_1  0
195  * SNMP_VERSION_2c 1
196  * SNMP_VERSION_3  3
197  *
198  * @param snmp_version version that will be used for sending traps
199  *
200  */
201 void
snmp_set_default_trap_version(u8_t snmp_version)202 snmp_set_default_trap_version(u8_t snmp_version)
203 {
204   snmp_default_trap_version = snmp_version;
205 }
206 
207 /**
208  * @ingroup snmp_traps
209  * Get default SNMP version for sending traps
210  *
211  * @return selected default version:
212  * 0 - SNMP_VERSION_1
213  * 1 - SNMP_VERSION_2c
214  * 3 - SNMP_VERSION_3
215  */
216 u8_t
snmp_get_default_trap_version(void)217 snmp_get_default_trap_version(void)
218 {
219   return snmp_default_trap_version;
220 }
221 
222 /**
223  * @ingroup snmp_traps
224  * Prepares snmpTrapOID for SNMP v2c
225  * @param dest_snmp_trap_oid pointer to destination snmpTrapOID
226  * @param eoid enterprise oid (can be NULL)
227  * @param generic_trap SNMP v1 generic trap
228  * @param specific_trap SNMP v1 specific trap
229  * @return ERR_OK if completed successfully;
230  *         ERR_MEM if there wasn't enough memory allocated for destination;
231  *         ERR_VAL if value for generic trap was incorrect;
232  */
233 static err_t
snmp_prepare_trap_oid(struct snmp_obj_id * dest_snmp_trap_oid,const struct snmp_obj_id * eoid,s32_t generic_trap,s32_t specific_trap)234 snmp_prepare_trap_oid(struct snmp_obj_id *dest_snmp_trap_oid, const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap)
235 {
236   err_t err = ERR_OK;
237   const u32_t snmpTrapOID[] = {1, 3, 6, 1, 6, 3, 1, 1, 5};     /* please see rfc3584 */
238 
239   if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
240     if (eoid == NULL) {
241       MEMCPY(dest_snmp_trap_oid, snmp_get_device_enterprise_oid(), sizeof(*dest_snmp_trap_oid));
242     } else {
243       MEMCPY(dest_snmp_trap_oid, eoid, sizeof(*dest_snmp_trap_oid));
244     }
245     if (dest_snmp_trap_oid->len + 2 < SNMP_MAX_OBJ_ID_LEN) {
246       dest_snmp_trap_oid->id[dest_snmp_trap_oid->len++] = 0;
247       dest_snmp_trap_oid->id[dest_snmp_trap_oid->len++] = specific_trap;
248     } else {
249       err = ERR_MEM;
250     }
251   } else if ((generic_trap >= SNMP_GENTRAP_COLDSTART) && (generic_trap < SNMP_GENTRAP_ENTERPRISE_SPECIFIC)) {
252     if (sizeof(dest_snmp_trap_oid->id) >= sizeof(snmpTrapOID)) {
253       MEMCPY(&dest_snmp_trap_oid->id, snmpTrapOID , sizeof(snmpTrapOID));
254       dest_snmp_trap_oid->len = LWIP_ARRAYSIZE(snmpTrapOID);
255       dest_snmp_trap_oid->id[dest_snmp_trap_oid->len++] = specific_trap + 1;
256     } else {
257       err = ERR_MEM;
258     }
259   } else {
260     err = ERR_VAL;
261   }
262   return err;
263 }
264 
265 /**
266  * @ingroup snmp_traps
267  * Prepare the rest of the necessary fields for trap/notification/inform message.
268  * @param trap_msg message that should be set
269  * @param eoid enterprise oid (can be NULL)
270  * @param generic_trap SNMP v1 generic trap
271  * @param specific_trap SNMP v1 specific trap
272  * @param varbinds list of varbinds
273  */
274 static void
snmp_prepare_necessary_msg_fields(struct snmp_msg_trap * trap_msg,const struct snmp_obj_id * eoid,s32_t generic_trap,s32_t specific_trap,struct snmp_varbind * varbinds)275 snmp_prepare_necessary_msg_fields(struct snmp_msg_trap *trap_msg, const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
276 {
277   if (trap_msg->snmp_version == SNMP_VERSION_1) {
278     trap_msg->enterprise = (eoid == NULL) ? snmp_get_device_enterprise_oid() : eoid;
279     trap_msg->gen_trap = generic_trap;
280     trap_msg->spc_trap = (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) ? specific_trap : 0;
281     MIB2_COPY_SYSUPTIME_TO(&trap_msg->ts);
282   } else if (trap_msg->snmp_version == SNMP_VERSION_2c) {
283     /* Copy sysUpTime into the first varbind */
284     MIB2_COPY_SYSUPTIME_TO((u32_t *)varbinds[0].value);
285   }
286 }
287 
288 /**
289  * @ingroup snmp_traps
290  * Copy trap message structure to pbuf and sends it
291  * @param trap_msg contains the data that should be sent
292  * @param varbinds list of varbinds
293  * @param tot_len total length of encoded data
294  * @param dip destination IP address
295  * @return ERR_OK if sending was successful
296  */
297 static err_t
snmp_send_msg(struct snmp_msg_trap * trap_msg,struct snmp_varbind * varbinds,u16_t tot_len,ip_addr_t * dip)298 snmp_send_msg(struct snmp_msg_trap *trap_msg, struct snmp_varbind *varbinds, u16_t tot_len, ip_addr_t *dip)
299 {
300   err_t err = ERR_OK;
301   struct pbuf *p = NULL;
302   /* allocate pbuf(s) */
303   p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
304   if (p != NULL) {
305     struct snmp_pbuf_stream pbuf_stream;
306     snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
307 
308     /* pass 1, encode packet ino the pbuf(s) */
309     BUILD_EXEC( snmp_trap_header_enc(trap_msg, &pbuf_stream) );
310     BUILD_EXEC( snmp_trap_varbind_enc(trap_msg, &pbuf_stream, varbinds) );
311 
312     snmp_stats.outtraps++;
313     snmp_stats.outpkts++;
314 
315     /** send to the TRAP destination */
316     err = snmp_sendto(snmp_traps_handle, p, dip, LWIP_IANA_PORT_SNMP_TRAP);
317     pbuf_free(p);
318   } else {
319     err = ERR_MEM;
320   }
321   return err;
322 }
323 
324 /**
325  * @ingroup snmp_traps
326  * Prepare and sends a generic or enterprise specific trap message, notification or inform.
327  *
328  * @param trap_msg defines msg type
329  * @param eoid points to enterprise object identifier
330  * @param generic_trap is the trap code
331  * @param specific_trap used for enterprise traps when generic_trap == 6
332  * @param varbinds linked list of varbinds to be sent
333  * @return ERR_OK when success, ERR_MEM if we're out of memory
334  *
335  * @note the use of the enterprise identifier field
336  * is per RFC1215.
337  * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
338  * and .iso.org.dod.internet.private.enterprises.yourenterprise
339  * (sysObjectID) for specific traps.
340  */
341 static err_t
snmp_send_trap_or_notification_or_inform_generic(struct snmp_msg_trap * trap_msg,const struct snmp_obj_id * eoid,s32_t generic_trap,s32_t specific_trap,struct snmp_varbind * varbinds)342 snmp_send_trap_or_notification_or_inform_generic(struct snmp_msg_trap *trap_msg, const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
343 {
344   struct snmp_trap_dst *td = NULL;
345   u16_t i = 0;
346   u16_t tot_len = 0;
347   err_t err = ERR_OK;
348   u32_t timestamp = 0;
349   struct snmp_obj_id snmp_trap_oid = { 0 };  /* used for converting SNMPv1 generic/specific trap parameter to SNMPv2 snmpTrapOID */
350   struct snmp_varbind snmp_v2_special_varbinds[] = {
351                                                      /* First varbind is used to store sysUpTime */
352                                                      {
353                                                        NULL,                            /* *next */
354                                                        {                                /* oid */
355                                                          8,                             /* oid len */
356                                                          {1, 3, 6, 1, 2, 1, 1, 3}       /* oid for sysUpTime */
357                                                        },
358                                                        SNMP_ASN1_TYPE_TIMETICKS,        /* type */
359                                                        sizeof(u32_t),                   /* value_len */
360                                                        NULL                             /* value */
361                                                      },
362                                                      /* Second varbind is used to store snmpTrapOID */
363                                                      {
364                                                        NULL,                            /* *next */
365                                                        {                                /* oid */
366                                                          10,                            /* oid len */
367                                                          {1, 3, 6, 1, 6, 3, 1, 1, 4, 1} /* oid for snmpTrapOID */
368                                                        },
369                                                        SNMP_ASN1_TYPE_OBJECT_ID,        /* type */
370                                                        0,                               /* value_len */
371                                                        NULL                             /* value */
372                                                      }
373    };
374 
375   LWIP_ASSERT_SNMP_LOCKED();
376 
377   snmp_v2_special_varbinds[0].next = &snmp_v2_special_varbinds[1];
378 
379   snmp_v2_special_varbinds[0].value = &timestamp;
380 
381   snmp_v2_special_varbinds[1].next = varbinds;
382 
383   /* see rfc3584 */
384   if (trap_msg->snmp_version == SNMP_VERSION_2c) {
385     err = snmp_prepare_trap_oid(&snmp_trap_oid, eoid, generic_trap, specific_trap);
386     if (err == ERR_OK) {
387       snmp_v2_special_varbinds[1].value_len = snmp_trap_oid.len * sizeof(snmp_trap_oid.id[0]);
388       snmp_v2_special_varbinds[1].value = snmp_trap_oid.id;
389       varbinds = snmp_v2_special_varbinds;  /* After inserting two varbinds at the beginning of the list, make sure that pointer is pointing to the first element  */
390     }
391   }
392 
393   for (i = 0, td = &trap_dst[0]; (i < SNMP_TRAP_DESTINATIONS) && (err == ERR_OK); i++, td++) {
394     if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
395       /* lookup current source address for this dst */
396       if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg->sip)) {
397         snmp_prepare_necessary_msg_fields(trap_msg, eoid, generic_trap, specific_trap, varbinds);
398 
399         /* pass 0, calculate length fields */
400         tot_len = snmp_trap_varbind_sum(trap_msg, varbinds);
401         tot_len = snmp_trap_header_sum(trap_msg, tot_len);
402 
403         /* allocate pbuf, fill it and send it */
404         err = snmp_send_msg(trap_msg, varbinds, tot_len, &td->dip);
405       } else {
406         /* routing error */
407         err = ERR_RTE;
408       }
409     }
410   }
411   req_id++;
412   return err;
413 }
414 
415 /**
416  * @ingroup snmp_traps
417  * This function is a wrapper function for preparing and sending generic or specific traps.
418  *
419  * @param oid points to enterprise object identifier
420  * @param generic_trap is the trap code
421  * @param specific_trap used for enterprise traps when generic_trap == 6
422  * @param varbinds linked list of varbinds to be sent
423  * @return ERR_OK when success, ERR_MEM if we're out of memory
424  *
425  * @note the use of the enterprise identifier field
426  * is per RFC1215.
427  * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
428  * and .iso.org.dod.internet.private.enterprises.yourenterprise
429  * (sysObjectID) for specific traps.
430  */
431 err_t
snmp_send_trap(const struct snmp_obj_id * oid,s32_t generic_trap,s32_t specific_trap,struct snmp_varbind * varbinds)432 snmp_send_trap(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
433 {
434   struct snmp_msg_trap trap_msg = {0};
435   trap_msg.snmp_version = snmp_default_trap_version;
436   trap_msg.trap_or_inform = SNMP_IS_TRAP;
437   return snmp_send_trap_or_notification_or_inform_generic(&trap_msg, oid, generic_trap, specific_trap, varbinds);
438 }
439 
440 /**
441  * @ingroup snmp_traps
442  * Send generic SNMP trap
443  * @param generic_trap is the trap code
444  * return ERR_OK when success
445  */
446 err_t
snmp_send_trap_generic(s32_t generic_trap)447 snmp_send_trap_generic(s32_t generic_trap)
448 {
449   err_t err = ERR_OK;
450   struct snmp_msg_trap trap_msg = {0};
451   trap_msg.snmp_version = snmp_default_trap_version;
452   trap_msg.trap_or_inform = SNMP_IS_TRAP;
453 
454   if(snmp_default_trap_version == SNMP_VERSION_1) {
455     static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
456     err = snmp_send_trap_or_notification_or_inform_generic(&trap_msg, &oid, generic_trap, 0, NULL);
457   } else if (snmp_default_trap_version == SNMP_VERSION_2c) {
458     err = snmp_send_trap_or_notification_or_inform_generic(&trap_msg, NULL, generic_trap, 0, NULL);
459   } else {
460     err = ERR_VAL;
461   }
462   return err;
463 }
464 
465 /**
466  * @ingroup snmp_traps
467  * Send specific SNMP trap with variable bindings
468  * @param specific_trap used for enterprise traps (generic_trap = 6)
469  * @param varbinds linked list of varbinds to be sent
470  * @return ERR_OK when success
471  */
472 err_t
snmp_send_trap_specific(s32_t specific_trap,struct snmp_varbind * varbinds)473 snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
474 {
475   struct snmp_msg_trap trap_msg = {0};
476   trap_msg.snmp_version = snmp_default_trap_version;
477   trap_msg.trap_or_inform = SNMP_IS_TRAP;
478   return snmp_send_trap_or_notification_or_inform_generic(&trap_msg, NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
479 }
480 
481 /**
482  * @ingroup snmp_traps
483  * Send coldstart trap
484  */
485 void
snmp_coldstart_trap(void)486 snmp_coldstart_trap(void)
487 {
488   snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
489 }
490 
491 /**
492  * @ingroup snmp_traps
493  * Send authentication failure trap (used internally by agent)
494  */
495 void
snmp_authfail_trap(void)496 snmp_authfail_trap(void)
497 {
498   if (snmp_auth_traps_enabled != 0) {
499     snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
500   }
501 }
502 
503 /**
504  * @ingroup snmp_traps
505  * Sums trap varbinds
506  *
507  * @param trap Trap message
508  * @param varbinds linked list of varbinds
509  * @return the required length for encoding of this part of the trap header
510  */
511 static u16_t
snmp_trap_varbind_sum(struct snmp_msg_trap * trap,struct snmp_varbind * varbinds)512 snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
513 {
514   struct snmp_varbind *varbind;
515   u16_t tot_len;
516   u8_t tot_len_len;
517 
518   tot_len = 0;
519   varbind = varbinds;
520   while (varbind != NULL) {
521     struct snmp_varbind_len len;
522 
523     if (snmp_varbind_length(varbind, &len) == ERR_OK) {
524       tot_len += 1 + len.vb_len_len + len.vb_value_len;
525     }
526 
527     varbind = varbind->next;
528   }
529 
530   trap->vbseqlen = tot_len;
531   snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
532   tot_len += 1 + tot_len_len;
533 
534   return tot_len;
535 }
536 
537 /**
538  * @ingroup snmp_traps
539  * Sums trap header fields that are specific for SNMP v1
540  *
541  * @param trap Trap message
542  * @return the required length for encoding of this part of the trap header
543  */
544 static u16_t
snmp_trap_header_sum_v1_specific(struct snmp_msg_trap * trap)545 snmp_trap_header_sum_v1_specific(struct snmp_msg_trap *trap)
546 {
547   u16_t tot_len = 0;
548   u16_t len = 0;
549   u8_t lenlen = 0;
550 
551   snmp_asn1_enc_u32t_cnt(trap->ts, &len);
552   snmp_asn1_enc_length_cnt(len, &lenlen);
553   tot_len += 1 + len + lenlen;
554 
555   snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
556   snmp_asn1_enc_length_cnt(len, &lenlen);
557   tot_len += 1 + len + lenlen;
558 
559   snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
560   snmp_asn1_enc_length_cnt(len, &lenlen);
561   tot_len += 1 + len + lenlen;
562 
563   if (IP_IS_V6_VAL(trap->sip)) {
564 #if LWIP_IPV6
565     len = sizeof(ip_2_ip6(&trap->sip)->addr);
566 #endif
567   } else {
568 #if LWIP_IPV4
569     len = sizeof(ip_2_ip4(&trap->sip)->addr);
570 #endif
571   }
572   snmp_asn1_enc_length_cnt(len, &lenlen);
573   tot_len += 1 + len + lenlen;
574 
575   snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
576   snmp_asn1_enc_length_cnt(len, &lenlen);
577   tot_len += 1 + len + lenlen;
578 
579   return tot_len;
580 }
581 
582 /**
583  * @ingroup snmp_traps
584  * Sums trap header fields that are specific for SNMP v2c
585  *
586  * @param trap Trap message
587  * @return the required length for encoding of this part of the trap header
588  */
589 static u16_t
snmp_trap_header_sum_v2c_specific(struct snmp_msg_trap * trap)590 snmp_trap_header_sum_v2c_specific(struct snmp_msg_trap *trap)
591 {
592   u16_t tot_len = 0;
593   u16_t len = 0;
594   u8_t lenlen = 0;
595 
596   snmp_asn1_enc_u32t_cnt(req_id, &len);
597   snmp_asn1_enc_length_cnt(len, &lenlen);
598   tot_len += 1 + len + lenlen;
599   snmp_asn1_enc_u32t_cnt(trap->error_status, &len);
600   snmp_asn1_enc_length_cnt(len, &lenlen);
601   tot_len += 1 + len + lenlen;
602   snmp_asn1_enc_u32t_cnt(trap->error_index, &len);
603   snmp_asn1_enc_length_cnt(len, &lenlen);
604   tot_len += 1 + len + lenlen;
605 
606   return tot_len;
607 }
608 
609 /**
610  * @ingroup snmp_traps
611  * Sums trap header field lengths from tail to head and
612  * returns trap_header_lengths for second encoding pass.
613  *
614  * @param trap Trap message
615  * @param vb_len varbind-list length
616  * @return the required length for encoding the trap header
617  */
618 static u16_t
snmp_trap_header_sum(struct snmp_msg_trap * trap,u16_t vb_len)619 snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
620 {
621   u16_t tot_len = vb_len;
622   u16_t len = 0;
623   u8_t lenlen = 0;
624 
625   if (trap->snmp_version == SNMP_VERSION_1) {
626     tot_len += snmp_trap_header_sum_v1_specific(trap);
627   } else if (trap->snmp_version == SNMP_VERSION_2c) {
628     tot_len += snmp_trap_header_sum_v2c_specific(trap);
629   }
630   trap->pdulen = tot_len;
631   snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
632   tot_len += 1 + lenlen;
633 
634   trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
635   snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
636   tot_len += 1 + lenlen + trap->comlen;
637 
638   snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
639   snmp_asn1_enc_length_cnt(len, &lenlen);
640   tot_len += 1 + len + lenlen;
641 
642   trap->seqlen = tot_len;
643   snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
644   tot_len += 1 + lenlen;
645 
646   return tot_len;
647 }
648 
649 /**
650  * @ingroup snmp_traps
651  * Encodes varbinds.
652  * @param trap Trap message
653  * @param pbuf_stream stream used for storing data inside pbuf
654  * @param varbinds linked list of varbinds
655  * @retval err_t ERR_OK if successful, ERR_ARG otherwise
656  */
657 static err_t
snmp_trap_varbind_enc(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream,struct snmp_varbind * varbinds)658 snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
659 {
660   struct snmp_asn1_tlv tlv;
661   struct snmp_varbind *varbind;
662 
663   varbind = varbinds;
664 
665   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
666   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
667 
668   while (varbind != NULL) {
669     BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
670 
671     varbind = varbind->next;
672   }
673 
674   return ERR_OK;
675 }
676 
677 /**
678  * @ingroup snmp_traps
679  * Encodes trap header PDU part.
680  * @param trap Trap message
681  * @param pbuf_stream stream used for storing data inside pbuf
682  * @retval err_t ERR_OK if successful, ERR_ARG otherwise
683  */
684 static err_t
snmp_trap_header_enc_pdu(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)685 snmp_trap_header_enc_pdu(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
686 {
687   struct snmp_asn1_tlv tlv;
688   /* 'PDU' sequence */
689   if (trap->snmp_version == SNMP_VERSION_1) {
690     /* TRAP V1 */
691     SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
692     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
693   } else if ((trap->snmp_version == SNMP_VERSION_2c) && (trap->trap_or_inform == SNMP_IS_INFORM)) {
694     /* TRAP v2 - INFORM */
695     SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_INFORM_REQ), 0, trap->pdulen);
696     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
697   } else if (trap->snmp_version == SNMP_VERSION_2c) {
698     /* TRAP v2 - NOTIFICATION*/
699     SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_V2_TRAP), 0, trap->pdulen);
700     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
701   }
702 
703   return ERR_OK;
704 }
705 
706 /**
707  * @ingroup snmp_traps
708  * Encodes trap header part that is SNMP v1 header specific.
709  * @param trap Trap message
710  * @param pbuf_stream stream used for storing data inside pbuf
711  */
712 static err_t
snmp_trap_header_enc_v1_specific(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)713 snmp_trap_header_enc_v1_specific(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
714 {
715   struct snmp_asn1_tlv tlv;
716   /* object ID */
717   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
718   snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
719   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
720   BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) );
721 
722   /* IP addr */
723   if (IP_IS_V6_VAL(trap->sip)) {
724 #if LWIP_IPV6
725     SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
726     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
727     BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) );
728 #endif
729   } else {
730 #if LWIP_IPV4
731     SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
732     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
733     BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) );
734 #endif
735   }
736 
737   /* generic trap */
738   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
739   snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
740   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
741   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) );
742 
743   /* specific trap */
744   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
745   snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
746   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
747   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) );
748 
749   /* timestamp */
750   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
751   snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
752   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
753   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) );
754 
755   return ERR_OK;
756 }
757 
758 /**
759  * @ingroup snmp_traps
760  * Encodes trap header part that is SNMP v2c header specific.
761  *
762  * @param trap Trap message
763  * @param pbuf_stream stream used for storing data inside pbuf
764  */
765 static err_t
snmp_trap_header_enc_v2c_specific(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)766 snmp_trap_header_enc_v2c_specific(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
767 {
768   struct snmp_asn1_tlv tlv;
769   /* request id */
770   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
771   snmp_asn1_enc_s32t_cnt(req_id, &tlv.value_len);
772   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
773   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, req_id) );
774 
775   /* error status */
776   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
777   snmp_asn1_enc_s32t_cnt(trap->error_status, &tlv.value_len);
778   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
779   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->error_status) );
780 
781   /* error index */
782   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
783   snmp_asn1_enc_s32t_cnt(trap->error_index, &tlv.value_len);
784   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
785   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->error_index) );
786 
787   return ERR_OK;
788 }
789 
790 /**
791  * @ingroup snmp_traps
792  * Encodes trap header from head to tail.
793  *
794  * @param trap Trap message
795  * @param pbuf_stream stream used for storing data inside pbuf
796  */
797 static err_t
snmp_trap_header_enc(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)798 snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
799 {
800   struct snmp_asn1_tlv tlv;
801 
802   /* 'Message' sequence */
803   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
804   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
805 
806   /* version */
807   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
808   snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
809   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
810   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) );
811 
812   /* community */
813   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
814   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
815   BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream,  (const u8_t *)snmp_community_trap, trap->comlen) );
816 
817   /* PDU */
818   BUILD_EXEC( snmp_trap_header_enc_pdu(trap, pbuf_stream) );
819   if (trap->snmp_version == SNMP_VERSION_1) {
820     /* object ID, IP addr, generic trap, specific trap, timestamp */
821     BUILD_EXEC( snmp_trap_header_enc_v1_specific(trap, pbuf_stream) );
822   } else if (SNMP_VERSION_2c == trap->snmp_version) {
823     /* request id, error status, error index */
824     BUILD_EXEC( snmp_trap_header_enc_v2c_specific(trap, pbuf_stream) );
825   }
826 
827   return ERR_OK;
828 }
829 
830 /**
831  * @ingroup snmp_traps
832  * Wrapper function for sending informs
833  * @param specific_trap will be appended to enterprise oid [see RFC 3584]
834  * @param varbinds linked list of varbinds (at the beginning of this list function will insert 2 special purpose varbinds [see RFC 3584])
835  * @param ptr_request_id [out] variable in which to store request_id needed to verify acknowledgement
836  * @return ERR_OK if successful
837  */
838 err_t
snmp_send_inform_specific(s32_t specific_trap,struct snmp_varbind * varbinds,s32_t * ptr_request_id)839 snmp_send_inform_specific(s32_t specific_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id)
840 {
841   return snmp_send_inform(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds, ptr_request_id);
842 }
843 
844 /**
845  * @ingroup snmp_traps
846  * Wrapper function for sending informs
847  * @param generic_trap is the trap code
848  * @param varbinds linked list of varbinds (at the beginning of this list function will insert 2 special purpose varbinds [see RFC 3584])
849  * @param ptr_request_id [out] variable in which to store request_id needed to verify acknowledgement
850  * @return ERR_OK if successful
851  */
852 err_t
snmp_send_inform_generic(s32_t generic_trap,struct snmp_varbind * varbinds,s32_t * ptr_request_id)853 snmp_send_inform_generic(s32_t generic_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id)
854 {
855   return snmp_send_inform(NULL, generic_trap, 0, varbinds, ptr_request_id);
856 }
857 
858 /**
859  * @ingroup snmp_traps
860  * Generic function for sending informs
861  * @param oid points to object identifier
862  * @param generic_trap is the trap code
863  * @param specific_trap used for enterprise traps when generic_trap == 6
864  * @param varbinds linked list of varbinds (at the beginning of this list function will insert 2 special purpose varbinds [see RFC 3584])
865  * @param ptr_request_id [out] variable in which to store request_id needed to verify acknowledgement
866  * @return ERR_OK if successful
867  */
868 err_t
snmp_send_inform(const struct snmp_obj_id * oid,s32_t generic_trap,s32_t specific_trap,struct snmp_varbind * varbinds,s32_t * ptr_request_id)869 snmp_send_inform(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id)
870 {
871   struct snmp_msg_trap trap_msg = {0};
872   trap_msg.snmp_version = SNMP_VERSION_2c;
873   trap_msg.trap_or_inform = SNMP_IS_INFORM;
874   *ptr_request_id = req_id;
875   return snmp_send_trap_or_notification_or_inform_generic(&trap_msg, oid, generic_trap, specific_trap, varbinds);
876 }
877 
878 #endif /* LWIP_SNMP */
879