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