2 * AgentX Administrative request handling
4 #include <net-snmp/net-snmp-config.h>
15 #if TIME_WITH_SYS_TIME
17 # include <sys/timeb.h>
19 # include <sys/time.h>
24 # include <sys/time.h>
30 #include <netinet/in.h>
36 #include <sys/socket.h>
42 #include <net-snmp/net-snmp-includes.h>
43 #include <net-snmp/agent/net-snmp-agent-includes.h>
45 #include "agentx/protocol.h"
46 #include "agentx/client.h"
48 #include <net-snmp/agent/agent_index.h>
49 #include <net-snmp/agent/agent_trap.h>
50 #include "mibII/sysORTable.h"
53 extern struct timeval starttime;
58 find_agentx_session(netsnmp_session * session, int sessid)
61 for (sp = session->subsession; sp != NULL; sp = sp->next) {
62 if (sp->sessid == sessid)
70 open_agentx_session(netsnmp_session * session, netsnmp_pdu *pdu)
75 DEBUGMSGTL(("agentx/master", "open %08p\n", session));
76 sp = (netsnmp_session *) malloc(sizeof(netsnmp_session));
78 session->s_snmp_errno = AGENTX_ERR_OPEN_FAILED;
82 memcpy(sp, session, sizeof(netsnmp_session));
83 sp->sessid = snmp_get_next_sessid();
84 sp->version = pdu->version;
85 sp->timeout = pdu->time;
88 * Be careful with fields: if these aren't zeroed, they will get free()d
89 * more than once when the session is closed -- once in the main session,
90 * and once in each subsession. Basically, if it's not being used for
91 * some AgentX-specific purpose, it ought to be zeroed here.
96 sp->contextEngineID = NULL;
97 sp->contextName = NULL;
98 sp->securityEngineID = NULL;
99 sp->securityPrivProto = NULL;
102 * This next bit utilises unused SNMPv3 fields
103 * to store the subagent OID and description.
104 * This really ought to use AgentX-specific fields,
105 * but it hardly seems worth it for a one-off use.
107 * But I'm willing to be persuaded otherwise.... */
108 sp->securityAuthProto = snmp_duplicate_objid(pdu->variables->name,
111 sp->securityAuthProtoLen = pdu->variables->name_length;
112 sp->securityName = strdup((char *) pdu->variables->val.string);
113 gettimeofday(&now, NULL);
114 sp->engineTime = calculate_time_diff(&now, &starttime);
116 sp->subsession = session; /* link back to head */
117 sp->flags |= SNMP_FLAGS_SUBSESSION;
118 sp->flags &= ~AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER;
119 sp->flags |= (pdu->flags & AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER);
120 sp->next = session->subsession;
121 session->subsession = sp;
122 DEBUGMSGTL(("agentx/master", "opened %08p = %d with flags = %02x\n",
123 sp, sp->sessid, sp->flags & AGENTX_MSG_FLAGS_MASK));
129 close_agentx_session(netsnmp_session * session, int sessid)
131 netsnmp_session *sp, **prevNext;
133 DEBUGMSGTL(("agentx/master", "close %08p, %d\n", session, sessid));
134 if (session != NULL && sessid == -1) {
135 unregister_mibs_by_session(session);
136 unregister_index_by_session(session);
137 unregister_sysORTable_by_session(session);
138 if (session->myvoid != NULL) {
139 free(session->myvoid);
142 * The following is necessary to avoid locking up the agent when
143 * a sugagent dies during a set request. We must clean up the
144 * requests, so that the delegated request will be completed and
145 * further requests can be processed
147 netsnmp_remove_delegated_requests_for_session(session);
148 if (session->subsession != NULL) {
149 netsnmp_session *subsession = session->subsession;
150 for(; subsession; subsession = subsession->next) {
151 netsnmp_remove_delegated_requests_for_session(subsession);
155 return AGENTX_ERR_NOERROR;
158 prevNext = &(session->subsession);
160 for (sp = session->subsession; sp != NULL; sp = sp->next) {
162 if (sp->sessid == sessid) {
163 unregister_mibs_by_session(sp);
164 unregister_index_by_session(sp);
165 unregister_sysORTable_by_session(sp);
167 *prevNext = sp->next;
169 if (sp->securityAuthProto != NULL) {
170 free(sp->securityAuthProto);
172 if (sp->securityName != NULL) {
173 free(sp->securityName);
178 DEBUGMSGTL(("agentx/master", "closed %08p, %d okay\n",
180 return AGENTX_ERR_NOERROR;
183 prevNext = &(sp->next);
186 DEBUGMSGTL(("agentx/master", "sessid %d not found\n", sessid));
187 return AGENTX_ERR_NOT_OPEN;
191 register_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
197 netsnmp_handler_registration *reg;
201 DEBUGMSGTL(("agentx/master", "in register_agentx_list\n"));
203 sp = find_agentx_session(session, pdu->sessid);
205 return AGENTX_ERR_NOT_OPEN;
207 sprintf(buf, "AgentX subagent %ld, session %8p, subsession %8p",
208 sp->sessid, session, sp);
210 * * TODO: registration timeout
211 * * registration context
213 if (pdu->range_subid) {
214 ubound = pdu->variables->val.objid[pdu->range_subid - 1];
217 if (pdu->flags & AGENTX_MSG_FLAG_INSTANCE_REGISTER) {
218 flags = FULLY_QUALIFIED_INSTANCE;
221 reg = netsnmp_create_handler_registration(buf, agentx_master_handler, pdu->variables->name, pdu->variables->name_length, HANDLER_CAN_RWRITE | HANDLER_CAN_GETBULK); /* fake it */
222 if (!session->myvoid) {
223 session->myvoid = malloc(sizeof(cacheid));
224 cacheid = netsnmp_allocate_globalcacheid();
225 *((int *) session->myvoid) = cacheid;
227 cacheid = *((int *) session->myvoid);
230 reg->handler->myvoid = session;
231 reg->global_cacheid = cacheid;
233 * register mib. Note that for failure cases, the registration info
234 * (reg) will be freed, and thus is no longer a valid pointer.
236 switch (netsnmp_register_mib(buf, NULL, 0, 1,
237 pdu->variables->name,
238 pdu->variables->name_length,
239 pdu->priority, pdu->range_subid, ubound,
240 sp, (char *) pdu->community, pdu->time,
243 case MIB_REGISTERED_OK:
244 DEBUGMSGTL(("agentx/master", "registered ok\n"));
245 return AGENTX_ERR_NOERROR;
247 case MIB_DUPLICATE_REGISTRATION:
248 DEBUGMSGTL(("agentx/master", "duplicate registration\n"));
249 rc = AGENTX_ERR_DUPLICATE_REGISTRATION;
252 case MIB_REGISTRATION_FAILED:
254 rc = AGENTX_ERR_REQUEST_DENIED;
255 DEBUGMSGTL(("agentx/master", "failed registration\n"));
261 unregister_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
266 sp = find_agentx_session(session, pdu->sessid);
268 return AGENTX_ERR_NOT_OPEN;
271 if (pdu->range_subid != 0) {
273 pdu->variables->val.objid[pdu->range_subid - 1];
274 rc = netsnmp_unregister_mib_table_row(pdu->variables->name,
275 pdu->variables->name_length,
277 pdu->range_subid, ubound,
278 (char *) pdu->community);
280 rc = unregister_mib_context(pdu->variables->name,
281 pdu->variables->name_length,
283 (char *) pdu->community);
287 case MIB_UNREGISTERED_OK:
288 return AGENTX_ERR_NOERROR;
289 case MIB_NO_SUCH_REGISTRATION:
290 return AGENTX_ERR_UNKNOWN_REGISTRATION;
291 case MIB_UNREGISTRATION_FAILED:
293 return AGENTX_ERR_REQUEST_DENIED;
298 allocate_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
301 netsnmp_variable_list *vp, *vp2, *next, *res;
304 sp = find_agentx_session(session, pdu->sessid);
306 return AGENTX_ERR_NOT_OPEN;
308 if (pdu->flags & AGENTX_MSG_FLAG_ANY_INSTANCE)
309 flags |= ALLOCATE_ANY_INDEX;
310 if (pdu->flags & AGENTX_MSG_FLAG_NEW_INSTANCE)
311 flags |= ALLOCATE_NEW_INDEX;
314 * XXX - what about errors?
316 * If any allocations fail, then we need to
317 * *fully* release the earlier ones.
318 * (i.e. remove them completely from the index registry,
319 * not simply mark them as available for re-use)
321 * For now - assume they all succeed.
323 for (vp = pdu->variables; vp != NULL; vp = next) {
324 next = vp->next_variable;
325 res = register_index(vp, flags, session);
328 * If any allocations fail, we need to *fully* release
329 * all previous ones (i.e. remove them completely
330 * from the index registry)
332 for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
333 remove_index(vp2, session);
335 return AGENTX_ERR_INDEX_NONE_AVAILABLE; /* XXX */
337 (void) snmp_clone_var(res, vp);
340 vp->next_variable = next;
342 return AGENTX_ERR_NOERROR;
346 release_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
349 netsnmp_variable_list *vp, *vp2, *rv = NULL;
352 sp = find_agentx_session(session, pdu->sessid);
354 return AGENTX_ERR_NOT_OPEN;
356 for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) {
357 res = unregister_index(vp, TRUE, session);
359 * If any releases fail,
360 * we need to reinstate all previous ones.
362 if (res != SNMP_ERR_NOERROR) {
363 for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
364 rv = register_index(vp2, ALLOCATE_THIS_INDEX, session);
367 return AGENTX_ERR_INDEX_NOT_ALLOCATED; /* Probably */
370 return AGENTX_ERR_NOERROR;
374 add_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
378 sp = find_agentx_session(session, pdu->sessid);
380 return AGENTX_ERR_NOT_OPEN;
382 register_sysORTable_sess(pdu->variables->name,
383 pdu->variables->name_length,
384 (char *) pdu->variables->val.string, sp);
385 return AGENTX_ERR_NOERROR;
389 remove_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
393 sp = find_agentx_session(session, pdu->sessid);
395 return AGENTX_ERR_NOT_OPEN;
397 if (unregister_sysORTable_sess(pdu->variables->name,
398 pdu->variables->name_length, sp) < 0)
399 return AGENTX_ERR_UNKNOWN_AGENTCAPS;
401 return AGENTX_ERR_NOERROR;
405 agentx_notify(netsnmp_session * session, netsnmp_pdu *pdu)
408 netsnmp_variable_list *var;
409 int got_sysuptime = 0;
410 extern oid sysuptime_oid[], snmptrap_oid[];
411 extern size_t sysuptime_oid_len, snmptrap_oid_len;
413 sp = find_agentx_session(session, pdu->sessid);
415 return AGENTX_ERR_NOT_OPEN;
417 var = pdu->variables;
419 return AGENTX_ERR_PROCESSING_ERROR;
421 if (snmp_oid_compare(var->name, var->name_length,
422 sysuptime_oid, sysuptime_oid_len) == 0) {
424 var = var->next_variable;
427 if (!var || snmp_oid_compare(var->name, var->name_length,
428 snmptrap_oid, snmptrap_oid_len) != 0)
429 return AGENTX_ERR_PROCESSING_ERROR;
432 * If sysUptime isn't the first varbind, don't worry.
433 * send_trap_vars() will add it if necessary.
435 * Note that if this behaviour is altered, it will
436 * be necessary to add sysUptime here,
437 * as this is valid AgentX syntax.
440 send_trap_vars(-1, -1, pdu->variables);
441 return AGENTX_ERR_NOERROR;
446 agentx_ping_response(netsnmp_session * session, netsnmp_pdu *pdu)
450 sp = find_agentx_session(session, pdu->sessid);
452 return AGENTX_ERR_NOT_OPEN;
454 return AGENTX_ERR_NOERROR;
458 handle_master_agentx_packet(int operation,
459 netsnmp_session * session,
460 int reqid, netsnmp_pdu *pdu, void *magic)
462 netsnmp_agent_session *asp;
465 if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
466 DEBUGMSGTL(("agentx/master",
467 "transport disconnect on session %08p\n", session));
469 * Shut this session down gracefully.
471 close_agentx_session(session, -1);
473 } else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
474 DEBUGMSGTL(("agentx/master", "unexpected callback op %d\n",
480 * Okay, it's a NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE op.
484 asp = (netsnmp_agent_session *) magic;
486 asp = init_agent_snmp_session(session, pdu);
489 DEBUGMSGTL(("agentx/master", "handle pdu (req=0x%x,trans=0x%x,sess=0x%x)\n",
490 pdu->reqid,pdu->transid, pdu->sessid));
492 switch (pdu->command) {
493 case AGENTX_MSG_OPEN:
494 asp->pdu->sessid = open_agentx_session(session, pdu);
495 if (asp->pdu->sessid == -1)
496 asp->status = session->s_snmp_errno;
499 case AGENTX_MSG_CLOSE:
500 asp->status = close_agentx_session(session, pdu->sessid);
503 case AGENTX_MSG_REGISTER:
504 asp->status = register_agentx_list(session, pdu);
507 case AGENTX_MSG_UNREGISTER:
508 asp->status = unregister_agentx_list(session, pdu);
511 case AGENTX_MSG_INDEX_ALLOCATE:
512 asp->status = allocate_idx_list(session, asp->pdu);
513 if (asp->status != AGENTX_ERR_NOERROR) {
514 snmp_free_pdu(asp->pdu);
515 asp->pdu = snmp_clone_pdu(pdu);
519 case AGENTX_MSG_INDEX_DEALLOCATE:
520 asp->status = release_idx_list(session, pdu);
523 case AGENTX_MSG_ADD_AGENT_CAPS:
524 asp->status = add_agent_caps_list(session, pdu);
527 case AGENTX_MSG_REMOVE_AGENT_CAPS:
528 asp->status = remove_agent_caps_list(session, pdu);
531 case AGENTX_MSG_NOTIFY:
532 asp->status = agentx_notify(session, pdu);
535 case AGENTX_MSG_PING:
536 asp->status = agentx_ping_response(session, pdu);
540 * TODO: Other admin packets
544 case AGENTX_MSG_GETNEXT:
545 case AGENTX_MSG_GETBULK:
546 case AGENTX_MSG_TESTSET:
547 case AGENTX_MSG_COMMITSET:
548 case AGENTX_MSG_UNDOSET:
549 case AGENTX_MSG_CLEANUPSET:
550 case AGENTX_MSG_RESPONSE:
552 * Shouldn't be handled here
557 asp->status = AGENTX_ERR_PARSE_FAILED;
561 gettimeofday(&now, NULL);
562 asp->pdu->time = calculate_time_diff(&now, &starttime);
563 asp->pdu->command = AGENTX_MSG_RESPONSE;
564 asp->pdu->errstat = asp->status;
565 DEBUGMSGTL(("agentx/master", "send response, stat %d (req=0x%x,trans="
567 asp->status, pdu->reqid,pdu->transid, pdu->sessid));
568 if (!snmp_send(asp->session, asp->pdu)) {
571 snmp_error(asp->session, &pe, &pse, &eb);
572 snmp_free_pdu(asp->pdu);
573 DEBUGMSGTL(("agentx/master", "FAILED %d %d %s\n", pe, pse, eb));
577 free_agent_snmp_session(asp);