Go Down

Topic: Agentuino - A lightweight SNMP Agent (Read 22000 times) previous topic - next topic

LAVco

Feb 02, 2011, 06:09 pm Last Edit: Feb 02, 2011, 06:11 pm by LAVco Reason: 1
Not certain how we are supposed to port old subject and posted chains.  So a new one has been created in continuation of the Agentuino Library.

The original community interactions can be found here; http://arduino.cc/forum/index.php/topic,38103.msg282272.html#msg282272

Project source code is hosted on Google and can be located here; http://code.google.com/p/agentuino/

Welcome to the new forum all.
Common sense is not so common...

LAVco

For those of you have been using the library I found bug and offering a correction that will prevent the MCU from hanging with an unknown request.

As per included sample sketch with the library, please see modifications, as per included code:
Code: [Select]
void pduReceived()
{
  SNMP_PDU pdu;
  //
  #ifdef DEBUG
    Serial << F("UDP Packet Received Start..") << F(" RAM:") << freeMemory() << endl;
  #endif
  //
  api_status = Agentuino.requestPdu(&pdu);
  //
  if ( pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GET_NEXT || pdu.type == SNMP_PDU_SET
    && pdu.error == SNMP_ERR_NO_ERROR && api_status == SNMP_API_STAT_SUCCESS ) {
    //
    pdu.OID.toString(oid);
    //
    Serial << "OID: " << oid << endl;
    //
    if ( strcmp_P(oid, sysDescr ) == 0 ) {
// handle sysDescr (set/get) requests
if ( pdu.type == SNMP_PDU_SET ) {
  // response packet from set-request - object is read-only
  pdu.VALUE.encode(SNMP_SYNTAX_NULL);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = SNMP_ERR_READ_ONLY;
} else {
  // response packet from get-request - locDescr
  status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locDescr);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
}
//
#ifdef DEBUG
  Serial << F("sysDescr...") << locDescr << F(" ") << pdu.VALUE.size << endl;
#endif
    } else if ( strcmp_P(oid, sysUpTime ) == 0 ) {
// handle sysName (set/get) requests
if ( pdu.type == SNMP_PDU_SET ) {
  // response packet from set-request - object is read-only
  pdu.VALUE.encode(SNMP_SYNTAX_NULL);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = SNMP_ERR_READ_ONLY;
} else {
  // response packet from get-request - locUpTime
  status = pdu.VALUE.encode(SNMP_SYNTAX_TIME_TICKS, locUpTime);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
}
//
#ifdef DEBUG
  Serial << F("sysUpTime...") << locUpTime << F(" ") << pdu.VALUE.size << endl;
#endif
    } else if ( strcmp_P(oid, sysName ) == 0 ) {
// handle sysName (set/get) requests
if ( pdu.type == SNMP_PDU_SET ) {
  // response packet from set-request - object is read/write
  status = pdu.VALUE.decode(locName, strlen(locName));
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
} else {
  // response packet from get-request - locName
  status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locName);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
}
//
#ifdef DEBUG
  Serial << F("sysName...") << locName << F(" ") << pdu.VALUE.size << endl;
#endif
    } else if ( strcmp_P(oid, sysContact ) == 0 ) {
// handle sysContact (set/get) requests
if ( pdu.type == SNMP_PDU_SET ) {
  // response packet from set-request - object is read/write
  status = pdu.VALUE.decode(locContact, strlen(locContact));
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
} else {
  // response packet from get-request - locContact
  status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locContact);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
}
//
#ifdef DEBUG
  Serial << F("sysContact...") << locContact << F(" ") << pdu.VALUE.size << endl;
#endif
    } else if ( strcmp_P(oid, sysLocation ) == 0 ) {
// handle sysLocation (set/get) requests
if ( pdu.type == SNMP_PDU_SET ) {
  // response packet from set-request - object is read/write
  status = pdu.VALUE.decode(locLocation, strlen(locLocation));
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
} else {
  // response packet from get-request - locLocation
  status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locLocation);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
}
//
#ifdef DEBUG
  Serial << F("sysLocation...") << locLocation << F(" ") << pdu.VALUE.size << endl;
