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