2
* AgentX Administrative request handling
4
#include <net-snmp/net-snmp-config.h>
5
#include <net-snmp/net-snmp-features.h>
16
#if TIME_WITH_SYS_TIME
17
# include <sys/time.h>
21
# include <sys/time.h>
27
#include <netinet/in.h>
30
#include <sys/socket.h>
33
#include <net-snmp/net-snmp-includes.h>
34
#include <net-snmp/agent/net-snmp-agent-includes.h>
36
#include "agentx/protocol.h"
37
#include "agentx/client.h"
39
#include <net-snmp/agent/agent_index.h>
40
#include <net-snmp/agent/agent_trap.h>
41
#include <net-snmp/agent/agent_callbacks.h>
42
#include <net-snmp/agent/agent_sysORTable.h>
45
netsnmp_feature_require(unregister_mib_table_row)
46
netsnmp_feature_require(trap_vars_with_context)
47
netsnmp_feature_require(calculate_sectime_diff)
48
netsnmp_feature_require(allocate_globalcacheid)
49
netsnmp_feature_require(remove_index)
52
find_agentx_session(netsnmp_session * session, int sessid)
55
for (sp = session->subsession; sp != NULL; sp = sp->next) {
56
if (sp->sessid == sessid)
64
open_agentx_session(netsnmp_session * session, netsnmp_pdu *pdu)
68
DEBUGMSGTL(("agentx/master", "open %8p\n", session));
69
sp = (netsnmp_session *) malloc(sizeof(netsnmp_session));
71
session->s_snmp_errno = AGENTX_ERR_OPEN_FAILED;
75
memcpy(sp, session, sizeof(netsnmp_session));
76
sp->sessid = snmp_get_next_sessid();
77
sp->version = pdu->version;
78
sp->timeout = pdu->time;
81
* Be careful with fields: if these aren't zeroed, they will get free()d
82
* more than once when the session is closed -- once in the main session,
83
* and once in each subsession. Basically, if it's not being used for
84
* some AgentX-specific purpose, it ought to be zeroed here.
89
sp->contextEngineID = NULL;
90
sp->contextName = NULL;
91
sp->securityEngineID = NULL;
92
sp->securityPrivProto = NULL;
95
* This next bit utilises unused SNMPv3 fields
96
* to store the subagent OID and description.
97
* This really ought to use AgentX-specific fields,
98
* but it hardly seems worth it for a one-off use.
100
* But I'm willing to be persuaded otherwise.... */
101
sp->securityAuthProto = snmp_duplicate_objid(pdu->variables->name,
104
sp->securityAuthProtoLen = pdu->variables->name_length;
105
sp->securityName = strdup((char *) pdu->variables->val.string);
106
sp->engineTime = (uint32_t)((netsnmp_get_agent_runtime() + 50) / 100) & 0x7fffffffL;
108
sp->subsession = session; /* link back to head */
109
sp->flags |= SNMP_FLAGS_SUBSESSION;
110
sp->flags &= ~AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER;
111
sp->flags |= (pdu->flags & AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER);
112
sp->next = session->subsession;
113
session->subsession = sp;
114
DEBUGMSGTL(("agentx/master", "opened %8p = %ld with flags = %02lx\n",
115
sp, sp->sessid, sp->flags & AGENTX_MSG_FLAGS_MASK));
121
close_agentx_session(netsnmp_session * session, int sessid)
123
netsnmp_session *sp, **prevNext;
126
return AGENTX_ERR_NOT_OPEN;
128
DEBUGMSGTL(("agentx/master", "close %8p, %d\n", session, sessid));
131
* The following is necessary to avoid locking up the agent when
132
* a sugagent dies during a set request. We must clean up the
133
* requests, so that the delegated request will be completed and
134
* further requests can be processed
136
netsnmp_remove_delegated_requests_for_session(session);
137
if (session->subsession != NULL) {
138
netsnmp_session *subsession = session->subsession;
139
for(; subsession; subsession = subsession->next) {
140
netsnmp_remove_delegated_requests_for_session(subsession);
144
unregister_mibs_by_session(session);
145
unregister_index_by_session(session);
146
unregister_sysORTable_by_session(session);
147
SNMP_FREE(session->myvoid);
148
return AGENTX_ERR_NOERROR;
151
prevNext = &(session->subsession);
153
for (sp = session->subsession; sp != NULL; sp = sp->next) {
155
if (sp->sessid == sessid) {
156
unregister_mibs_by_session(sp);
157
unregister_index_by_session(sp);
158
unregister_sysORTable_by_session(sp);
160
*prevNext = sp->next;
162
if (sp->securityAuthProto != NULL) {
163
free(sp->securityAuthProto);
165
if (sp->securityName != NULL) {
166
free(sp->securityName);
171
DEBUGMSGTL(("agentx/master", "closed %8p, %d okay\n",
173
return AGENTX_ERR_NOERROR;
176
prevNext = &(sp->next);
179
DEBUGMSGTL(("agentx/master", "sessid %d not found\n", sessid));
180
return AGENTX_ERR_NOT_OPEN;
184
register_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
190
netsnmp_handler_registration *reg;
194
DEBUGMSGTL(("agentx/master", "in register_agentx_list\n"));
196
sp = find_agentx_session(session, pdu->sessid);
198
return AGENTX_ERR_NOT_OPEN;
200
sprintf(buf, "AgentX subagent %ld, session %8p, subsession %8p",
201
sp->sessid, session, sp);
203
* * TODO: registration timeout
204
* * registration context
206
if (pdu->range_subid) {
207
ubound = pdu->variables->val.objid[pdu->range_subid - 1];
210
if (pdu->flags & AGENTX_MSG_FLAG_INSTANCE_REGISTER) {
211
flags = FULLY_QUALIFIED_INSTANCE;
214
reg = netsnmp_create_handler_registration(buf, agentx_master_handler, pdu->variables->name, pdu->variables->name_length, HANDLER_CAN_RWRITE | HANDLER_CAN_GETBULK); /* fake it */
215
if (!session->myvoid) {
216
session->myvoid = malloc(sizeof(cacheid));
217
cacheid = netsnmp_allocate_globalcacheid();
218
*((int *) session->myvoid) = cacheid;
220
cacheid = *((int *) session->myvoid);
223
reg->handler->myvoid = session;
224
reg->global_cacheid = cacheid;
225
if (NULL != pdu->community)
226
reg->contextName = strdup((char *)pdu->community);
229
* register mib. Note that for failure cases, the registration info
230
* (reg) will be freed, and thus is no longer a valid pointer.
232
switch (netsnmp_register_mib(buf, NULL, 0, 1,
233
pdu->variables->name,
234
pdu->variables->name_length,
235
pdu->priority, pdu->range_subid, ubound,
236
sp, (char *) pdu->community, pdu->time,
239
case MIB_REGISTERED_OK:
240
DEBUGMSGTL(("agentx/master", "registered ok\n"));
241
return AGENTX_ERR_NOERROR;
243
case MIB_DUPLICATE_REGISTRATION:
244
DEBUGMSGTL(("agentx/master", "duplicate registration\n"));
245
rc = AGENTX_ERR_DUPLICATE_REGISTRATION;
248
case MIB_REGISTRATION_FAILED:
250
rc = AGENTX_ERR_REQUEST_DENIED;
251
DEBUGMSGTL(("agentx/master", "failed registration\n"));
257
unregister_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
262
sp = find_agentx_session(session, pdu->sessid);
264
return AGENTX_ERR_NOT_OPEN;
267
if (pdu->range_subid != 0) {
269
pdu->variables->val.objid[pdu->range_subid - 1];
270
rc = netsnmp_unregister_mib_table_row(pdu->variables->name,
271
pdu->variables->name_length,
273
pdu->range_subid, ubound,
274
(char *) pdu->community);
276
rc = unregister_mib_context(pdu->variables->name,
277
pdu->variables->name_length,
279
(char *) pdu->community);
283
case MIB_UNREGISTERED_OK:
284
return AGENTX_ERR_NOERROR;
285
case MIB_NO_SUCH_REGISTRATION:
286
return AGENTX_ERR_UNKNOWN_REGISTRATION;
287
case MIB_UNREGISTRATION_FAILED:
289
return AGENTX_ERR_REQUEST_DENIED;
294
allocate_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
297
netsnmp_variable_list *vp, *vp2, *next, *res;
300
sp = find_agentx_session(session, pdu->sessid);
302
return AGENTX_ERR_NOT_OPEN;
304
if (pdu->flags & AGENTX_MSG_FLAG_ANY_INSTANCE)
305
flags |= ALLOCATE_ANY_INDEX;
306
if (pdu->flags & AGENTX_MSG_FLAG_NEW_INSTANCE)
307
flags |= ALLOCATE_NEW_INDEX;
310
* XXX - what about errors?
312
* If any allocations fail, then we need to
313
* *fully* release the earlier ones.
314
* (i.e. remove them completely from the index registry,
315
* not simply mark them as available for re-use)
317
* For now - assume they all succeed.
319
for (vp = pdu->variables; vp != NULL; vp = next) {
320
next = vp->next_variable;
321
res = register_index(vp, flags, session);
324
* If any allocations fail, we need to *fully* release
325
* all previous ones (i.e. remove them completely
326
* from the index registry)
328
for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
329
remove_index(vp2, session);
331
return AGENTX_ERR_INDEX_NONE_AVAILABLE; /* XXX */
333
(void) snmp_clone_var(res, vp);
336
vp->next_variable = next;
338
return AGENTX_ERR_NOERROR;
342
release_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
345
netsnmp_variable_list *vp, *vp2, *rv = NULL;
348
sp = find_agentx_session(session, pdu->sessid);
350
return AGENTX_ERR_NOT_OPEN;
352
for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) {
353
res = unregister_index(vp, TRUE, session);
355
* If any releases fail,
356
* we need to reinstate all previous ones.
358
if (res != SNMP_ERR_NOERROR) {
359
for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
360
rv = register_index(vp2, ALLOCATE_THIS_INDEX, session);
363
return AGENTX_ERR_INDEX_NOT_ALLOCATED; /* Probably */
366
return AGENTX_ERR_NOERROR;
370
add_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
375
sp = find_agentx_session(session, pdu->sessid);
377
return AGENTX_ERR_NOT_OPEN;
379
description = netsnmp_strdup_and_null(pdu->variables->val.string,
380
pdu->variables->val_len);
381
register_sysORTable_sess(pdu->variables->name, pdu->variables->name_length,
384
return AGENTX_ERR_NOERROR;
388
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
rc = unregister_sysORTable_sess(pdu->variables->name,
398
pdu->variables->name_length, sp);
401
return AGENTX_ERR_UNKNOWN_AGENTCAPS;
403
return AGENTX_ERR_NOERROR;
407
agentx_notify(netsnmp_session * session, netsnmp_pdu *pdu)
410
netsnmp_variable_list *var;
411
extern const oid sysuptime_oid[], snmptrap_oid[];
412
extern const size_t sysuptime_oid_len, snmptrap_oid_len;
414
sp = find_agentx_session(session, pdu->sessid);
416
return AGENTX_ERR_NOT_OPEN;
418
var = pdu->variables;
420
return AGENTX_ERR_PROCESSING_ERROR;
422
if (snmp_oid_compare(var->name, var->name_length,
423
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
/* If a context name was specified, send the trap using that context.
441
* Otherwise, send the trap without the context using the old method */
442
if (pdu->contextName != NULL)
444
send_trap_vars_with_context(-1, -1, pdu->variables,
449
send_trap_vars(-1, -1, pdu->variables);
452
return AGENTX_ERR_NOERROR;
457
agentx_ping_response(netsnmp_session * session, netsnmp_pdu *pdu)
461
sp = find_agentx_session(session, pdu->sessid);
463
return AGENTX_ERR_NOT_OPEN;
465
return AGENTX_ERR_NOERROR;
469
handle_master_agentx_packet(int operation,
470
netsnmp_session * session,
471
int reqid, netsnmp_pdu *pdu, void *magic)
473
netsnmp_agent_session *asp;
475
if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
476
DEBUGMSGTL(("agentx/master",
477
"transport disconnect on session %8p\n", session));
479
* Shut this session down gracefully.
481
close_agentx_session(session, -1);
483
} else if (operation == NETSNMP_CALLBACK_OP_CONNECT) {
484
DEBUGMSGTL(("agentx/master",
485
"transport connect on session %8p\n", session));
487
} else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
488
DEBUGMSGTL(("agentx/master", "unexpected callback op %d\n",
494
* Okay, it's a NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE op.
498
asp = (netsnmp_agent_session *) magic;
500
asp = init_agent_snmp_session(session, pdu);
503
DEBUGMSGTL(("agentx/master", "handle pdu (req=0x%lx,trans=0x%lx,sess=0x%lx)\n",
504
(unsigned long)pdu->reqid, (unsigned long)pdu->transid,
505
(unsigned long)pdu->sessid));
507
switch (pdu->command) {
508
case AGENTX_MSG_OPEN:
509
asp->pdu->sessid = open_agentx_session(session, pdu);
510
if (asp->pdu->sessid == -1)
511
asp->status = session->s_snmp_errno;
514
case AGENTX_MSG_CLOSE:
515
asp->status = close_agentx_session(session, pdu->sessid);
518
case AGENTX_MSG_REGISTER:
519
asp->status = register_agentx_list(session, pdu);
522
case AGENTX_MSG_UNREGISTER:
523
asp->status = unregister_agentx_list(session, pdu);
526
case AGENTX_MSG_INDEX_ALLOCATE:
527
asp->status = allocate_idx_list(session, asp->pdu);
528
if (asp->status != AGENTX_ERR_NOERROR) {
529
snmp_free_pdu(asp->pdu);
530
asp->pdu = snmp_clone_pdu(pdu);
534
case AGENTX_MSG_INDEX_DEALLOCATE:
535
asp->status = release_idx_list(session, pdu);
538
case AGENTX_MSG_ADD_AGENT_CAPS:
539
asp->status = add_agent_caps_list(session, pdu);
542
case AGENTX_MSG_REMOVE_AGENT_CAPS:
543
asp->status = remove_agent_caps_list(session, pdu);
546
case AGENTX_MSG_NOTIFY:
547
asp->status = agentx_notify(session, pdu);
550
case AGENTX_MSG_PING:
551
asp->status = agentx_ping_response(session, pdu);
555
* TODO: Other admin packets
559
case AGENTX_MSG_GETNEXT:
560
case AGENTX_MSG_GETBULK:
561
case AGENTX_MSG_TESTSET:
562
case AGENTX_MSG_COMMITSET:
563
case AGENTX_MSG_UNDOSET:
564
case AGENTX_MSG_CLEANUPSET:
565
case AGENTX_MSG_RESPONSE:
567
* Shouldn't be handled here
572
asp->status = AGENTX_ERR_PARSE_FAILED;
576
asp->pdu->time = netsnmp_get_agent_uptime();
577
asp->pdu->command = AGENTX_MSG_RESPONSE;
578
asp->pdu->errstat = asp->status;
579
DEBUGMSGTL(("agentx/master", "send response, stat %d (req=0x%lx,trans="
580
"0x%lx,sess=0x%lx)\n",
581
asp->status, (unsigned long)pdu->reqid,
582
(unsigned long)pdu->transid, (unsigned long)pdu->sessid));
583
if (!snmp_send(asp->session, asp->pdu)) {
586
snmp_error(asp->session, &pe, &pse, &eb);
587
snmp_free_pdu(asp->pdu);
588
DEBUGMSGTL(("agentx/master", "FAILED %d %d %s\n", pe, pse, eb));
592
free_agent_snmp_session(asp);