A version of LWIP, provided for backwards compatibility.

Dependents:   AA_DemoBoard DemoBoard HelloServerDemo DemoBoard_RangeIndicator ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers msg_in.c Source File

msg_in.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * SNMP input message processing (RFC1157).
00004  */
00005 
00006 /*
00007  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without modification,
00011  * are permitted provided that the following conditions are met:
00012  *
00013  * 1. Redistributions of source code must retain the above copyright notice,
00014  *    this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright notice,
00016  *    this list of conditions and the following disclaimer in the documentation
00017  *    and/or other materials provided with the distribution.
00018  * 3. The name of the author may not be used to endorse or promote products
00019  *    derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00023  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00024  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00025  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00026  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00029  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00030  * OF SUCH DAMAGE.
00031  *
00032  * Author: Christiaan Simons <christiaan.simons@axon.tv>
00033  */
00034 
00035 #include "lwip/opt.h"
00036 
00037 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
00038 
00039 #include "lwip/ip_addr.h"
00040 #include "lwip/mem.h"
00041 #include "lwip/udp.h"
00042 #include "lwip/stats.h"
00043 #include "lwip/snmp.h"
00044 #include "lwip/snmp_asn1.h"
00045 #include "lwip/snmp_msg.h"
00046 #include "lwip/snmp_structs.h"
00047 
00048 #include <string.h>
00049 
00050 /* public (non-static) constants */
00051 /** SNMP v1 == 0 */
00052 const s32_t snmp_version = 0;
00053 /** default SNMP community string */
00054 const char snmp_publiccommunity[7] = "public";
00055 
00056 /* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
00057 struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
00058 /* UDP Protocol Control Block */
00059 struct udp_pcb *snmp1_pcb;
00060 
00061 static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port);
00062 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);
00063 static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
00064 
00065 
00066 /**
00067  * Starts SNMP Agent.
00068  * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
00069  */
00070 void
00071 snmp_init(void)
00072 {
00073   struct snmp_msg_pstat *msg_ps;
00074   u8_t i;
00075 
00076   snmp1_pcb = udp_new();
00077   if (snmp1_pcb != NULL)
00078   {
00079     udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
00080     udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
00081   }
00082   msg_ps = &msg_input_list[0];
00083   for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
00084   {
00085     msg_ps->state = SNMP_MSG_EMPTY;
00086     msg_ps->error_index = 0;
00087     msg_ps->error_status = SNMP_ES_NOERROR;
00088     msg_ps++;
00089   }
00090   trap_msg.pcb = snmp1_pcb;
00091   /* The coldstart trap will only be output
00092      if our outgoing interface is up & configured  */
00093   snmp_coldstart_trap();
00094 }
00095 
00096 static void
00097 snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
00098 {
00099   snmp_varbind_list_free(&msg_ps->outvb);
00100   msg_ps->outvb = msg_ps->invb;
00101   msg_ps->invb.head = NULL;
00102   msg_ps->invb.tail = NULL;
00103   msg_ps->invb.count = 0;
00104   msg_ps->error_status = error;
00105   msg_ps->error_index = 1 + msg_ps->vb_idx;
00106   snmp_send_response(msg_ps);
00107   snmp_varbind_list_free(&msg_ps->outvb);
00108   msg_ps->state = SNMP_MSG_EMPTY;
00109 }
00110 
00111 static void
00112 snmp_ok_response(struct snmp_msg_pstat *msg_ps)
00113 {
00114   err_t err_ret;
00115 
00116   err_ret = snmp_send_response(msg_ps);
00117   if (err_ret == ERR_MEM)
00118   {
00119     /* serious memory problem, can't return tooBig */
00120   }
00121   else
00122   {
00123     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
00124   }
00125   /* free varbinds (if available) */
00126   snmp_varbind_list_free(&msg_ps->invb);
00127   snmp_varbind_list_free(&msg_ps->outvb);
00128   msg_ps->state = SNMP_MSG_EMPTY;
00129 }
00130 
00131 /**
00132  * Service an internal or external event for SNMP GET.
00133  *
00134  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00135  * @param msg_ps points to the assosicated message process state
00136  */
00137 static void
00138 snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
00139 {
00140   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
00141 
00142   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
00143   {
00144     struct mib_external_node *en;
00145     struct snmp_name_ptr np;
00146 
00147     /* get_object_def() answer*/
00148     en = msg_ps->ext_mib_node;
00149     np = msg_ps->ext_name_ptr;
00150 
00151     /* translate answer into a known lifeform */
00152     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
00153     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00154     {
00155       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
00156       en->get_value_q(request_id, &msg_ps->ext_object_def);
00157     }
00158     else
00159     {
00160       en->get_object_def_pc(request_id, np.ident_len, np.ident);
00161       /* search failed, object id points to unknown object (nosuchname) */
00162       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00163     }
00164   }
00165   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
00166   {
00167     struct mib_external_node *en;
00168     struct snmp_varbind *vb;
00169 
00170     /* get_value() answer */
00171     en = msg_ps->ext_mib_node;
00172 
00173     /* allocate output varbind */
00174     vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
00175     LWIP_ASSERT("vb != NULL",vb != NULL);
00176     if (vb != NULL)
00177     {
00178       vb->next = NULL;
00179       vb->prev = NULL;
00180 
00181       /* move name from invb to outvb */
00182       vb->ident = msg_ps->vb_ptr->ident;
00183       vb->ident_len = msg_ps->vb_ptr->ident_len;
00184       /* ensure this memory is refereced once only */
00185       msg_ps->vb_ptr->ident = NULL;
00186       msg_ps->vb_ptr->ident_len = 0;
00187 
00188       vb->value_type = msg_ps->ext_object_def.asn_type;
00189       vb->value_len =  msg_ps->ext_object_def.v_len;
00190       if (vb->value_len > 0)
00191       {
00192         vb->value = mem_malloc(vb->value_len);
00193         LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
00194         if (vb->value != NULL)
00195         {
00196           en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
00197           snmp_varbind_tail_add(&msg_ps->outvb, vb);
00198           /* search again (if vb_idx < msg_ps->invb.count) */
00199           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00200           msg_ps->vb_idx += 1;
00201         }
00202         else
00203         {
00204           en->get_value_pc(request_id, &msg_ps->ext_object_def);
00205           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
00206           msg_ps->vb_ptr->ident = vb->ident;
00207           msg_ps->vb_ptr->ident_len = vb->ident_len;
00208           mem_free(vb);
00209           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00210         }
00211       }
00212       else
00213       {
00214         /* vb->value_len == 0, empty value (e.g. empty string) */
00215         en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
00216         vb->value = NULL;
00217         snmp_varbind_tail_add(&msg_ps->outvb, vb);
00218         /* search again (if vb_idx < msg_ps->invb.count) */
00219         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00220         msg_ps->vb_idx += 1;
00221       }
00222     }
00223     else
00224     {
00225       en->get_value_pc(request_id, &msg_ps->ext_object_def);
00226       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
00227       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00228     }
00229   }
00230 
00231   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00232          (msg_ps->vb_idx < msg_ps->invb.count))
00233   {
00234     struct mib_node *mn;
00235     struct snmp_name_ptr np;
00236 
00237     if (msg_ps->vb_idx == 0)
00238     {
00239       msg_ps->vb_ptr = msg_ps->invb.head;
00240     }
00241     else
00242     {
00243       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00244     }
00245     /** test object identifier for .iso.org.dod.internet prefix */
00246     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
00247     {
00248       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
00249                              msg_ps->vb_ptr->ident + 4, &np);
00250       if (mn != NULL)
00251       {
00252         if (mn->node_type == MIB_NODE_EX)
00253         {
00254           /* external object */
00255           struct mib_external_node *en = (struct mib_external_node*)mn;
00256 
00257           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
00258           /* save en && args in msg_ps!! */
00259           msg_ps->ext_mib_node = en;
00260           msg_ps->ext_name_ptr = np;
00261 
00262           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
00263         }
00264         else
00265         {
00266           /* internal object */
00267           struct obj_def object_def;
00268 
00269           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
00270           mn->get_object_def(np.ident_len, np.ident, &object_def);
00271           if (object_def.instance != MIB_OBJECT_NONE)
00272           {
00273             mn = mn;
00274           }
00275           else
00276           {
00277             /* search failed, object id points to unknown object (nosuchname) */
00278             mn =  NULL;
00279           }
00280           if (mn != NULL)
00281           {
00282             struct snmp_varbind *vb;
00283 
00284             msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
00285             /* allocate output varbind */
00286             vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
00287             LWIP_ASSERT("vb != NULL",vb != NULL);
00288             if (vb != NULL)
00289             {
00290               vb->next = NULL;
00291               vb->prev = NULL;
00292 
00293               /* move name from invb to outvb */
00294               vb->ident = msg_ps->vb_ptr->ident;
00295               vb->ident_len = msg_ps->vb_ptr->ident_len;
00296               /* ensure this memory is refereced once only */
00297               msg_ps->vb_ptr->ident = NULL;
00298               msg_ps->vb_ptr->ident_len = 0;
00299 
00300               vb->value_type = object_def.asn_type;
00301               vb->value_len = object_def.v_len;
00302               if (vb->value_len > 0)
00303               {
00304                 vb->value = mem_malloc(vb->value_len);
00305                 LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
00306                 if (vb->value != NULL)
00307                 {
00308                   mn->get_value(&object_def, vb->value_len, vb->value);
00309                   snmp_varbind_tail_add(&msg_ps->outvb, vb);
00310                   msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00311                   msg_ps->vb_idx += 1;
00312                 }
00313                 else
00314                 {
00315                   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
00316                   msg_ps->vb_ptr->ident = vb->ident;
00317                   msg_ps->vb_ptr->ident_len = vb->ident_len;
00318                   mem_free(vb);
00319                   snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00320                 }
00321               }
00322               else
00323               {
00324                 /* vb->value_len == 0, empty value (e.g. empty string) */
00325                 vb->value = NULL;
00326                 snmp_varbind_tail_add(&msg_ps->outvb, vb);
00327                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00328                 msg_ps->vb_idx += 1;
00329               }
00330             }
00331             else
00332             {
00333               LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
00334               snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00335             }
00336           }
00337         }
00338       }
00339     }
00340     else
00341     {
00342       mn = NULL;
00343     }
00344     if (mn == NULL)
00345     {
00346       /* mn == NULL, noSuchName */
00347       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00348     }
00349   }
00350   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00351       (msg_ps->vb_idx == msg_ps->invb.count))
00352   {
00353     snmp_ok_response(msg_ps);
00354   }
00355 }
00356 
00357 /**
00358  * Service an internal or external event for SNMP GETNEXT.
00359  *
00360  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00361  * @param msg_ps points to the assosicated message process state
00362  */
00363 static void
00364 snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
00365 {
00366   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
00367 
00368   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
00369   {
00370     struct mib_external_node *en;
00371 
00372     /* get_object_def() answer*/
00373     en = msg_ps->ext_mib_node;
00374 
00375     /* translate answer into a known lifeform */
00376     en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
00377     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00378     {
00379       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
00380       en->get_value_q(request_id, &msg_ps->ext_object_def);
00381     }
00382     else
00383     {
00384       en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
00385       /* search failed, object id points to unknown object (nosuchname) */
00386       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00387     }
00388   }
00389   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
00390   {
00391     struct mib_external_node *en;
00392     struct snmp_varbind *vb;
00393 
00394     /* get_value() answer */
00395     en = msg_ps->ext_mib_node;
00396 
00397     vb = snmp_varbind_alloc(&msg_ps->ext_oid,
00398                             msg_ps->ext_object_def.asn_type,
00399                             msg_ps->ext_object_def.v_len);
00400     if (vb != NULL)
00401     {
00402       en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
00403       snmp_varbind_tail_add(&msg_ps->outvb, vb);
00404       msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00405       msg_ps->vb_idx += 1;
00406     }
00407     else
00408     {
00409       en->get_value_pc(request_id, &msg_ps->ext_object_def);
00410       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
00411       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00412     }
00413   }
00414 
00415   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00416          (msg_ps->vb_idx < msg_ps->invb.count))
00417   {
00418     struct mib_node *mn;
00419     struct snmp_obj_id oid;
00420 
00421     if (msg_ps->vb_idx == 0)
00422     {
00423       msg_ps->vb_ptr = msg_ps->invb.head;
00424     }
00425     else
00426     {
00427       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00428     }
00429     if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
00430     {
00431       if (msg_ps->vb_ptr->ident_len > 3)
00432       {
00433         /* can offset ident_len and ident */
00434         mn = snmp_expand_tree((struct mib_node*)&internet,
00435                               msg_ps->vb_ptr->ident_len - 4,
00436                               msg_ps->vb_ptr->ident + 4, &oid);
00437       }
00438       else
00439       {
00440         /* can't offset ident_len -4, ident + 4 */
00441         mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
00442       }
00443     }
00444     else
00445     {
00446       mn = NULL;
00447     }
00448     if (mn != NULL)
00449     {
00450       if (mn->node_type == MIB_NODE_EX)
00451       {
00452         /* external object */
00453         struct mib_external_node *en = (struct mib_external_node*)mn;
00454 
00455         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
00456         /* save en && args in msg_ps!! */
00457         msg_ps->ext_mib_node = en;
00458         msg_ps->ext_oid = oid;
00459 
00460         en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
00461       }
00462       else
00463       {
00464         /* internal object */
00465         struct obj_def object_def;
00466         struct snmp_varbind *vb;
00467 
00468         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
00469         mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
00470 
00471         vb = snmp_varbind_alloc(&oid, object_def.asn_type, object_def.v_len);
00472         if (vb != NULL)
00473         {
00474           msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
00475           mn->get_value(&object_def, object_def.v_len, vb->value);
00476           snmp_varbind_tail_add(&msg_ps->outvb, vb);
00477           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00478           msg_ps->vb_idx += 1;
00479         }
00480         else
00481         {
00482           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
00483           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00484         }
00485       }
00486     }
00487     if (mn == NULL)
00488     {
00489       /* mn == NULL, noSuchName */
00490       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00491     }
00492   }
00493   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00494       (msg_ps->vb_idx == msg_ps->invb.count))
00495   {
00496     snmp_ok_response(msg_ps);
00497   }
00498 }
00499 
00500 /**
00501  * Service an internal or external event for SNMP SET.
00502  *
00503  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00504  * @param msg_ps points to the assosicated message process state
00505  */
00506 static void
00507 snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
00508 {
00509   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
00510 
00511   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
00512   {
00513     struct mib_external_node *en;
00514     struct snmp_name_ptr np;
00515 
00516     /* get_object_def() answer*/
00517     en = msg_ps->ext_mib_node;
00518     np = msg_ps->ext_name_ptr;
00519 
00520     /* translate answer into a known lifeform */
00521     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
00522     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00523     {
00524       msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
00525       en->set_test_q(request_id, &msg_ps->ext_object_def);
00526     }
00527     else
00528     {
00529       en->get_object_def_pc(request_id, np.ident_len, np.ident);
00530       /* search failed, object id points to unknown object (nosuchname) */
00531       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00532     }
00533   }
00534   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
00535   {
00536     struct mib_external_node *en;
00537 
00538     /* set_test() answer*/
00539     en = msg_ps->ext_mib_node;
00540 
00541     if (msg_ps->ext_object_def.access == MIB_OBJECT_READ_WRITE)
00542     {
00543        if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
00544            (en->set_test_a(request_id,&msg_ps->ext_object_def,
00545                            msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
00546       {
00547         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00548         msg_ps->vb_idx += 1;
00549       }
00550       else
00551       {
00552         en->set_test_pc(request_id,&msg_ps->ext_object_def);
00553         /* bad value */
00554         snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
00555       }
00556     }
00557     else
00558     {
00559       en->set_test_pc(request_id,&msg_ps->ext_object_def);
00560       /* object not available for set */
00561       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00562     }
00563   }
00564   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
00565   {
00566     struct mib_external_node *en;
00567     struct snmp_name_ptr np;
00568 
00569     /* get_object_def() answer*/
00570     en = msg_ps->ext_mib_node;
00571     np = msg_ps->ext_name_ptr;
00572 
00573     /* translate answer into a known lifeform */
00574     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
00575     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00576     {
00577       msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
00578       en->set_value_q(request_id, &msg_ps->ext_object_def,
00579                       msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
00580     }
00581     else
00582     {
00583       en->get_object_def_pc(request_id, np.ident_len, np.ident);
00584       /* set_value failed, object has disappeared for some odd reason?? */
00585       snmp_error_response(msg_ps,SNMP_ES_GENERROR);
00586     }
00587   }
00588   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
00589   {
00590     struct mib_external_node *en;
00591 
00592     /** set_value_a() */
00593     en = msg_ps->ext_mib_node;
00594     en->set_value_a(request_id, &msg_ps->ext_object_def,
00595       msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
00596 
00597     /** @todo use set_value_pc() if toobig */
00598     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
00599     msg_ps->vb_idx += 1;
00600   }
00601 
00602   /* test all values before setting */
00603   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00604          (msg_ps->vb_idx < msg_ps->invb.count))
00605   {
00606     struct mib_node *mn;
00607     struct snmp_name_ptr np;
00608 
00609     if (msg_ps->vb_idx == 0)
00610     {
00611       msg_ps->vb_ptr = msg_ps->invb.head;
00612     }
00613     else
00614     {
00615       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00616     }
00617     /** test object identifier for .iso.org.dod.internet prefix */
00618     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
00619     {
00620       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
00621                              msg_ps->vb_ptr->ident + 4, &np);
00622       if (mn != NULL)
00623       {
00624         if (mn->node_type == MIB_NODE_EX)
00625         {
00626           /* external object */
00627           struct mib_external_node *en = (struct mib_external_node*)mn;
00628 
00629           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
00630           /* save en && args in msg_ps!! */
00631           msg_ps->ext_mib_node = en;
00632           msg_ps->ext_name_ptr = np;
00633 
00634           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
00635         }
00636         else
00637         {
00638           /* internal object */
00639           struct obj_def object_def;
00640 
00641           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
00642           mn->get_object_def(np.ident_len, np.ident, &object_def);
00643           if (object_def.instance != MIB_OBJECT_NONE)
00644           {
00645             mn = mn;
00646           }
00647           else
00648           {
00649             /* search failed, object id points to unknown object (nosuchname) */
00650             mn = NULL;
00651           }
00652           if (mn != NULL)
00653           {
00654             msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
00655 
00656             if (object_def.access == MIB_OBJECT_READ_WRITE)
00657             {
00658               if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
00659                   (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
00660               {
00661                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00662                 msg_ps->vb_idx += 1;
00663               }
00664               else
00665               {
00666                 /* bad value */
00667                 snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
00668               }
00669             }
00670             else
00671             {
00672               /* object not available for set */
00673               snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00674             }
00675           }
00676         }
00677       }
00678     }
00679     else
00680     {
00681       mn = NULL;
00682     }
00683     if (mn == NULL)
00684     {
00685       /* mn == NULL, noSuchName */
00686       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00687     }
00688   }
00689 
00690   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00691       (msg_ps->vb_idx == msg_ps->invb.count))
00692   {
00693     msg_ps->vb_idx = 0;
00694     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
00695   }
00696 
00697   /* set all values "atomically" (be as "atomic" as possible) */
00698   while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
00699          (msg_ps->vb_idx < msg_ps->invb.count))
00700   {
00701     struct mib_node *mn;
00702     struct snmp_name_ptr np;
00703 
00704     if (msg_ps->vb_idx == 0)
00705     {
00706       msg_ps->vb_ptr = msg_ps->invb.head;
00707     }
00708     else
00709     {
00710       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00711     }
00712     /* skip iso prefix test, was done previously while settesting() */
00713     mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
00714                            msg_ps->vb_ptr->ident + 4, &np);
00715     /* check if object is still available
00716        (e.g. external hot-plug thingy present?) */
00717     if (mn != NULL)
00718     {
00719       if (mn->node_type == MIB_NODE_EX)
00720       {
00721         /* external object */
00722         struct mib_external_node *en = (struct mib_external_node*)mn;
00723 
00724         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
00725         /* save en && args in msg_ps!! */
00726         msg_ps->ext_mib_node = en;
00727         msg_ps->ext_name_ptr = np;
00728 
00729         en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
00730       }
00731       else
00732       {
00733         /* internal object */
00734         struct obj_def object_def;
00735 
00736         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
00737         mn->get_object_def(np.ident_len, np.ident, &object_def);
00738         msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
00739         mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
00740         msg_ps->vb_idx += 1;
00741       }
00742     }
00743   }
00744   if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
00745       (msg_ps->vb_idx == msg_ps->invb.count))
00746   {
00747     /* simply echo the input if we can set it
00748        @todo do we need to return the actual value?
00749        e.g. if value is silently modified or behaves sticky? */
00750     msg_ps->outvb = msg_ps->invb;
00751     msg_ps->invb.head = NULL;
00752     msg_ps->invb.tail = NULL;
00753     msg_ps->invb.count = 0;
00754     snmp_ok_response(msg_ps);
00755   }
00756 }
00757 
00758 
00759 /**
00760  * Handle one internal or external event.
00761  * Called for one async event. (recv external/private answer)
00762  *
00763  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00764  */
00765 void
00766 snmp_msg_event(u8_t request_id)
00767 {
00768   struct snmp_msg_pstat *msg_ps;
00769 
00770   if (request_id < SNMP_CONCURRENT_REQUESTS)
00771   {
00772     msg_ps = &msg_input_list[request_id];
00773     if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
00774     {
00775       snmp_msg_getnext_event(request_id, msg_ps);
00776     }
00777     else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
00778     {
00779       snmp_msg_get_event(request_id, msg_ps);
00780     }
00781     else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
00782     {
00783       snmp_msg_set_event(request_id, msg_ps);
00784     }
00785   }
00786 }
00787 
00788 
00789 /* lwIP UDP receive callback function */
00790 static void
00791 snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
00792 {
00793   struct udp_hdr *udphdr;
00794 
00795   /* suppress unused argument warning */
00796   LWIP_UNUSED_ARG(arg);
00797   /* peek in the UDP header (goto IP payload) */
00798   if(pbuf_header(p, UDP_HLEN)){
00799     LWIP_ASSERT("Can't move to UDP header", 0);
00800     pbuf_free(p);
00801     return;
00802   }
00803   udphdr = p->payload;
00804 
00805   /* check if datagram is really directed at us (including broadcast requests) */
00806   if ((pcb == snmp1_pcb) && (ntohs(udphdr->dest) == SNMP_IN_PORT))
00807   {
00808     struct snmp_msg_pstat *msg_ps;
00809     u8_t req_idx;
00810 
00811     /* traverse input message process list, look for SNMP_MSG_EMPTY */
00812     msg_ps = &msg_input_list[0];
00813     req_idx = 0;
00814     while ((req_idx<SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
00815     {
00816       req_idx++;
00817       msg_ps++;
00818     }
00819     if (req_idx != SNMP_CONCURRENT_REQUESTS)
00820     {
00821       err_t err_ret;
00822       u16_t payload_len;
00823       u16_t payload_ofs;
00824       u16_t varbind_ofs = 0;
00825 
00826       /* accepting request */
00827       snmp_inc_snmpinpkts();
00828       /* record used 'protocol control block' */
00829       msg_ps->pcb = pcb;
00830       /* source address (network order) */
00831       msg_ps->sip = *addr;
00832       /* source port (host order (lwIP oddity)) */
00833       msg_ps->sp = port;
00834       /* read UDP payload length from UDP header */
00835       payload_len = ntohs(udphdr->len) - UDP_HLEN;
00836 
00837       /* adjust to UDP payload */
00838       payload_ofs = UDP_HLEN;
00839 
00840       /* check total length, version, community, pdu type */
00841       err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
00842       if (((msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) ||
00843            (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) ||
00844            (msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)) &&
00845           ((msg_ps->error_status == SNMP_ES_NOERROR) &&
00846            (msg_ps->error_index == 0)) )
00847       {
00848         /* Only accept requests and requests without error (be robust) */
00849         err_ret = err_ret;
00850       }
00851       else
00852       {
00853         /* Reject response and trap headers or error requests as input! */
00854         err_ret = ERR_ARG;
00855       }
00856       if (err_ret == ERR_OK)
00857       {
00858         LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
00859 
00860         /* Builds a list of variable bindings. Copy the varbinds from the pbuf
00861           chain to glue them when these are divided over two or more pbuf's. */
00862         err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
00863         if ((err_ret == ERR_OK) && (msg_ps->invb.count > 0))
00864         {
00865           /* we've decoded the incoming message, release input msg now */
00866           pbuf_free(p);
00867 
00868           msg_ps->error_status = SNMP_ES_NOERROR;
00869           msg_ps->error_index = 0;
00870           /* find object for each variable binding */
00871           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00872           /* first variable binding from list to inspect */
00873           msg_ps->vb_idx = 0;
00874 
00875           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
00876 
00877           /* handle input event and as much objects as possible in one go */
00878           snmp_msg_event(req_idx);
00879         }
00880         else
00881         {
00882           /* varbind-list decode failed, or varbind list empty.
00883              drop request silently, do not return error!
00884              (errors are only returned for a specific varbind failure) */
00885           pbuf_free(p);
00886           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
00887         }
00888       }
00889       else
00890       {
00891         /* header check failed
00892            drop request silently, do not return error! */
00893         pbuf_free(p);
00894         LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
00895       }
00896     }
00897     else
00898     {
00899       /* exceeding number of concurrent requests */
00900       pbuf_free(p);
00901     }
00902   }
00903   else
00904   {
00905     /* datagram not for us */
00906     pbuf_free(p);
00907   }
00908 }
00909 
00910 /**
00911  * Checks and decodes incoming SNMP message header, logs header errors.
00912  *
00913  * @param p points to pbuf chain of SNMP message (UDP payload)
00914  * @param ofs points to first octet of SNMP message
00915  * @param pdu_len the length of the UDP payload
00916  * @param ofs_ret returns the ofset of the variable bindings
00917  * @param m_stat points to the current message request state return
00918  * @return
00919  * - ERR_OK SNMP header is sane and accepted
00920  * - ERR_ARG SNMP header is either malformed or rejected
00921  */
00922 static err_t
00923 snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
00924 {
00925   err_t derr;
00926   u16_t len, ofs_base;
00927   u8_t  len_octets;
00928   u8_t  type;
00929   s32_t version;
00930 
00931   ofs_base = ofs;
00932   snmp_asn1_dec_type(p, ofs, &type);
00933   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00934   if ((derr != ERR_OK) ||
00935       (pdu_len != (1 + len_octets + len)) ||
00936       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
00937   {
00938     snmp_inc_snmpinasnparseerrs();
00939     return ERR_ARG;
00940   }
00941   ofs += (1 + len_octets);
00942   snmp_asn1_dec_type(p, ofs, &type);
00943   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00944   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
00945   {
00946     /* can't decode or no integer (version) */
00947     snmp_inc_snmpinasnparseerrs();
00948     return ERR_ARG;
00949   }
00950   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
00951   if (derr != ERR_OK)
00952   {
00953     /* can't decode */
00954     snmp_inc_snmpinasnparseerrs();
00955     return ERR_ARG;
00956   }
00957   if (version != 0)
00958   {
00959     /* not version 1 */
00960     snmp_inc_snmpinbadversions();
00961     return ERR_ARG;
00962   }
00963   ofs += (1 + len_octets + len);
00964   snmp_asn1_dec_type(p, ofs, &type);
00965   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00966   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
00967   {
00968     /* can't decode or no octet string (community) */
00969     snmp_inc_snmpinasnparseerrs();
00970     return ERR_ARG;
00971   }
00972   derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
00973   if (derr != ERR_OK)
00974   {
00975     snmp_inc_snmpinasnparseerrs();
00976     return ERR_ARG;
00977   }
00978   /* add zero terminator */
00979   len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
00980   m_stat->community[len] = 0;
00981   m_stat->com_strlen = len;
00982   if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
00983   {
00984     /** @todo: move this if we need to check more names */
00985     snmp_inc_snmpinbadcommunitynames();
00986     snmp_authfail_trap();
00987     return ERR_ARG;
00988   }
00989   ofs += (1 + len_octets + len);
00990   snmp_asn1_dec_type(p, ofs, &type);
00991   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00992   if (derr != ERR_OK)
00993   {
00994     snmp_inc_snmpinasnparseerrs();
00995     return ERR_ARG;
00996   }
00997   switch(type)
00998   {
00999     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
01000       /* GetRequest PDU */
01001       snmp_inc_snmpingetrequests();
01002       derr = ERR_OK;
01003       break;
01004     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
01005       /* GetNextRequest PDU */
01006       snmp_inc_snmpingetnexts();
01007       derr = ERR_OK;
01008       break;
01009     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
01010       /* GetResponse PDU */
01011       snmp_inc_snmpingetresponses();
01012       derr = ERR_ARG;
01013       break;
01014     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
01015       /* SetRequest PDU */
01016       snmp_inc_snmpinsetrequests();
01017       derr = ERR_OK;
01018       break;
01019     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
01020       /* Trap PDU */
01021       snmp_inc_snmpintraps();
01022       derr = ERR_ARG;
01023       break;
01024     default:
01025       snmp_inc_snmpinasnparseerrs();
01026       derr = ERR_ARG;
01027       break;
01028   }
01029   if (derr != ERR_OK)
01030   {
01031     /* unsupported input PDU for this agent (no parse error) */
01032     return ERR_ARG;
01033   }
01034   m_stat->rt = type & 0x1F;
01035   ofs += (1 + len_octets);
01036   if (len != (pdu_len - (ofs - ofs_base)))
01037   {
01038     /* decoded PDU length does not equal actual payload length */
01039     snmp_inc_snmpinasnparseerrs();
01040     return ERR_ARG;
01041   }
01042   snmp_asn1_dec_type(p, ofs, &type);
01043   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01044   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
01045   {
01046     /* can't decode or no integer (request ID) */
01047     snmp_inc_snmpinasnparseerrs();
01048     return ERR_ARG;
01049   }
01050   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
01051   if (derr != ERR_OK)
01052   {
01053     /* can't decode */
01054     snmp_inc_snmpinasnparseerrs();
01055     return ERR_ARG;
01056   }
01057   ofs += (1 + len_octets + len);
01058   snmp_asn1_dec_type(p, ofs, &type);
01059   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01060   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
01061   {
01062     /* can't decode or no integer (error-status) */
01063     snmp_inc_snmpinasnparseerrs();
01064     return ERR_ARG;
01065   }
01066   /* must be noError (0) for incoming requests.
01067      log errors for mib-2 completeness and for debug purposes */
01068   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
01069   if (derr != ERR_OK)
01070   {
01071     /* can't decode */
01072     snmp_inc_snmpinasnparseerrs();
01073     return ERR_ARG;
01074   }
01075   switch (m_stat->error_status)
01076   {
01077     case SNMP_ES_TOOBIG:
01078       snmp_inc_snmpintoobigs();
01079       break;
01080     case SNMP_ES_NOSUCHNAME:
01081       snmp_inc_snmpinnosuchnames();
01082       break;
01083     case SNMP_ES_BADVALUE:
01084       snmp_inc_snmpinbadvalues();
01085       break;
01086     case SNMP_ES_READONLY:
01087       snmp_inc_snmpinreadonlys();
01088       break;
01089     case SNMP_ES_GENERROR:
01090       snmp_inc_snmpingenerrs();
01091       break;
01092   }
01093   ofs += (1 + len_octets + len);
01094   snmp_asn1_dec_type(p, ofs, &type);
01095   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01096   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
01097   {
01098     /* can't decode or no integer (error-index) */
01099     snmp_inc_snmpinasnparseerrs();
01100     return ERR_ARG;
01101   }
01102   /* must be 0 for incoming requests.
01103      decode anyway to catch bad integers (and dirty tricks) */
01104   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
01105   if (derr != ERR_OK)
01106   {
01107     /* can't decode */
01108     snmp_inc_snmpinasnparseerrs();
01109     return ERR_ARG;
01110   }
01111   ofs += (1 + len_octets + len);
01112   *ofs_ret = ofs;
01113   return ERR_OK;
01114 }
01115 
01116 static err_t
01117 snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
01118 {
01119   err_t derr;
01120   u16_t len, vb_len;
01121   u8_t  len_octets;
01122   u8_t type;
01123 
01124   /* variable binding list */
01125   snmp_asn1_dec_type(p, ofs, &type);
01126   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
01127   if ((derr != ERR_OK) ||
01128       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
01129   {
01130     snmp_inc_snmpinasnparseerrs();
01131     return ERR_ARG;
01132   }
01133   ofs += (1 + len_octets);
01134 
01135   /* start with empty list */
01136   m_stat->invb.count = 0;
01137   m_stat->invb.head = NULL;
01138   m_stat->invb.tail = NULL;
01139 
01140   while (vb_len > 0)
01141   {
01142     struct snmp_obj_id oid, oid_value;
01143     struct snmp_varbind *vb;
01144 
01145     snmp_asn1_dec_type(p, ofs, &type);
01146     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01147     if ((derr != ERR_OK) ||
01148         (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
01149         (len == 0) || (len > vb_len))
01150     {
01151       snmp_inc_snmpinasnparseerrs();
01152       /* free varbinds (if available) */
01153       snmp_varbind_list_free(&m_stat->invb);
01154       return ERR_ARG;
01155     }
01156     ofs += (1 + len_octets);
01157     vb_len -= (1 + len_octets);
01158 
01159     snmp_asn1_dec_type(p, ofs, &type);
01160     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01161     if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
01162     {
01163       /* can't decode object name length */
01164       snmp_inc_snmpinasnparseerrs();
01165       /* free varbinds (if available) */
01166       snmp_varbind_list_free(&m_stat->invb);
01167       return ERR_ARG;
01168     }
01169     derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
01170     if (derr != ERR_OK)
01171     {
01172       /* can't decode object name */
01173       snmp_inc_snmpinasnparseerrs();
01174       /* free varbinds (if available) */
01175       snmp_varbind_list_free(&m_stat->invb);
01176       return ERR_ARG;
01177     }
01178     ofs += (1 + len_octets + len);
01179     vb_len -= (1 + len_octets + len);
01180 
01181     snmp_asn1_dec_type(p, ofs, &type);
01182     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01183     if (derr != ERR_OK)
01184     {
01185       /* can't decode object value length */
01186       snmp_inc_snmpinasnparseerrs();
01187       /* free varbinds (if available) */
01188       snmp_varbind_list_free(&m_stat->invb);
01189       return ERR_ARG;
01190     }
01191 
01192     switch (type)
01193     {
01194       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
01195         vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
01196         if (vb != NULL)
01197         {
01198           s32_t *vptr = vb->value;
01199 
01200           derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
01201           snmp_varbind_tail_add(&m_stat->invb, vb);
01202         }
01203         else
01204         {
01205           derr = ERR_ARG;
01206         }
01207         break;
01208       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
01209       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
01210       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
01211         vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
01212         if (vb != NULL)
01213         {
01214           u32_t *vptr = vb->value;
01215 
01216           derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
01217           snmp_varbind_tail_add(&m_stat->invb, vb);
01218         }
01219         else
01220         {
01221           derr = ERR_ARG;
01222         }
01223         break;
01224       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
01225       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
01226         vb = snmp_varbind_alloc(&oid, type, len);
01227         if (vb != NULL)
01228         {
01229           derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value);
01230           snmp_varbind_tail_add(&m_stat->invb, vb);
01231         }
01232         else
01233         {
01234           derr = ERR_ARG;
01235         }
01236         break;
01237       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
01238         vb = snmp_varbind_alloc(&oid, type, 0);
01239         if (vb != NULL)
01240         {
01241           snmp_varbind_tail_add(&m_stat->invb, vb);
01242           derr = ERR_OK;
01243         }
01244         else
01245         {
01246           derr = ERR_ARG;
01247         }
01248         break;
01249       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
01250         derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
01251         if (derr == ERR_OK)
01252         {
01253           vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
01254           if (vb != NULL)
01255           {
01256             u8_t i = oid_value.len;
01257             s32_t *vptr = vb->value;
01258 
01259             while(i > 0)
01260             {
01261               i--;
01262               vptr[i] = oid_value.id[i];
01263             }
01264             snmp_varbind_tail_add(&m_stat->invb, vb);
01265             derr = ERR_OK;
01266           }
01267           else
01268           {
01269             derr = ERR_ARG;
01270           }
01271         }
01272         break;
01273       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
01274         if (len == 4)
01275         {
01276           /* must be exactly 4 octets! */
01277           vb = snmp_varbind_alloc(&oid, type, 4);
01278           if (vb != NULL)
01279           {
01280             derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value);
01281             snmp_varbind_tail_add(&m_stat->invb, vb);
01282           }
01283           else
01284           {
01285             derr = ERR_ARG;
01286           }
01287         }
01288         else
01289         {
01290           derr = ERR_ARG;
01291         }
01292         break;
01293       default:
01294         derr = ERR_ARG;
01295         break;
01296     }
01297     if (derr != ERR_OK)
01298     {
01299       snmp_inc_snmpinasnparseerrs();
01300       /* free varbinds (if available) */
01301       snmp_varbind_list_free(&m_stat->invb);
01302       return ERR_ARG;
01303     }
01304     ofs += (1 + len_octets + len);
01305     vb_len -= (1 + len_octets + len);
01306   }
01307 
01308   if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
01309   {
01310     snmp_add_snmpintotalsetvars(m_stat->invb.count);
01311   }
01312   else
01313   {
01314     snmp_add_snmpintotalreqvars(m_stat->invb.count);
01315   }
01316 
01317   *ofs_ret = ofs;
01318   return ERR_OK;
01319 }
01320 
01321 struct snmp_varbind*
01322 snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
01323 {
01324   struct snmp_varbind *vb;
01325 
01326   vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
01327   LWIP_ASSERT("vb != NULL",vb != NULL);
01328   if (vb != NULL)
01329   {
01330     u8_t i;
01331 
01332     vb->next = NULL;
01333     vb->prev = NULL;
01334     i = oid->len;
01335     vb->ident_len = i;
01336     if (i > 0)
01337     {
01338       /* allocate array of s32_t for our object identifier */
01339       vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i);
01340       LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL);
01341       if (vb->ident == NULL)
01342       {
01343         mem_free(vb);
01344         return NULL;
01345       }
01346       while(i > 0)
01347       {
01348         i--;
01349         vb->ident[i] = oid->id[i];
01350       }
01351     }
01352     else
01353     {
01354       /* i == 0, pass zero length object identifier */
01355       vb->ident = NULL;
01356     }
01357     vb->value_type = type;
01358     vb->value_len = len;
01359     if (len > 0)
01360     {
01361       /* allocate raw bytes for our object value */
01362       vb->value = mem_malloc(len);
01363       LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
01364       if (vb->value == NULL)
01365       {
01366         if (vb->ident != NULL)
01367         {
01368           mem_free(vb->ident);
01369         }
01370         mem_free(vb);
01371         return NULL;
01372       }
01373     }
01374     else
01375     {
01376       /* ASN1_NUL type, or zero length ASN1_OC_STR */
01377       vb->value = NULL;
01378     }
01379   }
01380   return vb;
01381 }
01382 
01383 void
01384 snmp_varbind_free(struct snmp_varbind *vb)
01385 {
01386   if (vb->value != NULL )
01387   {
01388     mem_free(vb->value);
01389   }
01390   if (vb->ident != NULL )
01391   {
01392     mem_free(vb->ident);
01393   }
01394   mem_free(vb);
01395 }
01396 
01397 void
01398 snmp_varbind_list_free(struct snmp_varbind_root *root)
01399 {
01400   struct snmp_varbind *vb, *prev;
01401 
01402   vb = root->tail;
01403   while ( vb != NULL )
01404   {
01405     prev = vb->prev;
01406     snmp_varbind_free(vb);
01407     vb = prev;
01408   }
01409   root->count = 0;
01410   root->head = NULL;
01411   root->tail = NULL;
01412 }
01413 
01414 void
01415 snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
01416 {
01417   if (root->count == 0)
01418   {
01419     /* add first varbind to list */
01420     root->head = vb;
01421     root->tail = vb;
01422   }
01423   else
01424   {
01425     /* add nth varbind to list tail */
01426     root->tail->next = vb;
01427     vb->prev = root->tail;
01428     root->tail = vb;
01429   }
01430   root->count += 1;
01431 }
01432 
01433 struct snmp_varbind*
01434 snmp_varbind_tail_remove(struct snmp_varbind_root *root)
01435 {
01436   struct snmp_varbind* vb;
01437 
01438   if (root->count > 0)
01439   {
01440     /* remove tail varbind */
01441     vb = root->tail;
01442     root->tail = vb->prev;
01443     vb->prev->next = NULL;
01444     root->count -= 1;
01445   }
01446   else
01447   {
01448     /* nothing to remove */
01449     vb = NULL;
01450   }
01451   return vb;
01452 }
01453 
01454 #endif /* LWIP_SNMP */