1 /* $OpenBSD: application.c,v 1.6 2022/06/30 11:28:36 martijn Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Martijn van Duren <martijn@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/tree.h> 21 22 #include <assert.h> 23 #include <errno.h> 24 #include <event.h> 25 #include <inttypes.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 30 #include "application.h" 31 #include "log.h" 32 #include "smi.h" 33 #include "snmp.h" 34 #include "snmpe.h" 35 #include "mib.h" 36 37 TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts); 38 39 struct appl_context { 40 char ac_name[APPL_CONTEXTNAME_MAX + 1]; 41 42 RB_HEAD(appl_regions, appl_region) ac_regions; 43 44 TAILQ_ENTRY(appl_context) ac_entries; 45 }; 46 47 struct appl_region { 48 struct ber_oid ar_oid; 49 uint8_t ar_priority; 50 int32_t ar_timeout; 51 int ar_instance; 52 int ar_subtree; /* Claim entire subtree */ 53 struct appl_backend *ar_backend; 54 struct appl_region *ar_next; /* Sorted by priority */ 55 56 RB_ENTRY(appl_region) ar_entry; 57 }; 58 59 struct appl_request_upstream { 60 struct appl_context *aru_ctx; 61 struct snmp_message *aru_statereference; 62 int32_t aru_requestid; /* upstream requestid */ 63 int32_t aru_transactionid; /* RFC 2741 section 6.1 */ 64 int16_t aru_nonrepeaters; 65 int16_t aru_maxrepetitions; 66 struct appl_varbind_internal *aru_vblist; 67 size_t aru_varbindlen; 68 enum appl_error aru_error; 69 int16_t aru_index; 70 int aru_locked; /* Prevent recursion through appl_request_send */ 71 72 enum snmp_version aru_pduversion; 73 struct ber_element *aru_pdu; /* Original requested pdu */ 74 }; 75 76 struct appl_request_downstream { 77 struct appl_request_upstream *ard_request; 78 struct appl_backend *ard_backend; 79 enum snmp_pdutype ard_requesttype; 80 int16_t ard_nonrepeaters; 81 int16_t ard_maxrepetitions; 82 int32_t ard_requestid; 83 uint8_t ard_retries; 84 85 struct appl_varbind_internal *ard_vblist; 86 struct event ard_timer; 87 88 RB_ENTRY(appl_request_downstream) ard_entry; 89 }; 90 91 enum appl_varbind_state { 92 APPL_VBSTATE_MUSTFILL, 93 APPL_VBSTATE_NEW, 94 APPL_VBSTATE_PENDING, 95 APPL_VBSTATE_DONE 96 }; 97 98 struct appl_varbind_internal { 99 enum appl_varbind_state avi_state; 100 struct appl_varbind avi_varbind; 101 struct appl_region *avi_region; 102 int16_t avi_index; 103 struct appl_request_upstream *avi_request_upstream; 104 struct appl_request_downstream *avi_request_downstream; 105 struct appl_varbind_internal *avi_next; 106 struct appl_varbind_internal *avi_sub; 107 }; 108 109 /* SNMP-TARGET-MIB (RFC 3413) */ 110 struct snmp_target_mib { 111 uint32_t snmp_unavailablecontexts; 112 uint32_t snmp_unknowncontexts; 113 } snmp_target_mib; 114 115 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t, 116 struct ber_oid *, int, int, struct appl_backend *); 117 void appl_region_free(struct appl_context *, struct appl_region *); 118 struct appl_region *appl_region_find(struct appl_context *, 119 const struct ber_oid *); 120 struct appl_region *appl_region_next(struct appl_context *, 121 struct ber_oid *, struct appl_region *); 122 void appl_request_upstream_free(struct appl_request_upstream *); 123 void appl_request_downstream_free(struct appl_request_downstream *); 124 void appl_request_upstream_resolve(struct appl_request_upstream *); 125 void appl_request_downstream_send(struct appl_request_downstream *); 126 void appl_request_downstream_timeout(int, short, void *); 127 void appl_request_upstream_reply(struct appl_request_upstream *); 128 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int, 129 const char **); 130 int appl_varbind_backend(struct appl_varbind_internal *); 131 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); 132 void appl_report(struct snmp_message *, int32_t, struct ber_oid *, 133 struct ber_element *); 134 void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t, 135 uint16_t, struct appl_varbind *); 136 void ober_oid_nextsibling(struct ber_oid *); 137 138 int appl_region_cmp(struct appl_region *, struct appl_region *); 139 int appl_request_cmp(struct appl_request_downstream *, 140 struct appl_request_downstream *); 141 142 RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 143 RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry, 144 appl_request_cmp); 145 146 #define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name) 147 148 void 149 appl_init(void) 150 { 151 appl_blocklist_init(); 152 appl_legacy_init(); 153 } 154 155 void 156 appl_shutdown(void) 157 { 158 struct appl_context *ctx, *tctx; 159 160 appl_blocklist_shutdown(); 161 appl_legacy_shutdown(); 162 163 TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) { 164 assert(RB_EMPTY(&(ctx->ac_regions))); 165 TAILQ_REMOVE(&contexts, ctx, ac_entries); 166 free(ctx); 167 } 168 } 169 170 static struct appl_context * 171 appl_context(const char *name, int create) 172 { 173 struct appl_context *ctx; 174 175 if (name == NULL) 176 name = ""; 177 178 if (strlen(name) > APPL_CONTEXTNAME_MAX) { 179 errno = EINVAL; 180 return NULL; 181 } 182 183 TAILQ_FOREACH(ctx, &contexts, ac_entries) { 184 if (strcmp(name, ctx->ac_name) == 0) 185 return ctx; 186 } 187 188 /* Always allow the default namespace */ 189 if (!create && name[0] != '\0') { 190 errno = ENOENT; 191 return NULL; 192 } 193 194 if ((ctx = malloc(sizeof(*ctx))) == NULL) 195 return NULL; 196 197 strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name)); 198 RB_INIT(&(ctx->ac_regions)); 199 200 TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries); 201 return ctx; 202 } 203 204 enum appl_error 205 appl_region(struct appl_context *ctx, uint32_t timeout, uint8_t priority, 206 struct ber_oid *oid, int instance, int subtree, 207 struct appl_backend *backend) 208 { 209 struct appl_region *region = NULL, *nregion, search; 210 char oidbuf[1024], regionbuf[1024], subidbuf[11]; 211 size_t i; 212 213 /* 214 * Don't allow overlap when subtree flag is set. 215 * This allows us to keep control of certain regions like system. 216 */ 217 region = appl_region_find(ctx, oid); 218 if (region != NULL && region->ar_subtree) 219 goto overlap; 220 221 search.ar_oid = *oid; 222 region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search); 223 if (region != NULL && region->ar_subtree && ( 224 appl_region_cmp(&search, region) == 0 || 225 appl_region_cmp(&search, region) == -2)) 226 goto overlap; 227 228 /* Don't use smi_oid2string, because appl_register can't use it */ 229 oidbuf[0] = '\0'; 230 for (i = 0; i < oid->bo_n; i++) { 231 if (i != 0) 232 strlcat(oidbuf, ".", sizeof(oidbuf)); 233 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 234 oid->bo_id[i]); 235 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 236 } 237 if ((nregion = malloc(sizeof(*nregion))) == NULL) { 238 log_warn("%s: Can't register %s: Processing error", 239 backend->ab_name, oidbuf); 240 return APPL_ERROR_PROCESSINGERROR; 241 } 242 nregion->ar_oid = *oid; 243 nregion->ar_priority = priority; 244 nregion->ar_timeout = timeout; 245 nregion->ar_instance = instance; 246 nregion->ar_subtree = subtree; 247 nregion->ar_backend = backend; 248 nregion->ar_next = NULL; 249 250 region = RB_INSERT(appl_regions, &(ctx->ac_regions), nregion); 251 if (region == NULL) 252 return APPL_ERROR_NOERROR; 253 254 if (region->ar_priority == priority) 255 goto duplicate; 256 if (region->ar_priority > priority) { 257 RB_REMOVE(appl_regions, &(ctx->ac_regions), region); 258 RB_INSERT(appl_regions, &(ctx->ac_regions), nregion); 259 nregion->ar_next = region; 260 return APPL_ERROR_NOERROR; 261 } 262 263 while (region->ar_next != NULL && 264 region->ar_next->ar_priority < priority) 265 region = region->ar_next; 266 if (region->ar_next != NULL && region->ar_next->ar_priority == priority) 267 goto duplicate; 268 nregion->ar_next = region->ar_next; 269 region->ar_next = nregion; 270 271 return APPL_ERROR_NOERROR; 272 duplicate: 273 free(nregion); 274 log_info("%s: %s priority %"PRId8": Duplicate registration", 275 backend->ab_name, oidbuf, priority); 276 return APPL_ERROR_DUPLICATEREGISTRATION; 277 overlap: 278 regionbuf[0] = '\0'; 279 for (i = 0; i < region->ar_oid.bo_n; i++) { 280 if (i != 0) 281 strlcat(regionbuf, ".", sizeof(regionbuf)); 282 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 283 region->ar_oid.bo_id[i]); 284 strlcat(regionbuf, subidbuf, sizeof(regionbuf)); 285 } 286 log_info("%s: %s overlaps with %s: Request denied", 287 backend->ab_name, oidbuf, regionbuf); 288 return APPL_ERROR_REQUESTDENIED; 289 } 290 291 /* Name from RFC 2741 section 6.2.3 */ 292 enum appl_error 293 appl_register(const char *ctxname, uint32_t timeout, uint8_t priority, 294 struct ber_oid *oid, int instance, int subtree, uint8_t range_subid, 295 uint32_t upper_bound, struct appl_backend *backend) 296 { 297 struct appl_context *ctx; 298 struct appl_region *region, search; 299 char oidbuf[1024], subidbuf[11]; 300 enum appl_error error; 301 size_t i; 302 uint32_t lower_bound; 303 304 oidbuf[0] = '\0'; 305 /* smi_oid2string can't do ranges */ 306 for (i = 0; i < oid->bo_n; i++) { 307 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]); 308 if (i != 0) 309 strlcat(oidbuf, ".", sizeof(oidbuf)); 310 if (range_subid == i + 1) { 311 strlcat(oidbuf, "[", sizeof(oidbuf)); 312 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 313 strlcat(oidbuf, "-", sizeof(oidbuf)); 314 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 315 upper_bound); 316 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 317 strlcat(oidbuf, "]", sizeof(oidbuf)); 318 } else 319 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 320 } 321 322 if (ctxname == NULL) 323 ctxname = ""; 324 log_info("%s: Registering %s%s context(%s) priority(%"PRIu8") " 325 "timeout(%"PRIu32".%02us)", backend->ab_name, oidbuf, 326 instance ? "(instance)" : "", ctxname, priority, 327 timeout/100, timeout % 100); 328 329 if ((ctx = appl_context(ctxname, 0)) == NULL) { 330 if (errno == ENOMEM) { 331 log_warn("%s: Can't register %s: Processing error", 332 backend->ab_name, oidbuf); 333 return APPL_ERROR_PROCESSINGERROR; 334 } 335 log_info("%s: Can't register %s: Unsupported context \"%s\"", 336 backend->ab_name, oidbuf, ctxname); 337 return APPL_ERROR_UNSUPPORTEDCONTEXT; 338 } 339 /* Default timeouts should be handled by backend */ 340 if (timeout == 0) 341 fatalx("%s: Timeout can't be 0", __func__); 342 if (priority == 0) { 343 log_warnx("%s: Can't register %s: priority can't be 0", 344 backend->ab_name, oidbuf); 345 return APPL_ERROR_PARSEERROR; 346 } 347 if (range_subid > oid->bo_n) { 348 log_warnx("%s: Can't register %s: range_subid too large", 349 backend->ab_name, oidbuf); 350 return APPL_ERROR_PARSEERROR; 351 } 352 if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) { 353 log_warnx("%s: Can't register %s: upper bound smaller or equal " 354 "to range_subid", backend->ab_name, oidbuf); 355 return APPL_ERROR_PARSEERROR; 356 } 357 if (range_subid != 0) 358 lower_bound = oid->bo_id[range_subid]; 359 360 if (range_subid == 0) 361 return appl_region(ctx, timeout, priority, oid, instance, 362 subtree, backend); 363 364 do { 365 if ((error = appl_region(ctx, timeout, priority, oid, instance, 366 subtree, backend)) != APPL_ERROR_NOERROR) 367 goto fail; 368 } while (oid->bo_id[range_subid] != upper_bound); 369 if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree, 370 backend)) != APPL_ERROR_NOERROR) 371 goto fail; 372 373 return APPL_ERROR_NOERROR; 374 fail: 375 search.ar_oid = *oid; 376 if (search.ar_oid.bo_id[range_subid] == lower_bound) 377 return error; 378 379 for (search.ar_oid.bo_id[range_subid]--; 380 search.ar_oid.bo_id[range_subid] != lower_bound; 381 search.ar_oid.bo_id[range_subid]--) { 382 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 383 while (region->ar_priority != priority) 384 region = region->ar_next; 385 appl_region_free(ctx, region); 386 } 387 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 388 while (region->ar_priority != priority) 389 region = region->ar_next; 390 appl_region_free(ctx, region); 391 return error; 392 } 393 394 /* Name from RFC 2741 section 6.2.4 */ 395 enum appl_error 396 appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid, 397 uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend) 398 { 399 struct appl_region *region, search; 400 struct appl_context *ctx; 401 char oidbuf[1024], subidbuf[11]; 402 size_t i; 403 404 oidbuf[0] = '\0'; 405 for (i = 0; i < oid->bo_n; i++) { 406 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]); 407 if (i != 0) 408 strlcat(oidbuf, ".", sizeof(oidbuf)); 409 if (range_subid == i + 1) { 410 strlcat(oidbuf, "[", sizeof(oidbuf)); 411 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 412 strlcat(oidbuf, "-", sizeof(oidbuf)); 413 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 414 upper_bound); 415 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 416 strlcat(oidbuf, "]", sizeof(oidbuf)); 417 } else 418 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 419 } 420 421 if (ctxname == NULL) 422 ctxname = ""; 423 log_info("%s: Unregistering %s context(%s) priority(%"PRIu8")", 424 backend->ab_name, oidbuf,ctxname, priority); 425 426 if ((ctx = appl_context(ctxname, 0)) == NULL) { 427 if (errno == ENOMEM) { 428 log_warn("%s: Can't unregister %s: Processing error", 429 backend->ab_name, oidbuf); 430 return APPL_ERROR_PROCESSINGERROR; 431 } 432 log_info("%s: Can't unregister %s: Unsupported context \"%s\"", 433 backend->ab_name, oidbuf, ctxname); 434 return APPL_ERROR_UNSUPPORTEDCONTEXT; 435 } 436 437 if (priority == 0) { 438 log_warnx("%s: Can't unregister %s: priority can't be 0", 439 backend->ab_name, oidbuf); 440 return APPL_ERROR_PARSEERROR; 441 } 442 443 if (range_subid > oid->bo_n) { 444 log_warnx("%s: Can't unregiser %s: range_subid too large", 445 backend->ab_name, oidbuf); 446 return APPL_ERROR_PARSEERROR; 447 } 448 if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) { 449 log_warnx("%s: Can't unregister %s: upper bound smaller or " 450 "equal to range_subid", backend->ab_name, oidbuf); 451 return APPL_ERROR_PARSEERROR; 452 } 453 454 search.ar_oid = *oid; 455 while (range_subid != 0 && 456 search.ar_oid.bo_id[range_subid] != upper_bound) { 457 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 458 while (region != NULL && region->ar_priority < priority) 459 region = region->ar_next; 460 if (region == NULL || region->ar_priority != priority) { 461 log_warnx("%s: Can't unregister %s: region not found", 462 backend->ab_name, oidbuf); 463 return APPL_ERROR_UNKNOWNREGISTRATION; 464 } 465 if (region->ar_backend != backend) { 466 log_warnx("%s: Can't unregister %s: region not owned " 467 "by backend", backend->ab_name, oidbuf); 468 return APPL_ERROR_UNKNOWNREGISTRATION; 469 } 470 } 471 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 472 while (region != NULL && region->ar_priority < priority) 473 region = region->ar_next; 474 if (region == NULL || region->ar_priority != priority) { 475 log_warnx("%s: Can't unregister %s: region not found", 476 backend->ab_name, oidbuf); 477 return APPL_ERROR_UNKNOWNREGISTRATION; 478 } 479 if (region->ar_backend != backend) { 480 log_warnx("%s: Can't unregister %s: region not owned " 481 "by backend", backend->ab_name, oidbuf); 482 return APPL_ERROR_UNKNOWNREGISTRATION; 483 } 484 485 search.ar_oid = *oid; 486 while (range_subid != 0 && 487 search.ar_oid.bo_id[range_subid] != upper_bound) { 488 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 489 while (region != NULL && region->ar_priority != priority) 490 region = region->ar_next; 491 appl_region_free(ctx, region); 492 } 493 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 494 while (region != NULL && region->ar_priority != priority) 495 region = region->ar_next; 496 appl_region_free(ctx, region); 497 498 return APPL_ERROR_NOERROR; 499 } 500 501 void 502 appl_region_free(struct appl_context *ctx, struct appl_region *region) 503 { 504 struct appl_region *pregion; 505 506 pregion = RB_FIND(appl_regions, &(ctx->ac_regions), region); 507 508 if (pregion == region) { 509 RB_REMOVE(appl_regions, &(ctx->ac_regions), region); 510 if (region->ar_next != NULL) 511 RB_INSERT(appl_regions, &(ctx->ac_regions), 512 region->ar_next); 513 } else { 514 while (pregion->ar_next != region) 515 pregion = pregion->ar_next; 516 pregion->ar_next = region->ar_next; 517 } 518 519 free(region); 520 } 521 522 /* backend is owned by the sub-application, just release application.c stuff */ 523 void 524 appl_close(struct appl_backend *backend) 525 { 526 struct appl_context *ctx; 527 struct appl_region *region, *tregion, *nregion; 528 struct appl_request_downstream *request, *trequest; 529 530 TAILQ_FOREACH(ctx, &contexts, ac_entries) { 531 RB_FOREACH_SAFE(region, appl_regions, 532 &(ctx->ac_regions), tregion) { 533 while (region != NULL) { 534 nregion = region->ar_next; 535 if (region->ar_backend == backend) 536 appl_region_free(ctx, region); 537 region = nregion; 538 } 539 } 540 } 541 542 RB_FOREACH_SAFE(request, appl_requests, 543 &(backend->ab_requests), trequest) 544 appl_request_downstream_free(request); 545 } 546 547 struct appl_region * 548 appl_region_find(struct appl_context *ctx, 549 const struct ber_oid *oid) 550 { 551 struct appl_region *region, search; 552 553 search.ar_oid = *oid; 554 while (search.ar_oid.bo_n > 0) { 555 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 556 if (region != NULL) 557 return region; 558 search.ar_oid.bo_n--; 559 } 560 return NULL; 561 } 562 563 struct appl_region * 564 appl_region_next(struct appl_context *ctx, struct ber_oid *oid, 565 struct appl_region *cregion) 566 { 567 struct appl_region search, *nregion, *pregion; 568 int cmp; 569 570 search.ar_oid = *oid; 571 nregion = RB_NFIND(appl_regions, &(ctx->ac_regions), &search); 572 573 if (cregion == nregion) 574 nregion = RB_NEXT(appl_regions, &(ctx->ac_regions), nregion); 575 /* Past last element in tree, we might still have a parent */ 576 if (nregion == NULL) { 577 search.ar_oid = cregion->ar_oid; 578 search.ar_oid.bo_n--; 579 return appl_region_find(ctx, &(search.ar_oid)); 580 } 581 cmp = appl_region_cmp(cregion, nregion); 582 if (cmp >= 0) 583 fatalx("%s: wrong OID order", __func__); 584 /* Direct descendant */ 585 if (cmp == -2) 586 return nregion; 587 588 /* cmp == -1 */ 589 search.ar_oid = cregion->ar_oid; 590 /* Find direct next sibling */ 591 ober_oid_nextsibling(&(search.ar_oid)); 592 if (ober_oid_cmp(&(nregion->ar_oid), &(search.ar_oid)) == 0) 593 return nregion; 594 /* Sibling gaps go to parent, or end end at border */ 595 search.ar_oid = cregion->ar_oid; 596 search.ar_oid.bo_n--; 597 pregion = appl_region_find(ctx, &(search.ar_oid)); 598 599 return pregion != NULL ? pregion : nregion; 600 } 601 602 /* Name from RFC 3413 section 3.2 */ 603 void 604 appl_processpdu(struct snmp_message *statereference, const char *ctxname, 605 enum snmp_version pduversion, struct ber_element *pdu) 606 { 607 struct appl_context *ctx; 608 struct appl_request_upstream *ureq; 609 struct ber_oid oid; 610 struct ber_element *value, *varbind, *varbindlist; 611 long long nonrepeaters, maxrepetitions; 612 static uint32_t transactionid; 613 int32_t requestid; 614 size_t i, varbindlen = 0, repeaterlen; 615 616 /* pdu must be ASN.1 validated in snmpe.c */ 617 (void) ober_scanf_elements(pdu, "{diie", &requestid, &nonrepeaters, 618 &maxrepetitions, &varbindlist); 619 620 /* RFC 3413, section 3.2, processPDU, item 5, final bullet */ 621 if ((ctx = appl_context(ctxname, 0)) == NULL) { 622 oid = BER_OID(MIB_snmpUnknownContexts, 0); 623 snmp_target_mib.snmp_unknowncontexts++; 624 if ((value = ober_add_integer(NULL, 625 snmp_target_mib.snmp_unknowncontexts)) == NULL) 626 fatal("ober_add_integer"); 627 appl_report(statereference, requestid, &oid, value); 628 return; 629 } 630 631 if ((ureq = malloc(sizeof(*ureq))) == NULL) 632 fatal("malloc"); 633 634 ureq->aru_ctx = ctx; 635 ureq->aru_statereference = statereference; 636 ureq->aru_transactionid = transactionid++; 637 ureq->aru_requestid = requestid; 638 ureq->aru_error = APPL_ERROR_NOERROR; 639 ureq->aru_index = 0; 640 ureq->aru_nonrepeaters = nonrepeaters; 641 ureq->aru_maxrepetitions = maxrepetitions; 642 ureq->aru_varbindlen = 0; 643 ureq->aru_locked = 0; 644 ureq->aru_pduversion = pduversion; 645 ureq->aru_pdu = pdu; 646 647 varbind = varbindlist->be_sub; 648 for (; varbind != NULL; varbind = varbind->be_next) 649 varbindlen++; 650 651 repeaterlen = varbindlen - nonrepeaters; 652 if (pdu->be_type == SNMP_C_GETBULKREQ) 653 ureq->aru_varbindlen = nonrepeaters + 654 (repeaterlen * maxrepetitions); 655 else 656 ureq->aru_varbindlen = varbindlen; 657 if ((ureq->aru_vblist = calloc(ureq->aru_varbindlen, 658 sizeof(*ureq->aru_vblist))) == NULL) 659 fatal("malloc"); 660 661 varbind = varbindlist->be_sub; 662 /* Use aru_varbindlen in case maxrepetitions == 0 */ 663 for (i = 0; i < ureq->aru_varbindlen; i++) { 664 ureq->aru_vblist[i].avi_request_upstream = ureq; 665 ureq->aru_vblist[i].avi_index = i + 1; 666 ureq->aru_vblist[i].avi_state = APPL_VBSTATE_NEW; 667 /* This can only happen with bulkreq */ 668 if (varbind == NULL) { 669 ureq->aru_vblist[i - repeaterlen].avi_sub = 670 &(ureq->aru_vblist[i]); 671 ureq->aru_vblist[i].avi_state = APPL_VBSTATE_MUSTFILL; 672 continue; 673 } 674 ober_get_oid(varbind->be_sub, 675 &(ureq->aru_vblist[i].avi_varbind.av_oid)); 676 if (i + 1 < ureq->aru_varbindlen) { 677 ureq->aru_vblist[i].avi_next = 678 &(ureq->aru_vblist[i + 1]); 679 ureq->aru_vblist[i].avi_varbind.av_next = 680 &(ureq->aru_vblist[i + 1].avi_varbind); 681 } else { 682 ureq->aru_vblist[i].avi_next = NULL; 683 ureq->aru_vblist[i].avi_varbind.av_next = NULL; 684 } 685 varbind = varbind->be_next; 686 } 687 688 appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters, 689 maxrepetitions, &(ureq->aru_vblist[0].avi_varbind)); 690 691 appl_request_upstream_resolve(ureq); 692 } 693 694 void 695 appl_request_upstream_free(struct appl_request_upstream *ureq) 696 { 697 size_t i; 698 struct appl_varbind_internal *vb; 699 700 if (ureq == NULL) 701 return; 702 703 for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) { 704 vb = &(ureq->aru_vblist[i]); 705 ober_free_elements(vb->avi_varbind.av_value); 706 appl_request_downstream_free(vb->avi_request_downstream); 707 } 708 free(ureq->aru_vblist); 709 710 assert(ureq->aru_statereference == NULL); 711 712 free(ureq); 713 } 714 715 void 716 appl_request_downstream_free(struct appl_request_downstream *dreq) 717 { 718 struct appl_varbind_internal *vb; 719 720 if (dreq == NULL) 721 return; 722 723 RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq); 724 evtimer_del(&(dreq->ard_timer)); 725 726 for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) 727 vb->avi_request_downstream = NULL; 728 729 free(dreq); 730 } 731 732 void 733 appl_request_upstream_resolve(struct appl_request_upstream *ureq) 734 { 735 struct appl_varbind_internal *vb, *lvb, *tvb; 736 struct appl_request_downstream *dreq; 737 struct appl_region *region, *lregion; 738 struct timeval tv; 739 int done; 740 size_t i; 741 int32_t maxrepetitions; 742 int32_t timeout; 743 744 if (ureq->aru_locked) 745 return; 746 ureq->aru_locked = 1; 747 748 if (ureq->aru_pdu->be_type == SNMP_C_SETREQ) { 749 ureq->aru_error = APPL_ERROR_NOTWRITABLE; 750 ureq->aru_index = 1; 751 appl_request_upstream_reply(ureq); 752 return; 753 } 754 755 next: 756 dreq = NULL; 757 lvb = NULL; 758 done = 1; 759 timeout = 0; 760 761 if (ureq->aru_error != APPL_ERROR_NOERROR) { 762 appl_request_upstream_reply(ureq); 763 return; 764 } 765 for (i = 0; i < ureq->aru_varbindlen; i++) { 766 vb = &(ureq->aru_vblist[i]); 767 768 switch (vb->avi_state) { 769 case APPL_VBSTATE_MUSTFILL: 770 case APPL_VBSTATE_PENDING: 771 done = 0; 772 continue; 773 case APPL_VBSTATE_DONE: 774 continue; 775 case APPL_VBSTATE_NEW: 776 break; 777 } 778 if (appl_varbind_backend(vb) == -1) 779 fatal("appl_varbind_backend"); 780 if (vb->avi_state != APPL_VBSTATE_DONE) 781 done = 0; 782 } 783 784 for (i = 0; i < ureq->aru_varbindlen; i++) { 785 vb = &(ureq->aru_vblist[i]); 786 787 if (vb->avi_state != APPL_VBSTATE_NEW) 788 continue; 789 790 vb = &(ureq->aru_vblist[i]); 791 region = vb->avi_region; 792 lregion = lvb != NULL ? lvb->avi_region : NULL; 793 if (lvb != NULL && region->ar_backend != lregion->ar_backend) 794 continue; 795 796 vb->avi_varbind.av_next = NULL; 797 vb->avi_next = NULL; 798 tvb = vb; 799 for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub) 800 maxrepetitions++; 801 if (dreq == NULL) { 802 if ((dreq = malloc(sizeof(*dreq))) == NULL) 803 fatal("malloc"); 804 805 dreq->ard_request = ureq; 806 dreq->ard_vblist = vb; 807 dreq->ard_backend = vb->avi_region->ar_backend; 808 dreq->ard_retries = dreq->ard_backend->ab_retries; 809 dreq->ard_requesttype = ureq->aru_pdu->be_type; 810 /* 811 * We don't yet fully handle bulkrequest responses. 812 * It's completely valid to map onto getrequest. 813 * maxrepetitions calculated in preparation of support. 814 */ 815 if (dreq->ard_requesttype == SNMP_C_GETBULKREQ && 816 dreq->ard_backend->ab_fn->ab_getbulk == NULL) 817 dreq->ard_requesttype = SNMP_C_GETNEXTREQ; 818 /* 819 * If first varbind is nonrepeater, set maxrepetitions 820 * to 0, so that the next varbind with 821 * maxrepetitions > 1 determines length. 822 */ 823 if (maxrepetitions == 1) { 824 dreq->ard_maxrepetitions = 0; 825 dreq->ard_nonrepeaters = 1; 826 } else { 827 dreq->ard_maxrepetitions = maxrepetitions; 828 dreq->ard_nonrepeaters = 0; 829 } 830 do { 831 dreq->ard_requestid = arc4random(); 832 } while (RB_INSERT(appl_requests, 833 &(dreq->ard_backend->ab_requests), dreq) != NULL); 834 lvb = vb; 835 /* avi_sub isn't set on !bulkrequest, so we always enter here */ 836 } else if (maxrepetitions == 1) { 837 dreq->ard_nonrepeaters++; 838 vb->avi_varbind.av_next = 839 &(dreq->ard_vblist->avi_varbind); 840 vb->avi_next = dreq->ard_vblist; 841 dreq->ard_vblist = vb; 842 } else { 843 lvb->avi_varbind.av_next = &(vb->avi_varbind); 844 lvb->avi_next = vb; 845 /* RFC 2741 section 7.2.1.3: 846 * The value of g.max_repetitions in the GetBulk-PDU may 847 * be less than (but not greater than) the value in the 848 * original request PDU. 849 */ 850 if (dreq->ard_maxrepetitions > maxrepetitions || 851 dreq->ard_maxrepetitions == 0) 852 dreq->ard_maxrepetitions = maxrepetitions; 853 lvb = vb; 854 } 855 vb->avi_request_downstream = dreq; 856 vb->avi_state = APPL_VBSTATE_PENDING; 857 if (region->ar_timeout > timeout) 858 timeout = region->ar_timeout; 859 } 860 861 if (dreq == NULL) { 862 ureq->aru_locked = 0; 863 if (done) 864 appl_request_upstream_reply(ureq); 865 return; 866 } 867 868 tv.tv_sec = timeout / 100; 869 tv.tv_usec = (timeout % 100) * 10000; 870 evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq); 871 evtimer_add(&(dreq->ard_timer), &tv); 872 873 appl_request_downstream_send(dreq); 874 goto next; 875 } 876 877 void 878 appl_request_downstream_send(struct appl_request_downstream *dreq) 879 { 880 881 appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype, 882 dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind)); 883 884 if (dreq->ard_requesttype == SNMP_C_GETREQ) { 885 dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend, 886 dreq->ard_request->aru_transactionid, 887 dreq->ard_requestid, 888 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 889 &(dreq->ard_vblist->avi_varbind)); 890 } else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) { 891 dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend, 892 dreq->ard_request->aru_transactionid, 893 dreq->ard_requestid, 894 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 895 &(dreq->ard_vblist->avi_varbind)); 896 } 897 } 898 899 void 900 appl_request_downstream_timeout(__unused int fd, __unused short event, 901 void *cookie) 902 { 903 struct appl_request_downstream *dreq = cookie; 904 905 log_info("%s: %"PRIu32" timed out%s", 906 dreq->ard_backend->ab_name, dreq->ard_requestid, 907 dreq->ard_retries > 0 ? ": retrying" : ""); 908 if (dreq->ard_retries > 0) { 909 dreq->ard_retries--; 910 appl_request_downstream_send(dreq); 911 } else 912 appl_response(dreq->ard_backend, dreq->ard_requestid, 913 APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind)); 914 } 915 916 void 917 appl_request_upstream_reply(struct appl_request_upstream *ureq) 918 { 919 struct ber_element *varbindlist = NULL, *varbind = NULL, *value; 920 struct appl_varbind_internal *vb; 921 size_t i, repvarbinds, varbindlen; 922 ssize_t match = -1; 923 924 varbindlen = ureq->aru_varbindlen; 925 926 if (ureq->aru_pduversion == SNMP_V1) { 927 /* RFC 3584 section 4.2.2.2 Map exceptions */ 928 for (i = 0; i < varbindlen; i++) { 929 vb = &(ureq->aru_vblist[i]); 930 value = vb->avi_varbind.av_value; 931 if (value != NULL && 932 value->be_class == BER_CLASS_CONTEXT) 933 appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME); 934 } 935 /* RFC 3584 section 4.4 Map errors */ 936 switch (ureq->aru_error) { 937 case APPL_ERROR_WRONGVALUE: 938 case APPL_ERROR_WRONGENCODING: 939 case APPL_ERROR_WRONGTYPE: 940 case APPL_ERROR_WRONGLENGTH: 941 case APPL_ERROR_INCONSISTENTVALUE: 942 ureq->aru_error = APPL_ERROR_BADVALUE; 943 break; 944 case APPL_ERROR_NOACCESS: 945 case APPL_ERROR_NOTWRITABLE: 946 case APPL_ERROR_NOCREATION: 947 case APPL_ERROR_INCONSISTENTNAME: 948 case APPL_ERROR_AUTHORIZATIONERROR: 949 ureq->aru_error = APPL_ERROR_NOSUCHNAME; 950 break; 951 case APPL_ERROR_RESOURCEUNAVAILABLE: 952 case APPL_ERROR_COMMITFAILED: 953 case APPL_ERROR_UNDOFAILED: 954 ureq->aru_error = APPL_ERROR_GENERR; 955 break; 956 default: 957 break; 958 } 959 } 960 /* RFC 3416 section 4.2.{1,2,3} reset original varbinds */ 961 if (ureq->aru_error != APPL_ERROR_NOERROR) { 962 ober_scanf_elements(ureq->aru_pdu, "{SSS{e", &varbind); 963 for (varbindlen = 0; varbind != NULL; 964 varbindlen++, varbind = varbind->be_next) { 965 vb = &(ureq->aru_vblist[varbindlen]); 966 ober_get_oid(varbind->be_sub, 967 &(vb->avi_varbind.av_oid)); 968 ober_free_elements(vb->avi_varbind.av_value); 969 vb->avi_varbind.av_value = ober_add_null(NULL);; 970 } 971 /* RFC 3416 section 4.2.3: Strip excessive EOMV */ 972 } else if (ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ) { 973 repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) / 974 ureq->aru_maxrepetitions; 975 for (i = ureq->aru_nonrepeaters; 976 i < ureq->aru_varbindlen - repvarbinds; i++) { 977 value = ureq->aru_vblist[i].avi_varbind.av_value; 978 if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 && 979 value->be_class == BER_CLASS_CONTEXT && 980 value->be_type == APPL_EXC_ENDOFMIBVIEW) { 981 if (match != -1) 982 break; 983 match = i; 984 } 985 if (value->be_class != BER_CLASS_CONTEXT || 986 value->be_type != APPL_EXC_ENDOFMIBVIEW) 987 match = -1; 988 } 989 if (match != -1) 990 varbindlen = match + repvarbinds; 991 } 992 993 for (i = 0; i < varbindlen; i++) { 994 vb = &(ureq->aru_vblist[i]); 995 vb->avi_varbind.av_next = 996 &(ureq->aru_vblist[i + 1].avi_varbind); 997 } 998 999 ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL; 1000 appl_pdu_log(NULL, SNMP_C_RESPONSE, ureq->aru_requestid, 1001 ureq->aru_error, ureq->aru_index, 1002 &(ureq->aru_vblist[0].avi_varbind)); 1003 1004 for (i = 0; i < varbindlen; i++) { 1005 varbind = ober_printf_elements(varbind, "{Oe}", 1006 &(ureq->aru_vblist[i].avi_varbind.av_oid), 1007 ureq->aru_vblist[i].avi_varbind.av_value); 1008 ureq->aru_vblist[i].avi_varbind.av_value = NULL; 1009 if (varbind == NULL) 1010 fatal("ober_printf_elements"); 1011 if (varbindlist == NULL) 1012 varbindlist = varbind; 1013 } 1014 1015 snmpe_send(ureq->aru_statereference, SNMP_C_RESPONSE, 1016 ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist); 1017 ureq->aru_statereference = NULL; 1018 appl_request_upstream_free(ureq); 1019 } 1020 1021 /* Name from RFC 2741 section 6.2.16 */ 1022 void 1023 appl_response(struct appl_backend *backend, int32_t requestid, 1024 enum appl_error error, int16_t index, struct appl_varbind *vblist) 1025 { 1026 struct appl_request_downstream *dreq, search; 1027 struct appl_request_upstream *ureq = NULL; 1028 struct appl_region *nregion; 1029 const char *errstr; 1030 char oidbuf[1024]; 1031 enum snmp_pdutype pdutype; 1032 struct appl_varbind *vb; 1033 struct appl_varbind_internal *origvb = NULL; 1034 int invalid = 0; 1035 int next = 0; 1036 int32_t i; 1037 1038 appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist); 1039 1040 search.ard_requestid = requestid; 1041 dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search); 1042 if (dreq == NULL) { 1043 log_debug("%s: %"PRIu32" not outstanding", 1044 backend->ab_name, requestid); 1045 /* Continue to verify validity */ 1046 } else { 1047 ureq = dreq->ard_request; 1048 pdutype = ureq->aru_pdu->be_type; 1049 next = pdutype == SNMP_C_GETNEXTREQ || 1050 pdutype == SNMP_C_GETBULKREQ; 1051 origvb = dreq->ard_vblist; 1052 } 1053 1054 vb = vblist; 1055 for (i = 1; vb != NULL; vb = vb->av_next, i++) { 1056 if (!appl_varbind_valid(vb, origvb == NULL ? 1057 NULL : &(origvb->avi_varbind), next, 1058 error != APPL_ERROR_NOERROR, &errstr)) { 1059 smi_oid2string(&(vb->av_oid), oidbuf, 1060 sizeof(oidbuf), 0); 1061 log_warnx("%s: %"PRIu32" %s: %s", 1062 backend->ab_name, requestid, oidbuf, errstr); 1063 invalid = 1; 1064 } 1065 /* Transfer av_value */ 1066 if (origvb != NULL) { 1067 if (error != APPL_ERROR_NOERROR && i == index) 1068 appl_varbind_error(origvb, error); 1069 origvb->avi_state = APPL_VBSTATE_DONE; 1070 origvb->avi_varbind.av_oid = vb->av_oid; 1071 if (vb->av_value != NULL && 1072 vb->av_value->be_class == BER_CLASS_CONTEXT && 1073 vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) { 1074 nregion = appl_region_next(ureq->aru_ctx, 1075 &(vb->av_oid), origvb->avi_region); 1076 if (nregion != NULL) { 1077 ober_free_elements(vb->av_value); 1078 origvb->avi_varbind.av_oid = 1079 nregion->ar_oid; 1080 origvb->avi_varbind.av_include = 1; 1081 vb->av_value = NULL; 1082 origvb->avi_state = APPL_VBSTATE_NEW; 1083 } 1084 } 1085 /* RFC 3584 section 4.2.2.1 */ 1086 if (ureq->aru_pduversion == SNMP_V1 && 1087 vb->av_value != NULL && 1088 vb->av_value->be_class == BER_CLASS_APPLICATION && 1089 vb->av_value->be_type == SNMP_COUNTER64) { 1090 if (ureq->aru_pdu->be_type == SNMP_C_GETREQ) { 1091 appl_varbind_error(origvb, 1092 APPL_ERROR_NOSUCHNAME); 1093 } else { 1094 ober_free_elements(vb->av_value); 1095 vb->av_value = NULL; 1096 origvb->avi_varbind.av_oid = vb->av_oid; 1097 origvb->avi_varbind.av_include = 0; 1098 origvb->avi_state = APPL_VBSTATE_NEW; 1099 } 1100 } 1101 origvb->avi_varbind.av_value = vb->av_value; 1102 if (origvb->avi_varbind.av_next == NULL && 1103 vb->av_next != NULL) { 1104 log_warnx("%s: Request %"PRIu32" returned more " 1105 "varbinds then requested", 1106 backend->ab_name, requestid); 1107 invalid = 1; 1108 } 1109 if (origvb->avi_sub != NULL && 1110 origvb->avi_state == APPL_VBSTATE_DONE) { 1111 origvb->avi_sub->avi_varbind.av_oid = 1112 origvb->avi_varbind.av_oid; 1113 origvb->avi_sub->avi_state = APPL_VBSTATE_NEW; 1114 } 1115 origvb = origvb->avi_next; 1116 } else { 1117 ober_free_elements(vb->av_value); 1118 vb->av_value = NULL; 1119 } 1120 } 1121 if (error != APPL_ERROR_NOERROR && (index <= 0 || index >= i)) { 1122 log_warnx("Invalid error index"); 1123 invalid = 1; 1124 } 1125 if (error == APPL_ERROR_NOERROR && index != 0) { 1126 log_warnx("error index with no error"); 1127 invalid = 1; 1128 } 1129 if (vb == NULL && origvb != NULL) { 1130 log_warnx("%s: Request %"PRIu32" returned less varbinds then " 1131 "requested", backend->ab_name, requestid); 1132 invalid = 1; 1133 } 1134 1135 if (dreq != NULL) { 1136 if (invalid) 1137 appl_varbind_error(dreq->ard_vblist, APPL_ERROR_GENERR); 1138 appl_request_downstream_free(dreq); 1139 } 1140 1141 if (invalid && backend->ab_fn->ab_close != NULL) { 1142 log_warnx("%s: Closing: Too many parse errors", 1143 backend->ab_name); 1144 backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR); 1145 } 1146 1147 if (ureq != NULL) 1148 appl_request_upstream_resolve(ureq); 1149 } 1150 1151 int 1152 appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request, 1153 int next, int null, const char **errstr) 1154 { 1155 int cmp; 1156 int eomv = 0; 1157 1158 if (varbind->av_value == NULL) { 1159 *errstr = "missing value"; 1160 return 0; 1161 } 1162 if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) { 1163 switch (varbind->av_value->be_type) { 1164 case BER_TYPE_NULL: 1165 if (null) 1166 break; 1167 *errstr = "not expecting null value"; 1168 return 0; 1169 case BER_TYPE_INTEGER: 1170 case BER_TYPE_OCTETSTRING: 1171 case BER_TYPE_OBJECT: 1172 if (!null) 1173 break; 1174 /* FALLTHROUGH */ 1175 default: 1176 *errstr = "invalid value"; 1177 return 0; 1178 } 1179 } else if (varbind->av_value->be_class == BER_CLASS_APPLICATION) { 1180 switch (varbind->av_value->be_type) { 1181 case SNMP_T_IPADDR: 1182 case SNMP_T_COUNTER32: 1183 case SNMP_T_GAUGE32: 1184 case SNMP_T_TIMETICKS: 1185 case SNMP_T_OPAQUE: 1186 case SNMP_T_COUNTER64: 1187 if (!null) 1188 break; 1189 /* FALLTHROUGH */ 1190 default: 1191 *errstr = "expecting null value"; 1192 return 0; 1193 } 1194 } else if (varbind->av_value->be_class == BER_CLASS_CONTEXT) { 1195 switch (varbind->av_value->be_type) { 1196 case APPL_EXC_NOSUCHOBJECT: 1197 if (next && request != NULL) { 1198 *errstr = "Unexpected noSuchObject"; 1199 return 0; 1200 } 1201 /* FALLTHROUGH */ 1202 case APPL_EXC_NOSUCHINSTANCE: 1203 if (null) { 1204 *errstr = "expecting null value"; 1205 return 0; 1206 } 1207 if (next && request != NULL) { 1208 *errstr = "Unexpected noSuchInstance"; 1209 return 0; 1210 } 1211 break; 1212 case APPL_EXC_ENDOFMIBVIEW: 1213 if (null) { 1214 *errstr = "expecting null value"; 1215 return 0; 1216 } 1217 if (!next && request != NULL) { 1218 *errstr = "Unexpected endOfMibView"; 1219 return 0; 1220 } 1221 eomv = 1; 1222 break; 1223 default: 1224 *errstr = "invalid exception"; 1225 return 0; 1226 } 1227 } else { 1228 *errstr = "invalid value"; 1229 return 0; 1230 } 1231 1232 if (request == NULL) 1233 return 1; 1234 1235 cmp = ober_oid_cmp(&(request->av_oid), &(varbind->av_oid)); 1236 if (next && !eomv) { 1237 if (request->av_include) { 1238 if (cmp > 0) { 1239 *errstr = "oid not incrementing"; 1240 return 0; 1241 } 1242 } else { 1243 if (cmp >= 0) { 1244 *errstr = "oid not incrementing"; 1245 return 0; 1246 } 1247 } 1248 } else { 1249 if (cmp != 0) { 1250 *errstr = "oids not equal"; 1251 return 0; 1252 } 1253 } 1254 return 1; 1255 } 1256 1257 int 1258 appl_varbind_backend(struct appl_varbind_internal *ivb) 1259 { 1260 struct appl_request_upstream *ureq = ivb->avi_request_upstream; 1261 struct appl_region search, *region, *pregion; 1262 struct appl_varbind *vb = &(ivb->avi_varbind); 1263 int next, cmp; 1264 1265 next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ || 1266 ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ; 1267 1268 region = appl_region_find(ureq->aru_ctx, &(vb->av_oid)); 1269 if (region == NULL) { 1270 if (!next) { 1271 vb->av_value = appl_exception(APPL_EXC_NOSUCHOBJECT); 1272 ivb->avi_state = APPL_VBSTATE_DONE; 1273 if (vb->av_value == NULL) 1274 return -1; 1275 return 0; 1276 } 1277 search.ar_oid = vb->av_oid; 1278 region = RB_NFIND(appl_regions, 1279 &(ureq->aru_ctx->ac_regions), &search); 1280 if (region == NULL) 1281 goto eomv; 1282 vb->av_oid = region->ar_oid; 1283 vb->av_include = 1; 1284 } 1285 cmp = ober_oid_cmp(&(region->ar_oid), &(vb->av_oid)); 1286 if (cmp == -2) { 1287 if (region->ar_instance) { 1288 if (!next) { 1289 vb->av_value = 1290 appl_exception(APPL_EXC_NOSUCHINSTANCE); 1291 ivb->avi_state = APPL_VBSTATE_DONE; 1292 if (vb->av_value == NULL) 1293 return -1; 1294 return 0; 1295 } 1296 if ((region = appl_region_next(ureq->aru_ctx, 1297 &(vb->av_oid), region)) == NULL) 1298 goto eomv; 1299 vb->av_oid = region->ar_oid; 1300 vb->av_include = 1; 1301 } 1302 } else if (cmp == 0) { 1303 if (region->ar_instance && next && !vb->av_include) { 1304 if ((region = appl_region_next(ureq->aru_ctx, 1305 &(vb->av_oid), region)) == NULL) 1306 goto eomv; 1307 vb->av_oid = region->ar_oid; 1308 vb->av_include = 1; 1309 } 1310 } 1311 ivb->avi_region = region; 1312 if (next) { 1313 do { 1314 pregion = region; 1315 region = appl_region_next(ureq->aru_ctx, 1316 &(region->ar_oid), pregion); 1317 } while (region != NULL && 1318 region->ar_backend == pregion->ar_backend); 1319 if (region == NULL) { 1320 vb->av_oid_end = pregion->ar_oid; 1321 if (pregion->ar_instance && 1322 vb->av_oid_end.bo_n < BER_MAX_OID_LEN) 1323 vb->av_oid_end.bo_id[vb->av_oid_end.bo_n++] = 0; 1324 else 1325 ober_oid_nextsibling(&(vb->av_oid_end)); 1326 } else { 1327 if (ober_oid_cmp(&(region->ar_oid), 1328 &(ivb->avi_region->ar_oid)) == 2) 1329 vb->av_oid_end = region->ar_oid; 1330 else { 1331 vb->av_oid_end = ivb->avi_region->ar_oid; 1332 ober_oid_nextsibling(&(vb->av_oid_end)); 1333 } 1334 } 1335 } 1336 return 0; 1337 1338 eomv: 1339 do { 1340 ivb->avi_varbind.av_value = 1341 appl_exception(APPL_EXC_ENDOFMIBVIEW); 1342 ivb->avi_state = APPL_VBSTATE_DONE; 1343 if (ivb->avi_varbind.av_value == NULL) 1344 return -1; 1345 if (ivb->avi_sub != NULL) 1346 ivb->avi_sub->avi_varbind.av_oid = 1347 ivb->avi_varbind.av_oid; 1348 ivb = ivb->avi_sub; 1349 } while (ivb != NULL); 1350 1351 return 0; 1352 } 1353 1354 void 1355 appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error) 1356 { 1357 struct appl_request_upstream *ureq = avi->avi_request_upstream; 1358 1359 if (ureq->aru_error == APPL_ERROR_GENERR) 1360 return; 1361 if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR) 1362 return; 1363 ureq->aru_error = error; 1364 ureq->aru_index = avi->avi_index; 1365 } 1366 1367 void 1368 appl_report(struct snmp_message *msg, int32_t requestid, struct ber_oid *oid, 1369 struct ber_element *value) 1370 { 1371 struct ber_element *varbind; 1372 1373 varbind = ober_printf_elements(NULL, "{Oe}", oid, value); 1374 if (varbind == NULL) { 1375 log_warn("%"PRId32": ober_printf_elements", requestid); 1376 ober_free_elements(value); 1377 snmp_msgfree(msg); 1378 return; 1379 } 1380 1381 snmpe_send(msg, SNMP_C_REPORT, requestid, 0, 0, varbind); 1382 } 1383 1384 struct ber_element * 1385 appl_exception(enum appl_exception type) 1386 { 1387 struct ber_element *value; 1388 1389 if ((value = ober_add_null(NULL)) == NULL) { 1390 log_warn("malloc"); 1391 return NULL; 1392 } 1393 ober_set_header(value, BER_CLASS_CONTEXT, type); 1394 1395 return value; 1396 } 1397 1398 void 1399 appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype, 1400 int32_t requestid, uint16_t error, uint16_t index, 1401 struct appl_varbind *vblist) 1402 { 1403 struct appl_varbind *vb; 1404 char buf[1024], oidbuf[1024], *str; 1405 int next; 1406 1407 if (log_getverbose() < 2) 1408 return; 1409 1410 next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ); 1411 1412 buf[0] = '\0'; 1413 for (vb = vblist; vb != NULL; vb = vb->av_next) { 1414 strlcat(buf, "{", sizeof(buf)); 1415 strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf, 1416 sizeof(oidbuf), 0), sizeof(buf)); 1417 if (next) { 1418 if (vb->av_include) 1419 strlcat(buf, "(incl)", sizeof(buf)); 1420 if (vb->av_oid_end.bo_n > 0) { 1421 strlcat(buf, "-", sizeof(buf)); 1422 strlcat(buf, smi_oid2string(&(vb->av_oid_end), 1423 oidbuf, sizeof(oidbuf), 0), sizeof(buf)); 1424 } 1425 } 1426 strlcat(buf, ":", sizeof(buf)); 1427 if (vb->av_value != NULL) { 1428 str = smi_print_element(vb->av_value); 1429 strlcat(buf, str == NULL ? "???" : str, sizeof(buf)); 1430 free(str); 1431 } else 1432 strlcat(buf, "null", sizeof(buf)); 1433 strlcat(buf, "}", sizeof(buf)); 1434 } 1435 log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}", 1436 backend != NULL ? backend->ab_name : "", 1437 backend != NULL ? ": " : "", 1438 snmpe_pdutype2string(pdutype), requestid, error, index, buf); 1439 } 1440 1441 void 1442 ober_oid_nextsibling(struct ber_oid *oid) 1443 { 1444 while (oid->bo_n > 0) { 1445 oid->bo_id[oid->bo_n - 1]++; 1446 /* Overflow check */ 1447 if (oid->bo_id[oid->bo_n - 1] != 0) 1448 return; 1449 oid->bo_n--; 1450 } 1451 } 1452 1453 int 1454 appl_region_cmp(struct appl_region *r1, struct appl_region *r2) 1455 { 1456 return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid)); 1457 } 1458 1459 int 1460 appl_request_cmp(struct appl_request_downstream *r1, 1461 struct appl_request_downstream *r2) 1462 { 1463 return r1->ard_requestid < r2->ard_requestid ? -1 : 1464 r1->ard_requestid > r2->ard_requestid; 1465 } 1466 1467 RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 1468 RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry, 1469 appl_request_cmp); 1470