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