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 warnx("Snmp dialog - %s", strerror(errno)); 398 break; 399 } 400 401 if (snmp_parse_resp(&resp, &req) >= 0) { 402 snmp_output_resp(snmptoolctx, &resp, NULL); 403 break; 404 } 405 406 snmp_output_err_resp(snmptoolctx, &resp); 407 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK || 408 !ISSET_RETRY(snmptoolctx)) 409 break; 410 411 /* 412 * Loop through the object list and set object->error to the 413 * varbinding that caused the error. 414 */ 415 if (snmp_object_seterror(snmptoolctx, 416 &(resp.bindings[resp.error_index - 1]), 417 resp.error_status) <= 0) 418 break; 419 420 fprintf(stderr, "Retrying...\n"); 421 snmp_pdu_free(&resp); 422 snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); 423 } 424 425 snmp_pdu_free(&resp); 426 427 return (0); 428 } 429 430 431 /* ***************************************************************************** 432 * bsnmpwalk private functions. 433 */ 434 /* The default tree to walk. */ 435 static const struct asn_oid snmp_mibII_OID = { 436 6 , { 1, 3, 6, 1, 2, 1 } 437 }; 438 439 static int32_t 440 snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused, 441 struct snmp_object *obj, char *string __unused) 442 { 443 asn_append_oid(&(obj->val.var), &snmp_mibII_OID); 444 return (1); 445 } 446 447 /* 448 * Prepare the next GetNext/Get PDU to send. 449 */ 450 static void 451 snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu) 452 { 453 snmp_pdu_create(pdu, op); 454 asn_append_oid(&(pdu->bindings[0].var), var); 455 pdu->nbindings = 1; 456 } 457 458 static int 459 snmptool_walk(struct snmp_toolinfo *snmptoolctx) 460 { 461 struct snmp_pdu req, resp; 462 struct asn_oid root; /* Keep the initial oid. */ 463 int32_t outputs, rc; 464 uint32_t op; 465 466 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) 467 op = SNMP_PDU_GETBULK; 468 else 469 op = SNMP_PDU_GETNEXT; 470 471 snmp_pdu_create(&req, op); 472 473 while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL, 474 snmptool_add_vbind, &req, 1)) > 0) { 475 476 /* Remember the root where the walk started from. */ 477 memset(&root, 0, sizeof(struct asn_oid)); 478 asn_append_oid(&root, &(req.bindings[0].var)); 479 480 if (op == SNMP_PDU_GETBULK) 481 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), 482 GET_NONREP(snmptoolctx)); 483 484 outputs = 0; 485 while (snmp_dialog(&req, &resp) >= 0) { 486 if ((snmp_parse_resp(&resp, &req)) < 0) { 487 snmp_output_err_resp(snmptoolctx, &resp); 488 snmp_pdu_free(&resp); 489 outputs = -1; 490 break; 491 } 492 493 rc = snmp_output_resp(snmptoolctx, &resp, &root); 494 if (rc < 0) { 495 snmp_pdu_free(&resp); 496 outputs = -1; 497 break; 498 } 499 500 outputs += rc; 501 snmp_pdu_free(&resp); 502 503 if ((u_int)rc < resp.nbindings) 504 break; 505 506 snmpwalk_nextpdu_create(op, 507 &(resp.bindings[resp.nbindings - 1].var), &req); 508 if (op == SNMP_PDU_GETBULK) 509 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), 510 GET_NONREP(snmptoolctx)); 511 } 512 513 /* Just in case our root was a leaf. */ 514 if (outputs == 0) { 515 snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req); 516 if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) { 517 if (snmp_parse_resp(&resp,&req) < 0) 518 snmp_output_err_resp(snmptoolctx, &resp); 519 else 520 snmp_output_resp(snmptoolctx, &(resp), NULL); 521 522 snmp_pdu_free(&resp); 523 } else 524 warnx("Snmp dialog - %s", strerror(errno)); 525 } 526 527 if (snmp_object_remove(snmptoolctx, &root) < 0) { 528 warnx("snmp_object_remove"); 529 break; 530 } 531 532 snmp_pdu_create(&req, op); 533 } 534 535 if (rc == 0) 536 return (0); 537 else 538 return (1); 539 } 540 541 /* ***************************************************************************** 542 * bsnmpset private functions. 543 */ 544 545 static int32_t 546 parse_oid_numeric(struct snmp_value *value, char *val) 547 { 548 char *endptr; 549 int32_t saved_errno; 550 asn_subid_t suboid; 551 552 do { 553 saved_errno = errno; 554 errno = 0; 555 suboid = strtoul(val, &endptr, 10); 556 if (errno != 0) { 557 warnx("Value %s not supported - %s", val, 558 strerror(errno)); 559 errno = saved_errno; 560 return (-1); 561 } 562 errno = saved_errno; 563 if ((asn_subid_t) suboid > ASN_MAXID) { 564 warnx("Suboid %u > ASN_MAXID", suboid); 565 return (-1); 566 } 567 if (snmp_suboid_append(&(value->v.oid), suboid) < 0) 568 return (-1); 569 val = endptr + 1; 570 } while (*endptr == '.'); 571 572 if (*endptr != '\0') 573 warnx("OID value %s not supported", val); 574 575 value->syntax = SNMP_SYNTAX_OID; 576 return (0); 577 } 578 579 /* 580 * Allow OID leaf in both forms: 581 * 1) 1.3.6.1.2... -> in such case call directly the function reading raw OIDs; 582 * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that. 583 */ 584 static int32_t 585 parse_oid_string(struct snmp_toolinfo *snmptoolctx, 586 struct snmp_value *value, char *string) 587 { 588 struct snmp_object obj; 589 590 if (isdigit(string[0])) 591 return (parse_oid_numeric(value, string)); 592 593 memset(&obj, 0, sizeof(struct snmp_object)); 594 if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { 595 warnx("Unknown OID enum string - %s", string); 596 return (-1); 597 } 598 599 asn_append_oid(&(value->v.oid), &(obj.val.var)); 600 return (1); 601 } 602 603 static int32_t 604 parse_ip(struct snmp_value * value, char * val) 605 { 606 uint32_t v; 607 int32_t i; 608 char *endptr, *str; 609 610 str = val; 611 for (i = 0; i < 4; i++) { 612 v = strtoul(str, &endptr, 10); 613 if (v > 0xff) 614 return (-1); 615 if (*endptr != '.' && *endptr != '\0' && i != 3) 616 break; 617 str = endptr + 1; 618 value->v.ipaddress[i] = (uint8_t) v; 619 } 620 621 value->syntax = SNMP_SYNTAX_IPADDRESS; 622 return (0); 623 } 624 625 static int32_t 626 parse_int(struct snmp_value *value, char *val) 627 { 628 char *endptr; 629 int32_t v, saved_errno; 630 631 saved_errno = errno; 632 errno = 0; 633 634 v = strtol(val, &endptr, 10); 635 636 if (errno != 0) { 637 warnx("Value %s not supported - %s", val, strerror(errno)); 638 errno = saved_errno; 639 return (-1); 640 } 641 642 value->syntax = SNMP_SYNTAX_INTEGER; 643 value->v.integer = v; 644 errno = saved_errno; 645 646 return (0); 647 } 648 649 static int32_t 650 parse_int_string(struct snmp_object *object, char *val) 651 { 652 int32_t v; 653 654 if (isdigit(val[0])) 655 return ((parse_int(&(object->val), val))); 656 657 if (object->info == NULL) { 658 warnx("Unknown enumerated integer type - %s", val); 659 return (-1); 660 } 661 if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0) 662 warnx("Unknown enumerated integer type - %s", val); 663 664 object->val.v.integer = v; 665 return (1); 666 } 667 668 /* 669 * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, 670 * SNMP_SYNTAX_TIMETICKS. 671 */ 672 static int32_t 673 parse_uint(struct snmp_value *value, char *val) 674 { 675 char *endptr; 676 uint32_t v = 0; 677 int32_t saved_errno; 678 679 saved_errno = errno; 680 errno = 0; 681 682 v = strtoul(val, &endptr, 10); 683 684 if (errno != 0) { 685 warnx("Value %s not supported - %s", val, strerror(errno)); 686 errno = saved_errno; 687 return (-1); 688 } 689 690 value->v.uint32 = v; 691 errno = saved_errno; 692 693 return (0); 694 } 695 696 static int32_t 697 parse_ticks(struct snmp_value *value, char *val) 698 { 699 if (parse_uint(value, val) < 0) 700 return (-1); 701 702 value->syntax = SNMP_SYNTAX_TIMETICKS; 703 return (0); 704 } 705 706 static int32_t 707 parse_gauge(struct snmp_value *value, char *val) 708 { 709 if (parse_uint(value, val) < 0) 710 return (-1); 711 712 value->syntax = SNMP_SYNTAX_GAUGE; 713 return (0); 714 } 715 716 static int32_t 717 parse_counter(struct snmp_value *value, char *val) 718 { 719 if (parse_uint(value, val) < 0) 720 return (-1); 721 722 value->syntax = SNMP_SYNTAX_COUNTER; 723 return (0); 724 } 725 726 static int32_t 727 parse_uint64(struct snmp_value *value, char *val) 728 { 729 char *endptr; 730 int32_t saved_errno; 731 uint64_t v; 732 733 saved_errno = errno; 734 errno = 0; 735 736 v = strtoull(val, &endptr, 10); 737 738 if (errno != 0) { 739 warnx("Value %s not supported - %s", val, strerror(errno)); 740 errno = saved_errno; 741 return (-1); 742 } 743 744 value->syntax = SNMP_SYNTAX_COUNTER64; 745 value->v.counter64 = v; 746 errno = saved_errno; 747 748 return (0); 749 } 750 751 static int32_t 752 parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val) 753 { 754 switch (syntax) { 755 case SNMP_SYNTAX_INTEGER: 756 return (parse_int(value, val)); 757 case SNMP_SYNTAX_IPADDRESS: 758 return (parse_ip(value, val)); 759 case SNMP_SYNTAX_COUNTER: 760 return (parse_counter(value, val)); 761 case SNMP_SYNTAX_GAUGE: 762 return (parse_gauge(value, val)); 763 case SNMP_SYNTAX_TIMETICKS: 764 return (parse_ticks(value, val)); 765 case SNMP_SYNTAX_COUNTER64: 766 return (parse_uint64(value, val)); 767 case SNMP_SYNTAX_OCTETSTRING: 768 return (snmp_tc2oct(SNMP_STRING, value, val)); 769 case SNMP_SYNTAX_OID: 770 return (parse_oid_numeric(value, val)); 771 default: 772 /* NOTREACHED */ 773 break; 774 } 775 776 return (-1); 777 } 778 779 /* 780 * Parse a command line argument of type OID=syntax:value and fill in whatever 781 * fields can be derived from the input into snmp_value structure. Reads numeric 782 * OIDs. 783 */ 784 static int32_t 785 parse_pair_numoid_val(char *str, struct snmp_value *snmp_val) 786 { 787 int32_t cnt; 788 char *ptr; 789 enum snmp_syntax syntax; 790 char oid_str[ASN_OIDSTRLEN]; 791 792 ptr = str; 793 for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++) 794 if (ptr[cnt] == '=') 795 break; 796 797 if (cnt >= ASN_OIDSTRLEN) { 798 warnx("OID too long - %s", str); 799 return (-1); 800 } 801 strlcpy(oid_str, ptr, (size_t) (cnt + 1)); 802 803 ptr = str + cnt + 1; 804 for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++) 805 if(ptr[cnt] == ':') 806 break; 807 808 if (cnt >= MAX_CMD_SYNTAX_LEN) { 809 warnx("Unknown syntax in OID - %s", str); 810 return (-1); 811 } 812 813 if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) { 814 warnx("Unknown syntax in OID - %s", ptr); 815 return (-1); 816 } 817 818 ptr = ptr + cnt + 1; 819 for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++) 820 if (ptr[cnt] == '\0') 821 break; 822 823 if (ptr[cnt] != '\0') { 824 warnx("Value string too long - %s",ptr); 825 return (-1); 826 } 827 828 /* 829 * Here try parsing the OIDs and syntaxes and then check values - have 830 * to know syntax to check value boundaries. 831 */ 832 if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) { 833 warnx("Error parsing OID %s",oid_str); 834 return (-1); 835 } 836 837 if (parse_syntax_val(snmp_val, syntax, ptr) < 0) 838 return (-1); 839 840 return (1); 841 } 842 843 /* XXX-BZ aruments should be swapped. */ 844 static int32_t 845 parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str, 846 struct snmp_object *object) 847 { 848 uint32_t len; 849 enum snmp_syntax syn; 850 851 /* 852 * Syntax string here not required - still may be present. 853 */ 854 855 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { 856 for (len = 0 ; *(str + len) != ':'; len++) { 857 if (*(str + len) == '\0') { 858 warnx("Syntax missing in value - %s", str); 859 return (-1); 860 } 861 } 862 if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { 863 warnx("Unknown syntax in - %s", str); 864 return (-1); 865 } 866 if (syn != object->val.syntax) { 867 if (!ISSET_ERRIGNORE(snmptoolctx)) { 868 warnx("Bad syntax in - %s", str); 869 return (-1); 870 } else 871 object->val.syntax = syn; 872 } 873 len++; 874 } else 875 len = 0; 876 877 switch (object->val.syntax) { 878 case SNMP_SYNTAX_INTEGER: 879 return (parse_int_string(object, str + len)); 880 case SNMP_SYNTAX_IPADDRESS: 881 return (parse_ip(&(object->val), str + len)); 882 case SNMP_SYNTAX_COUNTER: 883 return (parse_counter(&(object->val), str + len)); 884 case SNMP_SYNTAX_GAUGE: 885 return (parse_gauge(&(object->val), str + len)); 886 case SNMP_SYNTAX_TIMETICKS: 887 return (parse_ticks(&(object->val), str + len)); 888 case SNMP_SYNTAX_COUNTER64: 889 return (parse_uint64(&(object->val), str + len)); 890 case SNMP_SYNTAX_OCTETSTRING: 891 return (snmp_tc2oct(object->info->tc, &(object->val), 892 str + len)); 893 case SNMP_SYNTAX_OID: 894 return (parse_oid_string(snmptoolctx, &(object->val), 895 str + len)); 896 default: 897 /* NOTREACHED */ 898 break; 899 } 900 901 return (-1); 902 } 903 904 static int32_t 905 parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx, 906 struct snmp_object *obj, char *argv) 907 { 908 char *ptr; 909 910 if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL) 911 return (-1); 912 913 if (*ptr != '=') { 914 warnx("Value to set expected after OID"); 915 return (-1); 916 } 917 918 if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0) 919 return (-1); 920 921 return (1); 922 } 923 924 925 static int32_t 926 snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx, 927 struct snmp_object *obj, char *argv) 928 { 929 if (argv == NULL) 930 return (-1); 931 932 if (ISSET_NUMERIC(snmptoolctx)) { 933 if (parse_pair_numoid_val(argv, &(obj->val)) < 0) 934 return (-1); 935 } else { 936 if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0) 937 return (-1); 938 } 939 940 return (1); 941 } 942 943 static int32_t 944 add_ip_syntax(struct snmp_value *dst, struct snmp_value *src) 945 { 946 int8_t i; 947 948 dst->syntax = SNMP_SYNTAX_IPADDRESS; 949 for (i = 0; i < 4; i++) 950 dst->v.ipaddress[i] = src->v.ipaddress[i]; 951 952 return (1); 953 } 954 955 static int32_t 956 add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src) 957 { 958 if (src->v.octetstring.len > ASN_MAXOCTETSTRING) { 959 warnx("OctetString len too big - %u",src->v.octetstring.len); 960 return (-1); 961 } 962 963 if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) == 964 NULL) { 965 syslog(LOG_ERR, "malloc() failed - %s", strerror(errno)); 966 return (-1); 967 } 968 969 memcpy(dst->v.octetstring.octets, src->v.octetstring.octets, 970 src->v.octetstring.len); 971 dst->syntax = SNMP_SYNTAX_OCTETSTRING; 972 dst->v.octetstring.len = src->v.octetstring.len; 973 974 return(0); 975 } 976 977 static int32_t 978 add_oid_syntax(struct snmp_value *dst, struct snmp_value *src) 979 { 980 asn_append_oid(&(dst->v.oid), &(src->v.oid)); 981 dst->syntax = SNMP_SYNTAX_OID; 982 return (0); 983 } 984 985 /* 986 * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT, 987 * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known - 988 * return error. 989 */ 990 static int32_t 991 snmpset_add_value(struct snmp_value *dst, struct snmp_value *src) 992 { 993 if (dst == NULL || src == NULL) 994 return (-1); 995 996 switch (src->syntax) { 997 case SNMP_SYNTAX_INTEGER: 998 dst->v.integer = src->v.integer; 999 dst->syntax = SNMP_SYNTAX_INTEGER; 1000 break; 1001 case SNMP_SYNTAX_TIMETICKS: 1002 dst->v.uint32 = src->v.uint32; 1003 dst->syntax = SNMP_SYNTAX_TIMETICKS; 1004 break; 1005 case SNMP_SYNTAX_GAUGE: 1006 dst->v.uint32 = src->v.uint32; 1007 dst->syntax = SNMP_SYNTAX_GAUGE; 1008 break; 1009 case SNMP_SYNTAX_COUNTER: 1010 dst->v.uint32 = src->v.uint32; 1011 dst->syntax = SNMP_SYNTAX_COUNTER; 1012 break; 1013 case SNMP_SYNTAX_COUNTER64: 1014 dst->v.counter64 = src->v.counter64; 1015 dst->syntax = SNMP_SYNTAX_COUNTER64; 1016 break; 1017 case SNMP_SYNTAX_IPADDRESS: 1018 add_ip_syntax(dst, src); 1019 break; 1020 case SNMP_SYNTAX_OCTETSTRING: 1021 add_octstring_syntax(dst, src); 1022 break; 1023 case SNMP_SYNTAX_OID: 1024 add_oid_syntax(dst, src); 1025 break; 1026 default: 1027 warnx("Unknown syntax %d", src->syntax); 1028 return (-1); 1029 } 1030 1031 return (0); 1032 } 1033 1034 static int32_t 1035 snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, 1036 struct snmp_object *obj) 1037 { 1038 if (pdu->version == SNMP_V1 && obj->val.syntax == 1039 SNMP_SYNTAX_COUNTER64) { 1040 warnx("64-bit counters are not supported in SNMPv1 PDU"); 1041 return (-1); 1042 } 1043 1044 if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx)) 1045 return (1); 1046 1047 if (obj->info->access < SNMP_ACCESS_SET) { 1048 warnx("Object %s not accessible for set - try 'bsnmpset -a'", 1049 obj->info->string); 1050 return (-1); 1051 } 1052 1053 return (1); 1054 } 1055 1056 static int32_t 1057 snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) 1058 { 1059 if (pdu->nbindings > SNMP_MAX_BINDINGS) { 1060 warnx("Too many OIDs for one PDU"); 1061 return (-1); 1062 } 1063 1064 if (obj->error > 0) 1065 return (0); 1066 1067 if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val)) 1068 < 0) 1069 return (-1); 1070 1071 asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); 1072 pdu->nbindings++; 1073 1074 return (pdu->nbindings); 1075 } 1076 1077 static int 1078 snmptool_set(struct snmp_toolinfo *snmptoolctx) 1079 { 1080 struct snmp_pdu req, resp; 1081 1082 snmp_pdu_create(&req, SNMP_PDU_SET); 1083 1084 while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind, 1085 snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { 1086 if (snmp_dialog(&req, &resp)) { 1087 warnx("Snmp dialog - %s", strerror(errno)); 1088 break; 1089 } 1090 1091 if (snmp_pdu_check(&req, &resp) > 0) { 1092 if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) 1093 snmp_output_resp(snmptoolctx, &resp, NULL); 1094 break; 1095 } 1096 1097 snmp_output_err_resp(snmptoolctx, &resp); 1098 if (!ISSET_RETRY(snmptoolctx)) 1099 break; 1100 1101 if (snmp_object_seterror(snmptoolctx, 1102 &(resp.bindings[resp.error_index - 1]), 1103 resp.error_status) <= 0) 1104 break; 1105 1106 fprintf(stderr, "Retrying...\n"); 1107 snmp_pdu_free(&req); 1108 snmp_pdu_free(&resp); 1109 snmp_pdu_create(&req, SNMP_PDU_SET); 1110 } 1111 1112 snmp_pdu_free(&resp); 1113 1114 return (0); 1115 } 1116 1117 /* ***************************************************************************** 1118 * main 1119 */ 1120 /* 1121 * According to command line options prepare SNMP Get | GetNext | GetBulk PDU. 1122 * Wait for a response and print it. 1123 */ 1124 /* 1125 * Do a 'snmp walk' - according to command line options request for values 1126 * lexicographically subsequent and subrooted at a common node. Send a GetNext 1127 * PDU requesting the value for each next variable and print the response. Stop 1128 * when a Response PDU is received that contains the value of a variable not 1129 * subrooted at the variable the walk started. 1130 */ 1131 int 1132 main(int argc, char ** argv) 1133 { 1134 struct snmp_toolinfo snmptoolctx; 1135 int32_t oid_cnt, last_oid, opt_num; 1136 int rc = 0; 1137 1138 /* Make sure program_name is set and valid. */ 1139 if (*argv == NULL) 1140 program_name = "snmptool"; 1141 else { 1142 program_name = strrchr(*argv, '/'); 1143 if (program_name != NULL) 1144 program_name++; 1145 else 1146 program_name = *argv; 1147 } 1148 1149 if (program_name == NULL) { 1150 fprintf(stderr, "Error: No program name?\n"); 1151 exit (1); 1152 } else if (strcmp(program_name, "bsnmpget") == 0) 1153 program = BSNMPGET; 1154 else if (strcmp(program_name, "bsnmpwalk") == 0) 1155 program = BSNMPWALK; 1156 else if (strcmp(program_name, "bsnmpset") == 0) 1157 program = BSNMPSET; 1158 else { 1159 fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name); 1160 exit (1); 1161 } 1162 1163 /* Initialize. */ 1164 if (snmptool_init(&snmptoolctx) < 0) 1165 exit (1); 1166 1167 if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) { 1168 snmp_tool_freeall(&snmptoolctx); 1169 /* On -h (help) exit without error. */ 1170 if (opt_num == -2) 1171 exit(0); 1172 else 1173 exit(1); 1174 } 1175 1176 oid_cnt = argc - opt_num - 1; 1177 if (oid_cnt == 0) { 1178 switch (program) { 1179 case BSNMPGET: 1180 if (!ISSET_EDISCOVER(&snmptoolctx) && 1181 !ISSET_LOCALKEY(&snmptoolctx)) { 1182 fprintf(stderr, "No OID given.\n"); 1183 usage(); 1184 snmp_tool_freeall(&snmptoolctx); 1185 exit(1); 1186 } 1187 break; 1188 1189 case BSNMPWALK: 1190 if (snmp_object_add(&snmptoolctx, snmpwalk_add_default, 1191 NULL) < 0) { 1192 fprintf(stderr, 1193 "Error setting default subtree.\n"); 1194 snmp_tool_freeall(&snmptoolctx); 1195 exit(1); 1196 } 1197 break; 1198 1199 case BSNMPSET: 1200 fprintf(stderr, "No OID given.\n"); 1201 usage(); 1202 snmp_tool_freeall(&snmptoolctx); 1203 exit(1); 1204 } 1205 } 1206 1207 if (snmp_import_all(&snmptoolctx) < 0) { 1208 snmp_tool_freeall(&snmptoolctx); 1209 exit(1); 1210 } 1211 1212 /* A simple sanity check - can not send GETBULK when using SNMPv1. */ 1213 if (program == BSNMPGET && snmp_client.version == SNMP_V1 && 1214 GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) { 1215 fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n"); 1216 snmp_tool_freeall(&snmptoolctx); 1217 exit(1); 1218 } 1219 1220 for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) { 1221 if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ? 1222 snmpset_parse_oid : snmptools_parse_oid, 1223 argv[last_oid])) < 0) { 1224 fprintf(stderr, "Error parsing OID string '%s'.\n", 1225 argv[last_oid]); 1226 snmp_tool_freeall(&snmptoolctx); 1227 exit(1); 1228 } 1229 } 1230 1231 if (snmp_open(NULL, NULL, NULL, NULL)) { 1232 warnx("Failed to open snmp session: %s.", strerror(errno)); 1233 snmp_tool_freeall(&snmptoolctx); 1234 exit(1); 1235 } 1236 1237 if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0) 1238 SET_EDISCOVER(&snmptoolctx); 1239 1240 if (ISSET_EDISCOVER(&snmptoolctx) && 1241 snmp_discover_engine(snmptoolctx.passwd) < 0) { 1242 warnx("Unknown SNMP Engine ID: %s.", strerror(errno)); 1243 rc = 1; 1244 goto cleanup; 1245 } 1246 1247 if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || 1248 ISSET_EDISCOVER(&snmptoolctx)) 1249 snmp_output_engine(); 1250 1251 if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) && 1252 !ISSET_EDISCOVER(&snmptoolctx)) { 1253 if (snmp_passwd_to_keys(&snmp_client.user, 1254 snmptoolctx.passwd) != SNMP_CODE_OK || 1255 snmp_get_local_keys(&snmp_client.user, 1256 snmp_client.engine.engine_id, 1257 snmp_client.engine.engine_len) != SNMP_CODE_OK) { 1258 warnx("Failed to get keys: %s.", strerror(errno)); 1259 rc = 1; 1260 goto cleanup; 1261 } 1262 } 1263 1264 if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || 1265 ISSET_EDISCOVER(&snmptoolctx)) 1266 snmp_output_keys(); 1267 1268 if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0) 1269 goto cleanup; 1270 1271 switch (program) { 1272 case BSNMPGET: 1273 rc = snmptool_get(&snmptoolctx); 1274 break; 1275 case BSNMPWALK: 1276 rc = snmptool_walk(&snmptoolctx); 1277 break; 1278 case BSNMPSET: 1279 rc = snmptool_set(&snmptoolctx); 1280 break; 1281 } 1282 1283 1284 cleanup: 1285 snmp_tool_freeall(&snmptoolctx); 1286 snmp_close(); 1287 1288 exit(rc); 1289 } 1290