f831ca13d876fdf4f6681b5ab471cee606650125
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / agentx / master_admin.c
1 /*
2  *  AgentX Administrative request handling
3  */
4 #include <net-snmp/net-snmp-config.h>
5
6 #include <sys/types.h>
7 #ifdef HAVE_STRING_H
8 #include <string.h>
9 #else
10 #include <strings.h>
11 #endif
12 #ifdef HAVE_STDLIB_H
13 #include <stdlib.h>
14 #endif
15 #if TIME_WITH_SYS_TIME
16 # ifdef WIN32
17 #  include <sys/timeb.h>
18 # else
19 #  include <sys/time.h>
20 # endif
21 # include <time.h>
22 #else
23 # if HAVE_SYS_TIME_H
24 #  include <sys/time.h>
25 # else
26 #  include <time.h>
27 # endif
28 #endif
29 #if HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #if HAVE_WINSOCK_H
33 #include <winsock.h>
34 #endif
35 #if HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
37 #endif
38 #if HAVE_DMALLOC_H
39 #include <dmalloc.h>
40 #endif
41
42 #include <net-snmp/net-snmp-includes.h>
43 #include <net-snmp/agent/net-snmp-agent-includes.h>
44
45 #include "agentx/protocol.h"
46 #include "agentx/client.h"
47
48 #include <net-snmp/agent/agent_index.h>
49 #include <net-snmp/agent/agent_trap.h>
50 #include "mibII/sysORTable.h"
51 #include "master.h"
52
53 extern struct timeval starttime;
54
55
56
57 netsnmp_session *
58 find_agentx_session(netsnmp_session * session, int sessid)
59 {
60     netsnmp_session *sp;
61     for (sp = session->subsession; sp != NULL; sp = sp->next) {
62         if (sp->sessid == sessid)
63             return sp;
64     }
65     return NULL;
66 }
67
68
69 int
70 open_agentx_session(netsnmp_session * session, netsnmp_pdu *pdu)
71 {
72     netsnmp_session *sp;
73     struct timeval  now;
74
75     DEBUGMSGTL(("agentx/master", "open %08p\n", session));
76     sp = (netsnmp_session *) malloc(sizeof(netsnmp_session));
77     if (sp == NULL) {
78         session->s_snmp_errno = AGENTX_ERR_OPEN_FAILED;
79         return -1;
80     }
81
82     memcpy(sp, session, sizeof(netsnmp_session));
83     sp->sessid = snmp_get_next_sessid();
84     sp->version = pdu->version;
85     sp->timeout = pdu->time;
86
87     /*
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. 
92      */
93
94     sp->community = NULL;
95     sp->peername = NULL;
96     sp->contextEngineID = NULL;
97     sp->contextName = NULL;
98     sp->securityEngineID = NULL;
99     sp->securityPrivProto = NULL;
100
101     /*
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.
106      *
107      * But I'm willing to be persuaded otherwise....  */
108     sp->securityAuthProto = snmp_duplicate_objid(pdu->variables->name,
109                                                  pdu->variables->
110                                                  name_length);
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);
115
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));
124
125     return sp->sessid;
126 }
127
128 int
129 close_agentx_session(netsnmp_session * session, int sessid)
130 {
131     netsnmp_session *sp, **prevNext;
132
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);
140         }
141         /*
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
146          */
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);
152             }
153         }
154                 
155         return AGENTX_ERR_NOERROR;
156     }
157
158     prevNext = &(session->subsession);
159
160     for (sp = session->subsession; sp != NULL; sp = sp->next) {
161
162         if (sp->sessid == sessid) {
163             unregister_mibs_by_session(sp);
164             unregister_index_by_session(sp);
165             unregister_sysORTable_by_session(sp);
166
167             *prevNext = sp->next;
168
169             if (sp->securityAuthProto != NULL) {
170                 free(sp->securityAuthProto);
171             }
172             if (sp->securityName != NULL) {
173                 free(sp->securityName);
174             }
175             free(sp);
176             sp = NULL;
177
178             DEBUGMSGTL(("agentx/master", "closed %08p, %d okay\n",
179                         session, sessid));
180             return AGENTX_ERR_NOERROR;
181         }
182
183         prevNext = &(sp->next);
184     }
185
186     DEBUGMSGTL(("agentx/master", "sessid %d not found\n", sessid));
187     return AGENTX_ERR_NOT_OPEN;
188 }
189
190 int
191 register_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
192 {
193     netsnmp_session *sp;
194     char            buf[128];
195     oid             ubound = 0;
196     u_long          flags = 0;
197     netsnmp_handler_registration *reg;
198     int             rc = 0;
199     int             cacheid;
200
201     DEBUGMSGTL(("agentx/master", "in register_agentx_list\n"));
202
203     sp = find_agentx_session(session, pdu->sessid);
204     if (sp == NULL)
205         return AGENTX_ERR_NOT_OPEN;
206
207     sprintf(buf, "AgentX subagent %ld, session %8p, subsession %8p",
208             sp->sessid, session, sp);
209     /*
210      * * TODO: registration timeout
211      * *   registration context
212      */
213     if (pdu->range_subid) {
214         ubound = pdu->variables->val.objid[pdu->range_subid - 1];
215     }
216
217     if (pdu->flags & AGENTX_MSG_FLAG_INSTANCE_REGISTER) {
218         flags = FULLY_QUALIFIED_INSTANCE;
219     }
220
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;
226     } else {
227         cacheid = *((int *) session->myvoid);
228     }
229
230     reg->handler->myvoid = session;
231     reg->global_cacheid = cacheid;
232     /*
233      * register mib. Note that for failure cases, the registration info
234      * (reg) will be freed, and thus is no longer a valid pointer.
235      */
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,
241                                  flags, reg, 1)) {
242
243     case MIB_REGISTERED_OK:
244         DEBUGMSGTL(("agentx/master", "registered ok\n"));
245         return AGENTX_ERR_NOERROR;
246
247     case MIB_DUPLICATE_REGISTRATION:
248         DEBUGMSGTL(("agentx/master", "duplicate registration\n"));
249         rc = AGENTX_ERR_DUPLICATE_REGISTRATION;
250         break;
251
252     case MIB_REGISTRATION_FAILED:
253     default:
254         rc = AGENTX_ERR_REQUEST_DENIED;
255         DEBUGMSGTL(("agentx/master", "failed registration\n"));
256     }
257     return rc;
258 }
259
260 int
261 unregister_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
262 {
263     netsnmp_session *sp;
264     int             rc = 0;
265
266     sp = find_agentx_session(session, pdu->sessid);
267     if (sp == NULL) {
268         return AGENTX_ERR_NOT_OPEN;
269     }
270
271     if (pdu->range_subid != 0) {
272         oid             ubound =
273             pdu->variables->val.objid[pdu->range_subid - 1];
274         rc = netsnmp_unregister_mib_table_row(pdu->variables->name,
275                                               pdu->variables->name_length,
276                                               pdu->priority,
277                                               pdu->range_subid, ubound,
278                                               (char *) pdu->community);
279     } else {
280         rc = unregister_mib_context(pdu->variables->name,
281                                     pdu->variables->name_length,
282                                     pdu->priority, 0, 0,
283                                     (char *) pdu->community);
284     }
285
286     switch (rc) {
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:
292     default:
293         return AGENTX_ERR_REQUEST_DENIED;
294     }
295 }
296
297 int
298 allocate_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
299 {
300     netsnmp_session *sp;
301     netsnmp_variable_list *vp, *vp2, *next, *res;
302     int             flags = 0;
303
304     sp = find_agentx_session(session, pdu->sessid);
305     if (sp == NULL)
306         return AGENTX_ERR_NOT_OPEN;
307
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;
312
313     /*
314      * XXX - what about errors?
315      *
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)
320      *
321      * For now - assume they all succeed.
322      */
323     for (vp = pdu->variables; vp != NULL; vp = next) {
324         next = vp->next_variable;
325         res = register_index(vp, flags, session);
326         if (res == NULL) {
327             /*
328              *  If any allocations fail, we need to *fully* release
329              *      all previous ones (i.e. remove them completely
330              *      from the index registry)
331              */
332             for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
333                 remove_index(vp2, session);
334             }
335             return AGENTX_ERR_INDEX_NONE_AVAILABLE;     /* XXX */
336         } else {
337             (void) snmp_clone_var(res, vp);
338             free(res);
339         }
340         vp->next_variable = next;
341     }
342     return AGENTX_ERR_NOERROR;
343 }
344
345 int
346 release_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
347 {
348     netsnmp_session *sp;
349     netsnmp_variable_list *vp, *vp2, *rv = NULL;
350     int             res;
351
352     sp = find_agentx_session(session, pdu->sessid);
353     if (sp == NULL)
354         return AGENTX_ERR_NOT_OPEN;
355
356     for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) {
357         res = unregister_index(vp, TRUE, session);
358         /*
359          *  If any releases fail,
360          *      we need to reinstate all previous ones.
361          */
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);
365                 free(rv);
366             }
367             return AGENTX_ERR_INDEX_NOT_ALLOCATED;      /* Probably */
368         }
369     }
370     return AGENTX_ERR_NOERROR;
371 }
372
373 int
374 add_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
375 {
376     netsnmp_session *sp;
377
378     sp = find_agentx_session(session, pdu->sessid);
379     if (sp == NULL)
380         return AGENTX_ERR_NOT_OPEN;
381
382     register_sysORTable_sess(pdu->variables->name,
383                              pdu->variables->name_length,
384                              (char *) pdu->variables->val.string, sp);
385     return AGENTX_ERR_NOERROR;
386 }
387
388 int
389 remove_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
390 {
391     netsnmp_session *sp;
392
393     sp = find_agentx_session(session, pdu->sessid);
394     if (sp == NULL)
395         return AGENTX_ERR_NOT_OPEN;
396
397     if (unregister_sysORTable_sess(pdu->variables->name,
398                                    pdu->variables->name_length, sp) < 0)
399         return AGENTX_ERR_UNKNOWN_AGENTCAPS;
400     else
401         return AGENTX_ERR_NOERROR;
402 }
403
404 int
405 agentx_notify(netsnmp_session * session, netsnmp_pdu *pdu)
406 {
407     netsnmp_session *sp;
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;
412
413     sp = find_agentx_session(session, pdu->sessid);
414     if (sp == NULL)
415         return AGENTX_ERR_NOT_OPEN;
416
417     var = pdu->variables;
418     if (!var)
419         return AGENTX_ERR_PROCESSING_ERROR;
420
421     if (snmp_oid_compare(var->name, var->name_length,
422                          sysuptime_oid, sysuptime_oid_len) == 0) {
423         got_sysuptime = 1;
424         var = var->next_variable;
425     }
426
427     if (!var || snmp_oid_compare(var->name, var->name_length,
428                                  snmptrap_oid, snmptrap_oid_len) != 0)
429         return AGENTX_ERR_PROCESSING_ERROR;
430
431     /*
432      *  If sysUptime isn't the first varbind, don't worry.  
433      *     send_trap_vars() will add it if necessary.
434      *
435      *  Note that if this behaviour is altered, it will
436      *     be necessary to add sysUptime here,
437      *     as this is valid AgentX syntax.
438      */
439
440     send_trap_vars(-1, -1, pdu->variables);
441     return AGENTX_ERR_NOERROR;
442 }
443
444
445 int
446 agentx_ping_response(netsnmp_session * session, netsnmp_pdu *pdu)
447 {
448     netsnmp_session *sp;
449
450     sp = find_agentx_session(session, pdu->sessid);
451     if (sp == NULL)
452         return AGENTX_ERR_NOT_OPEN;
453     else
454         return AGENTX_ERR_NOERROR;
455 }
456
457 int
458 handle_master_agentx_packet(int operation,
459                             netsnmp_session * session,
460                             int reqid, netsnmp_pdu *pdu, void *magic)
461 {
462     netsnmp_agent_session *asp;
463     struct timeval  now;
464
465     if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
466         DEBUGMSGTL(("agentx/master",
467                     "transport disconnect on session %08p\n", session));
468         /*
469          * Shut this session down gracefully.  
470          */
471         close_agentx_session(session, -1);
472         return 1;
473     } else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
474         DEBUGMSGTL(("agentx/master", "unexpected callback op %d\n",
475                     operation));
476         return 1;
477     }
478
479     /*
480      * Okay, it's a NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE op.  
481      */
482
483     if (magic) {
484         asp = (netsnmp_agent_session *) magic;
485     } else {
486         asp = init_agent_snmp_session(session, pdu);
487     }
488
489     DEBUGMSGTL(("agentx/master", "handle pdu (req=0x%x,trans=0x%x,sess=0x%x)\n",
490                 pdu->reqid,pdu->transid, pdu->sessid));
491     
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;
497         break;
498
499     case AGENTX_MSG_CLOSE:
500         asp->status = close_agentx_session(session, pdu->sessid);
501         break;
502
503     case AGENTX_MSG_REGISTER:
504         asp->status = register_agentx_list(session, pdu);
505         break;
506
507     case AGENTX_MSG_UNREGISTER:
508         asp->status = unregister_agentx_list(session, pdu);
509         break;
510
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);
516         }
517         break;
518
519     case AGENTX_MSG_INDEX_DEALLOCATE:
520         asp->status = release_idx_list(session, pdu);
521         break;
522
523     case AGENTX_MSG_ADD_AGENT_CAPS:
524         asp->status = add_agent_caps_list(session, pdu);
525         break;
526
527     case AGENTX_MSG_REMOVE_AGENT_CAPS:
528         asp->status = remove_agent_caps_list(session, pdu);
529         break;
530
531     case AGENTX_MSG_NOTIFY:
532         asp->status = agentx_notify(session, pdu);
533         break;
534
535     case AGENTX_MSG_PING:
536         asp->status = agentx_ping_response(session, pdu);
537         break;
538
539         /*
540          * TODO: Other admin packets 
541          */
542
543     case AGENTX_MSG_GET:
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:
551         /*
552          * Shouldn't be handled here 
553          */
554         break;
555
556     default:
557         asp->status = AGENTX_ERR_PARSE_FAILED;
558         break;
559     }
560
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="
566                 "0x%x,sess=0x%x)\n",
567                 asp->status, pdu->reqid,pdu->transid, pdu->sessid));
568     if (!snmp_send(asp->session, asp->pdu)) {
569         char           *eb = NULL;
570         int             pe, pse;
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));
574         free(eb);
575     }
576     asp->pdu = NULL;
577     free_agent_snmp_session(asp);
578
579     return 1;
580 }