snmplib.c revision d19c75f6ecd0e3a96a6dd30da832ecf2c0a5489d
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* The snmp library helps to prepare the PDUs and communicate with
* the snmp agent on the SP side via the ds_snmp driver.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <thread.h>
#include <synch.h>
#include <errno.h>
#include <fcntl.h>
#include <libnvpair.h>
#include "libpiclsnmp.h"
#include "snmplib.h"
#include "asn1.h"
#include "pdu.h"
#include "debug.h"
/*
* Data from the MIB is fetched based on the hints about object
* groups received from (possibly many threads in) the application.
* However, the fetched data is kept in a common cache for use across
* all threads, so even a GETBULK is issued only when absolutely
* necessary.
*
* Note that locking is not fine grained (there's no locking per row)
* since we don't expect too many MT consumers right away.
*
*/
static mutex_t mibcache_lock;
static uint_t n_mibcache_rows = 0;
static mutex_t snmp_reqid_lock;
static int snmp_reqid = 1;
#ifdef SNMP_DEBUG
uint_t snmp_nsends = 0;
uint_t snmp_sentbytes = 0;
uint_t snmp_nrecvs = 0;
uint_t snmp_rcvdbytes = 0;
#endif
#ifdef USE_SOCKETS
#define SNMP_DEFAULT_PORT 161
#endif
/*
* Static function declarations
*/
static void libpiclsnmp_init(void);
static int lookup_int(char *, int, int *, int);
static int lookup_str(char *, int, char **, int);
static int search_oid_in_group(char *, char *, int);
static void fetch_bulk(struct picl_snmphdl *, char *, int, int, int, int *);
static int fetch_single_str(struct picl_snmphdl *, char *, int,
char **, int *);
static int fetch_single_int(struct picl_snmphdl *, char *, int,
int *, int *);
static int fetch_single_bitstr(struct picl_snmphdl *, char *, int,
static int mibcache_realloc(int);
static void mibcache_populate(snmp_pdu_t *, int);
static void
libpiclsnmp_init(void)
{
if (mibcache_realloc(0) < 0)
(void) mutex_destroy(&mibcache_lock);
LOGINIT();
}
{
struct picl_snmphdl *smd;
#ifdef USE_SOCKETS
char *snmp_agent_addr;
#endif
return (NULL);
#ifdef USE_SOCKETS
return (NULL);
return (NULL);
#else
return (NULL);
}
#endif
return ((picl_snmphdl_t)smd);
}
void
{
if (smd) {
}
}
}
int
{
int i;
(void) mutex_lock(&mibcache_lock);
for (i = 0; i < n_mibcache_rows; i++) {
}
n_mibcache_rows = 0;
if (mibcache) {
}
(void) mutex_unlock(&mibcache_lock);
if (clr_linkreset) {
return (-1);
else
}
return (0);
}
void
{
char *p;
int i, sz;
/*
* Allocate a new oidgroup_t
*/
return;
/*
* Determine how much space is required to register this group
*/
sz = 0;
p = oidstrs;
for (i = 0; i < n_oids; i++) {
}
/*
* Create this oid group
*/
return;
}
/*
* Link it to the tail of the list of oid groups
*/
else
}
/*
* snmp_get_int() takes in an OID and returns the integer value
* of the object referenced in the passed arg. It returns 0 on
* success and -1 on failure.
*/
int
int *snmp_syserr)
{
int ret;
int err = 0;
return (-1);
/*
* If this item should not be cached, fetch it directly from
* the agent using fetch_single_xxx()
*/
if (snmp_syserr)
*snmp_syserr = err;
return (ret);
}
/*
* is it in the cache ?
*/
return (0);
/*
* fetch it from the agent and populate the cache
*/
if (snmp_syserr)
*snmp_syserr = err;
/*
* look it up again and return it
*/
return (-1);
return (0);
}
/*
* snmp_get_str() takes in an OID and returns the string value
* of the object referenced in the passed arg. Memory for the string
* is allocated within snmp_get_str() and is expected to be freed by
* the caller when it is no longer needed. The function returns 0
* on success and -1 on failure.
*/
int
int *snmp_syserr)
{
char *val;
int ret;
int err = 0;
return (-1);
/*
* Check if this item is cacheable or not. If not, call
* fetch_single_* to get it directly from the agent
*/
if (snmp_syserr)
*snmp_syserr = err;
return (ret);
}
/*
* See if it's in the cache already
*/
return (-1);
else
return (0);
}
/*
* Fetch it from the agent and populate cache
*/
if (snmp_syserr)
*snmp_syserr = err;
/*
* Retry lookup
*/
return (-1);
return (-1);
else
return (0);
}
/*
* snmp_get_bitstr() takes in an OID and returns the bit string value
* of the object referenced in the passed args. Memory for the bitstring
* is allocated within the function and is expected to be freed by
* the caller when it is no longer needed. The function returns 0
* on success and -1 on failure.
*/
int
{
int ret;
int err = 0;
return (-1);
/*
* Check if this item is cacheable or not. If not, call
* fetch_single_* to get it directly from the agent
*/
if (snmp_syserr)
*snmp_syserr = err;
return (ret);
}
/*
* See if it's in the cache already
*/
return (-1);
return (0);
}
/*
* Fetch it from the agent and populate cache
*/
if (snmp_syserr)
*snmp_syserr = err;
/*
* Retry lookup
*/
return (-1);
return (-1);
return (0);
}
/*
* snmp_get_nextrow() is similar in operation to SNMP_GETNEXT, but
* only just. In particular, this is only expected to return the next
* valid row number for the same object, not its value. Since we don't
* have any other means, we use this to determine the number of rows
* in the table (and the valid ones). This function returns 0 on success
* and -1 on failure.
*/
int
int *snmp_syserr)
{
char *nxt_oidstr;
int err = 0;
if (snmp_syserr)
*snmp_syserr = EINVAL;
return (-1);
}
/*
* The get_nextrow results should *never* go into any cache,
* since these relationships are dynamically discovered each time.
*/
if (snmp_syserr)
*snmp_syserr = err;
return (-1);
}
/*
* We are not concerned about the "value" of the lexicographically
* next object; we only care about the name of that object and
* its row number (and whether such an object exists or not).
*/
/*
* This indicates that we're at the end of the MIB view.
*/
if (snmp_syserr)
*snmp_syserr = ENOSPC;
return (-1);
}
/*
* need to be able to convert the OID
*/
if (snmp_syserr)
*snmp_syserr = ENOMEM;
return (-1);
}
/*
* We're on to the next table.
*/
if (snmp_syserr)
*snmp_syserr = ENOENT;
return (-1);
}
/*
* Ok, so we've got an oid that's simply the next valid row of the
* passed on object, return this row number.
*/
return (0);
}
/*
* Request ids for snmp messages to the agent are sequenced here.
*/
int
snmp_get_reqid(void)
{
int ret;
(void) mutex_lock(&snmp_reqid_lock);
ret = snmp_reqid++;
(void) mutex_unlock(&snmp_reqid_lock);
return (ret);
}
static int
{
int elapsed;
(void) mutex_lock(&mibcache_lock);
if (row >= n_mibcache_rows) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
/*
* If this is a volatile property, we should be searching
* for an integer-timestamp pair
*/
if (is_vol) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
} else {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
}
(void) mutex_unlock(&mibcache_lock);
return (0);
}
static int
{
char **val_arr;
int elapsed;
(void) mutex_lock(&mibcache_lock);
if (row >= n_mibcache_rows) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
/*
* If this is a volatile property, we should be searching
* for a string-timestamp pair
*/
if (is_vol) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
} else {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
}
(void) mutex_unlock(&mibcache_lock);
return (0);
}
static int
{
(void) mutex_lock(&mibcache_lock);
if (row >= n_mibcache_rows) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
/*
* We don't support volatile bit string values yet. The nvlist
* functions don't support bitstring arrays like they do charstring
* arrays, so we would need to do things in a convoluted way,
* probably by attaching the timestamp as part of the byte array
* itself. However, the need for volatile bitstrings isn't there
* yet, to justify the effort.
*/
if (is_vol) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
(void) mutex_unlock(&mibcache_lock);
return (0);
}
static int
{
char *p;
int i;
p = oidstrs;
for (i = 0; i < n_oids; i++) {
return (0);
p += strlen(p) + 1;
}
return (-1);
}
static oidgroup_t *
{
return (NULL);
return (NULL);
return (grp);
}
}
return (NULL);
}
static int
int *snmp_syserr)
{
return (-1);
/*
* Note that we don't make any distinction between unsigned int
* value and signed int value at this point, since we provide
* only snmp_get_int() at the higher level. While it is possible
* to provide an entirely separate interface such as snmp_get_uint(),
* that's quite unnecessary, because we don't do any interpretation
* of the received value. Besides, the sizes of int and uint are
* the same and the sizes of all pointers are the same (so val.iptr
* violate any of these assumptions, it will be time to add
* snmp_get_uint().
*/
return (-1);
}
return (0);
}
static int
int *snmp_syserr)
{
return (-1);
return (-1);
}
return (0);
}
static int
{
return (-1);
return (-1);
}
return (-1);
}
return (0);
}
static snmp_pdu_t *
{
return (NULL);
if (snmp_make_packet(pdu) < 0) {
return (NULL);
}
return (NULL);
}
return (NULL);
}
pdu->reply_pktsz);
return (reply_pdu);
}
static void
{
int max_reps;
/*
* If we're fetching volatile properties using BULKGET, don't
* venture to get multiple rows (passing max_reps=0 will make
* snmp_create_pdu() fetch SNMP_DEF_MAX_REPETITIONS rows)
*/
return;
/*
* Make an ASN.1 encoded packet from the PDU information
*/
if (snmp_make_packet(pdu) < 0) {
return;
}
/*
* Send the request packet to the agent
*/
return;
}
/*
* Receive response from the agent into the reply packet buffer
* in the request PDU
*/
return;
}
/*
* Parse the reply, validate the response and create a
* reply-PDU out of the information. Populate the mibcache
* with the received values.
*/
pdu->reply_pktsz);
if (reply_pdu) {
}
}
static snmp_pdu_t *
{
return (NULL);
if (snmp_make_packet(pdu) < 0) {
return (NULL);
}
return (NULL);
}
return (NULL);
}
pdu->reply_pktsz);
return (reply_pdu);
}
static int
{
extern int errno;
#ifdef USE_SOCKETS
int ret;
#endif
return (-1);
return (-1);
#ifdef USE_SOCKETS
ret = -1;
while (ret < 0) {
sizeof (struct sockaddr));
return (-1);
}
}
#else
if (snmp_syserr)
*snmp_syserr = errno;
return (-1);
}
#endif
#ifdef SNMP_DEBUG
snmp_nsends++;
#endif
return (0);
}
static int
{
struct dssnmp_info snmp_info;
extern int errno;
#ifdef USE_SOCKETS
struct sockaddr_in from;
int fromlen;
#endif
return (-1);
#ifdef USE_SOCKETS
return (-1);
fromlen = sizeof (struct sockaddr_in);
return (-1);
}
#else
/*
* The ioctl will block until we have snmp data available
*/
if (snmp_syserr)
*snmp_syserr = errno;
return (-1);
}
return (-1);
if (snmp_syserr)
*snmp_syserr = errno;
return (-1);
}
#endif
#ifdef SNMP_DEBUG
snmp_nrecvs++;
snmp_rcvdbytes += pktsz;
#endif
return (0);
}
static int
mibcache_realloc(int hint)
{
nvlist_t **p;
if (hint < 0)
return (-1);
(void) mutex_lock(&mibcache_lock);
if (hint < n_mibcache_rows) {
(void) mutex_unlock(&mibcache_lock);
return (0);
}
if (p == NULL) {
(void) mutex_unlock(&mibcache_lock);
return (-1);
}
if (mibcache) {
n_mibcache_rows * sizeof (nvlist_t *));
}
mibcache = p;
(void) mutex_unlock(&mibcache_lock);
return (0);
}
/*
* Scan each variable in the returned PDU's bindings and populate
* the cache appropriately
*/
static void
{
char *oidstr;
int tod; /* in secs */
char tod_str[MAX_INT_LEN];
int ival_arr[2];
char *sval_arr[2];
/*
* If we're populating volatile properties, we also store a
* timestamp with each property value. When we lookup, we
* check the current time against this timestamp to determine
* if we need to refetch the value or not (refetch if it has
* been in for far too long).
*/
if (is_vol) {
tod = -1;
else
tod_str[0] = 0;
}
continue;
}
continue;
(void) mutex_lock(&mibcache_lock);
if (row >= n_mibcache_rows) {
(void) mutex_unlock(&mibcache_lock);
if (mibcache_realloc(row) < 0)
continue;
(void) mutex_lock(&mibcache_lock);
}
ret = 0;
(void) mutex_unlock(&mibcache_lock);
if (ret != 0)
continue;
/*
* Convert the standard OID form into an oid string that
* we can use as the key to lookup. Since we only search
* by the prefix (mibcache is really an array of nvlist_t
* pointers), ignore the leaf subid.
*/
continue;
(void) mutex_lock(&mibcache_lock);
if (is_vol) {
} else {
}
if (is_vol) {
} else {
}
/*
* We don't support yet bit string objects that are
* volatile values.
*/
if (!is_vol) {
}
}
(void) mutex_unlock(&mibcache_lock);
}
}
static char *
{
char *oidstr;
char subid_str[MAX_INT_LEN];
int i, isize;
/*
* ugly, but for now this will have to do.
*/
for (i = 0; i < n_subids; i++) {
objid[i]);
return (NULL);
if (i < (n_subids - 1))
}
return (oidstr);
}