Agentuino - A lightweight SNMP Agent

A project was started (http://code.google.com/p/agentuino/).

Code will be uploaded soon (need to get SVN working on my linux box). Code licensing will be under GPLv2.

Please let me know if you would like to assist.

Here is a quick snippet on the implementation concept.

/*
Copyright 2010 Eric C. Gionet <lavco_eg@hotmail.com>
// ******************************************************
// implementation concept
// ******************************************************
static byte ip[] = { 192, 168, 2, 64 };
static byte gateway[] = { 192, 168, 2, 1 };
static byte subnet[] = { 255, 255, 255, 0 };

// SNMP Agent - basic credentials
char getCommName[] PROGMEM   = "public";
char setCommName[] PROGMEM   = "private";
//
// RFC1213-MIB OIDs
char sysDescr[] PROGMEM        = "1.3.6.1.2.1.1.1.0";  // read-only  (DisplayString)
char sysObjectID[] PROGMEM   = "1.3.6.1.2.1.1.2.0";  // read-only  (ObjectIdentifier)
char sysUpTime[] PROGMEM     = "1.3.6.1.2.1.1.3.0";  // read-only  (TimeTicks)
char sysContact[] PROGMEM      = "1.3.6.1.2.1.1.4.0";  // read-write (DisplayString)
char sysName[] PROGMEM        = "1.3.6.1.2.1.1.5.0";  // read-write (DisplayString)
char sysLocation[] PROGMEM     = "1.3.6.1.2.1.1.6.0";  // read-write (DisplayString)
char sysServices[] PROGMEM    = "1.3.6.1.2.1.1.7.0";  // read-only  (Integer)
//
//
/* RFC1213 local values */
char locDescr[] PROGMEM      = "Agentuino, a light-weight SNMP Agent.";  // read-only (static)
char locObjectID[] PROGMEM   = "1.3.6.1.3.2009.0";                       // read-only (static)
long locUpTime                       = 0;                                        // RTC is needed for this unless the NTP Time library is used
char locContact[20]          = "Eric Gionet";
char locName[20]             = "Agentuino";
char locLocation[20]         = "Nova Scotia, CA";
short locServices PROGMEM    = 7;                                        // read-only (static)

Agentiuno agent = Agentuino();

SNMP_SESSION session;

pduReceived() {
      SNMP_PDU request;
      SNMP_PDU response;
      //
      agent.requestPdu(request);
      //
      // process request
      //
      agent.responsePdu(response);
      //
      agent.freePdu(request);
      agent.freePdu(response);
}

setup() {
      Ethernet.begin(mac, ip);
      //
      session.setCommName = getCommName;
      session.getCommName = setCommName;
      session.ip = ip;
      session.port = 161;
      //
      agent.initSession(session);
      agent.onPduReceive(pduReceived);
}

loop() {
      agent.listen();
}

Cheers,

Eric

Count me in.

I would be willing to help also.

Hello,

The library is still in alpha stages and very close to getting a response packet out. The initial development sketch was ported to a library. Library was sent to John D. and hopefully he will have to time to upload it while I am away.

For those who are interested here is the sketch sample on usage implementation to date.

/**
* 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> 
//
static byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
static byte ip[] = { 192, 168, 2, 64 };
static byte gateway[] = { 192, 168, 2, 1 };
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)

Agentuino 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();
}

Cheers,

Eric

It works! Figured out the response issue I was having (issue resided with sequence lengths).

sysName get-request responds with "Agentuino". I've tested it with iReasoning in Windows and NET-SNMP in Linux using SNMP-V1.

Need to work on a memory issue now as it stops responding after 2-3 concurrent requests.

Cheers,

Eric G.

Hello,

Code was uploaded by John D. The initial code is synchronous and there is still a memory issue.

Feedback on the potential memory problem would be great.

UPDATE
Figured out the problem, silly rabbit, I wasn't treating it like a server (duh) and releasing client connections/sockets once their task was completed. Working on an update to resolve the problem.

Cheers,

Eric

I've just committed Eric's latest changes, and his example sketch, to SVN. The directory structure now mirrors that of an Arduino library and example. I've also written some notes on getting the code to compile and run, and committed those to SVN.

BTW, GPLv2 isn't usually considered well-matched to libraries, especially for embedded systems. You may want to make sure that it's not doing more than you want it to (ie; it makes anything that uses your library also need to be open source GPL2.) LGPL2 is the usual alternative...

Thanks westfw. I'll take look.

I looked at the code, but I use the eth libs for the ENC28J60. Alas they never wrote a 'ASocket' layer. So that will need some work, when I get some more time.

Also thinking the base Agentuino is great, but there should be a generalized interface, as the users will want to add their own data OIDs and code for their purposes.

ASocket was chosen for a few reasons so the older Ethernet librarry is not supported and wont be.

Also thinking the base Agentuino is great, but there should be a generalized interface, as the users will want to add their own data OIDs and code for their purposes

It's as generalized as it can get. The code provided are basic examples and by virtue sysName etc are base system objects and these can be removed or new objects can be added. I would suggest reading up SNMP agents and managers or clarify your comment.

Implementing an SNMP agent is not recommended for beginners.

Wow - impressive!

I love being to query the system with a GET at any time. However, sometimes having the unit actually report in on its own is useful...

Any chance support for sending an SNMP trap will be added?

Thanks and keep up the great work!

Quick update...

Figured out the memory issue and it now works with multiple get-requests. The issue found is somewhat concerning but perhaps someone may be able to shed some light on the subject of using malloc. When malloc was used to dynamically size the packet buffer it would hang after 3-4 requests. When the packet buffer is set to a static size it works fine.

The latest IDE (0019) has a udp library added by default based on bitwiseudp created some time ago, so this may be considered as well, based on the fact that this is what I am testing with now.

An update will be uploaded in a few days. In the meantime the code.google page has been updated for those of you who are interested.

Any chance support for sending an SNMP trap will be added?

Charles, yes I agree that it would be some slick to have traps. We need to finish up common ASN.1 type encoding first, testing them, work on set-requests, and then probe into traps.

Thanks

Eric

Another update...

A Private Enterprise Number (PEN) was requested from iana http://pen.iana.org for the Arduino project. Basically this allows users to implement their own objects under the following node; .iso.org.dod.internet.private.enterprises.arduino (.1.3.6.1.4.1.?????), ????? to be assigned once the application is processed and accepted.

example;

.iso.org.dod.internet.private.enterprises.arduino.wxAirTemp (oid: .1.3.6.1.4.1.?????.1.0)

Weather Object (abbreviation: wx)
wxAirTemp MIB definition; 
   - SYNTAX: INTEGER
   - MAX: 1000
   - MIN: -1000
   - ERROR: 1001
   - DESCRIPTION: air temperature in 1/10 degree Celsius

A sample MIB file will be created once the PEN value is assigned.

Regards,

Eric

We have a working library with example uploaded to the project page.

  1. Null (WIP)
  2. Boolean (WIP)
  3. Bits (WIP)
  4. Octet-String
  5. Object-Identifier (WIP)
  6. Integer and Integer32
  7. Counter and Counter64
  8. Gauge
  9. Time-Ticks
  10. IP-Address (WIP)
  11. Opaque (WIP)
  12. Network-Service-Access-Point (NSAP) Address (WIP)

WIP = Work In Progress.

Cheers,

Eric

Library updates:

  1. Null
  2. Boolean
  3. Bits (WIP)
  4. Octet-String
  5. Object-Identifier (WIP)
  6. Integer and Integer32
  7. Counter and Counter64
  8. Gauge
  9. Time-Ticks
  10. IP-Address
  11. Opaque
  12. Network-Service-Access-Point (NSAP) Address

WIP = Work In Progress.

Testers are needed.

Update..

There is a bug with OID decoding/encoding which is being worked on. Any OID value greater than 127 will not be handled properly until the fix is ready.

A Private Enterprise Number (PEN) was issued from iana http://pen.iana.org for the Arduino project.

Text OID...

.iso.org.dod.internet.private.enterprises.arduino

Numeric OID..

.1.3.6.1.4.1.36582

Again once the OID bug is fixed a MIB example will be supplied.

Another Update..
OID issue is fixed and an updated library will be uploaded shortly.

Cheers,

Eric

Hi

Is this library still under development, or is it in a working state as far as the developers are concerned and it'll just remain as is?

Thanks!

Jump in and help.

I don't have the wiznet eth module, so I am stuck until the pic eth enc28j60 library is properly ported....meaning I fix it myself, and thats a whole 'nother project in itself.

If anyone wants to work it, what needs to be done is the arduino ethernet lib needs to have a interface between the wiznet driver and the eth lib on top. As it is now its hardcoded for wiznet5100. Then the pic driver can be added. I need to reread and fully understand the pic eth chip to port the driver.

Frankly I am amazed that the guys got any kind of snmp lib up as soon as they did. I thank them for that.

I have found and fixed a bug in the currently available SNMP library but not sure what to do with the fix. Is anyone actively developing this library?

The issue occurs when an SNMP set is sent with a data length less than 'sizeof' the type being sent.

The iReasoning MIB Browser seems only to send the amount of data that it must send, i.e. for an integer snmpset with a value of 12, it will send 1 byte of data in the packet with the length of data set to 1.

Once the packet has been received, the main code will call a decode function to extract the data. As the code is expecting an integer, the decode(int32_t) function is used. The SNMP library doesn't check the data length and thus expects 4 bytes of data from the snmpset rather than the 1 it actually receives.

As there isn't a decode function for a 1 byte value, this would suggest that the library design is to deal with "short" data lengths using the decode for the expected size (int32_t/uint32_t) rather than expecting the calling code to check the size and to call the 'correct' decode (int8_t/uint8_t, which doesn't exist).

This means that an integer with data size of 1 could be extracted using any decode that takes a large enough integer parameter and the library sizes the data to match the incoming variable size.

As the code stands, decode(uint32_t *value) puts 0x12000000 into value as the low byte is data[0] instead of data[3] where the library expects it.

After the fix...

A packet contains data of 0x12 with a length of 1...

decode(uint32_t *value) puts 0x12 into value
decode(uint16_t *value) puts 0x12 into value
decode(uint8_t *value) puts 0x12 into value (this function variant doesn't actually exist, but could be added)

This present in all SNMP_VALUE decode functions other than char* and bool.