SNMP Agent http://mbed.org/users/okini3939/notebook/agentbed-library/

Dependents:   WeatherPlatform_20110408 WeatherPlatform WeatherStation

Agentbed.cpp

Committer:
okini3939
Date:
2011-01-28
Revision:
2:9e4369522e03
Parent:
0:bb21da73024a

File content as of revision 2:9e4369522e03:

/*
  Agentbed library
  Modified for mbed, 2010 Suga.
 
 
  Agentuino.cpp - An Arduino library for a lightweight SNMP Agent.
  Copyright (C) 2010 Eric C. Gionet <lavco_eg@hotmail.com>
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

//
// sketch_aug23a
//

#include "Agentbed.h"

SNMP_API_STAT_CODES AgentbedClass::begin(EthernetNetIf *eth)
{
    // set community names
    _getCommName = "public";
    _setCommName = "private";
    //
    // set community name set/get sizes
    _setSize = strlen(_setCommName);
    _getSize = strlen(_getCommName);
    //
    // init UDP socket
    udpsock = new UDPSocket;
    udpsock->setOnEvent(this, &AgentbedClass::listen);
    udpsock->bind(Host(eth->getIp(), SNMP_DEFAULT_PORT));
    //
    return SNMP_API_STAT_SUCCESS;
}

SNMP_API_STAT_CODES AgentbedClass::begin(char *getCommName, char *setCommName, uint16_t port, EthernetNetIf *eth)
{
    // set community name set/get sizes
    _setSize = strlen(setCommName);
    _getSize = strlen(getCommName);
    //
    // validate get/set community name sizes
    if ( _setSize > SNMP_MAX_NAME_LEN + 1 || _getSize > SNMP_MAX_NAME_LEN + 1 ) {
        return SNMP_API_STAT_NAME_TOO_BIG;
    }
    //
    // set community names
    _getCommName = getCommName;
    _setCommName = setCommName;
    //
    // validate session port number
    if ( port == NULL || port == 0 ) port = SNMP_DEFAULT_PORT;
    //
    // init UDP socket
    udpsock = new UDPSocket;
    udpsock->setOnEvent(this, &AgentbedClass::listen);
    udpsock->bind(Host(eth->getIp(), port));
    //
    return SNMP_API_STAT_SUCCESS;
}

void AgentbedClass::listen (UDPSocketEvent e)
{
    // if bytes available in receive buffer
    // and pointer to a function (delegate function)
    // isn't null, trigger the function
    if (e == UDPSOCKET_READABLE && _callback != NULL ) {
        (*_callback)();
    }
}


SNMP_API_STAT_CODES AgentbedClass::requestPdu(SNMP_PDU *pdu)
{
    char *community;
    // sequence length
    byte __attribute__((__unused__)) seqLen;
    // version
    byte verLen, verEnd;
    // community string
    byte comLen, comEnd;
    // pdu
    byte __attribute__((__unused__)) pduTyp, pduLen;
    byte ridLen, ridEnd;
    byte errLen, errEnd;
    byte eriLen, eriEnd;
    byte __attribute__((__unused__)) vblTyp, vblLen;
    byte __attribute__((__unused__)) vbiTyp, vbiLen;
    byte obiLen, obiEnd;
    byte __attribute__((__unused__)) valTyp, valLen, valEnd;
    byte i;
    Host dest;
    //
    // set packet packet size (skip UDP header)
//    _packetSize = Udp.available()-8;
    //
    memset(_packet, 0, SNMP_MAX_PACKET_LEN);
    //
    // get UDP packet
    _packetSize = udpsock->recvfrom((char*)_packet, SNMP_MAX_PACKET_LEN, &_dst);
    //
    // validate packet
    if ( _packetSize != 0 && _packetSize > SNMP_MAX_PACKET_LEN ) {
        //
        //SNMP_FREE(_packet);

        return SNMP_API_STAT_PACKET_TOO_BIG;
    }
    //
    // allocate byte array based on packet size
    //if ( (_packet = (byte *)malloc(sizeof(byte)*_packetSize)) == NULL ) {
        //
        //SNMP_FREE(_packet);

    //    return SNMP_API_STAT_MALLOC_ERR;
    //}
    //
    // packet check 1
    if ( _packet[0] != 0x30 ) {
        //
        //SNMP_FREE(_packet);

        return SNMP_API_STAT_PACKET_INVALID;
    }
    //
    // sequence length
    seqLen = _packet[1];
    // version
    verLen = _packet[3];
    verEnd = 3 + verLen;
    // community string
    comLen = _packet[verEnd + 2];
    comEnd = verEnd + 2 + comLen;
    // pdu
    pduTyp = _packet[comEnd + 1];
    pduLen = _packet[comEnd + 2];
    ridLen = _packet[comEnd + 4];
    ridEnd = comEnd + 4 + ridLen;
    errLen = _packet[ridEnd + 2];
    errEnd = ridEnd + 2 + errLen;
    eriLen = _packet[errEnd + 2];
    eriEnd = errEnd + 2 + eriLen;
    vblTyp = _packet[eriEnd + 1];
    vblLen = _packet[eriEnd + 2];
    vbiTyp = _packet[eriEnd + 3];
    vbiLen = _packet[eriEnd + 4];
    obiLen = _packet[eriEnd + 6];
    obiEnd = eriEnd + obiLen + 6;
    valTyp = _packet[obiEnd + 1];
    valLen = _packet[obiEnd + 2];
    valEnd = obiEnd + 2 + valLen;
    //
    // extract version
    pdu->version = 0;
    for ( i = 0; i < verLen; i++ ) {
        pdu->version = (pdu->version << 8) | _packet[5 + i];
    }
    //
    // pdu-type
    pdu->type = (SNMP_PDU_TYPES)pduTyp;
    _dstType = pdu->type;
    //
    // validate community size
    if ( comLen > SNMP_MAX_NAME_LEN ) {
        // set pdu error
        pdu->error = SNMP_ERR_TOO_BIG;
        //
        //SNMP_FREE(_packet);

        return SNMP_API_STAT_NAME_TOO_BIG;
    }
    //
    // extract and compare community name
    // allocate char array based on community size
    if ( (community = (char *)malloc(sizeof(char)*comLen)) == NULL ) {
        //
        //SNMP_FREE(_packet);

        return SNMP_API_STAT_MALLOC_ERR;
    }
    //
    for ( i = 0; i < comLen; i++ ) {
        community[i] = _packet[verEnd + 3 + i];
    }
    // terminate as a string
    community[comLen] = '\0';
    //
    // validate community name
    if ( pdu->type == SNMP_PDU_SET ) {
        if ( strcmp(_setCommName, community) != 0 )
            // set pdu error
            pdu->error = SNMP_ERR_NO_SUCH_NAME;
    } else {
        if ( strcmp(_getCommName, community) != 0 )
            // set pdu error
            pdu->error = SNMP_ERR_NO_SUCH_NAME;
    }
    //
    // free community buffer
    SNMP_FREE(community);
    //
    // extract reqiest-id 0x00 0x00 0x00 0x01 (4-byte int aka int32)
    pdu->requestId = 0;
    for ( i = 0; i < ridLen; i++ ) {
        pdu->requestId = (pdu->requestId << 8) | _packet[comEnd + 5 + i];
    }
    //
    // extract error 
    pdu->error = SNMP_ERR_NO_ERROR;
    int32_t err = 0;
    for ( i = 0; i < errLen; i++ ) {
        err = (err << 8) | _packet[ridEnd + 3 + i];
    }
    pdu->error = (SNMP_ERR_CODES)err;
    //
    // extract error-index 
    pdu->errorIndex = 0;
    for ( i = 0; i < eriLen; i++ ) {
        pdu->errorIndex = (pdu->errorIndex << 8) | _packet[errEnd + 3 + i];
    }
    //
    //
    // validate object-identifier size
    if ( obiLen > SNMP_MAX_OID_LEN ) {
        // set pdu error
        pdu->error = SNMP_ERR_TOO_BIG;
        //
        //SNMP_FREE(_packet);

        return SNMP_API_STAT_OID_TOO_BIG;
    }
    //
    // extract and contruct object-identifier
    /*
    if ( (pdu->OID.oid = (byte *)malloc(sizeof(byte)*obiLen)) == NULL ) {
        // free DPU receive buffer
        _socket.readSkip(_socket.available());
        //
        SNMP_FREE(_packet);

        return SNMP_API_STAT_MALLOC_ERR;
    }
    */
    memset(pdu->OID.data, 0, SNMP_MAX_OID_LEN);
    pdu->OID.size = obiLen;
    for ( i = 0; i < obiLen; i++ ) {
        pdu->OID.data[i] = _packet[eriEnd + 7 + i];
    }
    //
    // value-type
    pdu->VALUE.syntax = (SNMP_SYNTAXES)valTyp;
    //
    // validate value size
    if ( obiLen > SNMP_MAX_VALUE_LEN ) {
        // set pdu error
        pdu->error = SNMP_ERR_TOO_BIG;
        //
        //SNMP_FREE(_packet);

        return SNMP_API_STAT_VALUE_TOO_BIG;
    }
    //
    // value-size
    pdu->VALUE.size = valLen;
    //
    // extract value
    // allocate char array based on oid size
    /*
    if( (pdu->VALUE.value = (byte *)malloc(sizeof(byte)*valLen)) == NULL ) {
        // free DPU receive buffer
        _socket.readSkip(_socket.available());
        //
        SNMP_FREE(_packet);

        return SNMP_API_STAT_MALLOC_ERR;
    }
    */
    memset(pdu->VALUE.data, 0, SNMP_MAX_VALUE_LEN);
    for ( i = 0; i < valLen; i++ ) {
        pdu->VALUE.data[i] = _packet[obiEnd + 3 + i];
    }
    //
    //SNMP_FREE(_packet);
    //
    return SNMP_API_STAT_SUCCESS;
}

