5 #include <net-snmp/net-snmp-config.h>
18 #include <netinet/in.h>
24 #include <sys/socket.h>
32 #define SNMP_NEED_REQUEST_LIST
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/master_admin.h"
40 real_init_master(void)
42 netsnmp_session sess, *session;
46 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT)
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);
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);
63 agentx_sockets = strdup(AGENTX_SOCKET);
69 * If the AgentX socket string contains multiple descriptors,
70 * then pick this apart and handle them one by one.
73 cp2 = strchr(cp1, ',');
82 if (sess.peername[0] == '/') {
84 * If this is a Unix pathname,
85 * try and create the directory first.
87 if (mkdirhier(sess.peername, AGENT_DIRECTORY_MODE, 1)) {
89 "Failed to create the directory for the agentX socket: %s\n",
95 * Otherwise, let 'snmp_open' interpret the string.
97 sess.local_port = AGENTX_PORT; /* Indicate server & set default port */
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);
103 if (session == NULL && sess.s_errno == EADDRINUSE) {
105 * Could be a left-over socket (now deleted)
108 session = snmp_open_ex(&sess, NULL, agentx_parse, NULL, NULL,
109 agentx_realloc_build, agentx_check_packet);
112 if (session == NULL) {
114 * diagnose snmp_open errors with the input netsnmp_session pointer
116 if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
118 ("Error: Couldn't open a master agentx socket to listen on",
122 netsnmp_sess_log_error(LOG_WARNING,
123 "Warning: Couldn't open a agentx master socket to listen on",
128 * If we've processed the last (or only) socket, then we're done.
134 SNMP_FREE(agentx_sockets);
135 DEBUGMSGTL(("agentx/master", "initializing... DONE\n"));
139 * Handle the response from an AgentX subagent,
140 * merging the answers back into the original query
143 agentx_got_response(int operation,
144 netsnmp_session * session,
145 int reqid, netsnmp_pdu *pdu, void *magic)
147 netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) magic;
149 netsnmp_request_info *requests, *request;
150 netsnmp_variable_list *var;
151 netsnmp_session *ax_session;
153 cache = netsnmp_handler_check_cache(cache);
155 DEBUGMSGTL(("agentx/master", "response too late on session %08p\n",
159 requests = cache->requests;
162 case NETSNMP_CALLBACK_OP_TIMED_OUT:{
163 void *s = snmp_sess_pointer(session);
164 DEBUGMSGTL(("agentx/master", "timeout on session %08p\n",
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.
175 netsnmp_transport *t = snmp_sess_transport(s);
176 close_agentx_session(session, -1);
179 DEBUGMSGTL(("agentx/master", "close transport\n"));
182 DEBUGMSGTL(("agentx/master", "NULL transport??\n"));
185 DEBUGMSGTL(("agentx/master", "NULL sess_pointer??\n"));
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 */
191 ax_session = (netsnmp_session *) cache->localinfo;
192 netsnmp_free_agent_snmp_session_by_session(ax_session, NULL);
193 netsnmp_free_delegated_cache(cache);
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",
203 DEBUGMSGTL(("agentx/master", "send failed on session %08p\n",
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 */
211 netsnmp_free_delegated_cache(cache);
214 case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
216 * This session is alive
218 CLEAR_SNMP_STRIKE_FLAGS(session->flags);
221 snmp_log(LOG_ERR, "Unknown operation %d in agentx_got_response\n",
223 netsnmp_free_delegated_cache(cache);
227 DEBUGMSGTL(("agentx/master", "got response errstat=%d, (req=0x%x,trans="
229 pdu->errstat,pdu->reqid,pdu->transid, pdu->sessid));
231 if (pdu->errstat != AGENTX_ERR_NOERROR) {
233 * If the request failed, locate the
234 * original index of the variable resonsible
236 DEBUGMSGTL(("agentx/master",
237 "agentx_got_response() error branch\n"));
238 if (cache->reqinfo->mode == MODE_GETNEXT) {
240 * grr... got back an actual error for a getnext.
241 * Replace error with NULL and change the rest to retry
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;
249 request->delegated = REQUEST_IS_NOT_DELEGATED;
251 netsnmp_free_delegated_cache(cache);
252 DEBUGMSGTL(("agentx/master", "end error branch\n"));
255 DEBUGMSGTL(("agentx/master", "errindex=%d\n", pdu->errindex));
257 for (request = requests, i = 1; request;
258 request = request->next, i++) {
259 if (request->index == pdu->errindex) {
261 * mark this one as the one generating the error
263 netsnmp_set_request_error(cache->reqinfo, request,
267 request->delegated = REQUEST_IS_NOT_DELEGATED;
271 * ack, unknown, mark the first one
273 netsnmp_set_request_error(cache->reqinfo, request,
276 netsnmp_free_delegated_cache(cache);
277 DEBUGMSGTL(("agentx/master", "end error branch\n"));
280 } else if (cache->reqinfo->mode == MODE_GET ||
281 cache->reqinfo->mode == MODE_GETNEXT ||
282 cache->reqinfo->mode == MODE_GETBULK) {
284 * Replace varbinds for data request types, but not SETs.
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) {
291 * Otherwise, process successful requests
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"));
304 * update the oid in the original request
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,
312 request->delegated = REQUEST_IS_NOT_DELEGATED;
315 if (request || var) {
317 * ack, this is bad. The # of varbinds don't match and
318 * there is no way to fix the problem
321 "response to agentx request illegal. We're screwed.\n");
322 netsnmp_set_request_error(cache->reqinfo, requests,
326 if (cache->reqinfo->mode == MODE_GETBULK)
327 netsnmp_bulk_to_next_fix_requests(requests);
330 * mark set requests as handled
332 for (request = requests; request; request = request->next) {
333 request->delegated = REQUEST_IS_NOT_DELEGATED;
336 DEBUGMSGTL(("agentx/master",
337 "handle_agentx_response() finishing...\n"));
338 netsnmp_free_delegated_cache(cache);
344 * AgentX State diagram. [mode] = internal mode it's mapped from:
346 * TESTSET -success-> COMMIT -success-> CLEANUP
347 * [RESERVE1] [ACTION] [COMMIT]
349 * | \--failure-> UNDO
352 * --failure-> CLEANUP
356 agentx_master_handler(netsnmp_mib_handler *handler,
357 netsnmp_handler_registration *reginfo,
358 netsnmp_agent_request_info *reqinfo,
359 netsnmp_request_info *requests)
361 netsnmp_session *ax_session = (netsnmp_session *) handler->myvoid;
362 netsnmp_request_info *request = requests;
366 DEBUGMSGTL(("agentx/master",
367 "agentx master handler starting, mode = 0x%02x\n",
371 * build a new pdu based on the pdu type coming in
373 switch (reqinfo->mode) {
375 pdu = snmp_pdu_create(AGENTX_MSG_GET);
379 pdu = snmp_pdu_create(AGENTX_MSG_GETNEXT);
382 case MODE_GETBULK: /* WWWXXX */
383 pdu = snmp_pdu_create(AGENTX_MSG_GETNEXT);
386 case MODE_SET_RESERVE1:
387 pdu = snmp_pdu_create(AGENTX_MSG_TESTSET);
390 case MODE_SET_RESERVE2:
392 * don't do anything here for AgentX. Assume all is fine
393 * and go on since AgentX only has one test phase.
395 return SNMP_ERR_NOERROR;
397 case MODE_SET_ACTION:
398 pdu = snmp_pdu_create(AGENTX_MSG_COMMITSET);
402 pdu = snmp_pdu_create(AGENTX_MSG_UNDOSET);
405 case MODE_SET_COMMIT:
407 pdu = snmp_pdu_create(AGENTX_MSG_CLEANUPSET);
411 snmp_log(LOG_WARNING,
412 "unsupported mode for agentx/master called\n");
413 return SNMP_ERR_NOERROR;
416 if (!pdu || !ax_session) {
417 netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
418 return SNMP_ERR_NOERROR;
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;
430 size_t nlen = request->requestvb->name_length;
431 oid *nptr = request->requestvb->name;
433 DEBUGMSGTL(("agentx/master","request for variable ("));
434 DEBUGMSGOID(("agent/master", nptr, nlen));
435 DEBUGMSG(("agentx/master", ")\n"));
438 * loop through all the requests and create agentx ones out of them
441 if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
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;
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 *
465 request->inclusive = 0;
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 *
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);
487 * mark the request as delayed
489 if (pdu->command != AGENTX_MSG_CLEANUPSET)
490 request->delegated = REQUEST_IS_DELEGATED;
492 request->delegated = REQUEST_IS_NOT_DELEGATED;
497 request = request->next;
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.
505 if (pdu->command != AGENTX_MSG_CLEANUPSET)
506 cb_data = netsnmp_create_delegated_cache(handler, reginfo,
508 (void *) ax_session);
513 * send the requests out
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);
519 return SNMP_ERR_NOERROR;