1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * The snmp library helps to prepare the PDUs and communicate with 31 * the snmp agent on the SP side via the ds_snmp driver. 32 */ 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <thread.h> 39 #include <synch.h> 40 #include <errno.h> 41 #include <sys/time.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <fcntl.h> 45 #include <libnvpair.h> 46 #include <sys/ds_snmp.h> 47 48 #include "libpiclsnmp.h" 49 #include "snmplib.h" 50 #include "asn1.h" 51 #include "pdu.h" 52 #include "debug.h" 53 54 #pragma init(libpiclsnmp_init) /* need this in .init */ 55 56 /* 57 * Data from the MIB is fetched based on the hints about object 58 * groups received from (possibly many threads in) the application. 59 * However, the fetched data is kept in a common cache for use across 60 * all threads, so even a GETBULK is issued only when absolutely 61 * necessary. 62 * 63 * Note that locking is not fine grained (there's no locking per row) 64 * since we don't expect too many MT consumers right away. 65 * 66 */ 67 static mutex_t mibcache_lock; 68 static nvlist_t **mibcache = NULL; 69 static uint_t n_mibcache_rows = 0; 70 71 static mutex_t snmp_reqid_lock; 72 static int snmp_reqid = 1; 73 74 #ifdef SNMP_DEBUG 75 uint_t snmp_nsends = 0; 76 uint_t snmp_sentbytes = 0; 77 uint_t snmp_nrecvs = 0; 78 uint_t snmp_rcvdbytes = 0; 79 #endif 80 81 #ifdef USE_SOCKETS 82 #define SNMP_DEFAULT_PORT 161 83 #define SNMP_MAX_RECV_PKTSZ (64 * 1024) 84 #endif 85 86 /* 87 * Static function declarations 88 */ 89 static void libpiclsnmp_init(void); 90 91 static int lookup_int(char *, int, int *, int); 92 static int lookup_str(char *, int, char **, int); 93 static int lookup_bitstr(char *, int, uchar_t **, uint_t *, int); 94 95 static oidgroup_t *locate_oid_group(struct picl_snmphdl *, char *); 96 static int search_oid_in_group(char *, char *, int); 97 98 static snmp_pdu_t *fetch_single(struct picl_snmphdl *, char *, int, int *); 99 static snmp_pdu_t *fetch_next(struct picl_snmphdl *, char *, int, int *); 100 static void fetch_bulk(struct picl_snmphdl *, char *, int, int, int, int *); 101 static int fetch_single_str(struct picl_snmphdl *, char *, int, 102 char **, int *); 103 static int fetch_single_int(struct picl_snmphdl *, char *, int, 104 int *, int *); 105 static int fetch_single_bitstr(struct picl_snmphdl *, char *, int, 106 uchar_t **, uint_t *, int *); 107 108 static int snmp_send_request(struct picl_snmphdl *, snmp_pdu_t *, int *); 109 static int snmp_recv_reply(struct picl_snmphdl *, snmp_pdu_t *, int *); 110 111 static int mibcache_realloc(int); 112 static void mibcache_populate(snmp_pdu_t *, int); 113 static char *oid_to_oidstr(oid *, size_t); 114 115 116 static void 117 libpiclsnmp_init(void) 118 { 119 (void) mutex_init(&mibcache_lock, USYNC_THREAD, NULL); 120 if (mibcache_realloc(0) < 0) 121 (void) mutex_destroy(&mibcache_lock); 122 123 (void) mutex_init(&snmp_reqid_lock, USYNC_THREAD, NULL); 124 125 LOGINIT(); 126 } 127 128 picl_snmphdl_t 129 snmp_init() 130 { 131 struct picl_snmphdl *smd; 132 #ifdef USE_SOCKETS 133 int sbuf = (1 << 15); /* 16K */ 134 int rbuf = (1 << 17); /* 64K */ 135 char *snmp_agent_addr; 136 #endif 137 138 smd = (struct picl_snmphdl *)calloc(1, sizeof (struct picl_snmphdl)); 139 if (smd == NULL) 140 return (NULL); 141 142 #ifdef USE_SOCKETS 143 if ((snmp_agent_addr = getenv("SNMP_AGENT_IPADDR")) == NULL) 144 return (NULL); 145 146 if ((smd->fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) 147 return (NULL); 148 149 (void) setsockopt(smd->fd, SOL_SOCKET, SO_SNDBUF, &sbuf, sizeof (int)); 150 (void) setsockopt(smd->fd, SOL_SOCKET, SO_RCVBUF, &rbuf, sizeof (int)); 151 152 memset(&smd->agent_addr, 0, sizeof (struct sockaddr_in)); 153 smd->agent_addr.sin_family = AF_INET; 154 smd->agent_addr.sin_port = htons(SNMP_DEFAULT_PORT); 155 smd->agent_addr.sin_addr.s_addr = inet_addr(snmp_agent_addr); 156 #else 157 smd->fd = open(DS_SNMP_DRIVER, O_RDWR); 158 if (smd->fd < 0) { 159 free(smd); 160 return (NULL); 161 } 162 #endif 163 164 return ((picl_snmphdl_t)smd); 165 } 166 167 void 168 snmp_fini(picl_snmphdl_t hdl) 169 { 170 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 171 172 if (smd) { 173 if (smd->fd >= 0) { 174 (void) close(smd->fd); 175 } 176 free(smd); 177 } 178 } 179 180 int 181 snmp_reinit(picl_snmphdl_t hdl, int clr_linkreset) 182 { 183 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 184 nvlist_t *nvl; 185 int i; 186 187 (void) mutex_lock(&mibcache_lock); 188 189 for (i = 0; i < n_mibcache_rows; i++) { 190 if ((nvl = mibcache[i]) != NULL) 191 nvlist_free(nvl); 192 } 193 194 n_mibcache_rows = 0; 195 if (mibcache) { 196 free(mibcache); 197 mibcache = NULL; 198 } 199 200 (void) mutex_unlock(&mibcache_lock); 201 202 if (clr_linkreset) { 203 if (smd == NULL || smd->fd < 0) 204 return (-1); 205 else 206 return (ioctl(smd->fd, DSSNMP_CLRLNKRESET, NULL)); 207 } 208 209 return (0); 210 } 211 212 void 213 snmp_register_group(picl_snmphdl_t hdl, char *oidstrs, int n_oids, int is_vol) 214 { 215 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 216 oidgroup_t *oidg; 217 oidgroup_t *curr, *prev; 218 char *p; 219 int i, sz; 220 221 /* 222 * Allocate a new oidgroup_t 223 */ 224 oidg = (oidgroup_t *)calloc(1, sizeof (struct oidgroup)); 225 if (oidg == NULL) 226 return; 227 228 /* 229 * Determine how much space is required to register this group 230 */ 231 sz = 0; 232 p = oidstrs; 233 for (i = 0; i < n_oids; i++) { 234 sz += strlen(p) + 1; 235 p = oidstrs + sz; 236 } 237 238 /* 239 * Create this oid group 240 */ 241 if ((p = (char *)malloc(sz)) == NULL) { 242 free((void *) oidg); 243 return; 244 } 245 246 (void) memcpy(p, oidstrs, sz); 247 248 oidg->next = NULL; 249 oidg->oidstrs = p; 250 oidg->n_oids = n_oids; 251 oidg->is_volatile = is_vol; 252 253 /* 254 * Link it to the tail of the list of oid groups 255 */ 256 for (prev = NULL, curr = smd->group; curr; curr = curr->next) 257 prev = curr; 258 259 if (prev == NULL) 260 smd->group = oidg; 261 else 262 prev->next = oidg; 263 } 264 265 /* 266 * snmp_get_int() takes in an OID and returns the integer value 267 * of the object referenced in the passed arg. It returns 0 on 268 * success and -1 on failure. 269 */ 270 int 271 snmp_get_int(picl_snmphdl_t hdl, char *prefix, int row, int *val, 272 int *snmp_syserr) 273 { 274 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 275 oidgroup_t *grp; 276 int ret; 277 int err = 0; 278 279 if (smd == NULL || prefix == NULL || val == NULL) 280 return (-1); 281 282 /* 283 * If this item should not be cached, fetch it directly from 284 * the agent using fetch_single_xxx() 285 */ 286 if ((grp = locate_oid_group(smd, prefix)) == NULL) { 287 ret = fetch_single_int(smd, prefix, row, val, &err); 288 289 if (snmp_syserr) 290 *snmp_syserr = err; 291 292 return (ret); 293 } 294 295 /* 296 * is it in the cache ? 297 */ 298 if (lookup_int(prefix, row, val, grp->is_volatile) == 0) 299 return (0); 300 301 /* 302 * fetch it from the agent and populate the cache 303 */ 304 fetch_bulk(smd, grp->oidstrs, grp->n_oids, row, grp->is_volatile, &err); 305 if (snmp_syserr) 306 *snmp_syserr = err; 307 308 /* 309 * look it up again and return it 310 */ 311 if (lookup_int(prefix, row, val, grp->is_volatile) < 0) 312 return (-1); 313 314 return (0); 315 } 316 317 /* 318 * snmp_get_str() takes in an OID and returns the string value 319 * of the object referenced in the passed arg. Memory for the string 320 * is allocated within snmp_get_str() and is expected to be freed by 321 * the caller when it is no longer needed. The function returns 0 322 * on success and -1 on failure. 323 */ 324 int 325 snmp_get_str(picl_snmphdl_t hdl, char *prefix, int row, char **strp, 326 int *snmp_syserr) 327 { 328 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 329 oidgroup_t *grp; 330 char *val; 331 int ret; 332 int err = 0; 333 334 if (smd == NULL || prefix == NULL || strp == NULL) 335 return (-1); 336 337 *strp = NULL; 338 /* 339 * Check if this item is cacheable or not. If not, call 340 * fetch_single_* to get it directly from the agent 341 */ 342 if ((grp = locate_oid_group(smd, prefix)) == NULL) { 343 ret = fetch_single_str(smd, prefix, row, strp, &err); 344 345 if (snmp_syserr) 346 *snmp_syserr = err; 347 348 return (ret); 349 } 350 351 /* 352 * See if it's in the cache already 353 */ 354 if (lookup_str(prefix, row, &val, grp->is_volatile) == 0) { 355 if ((*strp = strdup(val)) == NULL) 356 return (-1); 357 else 358 return (0); 359 } 360 361 /* 362 * Fetch it from the agent and populate cache 363 */ 364 fetch_bulk(smd, grp->oidstrs, grp->n_oids, row, grp->is_volatile, &err); 365 if (snmp_syserr) 366 *snmp_syserr = err; 367 368 /* 369 * Retry lookup 370 */ 371 if (lookup_str(prefix, row, &val, grp->is_volatile) < 0) 372 return (-1); 373 374 375 if ((*strp = strdup(val)) == NULL) 376 return (-1); 377 else 378 return (0); 379 } 380 381 /* 382 * snmp_get_bitstr() takes in an OID and returns the bit string value 383 * of the object referenced in the passed args. Memory for the bitstring 384 * is allocated within the function and is expected to be freed by 385 * the caller when it is no longer needed. The function returns 0 386 * on success and -1 on failure. 387 */ 388 int 389 snmp_get_bitstr(picl_snmphdl_t hdl, char *prefix, int row, uchar_t **bitstrp, 390 uint_t *nbytes, int *snmp_syserr) 391 { 392 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 393 oidgroup_t *grp; 394 uchar_t *val; 395 int ret; 396 int err = 0; 397 398 if (smd == NULL || prefix == NULL || bitstrp == NULL || nbytes == NULL) 399 return (-1); 400 401 *bitstrp = NULL; 402 /* 403 * Check if this item is cacheable or not. If not, call 404 * fetch_single_* to get it directly from the agent 405 */ 406 if ((grp = locate_oid_group(smd, prefix)) == NULL) { 407 ret = fetch_single_bitstr(smd, prefix, row, bitstrp, 408 nbytes, &err); 409 410 if (snmp_syserr) 411 *snmp_syserr = err; 412 413 return (ret); 414 } 415 416 /* 417 * See if it's in the cache already 418 */ 419 if (lookup_bitstr(prefix, row, &val, nbytes, grp->is_volatile) == 0) { 420 if ((*bitstrp = (uchar_t *)calloc(*nbytes, 1)) == NULL) 421 return (-1); 422 (void) memcpy(*bitstrp, (const void *)val, *nbytes); 423 return (0); 424 } 425 426 /* 427 * Fetch it from the agent and populate cache 428 */ 429 fetch_bulk(smd, grp->oidstrs, grp->n_oids, row, grp->is_volatile, &err); 430 if (snmp_syserr) 431 *snmp_syserr = err; 432 433 /* 434 * Retry lookup 435 */ 436 if (lookup_bitstr(prefix, row, &val, nbytes, grp->is_volatile) < 0) 437 return (-1); 438 439 if ((*bitstrp = (uchar_t *)calloc(*nbytes, 1)) == NULL) 440 return (-1); 441 (void) memcpy(*bitstrp, (const void *)val, *nbytes); 442 443 return (0); 444 } 445 446 /* 447 * snmp_get_nextrow() is similar in operation to SNMP_GETNEXT, but 448 * only just. In particular, this is only expected to return the next 449 * valid row number for the same object, not its value. Since we don't 450 * have any other means, we use this to determine the number of rows 451 * in the table (and the valid ones). This function returns 0 on success 452 * and -1 on failure. 453 */ 454 int 455 snmp_get_nextrow(picl_snmphdl_t hdl, char *prefix, int row, int *nextrow, 456 int *snmp_syserr) 457 { 458 struct picl_snmphdl *smd = (struct picl_snmphdl *)hdl; 459 snmp_pdu_t *reply_pdu; 460 pdu_varlist_t *vp; 461 char *nxt_oidstr; 462 int err = 0; 463 464 if (smd == NULL || prefix == NULL || nextrow == NULL) { 465 if (snmp_syserr) 466 *snmp_syserr = EINVAL; 467 return (-1); 468 } 469 470 /* 471 * The get_nextrow results should *never* go into any cache, 472 * since these relationships are dynamically discovered each time. 473 */ 474 if ((reply_pdu = fetch_next(smd, prefix, row, &err)) == NULL) { 475 if (snmp_syserr) 476 *snmp_syserr = err; 477 return (-1); 478 } 479 480 /* 481 * We are not concerned about the "value" of the lexicographically 482 * next object; we only care about the name of that object and 483 * its row number (and whether such an object exists or not). 484 */ 485 vp = reply_pdu->vars; 486 487 /* 488 * This indicates that we're at the end of the MIB view. 489 */ 490 if (vp == NULL || vp->name == NULL || vp->type == SNMP_NOSUCHOBJECT || 491 vp->type == SNMP_NOSUCHINSTANCE || vp->type == SNMP_ENDOFMIBVIEW) { 492 snmp_free_pdu(reply_pdu); 493 if (snmp_syserr) 494 *snmp_syserr = ENOSPC; 495 return (-1); 496 } 497 498 /* 499 * need to be able to convert the OID 500 */ 501 if ((nxt_oidstr = oid_to_oidstr(vp->name, vp->name_len - 1)) == NULL) { 502 snmp_free_pdu(reply_pdu); 503 if (snmp_syserr) 504 *snmp_syserr = ENOMEM; 505 return (-1); 506 } 507 508 /* 509 * We're on to the next table. 510 */ 511 if (strcmp(nxt_oidstr, prefix) != 0) { 512 free(nxt_oidstr); 513 snmp_free_pdu(reply_pdu); 514 if (snmp_syserr) 515 *snmp_syserr = ENOENT; 516 return (-1); 517 } 518 519 /* 520 * Ok, so we've got an oid that's simply the next valid row of the 521 * passed on object, return this row number. 522 */ 523 *nextrow = (vp->name)[vp->name_len-1]; 524 525 free(nxt_oidstr); 526 snmp_free_pdu(reply_pdu); 527 528 return (0); 529 } 530 531 /* 532 * Request ids for snmp messages to the agent are sequenced here. 533 */ 534 int 535 snmp_get_reqid(void) 536 { 537 int ret; 538 539 (void) mutex_lock(&snmp_reqid_lock); 540 541 ret = snmp_reqid++; 542 543 (void) mutex_unlock(&snmp_reqid_lock); 544 545 return (ret); 546 } 547 548 static int 549 lookup_int(char *prefix, int row, int *valp, int is_vol) 550 { 551 int32_t *val_arr; 552 uint_t nelem; 553 struct timeval tv; 554 int elapsed; 555 556 (void) mutex_lock(&mibcache_lock); 557 558 if (row >= n_mibcache_rows) { 559 (void) mutex_unlock(&mibcache_lock); 560 return (-1); 561 } 562 563 if (mibcache[row] == NULL) { 564 (void) mutex_unlock(&mibcache_lock); 565 return (-1); 566 } 567 568 /* 569 * If this is a volatile property, we should be searching 570 * for an integer-timestamp pair 571 */ 572 if (is_vol) { 573 if (nvlist_lookup_int32_array(mibcache[row], prefix, 574 &val_arr, &nelem) != 0) { 575 (void) mutex_unlock(&mibcache_lock); 576 return (-1); 577 } 578 if (nelem != 2 || val_arr[1] < 0) { 579 (void) mutex_unlock(&mibcache_lock); 580 return (-1); 581 } 582 if (gettimeofday(&tv, NULL) < 0) { 583 (void) mutex_unlock(&mibcache_lock); 584 return (-1); 585 } 586 elapsed = tv.tv_sec - val_arr[1]; 587 if (elapsed < 0 || elapsed > MAX_INCACHE_TIME) { 588 (void) mutex_unlock(&mibcache_lock); 589 return (-1); 590 } 591 592 *valp = (int)val_arr[0]; 593 } else { 594 if (nvlist_lookup_int32(mibcache[row], prefix, valp) != 0) { 595 (void) mutex_unlock(&mibcache_lock); 596 return (-1); 597 } 598 } 599 600 (void) mutex_unlock(&mibcache_lock); 601 602 return (0); 603 } 604 605 static int 606 lookup_str(char *prefix, int row, char **valp, int is_vol) 607 { 608 char **val_arr; 609 uint_t nelem; 610 struct timeval tv; 611 int elapsed; 612 613 (void) mutex_lock(&mibcache_lock); 614 615 if (row >= n_mibcache_rows) { 616 (void) mutex_unlock(&mibcache_lock); 617 return (-1); 618 } 619 620 if (mibcache[row] == NULL) { 621 (void) mutex_unlock(&mibcache_lock); 622 return (-1); 623 } 624 625 /* 626 * If this is a volatile property, we should be searching 627 * for a string-timestamp pair 628 */ 629 if (is_vol) { 630 if (nvlist_lookup_string_array(mibcache[row], prefix, 631 &val_arr, &nelem) != 0) { 632 (void) mutex_unlock(&mibcache_lock); 633 return (-1); 634 } 635 if (nelem != 2 || atoi(val_arr[1]) <= 0) { 636 (void) mutex_unlock(&mibcache_lock); 637 return (-1); 638 } 639 if (gettimeofday(&tv, NULL) < 0) { 640 (void) mutex_unlock(&mibcache_lock); 641 return (-1); 642 } 643 elapsed = tv.tv_sec - atoi(val_arr[1]); 644 if (elapsed < 0 || elapsed > MAX_INCACHE_TIME) { 645 (void) mutex_unlock(&mibcache_lock); 646 return (-1); 647 } 648 649 *valp = val_arr[0]; 650 } else { 651 if (nvlist_lookup_string(mibcache[row], prefix, valp) != 0) { 652 (void) mutex_unlock(&mibcache_lock); 653 return (-1); 654 } 655 } 656 657 (void) mutex_unlock(&mibcache_lock); 658 659 return (0); 660 } 661 662 static int 663 lookup_bitstr(char *prefix, int row, uchar_t **valp, uint_t *nelem, int is_vol) 664 { 665 (void) mutex_lock(&mibcache_lock); 666 667 if (row >= n_mibcache_rows) { 668 (void) mutex_unlock(&mibcache_lock); 669 return (-1); 670 } 671 672 if (mibcache[row] == NULL) { 673 (void) mutex_unlock(&mibcache_lock); 674 return (-1); 675 } 676 677 /* 678 * We don't support volatile bit string values yet. The nvlist 679 * functions don't support bitstring arrays like they do charstring 680 * arrays, so we would need to do things in a convoluted way, 681 * probably by attaching the timestamp as part of the byte array 682 * itself. However, the need for volatile bitstrings isn't there 683 * yet, to justify the effort. 684 */ 685 if (is_vol) { 686 (void) mutex_unlock(&mibcache_lock); 687 return (-1); 688 } 689 690 if (nvlist_lookup_byte_array(mibcache[row], prefix, valp, nelem) != 0) { 691 (void) mutex_unlock(&mibcache_lock); 692 return (-1); 693 } 694 695 (void) mutex_unlock(&mibcache_lock); 696 697 return (0); 698 } 699 700 static int 701 search_oid_in_group(char *prefix, char *oidstrs, int n_oids) 702 { 703 char *p; 704 int i; 705 706 p = oidstrs; 707 for (i = 0; i < n_oids; i++) { 708 if (strcmp(p, prefix) == 0) 709 return (0); 710 711 p += strlen(p) + 1; 712 } 713 714 return (-1); 715 } 716 717 static oidgroup_t * 718 locate_oid_group(struct picl_snmphdl *smd, char *prefix) 719 { 720 oidgroup_t *grp; 721 722 if (smd == NULL) 723 return (NULL); 724 725 if (smd->group == NULL) 726 return (NULL); 727 728 for (grp = smd->group; grp; grp = grp->next) { 729 if (search_oid_in_group(prefix, grp->oidstrs, 730 grp->n_oids) == 0) { 731 return (grp); 732 } 733 } 734 735 return (NULL); 736 } 737 738 static int 739 fetch_single_int(struct picl_snmphdl *smd, char *prefix, int row, int *ival, 740 int *snmp_syserr) 741 { 742 snmp_pdu_t *reply_pdu; 743 pdu_varlist_t *vp; 744 745 if ((reply_pdu = fetch_single(smd, prefix, row, snmp_syserr)) == NULL) 746 return (-1); 747 748 /* 749 * Note that we don't make any distinction between unsigned int 750 * value and signed int value at this point, since we provide 751 * only snmp_get_int() at the higher level. While it is possible 752 * to provide an entirely separate interface such as snmp_get_uint(), 753 * that's quite unnecessary, because we don't do any interpretation 754 * of the received value. Besides, the sizes of int and uint are 755 * the same and the sizes of all pointers are the same (so val.iptr 756 * would be the same as val.uiptr in pdu_varlist_t). If/when we 757 * violate any of these assumptions, it will be time to add 758 * snmp_get_uint(). 759 */ 760 vp = reply_pdu->vars; 761 if (vp == NULL || vp->val.iptr == NULL) { 762 snmp_free_pdu(reply_pdu); 763 return (-1); 764 } 765 766 *ival = *(vp->val.iptr); 767 768 snmp_free_pdu(reply_pdu); 769 770 return (0); 771 } 772 773 static int 774 fetch_single_str(struct picl_snmphdl *smd, char *prefix, int row, char **valp, 775 int *snmp_syserr) 776 { 777 snmp_pdu_t *reply_pdu; 778 pdu_varlist_t *vp; 779 780 if ((reply_pdu = fetch_single(smd, prefix, row, snmp_syserr)) == NULL) 781 return (-1); 782 783 vp = reply_pdu->vars; 784 if (vp == NULL || vp->val.str == NULL) { 785 snmp_free_pdu(reply_pdu); 786 return (-1); 787 } 788 789 *valp = strdup((const char *)(vp->val.str)); 790 791 snmp_free_pdu(reply_pdu); 792 793 return (0); 794 } 795 796 static int 797 fetch_single_bitstr(struct picl_snmphdl *smd, char *prefix, int row, 798 uchar_t **valp, uint_t *nelem, int *snmp_syserr) 799 { 800 snmp_pdu_t *reply_pdu; 801 pdu_varlist_t *vp; 802 803 if ((reply_pdu = fetch_single(smd, prefix, row, snmp_syserr)) == NULL) 804 return (-1); 805 806 vp = reply_pdu->vars; 807 if (vp == NULL || vp->val.str == NULL) { 808 snmp_free_pdu(reply_pdu); 809 return (-1); 810 } 811 812 if ((*valp = (uchar_t *)calloc(vp->val_len, 1)) == NULL) { 813 snmp_free_pdu(reply_pdu); 814 return (-1); 815 } 816 817 *nelem = vp->val_len; 818 (void) memcpy(*valp, (const void *)(vp->val.str), 819 (size_t)(vp->val_len)); 820 821 snmp_free_pdu(reply_pdu); 822 823 return (0); 824 } 825 826 static snmp_pdu_t * 827 fetch_single(struct picl_snmphdl *smd, char *prefix, int row, int *snmp_syserr) 828 { 829 snmp_pdu_t *pdu, *reply_pdu; 830 831 LOGGET(TAG_CMD_REQUEST, prefix, row); 832 833 if ((pdu = snmp_create_pdu(SNMP_MSG_GET, 0, prefix, 1, row)) == NULL) 834 return (NULL); 835 836 LOGPDU(TAG_REQUEST_PDU, pdu); 837 838 if (snmp_make_packet(pdu) < 0) { 839 snmp_free_pdu(pdu); 840 return (NULL); 841 } 842 843 LOGPKT(TAG_REQUEST_PKT, pdu->req_pkt, pdu->req_pktsz); 844 845 if (snmp_send_request(smd, pdu, snmp_syserr) < 0) { 846 snmp_free_pdu(pdu); 847 return (NULL); 848 } 849 850 if (snmp_recv_reply(smd, pdu, snmp_syserr) < 0) { 851 snmp_free_pdu(pdu); 852 return (NULL); 853 } 854 855 LOGPKT(TAG_RESPONSE_PKT, pdu->reply_pkt, pdu->reply_pktsz); 856 857 reply_pdu = snmp_parse_reply(pdu->reqid, pdu->reply_pkt, 858 pdu->reply_pktsz); 859 860 LOGPDU(TAG_RESPONSE_PDU, reply_pdu); 861 862 snmp_free_pdu(pdu); 863 864 return (reply_pdu); 865 } 866 867 static void 868 fetch_bulk(struct picl_snmphdl *smd, char *oidstrs, int n_oids, 869 int row, int is_vol, int *snmp_syserr) 870 { 871 snmp_pdu_t *pdu, *reply_pdu; 872 int max_reps; 873 874 LOGBULK(TAG_CMD_REQUEST, n_oids, oidstrs, row); 875 876 /* 877 * If we're fetching volatile properties using BULKGET, don't 878 * venture to get multiple rows (passing max_reps=0 will make 879 * snmp_create_pdu() fetch SNMP_DEF_MAX_REPETITIONS rows) 880 */ 881 max_reps = is_vol ? 1 : 0; 882 883 pdu = snmp_create_pdu(SNMP_MSG_GETBULK, max_reps, oidstrs, n_oids, row); 884 if (pdu == NULL) 885 return; 886 887 LOGPDU(TAG_REQUEST_PDU, pdu); 888 889 /* 890 * Make an ASN.1 encoded packet from the PDU information 891 */ 892 if (snmp_make_packet(pdu) < 0) { 893 snmp_free_pdu(pdu); 894 return; 895 } 896 897 LOGPKT(TAG_REQUEST_PKT, pdu->req_pkt, pdu->req_pktsz); 898 899 /* 900 * Send the request packet to the agent 901 */ 902 if (snmp_send_request(smd, pdu, snmp_syserr) < 0) { 903 snmp_free_pdu(pdu); 904 return; 905 } 906 907 /* 908 * Receive response from the agent into the reply packet buffer 909 * in the request PDU 910 */ 911 if (snmp_recv_reply(smd, pdu, snmp_syserr) < 0) { 912 snmp_free_pdu(pdu); 913 return; 914 } 915 916 LOGPKT(TAG_RESPONSE_PKT, pdu->reply_pkt, pdu->reply_pktsz); 917 918 /* 919 * Parse the reply, validate the response and create a 920 * reply-PDU out of the information. Populate the mibcache 921 * with the received values. 922 */ 923 reply_pdu = snmp_parse_reply(pdu->reqid, pdu->reply_pkt, 924 pdu->reply_pktsz); 925 if (reply_pdu) { 926 LOGPDU(TAG_RESPONSE_PDU, reply_pdu); 927 928 if (reply_pdu->errstat == SNMP_ERR_NOERROR) 929 mibcache_populate(reply_pdu, is_vol); 930 931 snmp_free_pdu(reply_pdu); 932 } 933 934 snmp_free_pdu(pdu); 935 } 936 937 static snmp_pdu_t * 938 fetch_next(struct picl_snmphdl *smd, char *prefix, int row, int *snmp_syserr) 939 { 940 snmp_pdu_t *pdu, *reply_pdu; 941 942 LOGNEXT(TAG_CMD_REQUEST, prefix, row); 943 944 pdu = snmp_create_pdu(SNMP_MSG_GETNEXT, 0, prefix, 1, row); 945 if (pdu == NULL) 946 return (NULL); 947 948 LOGPDU(TAG_REQUEST_PDU, pdu); 949 950 if (snmp_make_packet(pdu) < 0) { 951 snmp_free_pdu(pdu); 952 return (NULL); 953 } 954 955 LOGPKT(TAG_REQUEST_PKT, pdu->req_pkt, pdu->req_pktsz); 956 957 if (snmp_send_request(smd, pdu, snmp_syserr) < 0) { 958 snmp_free_pdu(pdu); 959 return (NULL); 960 } 961 962 if (snmp_recv_reply(smd, pdu, snmp_syserr) < 0) { 963 snmp_free_pdu(pdu); 964 return (NULL); 965 } 966 967 LOGPKT(TAG_RESPONSE_PKT, pdu->reply_pkt, pdu->reply_pktsz); 968 969 reply_pdu = snmp_parse_reply(pdu->reqid, pdu->reply_pkt, 970 pdu->reply_pktsz); 971 972 LOGPDU(TAG_RESPONSE_PDU, reply_pdu); 973 974 snmp_free_pdu(pdu); 975 976 return (reply_pdu); 977 } 978 979 static int 980 snmp_send_request(struct picl_snmphdl *smd, snmp_pdu_t *pdu, int *snmp_syserr) 981 { 982 extern int errno; 983 #ifdef USE_SOCKETS 984 int ret; 985 #endif 986 987 if (smd->fd < 0) 988 return (-1); 989 990 if (pdu == NULL || pdu->req_pkt == NULL) 991 return (-1); 992 993 #ifdef USE_SOCKETS 994 ret = -1; 995 while (ret < 0) { 996 LOGIO(TAG_SENDTO, smd->fd, pdu->req_pkt, pdu->req_pktsz); 997 998 ret = sendto(smd->fd, pdu->req_pkt, pdu->req_pktsz, 0, 999 (struct sockaddr *)&smd->agent_addr, 1000 sizeof (struct sockaddr)); 1001 if (ret < 0 && errno != EINTR) { 1002 return (-1); 1003 } 1004 } 1005 #else 1006 LOGIO(TAG_WRITE, smd->fd, pdu->req_pkt, pdu->req_pktsz); 1007 1008 if (write(smd->fd, pdu->req_pkt, pdu->req_pktsz) < 0) { 1009 if (snmp_syserr) 1010 *snmp_syserr = errno; 1011 return (-1); 1012 } 1013 #endif 1014 1015 #ifdef SNMP_DEBUG 1016 snmp_nsends++; 1017 snmp_sentbytes += pdu->req_pktsz; 1018 #endif 1019 1020 return (0); 1021 } 1022 1023 static int 1024 snmp_recv_reply(struct picl_snmphdl *smd, snmp_pdu_t *pdu, int *snmp_syserr) 1025 { 1026 struct dssnmp_info snmp_info; 1027 size_t pktsz; 1028 uchar_t *pkt; 1029 extern int errno; 1030 #ifdef USE_SOCKETS 1031 struct sockaddr_in from; 1032 int fromlen; 1033 ssize_t msgsz; 1034 #endif 1035 1036 if (smd->fd < 0 || pdu == NULL) 1037 return (-1); 1038 1039 #ifdef USE_SOCKETS 1040 if ((pkt = (uchar_t *)calloc(1, SNMP_MAX_RECV_PKTSZ)) == NULL) 1041 return (-1); 1042 1043 fromlen = sizeof (struct sockaddr_in); 1044 1045 LOGIO(TAG_RECVFROM, smd->fd, pkt, SNMP_MAX_RECV_PKTSZ); 1046 1047 msgsz = recvfrom(smd->fd, pkt, SNMP_MAX_RECV_PKTSZ, 0, 1048 (struct sockaddr *)&from, &fromlen); 1049 if (msgsz < 0 || msgsz >= SNMP_MAX_RECV_PKTSZ) { 1050 free(pkt); 1051 return (-1); 1052 } 1053 1054 pktsz = (size_t)msgsz; 1055 #else 1056 LOGIO(TAG_IOCTL, smd->fd, DSSNMP_GETINFO, &snmp_info); 1057 1058 /* 1059 * The ioctl will block until we have snmp data available 1060 */ 1061 if (ioctl(smd->fd, DSSNMP_GETINFO, &snmp_info) < 0) { 1062 if (snmp_syserr) 1063 *snmp_syserr = errno; 1064 return (-1); 1065 } 1066 1067 pktsz = snmp_info.size; 1068 if ((pkt = (uchar_t *)calloc(1, pktsz)) == NULL) 1069 return (-1); 1070 1071 LOGIO(TAG_READ, smd->fd, pkt, pktsz); 1072 1073 if (read(smd->fd, pkt, pktsz) < 0) { 1074 free(pkt); 1075 if (snmp_syserr) 1076 *snmp_syserr = errno; 1077 return (-1); 1078 } 1079 #endif 1080 1081 pdu->reply_pkt = pkt; 1082 pdu->reply_pktsz = pktsz; 1083 1084 #ifdef SNMP_DEBUG 1085 snmp_nrecvs++; 1086 snmp_rcvdbytes += pktsz; 1087 #endif 1088 1089 return (0); 1090 } 1091 1092 static int 1093 mibcache_realloc(int hint) 1094 { 1095 uint_t count = (uint_t)hint; 1096 nvlist_t **p; 1097 1098 if (hint < 0) 1099 return (-1); 1100 1101 (void) mutex_lock(&mibcache_lock); 1102 1103 if (hint < n_mibcache_rows) { 1104 (void) mutex_unlock(&mibcache_lock); 1105 return (0); 1106 } 1107 1108 count = ((count >> MIBCACHE_BLK_SHIFT) + 1) << MIBCACHE_BLK_SHIFT; 1109 1110 p = (nvlist_t **)calloc(count, sizeof (nvlist_t *)); 1111 if (p == NULL) { 1112 (void) mutex_unlock(&mibcache_lock); 1113 return (-1); 1114 } 1115 1116 if (mibcache) { 1117 (void) memcpy((void *) p, (void *) mibcache, 1118 n_mibcache_rows * sizeof (nvlist_t *)); 1119 free((void *) mibcache); 1120 } 1121 1122 mibcache = p; 1123 n_mibcache_rows = count; 1124 1125 (void) mutex_unlock(&mibcache_lock); 1126 1127 return (0); 1128 } 1129 1130 1131 /* 1132 * Scan each variable in the returned PDU's bindings and populate 1133 * the cache appropriately 1134 */ 1135 static void 1136 mibcache_populate(snmp_pdu_t *pdu, int is_vol) 1137 { 1138 pdu_varlist_t *vp; 1139 int row, ret; 1140 char *oidstr; 1141 struct timeval tv; 1142 int tod; /* in secs */ 1143 char tod_str[MAX_INT_LEN]; 1144 int ival_arr[2]; 1145 char *sval_arr[2]; 1146 1147 /* 1148 * If we're populating volatile properties, we also store a 1149 * timestamp with each property value. When we lookup, we 1150 * check the current time against this timestamp to determine 1151 * if we need to refetch the value or not (refetch if it has 1152 * been in for far too long). 1153 */ 1154 if (is_vol) { 1155 if (gettimeofday(&tv, NULL) < 0) 1156 tod = -1; 1157 else 1158 tod = (int)tv.tv_sec; 1159 1160 tod_str[0] = 0; 1161 (void) snprintf(tod_str, MAX_INT_LEN, "%d", tod); 1162 1163 ival_arr[1] = tod; 1164 sval_arr[1] = (char *)tod_str; 1165 } 1166 1167 for (vp = pdu->vars; vp; vp = vp->nextvar) { 1168 if (vp->type != ASN_INTEGER && vp->type != ASN_OCTET_STR && 1169 vp->type != ASN_BIT_STR) { 1170 continue; 1171 } 1172 1173 if (vp->name == NULL || vp->val.str == NULL) 1174 continue; 1175 1176 row = (vp->name)[vp->name_len-1]; 1177 1178 (void) mutex_lock(&mibcache_lock); 1179 1180 if (row >= n_mibcache_rows) { 1181 (void) mutex_unlock(&mibcache_lock); 1182 if (mibcache_realloc(row) < 0) 1183 continue; 1184 (void) mutex_lock(&mibcache_lock); 1185 } 1186 ret = 0; 1187 if (mibcache[row] == NULL) 1188 ret = nvlist_alloc(&mibcache[row], NV_UNIQUE_NAME, 0); 1189 1190 (void) mutex_unlock(&mibcache_lock); 1191 1192 if (ret != 0) 1193 continue; 1194 1195 /* 1196 * Convert the standard OID form into an oid string that 1197 * we can use as the key to lookup. Since we only search 1198 * by the prefix (mibcache is really an array of nvlist_t 1199 * pointers), ignore the leaf subid. 1200 */ 1201 oidstr = oid_to_oidstr(vp->name, vp->name_len - 1); 1202 if (oidstr == NULL) 1203 continue; 1204 1205 (void) mutex_lock(&mibcache_lock); 1206 1207 if (vp->type == ASN_INTEGER) { 1208 if (is_vol) { 1209 ival_arr[0] = *(vp->val.iptr); 1210 (void) nvlist_add_int32_array(mibcache[row], 1211 oidstr, ival_arr, 2); 1212 } else { 1213 (void) nvlist_add_int32(mibcache[row], 1214 oidstr, *(vp->val.iptr)); 1215 } 1216 1217 } else if (vp->type == ASN_OCTET_STR) { 1218 if (is_vol) { 1219 sval_arr[0] = (char *)vp->val.str; 1220 (void) nvlist_add_string_array(mibcache[row], 1221 oidstr, sval_arr, 2); 1222 } else { 1223 (void) nvlist_add_string(mibcache[row], 1224 oidstr, (const char *)(vp->val.str)); 1225 } 1226 } else if (vp->type == ASN_BIT_STR) { 1227 /* 1228 * We don't support yet bit string objects that are 1229 * volatile values. 1230 */ 1231 if (!is_vol) { 1232 (void) nvlist_add_byte_array(mibcache[row], 1233 oidstr, (uchar_t *)(vp->val.str), 1234 (uint_t)vp->val_len); 1235 } 1236 } 1237 (void) mutex_unlock(&mibcache_lock); 1238 1239 free(oidstr); 1240 } 1241 } 1242 1243 static char * 1244 oid_to_oidstr(oid *objid, size_t n_subids) 1245 { 1246 char *oidstr; 1247 char subid_str[MAX_INT_LEN]; 1248 int i, isize; 1249 size_t oidstr_sz; 1250 1251 /* 1252 * ugly, but for now this will have to do. 1253 */ 1254 oidstr_sz = sizeof (subid_str) * n_subids; 1255 oidstr = calloc(1, oidstr_sz); 1256 1257 for (i = 0; i < n_subids; i++) { 1258 (void) memset(subid_str, 0, sizeof (subid_str)); 1259 isize = snprintf(subid_str, sizeof (subid_str), "%d", 1260 objid[i]); 1261 if (isize >= sizeof (subid_str)) 1262 return (NULL); 1263 1264 (void) strlcat(oidstr, subid_str, oidstr_sz); 1265 if (i < (n_subids - 1)) 1266 (void) strlcat(oidstr, ".", oidstr_sz); 1267 } 1268 1269 return (oidstr); 1270 } 1271