├── .vscode └── settings.json ├── library.properties ├── src ├── VarBinds.h ├── SNMPGet.h ├── SNMPGetResponse.h ├── Arduino_SNMP_Manager.h └── BER.h ├── library.json ├── LICENSE ├── CHANGELOG.md ├── examples ├── Arduino_Ethernet_SNMP_Manager │ └── Arduino_Ethernet_SNMP_Manager.ino ├── ESP_Multiple_SNMP_Device_Polling │ └── ESP_Multiple_SNMP_Device_Polling.ino └── ESP32_ESP8266_SNMP_Manager │ └── ESP32_ESP8266_SNMP_Manager.ino └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Atuc", 4 | "Curr", 5 | "Dekatron", 6 | "deprioritised", 7 | "ERRORID", 8 | "ERRORSTATUS", 9 | "LOWEROCTETLIMIT", 10 | "millis", 11 | "Niich's", 12 | "NULLTYPE", 13 | "println", 14 | "Seeed", 15 | "SNMP", 16 | "snmpgetresponse", 17 | "snmpwalk", 18 | "timeticks", 19 | "Trapv", 20 | "UPPEROCTETLIMIT", 21 | "varbindlist" 22 | ], 23 | "cSpell.language": "en,en-GB" 24 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SNMP Manager 2 | version=1.1.13 3 | author=Martin Rowan 4 | maintainer=Martin Rowan 5 | sentence=An SNMP Manager library to make SNMP requests to other SNMP enabled devices. 6 | paragraph=Supporting SNMP v1 and v2, SNMP requests can be sent (GetRequest) and their responses received (GetResponse) for various SNMP data types. 7 | category=Communication 8 | url=https://github.com/shortbloke/Arduino_SNMP_Manager 9 | architectures=* 10 | includes=Arduino_SNMP_Manager.h 11 | -------------------------------------------------------------------------------- /src/VarBinds.h: -------------------------------------------------------------------------------- 1 | #ifndef VarBinds_h 2 | #define VarBinds_h 3 | 4 | typedef struct VarBindStruct 5 | { 6 | ~VarBindStruct(){ 7 | // if(value) delete value; 8 | // if(oid) delete oid; 9 | }; 10 | OIDType *oid = 0; 11 | ASN_TYPE type; 12 | BER_CONTAINER *value = 0; 13 | } VarBind; 14 | 15 | typedef struct VarBindListStruct 16 | { 17 | ~VarBindListStruct() 18 | { 19 | delete next; 20 | next = 0; 21 | delete value; 22 | value = 0; 23 | }; 24 | struct VarBindStruct *value = 0; 25 | struct VarBindListStruct *next = 0; 26 | } VarBindList; 27 | 28 | #endif -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SNMP Manager", 3 | "version": "1.1.13", 4 | "description": "An SNMP Manager library to make SNMP requests to other SNMP enabled devices. Supporting SNMP v1 and v2, SNMP requests can be sent (GetRequest) and their responses received (GetResponse) for various SNMP data types.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/shortbloke/Arduino_SNMP_Manager.git" 8 | }, 9 | "authors": { 10 | "name": "Martin Rowan", 11 | "email": "martin@rowannet.co.uk", 12 | "url": "https://www.martinrowan.co.uk/contact/", 13 | "maintainer": true 14 | }, 15 | "license": "MIT", 16 | "frameworks": "arduino", 17 | "platforms": "*" 18 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2022 Martin Rowan (https://github.com/shortbloke/Arduino_SNMP_Manager) - Create SNMP Manager without Agent 4 | Parts Copyright (c) 2019 Niich (https://github.com/Niich/Arduino_SNMP) - Initial SNMP Manager derived from SNMP Agent 5 | Parts Copyright (c) 2017 Aidan Cyr (https://github.com/fusionps/Arduino_SNMP) - SNMP Agent 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG for SNMP Manager For ESP8266/ESP32/Arduino 2 | 3 | ## 1.1.13 4 | - Fix crash when using OIDs with 10 digits. Contributor: [AlphaArslan](https://github.com/AlphaArslan) 5 | 6 | ## 1.1.12 7 | 8 | - Added flag to suppress short packet errors. Add `#define SUPPRESS_ERROR_SHORT_PACKET` before `#include ` 9 | - Added flag to suppress failed to parse errors. Add `#define SUPPRESS_ERROR_FAILED_PARSE` before `#include ` 10 | - Corrected some spelling mistakes. 11 | 12 | ## 1.1.11 13 | 14 | - Fixed implementation of encoding integers to use the minimum number of bytes necessary. Previously was always used 4 bytes. This Fixes #25. 15 | 16 | ## 1.1.10 17 | 18 | - Fixed spelling error `Guage` now corrected all references to `Gauge`. This maybe a breaking change if for example you are were using `addGuageHandler` or referencing the type `GUAGE32`, which now should be updated to `addGaugeHandler` and `GAUGE32`. 19 | 20 | ## 1.1.9 21 | 22 | - Added a new example file for ESP MCU to show polling of multiple devices and storing results in a device record array. #20 23 | 24 | ## 1.1.8 25 | 26 | - Fixed #19 timeticks should be of type unsigned integer. This change impacts `SNMPManager::addTimestampHandler`. 27 | 28 | ## 1.1.7 29 | 30 | - Fixes #18 support OID that use large integers, up to 4 bytes. 31 | 32 | ## 1.1.6 33 | 34 | - Allow non standard port to be used when making SNMP requests. Default UDP port 161 can be overridden using `setPort()`. 35 | 36 | ## 1.1.5 37 | 38 | - Support longer OIDs. Change in v1.1.1 was incomplete 39 | 40 | ## 1.1.4 41 | 42 | - Fixes #12 where additional check for packet length was incorrect and unnecessary 43 | 44 | ## 1.1.3 45 | 46 | Focus: Increase robustness 47 | 48 | - Better handling devices sending invalid packets in response to requests 49 | - Better handling for receiving responses with OID that weren't requested 50 | - Added DEBUG log messages to aid future troubleshooting. Just add extra defines `#define DEBUG` and/or `#define DEBUG_BER` 51 | - Added support for using test data in `SNMPManager::receivePacket` to better support users experiencing issues 52 | 53 | ## 1.1.2 54 | 55 | - Reduce max size of SNMP message on ESP8266 to address [reported issue](https://github.com/shortbloke/Broadband_Usage_Display/issues/4_) which triggered exception: `Exception 9: LoadStoreAlignmentCause: Load or store to an unaligned address` 56 | 57 | ## 1.1.1 58 | 59 | - Improve OctetString handling for long strings 60 | - Handle OID > 50 characters 61 | 62 | ## 1.1.0 63 | 64 | - Initial library release 65 | -------------------------------------------------------------------------------- /src/SNMPGet.h: -------------------------------------------------------------------------------- 1 | #ifndef SNMPGet_h 2 | #define SNMPGet_h 3 | 4 | enum SNMPExpect 5 | { 6 | HEADER, 7 | SNMPVERSION, 8 | COMMUNITY, 9 | PDU, 10 | REQUESTID, 11 | ERRORSTATUS, 12 | ERRORID, 13 | VARBINDS, 14 | VARBIND, 15 | DONE 16 | }; 17 | 18 | class SNMPGet 19 | { 20 | public: 21 | SNMPGet(const char *community, short version) : _community(community), _version(version) 22 | { 23 | if (version == 0) 24 | { 25 | version1 = true; 26 | } 27 | if (version == 1) 28 | { 29 | version2 = true; 30 | } 31 | }; 32 | const char *_community; 33 | short _version; 34 | IPAddress agentIP; 35 | short port = 161; 36 | short requestID; 37 | short errorID = 0; 38 | short errorIndex = 0; 39 | 40 | // the setters that need to be configured for each Get. 41 | 42 | void setRequestID(short request) 43 | { 44 | requestID = request; 45 | } 46 | 47 | void setIP(IPAddress ip) 48 | { 49 | agentIP = ip; 50 | } 51 | 52 | void setPort(short portnumber) 53 | { 54 | port = portnumber; 55 | } 56 | 57 | void setUDP(UDP *udp) 58 | { 59 | _udp = udp; 60 | } 61 | 62 | void addOIDPointer(ValueCallback *callback); 63 | ValueCallbacks *callbacks = new ValueCallbacks(); 64 | ValueCallbacks *callbacksCursor = callbacks; 65 | 66 | UDP *_udp = 0; 67 | bool sendTo(IPAddress ip) 68 | { 69 | if (!_udp) 70 | { 71 | return false; 72 | } 73 | if (!build()) 74 | { 75 | Serial.println(F("Failed Building packet..")); 76 | delete packet; 77 | packet = 0; 78 | return false; 79 | } 80 | unsigned char _packetBuffer[SNMP_PACKET_LENGTH * 3]; 81 | memset(_packetBuffer, 0, SNMP_PACKET_LENGTH * 3); 82 | int length = packet->serialise(_packetBuffer); 83 | delete packet; 84 | packet = 0; 85 | #ifdef DEBUG 86 | Serial.print(F("[DEBUG] SNMPGet: Sending UDP packet to: ")); 87 | Serial.print(ip); 88 | Serial.print(F(":")); 89 | Serial.println(port); 90 | Serial.print("[DEBUG] composed packet: "); 91 | for (int i = 0; i < length; i++) 92 | { 93 | Serial.printf("%02x ", _packetBuffer[i]); 94 | } 95 | Serial.println(); 96 | #endif 97 | _udp->beginPacket(ip, port); 98 | _udp->write(_packetBuffer, length); 99 | return _udp->endPacket(); 100 | } 101 | 102 | ComplexType *packet = 0; 103 | bool build(); 104 | 105 | bool version1 = false; 106 | bool version2 = false; 107 | 108 | void clearOIDList() 109 | { // this just removes the list, does not kill the values in the list 110 | callbacksCursor = callbacks; 111 | delete callbacksCursor; 112 | callbacks = new ValueCallbacks(); 113 | callbacksCursor = callbacks; 114 | } 115 | }; 116 | 117 | bool SNMPGet::build() 118 | { 119 | // Build packet for making GetRequest 120 | if (packet) 121 | { 122 | packet = 0; 123 | } 124 | packet = new ComplexType(STRUCTURE); 125 | packet->addValueToList(new IntegerType((int)_version)); 126 | packet->addValueToList(new OctetType((char *)_community)); 127 | ComplexType *getPDU; 128 | getPDU = new ComplexType(GetRequestPDU); 129 | getPDU->addValueToList(new IntegerType(requestID)); 130 | getPDU->addValueToList(new IntegerType(errorID)); 131 | getPDU->addValueToList(new IntegerType(errorIndex)); 132 | ComplexType *varBindList = new ComplexType(STRUCTURE); 133 | 134 | callbacksCursor = callbacks; 135 | if (callbacksCursor->value) 136 | { 137 | while (true) 138 | { 139 | ComplexType *varBind = new ComplexType(STRUCTURE); 140 | varBind->addValueToList(new OIDType(callbacksCursor->value->OID)); 141 | // Value can be null for Request payload. 142 | BER_CONTAINER *value = new NullType(); 143 | varBind->addValueToList(value); 144 | varBindList->addValueToList(varBind); 145 | 146 | if (callbacksCursor->next) 147 | { 148 | callbacksCursor = callbacksCursor->next; 149 | } 150 | else 151 | { 152 | break; 153 | } 154 | } 155 | } 156 | getPDU->addValueToList(varBindList); 157 | packet->addValueToList(getPDU); 158 | return true; 159 | } 160 | 161 | void SNMPGet::addOIDPointer(ValueCallback *callback) 162 | { 163 | callbacksCursor = callbacks; 164 | if (callbacksCursor->value) 165 | { 166 | while (callbacksCursor->next != 0) 167 | { 168 | callbacksCursor = callbacksCursor->next; 169 | } 170 | callbacksCursor->next = new ValueCallbacks(); 171 | callbacksCursor = callbacksCursor->next; 172 | callbacksCursor->value = callback; 173 | callbacksCursor->next = 0; 174 | } 175 | else 176 | callbacks->value = callback; 177 | } 178 | 179 | #endif -------------------------------------------------------------------------------- /src/SNMPGetResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef SNMPGetResponse_h 2 | #define SNMPGetResponse_h 3 | 4 | class SNMPGetResponse 5 | { 6 | 7 | public: 8 | SNMPGetResponse(){}; 9 | ~SNMPGetResponse() 10 | { 11 | delete varBinds; 12 | delete SNMPPacket; 13 | }; 14 | char *communityString; 15 | int version; 16 | ASN_TYPE requestType; 17 | unsigned long requestID; 18 | int errorStatus; 19 | int errorIndex; 20 | VarBindList *varBinds = 0; 21 | VarBindList *varBindsCursor = 0; 22 | 23 | ComplexType *SNMPPacket = 0; 24 | bool parseFrom(unsigned char *buf); 25 | bool serialise(char *buf); 26 | enum SNMPExpect EXPECTING = SNMPVERSION; 27 | bool isCorrupt = false; 28 | }; 29 | 30 | bool SNMPGetResponse::parseFrom(unsigned char *buf) 31 | { 32 | // confirm that the packet is a STRUCTURE 33 | if (buf[0] != 0x30) 34 | { 35 | #ifdef DEBUG 36 | Serial.printf("[DEBUG] Packet is not an SNMPGetResponse, expected 0x30, received: 0x%02x\n", buf[0]); 37 | #endif 38 | isCorrupt = true; 39 | return false; 40 | } 41 | SNMPPacket = new ComplexType(STRUCTURE); // ensure SNMPPacket is initialised to avoid crash in deconstructor 42 | SNMPPacket->fromBuffer(buf); 43 | 44 | if (SNMPPacket->getLength() <= 30) 45 | { 46 | #ifdef DEBUG 47 | Serial.printf("[DEBUG] Packet too short. Expected > 30, Actual: %d\n", SNMPPacket->getLength()); 48 | #endif 49 | #ifndef SUPPRESS_ERROR_SHORT_PACKET 50 | Serial.print(F("SNMP packet too short, needs to be > 30.")); 51 | #endif 52 | return false; 53 | } 54 | // we now have a full ASN.1 packet in SNMPPacket 55 | ValuesList *cursor = SNMPPacket->_values; 56 | ValuesList *tempCursor = NULL; 57 | while (EXPECTING != DONE) 58 | { 59 | switch (EXPECTING) 60 | { 61 | case SNMPVERSION: 62 | if (cursor->value->_type == INTEGER) 63 | { 64 | version = ((IntegerType *)cursor->value)->_value + 1; 65 | if (!cursor->next) 66 | { 67 | isCorrupt = true; 68 | return false; 69 | } 70 | cursor = cursor->next; 71 | EXPECTING = COMMUNITY; 72 | } 73 | else 74 | { 75 | isCorrupt = true; 76 | return false; 77 | } 78 | break; 79 | case COMMUNITY: 80 | if (cursor->value->_type == STRING) 81 | { 82 | communityString = ((OctetType *)cursor->value)->_value; 83 | if (!cursor->next) 84 | { 85 | isCorrupt = true; 86 | return false; 87 | } 88 | cursor = cursor->next; 89 | EXPECTING = PDU; 90 | } 91 | else 92 | { 93 | isCorrupt = true; 94 | return false; 95 | } 96 | break; 97 | case PDU: 98 | switch (cursor->value->_type) 99 | { 100 | case GetRequestPDU: 101 | case GetNextRequestPDU: 102 | case GetResponsePDU: 103 | case SetRequestPDU: 104 | requestType = cursor->value->_type; 105 | break; 106 | default: 107 | isCorrupt = true; 108 | return false; 109 | break; 110 | } 111 | cursor = ((ComplexType *)cursor->value)->_values; 112 | EXPECTING = REQUESTID; 113 | break; 114 | case REQUESTID: 115 | if (cursor->value->_type == INTEGER) 116 | { 117 | requestID = ((IntegerType *)cursor->value)->_value; 118 | if (!cursor->next) 119 | { 120 | isCorrupt = true; 121 | return false; 122 | } 123 | cursor = cursor->next; 124 | EXPECTING = ERRORSTATUS; 125 | } 126 | else 127 | { 128 | isCorrupt = true; 129 | return false; 130 | } 131 | break; 132 | case ERRORSTATUS: 133 | if (cursor->value->_type == INTEGER) 134 | { 135 | errorStatus = ((IntegerType *)cursor->value)->_value; 136 | if (!cursor->next) 137 | { 138 | isCorrupt = true; 139 | return false; 140 | } 141 | cursor = cursor->next; 142 | EXPECTING = ERRORID; 143 | } 144 | else 145 | { 146 | isCorrupt = true; 147 | return false; 148 | } 149 | break; 150 | case ERRORID: 151 | if (cursor->value->_type == INTEGER) 152 | { 153 | errorIndex = ((IntegerType *)cursor->value)->_value; 154 | if (!cursor->next) 155 | { 156 | isCorrupt = true; 157 | return false; 158 | } 159 | cursor = cursor->next; 160 | EXPECTING = VARBINDS; 161 | } 162 | else 163 | { 164 | isCorrupt = true; 165 | return false; 166 | } 167 | break; 168 | case VARBINDS: // we have a varbind structure, lets dive into it. 169 | if (cursor->value->_type == STRUCTURE) 170 | { 171 | varBinds = new VarBindList(); 172 | varBindsCursor = varBinds; 173 | tempCursor = ((ComplexType *)cursor->value)->_values; 174 | EXPECTING = VARBIND; 175 | } 176 | else 177 | { 178 | isCorrupt = true; 179 | return false; 180 | } 181 | break; 182 | case VARBIND: 183 | // we need to keep the cursor outside the varbindlist itself so we always have access to the list 184 | if (tempCursor->value->_type == STRUCTURE && ((ComplexType *)tempCursor->value)->_values->value->_type == OID) 185 | { 186 | VarBind *varbind = new VarBind(); 187 | varbind->oid = ((OIDType *)((ComplexType *)tempCursor->value)->_values->value); 188 | varbind->type = ((ComplexType *)tempCursor->value)->_values->next->value->_type; 189 | varbind->value = ((ComplexType *)tempCursor->value)->_values->next->value; 190 | varBindsCursor->value = varbind; 191 | varBindsCursor->next = new VarBindList(); 192 | if (!tempCursor->next) 193 | { 194 | EXPECTING = DONE; 195 | } 196 | else 197 | { 198 | tempCursor = tempCursor->next; 199 | varBindsCursor = varBindsCursor->next; 200 | } 201 | } 202 | else 203 | { 204 | isCorrupt = true; 205 | return false; 206 | } 207 | break; 208 | default: 209 | break; 210 | } 211 | } 212 | return true; 213 | } 214 | 215 | #endif -------------------------------------------------------------------------------- /examples/Arduino_Ethernet_SNMP_Manager/Arduino_Ethernet_SNMP_Manager.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | //************************************ 7 | //* Arduino Boards * 8 | //************************************ 9 | // This sketch and associated libraries, require quite a bit of memory. Thus are more suited to the Mega/Mega 2560. 10 | // It may be possible to create a stripped down version of the libraries with just the capabilities required 11 | // in order to work with boards with less program and dynamic memory. 12 | 13 | //************************************ 14 | //* Ethernet Info * 15 | //************************************ 16 | byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x64, 0x29}; //Ethernet shield's MAC 17 | 18 | //************************************ 19 | 20 | //************************************ 21 | //* SNMP Device Info * 22 | //************************************ 23 | IPAddress router(192, 168, 200, 1); 24 | const char *community = "public"; 25 | const int snmpVersion = 1; // SNMP Version 1 = 0, SNMP Version 2 = 1 26 | // OIDs 27 | const char *oidIfSpeedGauge = ".1.3.6.1.2.1.10.94.1.1.4.1.2.4"; // Gauge ADSL Down Sync Speed (interface 4) 28 | // const char *oidIfSpeedGauge = ".1.3.6.1.2.1.2.2.1.5.4"; // Gauge Regular ethernet interface ifSpeed.4 29 | const char *oidInOctetsCount32 = ".1.3.6.1.2.1.2.2.1.10.4"; // Counter32 ifInOctets.4 30 | const char *oidUptime = ".1.3.6.1.2.1.1.3.0"; // TimeTicks uptime (hundredths of seconds) 31 | //************************************ 32 | 33 | //************************************ 34 | //* Settings * 35 | //************************************ 36 | int pollInterval = 10000; // delay in milliseconds 37 | //************************************ 38 | 39 | //************************************ 40 | //* Initialise * 41 | //************************************ 42 | // Variables 43 | long unsigned int ifSpeedResponse = 0; 44 | long unsigned int inOctetsResponse = 0; 45 | unsigned int uptime = 0; 46 | unsigned int lastUptime = 0; 47 | unsigned long pollStart = 0; 48 | unsigned long intervalBetweenPolls = 0; 49 | float bandwidthInUtilPct = 0; 50 | long unsigned int lastInOctets = 0; 51 | // SNMP Objects 52 | EthernetUDP udp; // UDP object used to send and receive packets 53 | SNMPManager snmp = SNMPManager(community); // Starts an SNMPManager to listen to replies to get-requests 54 | SNMPGet snmpRequest = SNMPGet(community, snmpVersion); // Starts an SNMPGet instance to send requests 55 | // Blank callback pointer for each OID 56 | ValueCallback *callbackIfSpeed; 57 | ValueCallback *callbackInOctets; 58 | ValueCallback *callbackUptime; 59 | //************************************ 60 | 61 | //************************************ 62 | //* Function declarations * 63 | //************************************ 64 | void getSNMP(); 65 | void doSNMPCalculations(); 66 | void printVariableValues(); 67 | //************************************ 68 | 69 | void setup() 70 | { 71 | Serial.begin(38400); 72 | Ethernet.begin(mac); 73 | while (!Serial); 74 | Serial.print("Establishing network connection... "); 75 | 76 | if (Ethernet.begin(mac) == 0) { 77 | Serial.println("Failed to configure Ethernet using DHCP"); 78 | } 79 | else 80 | { 81 | Serial.print("IP address: "); 82 | Serial.println(Ethernet.localIP()); 83 | } 84 | 85 | snmp.setUDP(&udp); // give snmp a pointer to the UDP object 86 | snmp.begin(); // start the SNMP Manager 87 | 88 | // Get callbacks from creating a handler for each of the OID 89 | callbackIfSpeed = snmp.addGaugeHandler(router, oidIfSpeedGauge, &ifSpeedResponse); 90 | callbackInOctets= snmp.addCounter32Handler(router, oidInOctetsCount32, &inOctetsResponse); 91 | callbackUptime = snmp.addTimestampHandler(router, oidUptime, &uptime); 92 | } 93 | 94 | void loop() 95 | { 96 | // put your main code here, to run repeatedly: 97 | snmp.loop(); 98 | intervalBetweenPolls = millis() - pollStart; 99 | if (intervalBetweenPolls >= pollInterval) 100 | { 101 | pollStart += pollInterval; // this prevents drift in the delays 102 | getSNMP(); 103 | doSNMPCalculations(); // Do something with the data collected 104 | printVariableValues(); // Print the values to the serial console 105 | } 106 | } 107 | 108 | void doSNMPCalculations() 109 | { 110 | 111 | if (uptime == lastUptime) 112 | { 113 | Serial.println("Data not updated between polls"); 114 | return; 115 | } 116 | else if (uptime < lastUptime) 117 | { // Check if device has rebooted which will reset counters 118 | Serial.println("Uptime < lastUptime. Device restarted?"); 119 | } 120 | else 121 | { 122 | if (inOctetsResponse > 0 && ifSpeedResponse > 0 && lastInOctets > 0) 123 | { 124 | if (inOctetsResponse > lastInOctets) 125 | { 126 | bandwidthInUtilPct = ((float)((inOctetsResponse - lastInOctets) * 8) / (float)(ifSpeedResponse * ((uptime - lastUptime) / 100)) * 100); 127 | } 128 | else if (lastInOctets > inOctetsResponse) 129 | { 130 | Serial.println("inOctets Counter wrapped"); 131 | bandwidthInUtilPct = (((float)((4294967295 - lastInOctets) + inOctetsResponse) * 8) / (float)(ifSpeedResponse * ((uptime - lastUptime) / 100)) * 100); 132 | } 133 | } 134 | } 135 | // Update last samples 136 | lastUptime = uptime; 137 | lastInOctets = inOctetsResponse; 138 | } 139 | 140 | void getSNMP() 141 | { 142 | // Build a SNMP get-request add each OID to the request 143 | snmpRequest.addOIDPointer(callbackIfSpeed); 144 | snmpRequest.addOIDPointer(callbackInOctets); 145 | snmpRequest.addOIDPointer(callbackUptime); 146 | 147 | snmpRequest.setIP(Ethernet.localIP()); //IP of the Arduino 148 | snmpRequest.setUDP(&udp); 149 | snmpRequest.setRequestID(rand() % 5555); 150 | snmpRequest.sendTo(router); 151 | snmpRequest.clearOIDList(); 152 | } 153 | 154 | void printVariableValues() 155 | { 156 | Serial.print(F("Bandwidth In Utilisation %:")); 157 | Serial.println(bandwidthInUtilPct); 158 | Serial.print(F("ifSpeedResponse: ")); 159 | Serial.println(ifSpeedResponse); 160 | Serial.print(F("inOctetsResponse:")); 161 | Serial.println(inOctetsResponse); 162 | Serial.print(F("Uptime: ")); 163 | Serial.println(uptime); 164 | Serial.println(F("----------------------")); 165 | } -------------------------------------------------------------------------------- /examples/ESP_Multiple_SNMP_Device_Polling/ESP_Multiple_SNMP_Device_Polling.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include // ESP8266 Core WiFi Library 3 | #else 4 | #include // ESP32 Core WiFi Library 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | //************************************ 11 | //* Your WiFi info * 12 | //************************************ 13 | const char *ssid = "SSID"; 14 | const char *password = "PASSWORD"; 15 | //************************************ 16 | 17 | //************************************ 18 | //* SNMP Device Info * 19 | //************************************ 20 | const char *community = "public"; // SNMP Community string 21 | const int snmpVersion = 1; // Enum, SNMP Version 1 = 0, SNMP Version 2 = 1 22 | // OIDs 23 | const char *oidSysName = ".1.3.6.1.2.1.1.5.0"; // OctetString SysName 24 | const char *oidUptime = ".1.3.6.1.2.1.1.3.0"; // TimeTicks uptime (hundredths of seconds) 25 | //************************************ 26 | 27 | //************************************ 28 | //* Settings * 29 | //************************************ 30 | int devicePollInterval = 100; // delay in milliseconds 31 | int lastDeviceWaitPeriod = 5000; // delay in milliseconds 32 | #define LOWEROCTETLIMIT 1 // Set the lowest IP address to 1, .0 typically isn't used and isn't well supported. 33 | #define UPPEROCTETLIMIT 6 // Set the upper limit of the range of IPs to query 34 | //************************************ 35 | 36 | //************************************ 37 | //* Initialise * 38 | //************************************ 39 | // Structures 40 | struct device 41 | { 42 | IPAddress address; 43 | char name[50]; 44 | char *sysName = name; // StringHandler needs pointer to char* 45 | unsigned int uptime; 46 | }; // Structure for the device records 47 | 48 | // Global Variables 49 | struct device deviceRecords[UPPEROCTETLIMIT + 1]; // Array of device records. _1 as we're not using the 0 index in the array. 50 | int lastOctet = LOWEROCTETLIMIT; // Initialise last octet to lowest IP 51 | bool allDevicesPolled = false; // Flag to to indicate all devices have been sent SNMP requests. Note responses may not yet have arrived. 52 | // Initialise variables used for timer counters to zero. 53 | unsigned long devicePollStart = 0; 54 | unsigned long intervalBetweenDevicePolls = 0; 55 | unsigned long intervalBetweenLastDeviceReadyPolls = 0; 56 | unsigned long deviceReadyStart = 0; 57 | 58 | // SNMP Objects 59 | WiFiUDP udp; // UDP object used to send and receive packets 60 | SNMPManager snmp = SNMPManager(community); // Starts an SNMPManager to listen to replies to get-requests 61 | SNMPGet snmpRequest = SNMPGet(community, snmpVersion); // Starts an SNMPGet instance to send requests 62 | ValueCallback *callbackSysName; // Callback pointer for each OID 63 | ValueCallback *callbackUptime; // Callback pointer for each OID 64 | //************************************ 65 | 66 | //************************************ 67 | //* Function declarations * 68 | //************************************ 69 | void sendSNMPRequest(IPAddress, struct device *deviceRecord); 70 | int getNextOctet(int current); 71 | void printVariableValues(); 72 | //************************************ 73 | 74 | void setup() 75 | { 76 | Serial.begin(115200); 77 | WiFi.begin(ssid, password); 78 | Serial.println(""); 79 | // Wait for connection 80 | while (WiFi.status() != WL_CONNECTED) 81 | { 82 | delay(500); 83 | Serial.print("."); 84 | } 85 | Serial.printf("\nConnected to SSID: %s - IP Address: ", ssid); 86 | Serial.println(WiFi.localIP()); 87 | 88 | snmp.setUDP(&udp); // give snmp a pointer to the UDP object 89 | snmp.begin(); // start the SNMP Manager 90 | } 91 | 92 | void loop() 93 | { 94 | snmp.loop(); // Needs to be called frequently to process incoming SNMP responses. 95 | intervalBetweenDevicePolls = millis() - devicePollStart; // Timer for triggering per device Polls 96 | intervalBetweenLastDeviceReadyPolls = millis() - deviceReadyStart; // Timer to trigger end of polling all devices (allowing some time for final packets to be processed) 97 | if (allDevicesPolled) 98 | { 99 | if (intervalBetweenLastDeviceReadyPolls > lastDeviceWaitPeriod) // Waiting time after all devices polled complete? 100 | { 101 | deviceReadyStart += lastDeviceWaitPeriod; // This prevents drift in the delays 102 | printVariableValues(); // Print the values to the serial console 103 | allDevicesPolled = false; // Reset the flag 104 | } 105 | } 106 | else 107 | { 108 | if (intervalBetweenDevicePolls >= devicePollInterval) // Time to poll the next device? 109 | { 110 | devicePollStart += devicePollInterval; // This prevents drift in the delays 111 | IPAddress deviceIP(192, 168, 200, lastOctet); // Set IP address to be queried. Note: This simple example will only work with the last Octet of the address changing as this is used as a simple index for the device records. 112 | sendSNMPRequest(deviceIP, &deviceRecords[lastOctet]); // Function call to send SNMP requests to specified device. Values will be stored in the deviceRecords array when they are returned (async) 113 | lastOctet = getNextOctet(lastOctet); // Update to the next IP address to be queried 114 | } 115 | } 116 | } 117 | 118 | void sendSNMPRequest(IPAddress target, struct device *deviceRecord) 119 | { 120 | Serial.print("sendSNMPRequest - target: "); 121 | Serial.println(target); 122 | deviceRecord->address = target; 123 | // Get callbacks from creating a handler for each of the OID 124 | callbackSysName = snmp.addStringHandler(target, oidSysName, &deviceRecord->sysName); 125 | callbackUptime = snmp.addTimestampHandler(target, oidUptime, &deviceRecord->uptime); 126 | 127 | // Build a SNMP get-request add each OID to the request 128 | snmpRequest.addOIDPointer(callbackSysName); 129 | snmpRequest.addOIDPointer(callbackUptime); 130 | 131 | snmpRequest.setIP(WiFi.localIP()); // IP of the listening MCU 132 | snmpRequest.setUDP(&udp); 133 | snmpRequest.setRequestID(rand() % 5555); 134 | snmpRequest.sendTo(target); 135 | snmpRequest.clearOIDList(); 136 | } 137 | 138 | int getNextOctet(int current) 139 | { 140 | if (current == UPPEROCTETLIMIT) 141 | { 142 | allDevicesPolled = true; 143 | return LOWEROCTETLIMIT; 144 | } 145 | return current + 1; 146 | } 147 | 148 | void printVariableValues() 149 | { 150 | int i; 151 | for (i = LOWEROCTETLIMIT; i <= UPPEROCTETLIMIT; i++) 152 | { 153 | Serial.print("Address: "); 154 | Serial.print(deviceRecords[i].address); 155 | Serial.print(" - Name: "); 156 | Serial.print(deviceRecords[i].name); 157 | Serial.print(" - Uptime: "); 158 | Serial.print(deviceRecords[i].uptime); 159 | Serial.println(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /examples/ESP32_ESP8266_SNMP_Manager/ESP32_ESP8266_SNMP_Manager.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include // ESP8266 Core WiFi Library 3 | #else 4 | #include // ESP32 Core WiFi Library 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | //************************************ 11 | //* Your WiFi info * 12 | //************************************ 13 | const char *ssid = "SSID"; 14 | const char *password = "PASSWORD"; 15 | //************************************ 16 | 17 | //************************************ 18 | //* SNMP Device Info * 19 | //************************************ 20 | IPAddress router(192, 168, 200, 1); 21 | const char *community = "public"; 22 | const int snmpVersion = 1; // SNMP Version 1 = 0, SNMP Version 2 = 1 23 | // OIDs 24 | const char *oidIfSpeedGauge = ".1.3.6.1.2.1.10.94.1.1.4.1.2.4"; // Gauge ADSL Down Sync Speed (interface 4) 25 | // const char *oidIfSpeedGauge = ".1.3.6.1.2.1.2.2.1.5.4"; // Gauge Regular ethernet interface ifSpeed.4 26 | const char *oidInOctetsCount32 = ".1.3.6.1.2.1.2.2.1.10.4"; // Counter32 ifInOctets.4 27 | const char *oidServiceCountInt = ".1.3.6.1.2.1.1.7.0"; // Integer sysServices 28 | const char *oidSysName = ".1.3.6.1.2.1.1.5.0"; // OctetString SysName 29 | const char *oid64Counter = ".1.3.6.1.2.1.31.1.1.1.6.4"; // Counter64 64-bit ifInOctets.4 30 | const char *oidUptime = ".1.3.6.1.2.1.1.3.0"; // TimeTicks uptime (hundredths of seconds) 31 | //************************************ 32 | 33 | //************************************ 34 | //* Settings * 35 | //************************************ 36 | int pollInterval = 10000; // delay in milliseconds 37 | //************************************ 38 | 39 | //************************************ 40 | //* Initialise * 41 | //************************************ 42 | // Variables 43 | unsigned int ifSpeedResponse = 0; 44 | unsigned int inOctetsResponse = 0; 45 | int servicesResponse = 0; 46 | char sysName[50]; 47 | char *sysNameResponse = sysName; 48 | long long unsigned int hcCounter = 0; 49 | unsigned int uptime = 0; 50 | unsigned int lastUptime = 0; 51 | unsigned long pollStart = 0; 52 | unsigned long intervalBetweenPolls = 0; 53 | float bandwidthInUtilPct = 0; 54 | unsigned int lastInOctets = 0; 55 | // SNMP Objects 56 | WiFiUDP udp; // UDP object used to send and receive packets 57 | SNMPManager snmp = SNMPManager(community); // Starts an SNMPManager to listen to replies to get-requests 58 | SNMPGet snmpRequest = SNMPGet(community, snmpVersion); // Starts an SNMPGet instance to send requests 59 | // Blank callback pointer for each OID 60 | ValueCallback *callbackIfSpeed; 61 | ValueCallback *callbackInOctets; 62 | ValueCallback *callbackServices; 63 | ValueCallback *callbackSysName; 64 | ValueCallback *callback64Counter; 65 | ValueCallback *callbackUptime; 66 | //************************************ 67 | 68 | //************************************ 69 | //* Function declarations * 70 | //************************************ 71 | void getSNMP(); 72 | void doSNMPCalculations(); 73 | void printVariableValues(); 74 | //************************************ 75 | 76 | void setup() 77 | { 78 | Serial.begin(115200); 79 | WiFi.begin(ssid, password); 80 | Serial.println(""); 81 | // Wait for connection 82 | while (WiFi.status() != WL_CONNECTED) 83 | { 84 | delay(500); 85 | Serial.print("."); 86 | } 87 | Serial.println(""); 88 | Serial.print("Connected to SSID: "); 89 | Serial.println(ssid); 90 | Serial.print("IP address: "); 91 | Serial.println(WiFi.localIP()); 92 | 93 | snmp.setUDP(&udp); // give snmp a pointer to the UDP object 94 | snmp.begin(); // start the SNMP Manager 95 | 96 | // Get callbacks from creating a handler for each of the OID 97 | callbackIfSpeed = snmp.addGaugeHandler(router, oidIfSpeedGauge, &ifSpeedResponse); 98 | callbackInOctets= snmp.addCounter32Handler(router, oidInOctetsCount32, &inOctetsResponse); 99 | callbackServices = snmp.addIntegerHandler(router, oidServiceCountInt, &servicesResponse); 100 | callbackSysName = snmp.addStringHandler(router, oidSysName, &sysNameResponse); 101 | callback64Counter = snmp.addCounter64Handler(router, oid64Counter, &hcCounter); 102 | callbackUptime = snmp.addTimestampHandler(router, oidUptime, &uptime); 103 | } 104 | 105 | void loop() 106 | { 107 | // put your main code here, to run repeatedly: 108 | snmp.loop(); 109 | intervalBetweenPolls = millis() - pollStart; 110 | if (intervalBetweenPolls >= pollInterval) 111 | { 112 | pollStart += pollInterval; // this prevents drift in the delays 113 | getSNMP(); 114 | doSNMPCalculations(); // Do something with the data collected 115 | printVariableValues(); // Print the values to the serial console 116 | } 117 | } 118 | 119 | void doSNMPCalculations() 120 | { 121 | 122 | if (uptime == lastUptime) 123 | { 124 | Serial.println("Data not updated between polls"); 125 | return; 126 | } 127 | else if (uptime < lastUptime) 128 | { // Check if device has rebooted which will reset counters 129 | Serial.println("Uptime < lastUptime. Device restarted?"); 130 | } 131 | else 132 | { 133 | if (inOctetsResponse > 0 && ifSpeedResponse > 0 && lastInOctets > 0) 134 | { 135 | if (inOctetsResponse > lastInOctets) 136 | { 137 | bandwidthInUtilPct = ((float)((inOctetsResponse - lastInOctets) * 8) / (float)(ifSpeedResponse * ((uptime - lastUptime) / 100)) * 100); 138 | } 139 | else if (lastInOctets > inOctetsResponse) 140 | { 141 | Serial.println("inOctets Counter wrapped"); 142 | bandwidthInUtilPct = (((float)((4294967295 - lastInOctets) + inOctetsResponse) * 8) / (float)(ifSpeedResponse * ((uptime - lastUptime) / 100)) * 100); 143 | } 144 | } 145 | } 146 | // Update last samples 147 | lastUptime = uptime; 148 | lastInOctets = inOctetsResponse; 149 | } 150 | 151 | void getSNMP() 152 | { 153 | // Build a SNMP get-request add each OID to the request 154 | snmpRequest.addOIDPointer(callbackIfSpeed); 155 | snmpRequest.addOIDPointer(callbackInOctets); 156 | snmpRequest.addOIDPointer(callbackServices); 157 | snmpRequest.addOIDPointer(callbackSysName); 158 | snmpRequest.addOIDPointer(callback64Counter); 159 | snmpRequest.addOIDPointer(callbackUptime); 160 | 161 | snmpRequest.setIP(WiFi.localIP()); // IP of the listening MCU 162 | // snmpRequest.setPort(501); // Default is UDP port 161 for SNMP. But can be overriden if necessary. 163 | snmpRequest.setUDP(&udp); 164 | snmpRequest.setRequestID(rand() % 5555); 165 | snmpRequest.sendTo(router); 166 | snmpRequest.clearOIDList(); 167 | } 168 | 169 | void printVariableValues() 170 | { 171 | Serial.printf("Bandwidth In Utilisation %%: %.1f\n", bandwidthInUtilPct); 172 | Serial.printf("ifSpeedResponse: %d\n", ifSpeedResponse); 173 | Serial.printf("inOctetsResponse: %d\n", inOctetsResponse); 174 | Serial.printf("servicesResponse: %d\n", servicesResponse); 175 | Serial.printf("sysNameResponse: %s\n", sysNameResponse); 176 | Serial.printf("Uptime: %d\n", uptime); 177 | Serial.printf("HCCounter: %llu\n", hcCounter); 178 | Serial.println("----------------------"); 179 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SNMP Manager For ESP8266/ESP32/Arduino (and more) 2 | 3 | An SNMP Manager for ESP, Arduino and similar MCU. Providing simple SNMP Get-Request support for specified OIDs. 4 | 5 | The library supports: 6 | 7 | - SNMP Versions: 8 | - v1 (protocol version 0) 9 | - v2 (protocol version 1) 10 | - SNMP PDUs 11 | - GetRequest (sending query to a SNMP Agent for a specified OID) 12 | - GetResponse (Decoding the response to the SNMP GetRequest) 13 | - SNMP Data Types: 14 | - Integer (Arduino data type: int) 15 | - String (Arduino data type: char*) 16 | - Counter32 (Arduino data type: unsigned int) 17 | - Counter64 (Arduino data type: long long unsigned int) 18 | - Gauge32 (Arduino data type: unsigned int) 19 | - Timestamp (Arduino data type: unsigned int) 20 | 21 | If you find this useful, consider providing some support: 22 | 23 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/martinrowan) 24 | 25 | **Changelog**: [CHANGELOG.md](CHANGELOG.md) 26 | 27 | ## Usage 28 | 29 | ### SNMPManager 30 | 31 | An SNMPManager object is created for listening (on UDP port 162) to and parsing the SNMP GetResponses. This is initialised with the SNMP community string. 32 | 33 | ```cpp 34 | SNMPManager snmpManager = SNMPManager("public"); 35 | ``` 36 | 37 | ### SNMPGet 38 | 39 | An SNMPGet object is created to make SNMP GetRequest calls (from UDP port 161 (by default)). This is initialised with the SNMP community string and an SNMP version. Note SNMPv1 = 0, SNMPv2 = 1. The port scan be changed if required using `setPort()` 40 | 41 | ```cpp 42 | SNMPGet snmpRequest = SNMPGet("public", 1); 43 | ``` 44 | 45 | ### Handlers and Callbacks 46 | 47 | The handlers and callbacks for receiving the incoming SNMP GetResponse are configured in `setup()` 48 | 49 | ```cpp 50 | ValueCallback *callbackSysName; // Blank Callback for each OID 51 | void setup() 52 | { 53 | IPAddress target(192, 168, 200, 187); 54 | callbackSysName = snmpManager.addStringHandler(target, ".1.3.6.1.2.1.1.5.0", &sysNameResponse); // Callback for SysName for target host 55 | } 56 | ``` 57 | 58 | Within the main program `snmpManager.loop()` needs to be called frequently to capture and parse incoming GetResponses. GetRequests can be sent as needed, though typically a significantly lower rate than the main loop. 59 | 60 | ```cpp 61 | void loop() 62 | { 63 | snmpManager.loop(); // Call frequently 64 | getSNMP(); 65 | } 66 | void getSNMP() 67 | { 68 | // Check to see if it is time to send an SNMP request. 69 | if ((timeLast + pollInterval) <= millis()) 70 | { 71 | // Send SNMP Get request 72 | snmpRequest.addOIDPointer(callbackSysName); 73 | snmpRequest.setIP(WiFi.localIP()); //IP of the arduino 74 | snmpRequest.setUDP(&udp); 75 | snmpRequest.setRequestID(rand() % 5555); 76 | snmpRequest.sendTo(router); 77 | snmpRequest.clearOIDList(); 78 | // Display response (first call might be empty) 79 | Serial.print("sysNameResponse: "); 80 | Serial.println(sysNameResponse); 81 | Serial.println("----------------------"); 82 | 83 | timeLast = millis(); 84 | } 85 | } 86 | ``` 87 | 88 | You can add multiple OID to be queried in a single request by calling `snmpRequest.addOIDPointer(another_callback);` This approach ensures all the requested OID are returned in the same response. Though I expect there are limits on the maximum packet sizes, so some experimentation may be required with large numbers of OID. 89 | 90 | ## Working With SNMP Data 91 | 92 | ### Time Based Measurements 93 | 94 | It's important to note that even if you make GetRequests every _n_ seconds, that the response may not arrive in the allotted time period. SNMP responses are often deprioritised by devices when under load. As such your poll interval shouldn't be used for any calculations of time, instead using the devices uptime counter will show the time elapsed between data collections. For example if I want to calculate the bandwidth utilisation of my ADSL connection, then we need to look calculate: `Utilisation = Amount of Data in time period / Max Possible data in time period` 95 | 96 | Which can be performed with the following: 97 | 98 | ```cpp 99 | // Note: Calculation will be incorrect if inOctets counter has wrapped. 100 | bandwidthUtilisationPercent = ((float)((inOctets - lastInOctets) * 8) / (float)(downSpeed * ((uptime - lastUptime) / 100)) * 100); 101 | ``` 102 | 103 | What does this mean? Well lets explain the variables: 104 | 105 | - inOctets: (Counter32) ifInOctets (.1.3.6.1.2.1.2.2.1.10.4) - Amount of bytes received on the specified interface, 4 in this example. 106 | - lastInOctets: Stores the inOctets from the previous poll. 107 | - downSpeed: (Gauge) - The maximum possible download speed in bps (bits per second). This can be measured value from your own speed test, or you might query the interface speed, or in the of (A/V)DSL you might query the sync speed adslAtucChanCurrTxRate (.1.3.6.1.2.1.10.94.1.1.4.1.2.4) again for interface 4. 108 | - uptime: (TimeTicks) - SysUpTime (.1.3.6.1.2.1.1.3.0) - The time in hundredths of seconds since the device was last reinitialised. 109 | - lastUptime: Stores the upTime from the previous poll. 110 | 111 | This can be broken down as: 112 | 113 | - `((inOctets - lastInOctets) * 8)` calculates the delta in data received and converts Bytes to Bits by multiplying by 8. 114 | - `((uptime - lastUptime) / 100)` calculates the time between two samples and converts it to seconds. 115 | - `(downSpeed * ((uptime - lastUptime) / 100))` We calculate how much data could have been theoretically received in the elapsed time for a given maximum download speed. 116 | 117 | ### Counters 118 | 119 | When working with SNMP Counters (COUNTER32 or COUNTER64) they can only be used for measuring a change between two values. A device reboot may reset the counter such that the calculating a delta would give an incorrect reading. Or depending on the sample period and the rate of change, the counter can wrap. 120 | 121 | #### Counters Wrapping 122 | 123 | To compensate for wrapping, you can: 124 | 125 | - Poll more frequently, giving less time for the counter to have wrapped. But doing so increased the load on the SNMP agent device and the manager. 126 | - If the device supports them, then High Capacity (HC) 64bit counters can be used. Note SNMPv1 doesn't support COUNTER64, this is only available in SNMPv2 and later. 127 | - If the counter has wrapped, you could assert it has only wrapped once in the sample period. For the example for Bandwidth utilisation above we'd need to adjust the formula to correct compensation if we detect it has wrapped. To calculate the delta in traffic being measured with a COUNTER32 which has is an unsigned integer (maximum value: 4294967295) gives us: `(((4294967295 - lastInOctets) + inOctets) * 8)` 128 | 129 | ```cpp 130 | if (inOctets > lastInOctets) 131 | { 132 | // Note: Calculation will be incorrect if inOctets counter has wrapped. 133 | bandwidthUtilisationPercent = ((float)((inOctets - lastInOctets) * 8) / (float)(downSpeed * ((uptime - lastUptime) / 100)) * 100); 134 | } 135 | else if (lastInOctets > inOctets) 136 | { 137 | // This handles 32bit counters wrapping a maximum of one time. 138 | bandwidthUtilisationPercent = (((float)((4294967295 - lastInOctets) + inOctets) * 8) / (float)(downSpeed * ((uptime - lastUptime) / 100)) * 100); 139 | } 140 | ``` 141 | 142 | #### Device Reset 143 | 144 | To compensate for device reset: 145 | 146 | - Monitor SysUptime and if is lower than the previous value, then assume the device has restarted, don't process the data, just store the new counter values and await the next poll to be able to calculate the difference. 147 | 148 | ### Strings 149 | 150 | SNMP can be used to query strings, however long strings lead to larger packet sizes needing larger buffers and increased memory usage. The ESP8266 appears to have a bug in the WiFi or UDP protocol support, leading to a maximum UDP packet size that can be received being 1024 bytes. As there are can be multiple OID responses in a single packet along with headers etc, this will reduce the maximum string size that can be received. Reading strings in to a character arrays can use a significant amount of memory, which may not be available on some MCUs. As such query strings should will likely need to be limited. 151 | 152 | ## Troubleshooting 153 | 154 | ### Additional Logging 155 | 156 | - Debug logging: add `#define DEBUG` before the library include `#include ` 157 | - Additional ASN.1 debug logging: add `#define DEBUG_BER` before the library include `#include ` 158 | 159 | ### Suppress Errors 160 | 161 | - Suppress errors when SNMP packet <= 30 bytes: add `#define SUPPRESS_ERROR_SHORT_PACKET` before `#include ` 162 | - Suppress SNMP payload parsing error: add `#define SUPPRESS_ERROR_FAILED_PARSE` before `#include ` 163 | 164 | ## Examples 165 | 166 | The examples folder contains an SNMP GetRequest example for each of the data types. Note that the OID will need to be adapted the device you are querying. To understand what OID your device supports and the data type of each one, I'd recommend walking to the device with standard SNMP tools: 167 | 168 | - [iReasoning MIB Browser](https://www.ireasoning.com/mibbrowser.shtml) 169 | - Using [net-snmp](http://www.net-snmp.org/) snmpwalk. A command line tool available for various OS. Basic introductory usage information can be found [in this article](https://www.comparitech.com/net-admin/snmpwalk-examples-windows-linux/) 170 | 171 | ### Examples folder contents 172 | 173 | - [ESP32_ESP8266_SNMP_Manager.ino](examples/ESP32_ESP8266_SNMP_Manager/ESP32_ESP8266_SNMP_Manager.ino) - ESP32/ESP2866 boards 174 | - [ESP_Multiple_SNMP_Device_Polling.ino](examples/ESP_Multiple_SNMP_Device_Polling/ESP_Multiple_SNMP_Device_Polling.ino) - ESP32/ESP8266 boards querying multiple devices and storing results in a device record array 175 | - [Arduino_Ethernet_SNMP_Manager.ino](examples/Arduino_Ethernet_SNMP_Manager/Arduino_Ethernet_SNMP_Manager.ino) - Arduino Mega with Ethernet Shield 176 | 177 | ## Tested Devices 178 | 179 | The following devices have been confirmed to work with this library (these are affiliate links that help support my work): 180 | 181 | - WeMos D1 Mini - ESP8266 - [Amazon UK](https://amzn.to/3z6rQBt) [Amazon US](https://amzn.to/3AY4aBE) 182 | - ESP32S Dev Module - [Amazon UK](https://amzn.to/2TAqWZJ) [Amazon US](https://amzn.to/3PgUZAx) 183 | 184 | ## Projects using this library 185 | 186 | I'd love to hear about projects that find this library useful. 187 | 188 | - [Broadband Utilisation Display](https://github.com/shortbloke/Broadband_Usage_Display) - An LED display showing broadband upstream and downstream utilisation. 189 | - [Dekatron-speed](https://github.com/elegantalchemist/dekatron-speed) - Uses a Dekatron (1950s era neon counting tube) spinning based on broadband utilisation rate. 190 | - [Wio Terminal - Router Graph LCD](https://github.com/dbzoo/wio_terminal/tree/master/router_graph_lcd) - Uses the SeeedStudio [Wio Terminal](https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/) to plot traffic received and transmitted rates on the integrated display. 191 | - [Wio Terminal - Router Stats LCD](https://github.com/dbzoo/wio_terminal/tree/master/router_stats_lcd) - Uses the SeeedStudio [Wio Terminal](https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/) to show the current receive and transmit rates on the integrated display. 192 | 193 | ## Acknowledgements 194 | 195 | This project a derived from an [SNMP Agent project](https://github.com/fusionps/Arduino_SNMP). With Manager functionality adapted from work by [Niich's fork](https://github.com/Niich/Arduino_SNMP). 196 | -------------------------------------------------------------------------------- /src/Arduino_SNMP_Manager.h: -------------------------------------------------------------------------------- 1 | // #define DEBUG_BER 2 | 3 | #ifndef SNMPManager_h 4 | #define SNMPManager_h 5 | 6 | #ifndef UDP_TX_PACKET_MAX_SIZE 7 | #define UDP_TX_PACKET_MAX_SIZE 484 8 | #endif 9 | 10 | #ifndef SNMP_PACKET_LENGTH 11 | #if defined(ESP32) 12 | #define SNMP_PACKET_LENGTH 1500 // This will limit the size of packets which can be handled. 13 | #else 14 | #define SNMP_PACKET_LENGTH 512 // This value may need to be made smaller for lower memory devices. 15 | #endif 16 | #endif 17 | 18 | #define MIN(X, Y) ((X < Y) ? X : Y) 19 | 20 | #include 21 | 22 | #include "BER.h" 23 | #include "VarBinds.h" 24 | 25 | class ValueCallback 26 | { 27 | public: 28 | ValueCallback(ASN_TYPE atype) : type(atype){}; 29 | IPAddress ip; 30 | char *OID; 31 | ASN_TYPE type; 32 | bool overwritePrefix = false; 33 | }; 34 | 35 | class IntegerCallback : public ValueCallback 36 | { 37 | public: 38 | IntegerCallback() : ValueCallback(INTEGER){}; 39 | int *value; 40 | bool isFloat = false; 41 | }; 42 | 43 | class TimestampCallback : public ValueCallback 44 | { 45 | public: 46 | TimestampCallback() : ValueCallback(TIMESTAMP){}; 47 | uint32_t *value; 48 | }; 49 | 50 | class StringCallback : public ValueCallback 51 | { 52 | public: 53 | StringCallback() : ValueCallback(STRING){}; 54 | char **value; 55 | }; 56 | 57 | class OIDCallback : public ValueCallback 58 | { 59 | public: 60 | OIDCallback() : ValueCallback(ASN_TYPE::OID){}; 61 | char *value; 62 | }; 63 | 64 | class Counter32Callback : public ValueCallback 65 | { 66 | public: 67 | Counter32Callback() : ValueCallback(ASN_TYPE::COUNTER32){}; 68 | uint32_t *value; 69 | }; 70 | 71 | class Gauge32Callback : public ValueCallback 72 | { 73 | public: 74 | Gauge32Callback() : ValueCallback(ASN_TYPE::GAUGE32){}; 75 | uint32_t *value; 76 | }; 77 | 78 | class Counter64Callback : public ValueCallback 79 | { 80 | public: 81 | Counter64Callback() : ValueCallback(ASN_TYPE::COUNTER64){}; 82 | uint64_t *value; 83 | }; 84 | 85 | typedef struct ValueCallbackList 86 | { 87 | ~ValueCallbackList() 88 | { 89 | delete next; 90 | } 91 | ValueCallback *value; 92 | struct ValueCallbackList *next = 0; 93 | } ValueCallbacks; 94 | 95 | #include "SNMPGet.h" 96 | #include "SNMPGetResponse.h" 97 | 98 | class SNMPManager 99 | { 100 | public: 101 | SNMPManager(){}; 102 | SNMPManager(const char *community) : _community(community){}; 103 | const char *_community; 104 | 105 | ValueCallbacks *callbacks = new ValueCallbacks(); 106 | ValueCallbacks *callbacksCursor = callbacks; 107 | ValueCallback *findCallback(IPAddress ip, const char *oid); // Find based on responding host IP address and OID 108 | ValueCallback *addFloatHandler(IPAddress ip, const char *oid, float *value); 109 | ValueCallback *addStringHandler(IPAddress ip, const char *, char **); // passing in a pointer to a char* 110 | ValueCallback *addIntegerHandler(IPAddress ip, const char *oid, int *value); 111 | ValueCallback *addTimestampHandler(IPAddress ip, const char *oid, uint32_t *value); 112 | ValueCallback *addOIDHandler(IPAddress ip, const char *oid, char *value); 113 | ValueCallback *addCounter64Handler(IPAddress ip, const char *oid, uint64_t *value); 114 | ValueCallback *addCounter32Handler(IPAddress ip, const char *oid, uint32_t *value); 115 | ValueCallback *addGaugeHandler(IPAddress ip, const char *oid, uint32_t *value); 116 | 117 | void setUDP(UDP *udp); 118 | bool begin(); 119 | bool loop(); 120 | bool testParsePacket(String testPacket); 121 | char OIDBuf[MAX_OID_LENGTH]; 122 | UDP *_udp; 123 | void addHandler(ValueCallback *callback); 124 | 125 | private: 126 | unsigned char _packetBuffer[SNMP_PACKET_LENGTH * 3]; 127 | bool inline receivePacket(int length); 128 | bool parsePacket(); 129 | void printPacket(int len); 130 | }; 131 | 132 | void SNMPManager::setUDP(UDP *udp) 133 | { 134 | if (_udp) 135 | { 136 | _udp->stop(); 137 | } 138 | _udp = udp; 139 | this->begin(); 140 | } 141 | 142 | bool SNMPManager::begin() 143 | { 144 | if (!_udp) 145 | return false; 146 | _udp->begin(162); 147 | return true; 148 | } 149 | 150 | bool SNMPManager::loop() 151 | { 152 | if (!_udp) 153 | { 154 | return false; 155 | } 156 | receivePacket(_udp->parsePacket()); 157 | return true; 158 | } 159 | 160 | void SNMPManager::printPacket(int len) 161 | { 162 | Serial.print("[DEBUG] packet: "); 163 | for (int i = 0; i < len; i++) 164 | { 165 | Serial.printf("%02x ", _packetBuffer[i]); 166 | } 167 | Serial.println(); 168 | } 169 | 170 | bool SNMPManager::testParsePacket(String testPacket) 171 | { 172 | // Function to test sample packet, each byte to be separated with a space: 173 | // e.g. "32 02 01 01 04 06 70 75 62 6c 69 63 a2 25 02 02 0c 01 02 01 00 02 c1 00 30 19 30 17 06 11 2b 06 01 04 01 81 9e 16 02 03 01 01 01 02 03 01 00 02 02 14 9f"; 174 | int len = testPacket.length() + 1; 175 | memset(_packetBuffer, 0, SNMP_PACKET_LENGTH * 3); 176 | char charArrayPacket[len]; 177 | testPacket.toCharArray(charArrayPacket, len); 178 | // split charArray at each ' ' and convert to uint8_t 179 | char *p = strtok(charArrayPacket, " "); 180 | int i = 0; 181 | while (p != NULL) 182 | { 183 | _packetBuffer[i++] = strtoul(p, NULL, 16); 184 | p = strtok(NULL, " "); 185 | } 186 | _packetBuffer[i] = 0; // null terminate the buffer 187 | #ifdef DEBUG 188 | printPacket(len); 189 | #endif 190 | 191 | return parsePacket(); 192 | } 193 | 194 | bool inline SNMPManager::receivePacket(int packetLength) 195 | { 196 | if (!packetLength) 197 | { 198 | return false; 199 | } 200 | #ifdef DEBUG 201 | Serial.print(F("[DEBUG] Packet Length: ")); 202 | Serial.print(packetLength); 203 | Serial.print(F(" From Address: ")); 204 | Serial.println(_udp->remoteIP()); 205 | #endif 206 | 207 | memset(_packetBuffer, 0, SNMP_PACKET_LENGTH * 3); 208 | int len = packetLength; 209 | _udp->read(_packetBuffer, MIN(len, SNMP_PACKET_LENGTH)); 210 | _udp->flush(); 211 | _packetBuffer[len] = 0; // null terminate the buffer 212 | 213 | #ifdef DEBUG 214 | printPacket(len); 215 | #endif 216 | 217 | return parsePacket(); 218 | } 219 | 220 | bool SNMPManager::parsePacket() 221 | { 222 | SNMPGetResponse *snmpgetresponse = new SNMPGetResponse(); 223 | if (snmpgetresponse->parseFrom(_packetBuffer)) 224 | { 225 | if (snmpgetresponse->requestType == GetResponsePDU) 226 | { 227 | if (!(snmpgetresponse->version != 1 || snmpgetresponse->version != 2) || strcmp(_community, snmpgetresponse->communityString) != 0) 228 | { 229 | Serial.print(F("Invalid community or version - Community: ")); 230 | Serial.print(snmpgetresponse->communityString); 231 | Serial.print(F(" - Version: ")); 232 | Serial.println(snmpgetresponse->version); 233 | delete snmpgetresponse; 234 | return false; 235 | } 236 | #ifdef DEBUG 237 | Serial.print(F("[DEBUG] Community: ")); 238 | Serial.println(snmpgetresponse->communityString); 239 | Serial.print(F("[DEBUG] SNMP Version: ")); 240 | Serial.println(snmpgetresponse->version); 241 | #endif 242 | int varBindIndex = 1; 243 | snmpgetresponse->varBindsCursor = snmpgetresponse->varBinds; 244 | while (true) 245 | { 246 | char *responseOID = snmpgetresponse->varBindsCursor->value->oid->_value; 247 | IPAddress responseIP = _udp->remoteIP(); 248 | ASN_TYPE responseType = snmpgetresponse->varBindsCursor->value->type; 249 | BER_CONTAINER *responseContainer = snmpgetresponse->varBindsCursor->value->value; 250 | #ifdef DEBUG 251 | Serial.print(F("[DEBUG] Response from: ")); 252 | Serial.print(responseIP); 253 | Serial.print(F(" - OID: ")); 254 | Serial.println(responseOID); 255 | #endif 256 | ValueCallback *callback = findCallback(responseIP, responseOID); 257 | if (!callback) 258 | { 259 | Serial.print(F("Matching callback not found for received SNMP response. Response OID: ")); 260 | Serial.print(responseOID); 261 | Serial.print(F(" - From IP Address: ")); 262 | Serial.println(responseIP); 263 | delete snmpgetresponse; 264 | return false; 265 | } 266 | ASN_TYPE callbackType = callback->type; 267 | if (callbackType != responseType) 268 | { 269 | switch (responseType) 270 | { 271 | case NOSUCHOBJECT: 272 | { 273 | Serial.print(F("No such object: ")); 274 | } 275 | break; 276 | case NOSUCHINSTANCE: 277 | { 278 | Serial.print(F("No such instance: ")); 279 | } 280 | break; 281 | case ENDOFMIBVIEW: 282 | { 283 | Serial.print(F("End of MIB view when calling: ")); 284 | } 285 | break; 286 | default: 287 | { 288 | Serial.print(F("Incorrect Callback type. Expected: ")); 289 | Serial.print(callbackType); 290 | Serial.print(F(" Received: ")); 291 | Serial.print(responseType); 292 | Serial.print(F(" - When calling: ")); 293 | } 294 | } 295 | Serial.println(responseOID); 296 | delete snmpgetresponse; 297 | snmpgetresponse = 0; 298 | return false; 299 | } 300 | switch (callbackType) 301 | { 302 | case STRING: 303 | { 304 | #ifdef DEBUG 305 | Serial.println("[DEBUG] Type: String"); 306 | #endif 307 | 308 | // Note: Requires that the size of the variable used to store the response is big enough. 309 | // Otherwise move responsibility for the creation of the variable to store the value here, but this would put the onus on the caller to free and reset to null. 310 | //*((StringCallback *)callback)->value = (char *)malloc(64); // Allocate memory for string, caller will need to free. Malloc updated to incoming message size. 311 | strncpy(*((StringCallback *)callback)->value, ((OctetType *)responseContainer)->_value, strlen(((OctetType *)responseContainer)->_value)); 312 | OctetType *value = new OctetType(*((StringCallback *)callback)->value); 313 | delete value; 314 | } 315 | break; 316 | case INTEGER: 317 | { 318 | #ifdef DEBUG 319 | Serial.println("[DEBUG] Type: Integer"); 320 | #endif 321 | IntegerType *value = new IntegerType(); 322 | if (!((IntegerCallback *)callback)->isFloat) 323 | { 324 | *(((IntegerCallback *)callback)->value) = ((IntegerType *)responseContainer)->_value; 325 | value->_value = *(((IntegerCallback *)callback)->value); 326 | } 327 | else 328 | { 329 | *(((IntegerCallback *)callback)->value) = (float)(((IntegerType *)responseContainer)->_value / 10); 330 | value->_value = *(float *)(((IntegerCallback *)callback)->value) * 10; 331 | } 332 | delete value; 333 | } 334 | break; 335 | case COUNTER32: 336 | { 337 | #ifdef DEBUG 338 | Serial.println("[DEBUG] Type: Counter32"); 339 | #endif 340 | Counter32 *value = new Counter32(); 341 | *(((Counter32Callback *)callback)->value) = ((Counter32 *)responseContainer)->_value; 342 | value->_value = *(((Counter32Callback *)callback)->value); 343 | delete value; 344 | } 345 | break; 346 | case COUNTER64: 347 | { 348 | #ifdef DEBUG 349 | Serial.println("[DEBUG] Type: Counter64"); 350 | #endif 351 | Counter64 *value = new Counter64(); 352 | *(((Counter64Callback *)callback)->value) = ((Counter64 *)responseContainer)->_value; 353 | value->_value = *(((Counter64Callback *)callback)->value); 354 | delete value; 355 | } 356 | break; 357 | case GAUGE32: 358 | { 359 | #ifdef DEBUG 360 | Serial.println("[DEBUG] Type: Gauge32"); 361 | #endif 362 | Gauge *value = new Gauge(); 363 | *(((Gauge32Callback *)callback)->value) = ((Gauge *)responseContainer)->_value; 364 | value->_value = *(((Gauge32Callback *)callback)->value); 365 | delete value; 366 | } 367 | break; 368 | case TIMESTAMP: 369 | { 370 | #ifdef DEBUG 371 | Serial.println("[DEBUG] Type: TimeStamp"); 372 | #endif 373 | TimestampType *value = new TimestampType(); 374 | *(((TimestampCallback *)callback)->value) = ((TimestampType *)responseContainer)->_value; 375 | value->_value = *(((TimestampCallback *)callback)->value); 376 | delete value; 377 | } 378 | break; 379 | default: 380 | { 381 | #ifdef DEBUG 382 | Serial.print(F("[DEBUG] Unsupported Type: ")); 383 | Serial.print(callbackType); 384 | #endif 385 | } 386 | break; 387 | } 388 | snmpgetresponse->varBindsCursor = snmpgetresponse->varBindsCursor->next; 389 | if (!snmpgetresponse->varBindsCursor->value) 390 | { 391 | break; 392 | } 393 | varBindIndex++; 394 | } // End while 395 | } // End if GetResponsePDU 396 | } 397 | else 398 | { 399 | #ifndef SUPPRESS_ERROR_FAILED_PARSE 400 | Serial.println(F("SNMPGETRESPONSE: FAILED TO PARSE")); 401 | #endif 402 | delete snmpgetresponse; 403 | return false; 404 | } 405 | #ifdef DEBUG 406 | Serial.println(F("[DEBUG] SNMPGETRESPONSE: SUCCESS")); 407 | #endif 408 | delete snmpgetresponse; 409 | return true; 410 | } 411 | 412 | ValueCallback *SNMPManager::findCallback(IPAddress ip, const char *oid) 413 | { 414 | callbacksCursor = callbacks; 415 | 416 | if (callbacksCursor->value) 417 | { 418 | while (true) 419 | { 420 | memset(OIDBuf, 0, MAX_OID_LENGTH); 421 | strcat(OIDBuf, callbacksCursor->value->OID); 422 | if ((strcmp(OIDBuf, oid) == 0) && (callbacksCursor->value->ip == ip)) 423 | { 424 | // Found 425 | #ifdef DEBUG 426 | Serial.println(F("[DEBUG] Found callback with matching IP")); 427 | #endif 428 | return callbacksCursor->value; 429 | } 430 | if (callbacksCursor->next) 431 | { 432 | callbacksCursor = callbacksCursor->next; 433 | } 434 | else 435 | { 436 | #ifdef DEBUG 437 | Serial.println(F("[DEBUG] No matching callback found.")); 438 | #endif 439 | break; 440 | } 441 | } 442 | } 443 | return 0; 444 | } 445 | 446 | ValueCallback *SNMPManager::addStringHandler(IPAddress ip, const char *oid, char **value) 447 | { 448 | ValueCallback *callback = new StringCallback(); 449 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 450 | strcpy(callback->OID, oid); 451 | ((StringCallback *)callback)->value = value; 452 | callback->ip = ip; 453 | addHandler(callback); 454 | return callback; 455 | } 456 | 457 | ValueCallback *SNMPManager::addIntegerHandler(IPAddress ip, const char *oid, int *value) 458 | { 459 | ValueCallback *callback = new IntegerCallback(); 460 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 461 | strcpy(callback->OID, oid); 462 | ((IntegerCallback *)callback)->value = value; 463 | ((IntegerCallback *)callback)->isFloat = false; 464 | callback->ip = ip; 465 | addHandler(callback); 466 | return callback; 467 | } 468 | 469 | ValueCallback *SNMPManager::addFloatHandler(IPAddress ip, const char *oid, float *value) 470 | { 471 | ValueCallback *callback = new IntegerCallback(); 472 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 473 | strcpy(callback->OID, oid); 474 | ((IntegerCallback *)callback)->value = (int *)value; 475 | ((IntegerCallback *)callback)->isFloat = true; 476 | callback->ip = ip; 477 | addHandler(callback); 478 | return callback; 479 | } 480 | 481 | ValueCallback *SNMPManager::addTimestampHandler(IPAddress ip, const char *oid, uint32_t *value) 482 | { 483 | ValueCallback *callback = new TimestampCallback(); 484 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 485 | strcpy(callback->OID, oid); 486 | ((TimestampCallback *)callback)->value = value; 487 | callback->ip = ip; 488 | addHandler(callback); 489 | return callback; 490 | } 491 | 492 | ValueCallback *SNMPManager::addOIDHandler(IPAddress ip, const char *oid, char *value) 493 | { 494 | ValueCallback *callback = new OIDCallback(); 495 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 496 | ((OIDCallback *)callback)->value = value; 497 | callback->ip = ip; 498 | addHandler(callback); 499 | return callback; 500 | } 501 | 502 | ValueCallback *SNMPManager::addCounter64Handler(IPAddress ip, const char *oid, uint64_t *value) 503 | { 504 | ValueCallback *callback = new Counter64Callback(); 505 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 506 | strcpy(callback->OID, oid); 507 | ((Counter64Callback *)callback)->value = value; 508 | callback->ip = ip; 509 | addHandler(callback); 510 | return callback; 511 | } 512 | 513 | ValueCallback *SNMPManager::addCounter32Handler(IPAddress ip, const char *oid, uint32_t *value) 514 | { 515 | ValueCallback *callback = new Counter32Callback(); 516 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 517 | strcpy(callback->OID, oid); 518 | ((Counter32Callback *)callback)->value = value; 519 | callback->ip = ip; 520 | addHandler(callback); 521 | return callback; 522 | } 523 | 524 | ValueCallback *SNMPManager::addGaugeHandler(IPAddress ip, const char *oid, uint32_t *value) 525 | { 526 | ValueCallback *callback = new Gauge32Callback(); 527 | callback->OID = (char *)malloc((sizeof(char) * strlen(oid)) + 1); 528 | strcpy(callback->OID, oid); 529 | ((Gauge32Callback *)callback)->value = value; 530 | callback->ip = ip; 531 | addHandler(callback); 532 | return callback; 533 | } 534 | 535 | void SNMPManager::addHandler(ValueCallback *callback) 536 | { 537 | callbacksCursor = callbacks; 538 | if (callbacksCursor->value) 539 | { 540 | while (callbacksCursor->next != 0) 541 | { 542 | callbacksCursor = callbacksCursor->next; 543 | } 544 | callbacksCursor->next = new ValueCallbacks(); 545 | callbacksCursor = callbacksCursor->next; 546 | callbacksCursor->value = callback; 547 | callbacksCursor->next = 0; 548 | } 549 | else 550 | callbacks->value = callback; 551 | } 552 | 553 | #endif 554 | -------------------------------------------------------------------------------- /src/BER.h: -------------------------------------------------------------------------------- 1 | #ifndef BER_h 2 | #define BER_h 3 | 4 | #ifndef SNMP_OCTETSTRING_MAX_LENGTH 5 | #define SNMP_OCTETSTRING_MAX_LENGTH 1024 6 | #endif 7 | 8 | #ifndef MAX_OID_LENGTH 9 | #define MAX_OID_LENGTH 128 10 | #endif 11 | 12 | #include 13 | #include 14 | 15 | typedef enum ASN_TYPE_WITH_VALUE 16 | { 17 | // Primitives 18 | INTEGER = 0x02, 19 | STRING = 0x04, 20 | NULLTYPE = 0x05, 21 | OID = 0x06, 22 | 23 | // Complex 24 | STRUCTURE = 0x30, 25 | NETWORK_ADDRESS = 0x40, 26 | COUNTER32 = 0x41, 27 | GAUGE32 = 0x42, // UNSIGNED32 28 | TIMESTAMP = 0x43, 29 | OPAQUE = 0x44, 30 | COUNTER64 = 0x46, 31 | 32 | NOSUCHOBJECT = 0x80, 33 | NOSUCHINSTANCE = 0x81, 34 | ENDOFMIBVIEW = 0x82, 35 | 36 | GetRequestPDU = 0xA0, 37 | GetNextRequestPDU = 0xA1, 38 | GetResponsePDU = 0xA2, 39 | SetRequestPDU = 0xA3, 40 | TrapPDU = 0xA4, 41 | GetBulkRequestPDU = 0xA5, 42 | Trapv2PDU = 0xA7 43 | } ASN_TYPE; 44 | 45 | // Primitive types inherits straight off the container, complex come off complexType. 46 | // All primitives have to serialise themselves (type, length, data), to be put straight into the packet. 47 | // For deserialising from the parent container we check the type, then create an object of that type and call deSerialise, 48 | // passing in the data, which pulls it out and saves it. 49 | // If complexType, first split up its children into separate BERs, then passes the child with it's data using the same process. 50 | // Complex types have a linked list of BER_CONTAINERS to hold its' children. 51 | 52 | class BER_CONTAINER 53 | { 54 | public: 55 | BER_CONTAINER(bool isPrimitive, ASN_TYPE type) : _isPrimitive(isPrimitive), _type(type){}; 56 | virtual ~BER_CONTAINER(){}; 57 | bool _isPrimitive; 58 | ASN_TYPE _type; 59 | unsigned short _length; 60 | virtual int serialise(unsigned char *buf) = 0; 61 | virtual bool fromBuffer(unsigned char *buf) = 0; 62 | virtual int getLength() = 0; 63 | }; 64 | 65 | class NetworkAddress : public BER_CONTAINER 66 | { 67 | public: 68 | NetworkAddress() : BER_CONTAINER(true, NETWORK_ADDRESS){}; 69 | NetworkAddress(IPAddress ip) : _value(ip), BER_CONTAINER(true, NETWORK_ADDRESS){}; 70 | ~NetworkAddress(){}; 71 | IPAddress _value; 72 | int serialise(unsigned char *buf) 73 | { 74 | #ifdef DEBUG_BER 75 | Serial.println("[DEBUG_BER] NetworkAddress:serialise"); 76 | #endif 77 | unsigned char *ptr = buf; 78 | *ptr++ = _type; 79 | 80 | _length = 4; 81 | 82 | *ptr++ = _length; 83 | *ptr++ = _value[0]; 84 | *ptr++ = _value[1]; 85 | *ptr++ = _value[2]; 86 | *ptr++ = _value[3]; 87 | return _length + 2; 88 | } 89 | bool fromBuffer(unsigned char *buf) 90 | { 91 | #ifdef DEBUG_BER 92 | Serial.println("[DEBUG_BER] NetworkAddress:fromBuffer"); 93 | #endif 94 | buf++; // skip Type 95 | _length = *buf; 96 | buf++; 97 | byte tempAddress[4]; 98 | tempAddress[0] = *buf++; 99 | tempAddress[1] = *buf++; 100 | tempAddress[2] = *buf++; 101 | tempAddress[3] = *buf++; 102 | _value = IPAddress(tempAddress); 103 | return true; 104 | } 105 | int getLength() 106 | { 107 | return _length; 108 | } 109 | }; 110 | 111 | class IntegerType : public BER_CONTAINER 112 | { 113 | public: 114 | IntegerType() : BER_CONTAINER(true, INTEGER){}; 115 | IntegerType(unsigned long value) : _value(value), BER_CONTAINER(true, INTEGER){}; 116 | ~IntegerType(){}; 117 | unsigned long _value; 118 | int serialise(unsigned char *buf) 119 | { 120 | #ifdef DEBUG_BER 121 | Serial.println("[DEBUG_BER] IntegerType:serialise"); 122 | #endif 123 | // here we print out the BER encoded ASN.1 bytes, which includes type, length and value. we return the length of the entire block (TL&V) in bytes; 124 | unsigned char *ptr = buf; 125 | *ptr = _type; 126 | ptr++; 127 | unsigned char *lengthPtr = ptr++; 128 | 129 | // // For values <= 127 we use a single byte for the value 130 | if (_value != 0 && _value <= 0x7F) 131 | { 132 | _length = 1; 133 | *ptr++ = _value; 134 | } 135 | else if (_value != 0 && _value >= 0x80) 136 | { 137 | // Determine the number of bytes required for encoding 138 | uint32_t temp = _value; 139 | _length = 0; 140 | while (temp > 0) { 141 | temp >>= 8; 142 | _length++; 143 | } 144 | // Multi-byte encoding 145 | *ptr++ = 0x80 | _length; // Tag for multi-byte encoding 146 | 147 | for (size_t i = 1; i <= _length; i++) { 148 | *ptr++ = _value & 0xFF; // Store the least significant byte 149 | _value >>= 8; 150 | } 151 | _length++; 152 | } 153 | else 154 | { 155 | _length = 1; 156 | *ptr = 0; 157 | } 158 | *lengthPtr = _length; 159 | return _length + 2; 160 | } 161 | bool fromBuffer(unsigned char *buf) 162 | { 163 | #ifdef DEBUG_BER 164 | Serial.println("[DEBUG_BER] Integer:fromBuffer"); 165 | #endif 166 | buf++; // skip Type 167 | _length = *buf; 168 | buf++; 169 | unsigned short tempLength = _length; 170 | // _value = *buf; // TODO: make work for integers more than 255 171 | _value = 0; 172 | while (tempLength > 0) 173 | { 174 | _value = _value << 8; 175 | _value = _value | *buf++; 176 | tempLength--; 177 | } 178 | return true; 179 | } 180 | int getLength() 181 | { 182 | return _length; 183 | } 184 | }; 185 | 186 | class TimestampType : public IntegerType 187 | { 188 | public: 189 | TimestampType() : IntegerType() 190 | { 191 | _type = TIMESTAMP; 192 | }; 193 | TimestampType(unsigned long value) : IntegerType(value) 194 | { 195 | _type = TIMESTAMP; 196 | }; 197 | ~TimestampType(){}; 198 | }; 199 | 200 | class OctetType : public BER_CONTAINER 201 | { 202 | public: 203 | OctetType() : BER_CONTAINER(true, STRING){}; 204 | OctetType(char *value) : BER_CONTAINER(true, STRING) 205 | { 206 | strncpy(_value, value, sizeof(_value)); 207 | _value[sizeof(_value)] = 0; 208 | }; 209 | ~OctetType(){}; 210 | char _value[SNMP_OCTETSTRING_MAX_LENGTH]; 211 | int serialise(unsigned char *buf) 212 | { 213 | #ifdef DEBUG_BER 214 | Serial.println("[DEBUG_BER] OctetType:serialise"); 215 | #endif 216 | // here we print out the BER encoded ASN.1 bytes, which includes type, length and value. 217 | char *ptr = (char *)buf; 218 | int numExtraBytes = 0; 219 | char temp[SNMP_OCTETSTRING_MAX_LENGTH]; 220 | int valueLength = sprintf(temp, "%s", _value); 221 | 222 | *ptr++ = _type; // Set the type identifier 223 | // If > 127 first byte needs to be 0x8x where x is the how many bytes follow which defines string length 224 | if (valueLength > 127) 225 | { 226 | numExtraBytes++; // Need an extra byte 227 | if (valueLength > 256) // Max 65,536 characters, but likely will fail due to UDP packet fragmentation. 228 | { 229 | numExtraBytes++; // Need another extra byte to store the length 230 | } 231 | *ptr++ = (numExtraBytes | 0x80); // 0x8x where x is the number of bytes which provide the total string length 232 | if (valueLength > 256) 233 | { 234 | *ptr++ = valueLength / 256; 235 | valueLength = valueLength % 256; 236 | } 237 | *ptr++ = valueLength; 238 | } 239 | else 240 | { 241 | *ptr++ = valueLength; 242 | } 243 | _length = sprintf(ptr, "%s", _value); 244 | return _length + numExtraBytes + 2; 245 | } 246 | bool fromBuffer(unsigned char *buf) 247 | { 248 | #ifdef DEBUG_BER 249 | Serial.println("[DEBUG_BER] OctetType:fromBuffer"); 250 | #endif 251 | buf++; // skip Type 252 | _length = *buf; 253 | // length should be treated as: if first byte is 0x8x, the x is how many bytes follow 254 | if (_length > 127) 255 | { 256 | int numBytes = _length &= 0x7F; 257 | unsigned int special_length = 0; 258 | for (int k = 0; k < numBytes; k++) 259 | { 260 | buf++; 261 | special_length <<= 8; 262 | special_length |= *buf; 263 | } 264 | _length = special_length; 265 | } 266 | buf++; 267 | memset(_value, 0, sizeof(_value)); // Null out _value 268 | if (_length < sizeof(_value)) 269 | { 270 | strncpy(_value, (char *)buf, _length); // Copy buffer to Value, using length from ASN structure. 271 | } 272 | else 273 | { 274 | Serial.println(F("OctetString too large, adjust SNMP_OCTETSTRING_MAX_LENGTH. String Truncated.")); 275 | strncpy(_value, (char *)buf, 253); // Copy truncated buffer to Value 276 | } 277 | return true; 278 | } 279 | int getLength() 280 | { 281 | return _length; 282 | } 283 | }; 284 | 285 | class OIDType : public BER_CONTAINER 286 | { 287 | public: 288 | OIDType() : BER_CONTAINER(true, OID){}; 289 | OIDType(char *value) : BER_CONTAINER(true, OID) 290 | { 291 | strncpy(_value, value, MAX_OID_LENGTH); 292 | }; 293 | ~OIDType(){}; 294 | char _value[MAX_OID_LENGTH]; 295 | int serialise(unsigned char *buf) 296 | { 297 | #ifdef DEBUG_BER 298 | Serial.println("[DEBUG_BER] OIDType:serialise"); 299 | #endif 300 | // here we print out the BER encoded ASN.1 bytes, which includes type, length and value. 301 | char *ptr = (char *)buf; 302 | *ptr = _type; 303 | ptr++; 304 | char *lengthPtr = ptr; 305 | ptr++; 306 | *ptr = 0x2b; 307 | char *internalPtr = ++ptr; 308 | char *valuePtr = &_value[5]; 309 | _length = 3; 310 | bool toBreak = false; 311 | while (true) 312 | { 313 | char *start = valuePtr; 314 | char *end = strchr(start, '.'); 315 | 316 | if (!end) 317 | { 318 | end = strchr(start, 0); 319 | toBreak = true; 320 | } 321 | char tempBuf[12]; 322 | memset(tempBuf, 0, 12); 323 | // char* tempBuf = (char*) malloc(sizeof(char) * (end-start)); 324 | strncpy(tempBuf, start, end - start + 1); 325 | long tempVal; 326 | char *pEnd; 327 | tempVal = (uint32_t)strtoul(tempBuf, &pEnd, 10); 328 | if (tempVal < 128) 329 | { 330 | _length += 1; 331 | *ptr++ = (char)tempVal; 332 | } 333 | else 334 | { 335 | // Serial.print("large num: ");Serial.println(tempVal); 336 | // FIXME: This will only encode integers upto 4 bytes. Ideally this should be a loop. 337 | if (tempVal / 128 / 128 > 128) 338 | { 339 | *ptr++ = ((tempVal / 128 / 128 / 128 ) | 0x80) & 0xFF; 340 | _length += 1; 341 | } 342 | if (tempVal / 128 > 128) 343 | { 344 | *ptr++ = ((tempVal / 128 / 128) | 0x80) & 0xFF; 345 | _length += 1; 346 | } 347 | *ptr++ = ((tempVal / 128) | 0x80) & 0xFF; 348 | 349 | *ptr++ = tempVal % 128 & 0xFF; 350 | _length += 2; 351 | } 352 | 353 | valuePtr = end + 1; 354 | 355 | // free(tempBuf); 356 | if (toBreak) 357 | break; 358 | // delay(1); 359 | } 360 | *lengthPtr = _length - 2; 361 | 362 | return _length; 363 | } 364 | bool fromBuffer(unsigned char *buf) 365 | { 366 | #ifdef DEBUG_BER 367 | Serial.println("[DEBUG_BER] OIDType:fromBuffer"); 368 | #endif 369 | buf++; // skip Type 370 | _length = *buf; 371 | buf++; 372 | buf++; 373 | memset(_value, 0, 128); 374 | _value[0] = '.'; 375 | _value[1] = '1'; 376 | _value[2] = '.'; 377 | _value[3] = '3'; // we fill in the first two bytes already 378 | char *ptr = &_value[4]; 379 | char i = _length - 1; 380 | while (i > 0) 381 | { 382 | if (*buf < 128) 383 | { // we can keep raw 384 | ptr += sprintf(ptr, ".%d", *buf); 385 | i--; 386 | buf++; 387 | } 388 | else 389 | { // we have to do the special >128 thing 390 | long value = 0; // keep track of the actual thing 391 | unsigned char n = 0; // count how many large bits have been set 392 | unsigned char tempBuf[6] = {0}; // no bigger than 4 bytes 393 | while (*buf > 127) 394 | { 395 | i--; 396 | *buf &= 0x7F; 397 | tempBuf[n] = *buf; 398 | n++; 399 | buf++; 400 | } 401 | value = *buf; 402 | buf++; 403 | i--; 404 | for (unsigned char k = 0; k < n; k++) 405 | { 406 | value += (pow(128, (n - k))) * tempBuf[k]; 407 | } 408 | ptr += sprintf(ptr, ".%d", value); 409 | } 410 | } 411 | // Serial.print("OID: " );Serial.println(_value); 412 | // memcpy(_value, buf, _length); 413 | return true; 414 | } 415 | 416 | int getLength() 417 | { 418 | return _length; 419 | } 420 | }; 421 | 422 | class NullType : public BER_CONTAINER 423 | { 424 | public: 425 | NullType() : BER_CONTAINER(true, NULLTYPE){}; 426 | ~NullType(){}; 427 | char _value = 0; 428 | int serialise(unsigned char *buf) 429 | { 430 | #ifdef DEBUG_BER 431 | Serial.println("[DEBUG_BER] NullType:serialise"); 432 | #endif 433 | // here we print out the BER encoded ASN.1 bytes, which includes type, length and value. 434 | char *ptr = (char *)buf; 435 | *ptr = _type; 436 | ptr++; 437 | *ptr = 0; 438 | return 2; 439 | } 440 | bool fromBuffer(unsigned char *buf) 441 | { 442 | #ifdef DEBUG_BER 443 | Serial.println("[DEBUG_BER] NullType:fromBuffer"); 444 | #endif 445 | _length = 0; 446 | return true; 447 | } 448 | 449 | int getLength() 450 | { 451 | return 0; 452 | } 453 | }; 454 | 455 | class Counter64 : public BER_CONTAINER 456 | { 457 | public: 458 | Counter64() : BER_CONTAINER(true, COUNTER64){}; 459 | Counter64(uint64_t value) : _value(value), BER_CONTAINER(true, COUNTER64){}; 460 | ~Counter64(){}; 461 | uint64_t _value; 462 | int serialise(unsigned char *buf) 463 | { 464 | #ifdef DEBUG_BER 465 | Serial.println("[DEBUG_BER] Counter64:serialise"); 466 | #endif 467 | // here we print out the BER encoded ASN.1 bytes, which includes type, length and value. we return the length of the entire block (TL&V) ni bytes; 468 | unsigned char *ptr = buf; 469 | *ptr = _type; 470 | ptr++; 471 | unsigned char *lengthPtr = ptr++; 472 | if (_value != 0) 473 | { 474 | _length = 8; 475 | *ptr++ = _value >> 56 & 0xFF; 476 | *ptr++ = _value >> 48 & 0xFF; 477 | *ptr++ = _value >> 40 & 0xFF; 478 | *ptr++ = _value >> 32 & 0xFF; 479 | *ptr++ = _value >> 24 & 0xFF; 480 | *ptr++ = _value >> 16 & 0xFF; 481 | *ptr++ = _value >> 8 & 0xFF; 482 | *ptr++ = _value & 0xFF; 483 | } 484 | else 485 | { 486 | _length = 1; 487 | *ptr = 0; 488 | } 489 | *lengthPtr = _length; 490 | return _length + 2; 491 | } 492 | bool fromBuffer(unsigned char *buf) 493 | { 494 | #ifdef DEBUG_BER 495 | Serial.println("[DEBUG_BER] Counter64:fromBuffer"); 496 | #endif 497 | buf++; // skip Type 498 | _length = *buf; 499 | buf++; 500 | unsigned short tempLength = _length; 501 | // _value = *buf; // TODO: make work for integers more than 255 502 | _value = 0; 503 | while (tempLength > 0) 504 | { 505 | _value = _value << 8; 506 | _value = _value | *buf++; 507 | tempLength--; 508 | } 509 | return true; 510 | } 511 | int getLength() 512 | { 513 | return _length; 514 | } 515 | }; 516 | 517 | class Counter32 : public IntegerType 518 | { 519 | public: 520 | Counter32() : IntegerType() 521 | { 522 | _type = COUNTER32; 523 | }; 524 | Counter32(unsigned int value) : IntegerType(value) 525 | { 526 | _type = COUNTER32; 527 | }; 528 | ~Counter32(){}; 529 | }; 530 | 531 | class Gauge : public IntegerType 532 | { // Unsigned int 533 | public: 534 | Gauge() : IntegerType() 535 | { 536 | _type = GAUGE32; 537 | }; 538 | Gauge(unsigned int value) : IntegerType(value) 539 | { 540 | _type = GAUGE32; 541 | }; 542 | ~Gauge(){}; 543 | }; 544 | 545 | typedef struct BER_LINKED_LIST 546 | { 547 | ~BER_LINKED_LIST() 548 | { 549 | delete next; 550 | next = 0; 551 | delete value; 552 | value = 0; 553 | } 554 | BER_CONTAINER *value = 0; 555 | struct BER_LINKED_LIST *next = 0; 556 | } ValuesList; 557 | 558 | class ComplexType : public BER_CONTAINER 559 | { 560 | public: 561 | ComplexType(ASN_TYPE type) : BER_CONTAINER(false, type){}; 562 | ~ComplexType() 563 | { 564 | delete _values; 565 | } 566 | ValuesList *_values = 0; 567 | bool fromBuffer(unsigned char *buf) 568 | { 569 | #ifdef DEBUG_BER 570 | Serial.println("[DEBUG_BER] ComplexType:fromBuffer"); 571 | #endif 572 | // the buffer we get passed in is the complete ASN Container, including the type header. 573 | buf++; // Skip our own type 574 | _length = *buf; 575 | // length should be treated as: if first byte is 0x8x, the x is how many bytes follow 576 | if (_length > 127) 577 | { 578 | int numBytes = _length &= 0x7F; 579 | unsigned int special_length = 0; 580 | for (int k = 0; k < numBytes; k++) 581 | { 582 | buf++; 583 | special_length <<= 8; 584 | special_length |= *buf; 585 | } 586 | _length = special_length; 587 | } 588 | buf++; 589 | // now we are at the front of a list of one or many other types, lets do our loop 590 | unsigned int i = 1; 591 | while (i < _length) 592 | { 593 | ASN_TYPE valueType = (ASN_TYPE)*buf; 594 | buf++; 595 | i++; 596 | unsigned int valueLength = *buf; 597 | 598 | int doubleL = 0; 599 | if (valueLength > 127) 600 | { 601 | int numBytes = valueLength &= 0x7F; 602 | unsigned int special_length = 0; 603 | for (int k = 0; k < numBytes; k++) 604 | { 605 | buf++; 606 | i++; 607 | special_length <<= 8; 608 | special_length |= *buf; 609 | } 610 | valueLength = special_length; 611 | doubleL = numBytes; 612 | } 613 | buf++; 614 | i++; 615 | 616 | BER_CONTAINER *newObj; 617 | switch (valueType) 618 | { 619 | case STRUCTURE: 620 | case GetRequestPDU: 621 | case GetNextRequestPDU: 622 | case GetResponsePDU: 623 | case SetRequestPDU: 624 | case GetBulkRequestPDU: 625 | case TrapPDU: // should never get trap, but put it in anyway 626 | case Trapv2PDU: 627 | newObj = new ComplexType(valueType); 628 | break; 629 | // primitive 630 | case INTEGER: 631 | newObj = new IntegerType(); 632 | break; 633 | case STRING: 634 | newObj = new OctetType(); 635 | break; 636 | case OID: 637 | newObj = new OIDType(); 638 | break; 639 | case NULLTYPE: 640 | newObj = new NullType(); 641 | break; 642 | // derived 643 | case NETWORK_ADDRESS: 644 | newObj = new NetworkAddress(); 645 | break; 646 | case TIMESTAMP: 647 | newObj = new TimestampType(); 648 | break; 649 | case COUNTER32: 650 | newObj = new Counter32(); 651 | break; 652 | case GAUGE32: 653 | newObj = new Gauge(); 654 | break; 655 | case COUNTER64: 656 | newObj = new Counter64(); 657 | break; 658 | /* OPAQUE = 0x44 */ 659 | 660 | default: 661 | #ifdef DEBUG 662 | Serial.println("[DEBUG_BER] default new ComplexType"); 663 | #endif 664 | newObj = new ComplexType(valueType); 665 | break; 666 | } 667 | newObj->fromBuffer(buf - (2 + doubleL)); 668 | 669 | buf += valueLength; 670 | i += valueLength; 671 | //newObj->fromBuffer(newValue); 672 | // free(newValue); 673 | addValueToList(newObj); 674 | } 675 | return true; 676 | } 677 | 678 | int serialise(unsigned char *buf) 679 | { 680 | #ifdef DEBUG_BER 681 | Serial.println("[DEBUG_BER] ComplexType:serialise"); 682 | #endif 683 | int actualLength = 0; 684 | unsigned char *ptr = buf; 685 | *ptr = _type; 686 | ptr++; 687 | unsigned char *lengthPtr = ptr++; 688 | *lengthPtr = 0; 689 | ValuesList *conductor = _values; 690 | int tempLength = 0; 691 | while (conductor) 692 | { 693 | // Serial.print("about to serialise something of type: ");Serial.println(conductor->value->_type, HEX); 694 | delay(0); 695 | 696 | int length = conductor->value->serialise(ptr); 697 | ptr += length; 698 | actualLength += length; 699 | conductor = conductor->next; 700 | } 701 | // printf("Length to return: %d\n", actualLength); 702 | if (actualLength > 127) 703 | { 704 | #ifdef DEBUG_BER 705 | Serial.println("TOO BIG - Adding extra byte"); 706 | #endif 707 | // bad, we have to add another byte and shift everything afterwards by 1 >> 708 | // first byte is 128 + (actualLength / 128) 709 | // second is actualLength % 128; 710 | int tempVal = 1; 711 | if (actualLength > 256) 712 | { 713 | *lengthPtr++ = (2 | 0x80) & 0xFF; // dodgy 714 | 715 | tempLength += 1; 716 | unsigned char *endPtrPos = ++ptr; 717 | for (unsigned char *i = endPtrPos; i > buf + 1; i--) 718 | { 719 | // i is the char we are moving INTO 720 | *i = *(i - 1); 721 | } 722 | tempVal = 2; 723 | *lengthPtr++ = actualLength / 256; 724 | } 725 | else 726 | { 727 | *lengthPtr++ = (1 | 0x80) & 0xFF; 728 | } 729 | 730 | // *lengthPtr = 129; 731 | // if(actualLength > 256){ 732 | // *lengthPtr = 130; 733 | // } 734 | // lets move everything right one byte, start from back.. 735 | unsigned char *endPtrPos = ptr + 1; 736 | for (unsigned char *i = endPtrPos; i > buf + tempVal; i--) 737 | { 738 | // i is the char we are moving INTO 739 | *i = *(i - 1); 740 | } 741 | *lengthPtr++ = actualLength % 256; 742 | 743 | // *(lengthPtr+1) = (actualLength % 128)|0x80; 744 | tempLength += 1; // account for extra byte in Length param 745 | } 746 | else 747 | { 748 | *lengthPtr = actualLength; 749 | } 750 | return actualLength + 2 + tempLength; 751 | } 752 | 753 | int getLength() 754 | { 755 | return _length; 756 | } 757 | 758 | void addValueToList(BER_CONTAINER *newObj) 759 | { 760 | ValuesList *conductor = _values; 761 | if (_values != 0) 762 | { 763 | while (conductor->next != 0) 764 | { 765 | conductor = conductor->next; 766 | delay(0); 767 | } 768 | conductor->next = new ValuesList; 769 | conductor = conductor->next; 770 | conductor->value = newObj; 771 | conductor->next = 0; 772 | } 773 | else 774 | { 775 | _values = new ValuesList; 776 | _values->value = newObj; 777 | _values->next = 0; 778 | } 779 | } 780 | }; 781 | 782 | #endif --------------------------------------------------------------------------------