/* * AgentX utility routines */ #include #include #include #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #if HAVE_UNISTD_H #include #endif #include #if TIME_WITH_SYS_TIME # ifdef WIN32 # include # else # include # endif # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_WINSOCK_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #include "agentx/protocol.h" #include "agentx/client.h" #include "agentx/subagent.h" extern struct timeval starttime; /* * AgentX handling utility routines * * Mostly wrappers round, or re-writes of * the SNMP equivalents */ int agentx_synch_input(int op, netsnmp_session * session, int reqid, netsnmp_pdu *pdu, void *magic) { struct synch_state *state = (struct synch_state *) magic; struct timeval now, diff; if (reqid != state->reqid) { return handle_agentx_packet(op, session, reqid, pdu, magic); } DEBUGMSGTL(("agentx/subagent", "synching input, op 0x%02x\n", op)); state->waiting = 0; if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) { if (pdu->command == AGENTX_MSG_RESPONSE) { state->pdu = snmp_clone_pdu(pdu); state->status = STAT_SUCCESS; session->s_snmp_errno = SNMPERR_SUCCESS; /* * Synchronise sysUpTime with the master agent */ gettimeofday(&now, NULL); now.tv_sec--; now.tv_usec += 1000000L; diff.tv_sec = pdu->time / 100; diff.tv_usec = (pdu->time - (diff.tv_sec * 100)) * 10000; starttime.tv_sec = now.tv_sec - diff.tv_sec; starttime.tv_usec = now.tv_usec - diff.tv_usec; if (starttime.tv_usec > 1000000L) { starttime.tv_usec -= 1000000L; starttime.tv_sec++; } } } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) { state->pdu = NULL; state->status = STAT_TIMEOUT; session->s_snmp_errno = SNMPERR_TIMEOUT; } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) { return handle_agentx_packet(op, session, reqid, pdu, magic); } return 1; } int agentx_synch_response(netsnmp_session * ss, netsnmp_pdu *pdu, netsnmp_pdu **response) { return snmp_synch_response_cb(ss, pdu, response, agentx_synch_input); } /* * AgentX PofE convenience functions */ int agentx_open_session(netsnmp_session * ss) { netsnmp_pdu *pdu, *response; extern oid version_sysoid[]; DEBUGMSGTL(("agentx/subagent", "opening session \n")); if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } pdu = snmp_pdu_create(AGENTX_MSG_OPEN); if (pdu == NULL) return 0; pdu->time = 0; snmp_add_var(pdu, version_sysoid, SYSTEM_DOT_MIB_LENGTH, 's', "Net-SNMP AgentX sub-agent"); if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return 0; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return 0; } ss->sessid = response->sessid; snmp_free_pdu(response); DEBUGMSGTL(("agentx/subagent", "open \n")); return 1; } int agentx_close_session(netsnmp_session * ss, int why) { netsnmp_pdu *pdu, *response; DEBUGMSGTL(("agentx/subagent", "closing session\n")); if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } pdu = snmp_pdu_create(AGENTX_MSG_CLOSE); if (pdu == NULL) return 0; pdu->time = 0; pdu->errstat = why; pdu->sessid = ss->sessid; (void) agentx_synch_response(ss, pdu, &response); snmp_free_pdu(response); DEBUGMSGTL(("agentx/subagent", "closed\n")); return 1; } int agentx_register(netsnmp_session * ss, oid start[], size_t startlen, int priority, int range_subid, oid range_ubound, int timeout, u_char flags) { netsnmp_pdu *pdu, *response; DEBUGMSGTL(("agentx/subagent", "registering: ")); DEBUGMSGOIDRANGE(("agentx/subagent", start, startlen, range_subid, range_ubound)); DEBUGMSG(("agentx/subagent", "\n")); if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } pdu = snmp_pdu_create(AGENTX_MSG_REGISTER); if (pdu == NULL) { return 0; } pdu->time = timeout; pdu->priority = priority; pdu->sessid = ss->sessid; pdu->range_subid = range_subid; if (flags & FULLY_QUALIFIED_INSTANCE) { pdu->flags |= AGENTX_MSG_FLAG_INSTANCE_REGISTER; } if (range_subid) { snmp_pdu_add_variable(pdu, start, startlen, ASN_OBJECT_ID, (u_char *) start, startlen * sizeof(oid)); pdu->variables->val.objid[range_subid - 1] = range_ubound; } else { snmp_add_null_var(pdu, start, startlen); } if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) { DEBUGMSGTL(("agentx/subagent", "registering failed!\n")); return 0; } if (response->errstat != SNMP_ERR_NOERROR) { DEBUGMSGTL(("agentx/subagent", "registering pdu failed: %d!\n", response->errstat)); snmp_free_pdu(response); return 0; } snmp_free_pdu(response); DEBUGMSGTL(("agentx/subagent", "registered\n")); return 1; } int agentx_unregister(netsnmp_session * ss, oid start[], size_t startlen, int priority, int range_subid, oid range_ubound) { netsnmp_pdu *pdu, *response; if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } DEBUGMSGTL(("agentx/subagent", "unregistering: ")); DEBUGMSGOIDRANGE(("agentx/subagent", start, startlen, range_subid, range_ubound)); DEBUGMSG(("agentx/subagent", "\n")); pdu = snmp_pdu_create(AGENTX_MSG_UNREGISTER); if (pdu == NULL) { return 0; } pdu->time = 0; pdu->priority = priority; pdu->sessid = ss->sessid; pdu->range_subid = range_subid; if (range_subid) { snmp_pdu_add_variable(pdu, start, startlen, ASN_OBJECT_ID, (u_char *) start, startlen * sizeof(oid)); pdu->variables->val.objid[range_subid - 1] = range_ubound; } else { snmp_add_null_var(pdu, start, startlen); } if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return 0; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return 0; } snmp_free_pdu(response); DEBUGMSGTL(("agentx/subagent", "unregistered\n")); return 1; } netsnmp_variable_list * agentx_register_index(netsnmp_session * ss, netsnmp_variable_list * varbind, int flags) { netsnmp_pdu *pdu, *response; netsnmp_variable_list *varbind2; if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return NULL; } /* * Make a copy of the index request varbind * for the AgentX request PDU * (since the pdu structure will be freed) */ varbind2 = (netsnmp_variable_list *) malloc(sizeof(netsnmp_variable_list)); if (varbind2 == NULL) return NULL; if (snmp_clone_var(varbind, varbind2)) { snmp_free_varbind(varbind2); return NULL; } if (varbind2->val.string == NULL) varbind2->val.string = varbind2->buf; /* ensure it points somewhere */ pdu = snmp_pdu_create(AGENTX_MSG_INDEX_ALLOCATE); if (pdu == NULL) { snmp_free_varbind(varbind2); return NULL; } pdu->time = 0; pdu->sessid = ss->sessid; if (flags == ALLOCATE_ANY_INDEX) pdu->flags |= AGENTX_MSG_FLAG_ANY_INSTANCE; if (flags == ALLOCATE_NEW_INDEX) pdu->flags |= AGENTX_MSG_FLAG_NEW_INSTANCE; /* * Just send a single index request varbind. * Although the AgentX protocol supports * multiple index allocations in a single * request, the model used in the net-snmp agent * doesn't currently take advantage of this. * I believe this is our prerogative - just as * long as the master side Index request handler * can cope with multiple index requests. */ pdu->variables = varbind2; if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return NULL; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return NULL; } /* * Unlink the (single) response varbind to return * to the main driving index request routine. * * This is a memory leak, as nothing will ever * release this varbind. If this becomes a problem, * we'll need to keep a list of these here, and * free the memory in the "index release" routine. * But the master side never frees these either (by * design, since it still needs them), so expecting * the subagent to is discrimination, pure & simple :-) */ varbind2 = response->variables; response->variables = NULL; snmp_free_pdu(response); return varbind2; } int agentx_unregister_index(netsnmp_session * ss, netsnmp_variable_list * varbind) { netsnmp_pdu *pdu, *response; netsnmp_variable_list *varbind2; if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return -1; } /* * Make a copy of the index request varbind * for the AgentX request PDU * (since the pdu structure will be freed) */ varbind2 = (netsnmp_variable_list *) malloc(sizeof(netsnmp_variable_list)); if (varbind2 == NULL) return -1; if (snmp_clone_var(varbind, varbind2)) { snmp_free_varbind(varbind2); return -1; } pdu = snmp_pdu_create(AGENTX_MSG_INDEX_DEALLOCATE); if (pdu == NULL) { snmp_free_varbind(varbind2); return -1; } pdu->time = 0; pdu->sessid = ss->sessid; /* * Just send a single index release varbind. * (as above) */ pdu->variables = varbind2; if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return -1; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return -1; /* XXX - say why */ } snmp_free_pdu(response); return SNMP_ERR_NOERROR; } int agentx_add_agentcaps(netsnmp_session * ss, oid * agent_cap, size_t agent_caplen, const char *descr) { netsnmp_pdu *pdu, *response; if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } pdu = snmp_pdu_create(AGENTX_MSG_ADD_AGENT_CAPS); if (pdu == NULL) return 0; pdu->time = 0; pdu->sessid = ss->sessid; snmp_add_var(pdu, agent_cap, agent_caplen, 's', descr); if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return 0; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return 0; } snmp_free_pdu(response); return 1; } int agentx_remove_agentcaps(netsnmp_session * ss, oid * agent_cap, size_t agent_caplen) { netsnmp_pdu *pdu, *response; if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } pdu = snmp_pdu_create(AGENTX_MSG_REMOVE_AGENT_CAPS); if (pdu == NULL) return 0; pdu->time = 0; pdu->sessid = ss->sessid; snmp_add_null_var(pdu, agent_cap, agent_caplen); if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return 0; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return 0; } snmp_free_pdu(response); return 1; } int agentx_send_ping(netsnmp_session * ss) { netsnmp_pdu *pdu, *response; if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { return 0; } pdu = snmp_pdu_create(AGENTX_MSG_PING); if (pdu == NULL) return 0; pdu->time = 0; pdu->sessid = ss->sessid; if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) return 0; if (response->errstat != SNMP_ERR_NOERROR) { snmp_free_pdu(response); return 0; } snmp_free_pdu(response); return 1; }