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