├── .gitignore ├── ArduinoSNMP.cpp ├── ArduinoSNMP.h ├── Example ├── Actual_SNMP_Agent │ ├── Actual_SNMP_Agent.ino │ ├── README.md │ ├── SNMPAgent.cpp │ ├── SNMPAgent.h │ ├── Time.cpp │ ├── Time.h │ ├── global.cpp │ └── global.h └── snmp_trap_listener.rb └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /ArduinoSNMP.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ArduinoSNMP.cpp - An Arduino library for a lightweight SNMP Agent. v2.2 3 | Copyright (C) 2013 Rex Park , Portions (C) 2010 Eric C. Gionet 4 | All rights reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #include "ArduinoSNMP.h" 22 | #include 23 | 24 | EthernetUDP Udp; 25 | 26 | SNMP_API_STAT_CODES SNMPClass::begin(const char *getCommName, const char *setCommName, const char *trapCommName, uint16_t port) 27 | { 28 | //initialize request counter 29 | requestCounter = 1; 30 | _extra_data_size = 0; 31 | _udp_extra_data_packet = false; 32 | 33 | // set community name set/get sizes 34 | _setSize = strlen(setCommName); 35 | _getSize = strlen(getCommName); 36 | _trapSize = strlen(trapCommName); 37 | // 38 | // validate get/set community name sizes 39 | if ( _setSize > SNMP_MAX_NAME_LEN + 1 || _getSize > SNMP_MAX_NAME_LEN + 1 || _trapSize > SNMP_MAX_NAME_LEN + 1) { 40 | return SNMP_API_STAT_NAME_TOO_BIG; 41 | } 42 | // 43 | // set community names 44 | _getCommName = getCommName; 45 | _setCommName = setCommName; 46 | _trapCommName = trapCommName; 47 | 48 | // validate session port number 49 | if ( port == NULL || port == 0 ) port = SNMP_DEFAULT_PORT; 50 | // 51 | // init UDP socket 52 | Udp.stop(); 53 | Udp.begin(port); 54 | 55 | return SNMP_API_STAT_SUCCESS; 56 | } 57 | 58 | boolean SNMPClass::listen(void) 59 | { 60 | // if bytes are available in receive buffer 61 | // and pointer to a function (delegate function) 62 | // isn't null, trigger the function 63 | 64 | if(Udp.parsePacket() > 1024){ 65 | _udp_extra_data_packet = true; 66 | }else{ 67 | _udp_extra_data_packet = false; 68 | } 69 | 70 | if (Udp.available()){ 71 | if(_callback != NULL){ 72 | (*_callback)(); 73 | }else{ 74 | return true; 75 | } 76 | } 77 | 78 | return false; 79 | } 80 | 81 | /** 82 | * Parses incoming SNMP messages. 83 | * 84 | * Original Auther: Rex Park 85 | * Updated: November 7, 2015 (Added support for Inform responses (SNMP_PDU_RESPONSE) 86 | */ 87 | SNMP_API_STAT_CODES SNMPClass::requestPdu(SNMP_PDU *pdu, char *extra_data, int extra_data_max_size) 88 | { 89 | // sequence length 90 | uint16_t seqLen, valLen, vblLen, pduLen; 91 | // version 92 | byte verLen, verEnd; 93 | // community string 94 | byte comLen, comEnd; 95 | // pdu 96 | byte pduTyp, pduEnd; 97 | byte ridLen, ridEnd; 98 | byte errLen, errEnd; 99 | byte eriLen, eriEnd; 100 | byte vblTyp; 101 | byte vbiTyp, vbiLen; 102 | byte obiLen, obiEnd; 103 | byte valTyp, valEnd; 104 | int i; 105 | 106 | // set packet packet size (skip UDP header) 107 | _packetSize = Udp.available(); 108 | _packetPos = 0; 109 | 110 | // reset packet array 111 | memset(_packet, 0, SNMP_MAX_PACKET_LEN); 112 | // 113 | // validate packet 114 | if ( _packetSize <= 0) { 115 | return SNMP_API_STAT_PACKET_TOO_BIG; 116 | } 117 | 118 | // get UDP packet 119 | if(_udp_extra_data_packet == true){ 120 | 121 | if(extra_data != NULL){ 122 | memset(extra_data, 0, extra_data_max_size); 123 | } 124 | 125 | Udp.read(_packet, _packetSize - extra_data_max_size); 126 | 127 | if(extra_data != NULL){ 128 | Udp.read((byte*)extra_data, extra_data_max_size); 129 | } 130 | }else{ 131 | Udp.read(_packet, _packetSize); 132 | } 133 | 134 | // Serial.println("Incomming: "); 135 | // for(int i = 0; i < _packetSize; i++){ 136 | // Serial.print(_packet[i],HEX); 137 | // Serial.print(" "); 138 | // } 139 | // Serial.println(); 140 | 141 | // packet check 1 142 | if ( _packet[0] != 0x30 ) { 143 | return SNMP_API_STAT_PACKET_INVALID; 144 | } 145 | 146 | 147 | // sequence length 148 | if(_packet[1] >= 0x82){ 149 | seqLen = combine_msb_lsb(_packet[2], _packet[3]); 150 | _packetPos = 4; 151 | } 152 | else if(_packet[1] == 0x81){ 153 | seqLen = _packet[2]; 154 | _packetPos = 3; 155 | }else{ 156 | seqLen = _packet[1]; 157 | _packetPos = 2; 158 | } 159 | 160 | // version 161 | if(_packet[_packetPos] != 0x02){ 162 | return SNMP_API_STAT_PACKET_INVALID; 163 | } 164 | 165 | verLen = _packet[_packetPos+1];//used to be hard coded as index 3 166 | verEnd = _packetPos+1 + verLen; 167 | 168 | // community string 169 | comLen = _packet[verEnd + 2]; 170 | comEnd = verEnd + 2 + comLen; 171 | 172 | // pdu 173 | pduTyp = _packet[comEnd + 1]; 174 | if(_packet[comEnd + 2] >= 0x82){ 175 | pduLen = combine_msb_lsb(_packet[comEnd +3], _packet[comEnd +4]); 176 | pduEnd = comEnd + 2 + pduLen + 2; 177 | _packetPos = comEnd + 2 + 2; 178 | } 179 | else if(_packet[comEnd + 2] == 0x81){ 180 | pduLen = _packet[comEnd +3]; 181 | pduEnd = comEnd + 2 + pduLen + 1; 182 | _packetPos = comEnd + 2 + 1; 183 | }else{ 184 | pduLen = _packet[comEnd + 2]; 185 | pduEnd = comEnd + 2 + pduLen; 186 | _packetPos = comEnd + 2; 187 | } 188 | 189 | //request id 190 | ridLen = _packet[_packetPos + 2]; 191 | ridEnd = _packetPos + 2 + ridLen; 192 | 193 | //error 194 | errLen = _packet[ridEnd + 2]; 195 | errEnd = ridEnd + 2 + errLen; 196 | 197 | //error index 198 | eriLen = _packet[errEnd + 2]; 199 | eriEnd = errEnd + 2 + eriLen; 200 | 201 | //variable bindings 202 | vblTyp = _packet[eriEnd + 1]; 203 | if(_packet[eriEnd + 2] >= 0x82){ 204 | vblLen = combine_msb_lsb(_packet[eriEnd +3], _packet[eriEnd +4]); 205 | _packetPos = eriEnd + 2 + 2; 206 | } 207 | else if(_packet[eriEnd + 2] == 0x81){ 208 | vblLen = _packet[eriEnd +3]; 209 | _packetPos = eriEnd + 2 + 1; 210 | }else{ 211 | vblLen = _packet[eriEnd + 2]; 212 | _packetPos = eriEnd + 2; 213 | } 214 | 215 | //variable bindings id 216 | vbiTyp = _packet[_packetPos + 1]; 217 | if(_packet[_packetPos + 2] > 0x80){ 218 | vbiLen = combine_msb_lsb(_packet[_packetPos +3], _packet[_packetPos +4]); 219 | _packetPos = _packetPos + 2 + 2; 220 | }else{ 221 | vbiLen = _packet[_packetPos + 2]; 222 | _packetPos = _packetPos + 2; 223 | } 224 | 225 | //object identifier 226 | obiLen = _packet[_packetPos + 2]; 227 | obiEnd = _packetPos + 2 + obiLen; 228 | 229 | //unknown 230 | valTyp = _packet[obiEnd + 1]; 231 | 232 | if(_packet[obiEnd + 2] >= 0x82){ 233 | valLen = combine_msb_lsb(_packet[obiEnd +3], _packet[obiEnd +4]); 234 | valEnd = obiEnd + 2 + valLen + 2; 235 | } 236 | else if(_packet[obiEnd + 2] == 0x81){ 237 | valLen = _packet[obiEnd + 3]; 238 | valEnd = obiEnd + 2 + valLen + 1; 239 | }else{ 240 | valLen = _packet[obiEnd + 2]; 241 | valEnd = obiEnd + 2 + valLen; 242 | } 243 | 244 | // Serial.println(verEnd); 245 | // Serial.println(comEnd); 246 | // Serial.println(ridEnd); 247 | // Serial.println(errEnd); 248 | // Serial.println(eriEnd); 249 | // Serial.println(obiEnd); 250 | // Serial.println(pduEnd); 251 | // Serial.println(pduTyp,HEX); 252 | 253 | // extract version 254 | pdu->version = _packet[verEnd]; 255 | // Serial.println(pdu->version,HEX); 256 | // validate version 257 | if(pdu->version != 0x0 && pdu->version != 0x1){ 258 | return SNMP_API_STAT_PACKET_INVALID; 259 | } 260 | 261 | // pdu-type 262 | pdu->type = (SNMP_PDU_TYPES)pduTyp; 263 | _dstType = pdu->type; 264 | 265 | // validate community size 266 | if ( comLen > SNMP_MAX_NAME_LEN ) { 267 | // set pdu error 268 | pdu->error = SNMP_ERR_TOO_BIG; 269 | // 270 | return SNMP_API_STAT_NAME_TOO_BIG; 271 | } 272 | 273 | // validate community name 274 | if ( pdu->type == SNMP_PDU_SET && comLen == _setSize ) { 275 | if(memcmp(_setCommName,_packet+verEnd+3,_setSize) != 0){ 276 | pdu->error = SNMP_ERR_NO_SUCH_NAME; 277 | return SNMP_API_STAT_NO_SUCH_NAME; 278 | } 279 | } else if ( pdu->type == SNMP_PDU_GET) { 280 | if(memcmp(_getCommName,_packet+verEnd+3,comLen) != 0 && memcmp(_setCommName,_packet+verEnd+3,comLen) != 0){ 281 | pdu->error = SNMP_ERR_NO_SUCH_NAME; 282 | return SNMP_API_STAT_NO_SUCH_NAME; 283 | } 284 | } else if (pdu->type == SNMP_PDU_RESPONSE){ 285 | if(memcmp(_trapCommName,_packet+verEnd+3,comLen) != 0){ 286 | pdu->error = SNMP_ERR_NO_SUCH_NAME; 287 | return SNMP_API_STAT_NO_SUCH_NAME; 288 | } 289 | } 290 | else { 291 | // set pdu error 292 | pdu->error = SNMP_ERR_NO_SUCH_NAME; 293 | // 294 | return SNMP_API_STAT_NO_SUCH_NAME; 295 | } 296 | 297 | // extract reqiest-id 0x00 0x00 0x00 0x01 (4-byte int aka int32) 298 | pdu->requestId = 0; 299 | for ( i = 0; i < ridLen; i++ ) { 300 | pdu->requestId = (pdu->requestId << 8) | _packet[ridEnd-ridLen+1 + i]; 301 | } 302 | 303 | // extract error 304 | pdu->error = SNMP_ERR_NO_ERROR; 305 | int32_t err = 0; 306 | for ( i = 0; i < errLen; i++ ) { 307 | err = (err << 8) | _packet[errEnd-errLen+1 + i]; 308 | } 309 | pdu->error = (SNMP_ERR_CODES)err; 310 | 311 | // extract error-index 312 | pdu->errorIndex = 0; 313 | for ( i = 0; i < eriLen; i++ ) { 314 | pdu->errorIndex = (pdu->errorIndex << 8) | _packet[eriEnd-eriLen+1 + i]; 315 | } 316 | 317 | /** 318 | * OID 319 | */ 320 | //validate length 321 | if ( obiLen > SNMP_MAX_OID_LEN ) { 322 | pdu->error = SNMP_ERR_TOO_BIG; 323 | return SNMP_API_STAT_OID_TOO_BIG; 324 | } 325 | //decode OID 326 | memset(pdu->value.OID.data, 0, SNMP_MAX_OID_LEN); 327 | if(pdu->value.OID.decode(_packet + (obiEnd-obiLen+1), obiLen) != SNMP_API_STAT_SUCCESS){ 328 | return SNMP_API_STAT_MALLOC_ERR; 329 | } 330 | 331 | /** 332 | * Value 333 | */ 334 | //syntax type 335 | pdu->value.syntax = (SNMP_SYNTAXES)valTyp; 336 | //check length of data 337 | 338 | if ( valLen > SNMP_MAX_VALUE_LEN && _udp_extra_data_packet == false) { 339 | pdu->error = SNMP_ERR_TOO_BIG; 340 | return SNMP_API_STAT_VALUE_TOO_BIG; 341 | } 342 | //set value size 343 | pdu->value.size = valLen; 344 | 345 | if(_udp_extra_data_packet == true){ 346 | // memset(extra_data, '\0', extra_data_max_size); 347 | // for ( i = 0; i < valLen; i++ ) { 348 | // extra_data[i] = _packet[obiEnd+3 + i]; 349 | // } 350 | }else{ 351 | memset(pdu->value.data, 0, SNMP_MAX_VALUE_LEN); 352 | for ( i = 0; i < valLen; i++ ) { 353 | pdu->value.data[i] = _packet[obiEnd+3 + i]; 354 | } 355 | } 356 | 357 | return SNMP_API_STAT_SUCCESS; 358 | } 359 | 360 | /** 361 | * Sends a PDU as a v1 trap. (needs testing after v2 trap changes) 362 | * OID: The full enterprise OID for the trap you want to send: everything in the trap's OID from the initial .1 363 | * up to the enterprise number, including any subtrees within the enterprise but not the specific trap number. 364 | * value: will be an array of pre-encoded data 365 | * 366 | * Original Author: Yazgoo 367 | * Updated: Rex Park, March 29, 2013. (Adjusting for new encoding changes and PDU structure) 368 | * 369 | * Broken as of November 10th 2013 due to writeHeaders re-write. Needs to be modified to work like responsePdu 370 | */ 371 | uint32_t SNMPClass::sendTrapv1(SNMP_PDU *pdu, SNMP_TRAP_TYPES trap_type, int16_t specific_trap, IPAddress manager_address){ 372 | 373 | byte i; 374 | SNMP_VALUE value; 375 | _dstType = pdu->type = SNMP_PDU_TRAP; 376 | _packetPos = 0; 377 | uint16_t size = 27 + 2 + pdu->value.size; 378 | 379 | this->writeHeaders(pdu); 380 | 381 | //unknown 382 | _packet[_packetPos++] = (byte) size - 1; 383 | i = _packetPos-1;//i now stores the location of the PDU size, need to update after OID encoding. 384 | 385 | //Enterprise OID 386 | _packetPos += pdu->value.OID.encode(_packet + _packetPos); 387 | //adjust sizes now that OID is encoded. 388 | _packetSize += pdu->value.OID.size-2; 389 | _packet[1] += pdu->value.OID.size-2; 390 | _packet[i] += pdu->value.OID.size-2; 391 | 392 | //Agent IP 393 | value.encode_address(SNMP_SYNTAX_IP_ADDRESS, pdu->agent_address, _packet + _packetPos); 394 | _packetPos += value.size; 395 | 396 | //trap type 397 | value.encode(SNMP_SYNTAX_INT, (int16_t)trap_type, _packet + _packetPos); 398 | _packetPos += value.size; 399 | 400 | //specific trap id 401 | value.encode(SNMP_SYNTAX_INT, specific_trap, _packet + _packetPos); 402 | _packetPos += value.size; 403 | 404 | //Time Ticks 405 | //Similar error as previous commit in ArduinoSNMP.h. 406 | //value.encode(SNMP_SYNTAX_TIME_TICKS, millis() / 10, _packet + _packetPos); 407 | value.encode(SNMP_SYNTAX_TIME_TICKS, (const uint64_t)millis()/10, _packet + _packetPos); 408 | _packetPos +=6;//syntax + length + 4 octets 409 | 410 | //unknown 411 | _packet[_packetPos++] = (byte) SNMP_SYNTAX_SEQUENCE; 412 | _packet[_packetPos++] = (byte) 4 + pdu->value.size; 413 | 414 | //trap data, already encoded prior to function call 415 | for ( i = 0; i < pdu->value.size; i++ ) { 416 | _packet[_packetPos++] = pdu->value.data[i]; 417 | } 418 | 419 | writePacket(manager_address, SNMP_MANAGER_PORT); 420 | 421 | return pdu->requestId; 422 | } 423 | 424 | /** 425 | * Generates SNMP header data. 426 | * 427 | * Original Auther: Rex Park 428 | * Updated: November 7, 2015 (Added support for Informs (SNMP_PDU_INFORM_REQUEST) 429 | */ 430 | 431 | uint16_t SNMPClass::writeHeaders(SNMP_PDU *pdu) 432 | { 433 | byte i; 434 | 435 | // SNMP community string 436 | if(_dstType == SNMP_PDU_SET){ 437 | for(i = _setSize-1; i >= 0 && i <= 30; i--){ 438 | _packet[_packetPos--] = (byte)_setCommName[i]; 439 | } 440 | _packet[_packetPos--] = (byte)_setSize; // length 441 | }else if(_dstType == SNMP_PDU_TRAP || _dstType == SNMP_PDU_TRAP2 || _dstType == SNMP_PDU_INFORM_REQUEST){ 442 | for(i = _trapSize-1; i >= 0 && i <= 30; i--){ 443 | _packet[_packetPos--] = (byte)_trapCommName[i]; 444 | } 445 | _packet[_packetPos--] = (byte)_trapSize; // length 446 | }else { 447 | for(i = _getSize-1; i >= 0 && i <= 30; i--){ 448 | _packet[_packetPos--] = (byte)_getCommName[i]; 449 | } 450 | _packet[_packetPos--] = (byte)_getSize; // length 451 | } 452 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_OCTETS;// type 453 | 454 | // version 455 | _packet[_packetPos--] = (byte)pdu->version;// value 456 | _packet[_packetPos--] = 0x01; // length 457 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_INT;//type 458 | 459 | //start of header 460 | //length of all data after this point 461 | _packet[_packetPos] = lsb(packet_length()+_extra_data_size); 462 | _packet[_packetPos-1] = msb(packet_length()+_extra_data_size); 463 | _packetPos -= 2; 464 | _packet[_packetPos--] = 0x82;//Sending length in two octets 465 | 466 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_SEQUENCE; 467 | 468 | //packet type 469 | if ( _dstType == SNMP_PDU_SET ) { 470 | _packetSize += _setSize; 471 | } else if ( _dstType == SNMP_PDU_TRAP ) { 472 | _packetSize += _trapSize; 473 | } else { 474 | _packetSize += _getSize; 475 | } 476 | 477 | return _packetPos; 478 | } 479 | 480 | uint32_t SNMPClass::send_message(SNMP_PDU *pdu, IPAddress to_address, uint16_t to_port, byte *temp_buff, char *extra_data) 481 | { 482 | memset(_packet, 0, SNMP_MAX_PACKET_LEN); 483 | _packetPos = SNMP_MAX_PACKET_LEN-1; 484 | int32_u u; 485 | int t = 0; 486 | _extra_data_size = 0; 487 | 488 | if(extra_data != NULL){ 489 | _extra_data_size = strlen(extra_data); 490 | } 491 | 492 | // Varbind List 493 | if(pdu->type == SNMP_PDU_RESPONSE){ 494 | 495 | t = pdu->add_data_private(&pdu->value,_packet + _packetPos,true,temp_buff, _extra_data_size); 496 | _packetPos -= t; 497 | 498 | 499 | //length of entire value being passed 500 | _packet[_packetPos--] = lsb(t+_extra_data_size); 501 | _packet[_packetPos--] = msb(t+_extra_data_size); 502 | _packet[_packetPos--] = 0x82;//Sending length in two octets 503 | 504 | }else if(pdu->type == SNMP_PDU_TRAP2 || pdu->type == SNMP_PDU_INFORM_REQUEST){ 505 | //set and increment requestId 506 | pdu->requestId = requestCounter++; 507 | 508 | for (t = pdu->value.size-1; t >= 0; t-- ) { 509 | _packet[_packetPos--] = pdu->value.data[t]; 510 | } 511 | //length of entire value being passed 512 | _packet[_packetPos--] = lsb(pdu->value.size); 513 | _packet[_packetPos--] = msb(pdu->value.size); 514 | _packet[_packetPos--] = 0x82;//Sending length in two octets 515 | } 516 | 517 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_SEQUENCE;// type 518 | 519 | // Error Index (size always 4 e.g. 4-byte int) 520 | u.int32 = pdu->errorIndex; 521 | _packet[_packetPos--] = u.data[0]; 522 | _packet[_packetPos--] = u.data[1]; 523 | _packet[_packetPos--] = u.data[2]; 524 | _packet[_packetPos--] = u.data[3]; 525 | _packet[_packetPos--] = 0x04; 526 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_INT;// type 527 | 528 | // Error (size always 4 e.g. 4-byte int) 529 | u.int32 = pdu->error; 530 | _packet[_packetPos--] = u.data[0]; 531 | _packet[_packetPos--] = u.data[1]; 532 | _packet[_packetPos--] = u.data[2]; 533 | _packet[_packetPos--] = u.data[3]; 534 | _packet[_packetPos--] = 0x04; 535 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_INT;// type 536 | 537 | // Request ID (size always 4 e.g. 4-byte int) 538 | u.int32 = pdu->requestId; 539 | _packet[_packetPos--] = u.data[0]; 540 | _packet[_packetPos--] = u.data[1]; 541 | _packet[_packetPos--] = u.data[2]; 542 | _packet[_packetPos--] = u.data[3]; 543 | _packet[_packetPos--] = 0x04; 544 | _packet[_packetPos--] = (byte)SNMP_SYNTAX_INT;// type 545 | 546 | //length value of all previous data 547 | _packet[_packetPos] = lsb(packet_length()+_extra_data_size); 548 | _packet[_packetPos-1] = msb(packet_length()+_extra_data_size); 549 | _packetPos -= 2; 550 | _packet[_packetPos--] = 0x82;//Sending length in two octets 551 | 552 | // SNMP PDU type 553 | _packet[_packetPos--] = (byte)pdu->type; 554 | 555 | //data needed for header 556 | _dstType = pdu->type; 557 | //byte header_size = this->writeHeaders(pdu); 558 | this->writeHeaders(pdu); 559 | 560 | _packetSize = packet_length(); 561 | 562 | // Serial.println("Outgoing: "); 563 | // for(byte i = 0; i < _packetSize; i++){ 564 | // Serial.print(_packet[_packetPos+1+i],HEX); 565 | // Serial.print("-"); 566 | // } 567 | // Serial.println(); 568 | 569 | this->writePacket(to_address, to_port, extra_data); 570 | 571 | return pdu->requestId; 572 | } 573 | 574 | /** 575 | * Copies an external byte array into _packet and then sends it out. 576 | * 577 | * Original Auther: Rex Park 578 | * Added: November 8, 2015 (Designed to be used with a system that resends informs that haven't been acknowledged) 579 | */ 580 | void SNMPClass::send_message(IPAddress address, uint16_t port, byte *packet, uint16_t packet_size){ 581 | Udp.beginPacket(address, port); 582 | Udp.write(packet, packet_size); 583 | Udp.endPacket(); 584 | } 585 | 586 | void SNMPClass::writePacket(IPAddress address, uint16_t port, char *extra_data) 587 | { 588 | Udp.beginPacket(address, port); 589 | Udp.write(_packet+_packetPos+1, _packetSize); 590 | 591 | if(extra_data != NULL){ 592 | Udp.write((byte*)extra_data, _extra_data_size); 593 | } 594 | 595 | Udp.endPacket(); 596 | } 597 | 598 | void SNMPClass::resend_message(IPAddress address, uint16_t port, char *extra_data) 599 | { 600 | this->writePacket(address, port, extra_data); 601 | } 602 | 603 | void SNMPClass::onPduReceive(onPduReceiveCallback pduReceived) 604 | { 605 | _callback = pduReceived; 606 | } 607 | 608 | void SNMPClass::freePdu(SNMP_PDU *pdu) 609 | { 610 | pdu->clear(); 611 | } 612 | 613 | uint16_t SNMPClass::copy_packet(byte *packet_store){ 614 | memcpy(packet_store,_packet+_packetPos+1,_packetSize); 615 | 616 | return _packetSize; 617 | } 618 | 619 | void SNMPClass::clear_packet(){ 620 | memset(_packet,0,SNMP_MAX_PACKET_LEN); 621 | } 622 | 623 | IPAddress SNMPClass::remoteIP(){ 624 | return Udp.remoteIP(); 625 | } 626 | 627 | uint16_t SNMPClass::remotePort(){ 628 | return Udp.remotePort(); 629 | } 630 | 631 | uint16_t SNMPClass::packet_length(){ 632 | return SNMP_MAX_PACKET_LEN - _packetPos - 1; 633 | } 634 | 635 | //returns the first byte of a two byte integer 636 | byte SNMPClass::msb(uint16_t num){ 637 | return num >> 8; 638 | } 639 | 640 | //returns the second byte of a two byte integer 641 | byte SNMPClass::lsb(uint16_t num){ 642 | return num & 0xFF; 643 | } 644 | 645 | uint16_t SNMPClass::combine_msb_lsb(byte msb, byte lsb){ 646 | return (msb << 8)| lsb; 647 | } 648 | 649 | // Create one global object 650 | SNMPClass SNMP; 651 | -------------------------------------------------------------------------------- /ArduinoSNMP.h: -------------------------------------------------------------------------------- 1 | /* 2 | ArduinoSNMP.h - An Arduino library for a lightweight SNMP Agent. v2.3 3 | Copyright (C) 2013 Rex Park , Portions (C) 2010 Eric C. Gionet 4 | All rights reserved. 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef ArduinoSNMP_h 22 | #define ArduinoSNMP_h 23 | 24 | #define SNMP_DEFAULT_PORT 161 25 | #define SNMP_MANAGER_PORT 162 26 | #define SNMP_MIN_OID_LEN 2 27 | #define SNMP_MAX_OID_LEN 64 // 128 28 | #define SNMP_MAX_NAME_LEN 20 29 | #define SNMP_MAX_VALUE_LEN 256 30 | #define SNMP_MAX_PACKET_LEN SNMP_MAX_VALUE_LEN + SNMP_MAX_OID_LEN + 25 //25 is arbitrary 31 | #define SNMP_FREE(s) do { if (s) { free((void *)s); s=NULL; } } while(0) 32 | //Frees a pointer only if it is !NULL and sets its value to NULL. 33 | 34 | #include "Arduino.h" 35 | #include "Udp.h" 36 | 37 | extern "C" { 38 | // callback function 39 | typedef void (*onPduReceiveCallback)(void); 40 | } 41 | 42 | //typedef long long int64_t; 43 | typedef unsigned long long uint64_t; 44 | //typedef long int32_t; 45 | //typedef unsigned long uint32_t; 46 | //typedef unsigned char uint8_t; 47 | //typedef short int16_t; 48 | //typedef unsigned short uint16_t; 49 | 50 | 51 | typedef union uint64_u { 52 | uint64_t uint64; 53 | byte data[8]; 54 | }; 55 | 56 | typedef union int32_u { 57 | int32_t int32; 58 | byte data[4]; 59 | }; 60 | 61 | typedef union uint32_u { 62 | uint32_t uint32; 63 | byte data[4]; 64 | }; 65 | 66 | typedef union int16_u { 67 | int16_t int16; 68 | byte data[2]; 69 | }; 70 | 71 | typedef union uint16_u { 72 | uint16_t uint16; 73 | byte data[2]; 74 | }; 75 | 76 | //typedef union uint16_u { 77 | // uint16_t uint16; 78 | // byte data[2]; 79 | //}; 80 | 81 | typedef enum ASN_BER_BASE_TYPES { 82 | // ASN/BER base types 83 | ASN_BER_BASE_UNIVERSAL = 0x0, 84 | ASN_BER_BASE_APPLICATION = 0x40, 85 | ASN_BER_BASE_CONTEXT = 0x80, 86 | ASN_BER_BASE_PUBLIC = 0xC0, 87 | ASN_BER_BASE_PRIMITIVE = 0x0, 88 | ASN_BER_BASE_CONSTRUCTOR = 0x20 89 | }; 90 | 91 | typedef enum SNMP_PDU_TYPES { 92 | // PDU choices 93 | SNMP_PDU_GET = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 0, 94 | SNMP_PDU_GET_NEXT = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 1, 95 | SNMP_PDU_RESPONSE = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 2, 96 | SNMP_PDU_SET = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 3, 97 | SNMP_PDU_TRAP = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 4,//obsolete 98 | SNMP_PDU_GET_BULK_REQUEST = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 5, 99 | SNMP_PDU_INFORM_REQUEST = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 6, 100 | SNMP_PDU_TRAP2 = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 7, 101 | SNMP_PDU_REPORT = ASN_BER_BASE_CONTEXT | ASN_BER_BASE_CONSTRUCTOR | 8, 102 | }; 103 | 104 | typedef enum SNMP_TRAP_TYPES { 105 | // Trap generic types: 106 | SNMP_TRAP_COLD_START = 0, 107 | SNMP_TRAP_WARM_START = 1, 108 | SNMP_TRAP_LINK_DOWN = 2, 109 | SNMP_TRAP_LINK_UP = 3, 110 | SNMP_TRAP_AUTHENTICATION_FAIL = 4, 111 | SNMP_TRAP_EGP_NEIGHBORLOSS = 5, 112 | SNMP_TRAP_ENTERPRISE_SPECIFIC = 6 113 | }; 114 | 115 | typedef enum SNMP_ERR_CODES { 116 | SNMP_ERR_NO_ERROR = 0, 117 | SNMP_ERR_TOO_BIG = 1, 118 | SNMP_ERR_NO_SUCH_NAME = 2, 119 | SNMP_ERR_BAD_VALUE = 3, 120 | SNMP_ERR_READ_ONLY = 4, 121 | SNMP_ERR_GEN_ERROR = 5, 122 | 123 | SNMP_ERR_NO_ACCESS = 6, 124 | SNMP_ERR_WRONG_TYPE = 7, 125 | SNMP_ERR_WRONG_LENGTH = 8, 126 | SNMP_ERR_WRONG_ENCODING = 9, 127 | SNMP_ERR_WRONG_VALUE = 10, 128 | SNMP_ERR_NO_CREATION = 11, 129 | SNMP_ERR_INCONSISTANT_VALUE = 12, 130 | SNMP_ERR_RESOURCE_UNAVAILABLE = 13, 131 | SNMP_ERR_COMMIT_FAILED = 14, 132 | SNMP_ERR_UNDO_FAILED = 15, 133 | SNMP_ERR_AUTHORIZATION_ERROR = 16, 134 | SNMP_ERR_NOT_WRITABLE = 17, 135 | SNMP_ERR_INCONSISTEN_NAME = 18 136 | }; 137 | 138 | typedef enum SNMP_API_STAT_CODES { 139 | SNMP_API_STAT_SUCCESS = 0, 140 | SNMP_API_STAT_MALLOC_ERR = 1, 141 | SNMP_API_STAT_NAME_TOO_BIG = 2, 142 | SNMP_API_STAT_OID_TOO_BIG = 3, 143 | SNMP_API_STAT_VALUE_TOO_BIG = 4, 144 | SNMP_API_STAT_PACKET_INVALID = 5, 145 | SNMP_API_STAT_PACKET_TOO_BIG = 6, 146 | SNMP_API_STAT_NO_SUCH_NAME = 7, 147 | }; 148 | 149 | // 150 | // http://oreilly.com/catalog/esnmp/chapter/ch02.html Table 2-1: SMIv1 Datatypes 151 | 152 | typedef enum SNMP_SYNTAXES { 153 | // SNMP ObjectSyntax values 154 | SNMP_SYNTAX_SEQUENCE = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_CONSTRUCTOR | 0x10, 155 | // These values are used in the "syntax" member of VALUEs 156 | SNMP_SYNTAX_BOOL = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 1, 157 | SNMP_SYNTAX_INT = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 2, 158 | SNMP_SYNTAX_BITS = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 3, 159 | SNMP_SYNTAX_OCTETS = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 4, 160 | SNMP_SYNTAX_NULL = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 5, 161 | SNMP_SYNTAX_OID = ASN_BER_BASE_UNIVERSAL | ASN_BER_BASE_PRIMITIVE | 6, 162 | SNMP_SYNTAX_INT32 = SNMP_SYNTAX_INT, 163 | SNMP_SYNTAX_IP_ADDRESS = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 0, 164 | SNMP_SYNTAX_COUNTER = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 1, 165 | SNMP_SYNTAX_GAUGE = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 2, 166 | SNMP_SYNTAX_TIME_TICKS = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 3, 167 | SNMP_SYNTAX_OPAQUE = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 4, 168 | SNMP_SYNTAX_NSAPADDR = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 5, 169 | SNMP_SYNTAX_COUNTER64 = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 6, 170 | SNMP_SYNTAX_UINT32 = ASN_BER_BASE_APPLICATION | ASN_BER_BASE_PRIMITIVE | 7, 171 | }; 172 | 173 | typedef struct SNMP_OID { 174 | unsigned int data[SNMP_MAX_OID_LEN]; // ushort array insted?? 175 | size_t size; 176 | 177 | /** 178 | * Decodes an OID 179 | * Decoding algorithm is based on decode_object_id from https://github.com/hallidave/ruby-snmp/blob/master/lib/snmp/ber.rb 180 | * Sets size equal to the decoded OID's legnth. 181 | * 182 | * Original Author: Rex Park 183 | * Updated: April 2, 2013 184 | */ 185 | SNMP_API_STAT_CODES decode(const byte *value, byte length){ 186 | clear(); 187 | 188 | byte value_index = 0; 189 | 190 | if(value[0] == 0x2b){ 191 | data[0] = 1; 192 | data[1] = 3; 193 | } 194 | else{ 195 | data[1] = value[0] % 40; 196 | data[0] = (value[0] - data[1]) / 40; 197 | 198 | if(data[0] > 2){ return SNMP_API_STAT_MALLOC_ERR; } 199 | } 200 | size = 2; 201 | value_index = 1; 202 | 203 | unsigned int n = 0; 204 | for(value_index; value_index < length; value_index++){ 205 | n = (n << 7) + (value[value_index] & 0x7f); 206 | if(value[value_index] < 0x80){ 207 | data[size++] = n; 208 | n = 0; 209 | if(size >= SNMP_MAX_OID_LEN){ 210 | value_index = length;//break loop 211 | return SNMP_API_STAT_OID_TOO_BIG; 212 | } 213 | } 214 | } 215 | 216 | return SNMP_API_STAT_SUCCESS; 217 | } 218 | 219 | /** 220 | * Prepares an OID for tranmission. 221 | * Sets the syntax value, encoded data length, and encodes the OID itself. 222 | * Assumes that size has been set and is accurate. 223 | * Encoding algorithm is based on encode_object_id from https://github.com/hallidave/ruby-snmp/blob/master/lib/snmp/ber.rb 224 | * 225 | * Returns the number of bytes modified in the buffer. 226 | * 227 | * Original Author: Rex Park 228 | * Updated: March 29, 2013 229 | */ 230 | byte encode(byte *buffer) { 231 | byte buffer_index = 2; 232 | 233 | if(size > 1){ 234 | if(data[0] < 2 && data[1] > 40){ return 0; }//invalid, get out of here 235 | 236 | buffer[0] = SNMP_SYNTAX_OID; 237 | 238 | buffer[buffer_index++] = 40*data[0]+data[1];//first encoded byte is a combination of first two OID values. 239 | 240 | //loop through rest of OID 241 | for(byte i = 2; i < size; i++){ 242 | if(data[i] < 0x80){ 243 | buffer[buffer_index++] = data[i]; 244 | }else{ 245 | unsigned int n = data[i]; 246 | byte octets[10]; 247 | byte octet_index = 9; 248 | 249 | while(n != 0){ 250 | octets[octet_index--] = (n & 0x7f | 0x80); 251 | n = n >> 7; 252 | } 253 | octets[9] = (octets[9] & 0x7f); 254 | octet_index++;//counteract the last decrementer 255 | 256 | while(octet_index < 10){ 257 | buffer[buffer_index++] = octets[octet_index++]; 258 | } 259 | } 260 | } 261 | } 262 | else if (size == 1) { 263 | buffer[buffer_index++] = (40*data[0]); 264 | } 265 | 266 | buffer[1] = buffer_index-2;//length of encoded OID 267 | size = buffer_index; 268 | 269 | return buffer_index; 270 | } 271 | 272 | /** 273 | * Parses OID byte data from char string. 274 | * 275 | * Original Author: Agentuino Project 276 | * Updated: Rex Park, April 4, 2013 (re-wrote integer parsing) 277 | */ 278 | byte fromString(const char *buffer){ 279 | clear(); 280 | 281 | byte b_size = strlen(buffer); 282 | byte n = 0; 283 | for(byte i = 0; i <= b_size; i++){ 284 | char t[6]; 285 | if(buffer[i] == '.' || n == 5 || buffer[i] == '\0'){ 286 | t[n] = '\0'; 287 | data[size++] = atoi(t); 288 | n = 0; 289 | }else if(n < 5){ 290 | t[n++] = buffer[i]; 291 | delay(10);//Delay needed to function on Due 292 | } 293 | } 294 | 295 | return size; 296 | } 297 | 298 | // byte fromString_P(prog_char *buffer){ 299 | // clear(); 300 | 301 | // byte b_size = strlen_P(buffer); 302 | // byte n = 0; 303 | // for(byte i = 0; i <= b_size; i++){ 304 | // char t[6]; 305 | // if(pgm_read_byte(buffer+i) == '.' || n == 5 || pgm_read_byte(buffer+i) == '\0'){ 306 | // t[n] = '\0'; 307 | // data[size++] = atoi(t); 308 | // n = 0; 309 | // }else if(n < 5){ 310 | // t[n++] = pgm_read_byte(buffer+i); 311 | // } 312 | // } 313 | 314 | // return size; 315 | // } 316 | 317 | /** 318 | * Copies OID data into a char buffer using dot notation. 319 | * 320 | * Original Author: Agentuino Project 321 | * Updated: Rex Park, April 2, 2013 (simplified process and removed hard coded 1.3 data) 322 | */ 323 | void toString(char *buffer, byte buffer_length) { 324 | if(size <= 0){ 325 | buffer[0] = '\0'; 326 | 327 | return; 328 | } 329 | 330 | char buff[16]; 331 | for(byte i = 0; i < size; i++){ 332 | utoa(data[i], buff, 10); 333 | 334 | if(strlen(buffer) + strlen(buff) + 1 < buffer_length){ 335 | if(i != 0){ 336 | strcat_P(buffer, PSTR(".")); 337 | } 338 | strcat(buffer, buff); 339 | }else{ 340 | i = size; 341 | } 342 | } 343 | 344 | buffer[buffer_length-1] = '\0';//make sure last byte is a null character 345 | } 346 | 347 | void clear(void) { 348 | memset(data, 0, SNMP_MAX_OID_LEN); 349 | size = 0; 350 | } 351 | }; 352 | 353 | /** 354 | * SNMP Value structure 355 | * data = fully encoded byte value (syntax + length of actual data + actual data) 356 | * size = length of data array. 357 | * 358 | * Original Author: Agentuino Project 359 | * Updated: Rex Park, April 3, 2013 (added comments) 360 | */ 361 | typedef struct SNMP_VALUE { 362 | byte data[SNMP_MAX_VALUE_LEN]; 363 | size_t size; 364 | SNMP_SYNTAXES syntax; 365 | SNMP_OID OID; 366 | 367 | uint16_t i; // for encoding/decoding functions 368 | 369 | // 370 | // ASN.1 decoding functions 371 | // 372 | 373 | /** 374 | * Decodes ASN Data Types: octet string, opaque syntax 375 | * to char string 376 | * 377 | * Original Author: Agentuino Project 378 | * Updated: Rex Park, April 3, 2013 (updated to account for storing syntax and length in data array) 379 | * Updated: November 13, 2013 (After resolving issues with the requestPdu and responsePdu methods, size and data are no longer stored in data for decodes) 380 | */ 381 | SNMP_ERR_CODES decode(char *value, size_t max_size) { 382 | if ( syntax == SNMP_SYNTAX_OCTETS || syntax == SNMP_SYNTAX_OID || syntax == SNMP_SYNTAX_OPAQUE ) { 383 | if ( size <= max_size ) { 384 | memcpy(value,data,size); 385 | value[size] = '\0'; 386 | 387 | return SNMP_ERR_NO_ERROR; 388 | } else { 389 | clear(); 390 | return SNMP_ERR_TOO_BIG; 391 | } 392 | } else { 393 | clear(); 394 | return SNMP_ERR_WRONG_TYPE; 395 | } 396 | } 397 | 398 | /** 399 | * Decodes ASN Data Types: int 400 | * to int16 401 | * 402 | * Original Author: Agentuino Project 403 | * Updated: Rex Park, April 3, 2013 (updated to account for storing syntax and length in data array) 404 | * Updated: November 13, 2013 (After resolving issues with the requestPdu and responsePdu methods, size and data are no longer stored in data for decodes) 405 | * Updated: December 13, 2013 (Modified decoding function to be accurate.) 406 | * Updated: November 18, 2015 (Fixed negative value decoding) 407 | */ 408 | SNMP_ERR_CODES decode(int16_t *value) { 409 | Serial.println("I'm Here"); 410 | if ( syntax == SNMP_SYNTAX_INT ) { 411 | memset(value, 0, sizeof(*value)); 412 | byte temp = (1 << 7) & data[0]; 413 | 414 | if(temp != 0){ 415 | *value = 0xFF; 416 | } 417 | 418 | for(i = 0;i < size;i++) 419 | { 420 | *value = *value<<8 | data[i]; 421 | } 422 | return SNMP_ERR_NO_ERROR; 423 | } else { 424 | clear(); 425 | return SNMP_ERR_WRONG_TYPE; 426 | } 427 | } 428 | 429 | /** 430 | * Decodes ASN Data Types: uint32, counter, time-ticks, guage 431 | * to uint16 432 | * 433 | * Original Author: Agentuino Project 434 | * Updated: December 13, 2013 (Decodes a UNIT32 into a unsigned int. Has the possibility to cut off data.) 435 | * Updated: December 13, 2013 (Modified decoding function to be accurate.) 436 | */ 437 | SNMP_ERR_CODES decode(uint16_t *value) { 438 | if ( syntax == SNMP_SYNTAX_COUNTER || syntax == SNMP_SYNTAX_TIME_TICKS 439 | || syntax == SNMP_SYNTAX_GAUGE || syntax == SNMP_SYNTAX_UINT32 ) { 440 | memset(value, 0, sizeof(*value)); 441 | 442 | for(i = 0;i < size;i++) 443 | { 444 | *value = *value<<8 | data[i]; 445 | } 446 | return SNMP_ERR_NO_ERROR; 447 | } else { 448 | clear(); 449 | return SNMP_ERR_WRONG_TYPE; 450 | } 451 | } 452 | 453 | /** 454 | * Decodes ASN Data Types: int32 455 | * to int32 456 | * 457 | * Original Author: Agentuino Project 458 | * Updated: Rex Park, April 3, 2013 (updated to account for storing syntax and length in data array) 459 | * Updated: November 13, 2013 (After resolving issues with the requestPdu and responsePdu methods, size and data are no longer stored in data for decodes) 460 | * Updated: December 13, 2013 (Modified decoding function to be accurate.) 461 | */ 462 | SNMP_ERR_CODES decode(int32_t *value) { 463 | if ( syntax == SNMP_SYNTAX_INT32 ) { 464 | memset(value, 0, sizeof(*value)); 465 | for(i = 0;i < size;i++) 466 | { 467 | *value = *value<<8 | data[i]; 468 | } 469 | return SNMP_ERR_NO_ERROR; 470 | } else { 471 | clear(); 472 | return SNMP_ERR_WRONG_TYPE; 473 | } 474 | } 475 | 476 | /** 477 | * Decodes ASN Data Types: uint32, counter, time-ticks, guage 478 | * to uint32 479 | * 480 | * Original Author: Agentuino Project 481 | * Updated: Rex Park, April 3, 2013 (updated to account for storing syntax and length in data array) 482 | * Updated: November 13, 2013 (After resolving issues with the requestPdu and responsePdu methods, size and data are no longer stored in data for decodes) 483 | * Updated: December 13, 2013 (Modified decoding function to be accurate.) 484 | */ 485 | SNMP_ERR_CODES decode(uint32_t *value) { 486 | if ( syntax == SNMP_SYNTAX_COUNTER || syntax == SNMP_SYNTAX_TIME_TICKS 487 | || syntax == SNMP_SYNTAX_GAUGE || syntax == SNMP_SYNTAX_UINT32 ) { 488 | memset(value, 0, sizeof(*value)); 489 | for(i = 0;i < size;i++) 490 | { 491 | *value = *value<<8 | data[i]; 492 | } 493 | return SNMP_ERR_NO_ERROR; 494 | } else { 495 | clear(); 496 | return SNMP_ERR_WRONG_TYPE; 497 | } 498 | } 499 | 500 | /** 501 | * Decodes ASN Data Types: ip-address, nsap-address 502 | * to byte array 503 | * 504 | * Original Author: Agentuino Project 505 | * Updated: Rex Park, April 3, 2013 (updated to account for storing syntax and length in data array) 506 | * Updated: November 13, 2013 (After resolving issues with the requestPdu and responsePdu methods, size and data are no longer stored in data for decodes) 507 | * Updated: December 14, 2013 (Data was coming in backwards) 508 | */ 509 | SNMP_ERR_CODES decode(byte *value) { 510 | if ( syntax == SNMP_SYNTAX_IP_ADDRESS || syntax == SNMP_SYNTAX_NSAPADDR ) { 511 | memcpy(value,data,size); 512 | 513 | return SNMP_ERR_NO_ERROR; 514 | } else { 515 | clear(); 516 | return SNMP_ERR_WRONG_TYPE; 517 | } 518 | } 519 | 520 | /** 521 | * Decodes ASN Data Types: boolean 522 | * to bool 523 | * 524 | * Original Author: Agentuino Project 525 | * Updated: Rex Park, April 3, 2013 (updated to account for storing syntax and length in data array) 526 | * Updated: November 13, 2013 (After resolving issues with the requestPdu and responsePdu methods, size and data are no longer stored in data for decodes) 527 | */ 528 | SNMP_ERR_CODES decode_bool(bool *value) { 529 | if ( syntax == SNMP_SYNTAX_BOOL || syntax == SNMP_SYNTAX_INT32 ) { 530 | *value = (data[0] != 0); 531 | return SNMP_ERR_NO_ERROR; 532 | } else { 533 | clear(); 534 | return SNMP_ERR_WRONG_TYPE; 535 | } 536 | } 537 | // 538 | // 539 | // ASN.1 encoding functions 540 | // 541 | 542 | /** 543 | * Encodes char string 544 | * ASN Data Types: octets and opaque 545 | * 546 | * Original Author: Agentuino Project 547 | * Updated: Rex Park, April 1, 2013 (added the ability to specify a buffer, added syntax and length) 548 | * Updated: Rex Park, November 14, 2013 (modified length to long-form) 549 | * Updated Rex Park, November 15, 2013 (modified to not add data to buffer, allows sending data that has its own buffer.) 550 | */ 551 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const char *value, byte *buffer=NULL, boolean ignore_data = false) { 552 | if(buffer == NULL){ 553 | clear(); 554 | buffer = data; 555 | } 556 | 557 | if ( syn == SNMP_SYNTAX_OCTETS || syn == SNMP_SYNTAX_OPAQUE ) { 558 | if ( strlen(value) - 1 < SNMP_MAX_VALUE_LEN || ignore_data == true) { 559 | size = strlen(value); 560 | 561 | buffer[0] = syn;//syntax 562 | buffer[1] = 0x82; 563 | buffer[2] = msb(size); 564 | buffer[3] = lsb(size); 565 | size += 4;//add in syntax & length bytes 566 | 567 | if(ignore_data == false){ 568 | for ( i = 4; i < size; i++ ) { 569 | buffer[i] = (byte)value[i-4]; 570 | } 571 | }else{ 572 | size = 4; 573 | } 574 | 575 | syntax = syn; 576 | return SNMP_ERR_NO_ERROR; 577 | } else { 578 | clear(); 579 | return SNMP_ERR_TOO_BIG; 580 | } 581 | } else { 582 | clear(); 583 | return SNMP_ERR_WRONG_TYPE; 584 | } 585 | } 586 | 587 | /** 588 | * Encodes byte array 589 | * ASN Data Types: octets and opaque 590 | * 591 | * Original Author: Agentuino Project 592 | * Updated Rex Park, January 08, 2014 (Created based off char array encoder.) 593 | */ 594 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, byte *value, byte length, byte *buffer=NULL) { 595 | if(buffer == NULL){ 596 | clear(); 597 | buffer = data; 598 | } 599 | 600 | if ( syn == SNMP_SYNTAX_OCTETS || syn == SNMP_SYNTAX_OPAQUE ) { 601 | if ( length < SNMP_MAX_VALUE_LEN) { 602 | size = length; 603 | 604 | buffer[0] = syn;//syntax 605 | buffer[1] = 0x82; 606 | buffer[2] = msb(size); 607 | buffer[3] = lsb(size); 608 | size += 4;//add in syntax & length bytes 609 | 610 | for ( i = 4; i < size; i++ ) { 611 | buffer[i] = value[i-4]; 612 | } 613 | 614 | syntax = syn; 615 | return SNMP_ERR_NO_ERROR; 616 | } else { 617 | clear(); 618 | return SNMP_ERR_TOO_BIG; 619 | } 620 | } else { 621 | clear(); 622 | return SNMP_ERR_WRONG_TYPE; 623 | } 624 | } 625 | 626 | /** 627 | * Encodes int16 (2 byte signed integer) 628 | * ASN Data Types: int and opaque 629 | * 630 | * Original Author: Agentuino Project 631 | * Updated: Rex Park, April 1, 2013 (modified encoding algorithm, added the ability to specify a buffer, added syntax and length) 632 | */ 633 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, int16_t value, byte *buffer=NULL) { 634 | if(buffer == NULL){ 635 | clear(); 636 | buffer = data; 637 | } 638 | if ( syn == SNMP_SYNTAX_INT || syn == SNMP_SYNTAX_OPAQUE ) { 639 | i = 3;//individual bytes are stored in reverse, start at the end 640 | 641 | buffer[0] = syn; 642 | buffer[1] = 2; 643 | 644 | if(value >= 0){ 645 | //i <= 5 stops an infinite loop caused by using decrementer 646 | while(i > 1 && i <= 3){ 647 | if(value > 0){ 648 | buffer[i--] = (value & 0xff); 649 | value = value >> 8; 650 | }else{ 651 | buffer[i--] = 0; 652 | } 653 | } 654 | }else{ 655 | //i <= 5 stops an infinite loop caused by using decrementer 656 | while(i > 1 && i <= 3){ 657 | if(value < 0){ 658 | buffer[i--] = (value & 0xff); 659 | value = value >> 8; 660 | }else{ 661 | buffer[i--] = 0; 662 | } 663 | } 664 | } 665 | 666 | syntax = syn; 667 | size = 4; 668 | 669 | /* hack for size bug when sending large messages*/ 670 | if(buffer[2] == 0 && buffer[3] < 0x80){ 671 | buffer[1] = 1; 672 | buffer[2] = buffer[3]; 673 | buffer[3] = 0; 674 | size = 3; 675 | } 676 | return SNMP_ERR_NO_ERROR; 677 | } else { 678 | clear(); 679 | return SNMP_ERR_WRONG_TYPE; 680 | } 681 | } 682 | 683 | /** 684 | * Encodes uint16 (2 byte unsigned integer) 685 | * ASN Data Types: uint32 686 | * 687 | * Updated: Rex Park, December 13, 2013 (Converts unsigned int into a SNMP UInT32) 688 | */ 689 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, uint16_t value, byte *buffer=NULL) { 690 | if(buffer == NULL){ 691 | clear(); 692 | buffer = data; 693 | } 694 | 695 | if(syn == SNMP_SYNTAX_UINT32){ 696 | 697 | i = 4;//individual bytes are stored in reverse 698 | 699 | buffer[0] = syn; 700 | buffer[1] = 3; 701 | buffer[2] = 0;//for compatibility with NET-SNMP 702 | 703 | while(value > 0 && i >= 0){ 704 | buffer[i--] = (value & 0xff); 705 | value = value >> 8; 706 | } 707 | 708 | syntax = syn; 709 | size = 5; 710 | return SNMP_ERR_NO_ERROR; 711 | }else{ 712 | clear(); 713 | return SNMP_ERR_WRONG_TYPE; 714 | } 715 | } 716 | 717 | /** 718 | * Encodes int32 (4 byte signed integer) 719 | * ASN Data Types: int32 and opaque 720 | * 721 | * Original Author: Agentuino Project 722 | * Updated: Rex Park, April 1, 2013 (modified encoding algorithm, added the ability to specify a buffer, added syntax and length) 723 | */ 724 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, int32_t value, byte *buffer=NULL) { 725 | if(buffer == NULL){ 726 | clear(); 727 | buffer = data; 728 | } 729 | 730 | if(syn == SNMP_SYNTAX_INT32 || syn == SNMP_SYNTAX_OPAQUE){ 731 | i = 5;//individual bytes are stored in reverse, start at the end 732 | 733 | buffer[0] = syn; 734 | buffer[1] = 4; 735 | 736 | if(value >= 0){ 737 | //i <= 5 stops an infinite loop caused by using decrementer 738 | while(i > 1 && i <= 5){ 739 | if(value > 0){ 740 | buffer[i--] = (value & 0xff); 741 | value = value >> 8; 742 | }else{ 743 | buffer[i--] = 0; 744 | } 745 | } 746 | }else{ 747 | //i <= 5 stops an infinite loop caused by using decrementer 748 | while(i > 1 && i <= 5){ 749 | if(value < 0){ 750 | buffer[i--] = (value & 0xff); 751 | value = value >> 8; 752 | }else{ 753 | buffer[i--] = 0; 754 | } 755 | } 756 | } 757 | 758 | syntax = syn; 759 | size = 6; 760 | return SNMP_ERR_NO_ERROR; 761 | } else { 762 | clear(); 763 | return SNMP_ERR_WRONG_TYPE; 764 | } 765 | } 766 | 767 | /** 768 | * Encodes uint32 (4 byte unsigned integer) 769 | * ASN Data Types: unit32, counter, time-ticks, gauge32, and opaque 770 | * 771 | * Original Author: Agentuino Project 772 | * Updated: Rex Park, March 29, 2013 (modified encoding algorithm, added the ability to specify a buffer, added syntax and length) 773 | */ 774 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, uint32_t value, byte *buffer=NULL) { 775 | if(buffer == NULL){ 776 | clear(); 777 | buffer = data; 778 | } 779 | 780 | if(syn == SNMP_SYNTAX_COUNTER || syn == SNMP_SYNTAX_TIME_TICKS 781 | || syn == SNMP_SYNTAX_GAUGE || syn == SNMP_SYNTAX_UINT32 782 | || syn == SNMP_SYNTAX_OPAQUE){ 783 | 784 | i = 5;//individual bytes are stored in reverse 785 | 786 | buffer[0] = syn; 787 | buffer[1] = 4; 788 | 789 | while(value > 0 && i >= 0){ 790 | buffer[i--] = (value & 0xff); 791 | value = value >> 8; 792 | } 793 | 794 | syntax = syn; 795 | size = 6; 796 | return SNMP_ERR_NO_ERROR; 797 | }else{ 798 | clear(); 799 | return SNMP_ERR_WRONG_TYPE; 800 | } 801 | } 802 | /** 803 | * Encodes IPAddress 804 | * ASN Data Types: ip address, nsap address, opaque 805 | * 806 | * Original Author: Agentuino Project 807 | * Updated: Rex Park, April 2, 2013 (modified encoding algorithm, added the ability to specify a buffer, added syntax and length) 808 | */ 809 | SNMP_ERR_CODES encode_address(SNMP_SYNTAXES syn, const IPAddress value, byte *buffer=NULL) { 810 | if(buffer == NULL){ 811 | clear(); 812 | buffer = data; 813 | } 814 | 815 | if ( syn == SNMP_SYNTAX_IP_ADDRESS || syn == SNMP_SYNTAX_NSAPADDR || syn == SNMP_SYNTAX_OPAQUE ) { 816 | buffer[0] = syn; 817 | buffer[1] = 4; 818 | 819 | buffer[2] = value[0]; 820 | buffer[3] = value[1]; 821 | buffer[4] = value[2]; 822 | buffer[5] = value[3]; 823 | 824 | syntax = syn; 825 | size = 6; 826 | return SNMP_ERR_NO_ERROR; 827 | } else { 828 | clear(); 829 | return SNMP_ERR_WRONG_TYPE; 830 | } 831 | } 832 | 833 | /** 834 | * Encodes boolean 835 | * ASN Data Types: boolean, opaque 836 | * 837 | * Original Author: Agentuino Project 838 | * Updated: Rex Park, April 2, 2013 (added the ability to specify a buffer, added syntax and length) 839 | * Updated: Rex Park, November 19, 2013 (BOOL is not a valid type. Encode as INT32) 840 | */ 841 | SNMP_ERR_CODES encode_bool(SNMP_SYNTAXES syn, const bool value, byte *buffer=NULL) { 842 | if(buffer == NULL){ 843 | clear(); 844 | buffer = data; 845 | } 846 | 847 | if ( syn == SNMP_SYNTAX_BOOL || syn == SNMP_SYNTAX_OPAQUE ) { 848 | buffer[0] = SNMP_SYNTAX_UINT32; 849 | buffer[1] = 1; 850 | 851 | buffer[2] = value ? 0x1 : 0x0; 852 | 853 | syntax = syn; 854 | size = 3; 855 | return SNMP_ERR_NO_ERROR; 856 | } else { 857 | clear(); 858 | return SNMP_ERR_WRONG_TYPE; 859 | } 860 | } 861 | 862 | /** 863 | * Encodes IPAddress 864 | * ASN Data Types: ip address, nsap address, opaque 865 | * 866 | * Original Author: Agentuino Project 867 | * Updated: Rex Park, April 2, 2013 (added the ability to specify a buffer, added syntax and length) 868 | */ 869 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, const uint64_t value, byte *buffer=NULL) { 870 | if(buffer == NULL){ 871 | clear(); 872 | buffer = data; 873 | } 874 | 875 | if ( syn == SNMP_SYNTAX_COUNTER64 || syn == SNMP_SYNTAX_OPAQUE ) { 876 | buffer[0] = syn; 877 | buffer[1]= 8; 878 | 879 | uint64_u tmp; 880 | tmp.uint64 = value; 881 | 882 | buffer[2] = tmp.data[7]; 883 | buffer[3] = tmp.data[6]; 884 | buffer[4] = tmp.data[5]; 885 | buffer[5] = tmp.data[4]; 886 | buffer[6] = tmp.data[3]; 887 | buffer[7] = tmp.data[2]; 888 | buffer[8] = tmp.data[1]; 889 | buffer[9] = tmp.data[0]; 890 | 891 | syntax = syn; 892 | size = 10; 893 | return SNMP_ERR_NO_ERROR; 894 | } else { 895 | clear(); 896 | return SNMP_ERR_WRONG_TYPE; 897 | } 898 | } 899 | 900 | /** 901 | * Encodes null 902 | * ASN Data Types: null, opaque 903 | * 904 | * Original Author: Agentuino Project 905 | * Updated: Rex Park, April 2, 2013 (added the ability to specify a buffer, added syntax and length) 906 | */ 907 | SNMP_ERR_CODES encode(SNMP_SYNTAXES syn, byte *buffer=NULL) { 908 | if(buffer == NULL){ 909 | clear(); 910 | buffer = data; 911 | } 912 | 913 | if ( syn == SNMP_SYNTAX_NULL || syn == SNMP_SYNTAX_OPAQUE ) { 914 | buffer[0] = syn; 915 | buffer[1] = 0; 916 | 917 | syntax = syn; 918 | size = 2; 919 | return SNMP_ERR_NO_ERROR; 920 | } else { 921 | return SNMP_ERR_WRONG_TYPE; 922 | } 923 | } 924 | 925 | // clear's buffer and sets size to 0 926 | void clear(void) { 927 | //OID.clear(); Breaks encoding 928 | memset(data, 0, SNMP_MAX_VALUE_LEN); 929 | size = 0; 930 | i = 0; 931 | } 932 | 933 | //returns the first byte of a two byte integer 934 | byte msb(uint16_t num){ 935 | return num >> 8; 936 | } 937 | 938 | //returns the second byte of a two byte integer 939 | byte lsb(uint16_t num){ 940 | return num & 0xFF; 941 | } 942 | }; 943 | 944 | typedef struct SNMP_PDU { 945 | SNMP_PDU_TYPES type; 946 | int32_t version; 947 | int32_t requestId; 948 | SNMP_ERR_CODES error; 949 | int32_t errorIndex; 950 | SNMP_VALUE value; 951 | IPAddress agent_address; 952 | 953 | /** 954 | * Adds standard v2c trap data 955 | * 956 | * Original Auther: Rex Park 957 | * Updated: April 5, 2013 958 | */ 959 | void prepare_trapv2(SNMP_VALUE *t_v){ 960 | version = 1; 961 | type = SNMP_PDU_TRAP2; 962 | error = SNMP_ERR_NO_ERROR; 963 | errorIndex = 0; 964 | 965 | //sysUpTime 966 | t_v->OID.clear(); 967 | t_v->clear(); 968 | t_v->OID.fromString("1.3.6.1.2.1.1.3.0");//OID of the value type being sent 969 | //This line below causes error, millis() type doesn't have a definite type. 970 | //This produces different candidates for one overloaded function call. 971 | //t_v->encode(SNMP_SYNTAX_TIME_TICKS, millis()/10); 972 | t_v->encode(SNMP_SYNTAX_TIME_TICKS, (const uint16_t)millis()/10); 973 | value.size = add_data_private(t_v); 974 | 975 | //SNMPv2 trapOID 976 | t_v->OID.clear(); 977 | t_v->clear(); 978 | t_v->OID.fromString("1.3.6.1.6.3.1.1.4.1.0");//OID of the value type being sent 979 | t_v->size = value.OID.encode(t_v->data); 980 | value.size = add_data_private(t_v); 981 | } 982 | 983 | /** 984 | * Adds standard v2c inform data 985 | * 986 | * Original Auther: Rex Park 987 | * Updated: October 24, 2015 988 | */ 989 | void prepare_inform(SNMP_VALUE *t_v){ 990 | prepare_trapv2(t_v); 991 | 992 | //overrides type set from prepare_trapv2 993 | type = SNMP_PDU_INFORM_REQUEST; 994 | } 995 | 996 | /** 997 | * Encodes data for transmission with the trap. 998 | * Each trap data item: 999 | * SNMP_SYNTAX_SEQUENCE 1000 | * length_of_sequence (remainder, does not include previous syntax type or length byte) 1001 | * OID Syntax 1002 | * OID length 1003 | * OID encoded bytes 1004 | * Data Syntax 1005 | * Data length 1006 | * Data encoded bytes 1007 | * 1008 | * Reverse section adds compatibility to responsePDU rewrite. 1009 | * 1010 | * Original Auther: Rex Park 1011 | * Updated: October 26, 2015 (Created: Calls add_data_private and then updates value.size. One less step for end user.) 1012 | */ 1013 | void add_data(SNMP_VALUE *data, byte *buffer=NULL, boolean reverse = false, byte *temp_buffer = NULL, int extra_data_size = 0){ 1014 | 1015 | value.size = add_data_private(data,buffer,reverse,temp_buffer,extra_data_size); 1016 | } 1017 | 1018 | /** 1019 | * Encodes data for transmission with the trap. 1020 | * Each trap data item: 1021 | * SNMP_SYNTAX_SEQUENCE 1022 | * length_of_sequence (remainder, does not include previous syntax type or length byte) 1023 | * OID Syntax 1024 | * OID length 1025 | * OID encoded bytes 1026 | * Data Syntax 1027 | * Data length 1028 | * Data encoded bytes 1029 | * 1030 | * Reverse section adds compatibility to responsePDU rewrite. 1031 | * 1032 | * Original Auther: Rex Park 1033 | * Updated: October 26, 2015 (Renamed to add_data_private) 1034 | * Updated: November 4, 2013 1035 | */ 1036 | byte add_data_private(SNMP_VALUE *data, byte *buffer=NULL, boolean reverse = false, byte *temp_buffer = NULL, int extra_data_size = 0){ 1037 | int index = 0; 1038 | byte start_index = index; 1039 | byte t_index = 0; 1040 | byte i = 0; 1041 | 1042 | if(buffer == NULL){ 1043 | buffer = value.data; 1044 | index = value.size; 1045 | start_index = index; 1046 | } 1047 | 1048 | if(reverse == true){ 1049 | //250 is arbitrary, just accounting for -1 being 255. 1050 | for(i = data->size-1; i >= 0 && i < 250; i--){//data syn + data len + data 1051 | buffer[index--] = data->data[i]; 1052 | } 1053 | 1054 | t_index = data->OID.encode(temp_buffer);//oid syn + oid len + oid data 1055 | //250 is arbitrary, just accounting for -1 being 255. 1056 | for(i = t_index-1; i >= 0 && i < 250; i--){ 1057 | buffer[index--] = temp_buffer[i]; 1058 | } 1059 | 1060 | //length of remainder of value 1061 | buffer[index] = lsb(start_index - index + extra_data_size); 1062 | buffer[index-1] = msb(start_index - index + extra_data_size); 1063 | index -= 2; 1064 | buffer[index--] = 0x82;//Sending length in two octets 1065 | buffer[index--] = SNMP_SYNTAX_SEQUENCE; 1066 | 1067 | }else{ 1068 | buffer[index++] = SNMP_SYNTAX_SEQUENCE; 1069 | //length of remainder of value 1070 | buffer[index++] = 0x82;//Sending length in two octets 1071 | buffer[index++] = 0; 1072 | buffer[index++] = 0; 1073 | 1074 | t_index = data->OID.encode(buffer+index);//oid syn + oid len + oid data 1075 | index += t_index; 1076 | 1077 | for(i = 0; i < data->size; i++){//data syn + data len + data 1078 | buffer[index++] = data->data[i]; 1079 | } 1080 | 1081 | buffer[start_index+2] = msb(index - start_index - 4 + extra_data_size);//set length 1082 | buffer[start_index+3] = lsb(index - start_index - 4 + extra_data_size); 1083 | } 1084 | 1085 | return start_index + data->size + t_index + 4;//current size of buffer data was stored in 1086 | } 1087 | 1088 | void clear(){ 1089 | version = 0; 1090 | requestId = 0; 1091 | errorIndex = 0; 1092 | error = SNMP_ERR_NO_ERROR; 1093 | value.clear(); 1094 | value.OID.clear(); 1095 | } 1096 | 1097 | //returns the first byte of a two byte integer 1098 | byte msb(uint16_t num){ 1099 | return num >> 8; 1100 | } 1101 | 1102 | //returns the second byte of a two byte integer 1103 | byte lsb(uint16_t num){ 1104 | return num & 0xFF; 1105 | } 1106 | }; 1107 | 1108 | class SNMPClass { 1109 | public: 1110 | SNMP_API_STAT_CODES begin(const char *getCommName,const char *setCommName,const char *trapComName, uint16_t port); 1111 | boolean listen(void); 1112 | SNMP_API_STAT_CODES requestPdu(SNMP_PDU *pdu, char *extra_data = NULL, int extra_data_max_size = 0); 1113 | void send_message(IPAddress address, uint16_t port, byte *packet, uint16_t packet_size); 1114 | uint32_t send_message(SNMP_PDU *pdu, IPAddress to_address, uint16_t to_port, byte* b = NULL, char *extra_data = NULL); 1115 | void resend_message(IPAddress address, uint16_t port, char *extra_data = NULL); 1116 | uint32_t sendTrapv1(SNMP_PDU *pdu, SNMP_TRAP_TYPES trap_type, int16_t specific_trap, IPAddress manager_address); 1117 | void onPduReceive(onPduReceiveCallback pduReceived); 1118 | void freePdu(SNMP_PDU *pdu); 1119 | void clear_packet(); 1120 | uint16_t copy_packet(byte *packet_store); 1121 | IPAddress remoteIP(); 1122 | uint16_t remotePort(); 1123 | uint32_t requestCounter; 1124 | 1125 | private: 1126 | uint16_t writeHeaders(SNMP_PDU *pdu); 1127 | void writePacket(IPAddress address, uint16_t port, char *extra_data = NULL); 1128 | byte _packet[SNMP_MAX_PACKET_LEN]; 1129 | uint16_t _packetSize; 1130 | uint16_t _packetPos; 1131 | uint16_t _packetTrapPos; 1132 | SNMP_PDU_TYPES _dstType; 1133 | uint8_t _dstIp[4]; 1134 | uint16_t _dstPort; 1135 | const char *_getCommName; 1136 | size_t _getSize; 1137 | const char *_setCommName; 1138 | size_t _setSize; 1139 | const char *_trapCommName; 1140 | size_t _trapSize; 1141 | onPduReceiveCallback _callback; 1142 | uint16_t packet_length(); 1143 | byte lsb(uint16_t num); 1144 | byte msb(uint16_t num); 1145 | uint16_t combine_msb_lsb(byte msb, byte lsb); 1146 | int _extra_data_size; 1147 | boolean _udp_extra_data_packet; 1148 | }; 1149 | 1150 | extern SNMPClass SNMP; 1151 | 1152 | #endif 1153 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/Actual_SNMP_Agent.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "SNMPAgent.h" 3 | #include "global.h" 4 | #include 5 | 6 | //SNMPAgent with debug enabled 7 | SNMPAgent snmp_agent = SNMPAgent(true); 8 | 9 | bool send_trap = true; 10 | 11 | void setup() { 12 | // Start serial port 13 | Serial.begin(9600); 14 | Serial.println("\n\n\n\nArduino Running..."); 15 | 16 | // Start Ethernet 17 | Ethernet.begin(mac, ip);// these are defined in global.cpp 18 | 19 | // Start SNMP Agent 20 | snmp_agent.setup(); 21 | } 22 | 23 | 24 | void loop() { 25 | char StringToSendToSNMP[80]; 26 | 27 | // listen/handle for incoming SNMP requests 28 | snmp_agent.update(); 29 | 30 | 31 | // send a basic trap with just a text string. 32 | if (send_trap) { 33 | String TrapMessage = "Whatever data you want to send."; 34 | 35 | int i = TrapMessage.length(); 36 | 37 | TrapMessage.toCharArray(StringToSendToSNMP, i+1); 38 | StringToSendToSNMP[i+1] = NULL; 39 | 40 | //Send the trap message 41 | snmp_agent.send_inform(NOTIFICATIONS_MAJOR_OID, StringToSendToSNMP); 42 | 43 | send_trap = false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/README.md: -------------------------------------------------------------------------------- 1 | SNMP Agent Example 2 | ============= 3 | 4 | This is an actual SNMP Agent implementation used in production. It has been anonymized for public distribution. 5 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/SNMPAgent.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Example Agent implementation using the ArduinoSNMP library. 3 | * Copyright (C) 2013 Rex Park 4 | * All rights reserved. 5 | * 6 | */ 7 | #include "SNMPAgent.h" 8 | #include "global.h" 9 | 10 | SNMPAgent::SNMPAgent(boolean _debug): _send_tag_data(false){ 11 | debug_enabled = true; 12 | } 13 | 14 | void SNMPAgent::setup(){ 15 | _oid_del = "."; 16 | snmp_inform_timeout = 60*SNMPTimeout; 17 | 18 | _api_status = SNMP.begin(snmp_read_community.c_str(),snmp_read_write_community.c_str(),snmp_trap_community.c_str(),SNMP_DEFAULT_PORT); 19 | 20 | if(_api_status == SNMP_API_STAT_SUCCESS){ 21 | Serial.println("SNMP Agent Started"); 22 | delay(10); 23 | 24 | }else{ 25 | Serial.println("Error starting SNMP Library"); 26 | } 27 | delay(10); 28 | } 29 | 30 | void SNMPAgent::update(){ 31 | 32 | process_inform_table(); 33 | 34 | if(SNMP.listen() == true){ 35 | process_snmp_pdu(); 36 | } 37 | } 38 | 39 | /** 40 | * SNMP PDU Handler 41 | */ 42 | void SNMPAgent::process_snmp_pdu(){ 43 | boolean success = false; 44 | boolean reply_necessary = true; 45 | 46 | _api_status = SNMP.requestPdu(&_pdu,NULL,0); 47 | 48 | Serial.println(SNMP_PDU_RECEIVED); 49 | 50 | //PDU Could Not Be Processed 51 | if(_api_status != SNMP_API_STAT_SUCCESS || _pdu.error != SNMP_ERR_NO_ERROR){ 52 | Serial.print("API Status: "); 53 | Serial.println(_api_status,true); 54 | Serial.print("Error: "); 55 | Serial.println(_pdu.error,true); 56 | Serial.println(_pdu.requestId); 57 | }//Process PDU 58 | else{ 59 | memset(_oid, '\0', SNMP_MAX_OID_LEN); 60 | _pdu.value.OID.toString(_oid,SNMP_MAX_OID_LEN); 61 | Serial.print("OID: "); 62 | Serial.println(_oid); 63 | 64 | //Process inform responses 65 | if(_pdu.type == SNMP_PDU_RESPONSE){ 66 | process_inform_response(); 67 | reply_necessary = false; 68 | }//process get/set 69 | else if(_pdu.type == SNMP_PDU_GET || _pdu.type == SNMP_PDU_SET){ 70 | 71 | //Route the message to the correct method for further processing. 72 | if(strncmp(_oid, CONFIG_OID, 21) == 0){ 73 | success = process_config_command(); 74 | } 75 | else if(strncmp(_oid, MIB2_SYS_OID, 9) == 0){ 76 | success = process_mib2_command(); 77 | } 78 | 79 | if(success == false){ 80 | Serial.println("SNMP Error: OID Not Found"); 81 | _pdu.error = SNMP_ERR_NO_SUCH_NAME; 82 | } 83 | }else{ 84 | Serial.println("SNMP: Invalid Type"); 85 | _pdu.error = SNMP_ERR_GEN_ERROR; 86 | } 87 | } 88 | 89 | //Send the response. 90 | if(reply_necessary == true && !(_api_status == SNMP_API_STAT_NO_SUCH_NAME || _api_status == SNMP_API_STAT_PACKET_INVALID)){ 91 | //send PDU response 92 | clear_buffer(big_buffer,BIG_BUFFER_SIZE); 93 | 94 | _pdu.type = SNMP_PDU_RESPONSE; 95 | 96 | if(_pdu.error != SNMP_ERR_NO_ERROR){ 97 | _pdu.value.encode(SNMP_SYNTAX_NULL); 98 | } 99 | 100 | SNMP.send_message(&_pdu, SNMP.remoteIP(), SNMP.remotePort(),(byte*)big_buffer); 101 | } 102 | 103 | //clear _pdu 104 | SNMP.freePdu(&_pdu); 105 | } 106 | 107 | /** 108 | * Process CONFIG OIDs 109 | */ 110 | boolean SNMPAgent::process_config_command(){ 111 | //Network Config Branch 112 | if(strncmp(_oid,CONFIG_NETWORK_OID,23) == 0){ 113 | 114 | if(strcmp_P(_oid,CONFIG_NETWORK_IP_OID) == 0){ 115 | if(_pdu.type == SNMP_PDU_SET){ 116 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 117 | 118 | _status = _pdu.value.decode((byte*)copy_buffer); 119 | 120 | if(_status == SNMP_ERR_NO_ERROR){ 121 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 122 | ip[0] = copy_buffer[0]; 123 | ip[1] = copy_buffer[1]; 124 | ip[2] = copy_buffer[2]; 125 | ip[3] = copy_buffer[3]; 126 | } 127 | } 128 | } 129 | 130 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, ip); 131 | _pdu.error = _status; 132 | return true; 133 | } 134 | else if(strcmp_P(_oid,CONFIG_NETWORK_GATEWAY_OID) == 0){ 135 | if(_pdu.type == SNMP_PDU_SET){ 136 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 137 | 138 | _status = _pdu.value.decode((byte*)copy_buffer); 139 | 140 | if(_status == SNMP_ERR_NO_ERROR){ 141 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 142 | gateway[0] = copy_buffer[0]; 143 | gateway[1] = copy_buffer[1]; 144 | gateway[2] = copy_buffer[2]; 145 | gateway[3] = copy_buffer[3]; 146 | } 147 | } 148 | } 149 | 150 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, gateway); 151 | _pdu.error = _status; 152 | return true; 153 | } 154 | else if(strcmp_P(_oid,CONFIG_NETWORK_DNS_OID) == 0){ 155 | if(_pdu.type == SNMP_PDU_SET){ 156 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 157 | 158 | _status = _pdu.value.decode((byte*)copy_buffer); 159 | 160 | if(_status == SNMP_ERR_NO_ERROR){ 161 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 162 | DNS[0] = copy_buffer[0]; 163 | DNS[1] = copy_buffer[1]; 164 | DNS[2] = copy_buffer[2]; 165 | DNS[3] = copy_buffer[3]; 166 | } 167 | } 168 | } 169 | 170 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, DNS); 171 | _pdu.error = _status; 172 | return true; 173 | } 174 | else if(strcmp_P(_oid,CONFIG_NETWORK_SUBNET_OID) == 0){ 175 | if(_pdu.type == SNMP_PDU_SET){ 176 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 177 | 178 | _status = _pdu.value.decode((byte*)copy_buffer); 179 | 180 | if(_status == SNMP_ERR_NO_ERROR){ 181 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 182 | subnet[0] = copy_buffer[0]; 183 | subnet[1] = copy_buffer[1]; 184 | subnet[2] = copy_buffer[2]; 185 | subnet[3] = copy_buffer[3]; 186 | } 187 | } 188 | } 189 | 190 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, subnet); 191 | _pdu.error = _status; 192 | return true; 193 | } 194 | } 195 | //SNMP CONFIG Branch 196 | else if(strncmp(_oid,CONFIG_SNMP_OID,23) == 0){ 197 | 198 | //Read Only Community String 199 | if(strcmp_P(_oid,CONFIG_SNMP_READ_STRING_OID) == 0){ 200 | if(_pdu.type == SNMP_PDU_SET){ 201 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 202 | 203 | _status = _pdu.value.decode(copy_buffer,SNMP_MAX_COMMUNITY_SIZE); 204 | 205 | if(_status == SNMP_ERR_NO_ERROR){ 206 | if(_pdu.value.size < SNMP_MAX_COMMUNITY_SIZE){ 207 | snmp_read_community = String(copy_buffer); 208 | } 209 | } 210 | } 211 | 212 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, snmp_read_community.c_str()); 213 | _pdu.error = _status; 214 | return true; 215 | } 216 | //Read Write Community String 217 | else if(strcmp_P(_oid,CONFIG_SNMP_WRITE_STRING_OID) == 0){ 218 | if(_pdu.type == SNMP_PDU_SET){ 219 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 220 | 221 | _status = _pdu.value.decode(copy_buffer,SNMP_MAX_COMMUNITY_SIZE); 222 | 223 | if(_status == SNMP_ERR_NO_ERROR){ 224 | if(_pdu.value.size < SNMP_MAX_COMMUNITY_SIZE){ 225 | snmp_read_write_community= String(copy_buffer); 226 | } 227 | } 228 | } 229 | 230 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, snmp_read_write_community.c_str()); 231 | _pdu.error = _status; 232 | return true; 233 | } 234 | //Trap Community String 235 | else if(strcmp_P(_oid,CONFIG_SNMP_TRAP_STRING_OID) == 0){ 236 | if(_pdu.type == SNMP_PDU_SET){ 237 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 238 | 239 | _status = _pdu.value.decode(copy_buffer,SNMP_MAX_COMMUNITY_SIZE); 240 | 241 | if(_status == SNMP_ERR_NO_ERROR){ 242 | if(_pdu.value.size < SNMP_MAX_COMMUNITY_SIZE){ 243 | snmp_trap_community= String(copy_buffer); 244 | } 245 | } 246 | } 247 | 248 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, snmp_trap_community.c_str()); 249 | _pdu.error = _status; 250 | return true; 251 | } 252 | //SNMP Manager IP Address 1 253 | else if(strcmp_P(_oid,CONFIG_SNMP_MANAGER_1_OID) == 0){ 254 | if(_pdu.type == SNMP_PDU_SET){ 255 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 256 | 257 | _status = _pdu.value.decode((byte*)copy_buffer); 258 | 259 | if(_status == SNMP_ERR_NO_ERROR){ 260 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 261 | SNMPIP1[0] = copy_buffer[0]; 262 | SNMPIP1[1] = copy_buffer[1]; 263 | SNMPIP1[2] = copy_buffer[2]; 264 | SNMPIP1[3] = copy_buffer[3]; 265 | } 266 | } 267 | } 268 | 269 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, SNMPIP1); 270 | _pdu.error = _status; 271 | return true; 272 | } 273 | //SNMP Manager IP Address 2 274 | else if(strcmp_P(_oid,CONFIG_SNMP_MANAGER_2_OID) == 0){ 275 | if(_pdu.type == SNMP_PDU_SET){ 276 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 277 | 278 | _status = _pdu.value.decode((byte*)copy_buffer); 279 | 280 | if(_status == SNMP_ERR_NO_ERROR){ 281 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 282 | SNMPIP2[0] = copy_buffer[0]; 283 | SNMPIP2[1] = copy_buffer[1]; 284 | SNMPIP2[2] = copy_buffer[2]; 285 | SNMPIP2[3] = copy_buffer[3]; 286 | } 287 | } 288 | } 289 | 290 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, SNMPIP2); 291 | _pdu.error = _status; 292 | return true; 293 | } 294 | else if(strcmp_P(_oid,CONFIG_SNMP_INFORM_ENABLED_OID) == 0){ 295 | if(_pdu.type == SNMP_PDU_SET){ 296 | _status = _pdu.value.decode(&temp_int); 297 | 298 | if(_status == SNMP_ERR_NO_ERROR){ 299 | if(temp_int == 0 || temp_int == 1){ 300 | SNMPInforms = temp_int; 301 | } 302 | } 303 | } 304 | 305 | _status = _pdu.value.encode(SNMP_SYNTAX_INT32, (int16_t)SNMPInforms); 306 | _pdu.error = _status; 307 | return true; 308 | } 309 | else if(strcmp_P(_oid,CONFIG_SNMP_INFORM_TIMEOUT_OID) == 0){ 310 | if(_pdu.type == SNMP_PDU_SET){ 311 | 312 | _status = _pdu.value.decode(&temp_int); 313 | 314 | if(_status == SNMP_ERR_NO_ERROR){ 315 | if(temp_int > 0 && temp_int <= 99){ 316 | SNMPTimeout = temp_int; 317 | snmp_inform_timeout = 60*SNMPTimeout; 318 | } 319 | } 320 | } 321 | 322 | _status = _pdu.value.encode(SNMP_SYNTAX_INT32, (int16_t)SNMPTimeout); 323 | _pdu.error = _status; 324 | return true; 325 | } 326 | } 327 | //Site CONFIG Branch 328 | else if(strncmp(_oid,CONFIG_SITE_OID,23) == 0){ 329 | 330 | //Site ID 331 | if(strcmp_P(_oid,CONFIG_SITE_ID_OID) == 0){ 332 | if(_pdu.type == SNMP_PDU_SET){ 333 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 334 | 335 | _status = _pdu.value.decode(copy_buffer,31); 336 | 337 | if(_status == SNMP_ERR_NO_ERROR){ 338 | if(_pdu.value.size < 31){ 339 | SiteID = String(copy_buffer); 340 | } 341 | } 342 | } 343 | 344 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, SiteID.c_str()); 345 | _pdu.error = _status; 346 | return true; 347 | } 348 | //City 349 | else if(strcmp_P(_oid,CONFIG_SITE_CITY_OID) == 0){ 350 | if(_pdu.type == SNMP_PDU_SET){ 351 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 352 | 353 | _status = _pdu.value.decode(copy_buffer,31); 354 | 355 | if(_status == SNMP_ERR_NO_ERROR){ 356 | if(_pdu.value.size < 31){ 357 | SiteCity = String(copy_buffer); 358 | } 359 | } 360 | } 361 | 362 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, SiteCity.c_str()); 363 | _pdu.error = _status; 364 | return true; 365 | } 366 | //State 367 | else if(strcmp_P(_oid,CONFIG_SITE_STATE_OID) == 0){ 368 | if(_pdu.type == SNMP_PDU_SET){ 369 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 370 | 371 | _status = _pdu.value.decode(copy_buffer,3); 372 | 373 | if(_status == SNMP_ERR_NO_ERROR){ 374 | if(_pdu.value.size < 3){ 375 | SiteState = String(copy_buffer); 376 | } 377 | } 378 | } 379 | 380 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, SiteState.c_str()); 381 | _pdu.error = _status; 382 | return true; 383 | } 384 | } 385 | //NTP CONFIG Branch 386 | else if(strncmp(_oid,CONFIG_TIME_OID,23) == 0){ 387 | 388 | //NTP IP 389 | if(strcmp_P(_oid,CONFIG_TIME_SERVER_OID) == 0){ 390 | if(_pdu.type == SNMP_PDU_SET){ 391 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 392 | 393 | _status = _pdu.value.decode((byte*)copy_buffer); 394 | 395 | if(_status == SNMP_ERR_NO_ERROR){ 396 | if((byte)copy_buffer[0] >= 0 && (byte)copy_buffer[1] >= 0 && (byte)copy_buffer[2] >= 0 && (byte)copy_buffer[3] >= 0){ 397 | tsIP[0] = copy_buffer[0]; 398 | tsIP[1] = copy_buffer[1]; 399 | tsIP[2] = copy_buffer[2]; 400 | tsIP[3] = copy_buffer[3]; 401 | } 402 | } 403 | } 404 | 405 | _status = _pdu.value.encode_address(SNMP_SYNTAX_IP_ADDRESS, tsIP); 406 | _pdu.error = _status; 407 | return true; 408 | } 409 | //NTP Enabled 410 | else if(strcmp_P(_oid,CONFIG_TIME_ENABLE_OID) == 0){ 411 | if(_pdu.type == SNMP_PDU_SET){ 412 | _status = _pdu.value.decode(&temp_int); 413 | 414 | if(_status == SNMP_ERR_NO_ERROR){ 415 | if(temp_int == 0 || temp_int == 1){ 416 | EnableTimeGet = temp_int; 417 | } 418 | } 419 | } 420 | 421 | _status = _pdu.value.encode(SNMP_SYNTAX_INT32, (int16_t)EnableTimeGet); 422 | _pdu.error = _status; 423 | return true; 424 | } 425 | //Timezone 426 | else if(strcmp_P(_oid,CONFIG_TIME_ZONE_OID) == 0){ 427 | if(_pdu.type == SNMP_PDU_SET){ 428 | 429 | _status = _pdu.value.decode(&temp_int); 430 | 431 | if(_status == SNMP_ERR_NO_ERROR){ 432 | if(temp_int >= -10 && temp_int <= 10){ 433 | timeZone = temp_int; 434 | } 435 | } 436 | } 437 | 438 | _status = _pdu.value.encode(SNMP_SYNTAX_INT32, (int16_t)timeZone); 439 | _pdu.error = _status; 440 | return true; 441 | } 442 | //User Field 443 | else if(strcmp_P(_oid,CONFIG_USER_OID) == 0){ 444 | if(_pdu.type == SNMP_PDU_SET){ 445 | clear_buffer(copy_buffer,COPY_BUFFER_SIZE); 446 | 447 | _status = _pdu.value.decode(copy_buffer,31); 448 | 449 | if(_status == SNMP_ERR_NO_ERROR){ 450 | if(_pdu.value.size < 31){ 451 | UserField = String(copy_buffer); 452 | } 453 | } 454 | } 455 | 456 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, UserField.c_str()); 457 | _pdu.error = _status; 458 | return true; 459 | } 460 | } 461 | //accept changes 462 | else if(strcmp_P(_oid,CONFIG_ACCEPT_CHANGES_OID) == 0){ 463 | 464 | if(_pdu.type == SNMP_PDU_SET){ 465 | _status = _pdu.value.decode(&temp_int); 466 | 467 | if(_status == SNMP_ERR_NO_ERROR){ 468 | if(temp_int == 0 || temp_int == 1){ 469 | accept_changes = temp_int; 470 | } 471 | } 472 | } 473 | 474 | _status = _pdu.value.encode(SNMP_SYNTAX_INT32, (int16_t)accept_changes); 475 | _pdu.error = _status; 476 | return true; 477 | } 478 | 479 | return false; 480 | } 481 | 482 | /** 483 | * Process MIB2 OIDs 484 | */ 485 | boolean SNMPAgent::process_mib2_command(){ 486 | if(strcmp(_oid,MIB2_SYS_DESC) == 0){ 487 | 488 | if(_pdu.type == SNMP_PDU_SET){ 489 | _pdu.error = SNMP_ERR_READ_ONLY; 490 | }else{ 491 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, SYS_DESCRIPTION); 492 | _pdu.error = _status; 493 | } 494 | 495 | return true; 496 | } 497 | 498 | return false; 499 | } 500 | 501 | void SNMPAgent::process_inform_table(){ 502 | for(byte i = 0; i < inform_holding_table.size(); i++){ 503 | 504 | tmp_entry = &inform_holding_table.get(i); 505 | 506 | if(now() - tmp_entry->last_sent > snmp_inform_timeout){//5 minutes, hard coded for now 507 | Serial.print("Resending inform "); 508 | Serial.print(tmp_entry->request_id); 509 | Serial.println("..."); 510 | if(SNMPIP1[0] != 0){ 511 | SNMP.send_message(SNMPIP1, SNMP_MANAGER_PORT, tmp_entry->snmp_packet, tmp_entry->packet_length); 512 | 513 | if(SNMPIP2[0] != 0){ 514 | SNMP.send_message(SNMPIP2, SNMP_MANAGER_PORT, tmp_entry->snmp_packet, tmp_entry->packet_length); 515 | } 516 | } 517 | tmp_entry->last_sent = now(); 518 | delay(1000); 519 | } 520 | } 521 | } 522 | 523 | /** 524 | * Process Inform Response 525 | * Removes entry from inform_holding_table 526 | */ 527 | boolean SNMPAgent::process_inform_response(){ 528 | for(byte i = 0; i < inform_holding_table.size(); i++){ 529 | 530 | tmp_entry = &inform_holding_table.get(i); 531 | 532 | if(tmp_entry->request_id == _pdu.requestId){ 533 | inform_holding_table.remove(i); 534 | break; 535 | } 536 | } 537 | } 538 | 539 | /** 540 | * Remove Inform from inform_holding_table 541 | */ 542 | boolean SNMPAgent::remove_inform(uint32_t request_id){ 543 | 544 | for(byte i = 0; i < inform_holding_table.size(); i++){ 545 | 546 | tmp_entry = &inform_holding_table.get(i); 547 | 548 | if(tmp_entry->request_id == request_id){ 549 | inform_holding_table.remove(i); 550 | return true; 551 | } 552 | } 553 | 554 | return false; 555 | } 556 | 557 | /** 558 | * Send SNMP Inform 559 | */ 560 | uint32_t SNMPAgent::send_inform(const char *oid, const char *data){ 561 | _pdu.clear(); 562 | _pdu.value.OID.fromString(oid);//trap oid 563 | 564 | //Pass it a value struct so it can use it for processing. Saves on overhead 565 | _pdu.prepare_inform(&_value); 566 | 567 | _value.OID.fromString(NOTIFICATIONS_OBJECT_OID);//Notification Object 568 | _value.encode(SNMP_SYNTAX_OCTETS, data); 569 | _pdu.add_data(&_value); 570 | 571 | //send it 572 | SNMP_INFORM_TABLE_ENTRY new_entry; 573 | 574 | if(SNMPIP1[0] != 0){ 575 | new_entry.request_id = SNMP.send_message(&_pdu,SNMPIP1,SNMP_MANAGER_PORT);//manager 1 576 | 577 | if(SNMPIP2[0] != 0){ 578 | SNMP.resend_message(SNMPIP2,SNMP_MANAGER_PORT);//manager 2 579 | } 580 | 581 | Serial.print("Inform "); 582 | Serial.print(new_entry.request_id); 583 | Serial.println(" sent..."); 584 | } 585 | 586 | new_entry.packet_length = SNMP.copy_packet(new_entry.snmp_packet); 587 | new_entry.last_sent = now(); 588 | inform_holding_table.add(new_entry); 589 | 590 | SNMP.clear_packet(); 591 | //clear _pdu 592 | SNMP.freePdu(&_pdu); 593 | } 594 | 595 | void SNMPAgent::clear_buffer(char buffer[], byte buffer_size){ 596 | memset(buffer,'\0',buffer_size); 597 | } 598 | 599 | void SNMPAgent::clear_buffer(byte buffer[], byte buffer_size){ 600 | memset(buffer,0,buffer_size); 601 | } 602 | 603 | void SNMPAgent::copy_message_to_buffer(const char *message, char buffer[], byte length){ 604 | clear_buffer(buffer, length); 605 | 606 | //copy up to length characters 607 | strncpy(buffer,message,length); 608 | } 609 | 610 | void SNMPAgent::concat_message_to_buffer(const char *message, char to_b[], char buffer[], byte length){ 611 | copy_message_to_buffer(message,buffer,length); 612 | strcat(to_b,buffer); 613 | } 614 | 615 | void SNMPAgent::set_next_request_id(uint32_t request_id){ 616 | SNMP.requestCounter = request_id; 617 | } 618 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/SNMPAgent.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Example Agent implementation using the ArduinoSNMP library. 3 | * Copyright (C) 2013 Rex Park 4 | * All rights reserved. 5 | * 6 | */ 7 | 8 | #ifndef __SNMP_AGENT_H__ 9 | #define __SNMP_AGENT_H__ 10 | #include 11 | #include "Arduino.h" 12 | #include 13 | #include //add to your libraries folder 14 | #include "Time.h" 15 | #include "global.h" 16 | 17 | #define SNMP_MAX_COMMUNITY_SIZE SNMP_MAX_NAME_LEN 18 | #define COPY_BUFFER_SIZE 41 19 | #define BIG_BUFFER_SIZE 80 20 | 21 | class SNMPAgent { 22 | private: 23 | SNMP_API_STAT_CODES _api_status; 24 | SNMP_ERR_CODES _status; 25 | SNMP_PDU _pdu; 26 | SNMP_VALUE _value; 27 | char _oid[SNMP_MAX_OID_LEN]; 28 | boolean _send_tag_data; 29 | char *_oid_del; 30 | unsigned long snmp_inform_timeout; 31 | 32 | SNMP_INFORM_TABLE_ENTRY *tmp_entry; 33 | 34 | int _factor; 35 | uint16_t temp_uint; 36 | int16_t temp_int; 37 | char copy_buffer[COPY_BUFFER_SIZE]; 38 | char big_buffer[BIG_BUFFER_SIZE]; 39 | 40 | boolean debug_enabled; 41 | 42 | void process_snmp_pdu(); 43 | boolean process_config_command(); 44 | boolean process_mib2_command(); 45 | 46 | void process_inform_table(); 47 | boolean process_inform_response(); 48 | void load_inform_table(); 49 | 50 | void clear_buffer(char buffer[], byte buffer_size); 51 | void clear_buffer(byte buffer[], byte buffer_size); 52 | void copy_message_to_buffer(const char *message, char buffer[], byte length); 53 | void concat_message_to_buffer(const char *message, char to_b[], char buffer[], byte length); 54 | 55 | public: 56 | SNMPAgent(boolean debug); 57 | void setup(); 58 | void update(); 59 | boolean remove_inform(uint32_t request_id); 60 | uint32_t send_inform(const char *oid, const char *data); 61 | void set_next_request_id(uint32_t request_id); 62 | }; 63 | #endif 64 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/Time.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | time.c - low level time and date functions 3 | Copyright (c) Michael Margolis 2009-2014 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | 1.0 6 Jan 2010 - initial release 20 | 1.1 12 Feb 2010 - fixed leap year calculation error 21 | 1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this) 22 | 1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update 23 | status, updated examples for Arduino 1.0, fixed ARM 24 | compatibility issues, added TimeArduinoDue and TimeTeensy3 25 | examples, add error checking and messages to RTC examples, 26 | add examples to DS1307RTC library. 27 | 1.4 5 Sep 2014 - compatibility with Arduino 1.5.7 28 | */ 29 | 30 | #if ARDUINO >= 100 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | #include "Time.h" 37 | 38 | static tmElements_t tm; // a cache of time elements 39 | static time_t cacheTime; // the time the cache was updated 40 | static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds 41 | 42 | void refreshCache(time_t t) { 43 | if (t != cacheTime) { 44 | breakTime(t, tm); 45 | cacheTime = t; 46 | } 47 | } 48 | 49 | int hour() { // the hour now 50 | return hour(now()); 51 | } 52 | 53 | int hour(time_t t) { // the hour for the given time 54 | refreshCache(t); 55 | return tm.Hour; 56 | } 57 | 58 | int hourFormat12() { // the hour now in 12 hour format 59 | return hourFormat12(now()); 60 | } 61 | 62 | int hourFormat12(time_t t) { // the hour for the given time in 12 hour format 63 | refreshCache(t); 64 | if( tm.Hour == 0 ) 65 | return 12; // 12 midnight 66 | else if( tm.Hour > 12) 67 | return tm.Hour - 12 ; 68 | else 69 | return tm.Hour ; 70 | } 71 | 72 | uint8_t isAM() { // returns true if time now is AM 73 | return !isPM(now()); 74 | } 75 | 76 | uint8_t isAM(time_t t) { // returns true if given time is AM 77 | return !isPM(t); 78 | } 79 | 80 | uint8_t isPM() { // returns true if PM 81 | return isPM(now()); 82 | } 83 | 84 | uint8_t isPM(time_t t) { // returns true if PM 85 | return (hour(t) >= 12); 86 | } 87 | 88 | int minute() { 89 | return minute(now()); 90 | } 91 | 92 | int minute(time_t t) { // the minute for the given time 93 | refreshCache(t); 94 | return tm.Minute; 95 | } 96 | 97 | int second() { 98 | return second(now()); 99 | } 100 | 101 | int second(time_t t) { // the second for the given time 102 | refreshCache(t); 103 | return tm.Second; 104 | } 105 | 106 | int day(){ 107 | return(day(now())); 108 | } 109 | 110 | int day(time_t t) { // the day for the given time (0-6) 111 | refreshCache(t); 112 | return tm.Day; 113 | } 114 | 115 | int weekday() { // Sunday is day 1 116 | return weekday(now()); 117 | } 118 | 119 | int weekday(time_t t) { 120 | refreshCache(t); 121 | return tm.Wday; 122 | } 123 | 124 | int month(){ 125 | return month(now()); 126 | } 127 | 128 | int month(time_t t) { // the month for the given time 129 | refreshCache(t); 130 | return tm.Month; 131 | } 132 | 133 | int year() { // as in Processing, the full four digit year: (2009, 2010 etc) 134 | return year(now()); 135 | } 136 | 137 | int year(time_t t) { // the year for the given time 138 | refreshCache(t); 139 | return tmYearToCalendar(tm.Year); 140 | } 141 | 142 | /*============================================================================*/ 143 | /* functions to convert to and from system time */ 144 | /* These are for interfacing with time serivces and are not normally needed in a sketch */ 145 | 146 | // leap year calulator expects year argument as years offset from 1970 147 | #define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) 148 | 149 | static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0 150 | 151 | void breakTime(time_t timeInput, tmElements_t &tm){ 152 | // break the given time_t into time components 153 | // this is a more compact version of the C library localtime function 154 | // note that year is offset from 1970 !!! 155 | 156 | uint8_t year; 157 | uint8_t month, monthLength; 158 | uint32_t time; 159 | unsigned long days; 160 | 161 | time = (uint32_t)timeInput; 162 | tm.Second = time % 60; 163 | time /= 60; // now it is minutes 164 | tm.Minute = time % 60; 165 | time /= 60; // now it is hours 166 | tm.Hour = time % 24; 167 | time /= 24; // now it is days 168 | tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1 169 | 170 | year = 0; 171 | days = 0; 172 | while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { 173 | year++; 174 | } 175 | tm.Year = year; // year is offset from 1970 176 | 177 | days -= LEAP_YEAR(year) ? 366 : 365; 178 | time -= days; // now it is days in this year, starting at 0 179 | 180 | days=0; 181 | month=0; 182 | monthLength=0; 183 | for (month=0; month<12; month++) { 184 | if (month==1) { // february 185 | if (LEAP_YEAR(year)) { 186 | monthLength=29; 187 | } else { 188 | monthLength=28; 189 | } 190 | } else { 191 | monthLength = monthDays[month]; 192 | } 193 | 194 | if (time >= monthLength) { 195 | time -= monthLength; 196 | } else { 197 | break; 198 | } 199 | } 200 | tm.Month = month + 1; // jan is month 1 201 | tm.Day = time + 1; // day of month 202 | } 203 | 204 | time_t makeTime(tmElements_t &tm){ 205 | // assemble time elements into time_t 206 | // note year argument is offset from 1970 (see macros in time.h to convert to other formats) 207 | // previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9 208 | 209 | int i; 210 | uint32_t seconds; 211 | 212 | // seconds from 1970 till 1 jan 00:00:00 of the given year 213 | seconds= tm.Year*(SECS_PER_DAY * 365); 214 | for (i = 0; i < tm.Year; i++) { 215 | if (LEAP_YEAR(i)) { 216 | seconds += SECS_PER_DAY; // add extra days for leap years 217 | } 218 | } 219 | 220 | // add days for this year, months start from 1 221 | for (i = 1; i < tm.Month; i++) { 222 | if ( (i == 2) && LEAP_YEAR(tm.Year)) { 223 | seconds += SECS_PER_DAY * 29; 224 | } else { 225 | seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0 226 | } 227 | } 228 | seconds+= (tm.Day-1) * SECS_PER_DAY; 229 | seconds+= tm.Hour * SECS_PER_HOUR; 230 | seconds+= tm.Minute * SECS_PER_MIN; 231 | seconds+= tm.Second; 232 | return (time_t)seconds; 233 | } 234 | /*=====================================================*/ 235 | /* Low level system time functions */ 236 | 237 | static uint32_t sysTime = 0; 238 | static uint32_t prevMillis = 0; 239 | static uint32_t nextSyncTime = 0; 240 | static timeStatus_t Status = timeNotSet; 241 | 242 | getExternalTime getTimePtr; // pointer to external sync function 243 | //setExternalTime setTimePtr; // not used in this version 244 | 245 | #ifdef TIME_DRIFT_INFO // define this to get drift data 246 | time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync 247 | #endif 248 | 249 | 250 | time_t now() { 251 | while (millis() - prevMillis >= 1000){ 252 | sysTime++; 253 | prevMillis += 1000; 254 | #ifdef TIME_DRIFT_INFO 255 | sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift 256 | #endif 257 | } 258 | if (nextSyncTime <= sysTime) { 259 | if (getTimePtr != 0) { 260 | time_t t = getTimePtr(); 261 | if (t != 0) { 262 | setTime(t); 263 | } else { 264 | nextSyncTime = sysTime + syncInterval; 265 | Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; 266 | } 267 | } 268 | } 269 | return (time_t)sysTime; 270 | } 271 | 272 | void setTime(time_t t) { 273 | #ifdef TIME_DRIFT_INFO 274 | if(sysUnsyncedTime == 0) 275 | sysUnsyncedTime = t; // store the time of the first call to set a valid Time 276 | #endif 277 | 278 | sysTime = (uint32_t)t; 279 | nextSyncTime = (uint32_t)t + syncInterval; 280 | Status = timeSet; 281 | prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) 282 | } 283 | 284 | void setTime(int hr,int min,int sec,int dy, int mnth, int yr){ 285 | // year can be given as full four digit year or two digts (2010 or 10 for 2010); 286 | //it is converted to years since 1970 287 | if( yr > 99) 288 | yr = yr - 1970; 289 | else 290 | yr += 30; 291 | tm.Year = yr; 292 | tm.Month = mnth; 293 | tm.Day = dy; 294 | tm.Hour = hr; 295 | tm.Minute = min; 296 | tm.Second = sec; 297 | setTime(makeTime(tm)); 298 | } 299 | 300 | void adjustTime(long adjustment) { 301 | sysTime += adjustment; 302 | } 303 | 304 | // indicates if time has been set and recently synchronized 305 | timeStatus_t timeStatus() { 306 | now(); // required to actually update the status 307 | return Status; 308 | } 309 | 310 | void setSyncProvider( getExternalTime getTimeFunction){ 311 | getTimePtr = getTimeFunction; 312 | nextSyncTime = sysTime; 313 | now(); // this will sync the clock 314 | } 315 | 316 | void setSyncInterval(time_t interval){ // set the number of seconds between re-sync 317 | syncInterval = (uint32_t)interval; 318 | nextSyncTime = sysTime + syncInterval; 319 | } 320 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/Time.h: -------------------------------------------------------------------------------- 1 | /* 2 | time.h - low level time and date functions 3 | */ 4 | 5 | /* 6 | July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this) 7 | - fixed daysToTime_t macro (thanks maniacbug) 8 | */ 9 | 10 | #ifndef _Time_h 11 | #ifdef __cplusplus 12 | #define _Time_h 13 | 14 | #include 15 | #ifndef __AVR__ 16 | #include // for __time_t_defined, but avr libc lacks sys/types.h 17 | #endif 18 | 19 | 20 | #if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc 21 | typedef unsigned long time_t; 22 | #endif 23 | 24 | 25 | // This ugly hack allows us to define C++ overloaded functions, when included 26 | // from within an extern "C", as newlib's sys/stat.h does. Actually it is 27 | // intended to include "time.h" from the C library (on ARM, but AVR does not 28 | // have that file at all). On Mac and Windows, the compiler will find this 29 | // "Time.h" instead of the C library "time.h", so we may cause other weird 30 | // and unpredictable effects by conflicting with the C library header "time.h", 31 | // but at least this hack lets us define C++ functions as intended. Hopefully 32 | // nothing too terrible will result from overriding the C library header?! 33 | extern "C++" { 34 | typedef enum {timeNotSet, timeNeedsSync, timeSet 35 | } timeStatus_t ; 36 | 37 | typedef enum { 38 | dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday 39 | } timeDayOfWeek_t; 40 | 41 | typedef enum { 42 | tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields 43 | } tmByteFields; 44 | 45 | typedef struct { 46 | uint8_t Second; 47 | uint8_t Minute; 48 | uint8_t Hour; 49 | uint8_t Wday; // day of week, sunday is day 1 50 | uint8_t Day; 51 | uint8_t Month; 52 | uint8_t Year; // offset from 1970; 53 | } tmElements_t, TimeElements, *tmElementsPtr_t; 54 | 55 | //convenience macros to convert to and from tm years 56 | #define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year 57 | #define CalendarYrToTm(Y) ((Y) - 1970) 58 | #define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000 59 | #define y2kYearToTm(Y) ((Y) + 30) 60 | 61 | typedef time_t(*getExternalTime)(); 62 | //typedef void (*setExternalTime)(const time_t); // not used in this version 63 | 64 | 65 | /*==============================================================================*/ 66 | /* Useful Constants */ 67 | #define SECS_PER_MIN (60UL) 68 | #define SECS_PER_HOUR (3600UL) 69 | #define SECS_PER_DAY (SECS_PER_HOUR * 24UL) 70 | #define DAYS_PER_WEEK (7UL) 71 | #define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK) 72 | #define SECS_PER_YEAR (SECS_PER_WEEK * 52UL) 73 | #define SECS_YR_2000 (946684800UL) // the time at the start of y2k 74 | 75 | /* Useful Macros for getting elapsed time */ 76 | #define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) 77 | #define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 78 | #define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR) 79 | #define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday 80 | #define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970 81 | #define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight 82 | // The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971 83 | // Always set the correct time before settting alarms 84 | #define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day 85 | #define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day 86 | #define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1 87 | #define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time 88 | #define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time 89 | 90 | 91 | /* Useful Macros for converting elapsed time to a time_t */ 92 | #define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) 93 | #define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) 94 | #define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011 95 | #define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) 96 | 97 | /*============================================================================*/ 98 | /* time and date functions */ 99 | int hour(); // the hour now 100 | int hour(time_t t); // the hour for the given time 101 | int hourFormat12(); // the hour now in 12 hour format 102 | int hourFormat12(time_t t); // the hour for the given time in 12 hour format 103 | uint8_t isAM(); // returns true if time now is AM 104 | uint8_t isAM(time_t t); // returns true the given time is AM 105 | uint8_t isPM(); // returns true if time now is PM 106 | uint8_t isPM(time_t t); // returns true the given time is PM 107 | int minute(); // the minute now 108 | int minute(time_t t); // the minute for the given time 109 | int second(); // the second now 110 | int second(time_t t); // the second for the given time 111 | int day(); // the day now 112 | int day(time_t t); // the day for the given time 113 | int weekday(); // the weekday now (Sunday is day 1) 114 | int weekday(time_t t); // the weekday for the given time 115 | int month(); // the month now (Jan is month 1) 116 | int month(time_t t); // the month for the given time 117 | int year(); // the full four digit year: (2009, 2010 etc) 118 | int year(time_t t); // the year for the given time 119 | 120 | time_t now(); // return the current time as seconds since Jan 1 1970 121 | void setTime(time_t t); 122 | void setTime(int hr,int min,int sec,int day, int month, int yr); 123 | void adjustTime(long adjustment); 124 | 125 | /* date strings */ 126 | #define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null) 127 | char* monthStr(uint8_t month); 128 | char* dayStr(uint8_t day); 129 | char* monthShortStr(uint8_t month); 130 | char* dayShortStr(uint8_t day); 131 | 132 | /* time sync functions */ 133 | timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized 134 | void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider 135 | void setSyncInterval(time_t interval); // set the number of seconds between re-sync 136 | 137 | /* low level functions to convert to and from system time */ 138 | void breakTime(time_t time, tmElements_t &tm); // break time_t into elements 139 | time_t makeTime(tmElements_t &tm); // convert time elements into time_t 140 | 141 | } // extern "C++" 142 | #endif // __cplusplus 143 | #endif /* _Time_h */ 144 | 145 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/global.cpp: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | //System Global Setup vars 4 | //These should be saved/loaded from EEPROM or an SD Card. 5 | IPAddress ip(192, 168, 1, 2); //local ip 6 | IPAddress gateway(0, 0, 0, 0); //gateway 7 | IPAddress subnet(0, 0, 0, 0); //subnet mask 8 | IPAddress DNS(0, 0, 0, 0); //DNS server ip address 9 | IPAddress SNMPIP1(192, 168, 1, 5); //Remote NMS for SNMP Informs #1 10 | IPAddress SNMPIP2(192, 168, 1, 6); //Remote NMS for SNMP Informs #2 11 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 12 | String SiteID = "999999"; //Site ID, value read from SD card 13 | String SiteCity = "Springfield"; // Site City 14 | String SiteState = "FX"; // Site State 15 | int SNMPInforms = 1; // SNMPInforms enabled by default, set to 0 to turn off 16 | String snmp_read_community = "public"; 17 | String snmp_read_write_community = "private"; 18 | String snmp_trap_community = "private"; 19 | IPAddress tsIP(192,168,1,1); // Time Server ip address 20 | String UserField = "User-Data"; // User Field 21 | int EnableTimeGet = 1; //Setting to allow retrieval of current time and date from the time server 22 | int SNMPTimeout = 5; // 5 minute default 23 | String Password = "1234ABCD"; 24 | int timeZone = -6; //Central Standard Time 25 | boolean accept_changes = false; 26 | 27 | LinkedList inform_holding_table = LinkedList(); 28 | 29 | /** 30 | * Read Only Strings 31 | */ 32 | const char *SYS_DESCRIPTION = {"Control Box"}; 33 | const char *SNMP_PDU_RECEIVED ={"SNMP PDU Received"}; 34 | 35 | // SNMPv2-MIB 36 | const char *MIB2_SYS_OID ={"1.3.6.1.2.1.1"}; 37 | const char *MIB2_SYS_DESC ={"1.3.6.1.2.1.1.1.0"}; 38 | const char *MIB2_SYS_OBJ ={"1.3.6.1.2.1.1.2.0"}; 39 | const char *MIB2_SYS_UPTIME ={"1.3.6.1.2.1.1.3.0"}; 40 | const char *MIB2_WARM_START_TRAP_OID ={"1.3.6.1.6.3.1.1.5.2"}; 41 | 42 | // Your OIDs, 12345 = your organizations private Enterprise OID from IANA. 43 | const char *SYS_OBJECT_ID ={"1.3.6.1.4.1.12345"}; 44 | 45 | // Config 46 | const char *CONFIG_OID ={"1.3.6.1.4.1.12345.1.1"}; 47 | const char *CONFIG_ACCEPT_CHANGES_OID ={"1.3.6.1.4.1.12345.1.1.5.0"}; 48 | 49 | // Config - Network 50 | const char *CONFIG_NETWORK_OID ={"1.3.6.1.4.1.12345.1.1.1"}; 51 | const char *CONFIG_NETWORK_IP_OID ={"1.3.6.1.4.1.12345.1.1.1.1.0"}; 52 | const char *CONFIG_NETWORK_GATEWAY_OID ={"1.3.6.1.4.1.12345.1.1.1.2.0"}; 53 | const char *CONFIG_NETWORK_SUBNET_OID ={"1.3.6.1.4.1.12345.1.1.1.3.0"}; 54 | const char *CONFIG_NETWORK_DNS_OID ={"1.3.6.1.4.1.12345.1.1.1.4.0"}; 55 | 56 | // Config - SNMP 57 | const char *CONFIG_SNMP_OID ={"1.3.6.1.4.1.12345.1.1.2"}; 58 | const char *CONFIG_SNMP_MANAGER_1_OID ={"1.3.6.1.4.1.12345.1.1.2.1.0"}; 59 | const char *CONFIG_SNMP_MANAGER_2_OID ={"1.3.6.1.4.1.12345.1.1.2.2.0"}; 60 | const char *CONFIG_SNMP_READ_STRING_OID ={"1.3.6.1.4.1.12345.1.1.2.3.0"}; 61 | const char *CONFIG_SNMP_WRITE_STRING_OID ={"1.3.6.1.4.1.12345.1.1.2.4.0"}; 62 | const char *CONFIG_SNMP_TRAP_STRING_OID ={"1.3.6.1.4.1.12345.1.1.2.5.0"}; 63 | const char *CONFIG_SNMP_INFORM_ENABLED_OID ={"1.3.6.1.4.1.12345.1.1.2.6.0"}; 64 | const char *CONFIG_SNMP_INFORM_TIMEOUT_OID ={"1.3.6.1.4.1.12345.1.1.2.7.0"}; 65 | 66 | // Config - Site 67 | const char *CONFIG_SITE_OID ={"1.3.6.1.4.1.12345.1.1.3"}; 68 | const char *CONFIG_SITE_ID_OID ={"1.3.6.1.4.1.12345.1.1.3.1.0"}; 69 | const char *CONFIG_SITE_CITY_OID ={"1.3.6.1.4.1.12345.1.1.3.2.0"}; 70 | const char *CONFIG_SITE_STATE_OID ={"1.3.6.1.4.1.12345.1.1.3.3.0"}; 71 | 72 | // Config - Time 73 | const char *CONFIG_TIME_OID ={"1.3.6.1.4.1.12345.1.1.4"}; 74 | const char *CONFIG_TIME_SERVER_OID ={"1.3.6.1.4.1.12345.1.1.4.1.0"}; 75 | const char *CONFIG_TIME_ENABLE_OID ={"1.3.6.1.4.1.12345.1.1.4.2.0"}; 76 | const char *CONFIG_TIME_ZONE_OID ={"1.3.6.1.4.1.12345.1.1.4.3.0"}; 77 | const char *CONFIG_USER_OID ={"1.3.6.1.4.1.12345.1.1.4.4.0"}; 78 | 79 | // Notifcations 80 | const char *NOTIFICATIONS_OID ={"1.3.6.1.4.1.12345.1.2"}; 81 | const char *NOTIFICATIONS_OBJECT_OID ={"1.3.6.1.4.1.12345.1.2.2"}; 82 | 83 | // Notifcations - Informs 84 | const char *NOTIFICATIONS_MAJOR_OID ={"1.3.6.1.4.1.12345.1.2.1.1"}; 85 | const char *NOTIFICATIONS_MINOR_OID ={"1.3.6.1.4.1.12345.1.2.1.2"}; 86 | const char *NOTIFICATIONS_CRITICAL_OID ={"1.3.6.1.4.1.12345.1.2.1.3"}; 87 | const char *NOTIFICATIONS_INFORMATIONAL_OID ={"1.3.6.1.4.1.12345.1.2.1.4"}; 88 | const char *NOTIFICATIONS_RECOVERY_OID ={"1.3.6.1.4.1.12345.1.2.1.5"}; 89 | -------------------------------------------------------------------------------- /Example/Actual_SNMP_Agent/global.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_H // header guards 2 | #define GLOBAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include //add to your libraries folder 8 | #include 9 | 10 | //System Global Setup vars 11 | //These should be saved/loaded from EEPROM or an SD Card. 12 | extern IPAddress ip; //local ip 13 | extern IPAddress gateway; //gateway 14 | extern IPAddress subnet; //subnet mask 15 | extern IPAddress DNS; //DNS server ip address 16 | extern IPAddress SNMPIP1; //Remote NMS for SNMP Informs #1 17 | extern IPAddress SNMPIP2; //Remote NMS for SNMP Informs #2 18 | extern byte mac[]; 19 | extern String SiteID; //Site ID, value read from SD card 20 | extern String SiteCity; // Site City 21 | extern String SiteState; // Site State 22 | extern int SNMPInforms; // SNMPInforms enabled by default, set to 0 to turn off 23 | extern String snmp_read_community; 24 | extern String snmp_read_write_community; 25 | extern String snmp_trap_community; 26 | extern IPAddress tsIP; // Time Server ip address 27 | extern String UserField; 28 | extern int EnableTimeGet; //Setting to allow retrieval of current time and date from the time server 29 | extern int SNMPTimeout; // 5 minute default 30 | extern String Password; 31 | extern int timeZone; //Eastern Standard Time 32 | extern boolean accept_changes; 33 | 34 | typedef struct SNMP_INFORM_TABLE_ENTRY { 35 | uint32_t request_id; 36 | byte snmp_packet[SNMP_MAX_PACKET_LEN]; 37 | uint16_t packet_length; 38 | time_t last_sent; 39 | }; 40 | 41 | extern LinkedList inform_holding_table; 42 | 43 | /** 44 | * Read Only Strings 45 | */ 46 | extern const char *SYS_DESCRIPTION; 47 | extern const char *SNMP_PDU_RECEIVED; 48 | 49 | // SNMPv2-MIB 50 | extern const char *MIB2_SYS_OID; 51 | extern const char *MIB2_SYS_DESC; 52 | extern const char *MIB2_SYS_OBJ; 53 | extern const char *MIB2_SYS_UPTIME; 54 | extern const char *MIB2_WARM_START_TRAP_OID; 55 | 56 | // Enterprise OIDs 57 | extern const char *SYS_OBJECT_ID; 58 | 59 | // Config 60 | extern const char *CONFIG_OID; 61 | extern const char *CONFIG_ACCEPT_CHANGES_OID; 62 | 63 | // Config - Network 64 | extern const char *CONFIG_NETWORK_OID; 65 | extern const char *CONFIG_NETWORK_IP_OID; 66 | extern const char *CONFIG_NETWORK_GATEWAY_OID; 67 | extern const char *CONFIG_NETWORK_SUBNET_OID; 68 | extern const char *CONFIG_NETWORK_DNS_OID; 69 | 70 | // Config - SNMP 71 | extern const char *CONFIG_SNMP_OID; 72 | extern const char *CONFIG_SNMP_MANAGER_1_OID; 73 | extern const char *CONFIG_SNMP_MANAGER_2_OID; 74 | extern const char *CONFIG_SNMP_READ_STRING_OID; 75 | extern const char *CONFIG_SNMP_WRITE_STRING_OID; 76 | extern const char *CONFIG_SNMP_TRAP_STRING_OID; 77 | extern const char *CONFIG_SNMP_INFORM_ENABLED_OID; 78 | extern const char *CONFIG_SNMP_INFORM_TIMEOUT_OID; 79 | 80 | // Config - Site 81 | extern const char *CONFIG_SITE_OID; 82 | extern const char *CONFIG_SITE_ID_OID; 83 | extern const char *CONFIG_SITE_CITY_OID; 84 | extern const char *CONFIG_SITE_STATE_OID; 85 | 86 | // Config - Time 87 | extern const char *CONFIG_TIME_OID; 88 | extern const char *CONFIG_TIME_SERVER_OID; 89 | extern const char *CONFIG_TIME_ENABLE_OID; 90 | extern const char *CONFIG_TIME_ZONE_OID; 91 | extern const char *CONFIG_USER_OID; 92 | 93 | // Notifcations 94 | extern const char *NOTIFICATIONS_OID; 95 | extern const char *NOTIFICATIONS_OBJECT_OID; 96 | 97 | // Notifcations - Informs 98 | extern const char *NOTIFICATIONS_MAJOR_OID; 99 | extern const char *NOTIFICATIONS_MINOR_OID; 100 | extern const char *NOTIFICATIONS_CRITICAL_OID; 101 | extern const char *NOTIFICATIONS_INFORMATIONAL_OID; 102 | extern const char *NOTIFICATIONS_RECOVERY_OID; 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /Example/snmp_trap_listener.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'snmp' 3 | require 'logger' 4 | 5 | log = Logger.new('traps.log') 6 | m = SNMP::TrapListener.new(:Port => 1062, :Community => 'public') do |manager| 7 | manager.on_trap_default do |trap| 8 | log.info trap.inspect 9 | end 10 | end 11 | m.join -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ArduinoSNMP v2.3.1 2 | ============= 3 | 4 | An updated and enhanced version of the Agentuino (http://code.google.com/p/agentuino/) SNMP library. 5 | 6 | Encoding updates referenced from Ruby-SNMP (https://github.com/hallidave/ruby-snmp). 7 | 8 | Example requires LinkedList library: https://github.com/ivanseidel/LinkedList 9 | Please note iostream issue: https://github.com/ivanseidel/LinkedList/pull/48 10 | 11 | User Guide: 12 | --------------------- 13 | 14 | July 5 2021 Update: The example agent is fairly memory intensive (~3000 bytes). If you plan to use this on a device with less memory then I suggest you learn to use the library on a MEGA 2560 then modify the example until it will fit. Moving the OIDs into PROGMEM is a good place to start. 15 | 16 | February 7 2018 Update: I've added the latest version of this library as well as a real world agent implementation (Example/Actual_SNMP_Agent). I'll try to answer any questions posted to the issues list. It's been a while since I've needed to work on this library so I'm not sure how accurate the information below is. 17 | 18 | 19 | Setup: 20 | `SNMP.begin(snmp_read_community,snmp_read_write_community,snmp_trap_community,SNMP_DEFAULT_PORT)` 21 | 22 | Receiving an SNMP Message, Processing it, Responding: 23 | ``` 24 | SNMP_API_STAT_CODES _api_status; 25 | SNMP_ERR_CODES _status; 26 | SNMP_PDU _pdu; 27 | char _oid[SNMP_MAX_OID_LEN]; 28 | 29 | //SNMP.listen() should be called often. Typically from Arduino's loop(). 30 | if(SNMP.listen() == true){ 31 | _api_status = SNMP.requestPdu(&_pdu,NULL,0); 32 | 33 | if(_api_status != SNMP_API_STAT_SUCCESS || _pdu.error != SNMP_ERR_NO_ERROR){ 34 | //Message can not be processed. 35 | } 36 | else{ 37 | memset(_oid, '\0', SNMP_MAX_OID_LEN); 38 | _pdu.value.OID.toString(_oid,SNMP_MAX_OID_LEN); 39 | //We only process GET and SET 40 | if(_pdu.type == SNMP_PDU_GET || _pdu.type == SNMP_PDU_SET){ 41 | //do something based on OID of the message 42 | if(strcmp_P(_oid,MIB2_SYS_DESC) == 0){ 43 | if(_pdu.type == SNMP_PDU_SET){ 44 | _pdu.error = SNMP_ERR_READ_ONLY; 45 | }else{ 46 | copy_message_to_buffer(SYS_DESCRIPTION,big_buffer,BIG_BUFFER_SIZE); 47 | _status = _pdu.value.encode(SNMP_SYNTAX_OCTETS, big_buffer); 48 | _pdu.error = _status; 49 | } 50 | } 51 | else{ 52 | //OID NOT FOUND 53 | _pdu.error = SNMP_ERR_NO_SUCH_NAME; 54 | } 55 | } 56 | else{ 57 | //invalid message type 58 | _pdu.error = SNMP_ERR_GEN_ERROR; 59 | } 60 | } 61 | 62 | //Send the response. 63 | if(!(_api_status == SNMP_API_STAT_NO_SUCH_NAME || _api_status == SNMP_API_STAT_PACKET_INVALID)){ 64 | 65 | //temp_buffer = byte array that can be used to hold data for processing. 66 | //Cuts down on the amount of memory required exclusively for ArduinoSNMP. 67 | byte temp_buffer[100]; 68 | 69 | _pdu.type = SNMP_PDU_RESPONSE; 70 | 71 | if(_pdu.error != SNMP_ERR_NO_ERROR){ 72 | _pdu.value.encode(SNMP_SYNTAX_NULL); 73 | } 74 | 75 | SNMP.responsePdu(&_pdu, SNMP.remoteIP(), SNMP.remotePort(),temp_buffer); 76 | } 77 | 78 | //clear _pdu 79 | SNMP.freePdu(&_pdu); 80 | } 81 | ``` 82 | 83 | Sending a Trap: 84 | ``` 85 | SNMP_PDU _pdu; 86 | SNMP_VALUE _value; 87 | 88 | _pdu.clear(); 89 | _pdu.value.OID.fromString_P(your_trap_oid);//trap oid 90 | 91 | //encode data and append it to PDUs value 92 | _pdu.value.size = 0; 93 | _pdu.prepare_trapv2(&_value); 94 | 95 | //Value 1 96 | _value.OID.fromString_P(PSTR("YOUR_DATA_OID_HERE"));//OID of the value type being sent 97 | _value.encode(SNMP_SYNTAX_INT, 10);//Sending an integer value of 10 98 | _pdu.value.size = _pdu.add_data(&_value); 99 | 100 | //Value 2 101 | _value.OID.fromString_P(PSTR("YOUR_DATA_OID_HERE"));//OID of the value type being sent 102 | _value.encode(SNMP_SYNTAX_OCTETS, "Hi There");//Send a character array 103 | _pdu.value.size = g_pdu.add_data(&g_value); 104 | 105 | //send it 106 | SNMP.responsePdu(&_pdu,snmp_manager_ip,snmp_manager_port); 107 | ``` 108 | --------------------------------------------------------------------------------