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