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  * @retval void
141  */
142 void
snmp_trap_dst_enable(u8_t dst_idx,u8_t enable)143 snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
144 {
145   LWIP_ASSERT_SNMP_LOCKED();
146   if (dst_idx < SNMP_TRAP_DESTINATIONS) {
147     trap_dst[dst_idx].enable = enable;
148   }
149 }
150 
151 /**
152  * @ingroup snmp_traps
153  * Sets IPv4 address for this trap destination.
154  * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
155  * @param dst IPv4 address in host order.
156  *
157  * @retval void
158  */
159 void
snmp_trap_dst_ip_set(u8_t dst_idx,const ip_addr_t * dst)160 snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
161 {
162   LWIP_ASSERT_SNMP_LOCKED();
163   if (dst_idx < SNMP_TRAP_DESTINATIONS) {
164     ip_addr_set(&trap_dst[dst_idx].dip, dst);
165   }
166 }
167 
168 /**
169  * @ingroup snmp_traps
170  * Enable/disable authentication traps
171  *
172  * @param enable enable SNMP traps
173  *
174  * @retval void
175  */
176 void
snmp_set_auth_traps_enabled(u8_t enable)177 snmp_set_auth_traps_enabled(u8_t enable)
178 {
179   snmp_auth_traps_enabled = enable;
180 }
181 
182 /**
183  * @ingroup snmp_traps
184  * Get authentication traps enabled state
185  *
186  * @return TRUE if traps are enabled, FALSE if they aren't
187  */
188 u8_t
snmp_get_auth_traps_enabled(void)189 snmp_get_auth_traps_enabled(void)
190 {
191   return snmp_auth_traps_enabled;
192 }
193 
194 /**
195  * @ingroup snmp_traps
196  * Choose default SNMP version for sending traps (if not specified, default version is SNMP_VERSION_1)
197  * SNMP_VERSION_1  0
198  * SNMP_VERSION_2c 1
199  * SNMP_VERSION_3  3
200  *
201  * @param snmp_version version that will be used for sending traps
202  *
203  * @retval void
204  */
205 void
snmp_set_default_trap_version(u8_t snmp_version)206 snmp_set_default_trap_version(u8_t snmp_version)
207 {
208   snmp_default_trap_version = snmp_version;
209 }
210 
211 /**
212  * @ingroup snmp_traps
213  * Get default SNMP version for sending traps
214  *
215  * @return selected default version:
216  * 0 - SNMP_VERSION_1
217  * 1 - SNMP_VERSION_2c
218  * 3 - SNMP_VERSION_3
219  */
220 u8_t
snmp_get_default_trap_version(void)221 snmp_get_default_trap_version(void)
222 {
223   return snmp_default_trap_version;
224 }
225 
226 /**
227  * @ingroup snmp_traps
228  * Prepares snmpTrapOID for SNMP v2c
229  * @param dest_snmp_trap_oid pointer to destination snmpTrapOID
230  * @param eoid enterprise oid (can be NULL)
231  * @param generic_trap SNMP v1 generic trap
232  * @param specific_trap SNMP v1 specific trap
233  * @return ERR_OK if completed successfully;
234  *         ERR_MEM if there wasn't enough memory allocated for destination;
235  *         ERR_VAL if value for generic trap was incorrect;
236  */
237 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)238 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)
239 {
240   err_t err = ERR_OK;
241   const u32_t snmpTrapOID[] = {1, 3, 6, 1, 6, 3, 1, 1, 5};     /* please see rfc3584 */
242 
243   if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
244     if (eoid == NULL) {
245       MEMCPY(dest_snmp_trap_oid, snmp_get_device_enterprise_oid(), sizeof(*dest_snmp_trap_oid));
246     } else {
247       MEMCPY(dest_snmp_trap_oid, eoid, sizeof(*dest_snmp_trap_oid));
248     }
249     if (dest_snmp_trap_oid->len + 2 < SNMP_MAX_OBJ_ID_LEN) {
250       dest_snmp_trap_oid->id[dest_snmp_trap_oid->len++] = 0;
251       dest_snmp_trap_oid->id[dest_snmp_trap_oid->len++] = specific_trap;
252     } else {
253       err = ERR_MEM;
254     }
255   } else if ((generic_trap >= SNMP_GENTRAP_COLDSTART) && (generic_trap < SNMP_GENTRAP_ENTERPRISE_SPECIFIC)) {
256     if (sizeof(dest_snmp_trap_oid->id) >= sizeof(snmpTrapOID)) {
257       MEMCPY(&dest_snmp_trap_oid->id, snmpTrapOID , sizeof(snmpTrapOID));
258       dest_snmp_trap_oid->len = LWIP_ARRAYSIZE(snmpTrapOID);
259       dest_snmp_trap_oid->id[dest_snmp_trap_oid->len++] = specific_trap + 1;
260     } else {
261       err = ERR_MEM;
262     }
263   } else {
264     err = ERR_VAL;
265   }
266   return err;
267 }
268 
269 /**
270  * @ingroup snmp_traps
271  * Prepare the rest of the necessary fields for trap/notification/inform message.
272  * @param trap_msg message that should be set
273  * @param eoid enterprise oid (can be NULL)
274  * @param generic_trap SNMP v1 generic trap
275  * @param specific_trap SNMP v1 specific trap
276  * @param varbinds list of varbinds
277  * @retval void
278  */
279 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)280 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)
281 {
282   if (trap_msg->snmp_version == SNMP_VERSION_1) {
283     trap_msg->enterprise = (eoid == NULL) ? snmp_get_device_enterprise_oid() : eoid;
284     trap_msg->gen_trap = generic_trap;
285     trap_msg->spc_trap = (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) ? specific_trap : 0;
286     MIB2_COPY_SYSUPTIME_TO(&trap_msg->ts);
287   } else if (trap_msg->snmp_version == SNMP_VERSION_2c) {
288     /* Copy sysUpTime into the first varbind */
289     MIB2_COPY_SYSUPTIME_TO((u32_t *)varbinds[0].value);
290   }
291 }
292 
293 /**
294  * @ingroup snmp_traps
295  * Copy trap message structure to pbuf and sends it
296  * @param trap_msg contains the data that should be sent
297  * @param varbinds list of varbinds
298  * @param tot_len total length of encoded data
299  * @param dip destination IP address
300  * @return ERR_OK if sending was successful
301  */
302 static err_t
snmp_send_msg(struct snmp_msg_trap * trap_msg,struct snmp_varbind * varbinds,u16_t tot_len,ip_addr_t * dip)303 snmp_send_msg(struct snmp_msg_trap *trap_msg, struct snmp_varbind *varbinds, u16_t tot_len, ip_addr_t *dip)
304 {
305   err_t err = ERR_OK;
306   struct pbuf *p = NULL;
307   /* allocate pbuf(s) */
308   p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
309   if (p != NULL) {
310     struct snmp_pbuf_stream pbuf_stream;
311     snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
312 
313     /* pass 1, encode packet ino the pbuf(s) */
314     BUILD_EXEC( snmp_trap_header_enc(trap_msg, &pbuf_stream) );
315     BUILD_EXEC( snmp_trap_varbind_enc(trap_msg, &pbuf_stream, varbinds) );
316 
317     snmp_stats.outtraps++;
318     snmp_stats.outpkts++;
319 
320     /** send to the TRAP destination */
321     err = snmp_sendto(snmp_traps_handle, p, dip, LWIP_IANA_PORT_SNMP_TRAP);
322     pbuf_free(p);
323   } else {
324     err = ERR_MEM;
325   }
326   return err;
327 }
328 
329 /**
330  * @ingroup snmp_traps
331  * Prepare and sends a generic or enterprise specific trap message, notification or inform.
332  *
333  * @param trap_msg defines msg type
334  * @param eoid points to enterprise object identifier
335  * @param generic_trap is the trap code
336  * @param specific_trap used for enterprise traps when generic_trap == 6
337  * @param varbinds linked list of varbinds to be sent
338  * @return ERR_OK when success, ERR_MEM if we're out of memory
339  *
340  * @note the use of the enterprise identifier field
341  * is per RFC1215.
342  * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
343  * and .iso.org.dod.internet.private.enterprises.yourenterprise
344  * (sysObjectID) for specific traps.
345  */
346 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)347 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)
348 {
349   struct snmp_trap_dst *td = NULL;
350   u16_t i = 0;
351   u16_t tot_len = 0;
352   err_t err = ERR_OK;
353   u32_t timestamp = 0;
354   struct snmp_varbind *original_varbinds = varbinds;
355   struct snmp_varbind *original_prev = NULL;
356   struct snmp_varbind snmp_v2_special_varbinds[] = {
357                                                      /* First varbind is used to store sysUpTime */
358                                                      {
359                                                        NULL,                            /* *next */
360                                                        NULL,                            /* *prev */
361                                                        {                                /* oid */
362                                                          8,                             /* oid len */
363                                                          {1, 3, 6, 1, 2, 1, 1, 3}       /* oid for sysUpTime */
364                                                        },
365                                                        SNMP_ASN1_TYPE_TIMETICKS,        /* type */
366                                                        sizeof(u32_t),                   /* value_len */
367                                                        NULL                             /* value */
368                                                      },
369                                                      /* Second varbind is used to store snmpTrapOID */
370                                                      {
371                                                        NULL,                            /* *next */
372                                                        NULL,                            /* *prev */
373                                                        {                                /* oid */
374                                                          10,                            /* oid len */
375                                                          {1, 3, 6, 1, 6, 3, 1, 1, 4, 1} /* oid for snmpTrapOID */
376                                                        },
377                                                        SNMP_ASN1_TYPE_OBJECT_ID,        /* type */
378                                                        0,                               /* value_len */
379                                                        NULL                             /* value */
380                                                      }
381    };
382 
383   LWIP_ASSERT_SNMP_LOCKED();
384 
385   snmp_v2_special_varbinds[0].next = &snmp_v2_special_varbinds[1];
386   snmp_v2_special_varbinds[1].prev = &snmp_v2_special_varbinds[0];
387 
388   snmp_v2_special_varbinds[0].value = &timestamp;
389 
390   snmp_v2_special_varbinds[1].next = varbinds;
391 
392   /* see rfc3584 */
393   if (trap_msg->snmp_version == SNMP_VERSION_2c) {
394     struct snmp_obj_id snmp_trap_oid =  { 0 };  /* used for converting SNMPv1 generic/specific trap parameter to SNMPv2 snmpTrapOID */
395     err = snmp_prepare_trap_oid(&snmp_trap_oid, eoid, generic_trap, specific_trap);
396     if (err == ERR_OK) {
397       snmp_v2_special_varbinds[1].value_len = snmp_trap_oid.len * sizeof(snmp_trap_oid.id[0]);
398       snmp_v2_special_varbinds[1].value = snmp_trap_oid.id;
399       if (varbinds != NULL) {
400         original_prev = varbinds->prev;
401         varbinds->prev = &snmp_v2_special_varbinds[1];
402       }
403       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  */
404     }
405   }
406 
407   for (i = 0, td = &trap_dst[0]; (i < SNMP_TRAP_DESTINATIONS) && (err == ERR_OK); i++, td++) {
408     if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
409       /* lookup current source address for this dst */
410       if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg->sip)) {
411         snmp_prepare_necessary_msg_fields(trap_msg, eoid, generic_trap, specific_trap, varbinds);
412 
413         /* pass 0, calculate length fields */
414         tot_len = snmp_trap_varbind_sum(trap_msg, varbinds);
415         tot_len = snmp_trap_header_sum(trap_msg, tot_len);
416 
417         /* allocate pbuf, fill it and send it */
418         err = snmp_send_msg(trap_msg, varbinds, tot_len, &td->dip);
419       } else {
420         /* routing error */
421         err = ERR_RTE;
422       }
423     }
424   }
425   if ((trap_msg->snmp_version == SNMP_VERSION_2c) && (original_varbinds != NULL)) {
426     original_varbinds->prev = original_prev;
427   }
428   req_id++;
429   return err;
430 }
431 
432 /**
433  * @ingroup snmp_traps
434  * This function is a wrapper function for preparing and sending generic or specific traps.
435  *
436  * @param oid points to enterprise object identifier
437  * @param generic_trap is the trap code
438  * @param specific_trap used for enterprise traps when generic_trap == 6
439  * @param varbinds linked list of varbinds to be sent
440  * @return ERR_OK when success, ERR_MEM if we're out of memory
441  *
442  * @note the use of the enterprise identifier field
443  * is per RFC1215.
444  * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
445  * and .iso.org.dod.internet.private.enterprises.yourenterprise
446  * (sysObjectID) for specific traps.
447  */
448 err_t
snmp_send_trap(const struct snmp_obj_id * oid,s32_t generic_trap,s32_t specific_trap,struct snmp_varbind * varbinds)449 snmp_send_trap(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
450 {
451   struct snmp_msg_trap trap_msg = {0};
452   trap_msg.snmp_version = snmp_default_trap_version;
453   trap_msg.trap_or_inform = SNMP_IS_TRAP;
454   return snmp_send_trap_or_notification_or_inform_generic(&trap_msg, oid, generic_trap, specific_trap, varbinds);
455 }
456 
457 /**
458  * @ingroup snmp_traps
459  * Send generic SNMP trap
460  * @param generic_trap is the trap code
461  * return ERR_OK when success
462  */
463 err_t
snmp_send_trap_generic(s32_t generic_trap)464 snmp_send_trap_generic(s32_t generic_trap)
465 {
466   err_t err = ERR_OK;
467   struct snmp_msg_trap trap_msg = {0};
468   trap_msg.snmp_version = snmp_default_trap_version;
469   trap_msg.trap_or_inform = SNMP_IS_TRAP;
470 
471   if(snmp_default_trap_version == SNMP_VERSION_1) {
472     static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
473     err = snmp_send_trap_or_notification_or_inform_generic(&trap_msg, &oid, generic_trap, 0, NULL);
474   } else if (snmp_default_trap_version == SNMP_VERSION_2c) {
475     err = snmp_send_trap_or_notification_or_inform_generic(&trap_msg, NULL, generic_trap, 0, NULL);
476   } else {
477     err = ERR_VAL;
478   }
479   return err;
480 }
481 
482 /**
483  * @ingroup snmp_traps
484  * Send specific SNMP trap with variable bindings
485  * @param specific_trap used for enterprise traps (generic_trap = 6)
486  * @param varbinds linked list of varbinds to be sent
487  * @return ERR_OK when success
488  */
489 err_t
snmp_send_trap_specific(s32_t specific_trap,struct snmp_varbind * varbinds)490 snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
491 {
492   struct snmp_msg_trap trap_msg = {0};
493   trap_msg.snmp_version = snmp_default_trap_version;
494   trap_msg.trap_or_inform = SNMP_IS_TRAP;
495   return snmp_send_trap_or_notification_or_inform_generic(&trap_msg, NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
496 }
497 
498 /**
499  * @ingroup snmp_traps
500  * Send coldstart trap
501  * @retval void
502  */
503 void
snmp_coldstart_trap(void)504 snmp_coldstart_trap(void)
505 {
506   snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
507 }
508 
509 /**
510  * @ingroup snmp_traps
511  * Send authentication failure trap (used internally by agent)
512  * @retval void
513  */
514 void
snmp_authfail_trap(void)515 snmp_authfail_trap(void)
516 {
517   if (snmp_auth_traps_enabled != 0) {
518     snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
519   }
520 }
521 
522 /**
523  * @ingroup snmp_traps
524  * Sums trap varbinds
525  *
526  * @param trap Trap message
527  * @param varbinds linked list of varbinds
528  * @return the required length for encoding of this part of the trap header
529  */
530 static u16_t
snmp_trap_varbind_sum(struct snmp_msg_trap * trap,struct snmp_varbind * varbinds)531 snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
532 {
533   struct snmp_varbind *varbind;
534   u16_t tot_len;
535   u8_t tot_len_len;
536 
537   tot_len = 0;
538   varbind = varbinds;
539   while (varbind != NULL) {
540     struct snmp_varbind_len len;
541 
542     if (snmp_varbind_length(varbind, &len) == ERR_OK) {
543       tot_len += 1 + len.vb_len_len + len.vb_value_len;
544     }
545 
546     varbind = varbind->next;
547   }
548 
549   trap->vbseqlen = tot_len;
550   snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
551   tot_len += 1 + tot_len_len;
552 
553   return tot_len;
554 }
555 
556 /**
557  * @ingroup snmp_traps
558  * Sums trap header fields that are specific for SNMP v1
559  *
560  * @param trap Trap message
561  * @return the required length for encoding of this part of the trap header
562  */
563 static u16_t
snmp_trap_header_sum_v1_specific(struct snmp_msg_trap * trap)564 snmp_trap_header_sum_v1_specific(struct snmp_msg_trap *trap)
565 {
566   u16_t tot_len = 0;
567   u16_t len = 0;
568   u8_t lenlen = 0;
569 
570   snmp_asn1_enc_u32t_cnt(trap->ts, &len);
571   snmp_asn1_enc_length_cnt(len, &lenlen);
572   tot_len += 1 + len + lenlen;
573 
574   snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
575   snmp_asn1_enc_length_cnt(len, &lenlen);
576   tot_len += 1 + len + lenlen;
577 
578   snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
579   snmp_asn1_enc_length_cnt(len, &lenlen);
580   tot_len += 1 + len + lenlen;
581 
582   if (IP_IS_V6_VAL(trap->sip)) {
583 #if LWIP_IPV6
584     len = sizeof(ip_2_ip6(&trap->sip)->addr);
585 #endif
586   } else {
587 #if LWIP_IPV4
588     len = sizeof(ip_2_ip4(&trap->sip)->addr);
589 #endif
590   }
591   snmp_asn1_enc_length_cnt(len, &lenlen);
592   tot_len += 1 + len + lenlen;
593 
594   snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
595   snmp_asn1_enc_length_cnt(len, &lenlen);
596   tot_len += 1 + len + lenlen;
597 
598   return tot_len;
599 }
600 
601 /**
602  * @ingroup snmp_traps
603  * Sums trap header fields that are specific for SNMP v2c
604  *
605  * @param trap Trap message
606  * @return the required length for encoding of this part of the trap header
607  */
608 static u16_t
snmp_trap_header_sum_v2c_specific(struct snmp_msg_trap * trap)609 snmp_trap_header_sum_v2c_specific(struct snmp_msg_trap *trap)
610 {
611   u16_t tot_len = 0;
612   u16_t len = 0;
613   u8_t lenlen = 0;
614 
615   snmp_asn1_enc_u32t_cnt(req_id, &len);
616   snmp_asn1_enc_length_cnt(len, &lenlen);
617   tot_len += 1 + len + lenlen;
618   snmp_asn1_enc_u32t_cnt(trap->error_status, &len);
619   snmp_asn1_enc_length_cnt(len, &lenlen);
620   tot_len += 1 + len + lenlen;
621   snmp_asn1_enc_u32t_cnt(trap->error_index, &len);
622   snmp_asn1_enc_length_cnt(len, &lenlen);
623   tot_len += 1 + len + lenlen;
624 
625   return tot_len;
626 }
627 
628 /**
629  * @ingroup snmp_traps
630  * Sums trap header field lengths from tail to head and
631  * returns trap_header_lengths for second encoding pass.
632  *
633  * @param trap Trap message
634  * @param vb_len varbind-list length
635  * @return the required length for encoding the trap header
636  */
637 static u16_t
snmp_trap_header_sum(struct snmp_msg_trap * trap,u16_t vb_len)638 snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
639 {
640   u16_t tot_len = vb_len;
641   u16_t len = 0;
642   u8_t lenlen = 0;
643 
644   if (trap->snmp_version == SNMP_VERSION_1) {
645     tot_len += snmp_trap_header_sum_v1_specific(trap);
646   } else if (trap->snmp_version == SNMP_VERSION_2c) {
647     tot_len += snmp_trap_header_sum_v2c_specific(trap);
648   }
649   trap->pdulen = tot_len;
650   snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
651   tot_len += 1 + lenlen;
652 
653   trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
654   snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
655   tot_len += 1 + lenlen + trap->comlen;
656 
657   snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
658   snmp_asn1_enc_length_cnt(len, &lenlen);
659   tot_len += 1 + len + lenlen;
660 
661   trap->seqlen = tot_len;
662   snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
663   tot_len += 1 + lenlen;
664 
665   return tot_len;
666 }
667 
668 /**
669  * @ingroup snmp_traps
670  * Encodes varbinds.
671  * @param trap Trap message
672  * @param pbuf_stream stream used for storing data inside pbuf
673  * @param varbinds linked list of varbinds
674  * @retval err_t ERR_OK if successful, ERR_ARG otherwise
675  */
676 static err_t
snmp_trap_varbind_enc(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream,struct snmp_varbind * varbinds)677 snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
678 {
679   struct snmp_asn1_tlv tlv;
680   struct snmp_varbind *varbind;
681 
682   varbind = varbinds;
683 
684   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
685   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
686 
687   while (varbind != NULL) {
688     BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
689 
690     varbind = varbind->next;
691   }
692 
693   return ERR_OK;
694 }
695 
696 /**
697  * @ingroup snmp_traps
698  * Encodes trap header PDU part.
699  * @param trap Trap message
700  * @param pbuf_stream stream used for storing data inside pbuf
701  * @retval err_t ERR_OK if successful, ERR_ARG otherwise
702  */
703 static err_t
snmp_trap_header_enc_pdu(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)704 snmp_trap_header_enc_pdu(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
705 {
706   struct snmp_asn1_tlv tlv;
707   /* 'PDU' sequence */
708   if (trap->snmp_version == SNMP_VERSION_1) {
709     /* TRAP V1 */
710     SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
711     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
712   } else if ((trap->snmp_version == SNMP_VERSION_2c) && (trap->trap_or_inform == SNMP_IS_INFORM)) {
713     /* TRAP v2 - INFORM */
714     SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_INFORM_REQ), 0, trap->pdulen);
715     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
716   } else if (trap->snmp_version == SNMP_VERSION_2c) {
717     /* TRAP v2 - NOTIFICATION*/
718     SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_V2_TRAP), 0, trap->pdulen);
719     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
720   }
721 
722   return ERR_OK;
723 }
724 
725 /**
726  * @ingroup snmp_traps
727  * Encodes trap header part that is SNMP v1 header specific.
728  * @param trap Trap message
729  * @param pbuf_stream stream used for storing data inside pbuf
730  * @retval void
731  */
732 static err_t
snmp_trap_header_enc_v1_specific(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)733 snmp_trap_header_enc_v1_specific(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
734 {
735   struct snmp_asn1_tlv tlv;
736   /* object ID */
737   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
738   snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
739   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
740   BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) );
741 
742   /* IP addr */
743   if (IP_IS_V6_VAL(trap->sip)) {
744 #if LWIP_IPV6
745     SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
746     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
747     BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) );
748 #endif
749   } else {
750 #if LWIP_IPV4
751     SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
752     BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
753     BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) );
754 #endif
755   }
756 
757   /* generic trap */
758   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
759   snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
760   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
761   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) );
762 
763   /* specific trap */
764   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
765   snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
766   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
767   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) );
768 
769   /* timestamp */
770   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
771   snmp_asn1_enc_s32t_cnt(trap->ts, &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, trap->ts) );
774 
775   return ERR_OK;
776 }
777 
778 /**
779  * @ingroup snmp_traps
780  * Encodes trap header part that is SNMP v2c header specific.
781  *
782  * @param trap Trap message
783  * @param pbuf_stream stream used for storing data inside pbuf
784  * @retval void
785  */
786 static err_t
snmp_trap_header_enc_v2c_specific(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)787 snmp_trap_header_enc_v2c_specific(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
788 {
789   struct snmp_asn1_tlv tlv;
790   /* request id */
791   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
792   snmp_asn1_enc_s32t_cnt(req_id, &tlv.value_len);
793   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
794   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, req_id) );
795 
796   /* error status */
797   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
798   snmp_asn1_enc_s32t_cnt(trap->error_status, &tlv.value_len);
799   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
800   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->error_status) );
801 
802   /* error index */
803   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
804   snmp_asn1_enc_s32t_cnt(trap->error_index, &tlv.value_len);
805   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
806   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->error_index) );
807 
808   return ERR_OK;
809 }
810 
811 /**
812  * @ingroup snmp_traps
813  * Encodes trap header from head to tail.
814  *
815  * @param trap Trap message
816  * @param pbuf_stream stream used for storing data inside pbuf
817  * @retval void
818  */
819 static err_t
snmp_trap_header_enc(struct snmp_msg_trap * trap,struct snmp_pbuf_stream * pbuf_stream)820 snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
821 {
822   struct snmp_asn1_tlv tlv;
823 
824   /* 'Message' sequence */
825   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
826   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
827 
828   /* version */
829   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
830   snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
831   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
832   BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) );
833 
834   /* community */
835   SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
836   BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
837   BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream,  (const u8_t *)snmp_community_trap, trap->comlen) );
838 
839   /* PDU */
840   BUILD_EXEC( snmp_trap_header_enc_pdu(trap, pbuf_stream) );
841   if (trap->snmp_version == SNMP_VERSION_1) {
842     /* object ID, IP addr, generic trap, specific trap, timestamp */
843     BUILD_EXEC( snmp_trap_header_enc_v1_specific(trap, pbuf_stream) );
844   } else if (SNMP_VERSION_2c == trap->snmp_version) {
845     /* request id, error status, error index */
846     BUILD_EXEC( snmp_trap_header_enc_v2c_specific(trap, pbuf_stream) );
847   }
848 
849   return ERR_OK;
850 }
851 
852 /**
853  * @ingroup snmp_traps
854  * Wrapper function for sending informs
855  * @param specific_trap will be appended to enterprise oid [see RFC 3584]
856  * @param varbinds linked list of varbinds (at the beginning of this list function will insert 2 special purpose varbinds [see RFC 3584])
857  * @param ptr_request_id [out] variable in which to store request_id needed to verify acknowledgement
858  * @return ERR_OK if successful
859  */
860 err_t
snmp_send_inform_specific(s32_t specific_trap,struct snmp_varbind * varbinds,s32_t * ptr_request_id)861 snmp_send_inform_specific(s32_t specific_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id)
862 {
863   return snmp_send_inform(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds, ptr_request_id);
864 }
865 
866 /**
867  * @ingroup snmp_traps
868  * Wrapper function for sending informs
869  * @param generic_trap is the trap code
870  * @param varbinds linked list of varbinds (at the beginning of this list function will insert 2 special purpose varbinds [see RFC 3584])
871  * @param ptr_request_id [out] variable in which to store request_id needed to verify acknowledgement
872  * @return ERR_OK if successful
873  */
874 err_t
snmp_send_inform_generic(s32_t generic_trap,struct snmp_varbind * varbinds,s32_t * ptr_request_id)875 snmp_send_inform_generic(s32_t generic_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id)
876 {
877   return snmp_send_inform(NULL, generic_trap, 0, varbinds, ptr_request_id);
878 }
879 
880 /**
881  * @ingroup snmp_traps
882  * Generic function for sending informs
883  * @param oid points to object identifier
884  * @param generic_trap is the trap code
885  * @param specific_trap used for enterprise traps when generic_trap == 6
886  * @param varbinds linked list of varbinds (at the beginning of this list function will insert 2 special purpose varbinds [see RFC 3584])
887  * @param ptr_request_id [out] variable in which to store request_id needed to verify acknowledgement
888  * @return ERR_OK if successful
889  */
890 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)891 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)
892 {
893   struct snmp_msg_trap trap_msg = {0};
894   trap_msg.snmp_version = SNMP_VERSION_2c;
895   trap_msg.trap_or_inform = SNMP_IS_INFORM;
896   *ptr_request_id = req_id;
897   return snmp_send_trap_or_notification_or_inform_generic(&trap_msg, oid, generic_trap, specific_trap, varbinds);
898 }
899 
900 #endif /* LWIP_SNMP */
901