Contiki-NG
Loading...
Searching...
No Matches
frame802154e-ie.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015, SICS Swedish ICT.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 *
31 */
32
33/**
34 * \file
35 * IEEE 802.15.4e Information Element (IE) creation and parsing.
36 * \author
37 * Simon Duquennoy <simonduq@sics.se>
38 */
39
40#include <string.h>
42
43/* Log configuration */
44#include "sys/log.h"
45#define LOG_MODULE "Frame 15.4"
46#define LOG_LEVEL LOG_LEVEL_FRAMER
47
48/* c.f. IEEE 802.15.4e Table 4b */
49enum ieee802154e_header_ie_id {
50 HEADER_IE_LE_CSL = 0x1a,
51 HEADER_IE_LE_RIT,
52 HEADER_IE_DSME_PAN_DESCRIPTOR,
53 HEADER_IE_RZ_TIME,
54 HEADER_IE_ACK_NACK_TIME_CORRECTION,
55 HEADER_IE_GACK,
56 HEADER_IE_LOW_LATENCY_NETWORK_INFO,
57 HEADER_IE_LIST_TERMINATION_1 = 0x7e,
58 HEADER_IE_LIST_TERMINATION_2 = 0x7f,
59};
60
61/* c.f. IEEE 802.15.4e Table 4c */
62enum ieee802154e_payload_ie_id {
63 PAYLOAD_IE_ESDU = 0,
64 PAYLOAD_IE_MLME,
65 PAYLOAD_IE_IETF = 0x5,
66 PAYLOAD_IE_LIST_TERMINATION = 0xf,
67};
68
69/* c.f. IEEE 802.15.4e Table 4d */
70enum ieee802154e_mlme_short_subie_id {
71 MLME_SHORT_IE_TSCH_SYNCHRONIZATION = 0x1a,
72 MLME_SHORT_IE_TSCH_SLOFTRAME_AND_LINK,
73 MLME_SHORT_IE_TSCH_TIMESLOT,
74 MLME_SHORT_IE_TSCH_HOPPING_TIMING,
75 MLME_SHORT_IE_TSCH_EB_FILTER,
76 MLME_SHORT_IE_TSCH_MAC_METRICS_1,
77 MLME_SHORT_IE_TSCH_MAC_METRICS_2,
78};
79
80/* c.f. IEEE 802.15.4e Table 4e */
81enum ieee802154e_mlme_long_subie_id {
82 MLME_LONG_IE_TSCH_CHANNEL_HOPPING_SEQUENCE = 0x9,
83};
84
86enum ieee802154e_ietf_subie_id {
87 IETF_IE_6TOP = SIXTOP_SUBIE_ID,
88};
89
90#define WRITE16(buf, val) \
91 do { ((uint8_t *)(buf))[0] = (val) & 0xff; \
92 ((uint8_t *)(buf))[1] = ((val) >> 8) & 0xff; } while(0)
93
94#define READ16(buf, var) \
95 (var) = ((uint8_t *)(buf))[0] | ((uint8_t *)(buf))[1] << 8
96
97/* Create a header IE 2-byte descriptor */
98static void
99create_header_ie_descriptor(uint8_t *buf, uint8_t element_id, int ie_len)
100{
101 uint16_t ie_desc;
102 /* Header IE descriptor: b0-6: len, b7-14: element id:, b15: type: 0 */
103 ie_desc = (ie_len & 0x7f) + ((element_id & 0xff) << 7);
104 WRITE16(buf, ie_desc);
105}
106
107/* Create a payload IE 2-byte descriptor */
108static void
109create_payload_ie_descriptor(uint8_t *buf, uint8_t group_id, int ie_len)
110{
111 uint16_t ie_desc;
112 /* MLME Long IE descriptor: b0-10: len, b11-14: group id:, b15: type: 1 */
113 ie_desc = (ie_len & 0x07ff) + ((group_id & 0x0f) << 11) + (1 << 15);
114 WRITE16(buf, ie_desc);
115}
116
117/* Create a MLME short IE 2-byte descriptor */
118static void
119create_mlme_short_ie_descriptor(uint8_t *buf, uint8_t sub_id, int ie_len)
120{
121 uint16_t ie_desc;
122 /* MLME Short IE descriptor: b0-7: len, b8-14: sub id:, b15: type: 0 */
123 ie_desc = (ie_len & 0xff) + ((sub_id & 0x7f) << 8);
124 WRITE16(buf, ie_desc);
125}
126
127/* Create a MLME long IE 2-byte descriptor */
128static void
129create_mlme_long_ie_descriptor(uint8_t *buf, uint8_t sub_id, int ie_len)
130{
131 uint16_t ie_desc;
132 /* MLME Long IE descriptor: b0-10: len, b11-14: sub id:, b15: type: 1 */
133 ie_desc = (ie_len & 0x07ff) + ((sub_id & 0x0f) << 11) + (1 << 15);
134 WRITE16(buf, ie_desc);
135}
136
137#if MAC_CONF_WITH_CSL
138int
139frame802154e_create_ie_rendezvous_time(uint8_t *buf, int len,
140 const struct ieee802154_ies *ies)
141{
142 if(!ies || (len < 4)) {
143 return -1;
144 }
145
146 WRITE16(buf + 2, ies->rendezvous_time);
147 create_header_ie_descriptor(buf, HEADER_IE_RZ_TIME, 2);
148 return 4;
149}
150
151int
152frame802154e_create_ie_csl(uint8_t *buf, int len,
153 const struct ieee802154_ies *ies)
154{
155 if(!ies || (len < 6)) {
156 return -1;
157 }
158
159 WRITE16(buf + 2, ies->csl_phase);
160 WRITE16(buf + 4, ies->csl_period);
161 create_header_ie_descriptor(buf, HEADER_IE_LE_CSL, 4);
162 return 6;
163}
164#endif /* MAC_CONF_WITH_CSL */
165
166/* Header IE. ACK/NACK time correction. Used in enhanced ACKs */
167int
169 const struct ieee802154_ies *ies)
170{
171 int ie_len = 2;
172 if(len >= 2 + ie_len && ies != NULL) {
173 int16_t drift_us;
174 uint16_t time_sync_field;
175 drift_us = ies->ie_time_correction;
176 time_sync_field = drift_us & 0x0fff;
177 if(ies->ie_is_nack) {
178 time_sync_field |= 0x8000;
179 }
180 WRITE16(buf+2, time_sync_field);
181 create_header_ie_descriptor(buf, HEADER_IE_ACK_NACK_TIME_CORRECTION, ie_len);
182 return 2 + ie_len;
183 } else {
184 return -1;
185 }
186}
187
188/* Header IE. List termination 1 (Signals the end of the Header IEs when
189 * followed by payload IEs) */
190int
191frame80215e_create_ie_header_list_termination_1(uint8_t *buf, int len,
192 const struct ieee802154_ies *ies)
193{
194 int ie_len = 0;
195 if(len >= 2 + ie_len && ies != NULL) {
196 create_header_ie_descriptor(buf, HEADER_IE_LIST_TERMINATION_1, 0);
197 return 2 + ie_len;
198 } else {
199 return -1;
200 }
201}
202
203/* Header IE. List termination 2 (Signals the end of the Header IEs when
204 * followed by an unformatted payload) */
205int
206frame80215e_create_ie_header_list_termination_2(uint8_t *buf, int len,
207 const struct ieee802154_ies *ies)
208{
209 int ie_len = 0;
210 if(len >= 2 + ie_len && ies != NULL) {
211 create_header_ie_descriptor(buf, HEADER_IE_LIST_TERMINATION_2, 0);
212 return 2 + ie_len;
213 } else {
214 return -1;
215 }
216}
217
218/* Payload IE. List termination */
219int
220frame80215e_create_ie_payload_list_termination(uint8_t *buf, int len,
221 const struct ieee802154_ies *ies)
222{
223 int ie_len = 0;
224 if(len >= 2 + ie_len && ies != NULL) {
225 create_payload_ie_descriptor(buf, PAYLOAD_IE_LIST_TERMINATION, 0);
226 return 2 + ie_len;
227 } else {
228 return -1;
229 }
230}
231
232#if TSCH_WITH_SIXTOP
233/* Payload IE. 6top. Used to nest sub-IEs */
234int
235frame80215e_create_ie_ietf(uint8_t *buf, int len,
236 const struct ieee802154_ies *ies)
237{
238 if(len >= 2 && ies != NULL) {
239 create_payload_ie_descriptor(buf,
240 PAYLOAD_IE_IETF,
241 ies->sixtop_ie_content_len);
242 return 2 + ies->sixtop_ie_content_len;
243 }
244 return -1;
245}
246#endif /* TSCH_WITH_SIXTOP */
247
248/* Payload IE. MLME. Used to nest sub-IEs */
249int
250frame80215e_create_ie_mlme(uint8_t *buf, int len,
251 const struct ieee802154_ies *ies)
252{
253 int ie_len = 0;
254 if(len >= 2 + ie_len && ies != NULL) {
255 /* The length of the outer MLME IE is the total length of sub-IEs */
256 create_payload_ie_descriptor(buf, PAYLOAD_IE_MLME, ies->ie_mlme_len);
257 return 2 + ie_len;
258 } else {
259 return -1;
260 }
261}
262
263/* MLME sub-IE. TSCH synchronization. Used in EBs: ASN and join priority */
264int
265frame80215e_create_ie_tsch_synchronization(uint8_t *buf, int len,
266 const struct ieee802154_ies *ies)
267{
268 int ie_len = 6;
269 if(len >= 2 + ie_len && ies != NULL) {
270 buf[2] = ies->ie_asn.ls4b;
271 buf[3] = ies->ie_asn.ls4b >> 8;
272 buf[4] = ies->ie_asn.ls4b >> 16;
273 buf[5] = ies->ie_asn.ls4b >> 24;
274 buf[6] = ies->ie_asn.ms1b;
275 buf[7] = ies->ie_join_priority;
276 create_mlme_short_ie_descriptor(buf, MLME_SHORT_IE_TSCH_SYNCHRONIZATION, ie_len);
277 return 2 + ie_len;
278 } else {
279 return -1;
280 }
281}
282
283/* MLME sub-IE. TSCH slotframe and link. Used in EBs: initial schedule */
284int
285frame80215e_create_ie_tsch_slotframe_and_link(uint8_t *buf, int len,
286 const struct ieee802154_ies *ies)
287{
288 if(ies != NULL) {
289 int i;
290 int num_slotframes = ies->ie_tsch_slotframe_and_link.num_slotframes;
291 int num_links = ies->ie_tsch_slotframe_and_link.num_links;
292 int ie_len = 1 + num_slotframes * (4 + 5 * num_links);
293 if(num_slotframes > 1 || num_links > FRAME802154E_IE_MAX_LINKS
294 || len < 2 + ie_len) {
295 /* We support only 0 or 1 slotframe in this IE and a predefined maximum number of links */
296 return -1;
297 }
298 /* Insert IE */
299 buf[2] = num_slotframes;
300 /* Insert slotframe */
301 if(num_slotframes == 1) {
302 buf[2 + 1] = ies->ie_tsch_slotframe_and_link.slotframe_handle;
303 WRITE16(buf + 2 + 2, ies->ie_tsch_slotframe_and_link.slotframe_size);
304 buf[2 + 4] = num_links;
305 /* Loop over links */
306 for(i = 0; i < num_links; i++) {
307 /* Insert links */
308 WRITE16(buf + 2 + 5 + i * 5, ies->ie_tsch_slotframe_and_link.links[i].timeslot);
309 WRITE16(buf + 2 + 5 + i * 5 + 2, ies->ie_tsch_slotframe_and_link.links[i].channel_offset);
310 buf[2 + 5 + i * 5 + 4] = ies->ie_tsch_slotframe_and_link.links[i].link_options;
311 }
312 }
313 create_mlme_short_ie_descriptor(buf, MLME_SHORT_IE_TSCH_SLOFTRAME_AND_LINK, ie_len);
314 return 2 + ie_len;
315 } else {
316 return -1;
317 }
318}
319
320/* MLME sub-IE. TSCH timeslot. Used in EBs: timeslot template (timing) */
321int
322frame80215e_create_ie_tsch_timeslot(uint8_t *buf, int len,
323 const struct ieee802154_ies *ies)
324{
325 int ie_len;
326 if(ies == NULL) {
327 return -1;
328 }
329 /* Only ID if ID == 0, else full timing description */
330 ie_len = ies->ie_tsch_timeslot_id == 0 ? 1 : 25;
331 if(len >= 2 + ie_len) {
332 buf[2] = ies->ie_tsch_timeslot_id;
333 if(ies->ie_tsch_timeslot_id != 0) {
334 int i;
335 for(i = 0; i < tsch_ts_elements_count; i++) {
336 WRITE16(buf + 3 + 2 * i, ies->ie_tsch_timeslot[i]);
337 }
338 }
339 create_mlme_short_ie_descriptor(buf, MLME_SHORT_IE_TSCH_TIMESLOT, ie_len);
340 return 2 + ie_len;
341 } else {
342 return -1;
343 }
344}
345
346/* MLME sub-IE. TSCH channel hopping sequence. Used in EBs: hopping sequence */
347int
348frame80215e_create_ie_tsch_channel_hopping_sequence(uint8_t *buf, int len,
349 const struct ieee802154_ies *ies)
350{
351 int ie_len;
352 if(ies == NULL || ies->ie_hopping_sequence_len > sizeof(ies->ie_hopping_sequence_list)) {
353 return -1;
354 }
355 ie_len = ies->ie_channel_hopping_sequence_id == 0 ? 1 : 12 + ies->ie_hopping_sequence_len;
356 if(len >= 2 + ie_len && ies != NULL) {
357 buf[2] = ies->ie_channel_hopping_sequence_id;
358 buf[3] = 0; /* channel page */
359 WRITE16(buf + 4, 0); /* number of channels */
360 WRITE16(buf + 6, 0); /* phy configuration */
361 WRITE16(buf + 8, 0);
362 /* Extended bitmap. Size: 0 */
363 WRITE16(buf + 10, ies->ie_hopping_sequence_len); /* sequence len */
364 memcpy(buf + 12, ies->ie_hopping_sequence_list, ies->ie_hopping_sequence_len); /* sequence list */
365 WRITE16(buf + 12 + ies->ie_hopping_sequence_len, 0); /* current hop */
366 create_mlme_long_ie_descriptor(buf, MLME_LONG_IE_TSCH_CHANNEL_HOPPING_SEQUENCE, ie_len);
367 return 2 + ie_len;
368 } else {
369 return -1;
370 }
371}
372
373/* Parse a header IE */
374static int
375frame802154e_parse_header_ie(const uint8_t *buf, int len,
376 uint8_t element_id, struct ieee802154_ies *ies)
377{
378 switch(element_id) {
379 case HEADER_IE_ACK_NACK_TIME_CORRECTION:
380 if(len == 2) {
381 if(ies != NULL) {
382 /* If the originator was a time source neighbor, the receiver adjust
383 * its own clock by incorporating the received drift correction */
384 uint16_t time_sync_field = 0;
385 int16_t drift_us = 0;
386 /* Extract drift correction from Sync-IE, cast from 12 to 16-bit,
387 * and convert it to RTIMER ticks.
388 * See page 88 in IEEE Std 802.15.4e-2012. */
389 READ16(buf, time_sync_field);
390 /* First extract NACK */
391 ies->ie_is_nack = (time_sync_field & (uint16_t)0x8000) ? 1 : 0;
392 /* Then cast from 12 to 16 bit signed */
393 if(time_sync_field & 0x0800) { /* Negative integer */
394 drift_us = time_sync_field | 0xf000;
395 } else { /* Positive integer */
396 drift_us = time_sync_field & 0x0fff;
397 }
398 /* Convert to RTIMER ticks */
399 ies->ie_time_correction = drift_us;
400 }
401 return len;
402 }
403 break;
404#if MAC_CONF_WITH_CSL
405 case HEADER_IE_RZ_TIME:
406 if((len == 2) && ies) {
407 READ16(buf, ies->rendezvous_time);
408 return len;
409 }
410 break;
411 case HEADER_IE_LE_CSL:
412 if(ies && (len == 4)) {
413 READ16(buf, ies->csl_phase);
414 READ16(buf + 2, ies->csl_period);
415 return len;
416 }
417 break;
418#endif /* MAC_CONF_WITH_CSL */
419 }
420 return -1;
421}
422
423/* Parse a MLME short IE */
424static int
425frame802154e_parse_mlme_short_ie(const uint8_t *buf, int len,
426 uint8_t sub_id, struct ieee802154_ies *ies)
427{
428 switch(sub_id) {
429 case MLME_SHORT_IE_TSCH_SLOFTRAME_AND_LINK:
430 if(len >= 1) {
431 int i;
432 int num_slotframes = buf[0];
433 int num_links = buf[4];
434 if(num_slotframes == 0) {
435 return len;
436 }
437 if(num_slotframes <= 1 && num_links <= FRAME802154E_IE_MAX_LINKS
438 && len == 1 + num_slotframes * (4 + 5 * num_links)) {
439 if(ies != NULL) {
440 /* We support only 0 or 1 slotframe in this IE and a predefined maximum number of links */
441 ies->ie_tsch_slotframe_and_link.num_slotframes = buf[0];
442 ies->ie_tsch_slotframe_and_link.slotframe_handle = buf[1];
443 READ16(buf + 2, ies->ie_tsch_slotframe_and_link.slotframe_size);
444 ies->ie_tsch_slotframe_and_link.num_links = buf[4];
445 for(i = 0; i < num_links; i++) {
446 READ16(buf + 5 + i * 5, ies->ie_tsch_slotframe_and_link.links[i].timeslot);
447 READ16(buf + 5 + i * 5 + 2, ies->ie_tsch_slotframe_and_link.links[i].channel_offset);
448 ies->ie_tsch_slotframe_and_link.links[i].link_options = buf[5 + i * 5 + 4];
449 }
450 }
451 return len;
452 }
453 }
454 break;
455 case MLME_SHORT_IE_TSCH_SYNCHRONIZATION:
456 if(len == 6) {
457 if(ies != NULL) {
458 ies->ie_asn.ls4b = (uint32_t)buf[0];
459 ies->ie_asn.ls4b |= (uint32_t)buf[1] << 8;
460 ies->ie_asn.ls4b |= (uint32_t)buf[2] << 16;
461 ies->ie_asn.ls4b |= (uint32_t)buf[3] << 24;
462 ies->ie_asn.ms1b = (uint8_t)buf[4];
463 ies->ie_join_priority = (uint8_t)buf[5];
464 }
465 return len;
466 }
467 break;
468 case MLME_SHORT_IE_TSCH_TIMESLOT:
469 if(len == 1 || len == 25) {
470 if(ies != NULL) {
471 ies->ie_tsch_timeslot_id = buf[0];
472 if(len == 25) {
473 int i;
474 for(i = 0; i < tsch_ts_elements_count; i++) {
475 READ16(buf+1+2*i, ies->ie_tsch_timeslot[i]);
476 }
477 }
478 }
479 return len;
480 }
481 break;
482 }
483 return -1;
484}
485
486/* Parse a MLME long IE */
487static int
488frame802154e_parse_mlme_long_ie(const uint8_t *buf, int len,
489 uint8_t sub_id, struct ieee802154_ies *ies)
490{
491 switch(sub_id) {
492 case MLME_LONG_IE_TSCH_CHANNEL_HOPPING_SEQUENCE:
493 /*
494 * Two valid encodings: a one-octet form carrying only the hopping
495 * sequence ID, or the full form whose 10-octet fixed header is followed
496 * by the sequence list and a two-octet "current hop" field, i.e.
497 * len == 12 + sequence_len. Read the sequence-length field (buf[8..9])
498 * only once the full fixed header is known to be present, and reject any
499 * other length rather than over-reading a short IE or leaving
500 * ie_hopping_sequence_len set from an IE we did not actually copy.
501 */
502 if(len == 1) {
503 if(ies != NULL) {
504 ies->ie_channel_hopping_sequence_id = buf[0];
505 }
506 return len;
507 }
508 if(len >= 12) {
509 uint16_t seq_len;
510 READ16(buf + 8, seq_len); /* sequence len */
511 if(seq_len <= sizeof(ies->ie_hopping_sequence_list)
512 && len == 12 + seq_len) {
513 if(ies != NULL) {
514 ies->ie_channel_hopping_sequence_id = buf[0];
515 ies->ie_hopping_sequence_len = seq_len;
516 memcpy(ies->ie_hopping_sequence_list, buf + 10, seq_len); /* sequence list */
517 }
518 return len;
519 }
520 }
521 break;
522 }
523 return -1;
524}
525
526/* Parse all IEEE 802.15.4e Information Elements (IE) from a frame */
527int
528frame802154e_parse_information_elements(const uint8_t *buf, uint8_t buf_size,
529 struct ieee802154_ies *ies)
530{
531 const uint8_t *start = buf;
532 uint16_t ie_desc;
533 uint8_t type;
534 uint8_t id;
535 uint16_t len = 0;
536 int nested_mlme_len = 0;
537 enum {PARSING_HEADER_IE, PARSING_PAYLOAD_IE, PARSING_MLME_SUBIE} parsing_state;
538
539 if(ies == NULL) {
540 return -1;
541 }
542
543 /* Always look for a header IE first (at least "list termination 1") */
544 parsing_state = PARSING_HEADER_IE;
545 ies->ie_payload_ie_offset = 0;
546
547 /* Loop over all IEs */
548 while(buf_size > 0) {
549 if(buf_size < 2) { /* Not enough space for IE descriptor */
550 return -1;
551 }
552 READ16(buf, ie_desc);
553 buf_size -= 2;
554 buf += 2;
555 type = ie_desc & 0x8000 ? 1 : 0; /* b15 */
556 LOG_DBG("ie type %u, current state %u\n", type, parsing_state);
557
558 switch(parsing_state) {
559 case PARSING_HEADER_IE:
560 if(type != 0) {
561 LOG_ERR("header ie: wrong type %04x\n", ie_desc);
562 return -1;
563 }
564 /* Header IE: 2 bytes descriptor, c.f. fig 48n in IEEE 802.15.4e */
565 len = ie_desc & 0x007f; /* b0-b6 */
566 id = (ie_desc & 0x7f80) >> 7; /* b7-b14 */
567 LOG_DBG("header ie: len %u id %x\n", len, id);
568 switch(id) {
569 case HEADER_IE_LIST_TERMINATION_1:
570 if(len == 0) {
571 /* End of payload IE list, now expect payload IEs */
572 parsing_state = PARSING_PAYLOAD_IE;
573 ies->ie_payload_ie_offset = buf - start; /* Save IE header len */
574 LOG_DBG("list termination 1, look for payload IEs\n");
575 } else {
576 LOG_ERR("list termination 1, wrong len %u\n", len);
577 return -1;
578 }
579 break;
580 case HEADER_IE_LIST_TERMINATION_2:
581 /* End of IE parsing */
582 if(len == 0) {
583 ies->ie_payload_ie_offset = buf - start; /* Save IE header len */
584 LOG_DBG("list termination 2\n");
585 return buf + len - start;
586 } else {
587 LOG_ERR("list termination 2, wrong len %u\n", len);
588 return -1;
589 }
590 default:
591 if(len > buf_size || frame802154e_parse_header_ie(buf, len, id, ies) == -1) {
592 LOG_ERR("failed to parse\n");
593 return -1;
594 }
595 break;
596 }
597 break;
598 case PARSING_PAYLOAD_IE:
599 if(type != 1) {
600 LOG_ERR("payload ie: wrong type %04x\n", ie_desc);
601 return -1;
602 }
603 /* Payload IE: 2 bytes descriptor, c.f. fig 48o in IEEE 802.15.4e */
604 len = ie_desc & 0x7ff; /* b0-b10 */
605 id = (ie_desc & 0x7800) >> 11; /* b11-b14 */
606 LOG_DBG("payload ie: len %u id %x\n", len, id);
607 switch(id) {
608 case PAYLOAD_IE_MLME:
609 /* Now expect 'len' bytes of MLME sub-IEs */
610 parsing_state = PARSING_MLME_SUBIE;
611 nested_mlme_len = len;
612 len = 0; /* Reset len as we want to read subIEs and not jump over them */
613 LOG_DBG("entering MLME ie with len %u\n", nested_mlme_len);
614 break;
615#if TSCH_WITH_SIXTOP
616 case PAYLOAD_IE_IETF:
617 /*
618 * The IETF payload IE carries at least a one-octet Sub-ID field,
619 * and its content must fit within the remaining buffer. Reject
620 * anything shorter or longer to avoid reading past the buffer when
621 * dereferencing the Sub-ID below, underflowing the 6top content
622 * length (len - 1), and underflowing buf_size at the loop tail.
623 */
624 if(len < 1 || len > buf_size) {
625 LOG_ERR("frame802154e: invalid IETF IE len %u (remaining %u)\n",
626 len, buf_size);
627 return -1;
628 }
629 switch(*buf) {
630 case IETF_IE_6TOP:
631 /*
632 * buf points to the Sub-ID field, a one-octet field, now;
633 * advance it by one and use the result as the head pointer of
634 * 6top IE Content.
635 */
636 ies->sixtop_ie_content_ptr = buf + 1;
637 /*
638 * Similarly as above, subtract 1 (one octet) from len, which
639 * includes the length of Sub-ID field, and use the result as
640 * the length of 6top IE Content.
641 */
642 ies->sixtop_ie_content_len = len - 1;
643 break;
644 default:
645 LOG_ERR("frame802154e: unsupported IETF sub-IE %u\n", *buf);
646 break;
647 }
648 break;
649#endif /* TSCH_WITH_SIXTOP */
650 case PAYLOAD_IE_LIST_TERMINATION:
651 LOG_DBG("payload ie list termination %u\n", len);
652 return (len == 0) ? buf + len - start : -1;
653 default:
654 LOG_ERR("non-supported payload ie\n");
655 return -1;
656 }
657 break;
658 case PARSING_MLME_SUBIE:
659 /* MLME sub-IE: 2 bytes descriptor, c.f. fig 48q in IEEE 802.15.4e */
660 /* type == 0 means short sub-IE, type == 1 means long sub-IE */
661 if(type == 0) {
662 /* Short sub-IE, c.f. fig 48r in IEEE 802.15.4e */
663 len = ie_desc & 0x00ff; /* b0-b7 */
664 id = (ie_desc & 0x7f00) >> 8; /* b8-b14 */
665 LOG_DBG("short mlme ie len %u id %x\n", len, id);
666 if(len > buf_size || frame802154e_parse_mlme_short_ie(buf, len, id, ies) == -1) {
667 LOG_ERR("failed to parse ie\n");
668 return -1;
669 }
670 } else {
671 /* Long sub-IE, c.f. fig 48s in IEEE 802.15.4e */
672 len = ie_desc & 0x7ff; /* b0-b10 */
673 id = (ie_desc & 0x7800) >> 11; /* b11-b14 */
674 LOG_DBG("long mlme ie len %u id %x\n", len, id);
675 if(len > buf_size || frame802154e_parse_mlme_long_ie(buf, len, id, ies) == -1) {
676 LOG_ERR("failed to parse ie\n");
677 return -1;
678 }
679 }
680 /* Update remaining nested MLME len */
681 nested_mlme_len -= 2 + len;
682 if(nested_mlme_len < 0) {
683 LOG_ERR("found more sub-IEs than initially advertised\n");
684 /* We found more sub-IEs than initially advertised */
685 return -1;
686 }
687 if(nested_mlme_len == 0) {
688 LOG_DBG("end of MLME IE parsing\n");
689 /* End of IE parsing, look for another payload IE */
690 parsing_state = PARSING_PAYLOAD_IE;
691 }
692 break;
693 }
694 /*
695 * Backstop for every IE type: never advance past the end of the buffer.
696 * Each case above is expected to bound len against buf_size, but this
697 * check enforces the invariant in one place and protects buf_size
698 * (uint8_t) from underflowing should a future case forget to.
699 */
700 if(len > buf_size) {
701 LOG_ERR("frame802154e: IE len %u exceeds remaining %u\n", len, buf_size);
702 return -1;
703 }
704 buf += len;
705 buf_size -= len;
706 }
707
708 if(parsing_state == PARSING_HEADER_IE) {
709 ies->ie_payload_ie_offset = buf - start; /* Save IE header len */
710 }
711
712 return buf - start;
713}
int frame80215e_create_ie_header_ack_nack_time_correction(uint8_t *buf, int len, const struct ieee802154_ies *ies)
Insert various Information Elements.
IEEE 802.15.4e Information Element (IE) creation and parsing.
static void start(void)
Start measurement.
Header file for the logging system.
6TiSCH Operation Sublayer (6top) APIs