nes-proj/os/net/mac/tsch/tsch-packet.c
2018-05-14 04:20:17 -07:00

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 *)&params.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 *)&params.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, &params);
hdr_len = frame802154_hdrlen(&params);
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(&params, 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;
}
/*---------------------------------------------------------------------------*/
/** @} */