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

Dependents:   WeatherPlatform_20110408 WeatherPlatform WeatherStation

Agentbed.h

Committer:
okini3939
Date:
2011-01-28
Revision:
2:9e4369522e03
Parent:
1:b2db0ea96703

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
*/

#ifndef Agentbed_h
#define Agentbed_h

#define SNMP_DEFAULT_PORT    161
#define SNMP_MIN_OID_LEN    2
#define SNMP_MAX_OID_LEN    64 // 128
#define SNMP_MAX_NAME_LEN    20
#define SNMP_MAX_VALUE_LEN      64  // 128 ??? should limit this
#define SNMP_MAX_PACKET_LEN     SNMP_MAX_VALUE_LEN + SNMP_MAX_OID_LEN + 25  //???
#define SNMP_FREE(s)   do { if (s) { free((void *)s); s=NULL; } } while(0)
//Frees a pointer only if it is !NULL and sets its value to NULL. 

#include "mbed.h"
#include "EthernetNetIf.h"
#include "UDPSocket.h"
#include <stdlib.h>

extern "C" {
    // callback function
    typedef void (*onPduReceiveCallback)(void);
}

//typedef long long int64_t;
typedef unsigned long long uint64_t;
//typedef long int32_t;
//typedef unsigned long uint32_t;
//typedef unsigned char uint8_t;
//typedef short int16_t;
//typedef unsigned short uint16_t;
typedef unsigned char byte;

typedef union uint64_u {
    uint64_t uint64;
    byte data[8];
} uint64_u;

typedef union int32_u {
    int32_t int32;
    byte data[4];
} int32_u;

typedef union uint32_u{
    uint32_t uint32;
    byte data[4];
} uint32_u;

typedef union int16_u {
    int16_t int16;
    byte data[2];
} int16_u;

//typedef union uint16_u {
//    uint16_t uint16;
//    byte data[2];
//};

typedef union float_u{
    float float_t;
    byte data[4];
} float_u;

enum ASN_BER_BASE_TYPES {
    //   ASN/BER base types
    ASN_BER_BASE_UNIVERSAL      = 0x0,
    ASN_BER_BASE_APPLICATION = 0x40,
    ASN_BER_BASE_CONTEXT      = 0x80,
    ASN_BER_BASE_PUBLIC      = 0xC0,
    ASN_BER_BASE_PRIMITIVE      = 0x0,
    ASN_BER_BASE_CONSTRUCTOR = 0x20
};

enum SNMP_PDU_TYPES {
    // PDU choices
    SNMP_PDU_GET      = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 0,
    SNMP_PDU_GET_NEXT = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 1,
    SNMP_PDU_RESPONSE = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 2,
    SNMP_PDU_SET      = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 3,
    SNMP_PDU_TRAP      = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 4
};

enum SNMP_TRAP_TYPES {
    //   Trap generic types:
    SNMP_TRAP_COLD_START           = 0,
    SNMP_TRAP_WARM_START           = 1,
    SNMP_TRAP_LINK_DOWN           = 2,
    SNMP_TRAP_LINK_UP           = 3,
    SNMP_TRAP_AUTHENTICATION_FAIL = 4,
    SNMP_TRAP_EGP_NEIGHBORLOSS    = 5,
    SNMP_TRAP_ENTERPRISE_SPECIFIC = 6
};

enum SNMP_ERR_CODES {
    SNMP_ERR_NO_ERROR               = 0,
    SNMP_ERR_TOO_BIG               = 1,
    SNMP_ERR_NO_SUCH_NAME             = 2,
    SNMP_ERR_BAD_VALUE               = 3,
    SNMP_ERR_READ_ONLY               = 4,
    SNMP_ERR_GEN_ERROR               = 5,

    SNMP_ERR_NO_ACCESS              = 6,
    SNMP_ERR_WRONG_TYPE               = 7,
    SNMP_ERR_WRONG_LENGTH             = 8,
    SNMP_ERR_WRONG_ENCODING            = 9,
    SNMP_ERR_WRONG_VALUE            = 10,
    SNMP_ERR_NO_CREATION            = 11,
    SNMP_ERR_INCONSISTANT_VALUE         = 12,
    SNMP_ERR_RESOURCE_UNAVAILABLE        = 13,
    SNMP_ERR_COMMIT_FAILED            = 14,
    SNMP_ERR_UNDO_FAILED            = 15,
    SNMP_ERR_AUTHORIZATION_ERROR        = 16,
    SNMP_ERR_NOT_WRITABLE            = 17,
    SNMP_ERR_INCONSISTEN_NAME        = 18
};

