1/** 2 * @file 3 * Abstract Syntax Notation One (ISO 8824, 8825) encoding 4 * 5 * @todo not optimised (yet), favor correctness over speed, favor speed over size 6 */ 7 8/* 9 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without modification, 13 * are permitted provided that the following conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright notice, 16 * this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright notice, 18 * this list of conditions and the following disclaimer in the documentation 19 * and/or other materials provided with the distribution. 20 * 3. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 26 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 28 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 31 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 32 * OF SUCH DAMAGE. 33 * 34 * Author: Christiaan Simons <christiaan.simons@axon.tv> 35 * Martin Hentschel <info@cl-soft.de> 36 */ 37 38#include "lwip/apps/snmp_opts.h" 39 40#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ 41 42#include "snmp_asn1.h" 43 44#define PBUF_OP_EXEC(code) \ 45 if ((code) != ERR_OK) { \ 46 return ERR_BUF; \ 47 } 48 49/** 50 * Encodes a TLV into a pbuf stream. 51 * 52 * @param pbuf_stream points to a pbuf stream 53 * @param tlv TLV to encode 54 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 55 */ 56err_t 57snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv) 58{ 59 u8_t data; 60 u8_t length_bytes_required; 61 62 /* write type */ 63 if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { 64 /* extended format is not used by SNMP so we do not accept those values */ 65 return ERR_ARG; 66 } 67 if (tlv->type_len != 0) { 68 /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */ 69 return ERR_ARG; 70 } 71 72 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type)); 73 tlv->type_len = 1; 74 75 /* write length */ 76 if (tlv->value_len <= 127) { 77 length_bytes_required = 1; 78 } else if (tlv->value_len <= 255) { 79 length_bytes_required = 2; 80 } else { 81 length_bytes_required = 3; 82 } 83 84 /* check for forced min length */ 85 if (tlv->length_len > 0) { 86 if (tlv->length_len < length_bytes_required) { 87 /* unable to code requested length in requested number of bytes */ 88 return ERR_ARG; 89 } 90 91 length_bytes_required = tlv->length_len; 92 } else { 93 tlv->length_len = length_bytes_required; 94 } 95 96 if (length_bytes_required > 1) { 97 /* multi byte representation required */ 98 length_bytes_required--; 99 data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */ 100 101 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); 102 103 while (length_bytes_required > 1) { 104 if (length_bytes_required == 2) { 105 /* append high byte */ 106 data = (u8_t)(tlv->value_len >> 8); 107 } else { 108 /* append leading 0x00 */ 109 data = 0x00; 110 } 111 112 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); 113 length_bytes_required--; 114 } 115 } 116 117 /* append low byte */ 118 data = (u8_t)(tlv->value_len & 0xFF); 119 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); 120 121 return ERR_OK; 122} 123 124/** 125 * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. 126 * 127 * @param pbuf_stream points to a pbuf stream 128 * @param raw_len raw data length 129 * @param raw points raw data 130 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 131 */ 132err_t 133snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len) 134{ 135 PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len)); 136 137 return ERR_OK; 138} 139 140/** 141 * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. 142 * 143 * @param pbuf_stream points to a pbuf stream 144 * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) 145 * @param value is the host order u32_t value to be encoded 146 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 147 * 148 * @see snmp_asn1_enc_u32t_cnt() 149 */ 150err_t 151snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value) 152{ 153 if (octets_needed > 5) { 154 return ERR_ARG; 155 } 156 if (octets_needed == 5) { 157 /* not enough bits in 'value' add leading 0x00 */ 158 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); 159 octets_needed--; 160 } 161 162 while (octets_needed > 1) { 163 octets_needed--; 164 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); 165 } 166 167 /* (only) one least significant octet */ 168 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); 169 170 return ERR_OK; 171} 172/** 173 * Encodes s32_t integer into a pbuf chained ASN1 msg. 174 * 175 * @param pbuf_stream points to a pbuf stream 176 * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) 177 * @param value is the host order s32_t value to be encoded 178 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 179 * 180 * @see snmp_asn1_enc_s32t_cnt() 181 */ 182err_t 183snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value) 184{ 185 while (octets_needed > 1) { 186 octets_needed--; 187 188 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); 189 } 190 191 /* (only) one least significant octet */ 192 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); 193 194 return ERR_OK; 195} 196 197/** 198 * Encodes object identifier into a pbuf chained ASN1 msg. 199 * 200 * @param pbuf_stream points to a pbuf stream 201 * @param oid points to object identifier array 202 * @param oid_len object identifier array length 203 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 204 */ 205err_t 206snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len) 207{ 208 if (oid_len > 1) { 209 /* write compressed first two sub id's */ 210 u32_t compressed_byte = ((oid[0] * 40) + oid[1]); 211 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte)); 212 oid_len -= 2; 213 oid += 2; 214 } else { 215 /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ 216 /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ 217 return ERR_ARG; 218 } 219 220 while (oid_len > 0) { 221 u32_t sub_id; 222 u8_t shift, tail; 223 224 oid_len--; 225 sub_id = *oid; 226 tail = 0; 227 shift = 28; 228 while (shift > 0) { 229 u8_t code; 230 231 code = (u8_t)(sub_id >> shift); 232 if ((code != 0) || (tail != 0)) { 233 tail = 1; 234 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80)); 235 } 236 shift -= 7; 237 } 238 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F)); 239 240 /* proceed to next sub-identifier */ 241 oid++; 242 } 243 return ERR_OK; 244} 245 246/** 247 * Returns octet count for length. 248 * 249 * @param length parameter length 250 * @param octets_needed points to the return value 251 */ 252void 253snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) 254{ 255 if (length < 0x80U) { 256 *octets_needed = 1; 257 } else if (length < 0x100U) { 258 *octets_needed = 2; 259 } else { 260 *octets_needed = 3; 261 } 262} 263 264/** 265 * Returns octet count for an u32_t. 266 * 267 * @param value value to be encoded 268 * @param octets_needed points to the return value 269 * 270 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 271 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 272 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! 273 */ 274void 275snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) 276{ 277 if (value < 0x80UL) { 278 *octets_needed = 1; 279 } else if (value < 0x8000UL) { 280 *octets_needed = 2; 281 } else if (value < 0x800000UL) { 282 *octets_needed = 3; 283 } else if (value < 0x80000000UL) { 284 *octets_needed = 4; 285 } else { 286 *octets_needed = 5; 287 } 288} 289 290/** 291 * Returns octet count for an s32_t. 292 * 293 * @param value value to be encoded 294 * @param octets_needed points to the return value 295 * 296 * @note ASN coded integers are _always_ signed. 297 */ 298void 299snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) 300{ 301 if (value < 0) { 302 value = ~value; 303 } 304 if (value < 0x80L) { 305 *octets_needed = 1; 306 } else if (value < 0x8000L) { 307 *octets_needed = 2; 308 } else if (value < 0x800000L) { 309 *octets_needed = 3; 310 } else { 311 *octets_needed = 4; 312 } 313} 314 315/** 316 * Returns octet count for an object identifier. 317 * 318 * @param oid points to object identifier array 319 * @param oid_len object identifier array length 320 * @param octets_needed points to the return value 321 */ 322void 323snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed) 324{ 325 u32_t sub_id; 326 327 *octets_needed = 0; 328 if (oid_len > 1) { 329 /* compressed prefix in one octet */ 330 (*octets_needed)++; 331 oid_len -= 2; 332 oid += 2; 333 } 334 while (oid_len > 0) { 335 oid_len--; 336 sub_id = *oid; 337 338 sub_id >>= 7; 339 (*octets_needed)++; 340 while (sub_id > 0) { 341 sub_id >>= 7; 342 (*octets_needed)++; 343 } 344 oid++; 345 } 346} 347 348/** 349 * Decodes a TLV from a pbuf stream. 350 * 351 * @param pbuf_stream points to a pbuf stream 352 * @param tlv returns decoded TLV 353 * @return ERR_OK if successful, ERR_VAL if we can't decode 354 */ 355err_t 356snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv) 357{ 358 u8_t data; 359 360 /* decode type first */ 361 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 362 tlv->type = data; 363 364 if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { 365 /* extended format is not used by SNMP so we do not accept those values */ 366 return ERR_VAL; 367 } 368 tlv->type_len = 1; 369 370 /* now, decode length */ 371 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 372 373 if (data < 0x80) { /* short form */ 374 tlv->length_len = 1; 375 tlv->value_len = data; 376 } else if (data > 0x80) { /* long form */ 377 u8_t length_bytes = data - 0x80; 378 if (length_bytes > pbuf_stream->length) { 379 return ERR_VAL; 380 } 381 tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */ 382 tlv->value_len = 0; 383 384 while (length_bytes > 0) { 385 /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */ 386 if (tlv->value_len > 0xFF) { 387 return ERR_VAL; 388 } 389 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 390 tlv->value_len <<= 8; 391 tlv->value_len |= data; 392 393 /* take care for special value used for indefinite length */ 394 if (tlv->value_len == 0xFFFF) { 395 return ERR_VAL; 396 } 397 398 length_bytes--; 399 } 400 } else { /* data == 0x80 indefinite length form */ 401 /* (not allowed for SNMP; RFC 1157, 3.2.2) */ 402 return ERR_VAL; 403 } 404 405 return ERR_OK; 406} 407 408/** 409 * Decodes positive integer (counter, gauge, timeticks) into u32_t. 410 * 411 * @param pbuf_stream points to a pbuf stream 412 * @param len length of the coded integer field 413 * @param value return host order integer 414 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 415 * 416 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 417 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 418 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! 419 */ 420err_t 421snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value) 422{ 423 u8_t data; 424 425 if ((len > 0) && (len <= 5)) { 426 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 427 428 /* expecting sign bit to be zero, only unsigned please! */ 429 if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) { 430 *value = data; 431 len--; 432 433 while (len > 0) { 434 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 435 len--; 436 437 *value <<= 8; 438 *value |= data; 439 } 440 441 return ERR_OK; 442 } 443 } 444 445 return ERR_VAL; 446} 447 448/** 449 * Decodes integer into s32_t. 450 * 451 * @param pbuf_stream points to a pbuf stream 452 * @param len length of the coded integer field 453 * @param value return host order integer 454 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 455 * 456 * @note ASN coded integers are _always_ signed! 457 */ 458err_t 459snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value) 460{ 461 u8_t data; 462 463 if ((len > 0) && (len < 5)) { 464 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 465 466 if (data & 0x80) { 467 /* negative, start from -1 */ 468 *value = -1; 469 *value = (*value << 8) | data; 470 } else { 471 /* positive, start from 0 */ 472 *value = data; 473 } 474 len--; 475 /* shift in the remaining value */ 476 while (len > 0) { 477 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 478 *value = (*value << 8) | data; 479 len--; 480 } 481 return ERR_OK; 482 } 483 484 return ERR_VAL; 485} 486 487/** 488 * Decodes object identifier from incoming message into array of u32_t. 489 * 490 * @param pbuf_stream points to a pbuf stream 491 * @param len length of the coded object identifier 492 * @param oid return decoded object identifier 493 * @param oid_len return decoded object identifier length 494 * @param oid_max_len size of oid buffer 495 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 496 */ 497err_t 498snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len) 499{ 500 u32_t *oid_ptr; 501 u8_t data; 502 503 *oid_len = 0; 504 oid_ptr = oid; 505 if (len > 0) { 506 if (oid_max_len < 2) { 507 return ERR_MEM; 508 } 509 510 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 511 len--; 512 513 /* first compressed octet */ 514 if (data == 0x2B) { 515 /* (most) common case 1.3 (iso.org) */ 516 *oid_ptr = 1; 517 oid_ptr++; 518 *oid_ptr = 3; 519 oid_ptr++; 520 } else if (data < 40) { 521 *oid_ptr = 0; 522 oid_ptr++; 523 *oid_ptr = data; 524 oid_ptr++; 525 } else if (data < 80) { 526 *oid_ptr = 1; 527 oid_ptr++; 528 *oid_ptr = data - 40; 529 oid_ptr++; 530 } else { 531 *oid_ptr = 2; 532 oid_ptr++; 533 *oid_ptr = data - 80; 534 oid_ptr++; 535 } 536 *oid_len = 2; 537 } else { 538 /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */ 539 return ERR_OK; 540 } 541 542 while ((len > 0) && (*oid_len < oid_max_len)) { 543 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 544 len--; 545 546 if ((data & 0x80) == 0x00) { 547 /* sub-identifier uses single octet */ 548 *oid_ptr = data; 549 } else { 550 /* sub-identifier uses multiple octets */ 551 u32_t sub_id = (data & ~0x80); 552 while ((len > 0) && ((data & 0x80) != 0)) { 553 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 554 len--; 555 556 sub_id = (sub_id << 7) + (data & ~0x80); 557 } 558 559 if ((data & 0x80) != 0) { 560 /* "more bytes following" bit still set at end of len */ 561 return ERR_VAL; 562 } 563 *oid_ptr = sub_id; 564 } 565 oid_ptr++; 566 (*oid_len)++; 567 } 568 569 if (len > 0) { 570 /* OID to long to fit in our buffer */ 571 return ERR_MEM; 572 } 573 574 return ERR_OK; 575} 576 577/** 578 * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) 579 * from incoming message into array. 580 * 581 * @param pbuf_stream points to a pbuf stream 582 * @param len length of the coded raw data (zero is valid, e.g. empty string!) 583 * @param buf return raw bytes 584 * @param buf_len returns length of the raw return value 585 * @param buf_max_len buffer size 586 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 587 */ 588err_t 589snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len) 590{ 591 if (len > buf_max_len) { 592 /* not enough dst space */ 593 return ERR_MEM; 594 } 595 *buf_len = len; 596 597 while (len > 0) { 598 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf)); 599 buf++; 600 len--; 601 } 602 603 return ERR_OK; 604} 605 606#if LWIP_HAVE_INT64 607/** 608 * Returns octet count for an u64_t. 609 * 610 * @param value value to be encoded 611 * @param octets_needed points to the return value 612 * 613 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 614 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 615 * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!! 616 */ 617void 618snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed) 619{ 620 /* check if high u32 is 0 */ 621 if ((value >> 32) == 0) { 622 /* only low u32 is important */ 623 snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed); 624 } else { 625 /* low u32 does not matter for length determination */ 626 snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed); 627 *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */ 628 } 629} 630 631/** 632 * Decodes large positive integer (counter64) into 2x u32_t. 633 * 634 * @param pbuf_stream points to a pbuf stream 635 * @param len length of the coded integer field 636 * @param value return 64 bit integer 637 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 638 * 639 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 640 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 641 * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!! 642 */ 643err_t 644snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value) 645{ 646 u8_t data; 647 648 if ((len > 0) && (len <= 9)) { 649 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 650 651 /* expecting sign bit to be zero, only unsigned please! */ 652 if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) { 653 *value = data; 654 len--; 655 656 while (len > 0) { 657 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 658 *value <<= 8; 659 *value |= data; 660 len--; 661 } 662 663 return ERR_OK; 664 } 665 } 666 667 return ERR_VAL; 668} 669 670/** 671 * Encodes u64_t (counter64) into a pbuf chained ASN1 msg. 672 * 673 * @param pbuf_stream points to a pbuf stream 674 * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) 675 * @param value is the value to be encoded 676 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 677 * 678 * @see snmp_asn1_enc_u64t_cnt() 679 */ 680err_t 681snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value) 682{ 683 if (octets_needed > 9) { 684 return ERR_ARG; 685 } 686 if (octets_needed == 9) { 687 /* not enough bits in 'value' add leading 0x00 */ 688 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); 689 octets_needed--; 690 } 691 692 while (octets_needed > 1) { 693 octets_needed--; 694 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); 695 } 696 697 /* always write at least one octet (also in case of value == 0) */ 698 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value))); 699 700 return ERR_OK; 701} 702#endif 703 704#endif /* LWIP_SNMP */ 705