#endif
    } else if ( strcmp_P(oid, sysServices) == 0 ) {
// handle sysServices (set/get) requests
if ( pdu.type == SNMP_PDU_SET ) {
  // response packet from set-request - object is read-only
  pdu.VALUE.encode(SNMP_SYNTAX_NULL);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = SNMP_ERR_READ_ONLY;
} else {
  // response packet from get-request - locServices
  status = pdu.VALUE.encode(SNMP_SYNTAX_INT, locServices);
  pdu.type = SNMP_PDU_RESPONSE;
  pdu.error = status;
}
//
#ifdef DEBUG
  Serial << F("locServices...") << locServices << F(" ") << pdu.VALUE.size << endl;
#endif
    } else {
// oid does not exist
//
// response packet - object not found
pdu.VALUE.encode(SNMP_SYNTAX_NULL);
pdu.type = SNMP_PDU_RESPONSE;
pdu.error = SNMP_ERR_NO_SUCH_NAME;
    }
    //
    Agentuino.responsePdu(&pdu);
  }
  //
  Agentuino.freePdu(&pdu);
  //
  Serial << "UDP Packet Received End.." << " RAM:" << freeMemory() << endl;
}


By adding "pdu.VALUE.encode(SNMP_SYNTAX_NULL);" it will send a response to the Manager either way and prevent the MCU from hanging.

More changes are planned and required in order to support Get-Next functionality, so stay tuned for improvements.  Again, developers are welcome to contribute, please contact me if your interested.
Common sense is not so common...

yesyes

OK, that makes sense in case a request comes in with an unknown OID.

However, my problem was that the request was apparently not an SNMP request at all. So the following if clause from your sample was not true:

Code: [Select]
// check if valid packet
  if ( (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GET_NEXT || pdu.type == SNMP_PDU_SET)
        && pdu.error == SNMP_ERR_NO_ERROR && api_status == SNMP_API_STAT_SUCCESS )
    {
    //


I worked around it by adding an else clause to that if and send an error response back anyway.

Code: [Select]
 
else if ( strcmp_P(oid, DewPoint ) == 0 ) // DewPoint request
    {
      // handle sysName (set/get) requests
      if ( pdu.type == SNMP_PDU_SET )
      {
        // response packet from set-request - object is read-only
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = SNMP_ERR_READ_ONLY;
      }
      else
      {
        // response packet from get-request - DewPoint
        status = pdu.VALUE.encode(SNMP_SYNTAX_INT32, v_DewPoint);
        pdu.type = SNMP_PDU_RESPONSE;
        pdu.error = status;
      }
      Serial << "v_DewPoint..." << v_DewPoint << " " << pdu.VALUE.size << endl;
    }


// add more custom OID blocks here


   
    else
    {
      // oid does not exist
      //
      // response packet - object not found
      pdu.VALUE.encode(SNMP_SYNTAX_NULL);
      pdu.type = SNMP_PDU_RESPONSE;
      pdu.error = SNMP_ERR_NO_SUCH_NAME;
      Serial << "Unknown OID" << endl;
    }
   
    //
    Serial << "Sending response..." << endl;
   
    Agentuino.responsePdu(&pdu);
    count++;


// close bracket for the 'if' in the first code block above
  }
 

  else
  // packet not valid, send GENERAL_ERROR response. Required, otherwise the invalid packet
  // will get stuck in the buffer and processed over and over again
 
  {
    Serial << "Unknown Packet!!" << endl;
    Serial << "PDU Type: " << pdu.type << " PDU Error: " << pdu.error << " API status: "<< api_status << endl;
    pdu.type = SNMP_PDU_RESPONSE;
    pdu.error = SNMP_ERR_GEN_ERROR;
    Agentuino.responsePdu(&pdu);
    Serial << "Sent 'GENERAL_ERROR' response" << endl;
  }

  //
  Serial << "freeing PDU.." << " RAM:" << freeMemory() << endl;
  Agentuino.freePdu(&pdu);
  //
  Serial << "UDP Packet Received End.." << " RAM:" << freeMemory() << endl;

} // end pduReceived()
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

LAVco

Common sense is not so common...

yesyes

I have to add that I have no idea just how wrong it is to reply this way. But it works for me as long as no new SNMP request comes in before the previous one has been replied to...
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

LAVco

#5
Feb 03, 2011, 12:47 am Last Edit: Feb 03, 2011, 12:50 am by LAVco Reason: 1
yesyes the short answer is no.  

We should probably clear the incoming buffer and send nothing back if it isn't a valid SNMP packet.  The problem with clearing the buffer is if another UDP protocol library is used in conjunction with the SNMP library.  I'll need to look into better handling of invalid packets but at least the "quick fix" you suggested seems to work.