enum SNMP_API_STAT_CODES {
    SNMP_API_STAT_SUCCESS = 0,
    SNMP_API_STAT_MALLOC_ERR = 1,
    SNMP_API_STAT_NAME_TOO_BIG = 2,
    SNMP_API_STAT_OID_TOO_BIG = 3,
    SNMP_API_STAT_VALUE_TOO_BIG = 4,
    SNMP_API_STAT_PACKET_INVALID = 5,
    SNMP_API_STAT_PACKET_TOO_BIG = 6
};

//
// http://oreilly.com/catalog/esnmp/chapter/ch02.html Table 2-1: SMIv1 Datatypes

enum SNMP_SYNTAXES {
    //   SNMP ObjectSyntax values
    SNMP_SYNTAX_SEQUENCE            = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_CONSTRUCTOR | 0x10,
    //   These values are used in the "syntax" member of VALUEs
    SNMP_SYNTAX_BOOL            = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 1,
    SNMP_SYNTAX_INT            = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 2,
    SNMP_SYNTAX_BITS            = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 3,
    SNMP_SYNTAX_OCTETS            = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 4,
    SNMP_SYNTAX_NULL            = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 5,
    SNMP_SYNTAX_OID               = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 6,
    SNMP_SYNTAX_INT32            = SNMP_SYNTAX_INT,
    SNMP_SYNTAX_IP_ADDRESS         = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 0,
    SNMP_SYNTAX_COUNTER            = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 1,
    SNMP_SYNTAX_GAUGE            = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 2,
    SNMP_SYNTAX_TIME_TICKS         = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 3,
    SNMP_SYNTAX_OPAQUE            = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 4,
    SNMP_SYNTAX_NSAPADDR            = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 5,
    SNMP_SYNTAX_COUNTER64            = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 6,
    SNMP_SYNTAX_UINT32            = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 7,
    SNMP_SYNTAX_OPAQUE_FLOAT      = 0x78,
};

typedef struct SNMP_OID {
    byte data[SNMP_MAX_OID_LEN];  // ushort array insted??
    size_t size;
    //
    void fromString(const char *buffer) {
    }
    //
    void toString(char *buffer) {
        buffer[0] = '1';
        buffer[1] = '.';
        buffer[2] = '3';
        buffer[3] = '\0';
        //
        // tmp buffer - short (Int16)
        //char *buff = (char *)malloc(sizeof(char)*16);  // I ya ya keeps hanging the MCU
        char buff[16];
        byte hsize = size - 1;
        byte hpos = 1;
        uint16_t subid;
        //
        while ( hsize > 0 ) {
            subid = 0;
            uint16_t b = 0;
            do {
                uint16_t next = data[hpos++];
                b = next & 0xFF;
                subid = (subid << 7) + (b & ~0x80);
                hsize--;
            } while ( (hsize > 0) && ((b & 0x80) != 0) );
            //utoa(subid, buff, 10);
            sprintf(buff, "%d", subid);
            strcat(buffer, ".");
            strcat(buffer, buff);
        }
        //
        // free buff
        //SNMP_FREE(buff);
    };
} SNMP_OID;

