STM32F769IDiscovery  1.00
uDANTE Audio Networking with STM32F7 DISCO board
snmp_asn1.c
Go to the documentation of this file.
1 
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 err_t
50 snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
51 {
52  u8_t data;
53  u8_t length_bytes_required;
54 
55  /* write type */
56  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
57  /* extended format is not used by SNMP so we do not accept those values */
58  return ERR_ARG;
59  }
60  if (tlv->type_len != 0) {
61  /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
62  return ERR_ARG;
63  }
64 
65  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
66  tlv->type_len = 1;
67 
68  /* write length */
69  if (tlv->value_len <= 127) {
70  length_bytes_required = 1;
71  } else if (tlv->value_len <= 255) {
72  length_bytes_required = 2;
73  } else {
74  length_bytes_required = 3;
75  }
76 
77  /* check for forced min length */
78  if (tlv->length_len > 0) {
79  if (tlv->length_len < length_bytes_required) {
80  /* unable to code requested length in requested number of bytes */
81  return ERR_ARG;
82  }
83 
84  length_bytes_required = tlv->length_len;
85  } else {
86  tlv->length_len = length_bytes_required;
87  }
88 
89  if (length_bytes_required > 1) {
90  /* multi byte representation required */
91  length_bytes_required--;
92  data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
93 
94  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
95 
96  while (length_bytes_required > 1)
97  {
98  if (length_bytes_required == 2) {
99  /* append high byte */
100  data = (u8_t)(tlv->value_len >> 8);
101  } else {
102  /* append leading 0x00 */
103  data = 0x00;
104  }
105 
106  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
107  length_bytes_required--;
108  }
109  }
110 
111  /* append low byte */
112  data = (u8_t)(tlv->value_len & 0xFF);
113  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
114 
115  return ERR_OK;
116 }
117 
127 err_t
128 snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
129 {
130  PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
131 
132  return ERR_OK;
133 }
134 
146 err_t
147 snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
148 {
149  if (octets_needed > 5) {
150  return ERR_ARG;
151  }
152  if (octets_needed == 5) {
153  /* not enough bits in 'value' add leading 0x00 */
154  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
155  octets_needed--;
156  }
157 
158  while (octets_needed > 1) {
159  octets_needed--;
160  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
161  }
162 
163  /* (only) one least significant octet */
164  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
165 
166  return ERR_OK;
167 }
168 
180 err_t
181 snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
182 {
183  if (octets_needed > 9) {
184  return ERR_ARG;
185  }
186  if (octets_needed == 9) {
187  /* not enough bits in 'value' add leading 0x00 */
188  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
189  octets_needed--;
190  }
191 
192  while (octets_needed > 4) {
193  octets_needed--;
194  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
195  }
196 
197  /* skip to low u32 */
198  value++;
199 
200  while (octets_needed > 1) {
201  octets_needed--;
202  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
203  }
204 
205  /* always write at least one octet (also in case of value == 0) */
206  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
207 
208  return ERR_OK;
209 }
210 
222 err_t
223 snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
224 {
225  while (octets_needed > 1) {
226  octets_needed--;
227 
228  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
229  }
230 
231  /* (only) one least significant octet */
232  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
233 
234  return ERR_OK;
235 }
236 
246 err_t
247 snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
248 {
249  if (oid_len > 1) {
250  /* write compressed first two sub id's */
251  u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
252  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
253  oid_len -= 2;
254  oid += 2;
255  } else {
256  /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
257  /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
258  return ERR_ARG;
259  }
260 
261  while (oid_len > 0) {
262  u32_t sub_id;
263  u8_t shift, tail;
264 
265  oid_len--;
266  sub_id = *oid;
267  tail = 0;
268  shift = 28;
269  while (shift > 0) {
270  u8_t code;
271 
272  code = (u8_t)(sub_id >> shift);
273  if ((code != 0) || (tail != 0)) {
274  tail = 1;
275  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
276  }
277  shift -= 7;
278  }
279  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
280 
281  /* proceed to next sub-identifier */
282  oid++;
283  }
284  return ERR_OK;
285 }
286 
293 void
294 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
295 {
296  if (length < 0x80U) {
297  *octets_needed = 1;
298  } else if (length < 0x100U) {
299  *octets_needed = 2;
300  } else {
301  *octets_needed = 3;
302  }
303 }
304 
315 void
316 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
317 {
318  if (value < 0x80UL) {
319  *octets_needed = 1;
320  } else if (value < 0x8000UL) {
321  *octets_needed = 2;
322  } else if (value < 0x800000UL) {
323  *octets_needed = 3;
324  } else if (value < 0x80000000UL) {
325  *octets_needed = 4;
326  } else {
327  *octets_needed = 5;
328  }
329 }
330 
341 void
342 snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
343 {
344  /* check if high u32 is 0 */
345  if (*value == 0x00)
346  {
347  /* only low u32 is important */
348  value++;
349  snmp_asn1_enc_u32t_cnt(*value, octets_needed);
350  } else {
351  /* low u32 does not matter for length determination */
352  snmp_asn1_enc_u32t_cnt(*value, octets_needed);
353  *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
354  }
355 }
356 
365 void
366 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
367 {
368  if (value < 0) {
369  value = ~value;
370  }
371  if (value < 0x80L) {
372  *octets_needed = 1;
373  } else if (value < 0x8000L) {
374  *octets_needed = 2;
375  } else if (value < 0x800000L) {
376  *octets_needed = 3;
377  } else {
378  *octets_needed = 4;
379  }
380 }
381 
389 void
390 snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
391 {
392  u32_t sub_id;
393 
394  *octets_needed = 0;
395  if (oid_len > 1) {
396  /* compressed prefix in one octet */
397  (*octets_needed)++;
398  oid_len -= 2;
399  oid += 2;
400  }
401  while (oid_len > 0) {
402  oid_len--;
403  sub_id = *oid;
404 
405  sub_id >>= 7;
406  (*octets_needed)++;
407  while (sub_id > 0) {
408  sub_id >>= 7;
409  (*octets_needed)++;
410  }
411  oid++;
412  }
413 }
414 
415 err_t
416 snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
417 {
418  u8_t data;
419 
420  /* decode type first */
421  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
422  tlv->type = data;
423 
424  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
425  /* extended format is not used by SNMP so we do not accept those values */
426  return ERR_VAL;
427  }
428  tlv->type_len = 1;
429 
430  /* now, decode length */
431  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
432 
433  if (data < 0x80) { /* short form */
434  tlv->length_len = 1;
435  tlv->value_len = data;
436  } else if (data > 0x80) { /* long form */
437  u8_t length_bytes = data - 0x80;
438  tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
439  tlv->value_len = 0;
440 
441  while (length_bytes > 0) {
442  /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
443  if (tlv->value_len > 0xFF) {
444  return ERR_VAL;
445  }
446  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
447  tlv->value_len <<= 8;
448  tlv->value_len |= data;
449 
450  /* take care for special value used for indefinite length */
451  if (tlv->value_len == 0xFFFF) {
452  return ERR_VAL;
453  }
454 
455  length_bytes--;
456  }
457  } else { /* data == 0x80 indefinite length form */
458  /* (not allowed for SNMP; RFC 1157, 3.2.2) */
459  return ERR_VAL;
460  }
461 
462  return ERR_OK;
463 }
464 
478 err_t
479 snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
480 {
481  u8_t data;
482 
483  if ((len > 0) && (len <= 5)) {
484  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
485 
486  /* expecting sign bit to be zero, only unsigned please! */
487  if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
488  *value = data;
489  len--;
490 
491  while (len > 0)
492  {
493  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
494  len--;
495 
496  *value <<= 8;
497  *value |= data;
498  }
499 
500  return ERR_OK;
501  }
502  }
503 
504  return ERR_VAL;
505 }
506 
520 err_t
521 snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
522 {
523  u8_t data;
524 
525  if (len <= 4) {
526  /* high u32 is 0 */
527  *value = 0;
528  /* directly skip to low u32 */
529  value++;
530  }
531 
532  if ((len > 0) && (len <= 9)) {
533  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
534 
535  /* expecting sign bit to be zero, only unsigned please! */
536  if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
537  *value = data;
538  len--;
539 
540  while (len > 0)
541  {
542  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
543 
544  if (len == 4) {
545  /* skip to low u32 */
546  value++;
547  *value = 0;
548  } else {
549  *value <<= 8;
550  }
551 
552  *value |= data;
553  len--;
554  }
555 
556  return ERR_OK;
557  }
558  }
559 
560  return ERR_VAL;
561 }
562 
574 err_t
575 snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
576 {
577 #if BYTE_ORDER == LITTLE_ENDIAN
578  u8_t *lsb_ptr = (u8_t*)value;
579 #endif
580 #if BYTE_ORDER == BIG_ENDIAN
581  u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
582 #endif
583  u8_t sign;
584  u8_t data;
585 
586  if ((len > 0) && (len < 5)) {
587  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
588  len--;
589 
590  if (data & 0x80) {
591  /* negative, start from -1 */
592  *value = -1;
593  sign = 1;
594  *lsb_ptr &= data;
595  } else {
596  /* positive, start from 0 */
597  *value = 0;
598  sign = 0;
599  *lsb_ptr |= data;
600  }
601 
602  /* OR/AND octets with value */
603  while (len > 0) {
604  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
605  len--;
606 
607 #if BYTE_ORDER == LITTLE_ENDIAN
608  *value <<= 8;
609 #endif
610 #if BYTE_ORDER == BIG_ENDIAN
611  *value >>= 8;
612 #endif
613 
614  if (sign) {
615  *lsb_ptr |= 255;
616  *lsb_ptr &= data;
617  } else {
618  *lsb_ptr |= data;
619  }
620  }
621 
622  return ERR_OK;
623  }
624 
625  return ERR_VAL;
626 }
627 
637 err_t
638 snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
639 {
640  u32_t *oid_ptr;
641  u8_t data;
642 
643  *oid_len = 0;
644  oid_ptr = oid;
645  if (len > 0) {
646  if (oid_max_len < 2) {
647  return ERR_MEM;
648  }
649 
650  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
651  len--;
652 
653  /* first compressed octet */
654  if (data == 0x2B) {
655  /* (most) common case 1.3 (iso.org) */
656  *oid_ptr = 1;
657  oid_ptr++;
658  *oid_ptr = 3;
659  oid_ptr++;
660  } else if (data < 40) {
661  *oid_ptr = 0;
662  oid_ptr++;
663  *oid_ptr = data;
664  oid_ptr++;
665  } else if (data < 80) {
666  *oid_ptr = 1;
667  oid_ptr++;
668  *oid_ptr = data - 40;
669  oid_ptr++;
670  } else {
671  *oid_ptr = 2;
672  oid_ptr++;
673  *oid_ptr = data - 80;
674  oid_ptr++;
675  }
676  *oid_len = 2;
677  } else {
678  /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
679  return ERR_OK;
680  }
681 
682  while ((len > 0) && (*oid_len < oid_max_len)) {
683  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
684  len--;
685 
686  if ((data & 0x80) == 0x00) {
687  /* sub-identifier uses single octet */
688  *oid_ptr = data;
689  } else {
690  /* sub-identifier uses multiple octets */
691  u32_t sub_id = (data & ~0x80);
692  while ((len > 0) && ((data & 0x80) != 0)) {
693  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
694  len--;
695 
696  sub_id = (sub_id << 7) + (data & ~0x80);
697  }
698 
699  if ((data & 0x80) != 0) {
700  /* "more bytes following" bit still set at end of len */
701  return ERR_VAL;
702  }
703  *oid_ptr = sub_id;
704  }
705  oid_ptr++;
706  (*oid_len)++;
707  }
708 
709  if (len > 0) {
710  /* OID to long to fit in our buffer */
711  return ERR_MEM;
712  }
713 
714  return ERR_OK;
715 }
716 
728 err_t
729 snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
730 {
731  if (len > buf_max_len) {
732  /* not enough dst space */
733  return ERR_MEM;
734  }
735  *buf_len = len;
736 
737  while (len > 0) {
738  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
739  buf++;
740  len--;
741  }
742 
743  return ERR_OK;
744 }
745 
746 #endif /* LWIP_SNMP */
#define ERR_VAL
Definition: err.h:58
unsigned long u32_t
Definition: cc.h:42
#define ERR_OK
Definition: err.h:52
s8_t err_t
Definition: err.h:47
#define ERR_ARG
Definition: err.h:71
unsigned char u8_t
Definition: cc.h:38
signed long s32_t
Definition: cc.h:43
#define ERR_MEM
Definition: err.h:53
unsigned short u16_t
Definition: cc.h:40