Our alpha release works fairly well considering it's footprint size (Flash/RAM) and the amount of time put into it based on the testing I've done to date.  I really want to get the get-next requests working and the more I dive into implementing this functionality, more will change with the library.
Common sense is not so common...

techadmin

No posts to this in a while?

I'd like to add the capability to set the community strings via the Ethernet shield web forms on these active posts:

http://arduino.cc/forum/index.php/topic,54324.0.html

http://arduino.cc/forum/index.php/topic,55044.0.html

LAVco

Take a look at the Agentuino constructors as there is one that allows you to set the community names.  This would allow you to declare public variables within your sketch that could be updated by a web form.  You may want to store them in EEPROM as well or any new settings will be lost on reboot.

You may want to look at a Webduino library; multi web page/form implementation is easier per say.

Eric
Common sense is not so common...

Steve1337

Why I'm getting errors from the example?

AgentuinoClass agent = Agentuino();

SNMP_SESSION session;

Both are not okay...

yesyes

Steve, which example are you referring to? I don't see these lines in the example I have (Agent.pde)
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

Steve1337

Code: [Select]
#include <Agentuino.h>

#include <Agentuino.h>

/**
* Agentuino SNMP Agent Library Prototyping...
*
* Copyright 2010 Eric C. Gionet <lavco_eg@hotmail.com>
*
*/
#include <Streaming.h>         // Include the Streaming library
#include <Ethernet.h>          // Include the Ethernet library
#include <MemoryFree.h>
#include <Agentuino.h>
#include <avr/pgmspace.h>
//
static byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
static byte ip[] = { 192, 168, 2, 64 };
static byte gateway[] = { 192, 168, 1, 190 };
static byte subnet[] = { 255, 255, 255, 0 };
//
//
// RFC1213-MIB OIDs
static char sysDescr[] PROGMEM      = "1.3.6.1.2.1.1.1.0";  // read-only  DisplayString
static char sysObjectID[] PROGMEM   = "1.3.6.1.2.1.1.2.0";  // read-only  (ObjectIdentifier)
static char sysUpTime[] PROGMEM     = "1.3.6.1.2.1.1.3.0";  // read-only  (TimeTicks)
static char sysContact[] PROGMEM    = "1.3.6.1.2.1.1.4.0";  // read-write (DisplayString)
static char sysName[] PROGMEM       = "1.3.6.1.2.1.1.5.0";  // read-write (DisplayString)
static char sysLocation[] PROGMEM   = "1.3.6.1.2.1.1.6.0";  // read-write (DisplayString)
static char sysServices[] PROGMEM   = "1.3.6.1.2.1.1.7.0";  // read-only  (Integer)
//
//
/* RFC1213 local values */
static char locDescr[] PROGMEM      = { "Agentuino, a light-weight SNMP Agent." };  // read-only (static)
static char locObjectID[] PROGMEM   = "1.3.6.1.3.2009.0";                       // read-only (static)
static long locUpTime               = 0;                                        // RTC is needed for this unless the NTP Time library is used
static char locContact[20]          = "Eric Gionet";
static char locName[20]             = "Agentuino";
static char locLocation[20]         = "Nova Scotia, CA";
static short locServices PROGMEM    = 7;                                        // read-only (static)

AgentuinoClass agent = Agentuino();

SNMP_SESSION session;


void pduReceived()
{
  char oidString[SNMP_MAX_OID_LEN];
  SNMP_PDU pdu;
  //
  Serial << "UDP Packet Received Start.." << " RAM:" << freeMemory() << endl;
  //
  agent.requestPdu(&pdu);
  //
  pdu.OID.toString(oidString);
  //
  Serial << "OID: " << oidString << endl;
  //
  if ( pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GET_NEXT ) {
    //
    if ( strcmp_P(oidString, sysName) == 0 ) {
      //
      // response packet - locName
      pdu.type = SNMP_PDU_RESPONSE;
      pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locName);
      //
      Serial << "sysName..." << locName << " " << pdu.VALUE.size << endl;
      //
      agent.responsePdu(&pdu);
    } else {
      // oid does not exist
      //
      // response packet - object not found
    }
  }
  //
  agent.freePdu(&pdu);
}

void setup()
{
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  //
  session.getCommName = "public";
  session.setCommName = "private";
  session.port = 161;
  //
  agent.initSession(&session);
  agent.onPduReceive(pduReceived);
  //
  delay(10);
  //
  Serial.println("Initalized...");
}

