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