]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/ankh/lib/lwip/lib/contrib/src/core/snmp/msg_in.c
update
[l4.git] / l4 / pkg / ankh / lib / lwip / lib / contrib / src / core / snmp / msg_in.c
1 /**
2  * @file
3  * SNMP input message processing (RFC1157).
4  */
5
6 /*
7  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
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  * Author: Christiaan Simons <christiaan.simons@axon.tv>
33  */
34
35 #include "lwip/opt.h"
36
37 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
38
39 #include "lwip/snmp.h"
40 #include "lwip/snmp_asn1.h"
41 #include "lwip/snmp_msg.h"
42 #include "lwip/snmp_structs.h"
43 #include "lwip/ip_addr.h"
44 #include "lwip/memp.h"
45 #include "lwip/udp.h"
46 #include "lwip/stats.h"
47
48 #include <string.h>
49
50 /* public (non-static) constants */
51 /** SNMP v1 == 0 */
52 const s32_t snmp_version = 0;
53 /** default SNMP community string */
54 const char snmp_publiccommunity[7] = "public";
55
56 /* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
57 struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
58 /* UDP Protocol Control Block */
59 struct udp_pcb *snmp1_pcb;
60
61 static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
62 static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
63 static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
64
65
66 /**
67  * Starts SNMP Agent.
68  * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
69  */
70 void
71 snmp_init(void)
72 {
73   struct snmp_msg_pstat *msg_ps;
74   u8_t i;
75
76   snmp1_pcb = udp_new();
77   if (snmp1_pcb != NULL)
78   {
79     udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
80     udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
81   }
82   msg_ps = &msg_input_list[0];
83   for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
84   {
85     msg_ps->state = SNMP_MSG_EMPTY;
86     msg_ps->error_index = 0;
87     msg_ps->error_status = SNMP_ES_NOERROR;
88     msg_ps++;
89   }
90   trap_msg.pcb = snmp1_pcb;
91
92 #ifdef SNMP_PRIVATE_MIB_INIT
93   /* If defined, rhis must be a function-like define to initialize the
94    * private MIB after the stack has been initialized.
95    * The private MIB can also be initialized in tcpip_callback (or after
96    * the stack is initialized), this define is only for convenience. */
97   SNMP_PRIVATE_MIB_INIT();
98 #endif /* SNMP_PRIVATE_MIB_INIT */
99
100   /* The coldstart trap will only be output
101      if our outgoing interface is up & configured  */
102   snmp_coldstart_trap();
103 }
104
105 static void
106 snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
107 {
108   snmp_varbind_list_free(&msg_ps->outvb);
109   msg_ps->outvb = msg_ps->invb;
110   msg_ps->invb.head = NULL;
111   msg_ps->invb.tail = NULL;
112   msg_ps->invb.count = 0;
113   msg_ps->error_status = error;
114   msg_ps->error_index = 1 + msg_ps->vb_idx;
115   snmp_send_response(msg_ps);
116   snmp_varbind_list_free(&msg_ps->outvb);
117   msg_ps->state = SNMP_MSG_EMPTY;
118 }
119
120 static void
121 snmp_ok_response(struct snmp_msg_pstat *msg_ps)
122 {
123   err_t err_ret;
124
125   err_ret = snmp_send_response(msg_ps);
126   if (err_ret == ERR_MEM)
127   {
128     /* serious memory problem, can't return tooBig */
129   }
130   else
131   {
132     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
133   }
134   /* free varbinds (if available) */
135   snmp_varbind_list_free(&msg_ps->invb);
136   snmp_varbind_list_free(&msg_ps->outvb);
137   msg_ps->state = SNMP_MSG_EMPTY;
138 }
139
140 /**
141  * Service an internal or external event for SNMP GET.
142  *
143  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
144  * @param msg_ps points to the assosicated message process state
145  */
146 static void
147 snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
148 {
149   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
150
151   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
152   {
153     struct mib_external_node *en;
154     struct snmp_name_ptr np;
155
156     /* get_object_def() answer*/
157     en = msg_ps->ext_mib_node;
158     np = msg_ps->ext_name_ptr;
159
160     /* translate answer into a known lifeform */
161     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
162     if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
163         (msg_ps->ext_object_def.access & MIB_ACCESS_READ))
164     {
165       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
166       en->get_value_q(request_id, &msg_ps->ext_object_def);
167     }
168     else
169     {
170       en->get_object_def_pc(request_id, np.ident_len, np.ident);
171       /* search failed, object id points to unknown object (nosuchname) */
172       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
173     }
174   }
175   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
176   {
177     struct mib_external_node *en;
178     struct snmp_varbind *vb;
179
180     /* get_value() answer */
181     en = msg_ps->ext_mib_node;
182
183     /* allocate output varbind */
184     vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
185     LWIP_ASSERT("vb != NULL",vb != NULL);
186     if (vb != NULL)
187     {
188       vb->next = NULL;
189       vb->prev = NULL;
190
191       /* move name from invb to outvb */
192       vb->ident = msg_ps->vb_ptr->ident;
193       vb->ident_len = msg_ps->vb_ptr->ident_len;
194       /* ensure this memory is refereced once only */
195       msg_ps->vb_ptr->ident = NULL;
196       msg_ps->vb_ptr->ident_len = 0;
197
198       vb->value_type = msg_ps->ext_object_def.asn_type;
199       LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
200       vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
201       if (vb->value_len > 0)
202       {
203         LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
204         vb->value = memp_malloc(MEMP_SNMP_VALUE);
205         LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
206         if (vb->value != NULL)
207         {
208           en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
209           snmp_varbind_tail_add(&msg_ps->outvb, vb);
210           /* search again (if vb_idx < msg_ps->invb.count) */
211           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
212           msg_ps->vb_idx += 1;
213         }
214         else
215         {
216           en->get_value_pc(request_id, &msg_ps->ext_object_def);
217           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
218           msg_ps->vb_ptr->ident = vb->ident;
219           msg_ps->vb_ptr->ident_len = vb->ident_len;
220           memp_free(MEMP_SNMP_VARBIND, vb);
221           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
222         }
223       }
224       else
225       {
226         /* vb->value_len == 0, empty value (e.g. empty string) */
227         en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
228         vb->value = NULL;
229         snmp_varbind_tail_add(&msg_ps->outvb, vb);
230         /* search again (if vb_idx < msg_ps->invb.count) */
231         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
232         msg_ps->vb_idx += 1;
233       }
234     }
235     else
236     {
237       en->get_value_pc(request_id, &msg_ps->ext_object_def);
238       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
239       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
240     }
241   }
242
243   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
244          (msg_ps->vb_idx < msg_ps->invb.count))
245   {
246     struct mib_node *mn;
247     struct snmp_name_ptr np;
248
249     if (msg_ps->vb_idx == 0)
250     {
251       msg_ps->vb_ptr = msg_ps->invb.head;
252     }
253     else
254     {
255       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
256     }
257     /** test object identifier for .iso.org.dod.internet prefix */
258     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
259     {
260       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
261                              msg_ps->vb_ptr->ident + 4, &np);
262       if (mn != NULL)
263       {
264         if (mn->node_type == MIB_NODE_EX)
265         {
266           /* external object */
267           struct mib_external_node *en = (struct mib_external_node*)mn;
268
269           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
270           /* save en && args in msg_ps!! */
271           msg_ps->ext_mib_node = en;
272           msg_ps->ext_name_ptr = np;
273
274           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
275         }
276         else
277         {
278           /* internal object */
279           struct obj_def object_def;
280
281           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
282           mn->get_object_def(np.ident_len, np.ident, &object_def);
283           if ((object_def.instance != MIB_OBJECT_NONE) &&
284             (object_def.access & MIB_ACCESS_READ))
285           {
286             mn = mn;
287           }
288           else
289           {
290             /* search failed, object id points to unknown object (nosuchname) */
291             mn =  NULL;
292           }
293           if (mn != NULL)
294           {
295             struct snmp_varbind *vb;
296
297             msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
298             /* allocate output varbind */
299             vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
300             LWIP_ASSERT("vb != NULL",vb != NULL);
301             if (vb != NULL)
302             {
303               vb->next = NULL;
304               vb->prev = NULL;
305
306               /* move name from invb to outvb */
307               vb->ident = msg_ps->vb_ptr->ident;
308               vb->ident_len = msg_ps->vb_ptr->ident_len;
309               /* ensure this memory is refereced once only */
310               msg_ps->vb_ptr->ident = NULL;
311               msg_ps->vb_ptr->ident_len = 0;
312
313               vb->value_type = object_def.asn_type;
314               LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
315               vb->value_len = (u8_t)object_def.v_len;
316               if (vb->value_len > 0)
317               {
318                 LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
319                   vb->value_len <= SNMP_MAX_VALUE_SIZE);
320                 vb->value = memp_malloc(MEMP_SNMP_VALUE);
321                 LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
322                 if (vb->value != NULL)
323                 {
324                   mn->get_value(&object_def, vb->value_len, vb->value);
325                   snmp_varbind_tail_add(&msg_ps->outvb, vb);
326                   msg_ps->state = SNMP_MSG_SEARCH_OBJ;
327                   msg_ps->vb_idx += 1;
328                 }
329                 else
330                 {
331                   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
332                   msg_ps->vb_ptr->ident = vb->ident;
333                   msg_ps->vb_ptr->ident_len = vb->ident_len;
334                   memp_free(MEMP_SNMP_VARBIND, vb);
335                   snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
336                 }
337               }
338               else
339               {
340                 /* vb->value_len == 0, empty value (e.g. empty string) */
341                 vb->value = NULL;
342                 snmp_varbind_tail_add(&msg_ps->outvb, vb);
343                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
344                 msg_ps->vb_idx += 1;
345               }
346             }
347             else
348             {
349               LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
350               snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
351             }
352           }
353         }
354       }
355     }
356     else
357     {
358       mn = NULL;
359     }
360     if (mn == NULL)
361     {
362       /* mn == NULL, noSuchName */
363       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
364     }
365   }
366   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
367       (msg_ps->vb_idx == msg_ps->invb.count))
368   {
369     snmp_ok_response(msg_ps);
370   }
371 }
372
373 /**
374  * Service an internal or external event for SNMP GETNEXT.
375  *
376  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
377  * @param msg_ps points to the assosicated message process state
378  */
379 static void
380 snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
381 {
382   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
383
384   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
385   {
386     struct mib_external_node *en;
387
388     /* get_object_def() answer*/
389     en = msg_ps->ext_mib_node;
390
391     /* translate answer into a known lifeform */
392     en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
393     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
394     {
395       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
396       en->get_value_q(request_id, &msg_ps->ext_object_def);
397     }
398     else
399     {
400       en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
401       /* search failed, object id points to unknown object (nosuchname) */
402       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
403     }
404   }
405   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
406   {
407     struct mib_external_node *en;
408     struct snmp_varbind *vb;
409
410     /* get_value() answer */
411     en = msg_ps->ext_mib_node;
412
413     LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
414     vb = snmp_varbind_alloc(&msg_ps->ext_oid,
415                             msg_ps->ext_object_def.asn_type,
416                             (u8_t)msg_ps->ext_object_def.v_len);
417     if (vb != NULL)
418     {
419       en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
420       snmp_varbind_tail_add(&msg_ps->outvb, vb);
421       msg_ps->state = SNMP_MSG_SEARCH_OBJ;
422       msg_ps->vb_idx += 1;
423     }
424     else
425     {
426       en->get_value_pc(request_id, &msg_ps->ext_object_def);
427       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
428       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
429     }
430   }
431
432   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
433          (msg_ps->vb_idx < msg_ps->invb.count))
434   {
435     struct mib_node *mn;
436     struct snmp_obj_id oid;
437
438     if (msg_ps->vb_idx == 0)
439     {
440       msg_ps->vb_ptr = msg_ps->invb.head;
441     }
442     else
443     {
444       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
445     }
446     if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
447     {
448       if (msg_ps->vb_ptr->ident_len > 3)
449       {
450         /* can offset ident_len and ident */
451         mn = snmp_expand_tree((struct mib_node*)&internet,
452                               msg_ps->vb_ptr->ident_len - 4,
453                               msg_ps->vb_ptr->ident + 4, &oid);
454       }
455       else
456       {
457         /* can't offset ident_len -4, ident + 4 */
458         mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
459       }
460     }
461     else
462     {
463       mn = NULL;
464     }
465     if (mn != NULL)
466     {
467       if (mn->node_type == MIB_NODE_EX)
468       {
469         /* external object */
470         struct mib_external_node *en = (struct mib_external_node*)mn;
471
472         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
473         /* save en && args in msg_ps!! */
474         msg_ps->ext_mib_node = en;
475         msg_ps->ext_oid = oid;
476
477         en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
478       }
479       else
480       {
481         /* internal object */
482         struct obj_def object_def;
483         struct snmp_varbind *vb;
484
485         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
486         mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
487
488         LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
489         vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
490         if (vb != NULL)
491         {
492           msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
493           mn->get_value(&object_def, object_def.v_len, vb->value);
494           snmp_varbind_tail_add(&msg_ps->outvb, vb);
495           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
496           msg_ps->vb_idx += 1;
497         }
498         else
499         {
500           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
501           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
502         }
503       }
504     }
505     if (mn == NULL)
506     {
507       /* mn == NULL, noSuchName */
508       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
509     }
510   }
511   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
512       (msg_ps->vb_idx == msg_ps->invb.count))
513   {
514     snmp_ok_response(msg_ps);
515   }
516 }
517
518 /**
519  * Service an internal or external event for SNMP SET.
520  *
521  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
522  * @param msg_ps points to the assosicated message process state
523  */
524 static void
525 snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
526 {
527   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
528
529   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
530   {
531     struct mib_external_node *en;
532     struct snmp_name_ptr np;
533
534     /* get_object_def() answer*/
535     en = msg_ps->ext_mib_node;
536     np = msg_ps->ext_name_ptr;
537
538     /* translate answer into a known lifeform */
539     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
540     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
541     {
542       msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
543       en->set_test_q(request_id, &msg_ps->ext_object_def);
544     }
545     else
546     {
547       en->get_object_def_pc(request_id, np.ident_len, np.ident);
548       /* search failed, object id points to unknown object (nosuchname) */
549       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
550     }
551   }
552   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
553   {
554     struct mib_external_node *en;
555
556     /* set_test() answer*/
557     en = msg_ps->ext_mib_node;
558
559     if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
560     {
561        if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
562            (en->set_test_a(request_id,&msg_ps->ext_object_def,
563                            msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
564       {
565         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
566         msg_ps->vb_idx += 1;
567       }
568       else
569       {
570         en->set_test_pc(request_id,&msg_ps->ext_object_def);
571         /* bad value */
572         snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
573       }
574     }
575     else
576     {
577       en->set_test_pc(request_id,&msg_ps->ext_object_def);
578       /* object not available for set */
579       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
580     }
581   }
582   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
583   {
584     struct mib_external_node *en;
585     struct snmp_name_ptr np;
586
587     /* get_object_def() answer*/
588     en = msg_ps->ext_mib_node;
589     np = msg_ps->ext_name_ptr;
590
591     /* translate answer into a known lifeform */
592     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
593     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
594     {
595       msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
596       en->set_value_q(request_id, &msg_ps->ext_object_def,
597                       msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
598     }
599     else
600     {
601       en->get_object_def_pc(request_id, np.ident_len, np.ident);
602       /* set_value failed, object has disappeared for some odd reason?? */
603       snmp_error_response(msg_ps,SNMP_ES_GENERROR);
604     }
605   }
606   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
607   {
608     struct mib_external_node *en;
609
610     /** set_value_a() */
611     en = msg_ps->ext_mib_node;
612     en->set_value_a(request_id, &msg_ps->ext_object_def,
613       msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
614
615     /** @todo use set_value_pc() if toobig */
616     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
617     msg_ps->vb_idx += 1;
618   }
619
620   /* test all values before setting */
621   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
622          (msg_ps->vb_idx < msg_ps->invb.count))
623   {
624     struct mib_node *mn;
625     struct snmp_name_ptr np;
626
627     if (msg_ps->vb_idx == 0)
628     {
629       msg_ps->vb_ptr = msg_ps->invb.head;
630     }
631     else
632     {
633       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
634     }
635     /** test object identifier for .iso.org.dod.internet prefix */
636     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
637     {
638       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
639                              msg_ps->vb_ptr->ident + 4, &np);
640       if (mn != NULL)
641       {
642         if (mn->node_type == MIB_NODE_EX)
643         {
644           /* external object */
645           struct mib_external_node *en = (struct mib_external_node*)mn;
646
647           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
648           /* save en && args in msg_ps!! */
649           msg_ps->ext_mib_node = en;
650           msg_ps->ext_name_ptr = np;
651
652           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
653         }
654         else
655         {
656           /* internal object */
657           struct obj_def object_def;
658
659           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
660           mn->get_object_def(np.ident_len, np.ident, &object_def);
661           if (object_def.instance != MIB_OBJECT_NONE)
662           {
663             mn = mn;
664           }
665           else
666           {
667             /* search failed, object id points to unknown object (nosuchname) */
668             mn = NULL;
669           }
670           if (mn != NULL)
671           {
672             msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
673
674             if (object_def.access & MIB_ACCESS_WRITE)
675             {
676               if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
677                   (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
678               {
679                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
680                 msg_ps->vb_idx += 1;
681               }
682               else
683               {
684                 /* bad value */
685                 snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
686               }
687             }
688             else
689             {
690               /* object not available for set */
691               snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
692             }
693           }
694         }
695       }
696     }
697     else
698     {
699       mn = NULL;
700     }
701     if (mn == NULL)
702     {
703       /* mn == NULL, noSuchName */
704       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
705     }
706   }
707
708   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
709       (msg_ps->vb_idx == msg_ps->invb.count))
710   {
711     msg_ps->vb_idx = 0;
712     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
713   }
714
715   /* set all values "atomically" (be as "atomic" as possible) */
716   while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
717          (msg_ps->vb_idx < msg_ps->invb.count))
718   {
719     struct mib_node *mn;
720     struct snmp_name_ptr np;
721
722     if (msg_ps->vb_idx == 0)
723     {
724       msg_ps->vb_ptr = msg_ps->invb.head;
725     }
726     else
727     {
728       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
729     }
730     /* skip iso prefix test, was done previously while settesting() */
731     mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
732                            msg_ps->vb_ptr->ident + 4, &np);
733     /* check if object is still available
734        (e.g. external hot-plug thingy present?) */
735     if (mn != NULL)
736     {
737       if (mn->node_type == MIB_NODE_EX)
738       {
739         /* external object */
740         struct mib_external_node *en = (struct mib_external_node*)mn;
741
742         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
743         /* save en && args in msg_ps!! */
744         msg_ps->ext_mib_node = en;
745         msg_ps->ext_name_ptr = np;
746
747         en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
748       }
749       else
750       {
751         /* internal object */
752         struct obj_def object_def;
753
754         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
755         mn->get_object_def(np.ident_len, np.ident, &object_def);
756         msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
757         mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
758         msg_ps->vb_idx += 1;
759       }
760     }
761   }
762   if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
763       (msg_ps->vb_idx == msg_ps->invb.count))
764   {
765     /* simply echo the input if we can set it
766        @todo do we need to return the actual value?
767        e.g. if value is silently modified or behaves sticky? */
768     msg_ps->outvb = msg_ps->invb;
769     msg_ps->invb.head = NULL;
770     msg_ps->invb.tail = NULL;
771     msg_ps->invb.count = 0;
772     snmp_ok_response(msg_ps);
773   }
774 }
775
776
777 /**
778  * Handle one internal or external event.
779  * Called for one async event. (recv external/private answer)
780  *
781  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
782  */
783 void
784 snmp_msg_event(u8_t request_id)
785 {
786   struct snmp_msg_pstat *msg_ps;
787
788   if (request_id < SNMP_CONCURRENT_REQUESTS)
789   {
790     msg_ps = &msg_input_list[request_id];
791     if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
792     {
793       snmp_msg_getnext_event(request_id, msg_ps);
794     }
795     else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
796     {
797       snmp_msg_get_event(request_id, msg_ps);
798     }
799     else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
800     {
801       snmp_msg_set_event(request_id, msg_ps);
802     }
803   }
804 }
805
806
807 /* lwIP UDP receive callback function */
808 static void
809 snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
810 {
811   struct snmp_msg_pstat *msg_ps;
812   u8_t req_idx;
813   err_t err_ret;
814   u16_t payload_len = p->tot_len;
815   u16_t payload_ofs = 0;
816   u16_t varbind_ofs = 0;
817
818   /* suppress unused argument warning */
819   LWIP_UNUSED_ARG(arg);
820
821   /* traverse input message process list, look for SNMP_MSG_EMPTY */
822   msg_ps = &msg_input_list[0];
823   req_idx = 0;
824   while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
825   {
826     req_idx++;
827     msg_ps++;
828   }
829   if (req_idx == SNMP_CONCURRENT_REQUESTS)
830   {
831     /* exceeding number of concurrent requests */
832     pbuf_free(p);
833     return;
834   }
835
836   /* accepting request */
837   snmp_inc_snmpinpkts();
838   /* record used 'protocol control block' */
839   msg_ps->pcb = pcb;
840   /* source address (network order) */
841   msg_ps->sip = *addr;
842   /* source port (host order (lwIP oddity)) */
843   msg_ps->sp = port;
844
845   /* check total length, version, community, pdu type */
846   err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
847   /* Only accept requests and requests without error (be robust) */
848   /* Reject response and trap headers or error requests as input! */
849   if ((err_ret != ERR_OK) ||
850       ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
851        (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
852        (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
853       ((msg_ps->error_status != SNMP_ES_NOERROR) ||
854        (msg_ps->error_index != 0)) )
855   {
856     /* header check failed drop request silently, do not return error! */
857     pbuf_free(p);
858     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
859     return;
860   }
861   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
862
863   /* Builds a list of variable bindings. Copy the varbinds from the pbuf
864     chain to glue them when these are divided over two or more pbuf's. */
865   err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
866   /* we've decoded the incoming message, release input msg now */
867   pbuf_free(p);
868   if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
869   {
870     /* varbind-list decode failed, or varbind list empty.
871        drop request silently, do not return error!
872        (errors are only returned for a specific varbind failure) */
873     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
874     return;
875   }
876
877   msg_ps->error_status = SNMP_ES_NOERROR;
878   msg_ps->error_index = 0;
879   /* find object for each variable binding */
880   msg_ps->state = SNMP_MSG_SEARCH_OBJ;
881   /* first variable binding from list to inspect */
882   msg_ps->vb_idx = 0;
883
884   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
885
886   /* handle input event and as much objects as possible in one go */
887   snmp_msg_event(req_idx);
888 }
889
890 /**
891  * Checks and decodes incoming SNMP message header, logs header errors.
892  *
893  * @param p points to pbuf chain of SNMP message (UDP payload)
894  * @param ofs points to first octet of SNMP message
895  * @param pdu_len the length of the UDP payload
896  * @param ofs_ret returns the ofset of the variable bindings
897  * @param m_stat points to the current message request state return
898  * @return
899  * - ERR_OK SNMP header is sane and accepted
900  * - ERR_ARG SNMP header is either malformed or rejected
901  */
902 static err_t
903 snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
904 {
905   err_t derr;
906   u16_t len, ofs_base;
907   u8_t  len_octets;
908   u8_t  type;
909   s32_t version;
910
911   ofs_base = ofs;
912   snmp_asn1_dec_type(p, ofs, &type);
913   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
914   if ((derr != ERR_OK) ||
915       (pdu_len != (1 + len_octets + len)) ||
916       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
917   {
918     snmp_inc_snmpinasnparseerrs();
919     return ERR_ARG;
920   }
921   ofs += (1 + len_octets);
922   snmp_asn1_dec_type(p, ofs, &type);
923   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
924   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
925   {
926     /* can't decode or no integer (version) */
927     snmp_inc_snmpinasnparseerrs();
928     return ERR_ARG;
929   }
930   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
931   if (derr != ERR_OK)
932   {
933     /* can't decode */
934     snmp_inc_snmpinasnparseerrs();
935     return ERR_ARG;
936   }
937   if (version != 0)
938   {
939     /* not version 1 */
940     snmp_inc_snmpinbadversions();
941     return ERR_ARG;
942   }
943   ofs += (1 + len_octets + len);
944   snmp_asn1_dec_type(p, ofs, &type);
945   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
946   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
947   {
948     /* can't decode or no octet string (community) */
949     snmp_inc_snmpinasnparseerrs();
950     return ERR_ARG;
951   }
952   derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
953   if (derr != ERR_OK)
954   {
955     snmp_inc_snmpinasnparseerrs();
956     return ERR_ARG;
957   }
958   /* add zero terminator */
959   len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
960   m_stat->community[len] = 0;
961   m_stat->com_strlen = (u8_t)len;
962   if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
963   {
964     /** @todo: move this if we need to check more names */
965     snmp_inc_snmpinbadcommunitynames();
966     snmp_authfail_trap();
967     return ERR_ARG;
968   }
969   ofs += (1 + len_octets + len);
970   snmp_asn1_dec_type(p, ofs, &type);
971   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
972   if (derr != ERR_OK)
973   {
974     snmp_inc_snmpinasnparseerrs();
975     return ERR_ARG;
976   }
977   switch(type)
978   {
979     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
980       /* GetRequest PDU */
981       snmp_inc_snmpingetrequests();
982       derr = ERR_OK;
983       break;
984     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
985       /* GetNextRequest PDU */
986       snmp_inc_snmpingetnexts();
987       derr = ERR_OK;
988       break;
989     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
990       /* GetResponse PDU */
991       snmp_inc_snmpingetresponses();
992       derr = ERR_ARG;
993       break;
994     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
995       /* SetRequest PDU */
996       snmp_inc_snmpinsetrequests();
997       derr = ERR_OK;
998       break;
999     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
1000       /* Trap PDU */
1001       snmp_inc_snmpintraps();
1002       derr = ERR_ARG;
1003       break;
1004     default:
1005       snmp_inc_snmpinasnparseerrs();
1006       derr = ERR_ARG;
1007       break;
1008   }
1009   if (derr != ERR_OK)
1010   {
1011     /* unsupported input PDU for this agent (no parse error) */
1012     return ERR_ARG;
1013   }
1014   m_stat->rt = type & 0x1F;
1015   ofs += (1 + len_octets);
1016   if (len != (pdu_len - (ofs - ofs_base)))
1017   {
1018     /* decoded PDU length does not equal actual payload length */
1019     snmp_inc_snmpinasnparseerrs();
1020     return ERR_ARG;
1021   }
1022   snmp_asn1_dec_type(p, ofs, &type);
1023   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1024   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
1025   {
1026     /* can't decode or no integer (request ID) */
1027     snmp_inc_snmpinasnparseerrs();
1028     return ERR_ARG;
1029   }
1030   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
1031   if (derr != ERR_OK)
1032   {
1033     /* can't decode */
1034     snmp_inc_snmpinasnparseerrs();
1035     return ERR_ARG;
1036   }
1037   ofs += (1 + len_octets + len);
1038   snmp_asn1_dec_type(p, ofs, &type);
1039   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1040   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
1041   {
1042     /* can't decode or no integer (error-status) */
1043     snmp_inc_snmpinasnparseerrs();
1044     return ERR_ARG;
1045   }
1046   /* must be noError (0) for incoming requests.
1047      log errors for mib-2 completeness and for debug purposes */
1048   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
1049   if (derr != ERR_OK)
1050   {
1051     /* can't decode */
1052     snmp_inc_snmpinasnparseerrs();
1053     return ERR_ARG;
1054   }
1055   switch (m_stat->error_status)
1056   {
1057     case SNMP_ES_TOOBIG:
1058       snmp_inc_snmpintoobigs();
1059       break;
1060     case SNMP_ES_NOSUCHNAME:
1061       snmp_inc_snmpinnosuchnames();
1062       break;
1063     case SNMP_ES_BADVALUE:
1064       snmp_inc_snmpinbadvalues();
1065       break;
1066     case SNMP_ES_READONLY:
1067       snmp_inc_snmpinreadonlys();
1068       break;
1069     case SNMP_ES_GENERROR:
1070       snmp_inc_snmpingenerrs();
1071       break;
1072   }
1073   ofs += (1 + len_octets + len);
1074   snmp_asn1_dec_type(p, ofs, &type);
1075   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1076   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
1077   {
1078     /* can't decode or no integer (error-index) */
1079     snmp_inc_snmpinasnparseerrs();
1080     return ERR_ARG;
1081   }
1082   /* must be 0 for incoming requests.
1083      decode anyway to catch bad integers (and dirty tricks) */
1084   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
1085   if (derr != ERR_OK)
1086   {
1087     /* can't decode */
1088     snmp_inc_snmpinasnparseerrs();
1089     return ERR_ARG;
1090   }
1091   ofs += (1 + len_octets + len);
1092   *ofs_ret = ofs;
1093   return ERR_OK;
1094 }
1095
1096 static err_t
1097 snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
1098 {
1099   err_t derr;
1100   u16_t len, vb_len;
1101   u8_t  len_octets;
1102   u8_t type;
1103
1104   /* variable binding list */
1105   snmp_asn1_dec_type(p, ofs, &type);
1106   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
1107   if ((derr != ERR_OK) ||
1108       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
1109   {
1110     snmp_inc_snmpinasnparseerrs();
1111     return ERR_ARG;
1112   }
1113   ofs += (1 + len_octets);
1114
1115   /* start with empty list */
1116   m_stat->invb.count = 0;
1117   m_stat->invb.head = NULL;
1118   m_stat->invb.tail = NULL;
1119
1120   while (vb_len > 0)
1121   {
1122     struct snmp_obj_id oid, oid_value;
1123     struct snmp_varbind *vb;
1124
1125     snmp_asn1_dec_type(p, ofs, &type);
1126     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1127     if ((derr != ERR_OK) ||
1128         (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
1129         (len == 0) || (len > vb_len))
1130     {
1131       snmp_inc_snmpinasnparseerrs();
1132       /* free varbinds (if available) */
1133       snmp_varbind_list_free(&m_stat->invb);
1134       return ERR_ARG;
1135     }
1136     ofs += (1 + len_octets);
1137     vb_len -= (1 + len_octets);
1138
1139     snmp_asn1_dec_type(p, ofs, &type);
1140     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1141     if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
1142     {
1143       /* can't decode object name length */
1144       snmp_inc_snmpinasnparseerrs();
1145       /* free varbinds (if available) */
1146       snmp_varbind_list_free(&m_stat->invb);
1147       return ERR_ARG;
1148     }
1149     derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
1150     if (derr != ERR_OK)
1151     {
1152       /* can't decode object name */
1153       snmp_inc_snmpinasnparseerrs();
1154       /* free varbinds (if available) */
1155       snmp_varbind_list_free(&m_stat->invb);
1156       return ERR_ARG;
1157     }
1158     ofs += (1 + len_octets + len);
1159     vb_len -= (1 + len_octets + len);
1160
1161     snmp_asn1_dec_type(p, ofs, &type);
1162     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1163     if (derr != ERR_OK)
1164     {
1165       /* can't decode object value length */
1166       snmp_inc_snmpinasnparseerrs();
1167       /* free varbinds (if available) */
1168       snmp_varbind_list_free(&m_stat->invb);
1169       return ERR_ARG;
1170     }
1171
1172     switch (type)
1173     {
1174       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
1175         vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
1176         if (vb != NULL)
1177         {
1178           s32_t *vptr = (s32_t*)vb->value;
1179
1180           derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
1181           snmp_varbind_tail_add(&m_stat->invb, vb);
1182         }
1183         else
1184         {
1185           derr = ERR_ARG;
1186         }
1187         break;
1188       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
1189       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
1190       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
1191         vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
1192         if (vb != NULL)
1193         {
1194           u32_t *vptr = (u32_t*)vb->value;
1195
1196           derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
1197           snmp_varbind_tail_add(&m_stat->invb, vb);
1198         }
1199         else
1200         {
1201           derr = ERR_ARG;
1202         }
1203         break;
1204       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
1205       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
1206         LWIP_ASSERT("invalid length", len <= 0xff);
1207         vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
1208         if (vb != NULL)
1209         {
1210           derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
1211           snmp_varbind_tail_add(&m_stat->invb, vb);
1212         }
1213         else
1214         {
1215           derr = ERR_ARG;
1216         }
1217         break;
1218       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
1219         vb = snmp_varbind_alloc(&oid, type, 0);
1220         if (vb != NULL)
1221         {
1222           snmp_varbind_tail_add(&m_stat->invb, vb);
1223           derr = ERR_OK;
1224         }
1225         else
1226         {
1227           derr = ERR_ARG;
1228         }
1229         break;
1230       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
1231         derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
1232         if (derr == ERR_OK)
1233         {
1234           vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
1235           if (vb != NULL)
1236           {
1237             u8_t i = oid_value.len;
1238             s32_t *vptr = (s32_t*)vb->value;
1239
1240             while(i > 0)
1241             {
1242               i--;
1243               vptr[i] = oid_value.id[i];
1244             }
1245             snmp_varbind_tail_add(&m_stat->invb, vb);
1246             derr = ERR_OK;
1247           }
1248           else
1249           {
1250             derr = ERR_ARG;
1251           }
1252         }
1253         break;
1254       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
1255         if (len == 4)
1256         {
1257           /* must be exactly 4 octets! */
1258           vb = snmp_varbind_alloc(&oid, type, 4);
1259           if (vb != NULL)
1260           {
1261             derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
1262             snmp_varbind_tail_add(&m_stat->invb, vb);
1263           }
1264           else
1265           {
1266             derr = ERR_ARG;
1267           }
1268         }
1269         else
1270         {
1271           derr = ERR_ARG;
1272         }
1273         break;
1274       default:
1275         derr = ERR_ARG;
1276         break;
1277     }
1278     if (derr != ERR_OK)
1279     {
1280       snmp_inc_snmpinasnparseerrs();
1281       /* free varbinds (if available) */
1282       snmp_varbind_list_free(&m_stat->invb);
1283       return ERR_ARG;
1284     }
1285     ofs += (1 + len_octets + len);
1286     vb_len -= (1 + len_octets + len);
1287   }
1288
1289   if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
1290   {
1291     snmp_add_snmpintotalsetvars(m_stat->invb.count);
1292   }
1293   else
1294   {
1295     snmp_add_snmpintotalreqvars(m_stat->invb.count);
1296   }
1297
1298   *ofs_ret = ofs;
1299   return ERR_OK;
1300 }
1301
1302 struct snmp_varbind*
1303 snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
1304 {
1305   struct snmp_varbind *vb;
1306
1307   vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
1308   LWIP_ASSERT("vb != NULL",vb != NULL);
1309   if (vb != NULL)
1310   {
1311     u8_t i;
1312
1313     vb->next = NULL;
1314     vb->prev = NULL;
1315     i = oid->len;
1316     vb->ident_len = i;
1317     if (i > 0)
1318     {
1319       LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
1320       /* allocate array of s32_t for our object identifier */
1321       vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
1322       LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL);
1323       if (vb->ident == NULL)
1324       {
1325         memp_free(MEMP_SNMP_VARBIND, vb);
1326         return NULL;
1327       }
1328       while(i > 0)
1329       {
1330         i--;
1331         vb->ident[i] = oid->id[i];
1332       }
1333     }
1334     else
1335     {
1336       /* i == 0, pass zero length object identifier */
1337       vb->ident = NULL;
1338     }
1339     vb->value_type = type;
1340     vb->value_len = len;
1341     if (len > 0)
1342     {
1343       LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
1344       /* allocate raw bytes for our object value */
1345       vb->value = memp_malloc(MEMP_SNMP_VALUE);
1346       LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
1347       if (vb->value == NULL)
1348       {
1349         if (vb->ident != NULL)
1350         {
1351           memp_free(MEMP_SNMP_VALUE, vb->ident);
1352         }
1353         memp_free(MEMP_SNMP_VARBIND, vb);
1354         return NULL;
1355       }
1356     }
1357     else
1358     {
1359       /* ASN1_NUL type, or zero length ASN1_OC_STR */
1360       vb->value = NULL;
1361     }
1362   }
1363   return vb;
1364 }
1365
1366 void
1367 snmp_varbind_free(struct snmp_varbind *vb)
1368 {
1369   if (vb->value != NULL )
1370   {
1371     memp_free(MEMP_SNMP_VALUE, vb->value);
1372   }
1373   if (vb->ident != NULL )
1374   {
1375     memp_free(MEMP_SNMP_VALUE, vb->ident);
1376   }
1377   memp_free(MEMP_SNMP_VARBIND, vb);
1378 }
1379
1380 void
1381 snmp_varbind_list_free(struct snmp_varbind_root *root)
1382 {
1383   struct snmp_varbind *vb, *prev;
1384
1385   vb = root->tail;
1386   while ( vb != NULL )
1387   {
1388     prev = vb->prev;
1389     snmp_varbind_free(vb);
1390     vb = prev;
1391   }
1392   root->count = 0;
1393   root->head = NULL;
1394   root->tail = NULL;
1395 }
1396
1397 void
1398 snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
1399 {
1400   if (root->count == 0)
1401   {
1402     /* add first varbind to list */
1403     root->head = vb;
1404     root->tail = vb;
1405   }
1406   else
1407   {
1408     /* add nth varbind to list tail */
1409     root->tail->next = vb;
1410     vb->prev = root->tail;
1411     root->tail = vb;
1412   }
1413   root->count += 1;
1414 }
1415
1416 struct snmp_varbind*
1417 snmp_varbind_tail_remove(struct snmp_varbind_root *root)
1418 {
1419   struct snmp_varbind* vb;
1420
1421   if (root->count > 0)
1422   {
1423     /* remove tail varbind */
1424     vb = root->tail;
1425     root->tail = vb->prev;
1426     vb->prev->next = NULL;
1427     root->count -= 1;
1428   }
1429   else
1430   {
1431     /* nothing to remove */
1432     vb = NULL;
1433   }
1434   return vb;
1435 }
1436
1437 #endif /* LWIP_SNMP */