void loop()
{
  agent.listen();
}

LAVco

Please take a look at the library's class constructor to see how to use the session struct.  Based on your posted code your missing some parameters.

Code: [Select]
// set community name set/get sizes

session->setSize = strlen(session->setCommName);

session->getSize = strlen(session->getCommName);

//

// validate get/set community name sizes

if ( session->setSize > SNMP_MAX_NAME_LEN || session->getSize > SNMP_MAX_NAME_LEN ) {

return SNMP_API_STAT_NAME_TOO_BIG;

}

//

// set session property

_session = session;

//

// validate session port number

if ( session->port == NULL || session->port == 0 ) session->port = SNMP_DEFAULT_PORT;

//

// init UDP socket

_socket.initUDP(session->port);

//

return SNMP_API_STAT_SUCCESS;
Common sense is not so common...

yesyes

I'm afraid I need a little more help with the SNMP library and possibly the underlying Ethernet library...

It's been a while but I've now come back to troubleshooting / debugging my weather station sketch. The problem is that occasionally a received packet gets stuck and is being processes over and over again. I'm not sure what that packet is but it's definitely not an SNMP packet.

What I have done is I added lots and lost of serial port output to the sketch and I keep a machine running to log that output to a file. I have attached both the sketch and the part of the log file that shows the sketch running into the problem.

The problem starts at line 2170 in the log.txt. What is happening is that (for some reason) the Arduino receives an unknown packet from the same IP that usually sends the SNMP GET requests. Since it is not a valid SNMP packet, the following if statement is not true (from line 200 in the sketch):

Code: [Select]
  // check if valid packet
  if ( (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GET_NEXT || pdu.type == SNMP_PDU_SET)
        && pdu.error == SNMP_ERR_NO_ERROR && api_status == SNMP_API_STAT_SUCCESS )
    {


So the sketch jumps to the following else statement (line 505)

Code: [Select]
  else
  // packet not valid, send GENERAL_ERROR response. Required, otherwise the invalid packet
  // will get stuck in the buffer and processed over and over again
 
  {
    Serial << "Unknown Packet!!" << endl;
    Serial << "PDU Type: " << pdu.type << " PDU Error: " << pdu.error << " API status: "<< api_status << endl;
    Serial << "from IP: " << Agentuino.g_dstIP(0) << "." << Agentuino.g_dstIP(1) << "." << Agentuino.g_dstIP(2) << "." << Agentuino.g_dstIP(3) << endl;   
    pdu.type = SNMP_PDU_RESPONSE;
    pdu.error = SNMP_ERR_GEN_ERROR;
//    Agentuino.responsePdu(&pdu);
    Serial << "Sent 'GENERAL_ERROR' response" << endl;
  }


So far so good. However, the next time and all subsequent times I call Agentuino.listen() that same packet is still in the buffer. No further received packets are being processed.

The question is, why does this packet stay in the buffer? Is there a way to flush the buffer from my sketch (or from Agentuino) if an unknown packet is being received? I've been through all the components of the Ethernet library (Udp, Ethernet, W5100, socket) and also the Agentuino library to figure out where and how the packet buffer is released but I couldn't find it.

BTW, the method Agentuino.g_dstIP() is one I added to the Agentuino library in order to get the IP address where the packet came from in an attempt to determine what the nature of the offending packets is.
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

yesyes

Hmmm, nobody? I was hoping LAVco would read this... ;-)
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

bborncr

#14
Dec 09, 2011, 01:41 pm Last Edit: Dec 09, 2011, 02:45 pm by bborncr Reason: 1
Yesyes,

I been using your Weather Station code as a basis to create a simple test using a TMP36 temperature sensor connected to analog0.  For the most part everything works well.  However, there are two problems:
1. The device does not respond if I connect the power (wall wart or usb).  Only after pressing reset does everything start working.
2. After a few hours the device hangs up and the SNMP stops responding.  I can still ping the device but no SNMP until I do a reset.

By comparing the first Weather Station code that I found initially in a previous post and the code in the post above I saw that you added a watchdog timer to the setup routine.  I don't know what a watchdog timer is but I added it and will be testing for stability.  Any tips would be greatly appreciated.

Also, I noticed that this line is commented out //    Agentuino.responsePdu(&pdu);


Have had any luck troubleshooting your issues?

The Agentuino library seems to be such a useful tool if it could be polished just a little more.  I wonder if anyone would work on it for a bounty?

Go Up