# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / agentx / master.c
1 /*
2  *  AgentX master agent
3  */
4
5 #include <net-snmp/net-snmp-config.h>
6
7 #include <stdio.h>
8 #include <sys/types.h>
9 #ifdef HAVE_STDLIB_H
10 #include <stdlib.h>
11 #endif
12 #if HAVE_STRING_H
13 #include <string.h>
14 #else
15 #include <strings.h>
16 #endif
17 #if HAVE_NETINET_IN_H
18 #include <netinet/in.h>
19 #endif
20 #if HAVE_WINSOCK_H
21 #include <winsock.h>
22 #endif
23 #if HAVE_SYS_SOCKET_H
24 #include <sys/socket.h>
25 #endif
26 #include <errno.h>
27
28 #if HAVE_DMALLOC_H
29 #include <dmalloc.h>
30 #endif
31
32 #define SNMP_NEED_REQUEST_LIST
33 #include <net-snmp/net-snmp-includes.h>
34 #include <net-snmp/agent/net-snmp-agent-includes.h>
35 #include "snmpd.h"
36 #include "agentx/protocol.h"
37 #include "agentx/master_admin.h"
38
39 void
40 real_init_master(void)
41 {
42     netsnmp_session sess, *session;
43     char *agentx_sockets;
44     char *cp1, *cp2;
45
46     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT)
47         return;
48
49     DEBUGMSGTL(("agentx/master", "initializing...\n"));
50     snmp_sess_init(&sess);
51     sess.version = AGENTX_VERSION_1;
52     sess.flags |= SNMP_FLAGS_STREAM_SOCKET;
53     sess.timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
54                                       NETSNMP_DS_AGENT_AGENTX_TIMEOUT);
55     sess.retries = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
56                                       NETSNMP_DS_AGENT_AGENTX_RETRIES);
57
58     if (netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
59                               NETSNMP_DS_AGENT_X_SOCKET)) {
60         agentx_sockets = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
61                                                NETSNMP_DS_AGENT_X_SOCKET);
62     } else {
63         agentx_sockets = strdup(AGENTX_SOCKET);
64     }
65
66     cp1 = agentx_sockets;
67     while (1) {
68         /*
69          *  If the AgentX socket string contains multiple descriptors,
70          *  then pick this apart and handle them one by one.
71          *
72          */
73         cp2 = strchr(cp1, ',');
74         if (cp2 != NULL) {
75             *cp2 = '\0';
76         }
77         sess.peername = cp1;
78         if (cp2 != NULL) {
79             cp1 = cp2+1;
80         }
81     
82         if (sess.peername[0] == '/') {
83             /*
84              *  If this is a Unix pathname,
85              *  try and create the directory first.
86              */
87             if (mkdirhier(sess.peername, AGENT_DIRECTORY_MODE, 1)) {
88                 snmp_log(LOG_ERR,
89                          "Failed to create the directory for the agentX socket: %s\n",
90                          sess.peername);
91             }
92         }
93     
94         /*
95          *  Otherwise, let 'snmp_open' interpret the string.
96          */
97         sess.local_port = AGENTX_PORT;      /* Indicate server & set default port */
98         sess.remote_port = 0;
99         sess.callback = handle_master_agentx_packet;
100         session = snmp_open_ex(&sess, NULL, agentx_parse, NULL, NULL,
101                                agentx_realloc_build, agentx_check_packet);
102     
103         if (session == NULL && sess.s_errno == EADDRINUSE) {
104             /*
105              * Could be a left-over socket (now deleted)
106              * Try again
107              */
108             session = snmp_open_ex(&sess, NULL, agentx_parse, NULL, NULL,
109                                    agentx_realloc_build, agentx_check_packet);
110         }
111     
112         if (session == NULL) {
113             /*
114              * diagnose snmp_open errors with the input netsnmp_session pointer 
115              */
116             if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
117                 snmp_sess_perror
118                     ("Error: Couldn't open a master agentx socket to listen on",
119                      &sess);
120                 exit(1);
121             } else {
122                 netsnmp_sess_log_error(LOG_WARNING,
123                                        "Warning: Couldn't open a agentx master socket to listen on",
124                                        &sess);
125             }
126         }
127         /*
128          * If we've processed the last (or only) socket, then we're done.
129          */
130         if (!cp2)
131             break;
132     }
133
134     SNMP_FREE(agentx_sockets);
135     DEBUGMSGTL(("agentx/master", "initializing...   DONE\n"));
136 }
137
138         /*
139          * Handle the response from an AgentX subagent,
140          *   merging the answers back into the original query
141          */
142 int
143 agentx_got_response(int operation,
144                     netsnmp_session * session,
145                     int reqid, netsnmp_pdu *pdu, void *magic)
146 {
147     netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) magic;
148     int             i, ret;
149     netsnmp_request_info *requests, *request;
150     netsnmp_variable_list *var;
151     netsnmp_session *ax_session;
152
153     cache = netsnmp_handler_check_cache(cache);
154     if (!cache) {
155         DEBUGMSGTL(("agentx/master", "response too late on session %08p\n",
156                     session));
157         return 0;
158     }
159     requests = cache->requests;
160
161     switch (operation) {
162     case NETSNMP_CALLBACK_OP_TIMED_OUT:{
163             void           *s = snmp_sess_pointer(session);
164             DEBUGMSGTL(("agentx/master", "timeout on session %08p\n",
165                         session));
166
167             /*
168              * This is a bit sledgehammer because the other sessions on this
169              * transport may be okay (e.g. some thread in the subagent has
170              * wedged, but the others are alright).  OTOH the overwhelming
171              * probability is that the whole agent has died somehow.  
172              */
173
174             if (s != NULL) {
175                 netsnmp_transport *t = snmp_sess_transport(s);
176                 close_agentx_session(session, -1);
177
178                 if (t != NULL) {
179                     DEBUGMSGTL(("agentx/master", "close transport\n"));
180                     t->f_close(t);
181                 } else {
182                     DEBUGMSGTL(("agentx/master", "NULL transport??\n"));
183                 }
184             } else {
185                 DEBUGMSGTL(("agentx/master", "NULL sess_pointer??\n"));
186             }
187             netsnmp_handler_mark_requests_as_delegated(requests,
188                                                        REQUEST_IS_NOT_DELEGATED);
189             netsnmp_set_request_error(cache->reqinfo, requests, /* XXXWWW: should be index=0 */
190                                       SNMP_ERR_GENERR);
191             ax_session = (netsnmp_session *) cache->localinfo;
192             netsnmp_free_agent_snmp_session_by_session(ax_session, NULL);
193             netsnmp_free_delegated_cache(cache);
194             return 0;
195         }
196
197     case NETSNMP_CALLBACK_OP_DISCONNECT:
198     case NETSNMP_CALLBACK_OP_SEND_FAILED:
199         if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
200             DEBUGMSGTL(("agentx/master", "disconnect on session %08p\n",
201                         session));
202         } else {
203             DEBUGMSGTL(("agentx/master", "send failed on session %08p\n",
204                         session));
205         }
206         close_agentx_session(session, -1);
207         netsnmp_handler_mark_requests_as_delegated(requests,
208                                                    REQUEST_IS_NOT_DELEGATED);
209         netsnmp_set_request_error(cache->reqinfo, requests,     /* XXXWWW: should be index=0 */
210                                   SNMP_ERR_GENERR);
211         netsnmp_free_delegated_cache(cache);
212         return 0;
213
214     case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
215         /*
216          * This session is alive 
217          */
218         CLEAR_SNMP_STRIKE_FLAGS(session->flags);
219         break;
220     default:
221         snmp_log(LOG_ERR, "Unknown operation %d in agentx_got_response\n",
222                  operation);
223         netsnmp_free_delegated_cache(cache);
224         return 0;
225     }
226
227     DEBUGMSGTL(("agentx/master", "got response errstat=%d, (req=0x%x,trans="
228                 "0x%x,sess=0x%x)\n",
229                 pdu->errstat,pdu->reqid,pdu->transid, pdu->sessid));
230
231     if (pdu->errstat != AGENTX_ERR_NOERROR) {
232         /*
233          *  If the request failed, locate the
234          *    original index of the variable resonsible
235          */
236         DEBUGMSGTL(("agentx/master",
237                     "agentx_got_response() error branch\n"));
238         if (cache->reqinfo->mode == MODE_GETNEXT) {
239             /*
240              * grr...  got back an actual error for a getnext.
241              * Replace error with NULL and change the rest to retry 
242              */
243             for (request = requests, i = 1; request;
244                  request = request->next, i++) {
245                 if (request->index != pdu->errindex
246                     && request->requestvb->type == ASN_NULL) {
247                     request->requestvb->type = ASN_PRIV_RETRY;
248                 }
249                 request->delegated = REQUEST_IS_NOT_DELEGATED;
250             }
251             netsnmp_free_delegated_cache(cache);
252             DEBUGMSGTL(("agentx/master", "end error branch\n"));
253             return 1;
254         } else {
255             DEBUGMSGTL(("agentx/master", "errindex=%d\n", pdu->errindex));
256             ret = 0;
257             for (request = requests, i = 1; request;
258                  request = request->next, i++) {
259                 if (request->index == pdu->errindex) {
260                     /*
261                      * mark this one as the one generating the error 
262                      */
263                     netsnmp_set_request_error(cache->reqinfo, request,
264                                               pdu->errstat);
265                     ret = 1;
266                 }
267                 request->delegated = REQUEST_IS_NOT_DELEGATED;
268             }
269             if (!ret) {
270                 /*
271                  * ack, unknown, mark the first one 
272                  */
273                 netsnmp_set_request_error(cache->reqinfo, request,
274                                           SNMP_ERR_GENERR);
275             }
276             netsnmp_free_delegated_cache(cache);
277             DEBUGMSGTL(("agentx/master", "end error branch\n"));
278             return 1;
279         }
280     } else if (cache->reqinfo->mode == MODE_GET ||
281                cache->reqinfo->mode == MODE_GETNEXT ||
282                cache->reqinfo->mode == MODE_GETBULK) {
283         /*
284          * Replace varbinds for data request types, but not SETs.  
285          */
286         DEBUGMSGTL(("agentx/master",
287                     "agentx_got_response() beginning...\n"));
288         for (var = pdu->variables, request = requests; request && var;
289              request = request->next, var = var->next_variable) {
290             /*
291              * Otherwise, process successful requests
292              */
293             DEBUGMSGTL(("agentx/master",
294                         "  handle_agentx_response: processing: "));
295             DEBUGMSGOID(("agentx/master", var->name, var->name_length));
296             DEBUGMSG(("agentx/master", "\n"));
297             if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE)) {
298                 DEBUGMSGTL(("snmp_agent", "    >> "));
299                 DEBUGMSGVAR(("snmp_agent", var));
300                 DEBUGMSG(("snmp_agent", "\n"));
301             }
302
303             /*
304              * update the oid in the original request 
305              */
306             if (var->type != SNMP_ENDOFMIBVIEW) {
307                 snmp_set_var_typed_value(request->requestvb, var->type,
308                                          var->val.string, var->val_len);
309                 snmp_set_var_objid(request->requestvb, var->name,
310                                    var->name_length);
311             }
312             request->delegated = REQUEST_IS_NOT_DELEGATED;
313         }
314
315         if (request || var) {
316             /*
317              * ack, this is bad.  The # of varbinds don't match and
318              * there is no way to fix the problem 
319              */
320             snmp_log(LOG_ERR,
321                      "response to agentx request illegal.  We're screwed.\n");
322             netsnmp_set_request_error(cache->reqinfo, requests,
323                                       SNMP_ERR_GENERR);
324         }
325
326         if (cache->reqinfo->mode == MODE_GETBULK)
327             netsnmp_bulk_to_next_fix_requests(requests);
328     } else {
329         /*
330          * mark set requests as handled 
331          */
332         for (request = requests; request; request = request->next) {
333             request->delegated = REQUEST_IS_NOT_DELEGATED;
334         }
335     }
336     DEBUGMSGTL(("agentx/master",
337                 "handle_agentx_response() finishing...\n"));
338     netsnmp_free_delegated_cache(cache);
339     return 1;
340 }
341
342 /*
343  *
344  * AgentX State diagram.  [mode] = internal mode it's mapped from:
345  *
346  * TESTSET -success-> COMMIT -success-> CLEANUP
347  * [RESERVE1]         [ACTION]          [COMMIT]
348  *    |                 |
349  *    |                 \--failure-> UNDO
350  *    |                              [UNDO]
351  *    |
352  *     --failure-> CLEANUP
353  *                 [FREE]
354  */
355 int
356 agentx_master_handler(netsnmp_mib_handler *handler,
357                       netsnmp_handler_registration *reginfo,
358                       netsnmp_agent_request_info *reqinfo,
359                       netsnmp_request_info *requests)
360 {
361     netsnmp_session *ax_session = (netsnmp_session *) handler->myvoid;
362     netsnmp_request_info *request = requests;
363     netsnmp_pdu    *pdu;
364     void           *cb_data;
365
366     DEBUGMSGTL(("agentx/master",
367                 "agentx master handler starting, mode = 0x%02x\n",
368                 reqinfo->mode));
369
370     /*
371      * build a new pdu based on the pdu type coming in 
372      */
373     switch (reqinfo->mode) {
374     case MODE_GET:
375         pdu = snmp_pdu_create(AGENTX_MSG_GET);
376         break;
377
378     case MODE_GETNEXT:
379         pdu = snmp_pdu_create(AGENTX_MSG_GETNEXT);
380         break;
381
382     case MODE_GETBULK:         /* WWWXXX */
383         pdu = snmp_pdu_create(AGENTX_MSG_GETNEXT);
384         break;
385
386     case MODE_SET_RESERVE1:
387         pdu = snmp_pdu_create(AGENTX_MSG_TESTSET);
388         break;
389
390     case MODE_SET_RESERVE2:
391         /*
392          * don't do anything here for AgentX.  Assume all is fine
393          * and go on since AgentX only has one test phase. 
394          */
395         return SNMP_ERR_NOERROR;
396
397     case MODE_SET_ACTION:
398         pdu = snmp_pdu_create(AGENTX_MSG_COMMITSET);
399         break;
400
401     case MODE_SET_UNDO:
402         pdu = snmp_pdu_create(AGENTX_MSG_UNDOSET);
403         break;
404
405     case MODE_SET_COMMIT:
406     case MODE_SET_FREE:
407         pdu = snmp_pdu_create(AGENTX_MSG_CLEANUPSET);
408         break;
409
410     default:
411         snmp_log(LOG_WARNING,
412                  "unsupported mode for agentx/master called\n");
413         return SNMP_ERR_NOERROR;
414     }
415
416     if (!pdu || !ax_session) {
417         netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
418         return SNMP_ERR_NOERROR;
419     }
420
421     pdu->version = AGENTX_VERSION_1;
422     pdu->reqid = snmp_get_next_transid();
423     pdu->transid = reqinfo->asp->pdu->transid;
424     pdu->sessid = ax_session->subsession->sessid;
425     if (ax_session->subsession->flags & AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER)
426         pdu->flags |= AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER;
427
428     while (request) {
429
430         size_t nlen = request->requestvb->name_length;
431         oid   *nptr = request->requestvb->name;
432         
433         DEBUGMSGTL(("agentx/master","request for variable ("));
434         DEBUGMSGOID(("agent/master", nptr, nlen));
435         DEBUGMSG(("agentx/master", ")\n"));
436         
437         /*
438          * loop through all the requests and create agentx ones out of them 
439          */
440
441         if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
442
443             if (snmp_oid_compare(nptr, nlen, request->subtree->start_a,
444                                  request->subtree->start_len) < 0) {
445                 DEBUGMSGTL(("agentx/master","inexact request preceeding region ("));
446                 DEBUGMSGOID(("agent/master", request->subtree->start_a,
447                              request->subtree->start_len));
448                 DEBUGMSG(("agentx/master", ")\n"));
449                 nptr = request->subtree->start_a;
450                 nlen = request->subtree->start_len;
451                 request->inclusive = 1;
452             }
453
454             if (request->inclusive) {
455                 DEBUGMSGTL(("agentx/master", "INCLUSIVE varbind "));
456                 DEBUGMSGOID(("agentx/master", nptr, nlen));
457                 DEBUGMSG(("agentx/master", " scoped to "));
458                 DEBUGMSGOID(("agentx/master", request->range_end,
459                              request->range_end_len));
460                 DEBUGMSG(("agentx/master", "\n"));
461                 snmp_pdu_add_variable(pdu, nptr, nlen, ASN_PRIV_INCL_RANGE,
462                                       (u_char *) request->range_end,
463                                       request->range_end_len *
464                                       sizeof(oid));
465                 request->inclusive = 0;
466             } else {
467                 DEBUGMSGTL(("agentx/master", "EXCLUSIVE varbind "));
468                 DEBUGMSGOID(("agentx/master", nptr, nlen));
469                 DEBUGMSG(("agentx/master", " scoped to "));
470                 DEBUGMSGOID(("agentx/master", request->range_end,
471                              request->range_end_len));
472                 DEBUGMSG(("agentx/master", "\n"));
473                 snmp_pdu_add_variable(pdu, nptr, nlen, ASN_PRIV_EXCL_RANGE,
474                                       (u_char *) request->range_end,
475                                       request->range_end_len *
476                                       sizeof(oid));
477             }
478         } else {
479             snmp_pdu_add_variable(pdu, request->requestvb->name,
480                                   request->requestvb->name_length,
481                                   request->requestvb->type,
482                                   request->requestvb->val.string,
483                                   request->requestvb->val_len);
484         }
485
486         /*
487          * mark the request as delayed 
488          */
489         if (pdu->command != AGENTX_MSG_CLEANUPSET)
490             request->delegated = REQUEST_IS_DELEGATED;
491         else
492             request->delegated = REQUEST_IS_NOT_DELEGATED;
493
494         /*
495          * next... 
496          */
497         request = request->next;
498     }
499
500     /*
501      * When the master sends a CleanupSet PDU, it will never get a response
502      * back from the subagent. So we shouldn't allocate the
503      * netsnmp_delegated_cache structure in this case.
504      */
505     if (pdu->command != AGENTX_MSG_CLEANUPSET)
506         cb_data = netsnmp_create_delegated_cache(handler, reginfo,
507                                                  reqinfo, requests,
508                                                  (void *) ax_session);
509     else
510         cb_data = NULL;
511
512     /*
513      * send the requests out 
514      */
515     DEBUGMSGTL(("agentx", "sending pdu (req=0x%x,trans=0x%x,sess=0x%x)\n",
516                 pdu->reqid,pdu->transid, pdu->sessid));
517     snmp_async_send(ax_session, pdu, agentx_got_response, cb_data);
518
519     return SNMP_ERR_NOERROR;
520 }