Re: snmp_async_send blocks during snmpv3 probe

2012-03-19 Thread Robert Story
On Tue, 13 Mar 2012 07:22:16 +0100 Vincent wrote:
VB> OoO En  cette matinée pluvieuse du  samedi 03 mars 2012,  vers 10:53, je
VB> disais:
VB> 
VB> > Hi!
VB> > I am hit by this annoying bug:
VB> >  
https://sourceforge.net/tracker/?func=detail&aid=3446148&group_id=12694&atid=112694
VB> 
VB> > I am  trying to fix  this. The situation  seems quite simple. Here  is a
VB> > pseudo-trace:
VB> [...]
VB> This  works fine  but  to propose  a  patch for  NetSNMP,  I have  three
VB> questions:
VB> [...]
VB>  2. How to handle the "send the original PDU as soon as you hit the main
VB> loop again" could be done properly with NetSNMP?

Check out snmp_alarm_register...

VB>  3. I am  allocating some  "magic" structure to  keep the  original PDU,
VB> callback  and callback  argument.  Is  there something  simpler with
VB> NetSNMP?
VB> 
VB> The bug report  from Robert Story tells that the bug  should be fixed by
VB> queueing the  original PDU. Maybe there  is a simpler way  of doing this
VB> than what I am currently doing?

Nope, I think you are on the right track..

--
This SF email is sponsosred by:
Try Windows Azure free for 90 days Click Here 
http://p.sf.net/sfu/sfd2d-msazure
___
Net-snmp-coders mailing list
Net-snmp-coders@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/net-snmp-coders


Re: snmp_async_send blocks during snmpv3 probe

2012-03-12 Thread Vincent Bernat
OoO En  cette matinée pluvieuse du  samedi 03 mars 2012,  vers 10:53, je
disais:

> Hi!
> I am hit by this annoying bug:
>  
> https://sourceforge.net/tracker/?func=detail&aid=3446148&group_id=12694&atid=112694

> I am  trying to fix  this. The situation  seems quite simple. Here  is a
> pseudo-trace:

>  - snmp_async_send()
>  - snmp_sess_async_send()
>  - _sess_async_send()
>  - snmpv3_engineID_probe()
>  - sptr->probe_engineid() and sptr->post_probe_engineid()

> Those two callbacks can be:
>  - snmpv3_probe_contextEngineID_rfc5343()
>  - usm_discover_engineid()
>  - usm_create_user_from_session_hook()

> The last one seems OK.

> The two first ones  call snmp_sess_synch_response(). The problem is that
> if  we want  to  fix  this, we  need  to change  the  semantics for  the
> callbacks. Is  it OK  to change the  first callback  to an async  one? I
> don't know if the semantics of the second one needs any adaptation.

I have tried  to fix this but  this is harder than I  thought.  There is
also a path from snmp_open() that could lead to snmpv3_engineID_probe().
This  path  is  disabled  by  default.  The  other  difficulty  is  when
receiving  the  Report message.  The  original  PDU  cannot be  sent  in
response  to  this  message  because  NetSNMP  did  not  yet  write  the
appropriate engineBoots/engineUptime to the  session. This is done after
the  callback   handling  the  Report  message   has  been  successfully
triggered.

I have  fixed my  problem right  into my application  because I  was too
incomfortable with NetSNMP.  I only corrected the snmp_sess_async_send()
path and only with USM.

I have written a wrapper around snmp_async_send():

  /* Copy the version from the session if needed. */
  if (pdu->version == SNMP_DEFAULT_VERSION)
pdu->version = session->version;

  /* Do we need probing? */
  if (pdu->version != SNMP_VERSION_3 ||
  session->securityEngineIDLen != 0 ||
  (0 != (session->flags & SNMP_FLAGS_DONT_PROBE))) {
/* No, we can just call snmp_async_send() */
int ret = snmp_async_send(session, pdu, cb, arg);
if (!ret) log_snmp_error(seat);
return ret;
  }

  /* Allocate some "magic" structure to remember PDU, callback and argument*/
  struct magic *magic = calloc(1, sizeof(struct magic));
  if (!magic)
return 0;
  magic->pdu  = pdu;
  magic->cb   = cb;
  magic->arg = arg;
  magic->session = session;

  netsnmp_pdu *probe = NULL;
  if (snmpv3_build_probe_pdu(&probe) != 0) {
free(magic);
return 0;
  }

  /* Send it. */
  session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
  if (!snmp_async_send(session, probe,
   probe_engine_step1_cb, magic)) {
snmp_free_pdu(probe);
free(magic);
session->flags &= ~SNMP_FLAGS_DONT_PROBE;
return 0;
  }

  return 1;

The snmpv3_build_probe_pdu is stolen  from NetSNMP (either in snmp_api.c
or in snmpusm.c).

Then, I have this function to handle the received probe answer:

static int
probe_engine_step1_cb(int operation,
  struct snmp_session *sp,
  int reqid,
  struct snmp_pdu *pdu,
  void *arg) {
  struct magic *magic = arg;
  struct timeval tv = {0, 0};
  int ret;

  /* Did we receive the appropriate Report message? */
  if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE &&
  pdu && pdu->command == SNMP_MSG_REPORT) {
/* We need to execute the remaining operations outside of this
   callback because the appropriate information are not put in the
   session, yet. I am using libevent! */
magic->next = evtimer_new(magic->cfg->base, probe_engine_step2_cb, magic);
if (!magic->next) goto probe_failed;
if (evtimer_add(magic->next, &tv) == -1) goto probe_failed;
return 1;
  }

 probe_failed:
  sp->flags &= ~SNMP_FLAGS_DONT_PROBE;
  ret = magic->cb(NETSNMP_CALLBACK_OP_SEND_FAILED,
  sp, reqid, pdu, magic->arg);
  if (magic->next) event_free(magic->next);
  free(magic);
  return ret;
}

To resend the  PDU outside the callback, I am using  my libevent loop to
schedule the original  PDU to be sent immediatly. I have  no idea on how
to   do   this   withNetSNMP.   This   is   necessary   because   in
_sess_process_packet(), the code is like this:

  if (callback == NULL
  || callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE, sp,
  pdu->reqid, pdu, magic) == 1) {
if (pdu->command == SNMP_MSG_REPORT) {
  if (sp->s_snmp_errno == SNMPERR_NOT_IN_TIME_WINDOW ||
  snmpv3_get_report_type(pdu) ==
  SNMPERR_NOT_IN_TIME_WINDOW) {
/*
 * trigger immediate retry on recoverable Reports 
 * * (notInTimeWindow), incr_retries == TRUE to prevent
 * * inifinite resend  
 */
if (rp->retries <= sp->retries) {
  snmp_resend_request(slp, rp, TRUE);
  break;
}
  } else {