SNMP_API_STAT_CODES AgentbedClass::responsePdu(SNMP_PDU *pdu)
{
    int32_u u;
    byte i;
    //
    // Length of entire SNMP packet
    _packetPos = 0;  // 23
    _packetSize = 25 - 1 + sizeof(pdu->requestId) + sizeof(pdu->error) + sizeof(pdu->errorIndex) + pdu->OID.size + pdu->VALUE.size;
    //
    memset(_packet, 0, SNMP_MAX_PACKET_LEN);
    //
    if ( _dstType == SNMP_PDU_SET ) {
        _packetSize += _setSize;
    } else {
        _packetSize += _getSize;
    }
    //
    // allocate byte array based on packet size
    //if ( (_packet = (byte *)malloc(sizeof(byte)*_packetSize)) == NULL ) {
        //
        //SNMP_FREE(_packet);

    //    return SNMP_API_STAT_MALLOC_ERR;
    //}
    //
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_SEQUENCE;    // type
    _packet[_packetPos++] = (byte)_packetSize;        // length
    //

    // SNMP version
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_INT;    // type
    _packet[_packetPos++] = 0x01;            // length
    _packet[_packetPos++] = 0x00;            // value
    //
    // SNMP community string
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_OCTETS;    // type
    if ( _dstType == SNMP_PDU_SET ) {
        _packet[_packetPos++] = (byte)_setSize;    // length
        for ( i = 0; i < _setSize; i++ ) {
            _packet[_packetPos++] = (byte)_setCommName[i];
        }
    } else {
        _packet[_packetPos++] = (byte)_getSize;    // length
        for ( i = 0; i < _getSize; i++ ) {
            _packet[_packetPos++] = (byte)_getCommName[i];
        }
    }
    //
    // SNMP PDU
    _packet[_packetPos++] = (byte)pdu->type;
    _packet[_packetPos++] = (byte)( sizeof(pdu->requestId) + sizeof((int32_t)pdu->error) + sizeof(pdu->errorIndex) + pdu->OID.size + pdu->VALUE.size + 14 );
    //
    // Request ID (size always 4 e.g. 4-byte int)
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_INT;    // type
    _packet[_packetPos++] = (byte)sizeof(pdu->requestId);
    u.int32 = pdu->requestId;
    _packet[_packetPos++] = u.data[3];
    _packet[_packetPos++] = u.data[2];
    _packet[_packetPos++] = u.data[1];
    _packet[_packetPos++] = u.data[0];
    //
    // Error (size always 4 e.g. 4-byte int)
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_INT;    // type
    _packet[_packetPos++] = (byte)sizeof((int32_t)pdu->error);
    u.int32 = pdu->error;
    _packet[_packetPos++] = u.data[3];
    _packet[_packetPos++] = u.data[2];
    _packet[_packetPos++] = u.data[1];
    _packet[_packetPos++] = u.data[0];
    //
    // Error Index (size always 4 e.g. 4-byte int)
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_INT;    // type
    _packet[_packetPos++] = (byte)sizeof(pdu->errorIndex);
    u.int32 = pdu->errorIndex;
    _packet[_packetPos++] = u.data[3];
    _packet[_packetPos++] = u.data[2];
    _packet[_packetPos++] = u.data[1];
    _packet[_packetPos++] = u.data[0];
    //
    // Varbind List
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_SEQUENCE;    // type
    _packet[_packetPos++] = (byte)( pdu->OID.size + pdu->VALUE.size + 6 ); //4
    //
    // Varbind
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_SEQUENCE;    // type
    _packet[_packetPos++] = (byte)( pdu->OID.size + pdu->VALUE.size + 4 ); //2
    //
    // ObjectIdentifier
    _packet[_packetPos++] = (byte)SNMP_SYNTAX_OID;    // type
    _packet[_packetPos++] = (byte)(pdu->OID.size);
    for ( i = 0; i < pdu->OID.size; i++ ) {
        _packet[_packetPos++] = pdu->OID.data[i];
    }
    //
    // Value
    _packet[_packetPos++] = (byte)pdu->VALUE.syntax;    // type
    _packet[_packetPos++] = (byte)(pdu->VALUE.size);
    for ( i = 0; i < pdu->VALUE.size; i++ ) {
        _packet[_packetPos++] = pdu->VALUE.data[i];
    }
    //
    udpsock->sendto((char*)_packet, _packetPos, &_dst);
    //
    //SNMP_FREE(_packet);
    //
    return SNMP_API_STAT_SUCCESS;
}



void AgentbedClass::onPduReceive(onPduReceiveCallback pduReceived)
{
    _callback = pduReceived;
}

void AgentbedClass::freePdu(SNMP_PDU *pdu)
{
    //SNMP_FREE(pdu->OID.oid);
    //SNMP_FREE(pdu->VALUE.value);
    memset(pdu->OID.data, 0, SNMP_MAX_OID_LEN);
    memset(pdu->VALUE.data, 0, SNMP_MAX_VALUE_LEN);
    //free((char *) pdu);
}

// Create one global object
//AgentbedClass Agentbed;