// union for values?
//
typedef struct SNMP_VALUE {
    byte data[SNMP_MAX_VALUE_LEN];
    size_t size;
    SNMP_SYNTAXES syntax;
    //
    byte i; // for encoding/decoding functions
    //
    // clear's buffer and sets size to 0
    void clear(void) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        size = 0;
    }
    //
    //
    // ASN.1 decoding functions
    //
    // decode's an octet string, object-identifier, opaque syntax to string
    SNMP_ERR_CODES decode(char *value, size_t max_size) {
        if ( syntax == SNMP_SYNTAX_OCTETS || syntax == SNMP_SYNTAX_OID
            || syntax == SNMP_SYNTAX_OPAQUE ) {
            if ( strlen(value) - 1 < max_size ) {
                if ( syntax == SNMP_SYNTAX_OID ) {
                    value[0] = '1';
                    value[1] = '.';
                    value[2] = '3';
                    value[3] = '\0';
                    //
                    // tmp buffer - ushort (UInt16)
                    //char *buff = (char *)malloc(sizeof(char)*16); // I ya ya..keep hanging the MCU
                    char buff[16];
                    byte hsize = size - 1;
                    byte hpos = 1;
                    uint16_t subid;
                    //
                    while ( hsize > 0 ) {
                        subid = 0;
                        uint16_t b = 0;
                        do {
                            uint16_t next = data[hpos++];
                            b = next & 0xFF;
                            subid = (subid << 7) + (b & ~0x80);
                            hsize--;
                        } while ( (hsize > 0) && ((b & 0x80) != 0) );
                        //utoa(subid, buff, 10);
                        sprintf(buff, "%d", subid);
                        strcat(value, ".");
                        strcat(value, buff);
                    }
                    //
                    // free buff
                    //SNMP_FREE(buff);
                } else {
                    for ( i = 0; i < size; i++ ) {
                        value[i] = (char)data[i];
                    }
                    value[size] = '\0';
                }
                return SNMP_ERR_NO_ERROR;
            } else {
                clear();    
                return SNMP_ERR_TOO_BIG;
            }
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // decode's an int syntax to int16 ?? is this applicable
    SNMP_ERR_CODES decode(int16_t *value) {
        if ( syntax == SNMP_SYNTAX_INT ) {
            int16_u tmp;
            tmp.data[1] = data[0];
            tmp.data[0] = data[1];
            *value = tmp.int16;
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // decode's an int32 syntax to int32
    SNMP_ERR_CODES decode(int32_t *value) {
        if ( syntax == SNMP_SYNTAX_INT32 ) {
            int32_u tmp;
            tmp.data[3] = data[0];
            tmp.data[2] = data[1];
            tmp.data[1] = data[2];
            tmp.data[0] = data[3];
            *value = tmp.int32;
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // decode's an uint32, counter, time-ticks, gauge syntax to uint32
    SNMP_ERR_CODES decode(uint32_t *value) {
        if ( syntax == SNMP_SYNTAX_COUNTER || syntax == SNMP_SYNTAX_TIME_TICKS
            || syntax == SNMP_SYNTAX_GAUGE || syntax == SNMP_SYNTAX_UINT32 ) {
            uint32_u tmp;
            tmp.data[3] = data[0];
            tmp.data[2] = data[1];
            tmp.data[1] = data[2];
            tmp.data[0] = data[3];
            *value = tmp.uint32;
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // decode's an ip-address, NSAP-address syntax to an ip-address byte array 
    SNMP_ERR_CODES decode(byte *value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syntax == SNMP_SYNTAX_IP_ADDRESS || syntax == SNMP_SYNTAX_NSAPADDR ) {
            if ( sizeof(value) > 4 ) {
                clear();
                return SNMP_ERR_TOO_BIG;
            } else {
                size = 4;
                data[0] = value[3];
                data[1] = value[2];
                data[2] = value[1];
                data[3] = value[0];
                return SNMP_ERR_NO_ERROR;
            }
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // decode's a boolean syntax to boolean
    SNMP_ERR_CODES decode(bool *value) {
        if ( syntax == SNMP_SYNTAX_BOOL ) {
            *value = (data[0] != 0);
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    //
    // ASN.1 encoding functions
    //
    // encode's a octet string to a string, opaque syntax
    // encode object-identifier here??
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const char *value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_OCTETS || syn == SNMP_SYNTAX_OPAQUE ) {
            if ( strlen(value) - 1 < SNMP_MAX_VALUE_LEN ) {
                syntax = syn;
                size = strlen(value);
                for ( i = 0; i < size; i++ ) {
                    data[i] = (byte)value[i];
                }
                return SNMP_ERR_NO_ERROR;
            } else {
                clear();    
                return SNMP_ERR_TOO_BIG;
            }
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's an int16 to int, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const int16_t value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_INT || syn == SNMP_SYNTAX_OPAQUE ) {
            int16_u tmp;
            size = 2;
            syntax = syn;
            tmp.int16 = value;
            data[0] = tmp.data[1];
            data[1] = tmp.data[0];
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's an int32 to int32, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const int32_t value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_INT32 || syn == SNMP_SYNTAX_OPAQUE ) {
            int32_u tmp;
            size = 4;
            syntax = syn;
            tmp.int32 = value;
            data[0] = tmp.data[3];
            data[1] = tmp.data[2];
            data[2] = tmp.data[1];
            data[3] = tmp.data[0];
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's an uint32 to uint32, counter, time-ticks, gauge, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const uint32_t value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_COUNTER || syn == SNMP_SYNTAX_TIME_TICKS
            || syn == SNMP_SYNTAX_GAUGE || syn == SNMP_SYNTAX_UINT32 
            || syn == SNMP_SYNTAX_OPAQUE ) {
            uint32_u tmp;
            size = 4;
            syntax = syn;
            tmp.uint32 = value;
            data[0] = tmp.data[3];
            data[1] = tmp.data[2];
            data[2] = tmp.data[1];
            data[3] = tmp.data[0];
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's an ip-address byte array to ip-address, NSAP-address, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const byte *value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_IP_ADDRESS || syn == SNMP_SYNTAX_NSAPADDR 
            || syn == SNMP_SYNTAX_OPAQUE ) {
            if ( sizeof(value) > 4 ) {
                clear();
                return SNMP_ERR_TOO_BIG;
            } else {
                size = 4;
                syntax = syn;
                data[0] = value[3];
                data[1] = value[2];
                data[2] = value[1];
                data[3] = value[0];
                return SNMP_ERR_NO_ERROR;
            }
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's a boolean to boolean, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const bool value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_BOOL || syn == SNMP_SYNTAX_OPAQUE ) {
            size = 1;
            syntax = syn;
            data[0] = value ? 0xff : 0;
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's an uint64 to counter64, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const uint64_t value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_COUNTER64 || syn == SNMP_SYNTAX_OPAQUE ) {
            uint64_u tmp;
            size = 8;
            syntax = syn;
            tmp.uint64 = value;
            data[0] = tmp.data[7];
            data[1] = tmp.data[6];
            data[2] = tmp.data[5];
            data[3] = tmp.data[4];
            data[4] = tmp.data[3];
            data[5] = tmp.data[2];
            data[6] = tmp.data[1];
            data[7] = tmp.data[0];
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's an int32 to int32, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const float value) {
        memset(data, 0, SNMP_MAX_VALUE_LEN);
        if ( syn == SNMP_SYNTAX_OPAQUE_FLOAT ) {
            float_u tmp;
            size = 4;
            syntax = syn;
            tmp.float_t = value;
            data[0] = tmp.data[3];
            data[1] = tmp.data[2];
            data[2] = tmp.data[1];
            data[3] = tmp.data[0];
            return SNMP_ERR_NO_ERROR;
        } else {
            clear();
            return SNMP_ERR_WRONG_TYPE;
        }
    }
    //
    // encode's a null to null, opaque  syntax
    SNMP_ERR_CODES encode(SNMP_SYNTAXES syn) {
        clear();
        if ( syn == SNMP_SYNTAX_NULL || syn == SNMP_SYNTAX_OPAQUE ) {
            size = 0;
            syntax = syn;
            return SNMP_ERR_NO_ERROR;
        } else {
            return SNMP_ERR_WRONG_TYPE;
        }
    }
} SNMP_VALUE;

typedef struct SNMP_PDU {
    SNMP_PDU_TYPES type;
    int32_t version;
    int32_t requestId;
    SNMP_ERR_CODES error;
    int32_t errorIndex;
    SNMP_OID OID;
    SNMP_VALUE VALUE;
} SNMP_PDU;

class AgentbedClass {
public:
    // Agent functions
    SNMP_API_STAT_CODES begin(EthernetNetIf *eth);
    SNMP_API_STAT_CODES begin(char *getCommName, char *setCommName, uint16_t port, EthernetNetIf *eth);
    void listen(UDPSocketEvent);
    SNMP_API_STAT_CODES requestPdu(SNMP_PDU *pdu);
    SNMP_API_STAT_CODES responsePdu(SNMP_PDU *pdu);
    void onPduReceive(onPduReceiveCallback pduReceived);
    void freePdu(SNMP_PDU *pdu);

    // Helper functions

private:
    byte _packet[SNMP_MAX_PACKET_LEN];
    uint16_t _packetSize;
    uint16_t _packetPos;
    SNMP_PDU_TYPES _dstType;
    char *_getCommName;
    size_t _getSize;
    char *_setCommName;
    size_t _setSize;
    onPduReceiveCallback _callback;
    UDPSocket *udpsock;
    Host _dst;
};

//extern AgentbedClass Agentbed;

#endif