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