2016-02-04 14:37:35 +01:00
|
|
|
/*
|
2017-02-06 18:26:45 +01:00
|
|
|
* Copyright (C) 2015-2017 Simon Brummer
|
2016-02-04 14:37:35 +01:00
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
|
|
* directory for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ingroup net_gnrc
|
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
2017-02-06 18:26:45 +01:00
|
|
|
* @brief GNRC TCP API implementation
|
2016-02-04 14:37:35 +01:00
|
|
|
*
|
2017-02-01 16:33:31 +01:00
|
|
|
* @author Simon Brummer <simon.brummer@posteo.de>
|
2016-02-04 14:37:35 +01:00
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <utlist.h>
|
|
|
|
#include "net/af.h"
|
|
|
|
#include "net/gnrc/tcp.h"
|
2017-02-01 15:09:46 +01:00
|
|
|
#include "internal/common.h"
|
2016-02-04 14:37:35 +01:00
|
|
|
#include "internal/fsm.h"
|
|
|
|
#include "internal/pkt.h"
|
|
|
|
#include "internal/option.h"
|
|
|
|
#include "internal/eventloop.h"
|
|
|
|
#include "internal/rcvbuf.h"
|
|
|
|
|
|
|
|
#ifdef MODULE_GNRC_IPV6
|
|
|
|
#include "net/gnrc/ipv6.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
/**
|
2017-03-06 08:47:06 +01:00
|
|
|
* @brief Allocate memory for GNRC TCP thread stack.
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
|
|
|
#if ENABLE_DEBUG
|
2017-02-01 15:09:46 +01:00
|
|
|
static char _stack[TCP_EVENTLOOP_STACK_SIZE + THREAD_EXTRA_STACKSIZE_PRINTF];
|
2016-02-04 14:37:35 +01:00
|
|
|
#else
|
2017-02-01 15:09:46 +01:00
|
|
|
static char _stack[TCP_EVENTLOOP_STACK_SIZE];
|
2016-02-04 14:37:35 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
2017-03-06 08:47:06 +01:00
|
|
|
* @brief TCPs eventloop pid, declared externally.
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
2017-02-01 15:09:46 +01:00
|
|
|
kernel_pid_t gnrc_tcp_pid = KERNEL_PID_UNDEF;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-06 08:47:06 +01:00
|
|
|
* @brief Head of liked TCB list.
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
2017-02-01 15:09:46 +01:00
|
|
|
gnrc_tcp_tcb_t *_list_tcb_head;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-06 08:47:06 +01:00
|
|
|
* @brief Mutex for TCB list synchronization.
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
2017-02-01 15:09:46 +01:00
|
|
|
mutex_t _list_tcb_lock;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/**
|
2017-02-25 18:40:37 +01:00
|
|
|
* @brief Helper struct, holding all argument data for_cb_mbox_put_msg.
|
|
|
|
*/
|
|
|
|
typedef struct _cb_arg {
|
|
|
|
uint32_t msg_type; /**< Message Type to Put into mbox behind mbox_ptr */
|
|
|
|
mbox_t *mbox_ptr; /**< Pointer to mbox */
|
|
|
|
} cb_arg_t;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Callback for xtimer, puts a message in a mbox.
|
|
|
|
*
|
|
|
|
* @param[in] arg Ptr to cb_arg_t. Must not be NULL or anything else.
|
|
|
|
*/
|
|
|
|
static void _cb_mbox_put_msg(void *arg)
|
|
|
|
{
|
|
|
|
msg_t msg;
|
|
|
|
msg.type = ((cb_arg_t *) arg)->msg_type;
|
|
|
|
mbox_try_put(((cb_arg_t *) arg)->mbox_ptr, &msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Setup timer with a callback function.
|
|
|
|
*
|
|
|
|
* @param[in/out] timer Ptr to timer, which should be set.
|
|
|
|
* @param[in] duration Duration after @p timer expires.
|
|
|
|
* @param[in] cb Function to be called after @p duration.
|
|
|
|
* @param[in] arg Arguments for @p cb.
|
|
|
|
*/
|
|
|
|
static void _setup_timeout(xtimer_t *timer, const uint32_t duration, const xtimer_callback_t cb,
|
|
|
|
cb_arg_t *arg)
|
|
|
|
{
|
|
|
|
timer->callback = cb;
|
|
|
|
timer->arg = arg;
|
|
|
|
xtimer_set(timer, duration);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Establishes a new TCP connection
|
2016-02-04 14:37:35 +01:00
|
|
|
*
|
2017-03-06 08:47:06 +01:00
|
|
|
* @param[in,out] tcb TCB holding the connection information.
|
|
|
|
* @param[in] target_addr Target address to connect to, if this is a active connection.
|
|
|
|
* @param[in] target_port Target port to connect to, if this is a active connection.
|
|
|
|
* @param[in] local_addr Local address to bind on, if this is a passive connection.
|
|
|
|
* @param[in] local_port Local port to bind on, if this is a passive connection.
|
|
|
|
* @param[in] passive Flag to indicate if this is a active or passive open.
|
2016-02-04 14:37:35 +01:00
|
|
|
*
|
2017-03-06 08:47:06 +01:00
|
|
|
* @returns Zero on success.
|
|
|
|
* -EISCONN if TCB is already connected.
|
|
|
|
* -ENOMEM if the receive buffer for the TCB could not be allocated.
|
|
|
|
* -EADDRINUSE if @p local_port is already in use.
|
|
|
|
* -ETIMEDOUT if the connection opening timed out.
|
|
|
|
* -ECONNREFUSED if the connection was resetted by the peer.
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
|
|
|
static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const uint8_t *target_addr, uint16_t target_port,
|
|
|
|
const uint8_t *local_addr, uint16_t local_port, uint8_t passive)
|
|
|
|
{
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
|
|
|
xtimer_t connection_timeout;
|
|
|
|
cb_arg_t connection_timeout_arg = {MSG_TYPE_CONNECTION_TIMEOUT, &(tcb->mbox)};
|
|
|
|
int8_t ret = 0;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Lock the TCB for this function call */
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_lock(&(tcb->function_lock));
|
|
|
|
|
|
|
|
/* Connection is already connected: Return -EISCONN */
|
2017-02-01 15:43:16 +01:00
|
|
|
if (tcb->state != FSM_STATE_CLOSED) {
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return -EISCONN;
|
|
|
|
}
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Mark TCB as waiting for incomming messages */
|
|
|
|
tcb->status |= STATUS_WAIT_FOR_MSG;
|
|
|
|
|
|
|
|
/* 'Flush' mbox */
|
|
|
|
while (mbox_try_get(&(tcb->mbox), &msg) != 0) {
|
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/* Setup passive connection */
|
2017-02-04 10:19:59 +01:00
|
|
|
if (passive) {
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Mark connection as passive opend */
|
2017-02-01 15:09:46 +01:00
|
|
|
tcb->status |= STATUS_PASSIVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
if (local_addr == NULL) {
|
2017-02-01 15:09:46 +01:00
|
|
|
tcb->status |= STATUS_ALLOW_ANY_ADDR;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2017-03-06 08:47:06 +01:00
|
|
|
/* If local address is specified: Copy it into TCB */
|
|
|
|
else if (tcb->address_family == AF_INET6) {
|
|
|
|
memcpy(tcb->local_addr, local_addr, sizeof(ipv6_addr_t));
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
#endif
|
|
|
|
/* Set port number to listen on */
|
2016-02-04 14:37:35 +01:00
|
|
|
tcb->local_port = local_port;
|
|
|
|
}
|
|
|
|
/* Setup active connection */
|
2017-02-04 10:19:59 +01:00
|
|
|
else {
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Copy target address and port number into TCB */
|
2016-02-04 14:37:35 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2017-03-06 08:47:06 +01:00
|
|
|
if ((target_addr != NULL) && (tcb->address_family == AF_INET6)) {
|
|
|
|
memcpy(tcb->peer_addr, target_addr, sizeof(ipv6_addr_t));
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
#endif
|
|
|
|
/* Assign port numbers, verfication happens in fsm */
|
2016-02-04 14:37:35 +01:00
|
|
|
tcb->local_port = local_port;
|
|
|
|
tcb->peer_port = target_port;
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Setup connection timeout: Put timeout message in TCBs mbox on expiration */
|
|
|
|
_setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
|
|
|
_cb_mbox_put_msg, &connection_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Call FSM with event: CALL_OPEN */
|
2017-02-01 15:43:16 +01:00
|
|
|
ret = _fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_connect() : Out of receive buffers.\n");
|
2017-02-04 10:19:59 +01:00
|
|
|
}
|
|
|
|
else if(ret == -EADDRINUSE) {
|
2016-02-04 14:37:35 +01:00
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_connect() : local_port is already in use.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait until a connection was established or closed */
|
2017-02-04 10:19:59 +01:00
|
|
|
while (ret >= 0 && tcb->state != FSM_STATE_CLOSED && tcb->state != FSM_STATE_ESTABLISHED &&
|
|
|
|
tcb->state != FSM_STATE_CLOSE_WAIT) {
|
2017-02-25 18:40:37 +01:00
|
|
|
mbox_get(&(tcb->mbox), &msg);
|
2016-02-04 14:37:35 +01:00
|
|
|
switch (msg.type) {
|
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : CONNECTION_TIMEOUT\n");
|
2017-02-01 15:43:16 +01:00
|
|
|
_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
|
|
|
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : MSG_TYPE_NOTIFY_USER\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : other message type\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2017-02-25 18:40:37 +01:00
|
|
|
xtimer_remove(&connection_timeout);
|
2017-02-01 15:43:16 +01:00
|
|
|
if (tcb->state == FSM_STATE_CLOSED && ret == 0) {
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ECONNREFUSED;
|
|
|
|
}
|
2017-02-25 18:40:37 +01:00
|
|
|
tcb->status &= ~STATUS_WAIT_FOR_MSG;
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* External GNRC TCP API */
|
2016-02-04 14:37:35 +01:00
|
|
|
int gnrc_tcp_init(void)
|
|
|
|
{
|
|
|
|
/* Guard: Check if thread is already running */
|
2017-02-01 15:09:46 +01:00
|
|
|
if (gnrc_tcp_pid != KERNEL_PID_UNDEF) {
|
2016-02-04 14:37:35 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Initialize mutex for TCB list synchronization */
|
2017-02-01 15:09:46 +01:00
|
|
|
mutex_init(&(_list_tcb_lock));
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Initialize TCB list */
|
2017-02-01 15:09:46 +01:00
|
|
|
_list_tcb_head = NULL;
|
2016-02-04 14:37:35 +01:00
|
|
|
_rcvbuf_init();
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Start TCP processing thread */
|
2017-05-15 13:16:18 +02:00
|
|
|
return thread_create(_stack, sizeof(_stack), TCP_EVENTLOOP_PRIO,
|
|
|
|
THREAD_CREATE_STACKTEST, _event_loop, NULL,
|
2017-02-01 15:09:46 +01:00
|
|
|
"gnrc_tcp");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2017-02-04 10:19:59 +01:00
|
|
|
void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
2017-03-06 08:47:06 +01:00
|
|
|
memset(tcb, 0, sizeof(gnrc_tcp_tcb_t));
|
2016-02-04 14:37:35 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
|
|
|
tcb->address_family = AF_INET6;
|
|
|
|
#else
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_tcb_init() : Address unspec, add netlayer module to makefile\n");
|
|
|
|
#endif
|
2017-02-01 15:09:46 +01:00
|
|
|
tcb->rtt_var = RTO_UNINITIALIZED;
|
|
|
|
tcb->srtt = RTO_UNINITIALIZED;
|
|
|
|
tcb->rto = RTO_UNINITIALIZED;
|
2017-02-25 18:40:37 +01:00
|
|
|
mbox_init(&(tcb->mbox), tcb->mbox_raw, GNRC_TCP_TCB_MBOX_SIZE);
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_init(&(tcb->fsm_lock));
|
|
|
|
mutex_init(&(tcb->function_lock));
|
|
|
|
}
|
|
|
|
|
|
|
|
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const uint8_t address_family,
|
|
|
|
const uint8_t *target_addr, const uint16_t target_port,
|
|
|
|
const uint16_t local_port)
|
|
|
|
{
|
|
|
|
assert(tcb != NULL);
|
|
|
|
assert(target_addr != NULL);
|
2017-02-01 15:09:46 +01:00
|
|
|
assert(target_port != PORT_UNSPEC);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Check if AF-Family of target_addr is supported */
|
2016-02-04 14:37:35 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2017-03-06 08:47:06 +01:00
|
|
|
if (address_family != AF_INET6) {
|
|
|
|
return -EAFNOSUPPORT;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
#else
|
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Check if AF-Family for target address matches internally used AF-Family */
|
2016-02-04 14:37:35 +01:00
|
|
|
if (tcb->address_family != address_family) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Proceed with connection opening */
|
2016-02-04 14:37:35 +01:00
|
|
|
return _gnrc_tcp_open(tcb, target_addr, target_port, NULL, local_port, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const uint8_t address_family,
|
|
|
|
const uint8_t *local_addr, const uint16_t local_port)
|
|
|
|
{
|
|
|
|
assert(tcb != NULL);
|
2017-05-12 09:35:02 +02:00
|
|
|
assert(local_port != PORT_UNSPEC);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/* Check AF-Family support if local address was supplied */
|
|
|
|
if (local_addr != NULL) {
|
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2017-03-06 08:47:06 +01:00
|
|
|
if (address_family != AF_INET6) {
|
|
|
|
return -EAFNOSUPPORT;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
#else
|
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
#endif
|
2016-02-04 14:37:35 +01:00
|
|
|
/* Check if AF-Family matches internally used AF-Family */
|
|
|
|
if (tcb->address_family != address_family) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Proceed with connection opening */
|
2016-02-04 14:37:35 +01:00
|
|
|
return _gnrc_tcp_open(tcb, NULL, 0, local_addr, local_port, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,
|
|
|
|
const uint32_t timeout_duration_us)
|
|
|
|
{
|
|
|
|
assert(tcb != NULL);
|
|
|
|
assert(data != NULL);
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
|
|
|
xtimer_t connection_timeout;
|
|
|
|
cb_arg_t connection_timeout_arg = {MSG_TYPE_CONNECTION_TIMEOUT, &(tcb->mbox)};
|
|
|
|
xtimer_t user_timeout;
|
|
|
|
cb_arg_t user_timeout_arg = {MSG_TYPE_USER_SPEC_TIMEOUT, &(tcb->mbox)};
|
|
|
|
xtimer_t probe_timeout;
|
|
|
|
cb_arg_t probe_timeout_arg = {MSG_TYPE_PROBE_TIMEOUT, &(tcb->mbox)};
|
|
|
|
uint32_t probe_timeout_duration_us = 0;
|
|
|
|
ssize_t ret = 0;
|
|
|
|
bool probing_mode = false;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Lock the TCB for this function call */
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_lock(&(tcb->function_lock));
|
|
|
|
|
|
|
|
/* Check if connection is in a valid state */
|
2017-02-04 10:19:59 +01:00
|
|
|
if (tcb->state != FSM_STATE_ESTABLISHED && tcb->state != FSM_STATE_CLOSE_WAIT) {
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return -ENOTCONN;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Mark TCB as waiting for incomming messages */
|
|
|
|
tcb->status |= STATUS_WAIT_FOR_MSG;
|
|
|
|
|
|
|
|
/* 'Flush' mbox */
|
|
|
|
while (mbox_try_get(&(tcb->mbox), &msg) != 0) {
|
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Setup connection timeout: Put timeout message in tcb's mbox on expiration */
|
|
|
|
_setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
|
|
|
_cb_mbox_put_msg, &connection_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Setup user specified timeout if timeout_us is greater than zero */
|
2016-02-04 14:37:35 +01:00
|
|
|
if (timeout_duration_us > 0) {
|
2017-02-25 18:40:37 +01:00
|
|
|
_setup_timeout(&user_timeout, timeout_duration_us, _cb_mbox_put_msg, &user_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop until something was sent and acked */
|
|
|
|
while (ret == 0 || tcb->pkt_retransmit != NULL) {
|
|
|
|
/* Check if the connections state is closed. If so, a reset was received */
|
2017-02-01 15:43:16 +01:00
|
|
|
if (tcb->state == FSM_STATE_CLOSED) {
|
2017-02-04 10:19:59 +01:00
|
|
|
ret = -ECONNRESET;
|
|
|
|
break;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If the send window is closed: Setup Probing */
|
|
|
|
if (tcb->snd_wnd <= 0) {
|
|
|
|
/* If this is the first probe: Setup probing duration */
|
2017-02-25 18:40:37 +01:00
|
|
|
if (!probing_mode) {
|
|
|
|
probing_mode = true;
|
2016-02-04 14:37:35 +01:00
|
|
|
probe_timeout_duration_us = tcb->rto;
|
|
|
|
}
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Setup probe timeout */
|
|
|
|
_setup_timeout(&probe_timeout, timeout_duration_us, _cb_mbox_put_msg,
|
|
|
|
&probe_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Try to send data in case there nothing has been sent and we are not probing */
|
|
|
|
if (ret == 0 && !probing_mode) {
|
2017-02-01 15:43:16 +01:00
|
|
|
ret = _fsm(tcb, FSM_EVENT_CALL_SEND, NULL, (void *) data, len);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for responses */
|
2017-02-25 18:40:37 +01:00
|
|
|
mbox_get(&(tcb->mbox), &msg);
|
2016-02-04 14:37:35 +01:00
|
|
|
switch (msg.type) {
|
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : CONNECTION_TIMEOUT\n");
|
2017-02-01 15:43:16 +01:00
|
|
|
_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ECONNABORTED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_USER_SPEC_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : USER_SPEC_TIMEOUT\n");
|
2017-02-01 15:43:16 +01:00
|
|
|
_fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_PROBE_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : PROBE_TIMEOUT\n");
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Send probe */
|
2017-02-01 15:43:16 +01:00
|
|
|
_fsm(tcb, FSM_EVENT_SEND_PROBE, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
probe_timeout_duration_us += probe_timeout_duration_us;
|
|
|
|
|
|
|
|
/* Boundry check for time interval between probes */
|
|
|
|
if (probe_timeout_duration_us < GNRC_TCP_PROBE_LOWER_BOUND) {
|
|
|
|
probe_timeout_duration_us = GNRC_TCP_PROBE_LOWER_BOUND;
|
|
|
|
}
|
|
|
|
else if (probe_timeout_duration_us > GNRC_TCP_PROBE_UPPER_BOUND) {
|
|
|
|
probe_timeout_duration_us = GNRC_TCP_PROBE_UPPER_BOUND;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : NOTIFY_USER\n");
|
2017-02-25 18:40:37 +01:00
|
|
|
|
|
|
|
/* Connection is alive: Reset Connection Timeout */
|
|
|
|
_setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
|
|
|
_cb_mbox_put_msg, &connection_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/* If the window re-opened and we are probing: Stop it */
|
2017-02-25 18:40:37 +01:00
|
|
|
if (tcb->snd_wnd > 0 && probing_mode) {
|
|
|
|
probing_mode = false;
|
|
|
|
xtimer_remove(&probe_timeout);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : other message type\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2017-02-25 18:40:37 +01:00
|
|
|
xtimer_remove(&probe_timeout);
|
|
|
|
xtimer_remove(&connection_timeout);
|
|
|
|
xtimer_remove(&user_timeout);
|
|
|
|
tcb->status &= ~STATUS_WAIT_FOR_MSG;
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t gnrc_tcp_recv(gnrc_tcp_tcb_t *tcb, void *data, const size_t max_len,
|
|
|
|
const uint32_t timeout_duration_us)
|
|
|
|
{
|
|
|
|
assert(tcb != NULL);
|
|
|
|
assert(data != NULL);
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
|
|
|
xtimer_t connection_timeout;
|
|
|
|
cb_arg_t connection_timeout_arg = {MSG_TYPE_CONNECTION_TIMEOUT, &(tcb->mbox)};
|
|
|
|
xtimer_t user_timeout;
|
|
|
|
cb_arg_t user_timeout_arg = {MSG_TYPE_USER_SPEC_TIMEOUT, &(tcb->mbox)};
|
|
|
|
ssize_t ret = 0;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Lock the TCB for this function call */
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_lock(&(tcb->function_lock));
|
|
|
|
|
|
|
|
/* Check if connection is in a valid state */
|
2017-02-04 10:19:59 +01:00
|
|
|
if (tcb->state != FSM_STATE_ESTABLISHED && tcb->state != FSM_STATE_FIN_WAIT_1 &&
|
|
|
|
tcb->state != FSM_STATE_FIN_WAIT_2 && tcb->state != FSM_STATE_CLOSE_WAIT) {
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return -ENOTCONN;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If this call is non-blocking (timeout_duration_us == 0): Try to read data and return */
|
|
|
|
if (timeout_duration_us == 0) {
|
2017-02-01 15:43:16 +01:00
|
|
|
ret = _fsm(tcb, FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
2017-02-04 10:19:59 +01:00
|
|
|
if (ret == 0) {
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -EAGAIN;
|
|
|
|
}
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Mark TCB as waiting for incomming messages */
|
|
|
|
tcb->status |= STATUS_WAIT_FOR_MSG;
|
|
|
|
|
|
|
|
/* 'Flush' mbox */
|
|
|
|
while (mbox_try_get(&(tcb->mbox), &msg) != 0) {
|
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Setup connection timeout: Put timeout message in tcb's mbox on expiration */
|
|
|
|
_setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
|
|
|
_cb_mbox_put_msg, &connection_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Setup user specified timeout */
|
2017-02-25 18:40:37 +01:00
|
|
|
_setup_timeout(&user_timeout, timeout_duration_us, _cb_mbox_put_msg, &user_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Processing loop */
|
2016-02-04 14:37:35 +01:00
|
|
|
while (ret == 0) {
|
|
|
|
/* Check if the connections state is closed. If so, a reset was received */
|
2017-02-01 15:43:16 +01:00
|
|
|
if (tcb->state == FSM_STATE_CLOSED) {
|
2017-02-04 10:19:59 +01:00
|
|
|
ret = -ECONNRESET;
|
|
|
|
break;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to read available data */
|
2017-02-01 15:43:16 +01:00
|
|
|
ret = _fsm(tcb, FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/* If there was no data: Wait for next packet or until the timeout fires */
|
|
|
|
if (ret <= 0) {
|
2017-02-25 18:40:37 +01:00
|
|
|
mbox_get(&(tcb->mbox), &msg);
|
2016-02-04 14:37:35 +01:00
|
|
|
switch (msg.type) {
|
2017-02-04 10:19:59 +01:00
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_recv() : CONNECTION_TIMEOUT\n");
|
|
|
|
_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
|
|
|
ret = -ECONNABORTED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_USER_SPEC_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : USER_SPEC_TIMEOUT\n");
|
|
|
|
_fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_recv() : NOTIFY_USER\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_recv() : other message type\n");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2017-02-25 18:40:37 +01:00
|
|
|
xtimer_remove(&connection_timeout);
|
|
|
|
xtimer_remove(&user_timeout);
|
|
|
|
tcb->status &= ~STATUS_WAIT_FOR_MSG;
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-03 14:37:51 +02:00
|
|
|
void gnrc_tcp_close(gnrc_tcp_tcb_t *tcb)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
|
|
|
assert(tcb != NULL);
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
|
|
|
xtimer_t connection_timeout;
|
|
|
|
cb_arg_t connection_timeout_arg = {MSG_TYPE_CONNECTION_TIMEOUT, &(tcb->mbox)};
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Lock the TCB for this function call */
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_lock(&(tcb->function_lock));
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Return if connection is closed */
|
|
|
|
if (tcb->state == FSM_STATE_CLOSED) {
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2017-05-17 18:39:38 +02:00
|
|
|
return;
|
2017-02-25 18:40:37 +01:00
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Mark TCB as waiting for incomming messages */
|
|
|
|
tcb->status |= STATUS_WAIT_FOR_MSG;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* 'Flush' mbox */
|
|
|
|
while (mbox_try_get(&(tcb->mbox), &msg) != 0) {
|
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Setup connection timeout: Put timeout message in tcb's mbox on expiration */
|
|
|
|
_setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
|
|
|
_cb_mbox_put_msg, &connection_timeout_arg);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Start connection teardown sequence */
|
|
|
|
_fsm(tcb, FSM_EVENT_CALL_CLOSE, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Loop until the connection has been closed */
|
|
|
|
while (tcb->state != FSM_STATE_CLOSED) {
|
|
|
|
mbox_get(&(tcb->mbox), &msg);
|
|
|
|
switch (msg.type) {
|
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_close() : CONNECTION_TIMEOUT\n");
|
|
|
|
_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_close() : NOTIFY_USER\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG("gnrc_tcp.c : gnrc_tcp_close() : other message type\n");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2017-02-25 18:40:37 +01:00
|
|
|
xtimer_remove(&connection_timeout);
|
|
|
|
tcb->status &= ~STATUS_WAIT_FOR_MSG;
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2017-05-03 14:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void gnrc_tcp_abort(gnrc_tcp_tcb_t *tcb)
|
|
|
|
{
|
|
|
|
assert(tcb != NULL);
|
|
|
|
|
|
|
|
/* Lock the TCB for this function call */
|
|
|
|
mutex_lock(&(tcb->function_lock));
|
|
|
|
if (tcb->state != FSM_STATE_CLOSED) {
|
|
|
|
/* Call FSM ABORT event */
|
|
|
|
_fsm(tcb, FSM_EVENT_CALL_ABORT, NULL, NULL, 0);
|
|
|
|
}
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int gnrc_tcp_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_hdr)
|
|
|
|
{
|
|
|
|
uint16_t csum;
|
|
|
|
|
|
|
|
if ((hdr == NULL) || (pseudo_hdr == NULL)) {
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
if (hdr->type != GNRC_NETTYPE_TCP) {
|
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
csum = _pkt_calc_csum(hdr, pseudo_hdr, hdr->next);
|
|
|
|
if (csum == 0) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
((tcp_hdr_t *)hdr->data)->checksum = byteorder_htons(csum);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
gnrc_pktsnip_t *gnrc_tcp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src, uint16_t dst)
|
|
|
|
{
|
|
|
|
gnrc_pktsnip_t *res;
|
|
|
|
tcp_hdr_t *hdr;
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Allocate header */
|
2016-02-04 14:37:35 +01:00
|
|
|
res = gnrc_pktbuf_add(payload, NULL, sizeof(tcp_hdr_t), GNRC_NETTYPE_TCP);
|
|
|
|
if (res == NULL) {
|
|
|
|
DEBUG("tcp: No space left in packet buffer\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
hdr = (tcp_hdr_t *) res->data;
|
|
|
|
|
|
|
|
/* Clear Header */
|
|
|
|
memset(hdr, 0, sizeof(tcp_hdr_t));
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Initialize header with sane defaults */
|
2016-02-04 14:37:35 +01:00
|
|
|
hdr->src_port = byteorder_htons(src);
|
|
|
|
hdr->dst_port = byteorder_htons(dst);
|
|
|
|
hdr->checksum = byteorder_htons(0);
|
2017-01-31 19:22:43 +01:00
|
|
|
hdr->off_ctl = byteorder_htons(TCP_HDR_OFFSET_MIN);
|
2016-02-04 14:37:35 +01:00
|
|
|
return res;
|
|
|
|
}
|