nes-proj/os/net/mac/tsch/sixtop/sixp.c
2017-09-14 21:48:08 +09:00

423 lines
14 KiB
C

/*
* Copyright (c) 2016, Yasuyuki Tanaka
* 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 copyright holder 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 COPYRIGHT HOLDERS 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
* COPYRIGHT HOLDER 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.
*/
/**
* \addtogroup sixtop
* @{
*/
/**
* \file
* 6top Protocol (6P)
* \author
* Yasuyuki Tanaka <yasuyuki.tanaka@inf.ethz.ch>
*/
#include "contiki-lib.h"
#include "lib/assert.h"
#include "sixtop.h"
#include "sixp-nbr.h"
#include "sixp-pkt.h"
#include "sixp-trans.h"
/* Log configuration */
#include "sys/log.h"
#define LOG_MODULE "6top"
#define LOG_LEVEL LOG_LEVEL_6TOP
static void mac_callback(void *ptr, int status, int transmissions);
static int send_back_error(sixp_pkt_type_t type, sixp_pkt_code_t code,
uint8_t sfid, uint8_t seqno,
const linkaddr_t *dest_addr);
/*---------------------------------------------------------------------------*/
static void
mac_callback(void *ptr, int status, int transmissions)
{
sixp_trans_t *trans = (sixp_trans_t *)ptr;
sixp_trans_state_t new_state, current_state;
assert(trans != NULL);
if(trans == NULL) {
LOG_ERR("6P: mac_callback() fails because trans is NULL\n");
return;
}
current_state = sixp_trans_get_state(trans);
if(status == MAC_TX_OK) {
switch(current_state) {
case SIXP_TRANS_STATE_INIT:
new_state = SIXP_TRANS_STATE_REQUEST_SENT;
break;
case SIXP_TRANS_STATE_REQUEST_RECEIVED:
new_state = SIXP_TRANS_STATE_RESPONSE_SENT;
break;
case SIXP_TRANS_STATE_RESPONSE_RECEIVED:
new_state = SIXP_TRANS_STATE_CONFIRMATION_SENT;
break;
default:
LOG_ERR("6P: mac_callback() fails because of an unexpected state (%u)\n",
current_state);
return;
}
} else {
/*
* In a case of transmission failure of a request, a corresponding SF would
* retransmit the request with a new transaction. For a response or a
* confirmation, the same transaction will be used for retransmission as
* long as it doesn't have timeout.
*/
if(current_state == SIXP_TRANS_STATE_INIT) {
/* request case */
new_state = SIXP_TRANS_STATE_TERMINATING;
} else {
/* response or confirmation case: stay the same state */
new_state = current_state;
}
}
if(new_state != current_state &&
sixp_trans_transit_state(trans, new_state) != 0) {
LOG_ERR("6P: mac_callback() fails because of state transition failure\n");
LOG_ERR("6P: something wrong; we're terminating the trans %p\n", trans);
(void)sixp_trans_transit_state(trans, SIXP_TRANS_STATE_TERMINATING);
return;
}
sixp_trans_invoke_callback(trans,
status == MAC_TX_OK ?
SIXP_OUTPUT_STATUS_SUCCESS :
SIXP_OUTPUT_STATUS_FAILURE);
sixp_trans_set_callback(trans, NULL, NULL, 0);
}
/*---------------------------------------------------------------------------*/
static int
send_back_error(sixp_pkt_type_t type, sixp_pkt_code_t code,
uint8_t sfid, uint8_t seqno,
const linkaddr_t *dest_addr)
{
/* create a 6P packet within packetbuf */
if(sixp_pkt_create(type, code, sfid, seqno, 0, NULL, 0, NULL) < 0) {
LOG_ERR("6P: failed to create a 6P packet to return an error [rc:%u]\n",
code.value);
return -1;
}
/* we don't care about how the transmission goes; no need to set callback */
sixtop_output(dest_addr, NULL, NULL);
return 0;
}
/*---------------------------------------------------------------------------*/
void
sixp_input(const uint8_t *buf, uint16_t len, const linkaddr_t *src_addr)
{
sixp_pkt_t pkt;
sixp_nbr_t *nbr;
uint8_t invalid_schedule_generation;
sixp_trans_t *trans;
const sixtop_sf_t *sf;
int16_t seqno;
int16_t gen;
int ret;
assert(buf != NULL && src_addr != NULL);
if(buf == NULL || src_addr == NULL) {
return;
}
if(sixp_pkt_parse(buf, len, &pkt) < 0) {
LOG_ERR("6P: sixp_input() fails because off a malformed 6P packet\n");
return;
}
if(pkt.type != SIXP_PKT_TYPE_REQUEST &&
pkt.type != SIXP_PKT_TYPE_RESPONSE &&
pkt.type != SIXP_PKT_TYPE_CONFIRMATION) {
LOG_ERR("6P: sixp_input() fails because of unsupported type [type:%u]\n",
pkt.type);
return;
}
if((sf = sixtop_find_sf(pkt.sfid)) == NULL) {
LOG_ERR("6P: sixp_input() fails because SF [sfid:%u] is unavailable\n",
pkt.sfid);
/*
* XXX: what if the incoming packet is a response? confirmation should be
* sent back?
*/
if(send_back_error(SIXP_PKT_TYPE_RESPONSE,
(sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_SFID,
pkt.sfid, pkt.seqno, src_addr) < 0) {
LOG_ERR("6P: sixp_input() fails to return an error response\n");
};
return;
}
nbr = sixp_nbr_find(src_addr);
/* Generation Management */
if(pkt.code.value == SIXP_PKT_CMD_CLEAR) {
/* Not need to validate generation counters in a case of CMD_CLEAR */
invalid_schedule_generation = 0;
} else if(nbr == NULL) {
if(pkt.gen == 0) {
invalid_schedule_generation = 0; /* valid combination */
} else {
LOG_ERR("6P: GEN should be 0 because of no corresponding nbr\n");
invalid_schedule_generation = 1;
}
} else {
if((gen = sixp_nbr_get_gen(nbr)) < 0) {
LOG_ERR("6P: unexpected error; cannot get our GEN\n");
return;
}
LOG_INFO("6P: received GEN %u, our GEN: %u\n",
pkt.gen, sixp_nbr_get_gen(nbr));
if(pkt.gen == gen) {
invalid_schedule_generation = 0; /* valid combination */
} else {
invalid_schedule_generation = 1;
}
}
if(invalid_schedule_generation) {
LOG_ERR("6P: sixp_input() fails because of schedule generation mismatch\n");
return;
}
/* Transaction Management */
trans = sixp_trans_find(src_addr);
if(pkt.type == SIXP_PKT_TYPE_REQUEST) {
if(trans != NULL) {
/* Error: not supposed to have another transaction with the peer. */
LOG_ERR("6P: sixp_input() fails because another request [peer_addr:");
LOG_ERR_LLADDR((const linkaddr_t *)src_addr);
LOG_ERR_(" seqno:%u] is in process\n", sixp_trans_get_seqno(trans));
if(send_back_error(SIXP_PKT_TYPE_RESPONSE,
(sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_BUSY,
pkt.sfid, pkt.seqno, src_addr) < 0) {
LOG_ERR("6P: sixp_input() fails to return an error response");
}
return;
} else if((trans = sixp_trans_alloc(&pkt, src_addr)) == NULL) {
LOG_ERR("6P: sixp_input() fails because of lack of memory\n");
if(send_back_error(SIXP_PKT_TYPE_RESPONSE,
(sixp_pkt_code_t)(uint8_t)SIXP_PKT_RC_NORES,
pkt.sfid, pkt.seqno, src_addr) < 0) {
LOG_ERR("6P: sixp_input() fails to return an error response\n");
}
return;
}
} else if(pkt.type == SIXP_PKT_TYPE_RESPONSE ||
pkt.type == SIXP_PKT_TYPE_CONFIRMATION) {
if(trans == NULL) {
/* Error: should have a transaction for incoming packet */
LOG_ERR("6P: sixp_input() fails because of no trans [peer_addr:");
LOG_ERR_LLADDR((const linkaddr_t *)src_addr);
LOG_ERR_("]\n");
return;
} else if((seqno = sixp_trans_get_seqno(trans)) < 0 ||
seqno != pkt.seqno) {
LOG_ERR("6P: sixp_input() fails because of invalid seqno [seqno:%u, %u]\n",
seqno, pkt.seqno);
return;
}
}
/* state transition */
assert(trans != NULL);
switch(pkt.type) {
case SIXP_PKT_TYPE_REQUEST:
ret = sixp_trans_transit_state(trans,
SIXP_TRANS_STATE_REQUEST_RECEIVED);
break;
case SIXP_PKT_TYPE_RESPONSE:
ret = sixp_trans_transit_state(trans,
SIXP_TRANS_STATE_RESPONSE_RECEIVED);
break;
case SIXP_PKT_TYPE_CONFIRMATION:
ret = sixp_trans_transit_state(trans,
SIXP_TRANS_STATE_CONFIRMATION_RECEIVED);
break;
default:
LOG_ERR("6P: sixp_input() fails because of unsupported type [type:%u]\n",
pkt.type);
return;
}
if(ret < 0) {
LOG_ERR("6P: sixp_input() fails because of state transition failure\n");
LOG_ERR("6P: something wrong; we're terminating the trans %p\n", trans);
(void)sixp_trans_transit_state(trans, SIXP_TRANS_STATE_TERMINATING);
return;
}
if(sf->input != NULL) {
sf->input(pkt.type, pkt.code, pkt.body, pkt.body_len, src_addr);
}
return;
}
/*---------------------------------------------------------------------------*/
int
sixp_output(sixp_pkt_type_t type, sixp_pkt_code_t code, uint8_t sfid,
const uint8_t *body, uint16_t body_len,
const linkaddr_t *dest_addr,
sixp_sent_callback_t func, void *arg, uint16_t arg_len)
{
sixp_trans_t *trans;
sixp_nbr_t *nbr;
sixp_pkt_cmd_t cmd;
int16_t seqno, gen;
sixp_pkt_t pkt;
assert(dest_addr != NULL);
/* validate the state of a transaction with a specified peer */
trans = sixp_trans_find(dest_addr);
if(type == SIXP_PKT_TYPE_REQUEST) {
if(trans != NULL) {
LOG_ERR("6P: sixp_output() fails because another trans for [peer_addr:");
LOG_ERR_LLADDR((const linkaddr_t *)dest_addr);
LOG_ERR_("] is in process\n");
return -1;
} else {
/* ready to send a request */
/* we're going to allocate a new transaction later */
}
} else if(type == SIXP_PKT_TYPE_RESPONSE) {
if(trans == NULL) {
LOG_ERR("6P: sixp_output() fails because of no transaction [peer_addr:");
LOG_ERR_LLADDR((const linkaddr_t *)dest_addr);
LOG_ERR_("]\n");
return -1;
} else if(sixp_trans_get_state(trans) !=
SIXP_TRANS_STATE_REQUEST_RECEIVED) {
LOG_ERR("6P: sixp_output() fails because of invalid transaction state\n");
return -1;
} else {
/* ready to send a response */
}
} else if(type == SIXP_PKT_TYPE_CONFIRMATION) {
if(trans == NULL) {
LOG_ERR("6P: sixp_output() fails because of no transaction [peer_addr:");
LOG_ERR_LLADDR((const linkaddr_t *)dest_addr);
LOG_ERR_("\n");
return -1;
} else if(sixp_trans_get_state(trans) !=
SIXP_TRANS_STATE_RESPONSE_RECEIVED) {
LOG_ERR("6P: sixp_output() fails because of invalid transaction state\n");
return -1;
} else {
/* ready to send a confirmation */
}
} else {
LOG_ERR("6P: sixp_output() fails because of unsupported type [type:%u]\n",
type);
return -1;
}
nbr = sixp_nbr_find(dest_addr);
/*
* Make sure we have a nbr for the peer if the packet is a response with
* success so that we can manage the schedule generation.
*/
if(nbr == NULL &&
type == SIXP_PKT_TYPE_RESPONSE && code.value == SIXP_PKT_RC_SUCCESS &&
((cmd = sixp_trans_get_cmd(trans)) == SIXP_PKT_CMD_ADD ||
cmd == SIXP_PKT_CMD_DELETE) &&
(nbr = sixp_nbr_alloc(dest_addr)) == NULL) {
LOG_ERR("6P: sixp_output() fails because of no memory for another nbr\n");
return -1;
}
/* set SeqNum */
if(type == SIXP_PKT_TYPE_REQUEST) {
if(nbr == NULL &&
(nbr = sixp_nbr_alloc(dest_addr)) == NULL) {
LOG_ERR("6P: sixp_output() fails because it fails to allocate a nbr\n");
return -1;
}
if((seqno = sixp_nbr_get_next_seqno(nbr)) < 0){
LOG_ERR("6P: sixp_output() fails to get the next sequence number\n");
return -1;
}
if(sixp_nbr_increment_next_seqno(nbr) < 0) {
LOG_ERR("6P: sixp_output() fails to increment the next sequence number\n");
return -1;
}
} else {
assert(trans != NULL);
if((seqno = sixp_trans_get_seqno(trans)) < 0) {
LOG_ERR("6P: sixp_output() fails because it fails to get seqno\n");
return -1;
}
}
/* set GEN */
if(nbr == NULL) {
gen = 0;
} else if((gen = sixp_nbr_get_gen(nbr)) < 0) {
LOG_ERR("6P: sixp_output() fails to get GEN\n");
return -1;
}
/* create a 6P packet within packetbuf */
if(sixp_pkt_create(type, code, sfid,
(uint8_t)seqno, (uint8_t)gen,
body, body_len,
type == SIXP_PKT_TYPE_REQUEST ? &pkt : NULL) < 0) {
LOG_ERR("6P: sixp_output() fails to create a 6P packet\n");
return -1;
}
/* allocate a transaction for a sending request */
if(type == SIXP_PKT_TYPE_REQUEST) {
assert(trans == NULL);
if((trans = sixp_trans_alloc(&pkt, dest_addr)) == NULL) {
LOG_ERR("6P: sixp_output() is aborted because of no memory\n");
return -1;
} else {
/* ready for proceed */
}
}
assert(trans != NULL);
sixp_trans_set_callback(trans, func, arg, arg_len);
sixtop_output(dest_addr, mac_callback, trans);
return 0;
}
/*---------------------------------------------------------------------------*/
void
sixp_init(void)
{
sixp_nbr_init();
sixp_trans_init();
}
/*---------------------------------------------------------------------------*/
/** @} */