📄 snmp.xs
字号:
else done = _bulkwalk_done(context); /* Also set req ignore flags */ break; } default: { DBPRT(1,(DBOUT "unexpected pdu->command %d\n", pdu->command)); done = 1; /* "This can't happen!", so bail out when it does. */ break; } } break; } case NETSNMP_CALLBACK_OP_TIMED_OUT: { DBPRT(1,(DBOUT "\n*** Timeout for reqid 0x%08X\n\n", reqid)); sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_TIMEOUT)); sv_setiv(*err_num_svp, SNMPERR_TIMEOUT); /* Timeout means something bad has happened. Return a not-okay ** result to the async callback. */ npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */); return 1; } default: { DBPRT(1,(DBOUT "unexpected callback op %d\n", op)); sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_GENERR)); sv_setiv(*err_num_svp, SNMPERR_GENERR); npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */); return 1; } } /* We have either timed out, or received and parsed in a response. Now, ** if we have more variables to test, call bulkwalk_send_pdu() to enqueue ** another async packet, and return. ** ** If, however, the bulkwalk has completed (or an error has occurred that ** cuts the walk short), call bulkwalk_finish() to push the results onto ** the Perl call stack. Then explicitly call the Perl callback that was ** passed in by the user oh-so-long-ago. */ if (!done) { DBPRT(1,(DBOUT "bulkwalk not complete -- send next pdu from callback\n")); if (_bulkwalk_send_pdu(context) != NULL) return 1; DBPRT(1,(DBOUT "send_pdu() failed!\n")); /* Fall through and return what we have so far. */ } /* Call the perl callback with the return values and we're done. */ npushed = _bulkwalk_finish(context, 1 /* OKAY */); return 1;}static netsnmp_pdu *_bulkwalk_send_pdu(walk_context *context){ netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; struct bulktbl *bt_entry; int nvars = 0; int reqid; int status; int i; /* Send a pdu requesting any remaining variables in the context. ** ** In synchronous mode, returns a pointer to the response packet. ** ** In asynchronous mode, it returns the request ID, cast to a struct snmp *, ** not a valid SNMP response packet. The async code should not be trying ** to get variables out of this "response". ** ** In either case, return a NULL pointer on error or failure. */ SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1); netsnmp_session *ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1); SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1); SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1); /* Create a new PDU and send the remaining set of requests to the agent. */ pdu = snmp_pdu_create(SNMP_MSG_GETBULK); if (pdu == NULL) { sv_setpv(*err_str_svp, "snmp_pdu_create(GETBULK) failed: "); sv_catpv(*err_str_svp, strerror(errno)); sv_setiv(*err_num_svp, SNMPERR_MALLOC); goto err; } /* Request non-repeater variables only in the first packet exchange. */ pdu->errstat = (context->pkts_exch == 0) ? context->non_reps : 0; pdu->errindex = context->max_reps; for (i = 0; i < context->nreq_oids; i++) { bt_entry = &context->req_oids[i]; if (bt_entry->ignore) continue; assert(bt_entry->complete == 0); if (!snmp_add_null_var(pdu, bt_entry->last_oid, bt_entry->last_len)) { sv_setpv(*err_str_svp, "snmp_add_null_var() failed"); sv_setiv(*err_num_svp, SNMPERR_GENERR); sv_setiv(*err_ind_svp, i); goto err; } nvars ++; DBPRT(1, (DBOUT " Add %srepeater %s\n", bt_entry->norepeat ? "non" : "", __snprint_oid(bt_entry->last_oid, bt_entry->last_len))); } /* Make sure variables are actually being requested in the packet. */ assert (nvars != 0); context->pkts_exch ++; DBPRT(1, (DBOUT "Sending %ssynchronous request %d...\n", SvTRUE(context->perl_cb) ? "a" : "", context->pkts_exch)); /* We handle the asynchronous and synchronous requests differently here. ** For async, we simply enqueue the packet with a callback to handle the ** returned response, then return. Note that this we call the bulkwalk ** callback, and hand it the walk_context, not the Perl callback. The ** snmp_async_send() function returns the reqid on success, 0 on failure. */ if (SvTRUE(context->perl_cb)) { reqid = snmp_async_send(ss, pdu, _bulkwalk_async_cb, (void *)context); DBPRT(2,(DBOUT "bulkwalk_send_pdu(): snmp_async_send => 0x%08X\n", reqid)); if (reqid == 0) { sv_setpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); goto err; } /* Make a note of the request we expect to be answered. */ context->exp_reqid = reqid; /* Callbacks take care of the rest. Let the caller know how many vars ** we sent in this request. Note that this is not a valid SNMP PDU, ** but that's because a response has not yet been received. */ return (netsnmp_pdu *)(intptr_t)reqid; } /* This code is for synchronous mode support. ** ** Send the PDU and block awaiting the response. Return the response ** packet back to the caller. Note that snmp_sess_read() frees the pdu. */ status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, *err_str_svp, *err_num_svp, *err_ind_svp); pdu = NULL; /* Check for a failed request. __send_sync_pdu() will set the appropriate ** values in the error string and number SV's. */ if (status != STAT_SUCCESS) { DBPRT(1,(DBOUT "__send_sync_pdu() -> %d\n",(int)status)); goto err; } DBPRT(1, (DBOUT "%d packets exchanged, response 0x%p\n", context->pkts_exch, response)); return response; err: if (pdu) snmp_free_pdu(pdu); return NULL;}/* Handle an incoming GETBULK response PDU. This function just pulls the** variables off of the PDU and builds up the arrays of returned values** that are stored in the context.**** Returns the number of variables found in this packet, or -1 on error.** Note that the caller is expected to free the pdu.*/static int_bulkwalk_recv_pdu(walk_context *context, netsnmp_pdu *pdu){ netsnmp_variable_list *vars; struct tree *tp; char type_str[MAX_TYPE_NAME_LEN]; char str_buf[STR_BUF_SIZE], *str_bufp = str_buf; size_t str_buf_len = sizeof(str_buf); size_t out_len = 0; int buf_over = 0; char *label; char *iid; bulktbl *expect = NULL; int old_numeric; int old_printfull; int old_format; int getlabel_flag; int type; int pix; int len; int i; AV *varbind; SV *rv; DBDCL(SV**sess_ptr_sv=hv_fetch((HV*)SvRV(context->sess_ref),"SessPtr",7,1);) SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1); SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1); SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1); int check = SvIV(*hv_fetch((HV*)SvRV(context->sess_ref), "NonIncreasing",13,1)); DBPRT(3, (DBOUT "bulkwalk: sess_ref = 0x%p, sess_ptr_sv = 0x%p\n", context->sess_ref, sess_ptr_sv)); /* Set up for numeric OID's, if necessary. Save the old values ** so that they can be restored when we finish -- these are ** library-wide globals, and have to be set/restored for each ** session. */ old_numeric = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS); old_printfull = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID); old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); if (context->getlabel_f & USE_NUMERIC_OIDS) { DBPRT(2,(DBOUT "Using numeric oid's\n")); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, 1); netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); } /* Parse through the list of variables returned, adding each return to ** the appropriate array (as a VarBind). Also keep track of which ** repeated OID we're expecting to see, and check if that tree walk has ** been completed (i.e. we've walked past the root of our request). If ** so, mark the request complete so that we don't send it again in any ** subsequent request packets. */ if (context->pkts_exch == 1) context->reqbase = context->req_oids; /* Request with non-repeaters */ else context->reqbase = context->repbase; /* Request only repeater vars */ /* Note the first variable we expect to see. Should be reqbase. */ expect = context->reqbase; for (vars = pdu->variables, pix = 0; vars != NULL; vars = vars->next_variable, pix ++) { /* If no outstanding requests remain, we're done. This works, but it ** causes the reported total variable count to be wrong (since the ** remaining vars on the last packet are not counted). In practice ** this is probably worth the win, but for debugging it's not. */ if (context->req_remain == 0) { DBPRT(2,(DBOUT "No outstanding requests remain. Terminating processing.\n")); while (vars) { pix ++; vars = vars->next_variable; } break; } /* Determine which OID we expect to see next. We assert that the OID's ** must be returned in the expected order. The first nreq_oids returns ** should match the req_oids array, after that, we must cycle through ** the repeaters in order. Non-repeaters are not included in later ** packets, so cannot have the "ignore" flag set. */ if (context->oid_saved < context->non_reps) { assert(context->pkts_exch == 1); expect = context->reqbase ++; assert(expect->norepeat); } else { /* Must be a repeater. Look for the first one that is not being ** ignored. Make sure we don't loop around to where we started. ** If we get here but everything is being ignored, there's a problem. ** ** Note that we *do* accept completed but not ignored OID's -- these ** are OID's for trees that have been completed sometime in this ** response, but must be looked at to maintain ordering. */ /* In previous version we started from 1st repeater any time when ** pix == 0. But if 1st repeater is ignored we can get wrong results, ** because it was not included in 2nd and later request. So we set ** expect to repbase-1 and then search for 1st non-ignored repeater. ** repbase-1 is nessessary because we're starting search in loop below ** from ++expect and it will be exactly repbase on 1st search pass. */ if (pix == 0) expect = context->repbase - 1; /* Find the repeater OID we expect to see. Ignore any ** OID's marked 'ignore' -- these have been completed ** and were not requested in this iteration. */ for (i = 0; i < context->repeaters; i++) { /* Loop around to first repeater if we hit the end. */ if (++ expect == &context->req_oids[context->nreq_oids]) expect = context->reqbase = context->repbase; /* Stop if this OID is not being ignored. */ if (!expect->ignore) break; } } DBPRT(2, (DBOUT "Var %03d request %s\n", pix, __snprint_oid(expect->req_oid, expect->req_len))); /* Did we receive an error condition for this variable? ** If it's a repeated variable, mark it as complete and ** fall through to the block below. */ if ((vars->type == SNMP_ENDOFMIBVIEW) || (vars->type == SNMP_NOSUCHOBJECT) || (vars->type == SNMP_NOSUCHINSTANCE)) { DBPRT(2,(DBOUT "error type %d\n", (int)vars->type)); /* ENDOFMIBVIEW should be okay for a repeater - just walked off the ** end of the tree. Mark the request as complete, and go on to the ** next one. */ if ((context->oid_saved >= context->non_reps) && (vars->type == SNMP_ENDOFMIBVIEW)) { expect->complete = 1; DBPRT(2, (DBOUT "Ran out of tree for oid %s\n", __snprint_oid(vars->name,vars->name_length))); context->req_remain --; /* Go on to the next variable. */ continue; } sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); sv_setiv(*err_ind_svp, pix); goto err; } /* If this is not the first packet, skip any duplicated OID values, if ** present. These should be the seed values copied from the last OID's ** of the previous packet. In practice we don't see this, but it is ** easy enough to do, and will avoid confusion for the caller from mis- ** behaving agents (badly misbehaving... ;^). */ if ((context->pkts_exch > 1) && (pix < context->repeaters)) { if (__oid_cmp(vars->name, vars->name_length, context->reqbase[pix].last_oid, context->reqbase[pix].last_len) <= 0) { if (check) { DBPRT(2, (DBOUT "Error: OID not increasing: %s\n", __snprint_oid(vars->name,vars->name_length))); sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_OID_NONINCREASING)); sv_setiv(*err_num_svp, SNMPERR_OID_NONINCREASING); sv_setiv(*err_ind_svp, pix); goto err; } DBPRT(2, (DBOUT "Ignoring repeat oid: %s\n", __snprint_oid(vars->name,vars->name_length))); continue; } } context->oid_total ++; /* Count each variable received. */ /* If this is a non-repeater, handle it. Otherwise, if it is a ** repeater, has the walk wandered off of the requested tree? If so, ** this request is complete, so mark it as such. Ignore any other ** variables in a completed request. In order to maintain the correct ** ordering of which variables we expect to see in this packet, we must ** not set the ignore flags immediately. It is done in bulkwalk_done(). ** XXX Can we use 'expect' instead of 'context->req_oids[pix]'? */ if (context->oid_saved < context->non_reps) { DBPRT(2, (DBOUT " expected var %s (nonrepeater %d/%d)\n", __snprint_oid(context->req_oids[pix].req_oid, context->req_oids[pix].req_len), pix, context->non_reps)); DBPRT(2, (DBOUT " received var %s\n", __snprint_oid(vars->name, vars->name_length))); /* This non-repeater has now been seen, so mark the sub-tree as ** completed. Note that this may not be the same oid as requested, ** since non-repeaters act like GETNEXT requests, not GET's. <sigh> */ context->req_oids[pix].complete = 1; context->req_remain --; } else { /* Must be a repeater variable. */ DBPRT(2, (DBOUT " received oid %s\n", __snprint_oid(vars->name, vars->name_length))); /* Are we already done with this tree? If so, just ignore this ** variable and move on to the next expected variable. */ if (expect->complete) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -