efd84940d8
While the timer is designed to get started on state transition from SIXP_TRANS_STATE_INIT, the state transition may not happen in some erroneous situation. This change makes sure that the timer is started once it's allocated.
398 lines
12 KiB
C
398 lines
12 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
|
|
* Transaction Management for 6top Protocol (6P)
|
|
* \author
|
|
* Yasuyuki Tanaka <yasuyuki.tanaka@inf.ethz.ch>
|
|
*/
|
|
|
|
#include "contiki-lib.h"
|
|
#include "lib/assert.h"
|
|
|
|
#include "sixtop.h"
|
|
#include "sixtop-conf.h"
|
|
#include "sixp-trans.h"
|
|
|
|
/* Log configuration */
|
|
#include "sys/log.h"
|
|
#define LOG_MODULE "6top"
|
|
#define LOG_LEVEL LOG_LEVEL_6TOP
|
|
|
|
/**
|
|
* \brief 6P Transaction Data Structure (for internal use)
|
|
*/
|
|
typedef struct sixp_trans {
|
|
struct sixp_trans *next;
|
|
const sixtop_sf_t *sf;
|
|
linkaddr_t peer_addr;
|
|
uint8_t seqno;
|
|
sixp_pkt_cmd_t cmd;
|
|
sixp_trans_state_t state;
|
|
sixp_trans_mode_t mode;
|
|
struct {
|
|
sixp_sent_callback_t func;
|
|
void *arg;
|
|
uint16_t arg_len;
|
|
} callback;
|
|
struct ctimer timer;
|
|
} sixp_trans_t;
|
|
|
|
static void handle_trans_timeout(void *ptr);
|
|
static void process_trans(void *ptr);
|
|
static void schedule_trans_process(sixp_trans_t *trans);
|
|
static void free_trans(sixp_trans_t *trans);
|
|
static sixp_trans_mode_t determine_trans_mode(const sixp_pkt_t *req);
|
|
|
|
MEMB(trans_memb, sixp_trans_t, SIXTOP_MAX_TRANSACTIONS);
|
|
LIST(trans_list);
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
handle_trans_timeout(void *ptr)
|
|
{
|
|
sixp_trans_t *trans = (sixp_trans_t *)ptr;
|
|
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return;
|
|
}
|
|
|
|
if(trans->sf->timeout != NULL) {
|
|
trans->sf->timeout(trans->cmd,
|
|
(const linkaddr_t *)&trans->peer_addr);
|
|
}
|
|
|
|
(void)sixp_trans_transit_state(trans, SIXP_TRANS_STATE_TERMINATING);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
start_trans_timer(sixp_trans_t *trans)
|
|
{
|
|
ctimer_set(&trans->timer, trans->sf->timeout_interval,
|
|
handle_trans_timeout, trans);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
process_trans(void *ptr)
|
|
{
|
|
sixp_trans_t *trans = (sixp_trans_t *)ptr;
|
|
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* make sure that the timer is stopped */
|
|
ctimer_stop(&trans->timer);
|
|
|
|
/* state-specific operation */
|
|
if(trans->state == SIXP_TRANS_STATE_TERMINATING) {
|
|
/* handle the terminating state first */
|
|
LOG_INFO("6P-trans: trans [peer_addr:");
|
|
LOG_INFO_LLADDR((const linkaddr_t *)&trans->peer_addr);
|
|
LOG_INFO_(", seqno:%u] is going to be freed\n", trans->seqno);
|
|
free_trans(trans);
|
|
return;
|
|
}
|
|
|
|
switch(trans->state) {
|
|
/* do for others */
|
|
case SIXP_TRANS_STATE_RESPONSE_SENT:
|
|
case SIXP_TRANS_STATE_RESPONSE_RECEIVED:
|
|
if(trans->mode == SIXP_TRANS_MODE_2_STEP) {
|
|
(void)sixp_trans_transit_state(trans, SIXP_TRANS_STATE_TERMINATING);
|
|
}
|
|
break;
|
|
case SIXP_TRANS_STATE_CONFIRMATION_SENT:
|
|
case SIXP_TRANS_STATE_CONFIRMATION_RECEIVED:
|
|
(void)sixp_trans_transit_state(trans, SIXP_TRANS_STATE_TERMINATING);
|
|
break;
|
|
case SIXP_TRANS_STATE_TERMINATING:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(trans->state != SIXP_TRANS_STATE_TERMINATING) {
|
|
/* set the timer with a timeout values defined by the SF */
|
|
start_trans_timer(trans);
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
schedule_trans_process(sixp_trans_t *trans)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return;
|
|
}
|
|
|
|
ctimer_stop(&trans->timer);
|
|
ctimer_set(&trans->timer, 0, process_trans, trans); /* expires immediately */
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
free_trans(sixp_trans_t *trans)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return;
|
|
}
|
|
|
|
list_remove(trans_list, trans);
|
|
memb_free(&trans_memb, trans);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static sixp_trans_mode_t
|
|
determine_trans_mode(const sixp_pkt_t *req)
|
|
{
|
|
uint16_t cell_list_len;
|
|
|
|
assert(req != NULL);
|
|
if(req == NULL) {
|
|
return SIXP_TRANS_MODE_UNAVAILABLE;
|
|
}
|
|
|
|
/*
|
|
* We consider a transaction as 3-step if and only if its request command is
|
|
* either Add or Delete AND its cell_list is empty. Otherwise, 2-step.
|
|
*/
|
|
if(req->type == SIXP_PKT_TYPE_REQUEST &&
|
|
(req->code.cmd == SIXP_PKT_CMD_ADD ||
|
|
req->code.cmd == SIXP_PKT_CMD_DELETE) &&
|
|
sixp_pkt_get_cell_list(req->type, (sixp_pkt_code_t)req->code.value,
|
|
NULL, &cell_list_len,
|
|
req->body, req->body_len) == 0 &&
|
|
cell_list_len == 0) {
|
|
return SIXP_TRANS_MODE_3_STEP;
|
|
}
|
|
|
|
return SIXP_TRANS_MODE_2_STEP;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
sixp_trans_transit_state(sixp_trans_t *trans, sixp_trans_state_t new_state)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
LOG_ERR("6top: invalid argument, trans is NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
/* enforce state transition rules */
|
|
if(new_state == SIXP_TRANS_STATE_TERMINATING ||
|
|
(new_state == SIXP_TRANS_STATE_REQUEST_SENT &&
|
|
trans->state == SIXP_TRANS_STATE_INIT) ||
|
|
(new_state == SIXP_TRANS_STATE_REQUEST_RECEIVED &&
|
|
trans->state == SIXP_TRANS_STATE_INIT) ||
|
|
(new_state == SIXP_TRANS_STATE_RESPONSE_SENT &&
|
|
trans->state == SIXP_TRANS_STATE_REQUEST_RECEIVED) ||
|
|
(new_state == SIXP_TRANS_STATE_RESPONSE_RECEIVED &&
|
|
trans->state == SIXP_TRANS_STATE_REQUEST_SENT) ||
|
|
(new_state == SIXP_TRANS_STATE_CONFIRMATION_RECEIVED &&
|
|
trans->state == SIXP_TRANS_STATE_RESPONSE_SENT &&
|
|
trans->mode == SIXP_TRANS_MODE_3_STEP) ||
|
|
(new_state == SIXP_TRANS_STATE_CONFIRMATION_SENT &&
|
|
trans->state == SIXP_TRANS_STATE_RESPONSE_RECEIVED &&
|
|
trans->mode == SIXP_TRANS_MODE_3_STEP)) {
|
|
LOG_INFO("6P-trans: trans %p state changes from %u to %u\n",
|
|
trans, trans->state, new_state);
|
|
trans->state = new_state;
|
|
schedule_trans_process(trans);
|
|
return 0;
|
|
}
|
|
|
|
/* invalid transition */
|
|
LOG_ERR("6P-trans: invalid transaction, from %u to %u, detected on trans %p\n",
|
|
trans->state, new_state, trans);
|
|
return -1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
sixp_pkt_cmd_t
|
|
sixp_trans_get_cmd(sixp_trans_t *trans)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return SIXP_PKT_CMD_UNAVAILABLE;
|
|
}
|
|
return trans->cmd;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
sixp_trans_state_t
|
|
sixp_trans_get_state(sixp_trans_t *trans)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return SIXP_TRANS_STATE_UNAVAILABLE;
|
|
}
|
|
return trans->state;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int16_t
|
|
sixp_trans_get_seqno(sixp_trans_t *trans)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
LOG_ERR("6P-trans: sixp_trans_get_seqno() fails because trans is NULL\n");
|
|
return -1;
|
|
}
|
|
return trans->seqno;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
sixp_trans_mode_t
|
|
sixp_trans_get_mode(sixp_trans_t *trans)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
LOG_ERR("6P-trans: sixp_trans_get_mode() fails because trans is NULL\n");
|
|
return SIXP_TRANS_STATE_UNAVAILABLE;
|
|
}
|
|
return trans->mode;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
sixp_trans_invoke_callback(sixp_trans_t *trans, sixp_output_status_t status)
|
|
{
|
|
assert(trans != NULL);
|
|
|
|
if(trans == NULL || trans->callback.func == NULL) {
|
|
return;
|
|
}
|
|
trans->callback.func(trans->callback.arg, trans->callback.arg_len,
|
|
&trans->peer_addr, status);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
sixp_trans_set_callback(sixp_trans_t *trans,
|
|
sixp_sent_callback_t func, void *arg, uint16_t arg_len)
|
|
{
|
|
assert(trans != NULL);
|
|
if(trans == NULL) {
|
|
return;
|
|
}
|
|
trans->callback.func = func;
|
|
trans->callback.arg = arg;
|
|
trans->callback.arg_len = arg_len;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
sixp_trans_t *
|
|
sixp_trans_alloc(const sixp_pkt_t *pkt, const linkaddr_t *peer_addr)
|
|
{
|
|
const sixtop_sf_t *sf;
|
|
sixp_trans_t *trans;
|
|
|
|
assert(pkt != NULL && peer_addr != NULL);
|
|
if(pkt == NULL || peer_addr == NULL) {
|
|
LOG_ERR("6P-trans: sixp_trans_alloc() fails because of invalid argument\n");
|
|
return NULL;
|
|
}
|
|
|
|
if((sf = sixtop_find_sf(pkt->sfid)) == NULL) {
|
|
LOG_ERR("6P-trans: sixp_trans_alloc() fails; no suitable SF [sfid:%u]\n",
|
|
pkt->sfid);
|
|
return NULL;
|
|
}
|
|
|
|
if(sixp_trans_find(peer_addr) != NULL) {
|
|
LOG_ERR("6P-trans: sixp_trans_alloc() fails because another trans with ");
|
|
LOG_ERR_LLADDR((const linkaddr_t *)peer_addr);
|
|
LOG_ERR_("is in process\n");
|
|
return NULL;
|
|
}
|
|
|
|
if((trans = memb_alloc(&trans_memb)) == NULL) {
|
|
LOG_ERR("6P-trans: sixp_trans_alloc() fails because of lack of memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
memset(trans, 0, sizeof(sixp_trans_t));
|
|
trans->sf = sf;
|
|
trans->peer_addr = *peer_addr;
|
|
trans->seqno = pkt->seqno;
|
|
trans->cmd = pkt->code.value;
|
|
trans->state = SIXP_TRANS_STATE_INIT;
|
|
trans->mode = determine_trans_mode(pkt);
|
|
list_add(trans_list, trans);
|
|
start_trans_timer(trans);
|
|
|
|
return trans;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
sixp_trans_t *
|
|
sixp_trans_find(const linkaddr_t *peer_addr)
|
|
{
|
|
sixp_trans_t *trans;
|
|
|
|
assert(peer_addr != NULL);
|
|
if(peer_addr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* XXX: we don't support concurrent 6P transactions which is mentioned in
|
|
* Section 4.3.3, draft-ietf-6tisch-6top-protocol-03.
|
|
*
|
|
* The assumption here is that there is one transactions for a single peer at
|
|
* most.
|
|
*/
|
|
for(trans = list_head(trans_list);
|
|
trans != NULL; trans = trans->next) {
|
|
if(memcmp(peer_addr, &trans->peer_addr, sizeof(linkaddr_t)) == 0) {
|
|
return trans;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
sixp_trans_init(void)
|
|
{
|
|
sixp_trans_t *trans, *next_trans;
|
|
|
|
/* make sure there's no timer task left before the initialization */
|
|
for(trans = list_head(trans_list);
|
|
trans != NULL; trans = next_trans) {
|
|
next_trans = trans->next;
|
|
ctimer_stop(&trans->timer);
|
|
free_trans(trans);
|
|
}
|
|
|
|
list_init(trans_list);
|
|
memb_init(&trans_memb);
|
|
return 0;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/** @} */
|