Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / 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() @todo: use reply value?? */
593     en = msg_ps->ext_mib_node;
594     en->set_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
595
596     /** @todo use set_value_pc() if toobig */
597     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
598     msg_ps->vb_idx += 1;
599   }
600
601   /* test all values before setting */
602   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
603          (msg_ps->vb_idx < msg_ps->invb.count))
604   {
605     struct mib_node *mn;
606     struct snmp_name_ptr np;
607
608     if (msg_ps->vb_idx == 0)
609     {
610       msg_ps->vb_ptr = msg_ps->invb.head;
611     }
612     else
613     {
614       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
615     }
616     /** test object identifier for .iso.org.dod.internet prefix */
617     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
618     {
619       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
620                              msg_ps->vb_ptr->ident + 4, &np);
621       if (mn != NULL)
622       {
623         if (mn->node_type == MIB_NODE_EX)
624         {
625           /* external object */
626           struct mib_external_node *en = (struct mib_external_node*)mn;
627
628           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
629           /* save en && args in msg_ps!! */
630           msg_ps->ext_mib_node = en;
631           msg_ps->ext_name_ptr = np;
632
633           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
634         }
635         else
636         {
637           /* internal object */
638           struct obj_def object_def;
639
640           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
641           mn->get_object_def(np.ident_len, np.ident, &object_def);
642           if (object_def.instance != MIB_OBJECT_NONE)
643           {
644             mn = mn;
645           }
646           else
647           {
648             /* search failed, object id points to unknown object (nosuchname) */
649             mn = NULL;
650           }
651           if (mn != NULL)
652           {
653             msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
654
655             if (object_def.access == MIB_OBJECT_READ_WRITE)
656             {
657               if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
658                   (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
659               {
660                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
661                 msg_ps->vb_idx += 1;
662               }
663               else
664               {
665                 /* bad value */
666                 snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
667               }
668             }
669             else
670             {
671               /* object not available for set */
672               snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
673             }
674           }
675         }
676       }
677     }
678     else
679     {
680       mn = NULL;
681     }
682     if (mn == NULL)
683     {
684       /* mn == NULL, noSuchName */
685       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
686     }
687   }
688
689   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
690       (msg_ps->vb_idx == msg_ps->invb.count))
691   {
692     msg_ps->vb_idx = 0;
693     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
694   }
695
696   /* set all values "atomically" (be as "atomic" as possible) */
697   while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
698          (msg_ps->vb_idx < msg_ps->invb.count))
699   {
700     struct mib_node *mn;
701     struct snmp_name_ptr np;
702
703     if (msg_ps->vb_idx == 0)
704     {
705       msg_ps->vb_ptr = msg_ps->invb.head;
706     }
707     else
708     {
709       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
710     }
711     /* skip iso prefix test, was done previously while settesting() */
712     mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
713                            msg_ps->vb_ptr->ident + 4, &np);
714     /* check if object is still available
715        (e.g. external hot-plug thingy present?) */
716     if (mn != NULL)
717     {
718       if (mn->node_type == MIB_NODE_EX)
719       {
720         /* external object */
721         struct mib_external_node *en = (struct mib_external_node*)mn;
722
723         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
724         /* save en && args in msg_ps!! */
725         msg_ps->ext_mib_node = en;
726         msg_ps->ext_name_ptr = np;
727
728         en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
729       }
730       else
731       {
732         /* internal object */
733         struct obj_def object_def;
734
735         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
736         mn->get_object_def(np.ident_len, np.ident, &object_def);
737         msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
738         mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
739         msg_ps->vb_idx += 1;
740       }
741     }
742   }
743   if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
744       (msg_ps->vb_idx == msg_ps->invb.count))
745   {
746     /* simply echo the input if we can set it
747        @todo do we need to return the actual value?
748        e.g. if value is silently modified or behaves sticky? */
749     msg_ps->outvb = msg_ps->invb;
750     msg_ps->invb.head = NULL;
751     msg_ps->invb.tail = NULL;
752     msg_ps->invb.count = 0;
753     snmp_ok_response(msg_ps);
754   }
755 }
756
757
758 /**
759  * Handle one internal or external event.
760  * Called for one async event. (recv external/private answer)
761  *
762  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
763  */
764 void
765 snmp_msg_event(u8_t request_id)
766 {
767   struct snmp_msg_pstat *msg_ps;
768
769   if (request_id < SNMP_CONCURRENT_REQUESTS)
770   {
771     msg_ps = &msg_input_list[request_id];
772     if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
773     {
774       snmp_msg_getnext_event(request_id, msg_ps);
775     }
776     else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
777     {
778       snmp_msg_get_event(request_id, msg_ps);
779     }
780     else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
781     {
782       snmp_msg_set_event(request_id, msg_ps);
783     }
784   }
785 }
786
787
788 /* lwIP UDP receive callback function */
789 static void
790 snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
791 {
792   struct udp_hdr *udphdr;
793
794   /* suppress unused argument warning */
795   LWIP_UNUSED_ARG(arg);
796   /* peek in the UDP header (goto IP payload) */
797   if(pbuf_header(p, UDP_HLEN)){
798     LWIP_ASSERT("Can't move to UDP header", 0);
799     pbuf_free(p);
800     return;
801   }
802   udphdr = p->payload;
803
804   /* check if datagram is really directed at us (including broadcast requests) */
805   if ((pcb == snmp1_pcb) && (ntohs(udphdr->dest) == SNMP_IN_PORT))
806   {
807     struct snmp_msg_pstat *msg_ps;
808     u8_t req_idx;
809
810     /* traverse input message process list, look for SNMP_MSG_EMPTY */
811     msg_ps = &msg_input_list[0];
812     req_idx = 0;
813     while ((req_idx<SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
814     {
815       req_idx++;
816       msg_ps++;
817     }
818     if (req_idx != SNMP_CONCURRENT_REQUESTS)
819     {
820       err_t err_ret;
821       u16_t payload_len;
822       u16_t payload_ofs;
823       u16_t varbind_ofs = 0;
824
825       /* accepting request */
826       snmp_inc_snmpinpkts();
827       /* record used 'protocol control block' */
828       msg_ps->pcb = pcb;
829       /* source address (network order) */
830       msg_ps->sip = *addr;
831       /* source port (host order (lwIP oddity)) */
832       msg_ps->sp = port;
833       /* read UDP payload length from UDP header */
834       payload_len = ntohs(udphdr->len) - UDP_HLEN;
835
836       /* adjust to UDP payload */
837       payload_ofs = UDP_HLEN;
838
839       /* check total length, version, community, pdu type */
840       err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
841       if (((msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) ||
842            (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) ||
843            (msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)) &&
844           ((msg_ps->error_status == SNMP_ES_NOERROR) &&
845            (msg_ps->error_index == 0)) )
846       {
847         /* Only accept requests and requests without error (be robust) */
848         err_ret = err_ret;
849       }
850       else
851       {
852         /* Reject response and trap headers or error requests as input! */
853         err_ret = ERR_ARG;
854       }
855       if (err_ret == ERR_OK)
856       {
857         LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
858
859         /* Builds a list of variable bindings. Copy the varbinds from the pbuf
860           chain to glue them when these are divided over two or more pbuf's. */
861         err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
862         if ((err_ret == ERR_OK) && (msg_ps->invb.count > 0))
863         {
864           /* we've decoded the incoming message, release input msg now */
865           pbuf_free(p);
866
867           msg_ps->error_status = SNMP_ES_NOERROR;
868           msg_ps->error_index = 0;
869           /* find object for each variable binding */
870           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
871           /* first variable binding from list to inspect */
872           msg_ps->vb_idx = 0;
873
874           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
875
876           /* handle input event and as much objects as possible in one go */
877           snmp_msg_event(req_idx);
878         }
879         else
880         {
881           /* varbind-list decode failed, or varbind list empty.
882              drop request silently, do not return error!
883              (errors are only returned for a specific varbind failure) */
884           pbuf_free(p);
885           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
886         }
887       }
888       else
889       {
890         /* header check failed
891            drop request silently, do not return error! */
892         pbuf_free(p);
893         LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
894       }
895     }
896     else
897     {
898       /* exceeding number of concurrent requests */
899       pbuf_free(p);
900     }
901   }
902   else
903   {
904     /* datagram not for us */
905     pbuf_free(p);
906   }
907 }
908
909 /**
910  * Checks and decodes incoming SNMP message header, logs header errors.
911  *
912  * @param p points to pbuf chain of SNMP message (UDP payload)
913  * @param ofs points to first octet of SNMP message
914  * @param pdu_len the length of the UDP payload
915  * @param ofs_ret returns the ofset of the variable bindings
916  * @param m_stat points to the current message request state return
917  * @return
918  * - ERR_OK SNMP header is sane and accepted
919  * - ERR_ARG SNMP header is either malformed or rejected
920  */
921 static err_t
922 snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
923 {
924   err_t derr;
925   u16_t len, ofs_base;
926   u8_t  len_octets;
927   u8_t  type;
928   s32_t version;
929
930   ofs_base = ofs;
931   snmp_asn1_dec_type(p, ofs, &type);
932   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
933   if ((derr != ERR_OK) ||
934       (pdu_len != (1 + len_octets + len)) ||
935       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
936   {
937     snmp_inc_snmpinasnparseerrs();
938     return ERR_ARG;
939   }
940   ofs += (1 + len_octets);
941   snmp_asn1_dec_type(p, ofs, &type);
942   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
943   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
944   {
945     /* can't decode or no integer (version) */
946     snmp_inc_snmpinasnparseerrs();
947     return ERR_ARG;
948   }
949   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
950   if (derr != ERR_OK)
951   {
952     /* can't decode */
953     snmp_inc_snmpinasnparseerrs();
954     return ERR_ARG;
955   }
956   if (version != 0)
957   {
958     /* not version 1 */
959     snmp_inc_snmpinbadversions();
960     return ERR_ARG;
961   }
962   ofs += (1 + len_octets + len);
963   snmp_asn1_dec_type(p, ofs, &type);
964   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
965   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
966   {
967     /* can't decode or no octet string (community) */
968     snmp_inc_snmpinasnparseerrs();
969     return ERR_ARG;
970   }
971   derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
972   if (derr != ERR_OK)
973   {
974     snmp_inc_snmpinasnparseerrs();
975     return ERR_ARG;
976   }
977   /* add zero terminator */
978   len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
979   m_stat->community[len] = 0;
980   m_stat->com_strlen = len;
981   if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
982   {
983     /** @todo: move this if we need to check more names */
984     snmp_inc_snmpinbadcommunitynames();
985     snmp_authfail_trap();
986     return ERR_ARG;
987   }
988   ofs += (1 + len_octets + len);
989   snmp_asn1_dec_type(p, ofs, &type);
990   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
991   if (derr != ERR_OK)
992   {
993     snmp_inc_snmpinasnparseerrs();
994     return ERR_ARG;
995   }
996   switch(type)
997   {
998     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
999       /* GetRequest PDU */
1000       snmp_inc_snmpingetrequests();
1001       derr = ERR_OK;
1002       break;
1003     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
1004       /* GetNextRequest PDU */
1005       snmp_inc_snmpingetnexts();
1006       derr = ERR_OK;
1007       break;
1008     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
1009       /* GetResponse PDU */
1010       snmp_inc_snmpingetresponses();
1011       derr = ERR_ARG;
1012       break;
1013     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
1014       /* SetRequest PDU */
1015       snmp_inc_snmpinsetrequests();
1016       derr = ERR_OK;
1017       break;
1018     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
1019       /* Trap PDU */
1020       snmp_inc_snmpintraps();
1021       derr = ERR_ARG;
1022       break;
1023     default:
1024       snmp_inc_snmpinasnparseerrs();
1025       derr = ERR_ARG;
1026       break;
1027   }
1028   if (derr != ERR_OK)
1029   {
1030     /* unsupported input PDU for this agent (no parse error) */
1031     return ERR_ARG;
1032   }
1033   m_stat->rt = type & 0x1F;
1034   ofs += (1 + len_octets);
1035   if (len != (pdu_len - (ofs - ofs_base)))
1036   {
1037     /* decoded PDU length does not equal actual payload length */
1038     snmp_inc_snmpinasnparseerrs();
1039     return ERR_ARG;
1040   }
1041   snmp_asn1_dec_type(p, ofs, &type);
1042   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1043   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
1044   {
1045     /* can't decode or no integer (request ID) */
1046     snmp_inc_snmpinasnparseerrs();
1047     return ERR_ARG;
1048   }
1049   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
1050   if (derr != ERR_OK)
1051   {
1052     /* can't decode */
1053     snmp_inc_snmpinasnparseerrs();
1054     return ERR_ARG;
1055   }
1056   ofs += (1 + len_octets + len);
1057   snmp_asn1_dec_type(p, ofs, &type);
1058   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1059   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
1060   {
1061     /* can't decode or no integer (error-status) */
1062     snmp_inc_snmpinasnparseerrs();
1063     return ERR_ARG;
1064   }
1065   /* must be noError (0) for incoming requests.
1066      log errors for mib-2 completeness and for debug purposes */
1067   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
1068   if (derr != ERR_OK)
1069   {
1070     /* can't decode */
1071     snmp_inc_snmpinasnparseerrs();
1072     return ERR_ARG;
1073   }
1074   switch (m_stat->error_status)
1075   {
1076     case SNMP_ES_TOOBIG:
1077       snmp_inc_snmpintoobigs();
1078       break;
1079     case SNMP_ES_NOSUCHNAME:
1080       snmp_inc_snmpinnosuchnames();
1081       break;
1082     case SNMP_ES_BADVALUE:
1083       snmp_inc_snmpinbadvalues();
1084       break;
1085     case SNMP_ES_READONLY:
1086       snmp_inc_snmpinreadonlys();
1087       break;
1088     case SNMP_ES_GENERROR:
1089       snmp_inc_snmpingenerrs();
1090       break;
1091   }
1092   ofs += (1 + len_octets + len);
1093   snmp_asn1_dec_type(p, ofs, &type);
1094   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1095   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
1096   {
1097     /* can't decode or no integer (error-index) */
1098     snmp_inc_snmpinasnparseerrs();
1099     return ERR_ARG;
1100   }
1101   /* must be 0 for incoming requests.
1102      decode anyway to catch bad integers (and dirty tricks) */
1103   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
1104   if (derr != ERR_OK)
1105   {
1106     /* can't decode */
1107     snmp_inc_snmpinasnparseerrs();
1108     return ERR_ARG;
1109   }
1110   ofs += (1 + len_octets + len);
1111   *ofs_ret = ofs;
1112   return ERR_OK;
1113 }
1114
1115 static err_t
1116 snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
1117 {
1118   err_t derr;
1119   u16_t len, vb_len;
1120   u8_t  len_octets;
1121   u8_t type;
1122
1123   /* variable binding list */
1124   snmp_asn1_dec_type(p, ofs, &type);
1125   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
1126   if ((derr != ERR_OK) ||
1127       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
1128   {
1129     snmp_inc_snmpinasnparseerrs();
1130     return ERR_ARG;
1131   }
1132   ofs += (1 + len_octets);
1133
1134   /* start with empty list */
1135   m_stat->invb.count = 0;
1136   m_stat->invb.head = NULL;
1137   m_stat->invb.tail = NULL;
1138
1139   while (vb_len > 0)
1140   {
1141     struct snmp_obj_id oid, oid_value;
1142     struct snmp_varbind *vb;
1143
1144     snmp_asn1_dec_type(p, ofs, &type);
1145     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1146     if ((derr != ERR_OK) ||
1147         (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
1148         (len <= 0) || (len > vb_len))
1149     {
1150       snmp_inc_snmpinasnparseerrs();
1151       /* free varbinds (if available) */
1152       snmp_varbind_list_free(&m_stat->invb);
1153       return ERR_ARG;
1154     }
1155     ofs += (1 + len_octets);
1156     vb_len -= (1 + len_octets);
1157
1158     snmp_asn1_dec_type(p, ofs, &type);
1159     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1160     if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
1161     {
1162       /* can't decode object name length */
1163       snmp_inc_snmpinasnparseerrs();
1164       /* free varbinds (if available) */
1165       snmp_varbind_list_free(&m_stat->invb);
1166       return ERR_ARG;
1167     }
1168     derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
1169     if (derr != ERR_OK)
1170     {
1171       /* can't decode object name */
1172       snmp_inc_snmpinasnparseerrs();
1173       /* free varbinds (if available) */
1174       snmp_varbind_list_free(&m_stat->invb);
1175       return ERR_ARG;
1176     }
1177     ofs += (1 + len_octets + len);
1178     vb_len -= (1 + len_octets + len);
1179
1180     snmp_asn1_dec_type(p, ofs, &type);
1181     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
1182     if (derr != ERR_OK)
1183     {
1184       /* can't decode object value length */
1185       snmp_inc_snmpinasnparseerrs();
1186       /* free varbinds (if available) */
1187       snmp_varbind_list_free(&m_stat->invb);
1188       return ERR_ARG;
1189     }
1190
1191     switch (type)
1192     {
1193       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
1194         vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
1195         if (vb != NULL)
1196         {
1197           s32_t *vptr = vb->value;
1198
1199           derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
1200           snmp_varbind_tail_add(&m_stat->invb, vb);
1201         }
1202         else
1203         {
1204           derr = ERR_ARG;
1205         }
1206         break;
1207       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
1208       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
1209       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
1210         vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
1211         if (vb != NULL)
1212         {
1213           u32_t *vptr = vb->value;
1214
1215           derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
1216           snmp_varbind_tail_add(&m_stat->invb, vb);
1217         }
1218         else
1219         {
1220           derr = ERR_ARG;
1221         }
1222         break;
1223       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
1224       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
1225         vb = snmp_varbind_alloc(&oid, type, len);
1226         if (vb != NULL)
1227         {
1228           derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value);
1229           snmp_varbind_tail_add(&m_stat->invb, vb);
1230         }
1231         else
1232         {
1233           derr = ERR_ARG;
1234         }
1235         break;
1236       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
1237         vb = snmp_varbind_alloc(&oid, type, 0);
1238         if (vb != NULL)
1239         {
1240           snmp_varbind_tail_add(&m_stat->invb, vb);
1241           derr = ERR_OK;
1242         }
1243         else
1244         {
1245           derr = ERR_ARG;
1246         }
1247         break;
1248       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
1249         derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
1250         if (derr == ERR_OK)
1251         {
1252           vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
1253           if (vb != NULL)
1254           {
1255             u8_t i = oid_value.len;
1256             s32_t *vptr = vb->value;
1257
1258             while(i > 0)
1259             {
1260               i--;
1261               vptr[i] = oid_value.id[i];
1262             }
1263             snmp_varbind_tail_add(&m_stat->invb, vb);
1264             derr = ERR_OK;
1265           }
1266           else
1267           {
1268             derr = ERR_ARG;
1269           }
1270         }
1271         break;
1272       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
1273         if (len == 4)
1274         {
1275           /* must be exactly 4 octets! */
1276           vb = snmp_varbind_alloc(&oid, type, 4);
1277           if (vb != NULL)
1278           {
1279             derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value);
1280             snmp_varbind_tail_add(&m_stat->invb, vb);
1281           }
1282           else
1283           {
1284             derr = ERR_ARG;
1285           }
1286         }
1287         else
1288         {
1289           derr = ERR_ARG;
1290         }
1291         break;
1292       default:
1293         derr = ERR_ARG;
1294         break;
1295     }
1296     if (derr != ERR_OK)
1297     {
1298       snmp_inc_snmpinasnparseerrs();
1299       /* free varbinds (if available) */
1300       snmp_varbind_list_free(&m_stat->invb);
1301       return ERR_ARG;
1302     }
1303     ofs += (1 + len_octets + len);
1304     vb_len -= (1 + len_octets + len);
1305   }
1306
1307   if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
1308   {
1309     snmp_add_snmpintotalsetvars(m_stat->invb.count);
1310   }
1311   else
1312   {
1313     snmp_add_snmpintotalreqvars(m_stat->invb.count);
1314   }
1315
1316   *ofs_ret = ofs;
1317   return ERR_OK;
1318 }
1319
1320 struct snmp_varbind*
1321 snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
1322 {
1323   struct snmp_varbind *vb;
1324
1325   vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
1326   LWIP_ASSERT("vb != NULL",vb != NULL);
1327   if (vb != NULL)
1328   {
1329     u8_t i;
1330
1331     vb->next = NULL;
1332     vb->prev = NULL;
1333     i = oid->len;
1334     vb->ident_len = i;
1335     if (i > 0)
1336     {
1337       /* allocate array of s32_t for our object identifier */
1338       vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i);
1339       LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL);
1340       if (vb->ident == NULL)
1341       {
1342         mem_free(vb);
1343         return NULL;
1344       }
1345       while(i > 0)
1346       {
1347         i--;
1348         vb->ident[i] = oid->id[i];
1349       }
1350     }
1351     else
1352     {
1353       /* i == 0, pass zero length object identifier */
1354       vb->ident = NULL;
1355     }
1356     vb->value_type = type;
1357     vb->value_len = len;
1358     if (len > 0)
1359     {
1360       /* allocate raw bytes for our object value */
1361       vb->value = mem_malloc(len);
1362       LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
1363       if (vb->value == NULL)
1364       {
1365         if (vb->ident != NULL)
1366         {
1367           mem_free(vb->ident);
1368         }
1369         mem_free(vb);
1370         return NULL;
1371       }
1372     }
1373     else
1374     {
1375       /* ASN1_NUL type, or zero length ASN1_OC_STR */
1376       vb->value = NULL;
1377     }
1378   }
1379   return vb;
1380 }
1381
1382 void
1383 snmp_varbind_free(struct snmp_varbind *vb)
1384 {
1385   if (vb->value != NULL )
1386   {
1387     mem_free(vb->value);
1388   }
1389   if (vb->ident != NULL )
1390   {
1391     mem_free(vb->ident);
1392   }
1393   mem_free(vb);
1394 }
1395
1396 void
1397 snmp_varbind_list_free(struct snmp_varbind_root *root)
1398 {
1399   struct snmp_varbind *vb, *prev;
1400
1401   vb = root->tail;
1402   while ( vb != NULL )
1403   {
1404     prev = vb->prev;
1405     snmp_varbind_free(vb);
1406     vb = prev;
1407   }
1408   root->count = 0;
1409   root->head = NULL;
1410   root->tail = NULL;
1411 }
1412
1413 void
1414 snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
1415 {
1416   if (root->count == 0)
1417   {
1418     /* add first varbind to list */
1419     root->head = vb;
1420     root->tail = vb;
1421   }
1422   else
1423   {
1424     /* add nth varbind to list tail */
1425     root->tail->next = vb;
1426     vb->prev = root->tail;
1427     root->tail = vb;
1428   }
1429   root->count += 1;
1430 }
1431
1432 struct snmp_varbind*
1433 snmp_varbind_tail_remove(struct snmp_varbind_root *root)
1434 {
1435   struct snmp_varbind* vb;
1436
1437   if (root->count > 0)
1438   {
1439     /* remove tail varbind */
1440     vb = root->tail;
1441     root->tail = vb->prev;
1442     vb->prev->next = NULL;
1443     root->count -= 1;
1444   }
1445   else
1446   {
1447     /* nothing to remove */
1448     vb = NULL;
1449   }
1450   return vb;
1451 }
1452
1453 #endif /* LWIP_SNMP */