477 lines
16 KiB
C
477 lines
16 KiB
C
/*
|
|
* Copyright (c) 2014, SICS Swedish ICT.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* This file is part of the Contiki operating system.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* TSCH packet format management
|
|
* \author
|
|
* Simon Duquennoy <simonduq@sics.se>
|
|
* Beshr Al Nahas <beshr@sics.se>
|
|
*/
|
|
|
|
/**
|
|
* \addtogroup tsch
|
|
* @{
|
|
*/
|
|
|
|
#include "contiki.h"
|
|
#include "net/packetbuf.h"
|
|
#include "net/mac/tsch/tsch.h"
|
|
#include "net/mac/framer/frame802154.h"
|
|
#include "net/mac/framer/framer-802154.h"
|
|
#include "net/netstack.h"
|
|
#include "lib/ccm-star.h"
|
|
#include "lib/aes-128.h"
|
|
|
|
/* Log configuration */
|
|
#include "sys/log.h"
|
|
#define LOG_MODULE "TSCH Pkt"
|
|
#define LOG_LEVEL LOG_LEVEL_MAC
|
|
|
|
/*
|
|
* We use a local packetbuf_attr array to collect necessary frame settings to
|
|
* create an EACK because EACK is generated in the interrupt context where
|
|
* packetbuf and packetbuf_attrs[] may be in use for another purpose.
|
|
*
|
|
* We have accessors of eackbuf_attrs: tsch_packet_eackbuf_set_attr() and
|
|
* tsch_packet_eackbuf_attr(). For some platform, they might need to be
|
|
* implemented as inline functions. However, for now, we don't provide the
|
|
* inline option. Such an optimization is left to the compiler for a target
|
|
* platform.
|
|
*/
|
|
static struct packetbuf_attr eackbuf_attrs[PACKETBUF_NUM_ATTRS];
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
tsch_packet_eackbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
|
|
{
|
|
eackbuf_attrs[type].val = val;
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Return the value of a specified attribute */
|
|
packetbuf_attr_t
|
|
tsch_packet_eackbuf_attr(uint8_t type)
|
|
{
|
|
return eackbuf_attrs[type].val;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Construct enhanced ACK packet and return ACK length */
|
|
int
|
|
tsch_packet_create_eack(uint8_t *buf, uint16_t buf_len,
|
|
const linkaddr_t *dest_addr, uint8_t seqno,
|
|
int16_t drift, int nack)
|
|
{
|
|
frame802154_t params;
|
|
struct ieee802154_ies ies;
|
|
int hdr_len;
|
|
int ack_len;
|
|
|
|
if(buf == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
memset(eackbuf_attrs, 0, sizeof(eackbuf_attrs));
|
|
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_ACKFRAME);
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_METADATA, 1);
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, seqno);
|
|
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_DEST_ADDR, 1);
|
|
#if TSCH_PACKET_EACK_WITH_DEST_ADDR
|
|
if(dest_addr != NULL) {
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_DEST_ADDR, 0);
|
|
linkaddr_copy((linkaddr_t *)¶ms.dest_addr, dest_addr);
|
|
}
|
|
#endif
|
|
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_SRC_ADDR, 1);
|
|
#if TSCH_PACKET_EACK_WITH_SRC_ADDR
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_SRC_ADDR, 0);
|
|
linkaddr_copy((linkaddr_t *)¶ms.src_addr, &linkaddr_node_addr);
|
|
#endif
|
|
|
|
#if LLSEC802154_ENABLED
|
|
if(tsch_is_pan_secured) {
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL,
|
|
TSCH_SECURITY_KEY_SEC_LEVEL_ACK);
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE,
|
|
FRAME802154_1_BYTE_KEY_ID_MODE);
|
|
tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX,
|
|
TSCH_SECURITY_KEY_INDEX_ACK);
|
|
}
|
|
#endif /* LLSEC802154_ENABLED */
|
|
|
|
framer_802154_setup_params(tsch_packet_eackbuf_attr, 0, ¶ms);
|
|
hdr_len = frame802154_hdrlen(¶ms);
|
|
|
|
memset(buf, 0, buf_len);
|
|
|
|
/* Setup IE timesync */
|
|
memset(&ies, 0, sizeof(ies));
|
|
ies.ie_time_correction = drift;
|
|
ies.ie_is_nack = nack;
|
|
|
|
ack_len =
|
|
frame80215e_create_ie_header_ack_nack_time_correction(buf + hdr_len,
|
|
buf_len - hdr_len, &ies);
|
|
if(ack_len < 0) {
|
|
return -1;
|
|
}
|
|
ack_len += hdr_len;
|
|
|
|
frame802154_create(¶ms, buf);
|
|
|
|
return ack_len;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Parse enhanced ACK packet, extract drift and nack */
|
|
int
|
|
tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
|
|
uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
|
|
{
|
|
uint8_t curr_len = 0;
|
|
int ret;
|
|
linkaddr_t dest;
|
|
|
|
if(frame == NULL || buf_size < 0) {
|
|
return 0;
|
|
}
|
|
/* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
|
|
if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) < 3) {
|
|
return 0;
|
|
}
|
|
if(hdr_len != NULL) {
|
|
*hdr_len = ret;
|
|
}
|
|
curr_len += ret;
|
|
|
|
/* Check seqno */
|
|
if(seqno != frame->seq) {
|
|
return 0;
|
|
}
|
|
|
|
/* Check destination PAN ID */
|
|
if(frame802154_check_dest_panid(frame) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Check destination address (if any) */
|
|
if(frame802154_extract_linkaddr(frame, NULL, &dest) == 0 ||
|
|
(!linkaddr_cmp(&dest, &linkaddr_node_addr)
|
|
&& !linkaddr_cmp(&dest, &linkaddr_null))) {
|
|
return 0;
|
|
}
|
|
|
|
if(ies != NULL) {
|
|
memset(ies, 0, sizeof(struct ieee802154_ies));
|
|
}
|
|
|
|
if(frame->fcf.ie_list_present) {
|
|
int mic_len = 0;
|
|
#if LLSEC802154_ENABLED
|
|
/* Check if there is space for the security MIC (if any) */
|
|
mic_len = tsch_security_mic_len(frame);
|
|
if(buf_size < curr_len + mic_len) {
|
|
return 0;
|
|
}
|
|
#endif /* LLSEC802154_ENABLED */
|
|
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
|
|
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
|
|
return 0;
|
|
}
|
|
curr_len += ret;
|
|
}
|
|
|
|
if(hdr_len != NULL) {
|
|
*hdr_len += ies->ie_payload_ie_offset;
|
|
}
|
|
|
|
return curr_len;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Create an EB packet */
|
|
int
|
|
tsch_packet_create_eb(uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
|
|
{
|
|
struct ieee802154_ies ies;
|
|
uint8_t *p;
|
|
int ie_len;
|
|
const uint16_t payload_ie_hdr_len = 2;
|
|
|
|
packetbuf_clear();
|
|
|
|
/* Prepare Information Elements for inclusion in the EB */
|
|
memset(&ies, 0, sizeof(ies));
|
|
|
|
/* Add TSCH timeslot timing IE. */
|
|
#if TSCH_PACKET_EB_WITH_TIMESLOT_TIMING
|
|
{
|
|
int i;
|
|
ies.ie_tsch_timeslot_id = 1;
|
|
for(i = 0; i < tsch_ts_elements_count; i++) {
|
|
ies.ie_tsch_timeslot[i] = RTIMERTICKS_TO_US(tsch_timing[i]);
|
|
}
|
|
}
|
|
#endif /* TSCH_PACKET_EB_WITH_TIMESLOT_TIMING */
|
|
|
|
/* Add TSCH hopping sequence IE */
|
|
#if TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE
|
|
if(tsch_hopping_sequence_length.val <= sizeof(ies.ie_hopping_sequence_list)) {
|
|
ies.ie_channel_hopping_sequence_id = 1;
|
|
ies.ie_hopping_sequence_len = tsch_hopping_sequence_length.val;
|
|
memcpy(ies.ie_hopping_sequence_list, tsch_hopping_sequence,
|
|
ies.ie_hopping_sequence_len);
|
|
}
|
|
#endif /* TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE */
|
|
|
|
/* Add Slotframe and Link IE */
|
|
#if TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK
|
|
{
|
|
/* Send slotframe 0 with link at timeslot 0 */
|
|
struct tsch_slotframe *sf0 = tsch_schedule_get_slotframe_by_handle(0);
|
|
struct tsch_link *link0 = tsch_schedule_get_link_by_timeslot(sf0, 0);
|
|
if(sf0 && link0) {
|
|
ies.ie_tsch_slotframe_and_link.num_slotframes = 1;
|
|
ies.ie_tsch_slotframe_and_link.slotframe_handle = sf0->handle;
|
|
ies.ie_tsch_slotframe_and_link.slotframe_size = sf0->size.val;
|
|
ies.ie_tsch_slotframe_and_link.num_links = 1;
|
|
ies.ie_tsch_slotframe_and_link.links[0].timeslot = link0->timeslot;
|
|
ies.ie_tsch_slotframe_and_link.links[0].channel_offset =
|
|
link0->channel_offset;
|
|
ies.ie_tsch_slotframe_and_link.links[0].link_options =
|
|
link0->link_options;
|
|
}
|
|
}
|
|
#endif /* TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK */
|
|
|
|
p = packetbuf_dataptr();
|
|
|
|
ie_len = frame80215e_create_ie_tsch_synchronization(p,
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
p += ie_len;
|
|
packetbuf_set_datalen(packetbuf_datalen() + ie_len);
|
|
|
|
ie_len = frame80215e_create_ie_tsch_timeslot(p,
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
p += ie_len;
|
|
packetbuf_set_datalen(packetbuf_datalen() + ie_len);
|
|
|
|
ie_len = frame80215e_create_ie_tsch_channel_hopping_sequence(p,
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
p += ie_len;
|
|
packetbuf_set_datalen(packetbuf_datalen() + ie_len);
|
|
|
|
ie_len = frame80215e_create_ie_tsch_slotframe_and_link(p,
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
p += ie_len;
|
|
packetbuf_set_datalen(packetbuf_datalen() + ie_len);
|
|
|
|
#if 0
|
|
/* Payload IE list termination: optional */
|
|
ie_len = frame80215e_create_ie_payload_list_termination(p,
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
p += ie_len;
|
|
packetbuf_set_datalen(packetbuf_datalen() + ie_len);
|
|
#endif
|
|
|
|
ies.ie_mlme_len = packetbuf_datalen();
|
|
|
|
/* make room for Payload IE header */
|
|
memmove((uint8_t *)packetbuf_dataptr() + payload_ie_hdr_len,
|
|
packetbuf_dataptr(), packetbuf_datalen());
|
|
packetbuf_set_datalen(packetbuf_datalen() + payload_ie_hdr_len);
|
|
ie_len = frame80215e_create_ie_mlme(packetbuf_dataptr(),
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* allocate space for Header Termination IE, the size of which is 2 octets */
|
|
packetbuf_hdralloc(2);
|
|
ie_len = frame80215e_create_ie_header_list_termination_1(packetbuf_hdrptr(),
|
|
packetbuf_remaininglen(),
|
|
&ies);
|
|
if(ie_len < 0) {
|
|
return -1;
|
|
}
|
|
|
|
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_BEACONFRAME);
|
|
packetbuf_set_attr(PACKETBUF_ATTR_MAC_METADATA, 1);
|
|
|
|
packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
|
|
packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &tsch_eb_address);
|
|
|
|
#if LLSEC802154_ENABLED
|
|
if(tsch_is_pan_secured) {
|
|
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL,
|
|
TSCH_SECURITY_KEY_SEC_LEVEL_EB);
|
|
packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE,
|
|
FRAME802154_1_BYTE_KEY_ID_MODE);
|
|
packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX,
|
|
TSCH_SECURITY_KEY_INDEX_EB);
|
|
}
|
|
#endif /* LLSEC802154_ENABLED */
|
|
|
|
if(NETSTACK_FRAMER.create() < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if(hdr_len != NULL) {
|
|
*hdr_len = packetbuf_hdrlen();
|
|
}
|
|
|
|
/*
|
|
* Save the offset of the TSCH Synchronization IE, which is expected to be
|
|
* located just after the Payload IE header, needed to update ASN and join
|
|
* priority before sending.
|
|
*/
|
|
if(tsch_sync_ie_offset != NULL) {
|
|
*tsch_sync_ie_offset = packetbuf_hdrlen() + payload_ie_hdr_len;
|
|
}
|
|
|
|
return packetbuf_totlen();
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Update ASN in EB packet */
|
|
int
|
|
tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
|
|
{
|
|
struct ieee802154_ies ies;
|
|
ies.ie_asn = tsch_current_asn;
|
|
ies.ie_join_priority = tsch_join_priority;
|
|
return frame80215e_create_ie_tsch_synchronization(buf+tsch_sync_ie_offset, buf_size-tsch_sync_ie_offset, &ies) != -1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Parse a IEEE 802.15.4e TSCH Enhanced Beacon (EB) */
|
|
int
|
|
tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
|
|
frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
|
|
{
|
|
uint8_t curr_len = 0;
|
|
int ret;
|
|
|
|
if(frame == NULL || buf_size < 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
|
|
if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) == 0) {
|
|
LOG_ERR("! parse_eb: failed to parse frame\n");
|
|
return 0;
|
|
}
|
|
|
|
if(frame->fcf.frame_version < FRAME802154_IEEE802154_2015
|
|
|| frame->fcf.frame_type != FRAME802154_BEACONFRAME) {
|
|
LOG_INFO("! parse_eb: frame is not a valid TSCH beacon. Frame version %u, type %u, FCF %02x %02x\n",
|
|
frame->fcf.frame_version, frame->fcf.frame_type, buf[0], buf[1]);
|
|
LOG_INFO("! parse_eb: frame was from 0x%x/", frame->src_pid);
|
|
LOG_INFO_LLADDR((const linkaddr_t *)&frame->src_addr);
|
|
LOG_INFO_(" to 0x%x/", frame->dest_pid);
|
|
LOG_INFO_LLADDR((const linkaddr_t *)&frame->dest_addr);
|
|
LOG_INFO_("\n");
|
|
return 0;
|
|
}
|
|
|
|
if(hdr_len != NULL) {
|
|
*hdr_len = ret;
|
|
}
|
|
curr_len += ret;
|
|
|
|
if(ies != NULL) {
|
|
memset(ies, 0, sizeof(struct ieee802154_ies));
|
|
ies->ie_join_priority = 0xff; /* Use max value in case the Beacon does not include a join priority */
|
|
}
|
|
if(frame->fcf.ie_list_present) {
|
|
/* Calculate space needed for the security MIC, if any, before attempting to parse IEs */
|
|
int mic_len = 0;
|
|
#if LLSEC802154_ENABLED
|
|
if(!frame_without_mic) {
|
|
mic_len = tsch_security_mic_len(frame);
|
|
if(buf_size < curr_len + mic_len) {
|
|
return 0;
|
|
}
|
|
}
|
|
#endif /* LLSEC802154_ENABLED */
|
|
|
|
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
|
|
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
|
|
LOG_ERR("! parse_eb: failed to parse IEs\n");
|
|
return 0;
|
|
}
|
|
curr_len += ret;
|
|
}
|
|
|
|
if(hdr_len != NULL) {
|
|
*hdr_len += ies->ie_payload_ie_offset;
|
|
}
|
|
|
|
return curr_len;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Set frame pending bit in a packet (whose header was already build) */
|
|
void
|
|
tsch_packet_set_frame_pending(uint8_t *buf, int buf_size)
|
|
{
|
|
buf[0] |= (1 << 4);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Get frame pending bit from a packet */
|
|
int
|
|
tsch_packet_get_frame_pending(uint8_t *buf, int buf_size)
|
|
{
|
|
return (buf[0] >> 4) & 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/** @} */
|