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