summaryrefslogtreecommitdiff
path: root/agent/mibgroup/ucd-snmp/proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/ucd-snmp/proxy.c')
-rw-r--r--agent/mibgroup/ucd-snmp/proxy.c705
1 files changed, 705 insertions, 0 deletions
diff --git a/agent/mibgroup/ucd-snmp/proxy.c b/agent/mibgroup/ucd-snmp/proxy.c
new file mode 100644
index 0000000..017fd5c
--- /dev/null
+++ b/agent/mibgroup/ucd-snmp/proxy.c
@@ -0,0 +1,705 @@
+/* Portions of this file are subject to the following copyright(s). See
+ * the Net-SNMP's COPYING file for more details and other copyrights
+ * that may apply:
+ */
+/*
+ * Portions of this file are copyrighted by:
+ * Copyright @ 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-features.h>
+
+#include <sys/types.h>
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include "proxy.h"
+
+netsnmp_feature_require(handler_mark_requests_as_delegated)
+netsnmp_feature_require(request_set_error_idx)
+
+static struct simple_proxy *proxies = NULL;
+
+oid testoid[] = { 1, 3, 6, 1, 4, 1, 2021, 8888, 1 };
+
+/*
+ * this must be standardized somewhere, right?
+ */
+#define MAX_ARGS 128
+
+char *context_string;
+
+static void
+proxyOptProc(int argc, char *const *argv, int opt)
+{
+ switch (opt) {
+ case 'C':
+ while (*optarg) {
+ switch (*optarg++) {
+ case 'n':
+ optind++;
+ if (optind < argc) {
+ context_string = argv[optind - 1];
+ } else {
+ config_perror("No context name passed to -Cn");
+ }
+ break;
+ case 'c':
+ netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_IGNORE_NO_COMMUNITY, 1);
+ break;
+ default:
+ config_perror("unknown argument passed to -C");
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ /*
+ * shouldn't get here
+ */
+ }
+}
+
+void
+proxy_parse_config(const char *token, char *line)
+{
+ /*
+ * proxy args [base-oid] [remap-to-remote-oid]
+ */
+
+ netsnmp_session session, *ss;
+ struct simple_proxy *newp, **listpp;
+ char args[MAX_ARGS][SPRINT_MAX_LEN], *argv[MAX_ARGS];
+ int argn, arg;
+ char *cp;
+ netsnmp_handler_registration *reg;
+
+ context_string = NULL;
+
+ DEBUGMSGTL(("proxy_config", "entering\n"));
+
+ /*
+ * create the argv[] like array
+ */
+ strcpy(argv[0] = args[0], "snmpd-proxy"); /* bogus entry for getopt() */
+ for (argn = 1, cp = line; cp && argn < MAX_ARGS;) {
+ argv[argn] = args[argn];
+ cp = copy_nword(cp, argv[argn], SPRINT_MAX_LEN);
+ argn++;
+ }
+
+ for (arg = 0; arg < argn; arg++) {
+ DEBUGMSGTL(("proxy_args", "final args: %d = %s\n", arg,
+ argv[arg]));
+ }
+
+ DEBUGMSGTL(("proxy_config", "parsing args: %d\n", argn));
+ /* Call special parse_args that allows for no specified community string */
+ arg = netsnmp_parse_args(argn, argv, &session, "C:", proxyOptProc,
+ NETSNMP_PARSE_ARGS_NOLOGGING |
+ NETSNMP_PARSE_ARGS_NOZERO);
+
+ /* reset this in case we modified it */
+ netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_IGNORE_NO_COMMUNITY, 0);
+
+ if (arg < 0) {
+ config_perror("failed to parse proxy args");
+ return;
+ }
+ DEBUGMSGTL(("proxy_config", "done parsing args\n"));
+
+ if (arg >= argn) {
+ config_perror("missing base oid");
+ return;
+ }
+
+ /*
+ * usm_set_reportErrorOnUnknownID(0);
+ *
+ * hack, stupid v3 ASIs.
+ */
+ /*
+ * XXX: on a side note, we don't really need to be a reference
+ * platform any more so the proper thing to do would be to fix
+ * snmplib/snmpusm.c to pass in the pdu type to usm_process_incoming
+ * so this isn't needed.
+ */
+ ss = snmp_open(&session);
+ /*
+ * usm_set_reportErrorOnUnknownID(1);
+ */
+ if (ss == NULL) {
+ /*
+ * diagnose snmp_open errors with the input netsnmp_session pointer
+ */
+ snmp_sess_perror("snmpget", &session);
+ SOCK_CLEANUP;
+ return;
+ }
+
+ newp = (struct simple_proxy *) calloc(1, sizeof(struct simple_proxy));
+
+ newp->sess = ss;
+ DEBUGMSGTL(("proxy_init", "name = %s\n", args[arg]));
+ newp->name_len = MAX_OID_LEN;
+ if (!snmp_parse_oid(args[arg++], newp->name, &newp->name_len)) {
+ snmp_perror("proxy");
+ config_perror("illegal proxy oid specified\n");
+ return;
+ }
+
+ if (arg < argn) {
+ DEBUGMSGTL(("proxy_init", "base = %s\n", args[arg]));
+ newp->base_len = MAX_OID_LEN;
+ if (!snmp_parse_oid(args[arg++], newp->base, &newp->base_len)) {
+ snmp_perror("proxy");
+ config_perror("illegal variable name specified (base oid)\n");
+ return;
+ }
+ }
+ if ( context_string )
+ newp->context = strdup(context_string);
+
+ DEBUGMSGTL(("proxy_init", "registering at: "));
+ DEBUGMSGOID(("proxy_init", newp->name, newp->name_len));
+ DEBUGMSG(("proxy_init", "\n"));
+
+ /*
+ * add to our chain
+ */
+ /*
+ * must be sorted!
+ */
+ listpp = &proxies;
+ while (*listpp &&
+ snmp_oid_compare(newp->name, newp->name_len,
+ (*listpp)->name, (*listpp)->name_len) > 0) {
+ listpp = &((*listpp)->next);
+ }
+
+ /*
+ * listpp should be next in line from us.
+ */
+ if (*listpp) {
+ /*
+ * make our next in the link point to the current link
+ */
+ newp->next = *listpp;
+ }
+ /*
+ * replace current link with us
+ */
+ *listpp = newp;
+
+ reg = netsnmp_create_handler_registration("proxy",
+ proxy_handler,
+ newp->name,
+ newp->name_len,
+ HANDLER_CAN_RWRITE);
+ reg->handler->myvoid = newp;
+ if (context_string)
+ reg->contextName = strdup(context_string);
+
+ netsnmp_register_handler(reg);
+}
+
+void
+proxy_free_config(void)
+{
+ struct simple_proxy *rm;
+
+ DEBUGMSGTL(("proxy_free_config", "Free config\n"));
+ while (proxies) {
+ rm = proxies;
+ proxies = rm->next;
+
+ DEBUGMSGTL(( "proxy_free_config", "freeing "));
+ DEBUGMSGOID(("proxy_free_config", rm->name, rm->name_len));
+ DEBUGMSG(( "proxy_free_config", " (%s)\n", rm->context));
+ unregister_mib_context(rm->name, rm->name_len,
+ DEFAULT_MIB_PRIORITY, 0, 0,
+ rm->context);
+ SNMP_FREE(rm->variables);
+ SNMP_FREE(rm->context);
+ snmp_close(rm->sess);
+ SNMP_FREE(rm);
+ }
+}
+
+/*
+ * Configure special parameters on the session.
+ * Currently takes the parameter configured and changes it if something
+ * was configured. It becomes "-c" if the community string from the pdu
+ * is placed on the session.
+ */
+int
+proxy_fill_in_session(netsnmp_mib_handler *handler,
+ netsnmp_agent_request_info *reqinfo,
+ void **configured)
+{
+ netsnmp_session *session;
+ struct simple_proxy *sp;
+
+ sp = (struct simple_proxy *) handler->myvoid;
+ if (!sp) {
+ return 0;
+ }
+ session = sp->sess;
+ if (!session) {
+ return 0;
+ }
+
+#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
+#if defined(NETSNMP_DISABLE_SNMPV1)
+ if (session->version == SNMP_VERSION_2c) {
+#else
+#if defined(NETSNMP_DISABLE_SNMPV2C)
+ if (session->version == SNMP_VERSION_1) {
+#else
+ if (session->version == SNMP_VERSION_1 ||
+ session->version == SNMP_VERSION_2c) {
+#endif
+#endif
+
+ /*
+ * Check if session has community string defined for it.
+ * If not, need to extract community string from the pdu.
+ * Copy to session and set 'configured' to indicate this.
+ */
+ if (session->community_len == 0) {
+ DEBUGMSGTL(("proxy", "session has no community string\n"));
+ if (reqinfo->asp == NULL || reqinfo->asp->pdu == NULL ||
+ reqinfo->asp->pdu->community_len == 0) {
+ return 0;
+ }
+
+ *configured = strdup("-c");
+ DEBUGMSGTL(("proxy", "pdu has community string\n"));
+ session->community_len = reqinfo->asp->pdu->community_len;
+ session->community = malloc(session->community_len + 1);
+ sprintf((char *)session->community, "%.*s",
+ (int) session->community_len,
+ (const char *)reqinfo->asp->pdu->community);
+ }
+ }
+#endif
+
+ return 1;
+}
+
+/*
+ * Free any specially configured parameters used on the session.
+ */
+void
+proxy_free_filled_in_session_args(netsnmp_session *session, void **configured)
+{
+
+ /* Only do comparisions, etc., if something was configured */
+ if (*configured == NULL) {
+ return;
+ }
+
+ /* If used community string from pdu, release it from session now */
+ if (strcmp((const char *)(*configured), "-c") == 0) {
+ free(session->community);
+ session->community = NULL;
+ session->community_len = 0;
+ }
+
+ free((u_char *)(*configured));
+ *configured = NULL;
+}
+
+void
+init_proxy(void)
+{
+ snmpd_register_config_handler("proxy", proxy_parse_config,
+ proxy_free_config,
+ "[snmpcmd args] host oid [remoteoid]");
+}
+
+void
+shutdown_proxy(void)
+{
+ proxy_free_config();
+}
+
+int
+proxy_handler(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+
+ netsnmp_pdu *pdu;
+ struct simple_proxy *sp;
+ oid *ourname;
+ size_t ourlength;
+ netsnmp_request_info *request = requests;
+ u_char *configured = NULL;
+
+ DEBUGMSGTL(("proxy", "proxy handler starting, mode = %d\n",
+ reqinfo->mode));
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ case MODE_GETNEXT:
+ case MODE_GETBULK: /* WWWXXX */
+ pdu = snmp_pdu_create(reqinfo->mode);
+ break;
+
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+ case MODE_SET_ACTION:
+ pdu = snmp_pdu_create(SNMP_MSG_SET);
+ break;
+
+ case MODE_SET_UNDO:
+ /*
+ * If we set successfully (status == NOERROR),
+ * we can't back out again, so need to report the fact.
+ * If we failed to set successfully, then we're fine.
+ */
+ for (request = requests; request; request=request->next) {
+ if (request->status == SNMP_ERR_NOERROR) {
+ netsnmp_set_request_error(reqinfo, requests,
+ SNMP_ERR_UNDOFAILED);
+ return SNMP_ERR_UNDOFAILED;
+ }
+ }
+ return SNMP_ERR_NOERROR;
+
+ case MODE_SET_RESERVE1:
+ case MODE_SET_RESERVE2:
+ case MODE_SET_FREE:
+ case MODE_SET_COMMIT:
+ /*
+ * Nothing to do in this pass
+ */
+ return SNMP_ERR_NOERROR;
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+
+ default:
+ snmp_log(LOG_WARNING, "unsupported mode for proxy called (%d)\n",
+ reqinfo->mode);
+ return SNMP_ERR_NOERROR;
+ }
+
+ sp = (struct simple_proxy *) handler->myvoid;
+
+ if (!pdu || !sp) {
+ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
+ if (pdu)
+ snmp_free_pdu(pdu);
+ return SNMP_ERR_NOERROR;
+ }
+
+ while (request) {
+ ourname = request->requestvb->name;
+ ourlength = request->requestvb->name_length;
+
+ if (sp->base_len &&
+ reqinfo->mode == MODE_GETNEXT &&
+ (snmp_oid_compare(ourname, ourlength,
+ sp->base, sp->base_len) < 0)) {
+ DEBUGMSGTL(( "proxy", "request is out of registered range\n"));
+ /*
+ * Create GETNEXT request with an OID so the
+ * master returns the first OID in the registered range.
+ */
+ memcpy(ourname, sp->base, sp->base_len * sizeof(oid));
+ ourlength = sp->base_len;
+ if (ourname[ourlength-1] <= 1) {
+ /*
+ * The registered range ends with x.y.z.1
+ * -> ask for the next of x.y.z
+ */
+ ourlength--;
+ } else {
+ /*
+ * The registered range ends with x.y.z.A
+ * -> ask for the next of x.y.z.A-1.MAX_SUBID
+ */
+ ourname[ourlength-1]--;
+ ourname[ourlength] = MAX_SUBID;
+ ourlength++;
+ }
+ } else if (sp->base_len > 0) {
+ if ((ourlength - sp->name_len + sp->base_len) > MAX_OID_LEN) {
+ /*
+ * too large
+ */
+ if (pdu)
+ snmp_free_pdu(pdu);
+ snmp_log(LOG_ERR,
+ "proxy oid request length is too long\n");
+ return SNMP_ERR_NOERROR;
+ }
+ /*
+ * suffix appended?
+ */
+ DEBUGMSGTL(("proxy", "length=%d, base_len=%d, name_len=%d\n",
+ (int)ourlength, (int)sp->base_len, (int)sp->name_len));
+ if (ourlength > sp->name_len)
+ memcpy(&(sp->base[sp->base_len]), &(ourname[sp->name_len]),
+ sizeof(oid) * (ourlength - sp->name_len));
+ ourlength = ourlength - sp->name_len + sp->base_len;
+ ourname = sp->base;
+ }
+
+ snmp_pdu_add_variable(pdu, ourname, ourlength,
+ request->requestvb->type,
+ request->requestvb->val.string,
+ request->requestvb->val_len);
+ request->delegated = 1;
+ request = request->next;
+ }
+
+ /*
+ * Customize session parameters based on request information
+ */
+ if (!proxy_fill_in_session(handler, reqinfo, (void **)&configured)) {
+ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
+ if (pdu)
+ snmp_free_pdu(pdu);
+ return SNMP_ERR_NOERROR;
+ }
+
+ /*
+ * send the request out
+ */
+ DEBUGMSGTL(("proxy", "sending pdu\n"));
+ snmp_async_send(sp->sess, pdu, proxy_got_response,
+ netsnmp_create_delegated_cache(handler, reginfo,
+ reqinfo, requests,
+ (void *) sp));
+
+ /* Free any special parameters generated on the session */
+ proxy_free_filled_in_session_args(sp->sess, (void **)&configured);
+
+ return SNMP_ERR_NOERROR;
+}
+
+int
+proxy_got_response(int operation, netsnmp_session * sess, int reqid,
+ netsnmp_pdu *pdu, void *cb_data)
+{
+ netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) cb_data;
+ netsnmp_request_info *requests, *request = NULL;
+ netsnmp_variable_list *vars, *var = NULL;
+
+ struct simple_proxy *sp;
+ oid myname[MAX_OID_LEN];
+ size_t myname_len = MAX_OID_LEN;
+
+ cache = netsnmp_handler_check_cache(cache);
+
+ if (!cache) {
+ DEBUGMSGTL(("proxy", "a proxy request was no longer valid.\n"));
+ return SNMP_ERR_NOERROR;
+ }
+
+ requests = cache->requests;
+
+
+ sp = (struct simple_proxy *) cache->localinfo;
+
+ if (!sp) {
+ DEBUGMSGTL(("proxy", "a proxy request was no longer valid.\n"));
+ return SNMP_ERR_NOERROR;
+ }
+
+ switch (operation) {
+ case NETSNMP_CALLBACK_OP_TIMED_OUT:
+ /*
+ * WWWXXX: don't leave requests delayed if operation is
+ * something like TIMEOUT
+ */
+ DEBUGMSGTL(("proxy", "got timed out... requests = %8p\n", requests));
+
+ netsnmp_handler_mark_requests_as_delegated(requests,
+ REQUEST_IS_NOT_DELEGATED);
+ if(cache->reqinfo->mode != MODE_GETNEXT) {
+ DEBUGMSGTL(("proxy", " ignoring timeout\n"));
+ netsnmp_set_request_error(cache->reqinfo, requests, /* XXXWWW: should be index = 0 */
+ SNMP_ERR_GENERR);
+ }
+ netsnmp_free_delegated_cache(cache);
+ return 0;
+
+ case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
+ vars = pdu->variables;
+
+ if (pdu->errstat != SNMP_ERR_NOERROR) {
+ /*
+ * If we receive an error from the proxy agent, pass it on up.
+ * The higher-level processing seems to Do The Right Thing.
+ *
+ * 2005/06 rks: actually, it doesn't do the right thing for
+ * a get-next request that returns NOSUCHNAME. If we do nothing,
+ * it passes that error back to the comman initiator. What it should
+ * do is ignore the error and move on to the next tree. To
+ * accomplish that, all we need to do is clear the delegated flag.
+ * Not sure if any other error codes need the same treatment. Left
+ * as an exercise to the reader...
+ */
+ DEBUGMSGTL(("proxy", "got error response (%ld)\n", pdu->errstat));
+ if((cache->reqinfo->mode == MODE_GETNEXT) &&
+ (SNMP_ERR_NOSUCHNAME == pdu->errstat)) {
+ DEBUGMSGTL(("proxy", " ignoring error response\n"));
+ netsnmp_handler_mark_requests_as_delegated(requests,
+ REQUEST_IS_NOT_DELEGATED);
+ }
+#ifndef NETSNMP_NO_WRITE_SUPPORT
+ else if (cache->reqinfo->mode == MODE_SET_ACTION) {
+ /*
+ * In order for netsnmp_wrap_up_request to consider the
+ * SET request complete,
+ * there must be no delegated requests pending.
+ * https://sourceforge.net/tracker/
+ * ?func=detail&atid=112694&aid=1554261&group_id=12694
+ */
+ DEBUGMSGTL(("proxy",
+ "got SET error %s, index %ld\n",
+ snmp_errstring(pdu->errstat), pdu->errindex));
+ netsnmp_handler_mark_requests_as_delegated(
+ requests, REQUEST_IS_NOT_DELEGATED);
+ netsnmp_request_set_error_idx(requests, pdu->errstat,
+ pdu->errindex);
+ }
+#endif /* !NETSNMP_NO_WRITE_SUPPORT */
+ else {
+ netsnmp_handler_mark_requests_as_delegated( requests,
+ REQUEST_IS_NOT_DELEGATED);
+ netsnmp_request_set_error_idx(requests, pdu->errstat,
+ pdu->errindex);
+ }
+
+ /*
+ * update the original request varbinds with the results
+ */
+ } else for (var = vars, request = requests;
+ request && var;
+ request = request->next, var = var->next_variable) {
+ /*
+ * XXX - should this be done here?
+ * Or wait until we know it's OK?
+ */
+ snmp_set_var_typed_value(request->requestvb, var->type,
+ var->val.string, var->val_len);
+
+ DEBUGMSGTL(("proxy", "got response... "));
+ DEBUGMSGOID(("proxy", var->name, var->name_length));
+ DEBUGMSG(("proxy", "\n"));
+ request->delegated = 0;
+
+ /*
+ * Check the response oid is legitimate,
+ * and discard the value if not.
+ *
+ * XXX - what's the difference between these cases?
+ */
+ if (sp->base_len &&
+ (var->name_length < sp->base_len ||
+ snmp_oid_compare(var->name, sp->base_len, sp->base,
+ sp->base_len) != 0)) {
+ DEBUGMSGTL(( "proxy", "out of registered range... "));
+ DEBUGMSGOID(("proxy", var->name, sp->base_len));
+ DEBUGMSG(( "proxy", " (%d) != ", (int)sp->base_len));
+ DEBUGMSGOID(("proxy", sp->base, sp->base_len));
+ DEBUGMSG(( "proxy", "\n"));
+ snmp_set_var_typed_value(request->requestvb, ASN_NULL, NULL, 0);
+
+ continue;
+ } else if (!sp->base_len &&
+ (var->name_length < sp->name_len ||
+ snmp_oid_compare(var->name, sp->name_len, sp->name,
+ sp->name_len) != 0)) {
+ DEBUGMSGTL(( "proxy", "out of registered base range... "));
+ DEBUGMSGOID(("proxy", var->name, sp->name_len));
+ DEBUGMSG(( "proxy", " (%d) != ", (int)sp->name_len));
+ DEBUGMSGOID(("proxy", sp->name, sp->name_len));
+ DEBUGMSG(( "proxy", "\n"));
+ snmp_set_var_typed_value(request->requestvb, ASN_NULL, NULL, 0);
+ continue;
+ } else {
+ /*
+ * If the returned OID is legitimate, then update
+ * the original request varbind accordingly.
+ */
+ if (sp->base_len) {
+ /*
+ * XXX: oid size maxed?
+ */
+ memcpy(myname, sp->name, sizeof(oid) * sp->name_len);
+ myname_len =
+ sp->name_len + var->name_length - sp->base_len;
+ if (myname_len > MAX_OID_LEN) {
+ snmp_log(LOG_WARNING,
+ "proxy OID return length too long.\n");
+ netsnmp_set_request_error(cache->reqinfo, requests,
+ SNMP_ERR_GENERR);
+ if (pdu)
+ snmp_free_pdu(pdu);
+ netsnmp_free_delegated_cache(cache);
+ return 1;
+ }
+
+ if (var->name_length > sp->base_len)
+ memcpy(&myname[sp->name_len],
+ &var->name[sp->base_len],
+ sizeof(oid) * (var->name_length -
+ sp->base_len));
+ snmp_set_var_objid(request->requestvb, myname,
+ myname_len);
+ } else {
+ snmp_set_var_objid(request->requestvb, var->name,
+ var->name_length);
+ }
+ }
+ }
+
+ if (request || var) {
+ /*
+ * ack, this is bad. The # of varbinds don't match and
+ * there is no way to fix the problem
+ */
+ if (pdu)
+ snmp_free_pdu(pdu);
+ snmp_log(LOG_ERR,
+ "response to proxy request illegal. We're screwed.\n");
+ netsnmp_set_request_error(cache->reqinfo, requests,
+ SNMP_ERR_GENERR);
+ }
+
+ /* fix bulk_to_next operations */
+ if (cache->reqinfo->mode == MODE_GETBULK)
+ netsnmp_bulk_to_next_fix_requests(requests);
+
+ /*
+ * free the response
+ */
+ if (pdu && 0)
+ snmp_free_pdu(pdu);
+ break;
+
+ default:
+ DEBUGMSGTL(("proxy", "no response received: op = %d\n",
+ operation));
+ break;
+ }
+
+ netsnmp_free_delegated_cache(cache);
+ return 1;
+}