1 /*- 2 * Copyright (c) 2005-2006 The FreeBSD Project 3 * All rights reserved. 4 * 5 * Author: Shteryana Shopova <syrinx@FreeBSD.org> 6 * 7 * Redistribution of this software and documentation and use in source and 8 * binary forms, with or without modification, are permitted provided that 9 * the following conditions are met: 10 * 11 * 1. Redistributions of source code or documentation must retain the above 12 * copyright notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents, 30 * bsnmpset can be used to set MIB objects in an agent. 31 */ 32 33 #include <sys/queue.h> 34 #include <sys/types.h> 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <stdarg.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <syslog.h> 45 #include <unistd.h> 46 47 #include <bsnmp/asn1.h> 48 #include <bsnmp/snmp.h> 49 #include <bsnmp/snmpclient.h> 50 #include "bsnmptc.h" 51 #include "bsnmptools.h" 52 53 static const char *program_name = NULL; 54 static enum program_e { 55 BSNMPGET, 56 BSNMPWALK, 57 BSNMPSET 58 } program; 59 60 /* ***************************************************************************** 61 * Common bsnmptools functions. 62 */ 63 static void 64 usage(void) 65 { 66 fprintf(stderr, 67 "Usage:\n" 68 "%s %s [-A options] [-b buffersize] [-C options] [-I options]\n" 69 "\t[-i filelist] [-l filename]%s [-o output] [-P options]\n" 70 "\t%s[-r retries] [-s [trans::][community@][server][:port]]\n" 71 "\t[-t timeout] [-U options] [-v version]%s\n", 72 program_name, 73 (program == BSNMPGET) ? "[-aDdehnK]" : 74 (program == BSNMPWALK) ? "[-dhnK]" : 75 (program == BSNMPSET) ? "[-adehnK]" : 76 "", 77 (program == BSNMPGET || program == BSNMPWALK) ? 78 " [-M max-repetitions] [-N non-repeaters]" : "", 79 (program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "", 80 (program == BSNMPGET) ? " OID [OID ...]" : 81 (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" : 82 "" 83 ); 84 } 85 86 static int32_t 87 parse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg) 88 { 89 uint32_t v; 90 91 assert(opt_arg != NULL); 92 93 v = strtoul(opt_arg, (void *) NULL, 10); 94 95 if (v > SNMP_MAX_BINDINGS) { 96 warnx("Max repetitions value greater than %d maximum allowed.", 97 SNMP_MAX_BINDINGS); 98 return (-1); 99 } 100 101 SET_MAXREP(snmptoolctx, v); 102 return (2); 103 } 104 105 static int32_t 106 parse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg) 107 { 108 uint32_t v; 109 110 assert(opt_arg != NULL); 111 112 v = strtoul(opt_arg, (void *) NULL, 10); 113 114 if (v > SNMP_MAX_BINDINGS) { 115 warnx("Non repeaters value greater than %d maximum allowed.", 116 SNMP_MAX_BINDINGS); 117 return (-1); 118 } 119 120 SET_NONREP(snmptoolctx, v); 121 return (2); 122 } 123 124 static int32_t 125 parse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg) 126 { 127 assert(opt_arg != NULL); 128 129 if (strcasecmp(opt_arg, "getbulk") == 0) 130 SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK); 131 else if (strcasecmp(opt_arg, "getnext") == 0) 132 SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT); 133 else if (strcasecmp(opt_arg, "get") == 0) 134 SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET); 135 else { 136 warnx("PDU type '%s' not supported.", opt_arg); 137 return (-1); 138 } 139 140 return (2); 141 } 142 143 static int32_t 144 snmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv) 145 { 146 int32_t count, optnum = 0; 147 int ch; 148 const char *opts; 149 150 switch (program) { 151 case BSNMPWALK: 152 opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; 153 break; 154 case BSNMPGET: 155 opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; 156 break; 157 case BSNMPSET: 158 opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:"; 159 break; 160 default: 161 return (-1); 162 } 163 164 while ((ch = getopt(argc, argv, opts)) != EOF) { 165 switch (ch) { 166 case 'A': 167 count = parse_authentication(snmptoolctx, optarg); 168 break; 169 case 'a': 170 count = parse_skip_access(snmptoolctx); 171 break; 172 case 'b': 173 count = parse_buflen(optarg); 174 break; 175 case 'D': 176 count = parse_discovery(snmptoolctx); 177 break; 178 case 'd': 179 count = parse_debug(); 180 break; 181 case 'e': 182 count = parse_errors(snmptoolctx); 183 break; 184 case 'h': 185 usage(); 186 return (-2); 187 case 'C': 188 count = parse_context(snmptoolctx, optarg); 189 break; 190 case 'I': 191 count = parse_include(snmptoolctx, optarg); 192 break; 193 case 'i': 194 count = parse_file(snmptoolctx, optarg); 195 break; 196 case 'K': 197 count = parse_local_key(snmptoolctx); 198 break; 199 case 'l': 200 count = parse_local_path(optarg); 201 break; 202 case 'M': 203 count = parse_max_repetitions(snmptoolctx, optarg); 204 break; 205 case 'N': 206 count = parse_non_repeaters(snmptoolctx, optarg); 207 break; 208 case 'n': 209 count = parse_num_oids(snmptoolctx); 210 break; 211 case 'o': 212 count = parse_output(snmptoolctx, optarg); 213 break; 214 case 'P': 215 count = parse_privacy(snmptoolctx, optarg); 216 break; 217 case 'p': 218 count = parse_pdu_type(snmptoolctx, optarg); 219 break; 220 case 'r': 221 count = parse_retry(optarg); 222 break; 223 case 's': 224 count = parse_server(optarg); 225 break; 226 case 't': 227 count = parse_timeout(optarg); 228 break; 229 case 'U': 230 count = parse_user_security(snmptoolctx, optarg); 231 break; 232 case 'v': 233 count = parse_version(optarg); 234 break; 235 case '?': 236 default: 237 usage(); 238 return (-1); 239 } 240 if (count < 0) 241 return (-1); 242 optnum += count; 243 } 244 245 return (optnum); 246 } 247 248 /* 249 * Read user input OID - one of following formats: 250 * 1) 1.2.1.1.2.1.0 - that is if option numeric was given; 251 * 2) string - in such case append .0 to the asn_oid subs; 252 * 3) string.1 - no additional processing required in such case. 253 */ 254 static char * 255 snmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx, 256 struct snmp_object *obj, char *argv) 257 { 258 char string[MAXSTR], *str; 259 int32_t i = 0; 260 struct asn_oid in_oid; 261 262 str = argv; 263 264 if (*str == '.') 265 str++; 266 267 while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) { 268 str++; 269 i++; 270 } 271 272 if (i <= 0 || i >= MAXSTR) 273 return (NULL); 274 275 memset(&in_oid, 0, sizeof(struct asn_oid)); 276 if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) { 277 warnx("Invalid OID - %s", argv); 278 return (NULL); 279 } 280 281 strlcpy(string, argv, i + 1); 282 if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) { 283 warnx("No entry for %s in mapping lists", string); 284 return (NULL); 285 } 286 287 /* If OID given on command line append it. */ 288 if (in_oid.len > 0) 289 asn_append_oid(&(obj->val.var), &in_oid); 290 else if (*str == '[') { 291 if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL) 292 return (NULL); 293 } else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) == 294 SNMP_PDU_GET) { 295 if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0) 296 return (NULL); 297 } 298 299 return (str); 300 } 301 302 static int32_t 303 snmptools_parse_oid(struct snmp_toolinfo *snmptoolctx, 304 struct snmp_object *obj, char *argv) 305 { 306 if (argv == NULL) 307 return (-1); 308 309 if (ISSET_NUMERIC(snmptoolctx)) { 310 if (snmp_parse_numoid(argv, &(obj->val.var)) < 0) 311 return (-1); 312 } else { 313 if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL && 314 snmp_parse_numoid(argv, &(obj->val.var)) < 0) 315 return (-1); 316 } 317 318 return (1); 319 } 320 321 static int32_t 322 snmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) 323 { 324 if (obj->error > 0) 325 return (0); 326 327 asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); 328 pdu->nbindings++; 329 330 return (pdu->nbindings); 331 } 332 333 /* ***************************************************************************** 334 * bsnmpget private functions. 335 */ 336 static int32_t 337 snmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, 338 struct snmp_object *obj) 339 { 340 if (pdu->version == SNMP_V1 && obj->val.syntax == 341 SNMP_SYNTAX_COUNTER64) { 342 warnx("64-bit counters are not supported in SNMPv1 PDU"); 343 return (-1); 344 } 345 346 if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT || 347 pdu->type == SNMP_PDU_GETBULK) 348 return (1); 349 350 if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) { 351 warnx("Only leaf object values can be added to GET PDU"); 352 return (-1); 353 } 354 355 return (1); 356 } 357 358 /* 359 * In case of a getbulk PDU, the error_status and error_index fields are used by 360 * libbsnmp to hold the values of the non-repeaters and max-repetitions fields 361 * that are present only in the getbulk - so before sending the PDU make sure 362 * these have correct values as well. 363 */ 364 static void 365 snmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep) 366 { 367 assert(pdu != NULL); 368 369 if (pdu->nbindings < non_rep) 370 pdu->error_status = pdu->nbindings; 371 else 372 pdu->error_status = non_rep; 373 374 if (max_rep > 0) 375 pdu->error_index = max_rep; 376 else 377 pdu->error_index = 1; 378 } 379 380 static int 381 snmptool_get(struct snmp_toolinfo *snmptoolctx) 382 { 383 struct snmp_pdu req, resp; 384 385 snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); 386 387 while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind, 388 snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { 389 390 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) 391 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), 392 GET_NONREP(snmptoolctx)); 393 394 if (snmp_dialog(&req, &resp) == -1) { 395 warn("Snmp dialog"); 396 break; 397 } 398 399 if (snmp_parse_resp(&resp, &req) >= 0) { 400 snmp_output_resp(snmptoolctx, &resp, NULL); 401 snmp_pdu_free(&resp); 402 break; 403 } 404 405 snmp_output_err_resp(snmptoolctx, &resp); 406 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK || 407 !ISSET_RETRY(snmptoolctx)) { 408 snmp_pdu_free(&resp); 409 break; 410 } 411 412 /* 413 * Loop through the object list and set object->error to the 414 * varbinding that caused the error. 415 */ 416 if (snmp_object_seterror(snmptoolctx, 417 &(resp.bindings[resp.error_index - 1]), 418 resp.error_status) <= 0) { 419 snmp_pdu_free(&resp); 420 break; 421 } 422 423 fprintf(stderr, "Retrying...\n"); 424 snmp_pdu_free(&resp); 425 snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); 426 } 427 428 snmp_pdu_free(&req); 429 430 return (0); 431 } 432 433 434 /* ***************************************************************************** 435 * bsnmpwalk private functions. 436 */ 437 /* The default tree to walk. */ 438 static const struct asn_oid snmp_mibII_OID = { 439 6 , { 1, 3, 6, 1, 2, 1 } 440 }; 441 442 static int32_t 443 snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused, 444 struct snmp_object *obj, char *string __unused) 445 { 446 asn_append_oid(&(obj->val.var), &snmp_mibII_OID); 447 return (1); 448 } 449 450 /* 451 * Prepare the next GetNext/Get PDU to send. 452 */ 453 static void 454 snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu) 455 { 456 snmp_pdu_create(pdu, op); 457 asn_append_oid(&(pdu->bindings[0].var), var); 458 pdu->nbindings = 1; 459 } 460 461 static int 462 snmptool_walk(struct snmp_toolinfo *snmptoolctx) 463 { 464 struct snmp_pdu req, resp; 465 struct asn_oid root; /* Keep the initial oid. */ 466 int32_t outputs, rc; 467 uint32_t op; 468 469 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) 470 op = SNMP_PDU_GETBULK; 471 else 472 op = SNMP_PDU_GETNEXT; 473 474 snmp_pdu_create(&req, op); 475 476 while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL, 477 snmptool_add_vbind, &req, 1)) > 0) { 478 479 /* Remember the root where the walk started from. */ 480 memset(&root, 0, sizeof(struct asn_oid)); 481 asn_append_oid(&root, &(req.bindings[0].var)); 482 483 if (op == SNMP_PDU_GETBULK) 484 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), 485 GET_NONREP(snmptoolctx)); 486 487 outputs = 0; 488 while (snmp_dialog(&req, &resp) >= 0) { 489 if ((snmp_parse_resp(&resp, &req)) < 0) { 490 snmp_output_err_resp(snmptoolctx, &resp); 491 snmp_pdu_free(&resp); 492 outputs = -1; 493 break; 494 } 495 496 rc = snmp_output_resp(snmptoolctx, &resp, &root); 497 if (rc < 0) { 498 snmp_pdu_free(&resp); 499 outputs = -1; 500 break; 501 } 502 503 outputs += rc; 504 505 if ((u_int)rc < resp.nbindings) { 506 snmp_pdu_free(&resp); 507 break; 508 } 509 510 snmpwalk_nextpdu_create(op, 511 &(resp.bindings[resp.nbindings - 1].var), &req); 512 if (op == SNMP_PDU_GETBULK) 513 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), 514 GET_NONREP(snmptoolctx)); 515 snmp_pdu_free(&resp); 516 } 517 518 /* Just in case our root was a leaf. */ 519 if (outputs == 0) { 520 snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req); 521 if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) { 522 if (snmp_parse_resp(&resp, &req) < 0) 523 snmp_output_err_resp(snmptoolctx, &resp); 524 else 525 snmp_output_resp(snmptoolctx, &resp, 526 NULL); 527 snmp_pdu_free(&resp); 528 } else 529 warn("Snmp dialog"); 530 } 531 532 if (snmp_object_remove(snmptoolctx, &root) < 0) { 533 warnx("snmp_object_remove"); 534 break; 535 } 536 537 snmp_pdu_free(&req); 538 snmp_pdu_create(&req, op); 539 } 540 541 snmp_pdu_free(&req); 542 543 if (rc == 0) 544 return (0); 545 else 546 return (1); 547 } 548 549 /* ***************************************************************************** 550 * bsnmpset private functions. 551 */ 552 553 static int32_t 554 parse_oid_numeric(struct snmp_value *value, char *val) 555 { 556 char *endptr; 557 int32_t saved_errno; 558 asn_subid_t suboid; 559 560 do { 561 saved_errno = errno; 562 errno = 0; 563 suboid = strtoul(val, &endptr, 10); 564 if (errno != 0) { 565 warn("Value %s not supported", val); 566 errno = saved_errno; 567 return (-1); 568 } 569 errno = saved_errno; 570 if ((asn_subid_t) suboid > ASN_MAXID) { 571 warnx("Suboid %u > ASN_MAXID", suboid); 572 return (-1); 573 } 574 if (snmp_suboid_append(&(value->v.oid), suboid) < 0) 575 return (-1); 576 val = endptr + 1; 577 } while (*endptr == '.'); 578 579 if (*endptr != '\0') 580 warnx("OID value %s not supported", val); 581 582 value->syntax = SNMP_SYNTAX_OID; 583 return (0); 584 } 585 586 /* 587 * Allow OID leaf in both forms: 588 * 1) 1.3.6.1.2... -> in such case call directly the function reading raw OIDs; 589 * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that. 590 */ 591 static int32_t 592 parse_oid_string(struct snmp_toolinfo *snmptoolctx, 593 struct snmp_value *value, char *string) 594 { 595 struct snmp_object obj; 596 597 if (isdigit(string[0])) 598 return (parse_oid_numeric(value, string)); 599 600 memset(&obj, 0, sizeof(struct snmp_object)); 601 if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { 602 warnx("Unknown OID enum string - %s", string); 603 return (-1); 604 } 605 606 asn_append_oid(&(value->v.oid), &(obj.val.var)); 607 return (1); 608 } 609 610 static int32_t 611 parse_ip(struct snmp_value * value, char * val) 612 { 613 char *endptr, *str; 614 int32_t i; 615 uint32_t v; 616 617 str = val; 618 for (i = 0; i < 4; i++) { 619 v = strtoul(str, &endptr, 10); 620 if (v > 0xff) 621 return (-1); 622 if (*endptr != '.' && *endptr != '\0' && i != 3) 623 break; 624 str = endptr + 1; 625 value->v.ipaddress[i] = (uint8_t) v; 626 } 627 value->syntax = SNMP_SYNTAX_IPADDRESS; 628 629 return (0); 630 } 631 632 static int32_t 633 parse_int(struct snmp_value *value, char *val) 634 { 635 char *endptr; 636 int32_t v, saved_errno; 637 638 saved_errno = errno; 639 errno = 0; 640 641 v = strtol(val, &endptr, 10); 642 643 if (errno != 0) { 644 warn("Value %s not supported", val); 645 errno = saved_errno; 646 return (-1); 647 } 648 649 value->syntax = SNMP_SYNTAX_INTEGER; 650 value->v.integer = v; 651 errno = saved_errno; 652 653 return (0); 654 } 655 656 static int32_t 657 parse_int_string(struct snmp_object *object, char *val) 658 { 659 int32_t v; 660 661 if (isdigit(val[0])) 662 return ((parse_int(&(object->val), val))); 663 664 if (object->info == NULL) { 665 warnx("Unknown enumerated integer type - %s", val); 666 return (-1); 667 } 668 if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0) 669 warnx("Unknown enumerated integer type - %s", val); 670 671 object->val.v.integer = v; 672 return (1); 673 } 674 675 /* 676 * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, 677 * SNMP_SYNTAX_TIMETICKS. 678 */ 679 static int32_t 680 parse_uint(struct snmp_value *value, char *val) 681 { 682 char *endptr; 683 uint32_t v = 0; 684 int32_t saved_errno; 685 686 saved_errno = errno; 687 errno = 0; 688 689 v = strtoul(val, &endptr, 10); 690 691 if (errno != 0) { 692 warn("Value %s not supported", val); 693 errno = saved_errno; 694 return (-1); 695 } 696 697 value->v.uint32 = v; 698 errno = saved_errno; 699 700 return (0); 701 } 702 703 static int32_t 704 parse_ticks(struct snmp_value *value, char *val) 705 { 706 if (parse_uint(value, val) < 0) 707 return (-1); 708 709 value->syntax = SNMP_SYNTAX_TIMETICKS; 710 return (0); 711 } 712 713 static int32_t 714 parse_gauge(struct snmp_value *value, char *val) 715 { 716 if (parse_uint(value, val) < 0) 717 return (-1); 718 719 value->syntax = SNMP_SYNTAX_GAUGE; 720 return (0); 721 } 722 723 static int32_t 724 parse_counter(struct snmp_value *value, char *val) 725 { 726 if (parse_uint(value, val) < 0) 727 return (-1); 728 729 value->syntax = SNMP_SYNTAX_COUNTER; 730 return (0); 731 } 732 733 static int32_t 734 parse_uint64(struct snmp_value *value, char *val) 735 { 736 char *endptr; 737 int32_t saved_errno; 738 uint64_t v; 739 740 saved_errno = errno; 741 errno = 0; 742 743 v = strtoull(val, &endptr, 10); 744 745 if (errno != 0) { 746 warnx("Value %s not supported", val); 747 errno = saved_errno; 748 return (-1); 749 } 750 751 value->syntax = SNMP_SYNTAX_COUNTER64; 752 value->v.counter64 = v; 753 errno = saved_errno; 754 755 return (0); 756 } 757 758 static int32_t 759 parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val) 760 { 761 switch (syntax) { 762 case SNMP_SYNTAX_INTEGER: 763 return (parse_int(value, val)); 764 case SNMP_SYNTAX_IPADDRESS: 765 return (parse_ip(value, val)); 766 case SNMP_SYNTAX_COUNTER: 767 return (parse_counter(value, val)); 768 case SNMP_SYNTAX_GAUGE: 769 return (parse_gauge(value, val)); 770 case SNMP_SYNTAX_TIMETICKS: 771 return (parse_ticks(value, val)); 772 case SNMP_SYNTAX_COUNTER64: 773 return (parse_uint64(value, val)); 774 case SNMP_SYNTAX_OCTETSTRING: 775 return (snmp_tc2oct(SNMP_STRING, value, val)); 776 case SNMP_SYNTAX_OID: 777 return (parse_oid_numeric(value, val)); 778 default: 779 /* NOTREACHED */ 780 break; 781 } 782 783 return (-1); 784 } 785 786 /* 787 * Parse a command line argument of type OID=syntax:value and fill in whatever 788 * fields can be derived from the input into snmp_value structure. Reads numeric 789 * OIDs. 790 */ 791 static int32_t 792 parse_pair_numoid_val(char *str, struct snmp_value *snmp_val) 793 { 794 int32_t cnt; 795 char *ptr; 796 enum snmp_syntax syntax; 797 char oid_str[ASN_OIDSTRLEN]; 798 799 ptr = str; 800 for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++) 801 if (ptr[cnt] == '=') 802 break; 803 804 if (cnt >= ASN_OIDSTRLEN) { 805 warnx("OID too long - %s", str); 806 return (-1); 807 } 808 strlcpy(oid_str, ptr, (size_t) (cnt + 1)); 809 810 ptr = str + cnt + 1; 811 for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++) 812 if(ptr[cnt] == ':') 813 break; 814 815 if (cnt >= MAX_CMD_SYNTAX_LEN) { 816 warnx("Unknown syntax in OID - %s", str); 817 return (-1); 818 } 819 820 if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) { 821 warnx("Unknown syntax in OID - %s", ptr); 822 return (-1); 823 } 824 825 ptr = ptr + cnt + 1; 826 for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++) 827 if (ptr[cnt] == '\0') 828 break; 829 830 if (ptr[cnt] != '\0') { 831 warnx("Value string too long - %s", ptr); 832 return (-1); 833 } 834 835 /* 836 * Here try parsing the OIDs and syntaxes and then check values - have 837 * to know syntax to check value boundaries. 838 */ 839 if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) { 840 warnx("Error parsing OID %s", oid_str); 841 return (-1); 842 } 843 844 if (parse_syntax_val(snmp_val, syntax, ptr) < 0) 845 return (-1); 846 847 return (1); 848 } 849 850 static int32_t 851 parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, 852 struct snmp_object *object, char *str) 853 { 854 uint32_t len; 855 enum snmp_syntax syn; 856 857 /* 858 * Syntax string here not required - still may be present. 859 */ 860 861 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { 862 for (len = 0 ; *(str + len) != ':'; len++) { 863 if (*(str + len) == '\0') { 864 warnx("Syntax missing in value - %s", str); 865 return (-1); 866 } 867 } 868 if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { 869 warnx("Unknown syntax in - %s", str); 870 return (-1); 871 } 872 if (syn != object->val.syntax) { 873 if (!ISSET_ERRIGNORE(snmptoolctx)) { 874 warnx("Bad syntax in - %s", str); 875 return (-1); 876 } else 877 object->val.syntax = syn; 878 } 879 len++; 880 } else 881 len = 0; 882 883 switch (object->val.syntax) { 884 case SNMP_SYNTAX_INTEGER: 885 return (parse_int_string(object, str + len)); 886 case SNMP_SYNTAX_IPADDRESS: 887 return (parse_ip(&(object->val), str + len)); 888 case SNMP_SYNTAX_COUNTER: 889 return (parse_counter(&(object->val), str + len)); 890 case SNMP_SYNTAX_GAUGE: 891 return (parse_gauge(&(object->val), str + len)); 892 case SNMP_SYNTAX_TIMETICKS: 893 return (parse_ticks(&(object->val), str + len)); 894 case SNMP_SYNTAX_COUNTER64: 895 return (parse_uint64(&(object->val), str + len)); 896 case SNMP_SYNTAX_OCTETSTRING: 897 return (snmp_tc2oct(object->info->tc, &(object->val), 898 str + len)); 899 case SNMP_SYNTAX_OID: 900 return (parse_oid_string(snmptoolctx, &(object->val), 901 str + len)); 902 default: 903 /* NOTREACHED */ 904 break; 905 } 906 907 return (-1); 908 } 909 910 static int32_t 911 parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx, 912 struct snmp_object *obj, char *argv) 913 { 914 char *ptr; 915 916 if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL) 917 return (-1); 918 919 if (*ptr != '=') { 920 warnx("Value to set expected after OID"); 921 return (-1); 922 } 923 924 if (parse_syntax_strval(snmptoolctx, obj, ptr + 1) < 0) 925 return (-1); 926 927 return (1); 928 } 929 930 931 static int32_t 932 snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx, 933 struct snmp_object *obj, char *argv) 934 { 935 if (argv == NULL) 936 return (-1); 937 938 if (ISSET_NUMERIC(snmptoolctx)) { 939 if (parse_pair_numoid_val(argv, &(obj->val)) < 0) 940 return (-1); 941 } else { 942 if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0) 943 return (-1); 944 } 945 946 return (1); 947 } 948 949 static int32_t 950 add_ip_syntax(struct snmp_value *dst, struct snmp_value *src) 951 { 952 int8_t i; 953 954 dst->syntax = SNMP_SYNTAX_IPADDRESS; 955 for (i = 0; i < 4; i++) 956 dst->v.ipaddress[i] = src->v.ipaddress[i]; 957 958 return (1); 959 } 960 961 static int32_t 962 add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src) 963 { 964 if (src->v.octetstring.len > ASN_MAXOCTETSTRING) { 965 warnx("OctetString len too big - %u", src->v.octetstring.len); 966 return (-1); 967 } 968 969 if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) == 970 NULL) { 971 syslog(LOG_ERR, "malloc() failed - %s", strerror(errno)); 972 return (-1); 973 } 974 975 memcpy(dst->v.octetstring.octets, src->v.octetstring.octets, 976 src->v.octetstring.len); 977 dst->syntax = SNMP_SYNTAX_OCTETSTRING; 978 dst->v.octetstring.len = src->v.octetstring.len; 979 980 return(0); 981 } 982 983 static int32_t 984 add_oid_syntax(struct snmp_value *dst, struct snmp_value *src) 985 { 986 asn_append_oid(&(dst->v.oid), &(src->v.oid)); 987 dst->syntax = SNMP_SYNTAX_OID; 988 return (0); 989 } 990 991 /* 992 * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT, 993 * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known - 994 * return error. 995 */ 996 static int32_t 997 snmpset_add_value(struct snmp_value *dst, struct snmp_value *src) 998 { 999 if (dst == NULL || src == NULL) 1000 return (-1); 1001 1002 switch (src->syntax) { 1003 case SNMP_SYNTAX_INTEGER: 1004 dst->v.integer = src->v.integer; 1005 dst->syntax = SNMP_SYNTAX_INTEGER; 1006 break; 1007 case SNMP_SYNTAX_TIMETICKS: 1008 dst->v.uint32 = src->v.uint32; 1009 dst->syntax = SNMP_SYNTAX_TIMETICKS; 1010 break; 1011 case SNMP_SYNTAX_GAUGE: 1012 dst->v.uint32 = src->v.uint32; 1013 dst->syntax = SNMP_SYNTAX_GAUGE; 1014 break; 1015 case SNMP_SYNTAX_COUNTER: 1016 dst->v.uint32 = src->v.uint32; 1017 dst->syntax = SNMP_SYNTAX_COUNTER; 1018 break; 1019 case SNMP_SYNTAX_COUNTER64: 1020 dst->v.counter64 = src->v.counter64; 1021 dst->syntax = SNMP_SYNTAX_COUNTER64; 1022 break; 1023 case SNMP_SYNTAX_IPADDRESS: 1024 add_ip_syntax(dst, src); 1025 break; 1026 case SNMP_SYNTAX_OCTETSTRING: 1027 add_octstring_syntax(dst, src); 1028 break; 1029 case SNMP_SYNTAX_OID: 1030 add_oid_syntax(dst, src); 1031 break; 1032 default: 1033 warnx("Unknown syntax %d", src->syntax); 1034 return (-1); 1035 } 1036 1037 return (0); 1038 } 1039 1040 static int32_t 1041 snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, 1042 struct snmp_object *obj) 1043 { 1044 if (pdu->version == SNMP_V1 && obj->val.syntax == 1045 SNMP_SYNTAX_COUNTER64) { 1046 warnx("64-bit counters are not supported in SNMPv1 PDU"); 1047 return (-1); 1048 } 1049 1050 if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx)) 1051 return (1); 1052 1053 if (obj->info->access < SNMP_ACCESS_SET) { 1054 warnx("Object %s not accessible for set - try 'bsnmpset -a'", 1055 obj->info->string); 1056 return (-1); 1057 } 1058 1059 return (1); 1060 } 1061 1062 static int32_t 1063 snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) 1064 { 1065 if (pdu->nbindings > SNMP_MAX_BINDINGS) { 1066 warnx("Too many OIDs for one PDU"); 1067 return (-1); 1068 } 1069 1070 if (obj->error > 0) 1071 return (0); 1072 1073 if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val)) 1074 < 0) 1075 return (-1); 1076 1077 asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); 1078 pdu->nbindings++; 1079 1080 return (pdu->nbindings); 1081 } 1082 1083 static int 1084 snmptool_set(struct snmp_toolinfo *snmptoolctx) 1085 { 1086 struct snmp_pdu req, resp; 1087 1088 snmp_pdu_create(&req, SNMP_PDU_SET); 1089 1090 while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind, 1091 snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { 1092 if (snmp_dialog(&req, &resp)) { 1093 warn("Snmp dialog"); 1094 break; 1095 } 1096 1097 if (snmp_pdu_check(&req, &resp) > 0) { 1098 if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) 1099 snmp_output_resp(snmptoolctx, &resp, NULL); 1100 snmp_pdu_free(&resp); 1101 break; 1102 } 1103 1104 snmp_output_err_resp(snmptoolctx, &resp); 1105 if (!ISSET_RETRY(snmptoolctx)) { 1106 snmp_pdu_free(&resp); 1107 break; 1108 } 1109 1110 if (snmp_object_seterror(snmptoolctx, 1111 &(resp.bindings[resp.error_index - 1]), 1112 resp.error_status) <= 0) { 1113 snmp_pdu_free(&resp); 1114 break; 1115 } 1116 1117 fprintf(stderr, "Retrying...\n"); 1118 snmp_pdu_free(&req); 1119 snmp_pdu_create(&req, SNMP_PDU_SET); 1120 } 1121 1122 snmp_pdu_free(&req); 1123 1124 return (0); 1125 } 1126 1127 /* ***************************************************************************** 1128 * main 1129 */ 1130 /* 1131 * According to command line options prepare SNMP Get | GetNext | GetBulk PDU. 1132 * Wait for a response and print it. 1133 */ 1134 /* 1135 * Do a 'snmp walk' - according to command line options request for values 1136 * lexicographically subsequent and subrooted at a common node. Send a GetNext 1137 * PDU requesting the value for each next variable and print the response. Stop 1138 * when a Response PDU is received that contains the value of a variable not 1139 * subrooted at the variable the walk started. 1140 */ 1141 int 1142 main(int argc, char ** argv) 1143 { 1144 struct snmp_toolinfo snmptoolctx; 1145 int32_t oid_cnt, last_oid, opt_num; 1146 int rc = 0; 1147 1148 /* Make sure program_name is set and valid. */ 1149 if (*argv == NULL) 1150 program_name = "snmptool"; 1151 else { 1152 program_name = strrchr(*argv, '/'); 1153 if (program_name != NULL) 1154 program_name++; 1155 else 1156 program_name = *argv; 1157 } 1158 1159 if (program_name == NULL) { 1160 fprintf(stderr, "Error: No program name?\n"); 1161 exit (1); 1162 } else if (strcmp(program_name, "bsnmpget") == 0) 1163 program = BSNMPGET; 1164 else if (strcmp(program_name, "bsnmpwalk") == 0) 1165 program = BSNMPWALK; 1166 else if (strcmp(program_name, "bsnmpset") == 0) 1167 program = BSNMPSET; 1168 else { 1169 fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name); 1170 exit (1); 1171 } 1172 1173 /* Initialize. */ 1174 if (snmptool_init(&snmptoolctx) < 0) 1175 exit (1); 1176 1177 if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) { 1178 snmp_tool_freeall(&snmptoolctx); 1179 /* On -h (help) exit without error. */ 1180 if (opt_num == -2) 1181 exit(0); 1182 else 1183 exit(1); 1184 } 1185 1186 oid_cnt = argc - opt_num - 1; 1187 if (oid_cnt == 0) { 1188 switch (program) { 1189 case BSNMPGET: 1190 if (!ISSET_EDISCOVER(&snmptoolctx) && 1191 !ISSET_LOCALKEY(&snmptoolctx)) { 1192 fprintf(stderr, "No OID given.\n"); 1193 usage(); 1194 snmp_tool_freeall(&snmptoolctx); 1195 exit(1); 1196 } 1197 break; 1198 1199 case BSNMPWALK: 1200 if (snmp_object_add(&snmptoolctx, snmpwalk_add_default, 1201 NULL) < 0) { 1202 fprintf(stderr, 1203 "Error setting default subtree.\n"); 1204 snmp_tool_freeall(&snmptoolctx); 1205 exit(1); 1206 } 1207 break; 1208 1209 case BSNMPSET: 1210 fprintf(stderr, "No OID given.\n"); 1211 usage(); 1212 snmp_tool_freeall(&snmptoolctx); 1213 exit(1); 1214 } 1215 } 1216 1217 if (snmp_import_all(&snmptoolctx) < 0) { 1218 snmp_tool_freeall(&snmptoolctx); 1219 exit(1); 1220 } 1221 1222 /* A simple sanity check - can not send GETBULK when using SNMPv1. */ 1223 if (program == BSNMPGET && snmp_client.version == SNMP_V1 && 1224 GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) { 1225 fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n"); 1226 snmp_tool_freeall(&snmptoolctx); 1227 exit(1); 1228 } 1229 1230 for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) { 1231 if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ? 1232 snmpset_parse_oid : snmptools_parse_oid, 1233 argv[last_oid])) < 0) { 1234 fprintf(stderr, "Error parsing OID string '%s'.\n", 1235 argv[last_oid]); 1236 snmp_tool_freeall(&snmptoolctx); 1237 exit(1); 1238 } 1239 } 1240 1241 if (snmp_open(NULL, NULL, NULL, NULL)) { 1242 warn("Failed to open snmp session"); 1243 snmp_tool_freeall(&snmptoolctx); 1244 exit(1); 1245 } 1246 1247 if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0) 1248 SET_EDISCOVER(&snmptoolctx); 1249 1250 if (ISSET_EDISCOVER(&snmptoolctx) && 1251 snmp_discover_engine(snmptoolctx.passwd) < 0) { 1252 warn("Unknown SNMP Engine ID"); 1253 rc = 1; 1254 goto cleanup; 1255 } 1256 1257 if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || 1258 ISSET_EDISCOVER(&snmptoolctx)) 1259 snmp_output_engine(); 1260 1261 if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) && 1262 !ISSET_EDISCOVER(&snmptoolctx)) { 1263 if (snmp_passwd_to_keys(&snmp_client.user, 1264 snmptoolctx.passwd) != SNMP_CODE_OK || 1265 snmp_get_local_keys(&snmp_client.user, 1266 snmp_client.engine.engine_id, 1267 snmp_client.engine.engine_len) != SNMP_CODE_OK) { 1268 warn("Failed to get keys"); 1269 rc = 1; 1270 goto cleanup; 1271 } 1272 } 1273 1274 if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || 1275 ISSET_EDISCOVER(&snmptoolctx)) 1276 snmp_output_keys(); 1277 1278 if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0) 1279 goto cleanup; 1280 1281 switch (program) { 1282 case BSNMPGET: 1283 rc = snmptool_get(&snmptoolctx); 1284 break; 1285 case BSNMPWALK: 1286 rc = snmptool_walk(&snmptoolctx); 1287 break; 1288 case BSNMPSET: 1289 rc = snmptool_set(&snmptoolctx); 1290 break; 1291 } 1292 1293 1294 cleanup: 1295 snmp_tool_freeall(&snmptoolctx); 1296 snmp_close(); 1297 1298 exit(rc); 1299 } 1300