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
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
2020-10-21 15:58:33 +02:00
|
|
|
#include <assert.h>
|
2016-02-04 14:37:35 +01:00
|
|
|
#include <errno.h>
|
2018-07-10 15:25:02 +02:00
|
|
|
#include <string.h>
|
2018-07-21 10:13:00 +02:00
|
|
|
#include <utlist.h>
|
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
#include "evtimer.h"
|
|
|
|
#include "evtimer_mbox.h"
|
2020-06-08 11:11:31 +02:00
|
|
|
#include "mbox.h"
|
2016-02-04 14:37:35 +01:00
|
|
|
#include "net/af.h"
|
2020-10-15 10:18:57 +02:00
|
|
|
#include "net/tcp.h"
|
2018-07-10 15:25:02 +02:00
|
|
|
#include "net/gnrc.h"
|
2016-02-04 14:37:35 +01:00
|
|
|
#include "net/gnrc/tcp.h"
|
2020-10-15 10:18:57 +02:00
|
|
|
#include "include/gnrc_tcp_common.h"
|
|
|
|
#include "include/gnrc_tcp_fsm.h"
|
|
|
|
#include "include/gnrc_tcp_pkt.h"
|
|
|
|
#include "include/gnrc_tcp_eventloop.h"
|
|
|
|
#include "include/gnrc_tcp_rcvbuf.h"
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
#ifdef MODULE_GNRC_IPV6
|
|
|
|
#include "net/gnrc/ipv6.h"
|
|
|
|
#endif
|
|
|
|
|
2020-10-22 11:35:22 +02:00
|
|
|
#define ENABLE_DEBUG 0
|
2016-02-04 14:37:35 +01:00
|
|
|
#include "debug.h"
|
|
|
|
|
2020-07-04 09:19:54 +02:00
|
|
|
#define TCP_MSG_QUEUE_SIZE (1 << CONFIG_GNRC_TCP_MSG_QUEUE_SIZE_EXP)
|
|
|
|
|
2016-02-04 14:37:35 +01:00
|
|
|
|
|
|
|
/**
|
2020-07-12 08:22:16 +02:00
|
|
|
* @brief Central MBOX evtimer used by gnrc_tcp
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
2020-07-12 08:22:16 +02:00
|
|
|
static evtimer_t _tcp_mbox_timer;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
static void _sched_mbox(evtimer_mbox_event_t *event, uint32_t offset,
|
|
|
|
uint16_t type, mbox_t *mbox)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2020-07-12 08:22:16 +02:00
|
|
|
event->event.offset = offset;
|
|
|
|
event->msg.type = type;
|
|
|
|
evtimer_add_mbox(&_tcp_mbox_timer, event, mbox);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2020-07-12 08:22:16 +02:00
|
|
|
}
|
2017-02-25 18:40:37 +01:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
static void _sched_connection_timeout(evtimer_mbox_event_t *event, mbox_t *mbox)
|
2017-02-25 18:40:37 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2020-07-12 08:22:16 +02:00
|
|
|
_sched_mbox(event, CONFIG_GNRC_TCP_CONNECTION_TIMEOUT_DURATION_MS,
|
|
|
|
MSG_TYPE_CONNECTION_TIMEOUT, mbox);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2017-02-25 18:40:37 +01:00
|
|
|
}
|
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
static void _unsched_mbox(evtimer_mbox_event_t *event)
|
2017-02-25 18:40:37 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2020-07-12 08:22:16 +02:00
|
|
|
evtimer_del(&_tcp_mbox_timer, (evtimer_event_t *)event);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2017-02-25 18:40:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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.
|
2019-10-23 21:16:23 +02:00
|
|
|
* -ECONNREFUSED if the connection was reset by the peer.
|
2016-02-04 14:37:35 +01:00
|
|
|
*/
|
2020-01-02 15:20:20 +01:00
|
|
|
static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote,
|
|
|
|
const uint8_t *local_addr, uint16_t local_port, int passive)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
2020-06-08 11:11:31 +02:00
|
|
|
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
|
|
|
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
2020-01-02 15:20:20 +01:00
|
|
|
int ret = 0;
|
2021-04-04 17:31:42 +02:00
|
|
|
_gnrc_tcp_fsm_state_t state = 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));
|
|
|
|
|
2020-01-02 15:20:20 +01:00
|
|
|
/* TCB is already connected: Return -EISCONN */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
if (state != FSM_STATE_CLOSED) {
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EISCONN: TCB already connected.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return -EISCONN;
|
|
|
|
}
|
|
|
|
|
2020-06-08 11:11:31 +02:00
|
|
|
/* Setup messaging */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
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
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2017-03-06 08:47:06 +01:00
|
|
|
/* If local address is specified: Copy it into TCB */
|
2020-01-02 15:20:20 +01:00
|
|
|
if (local_addr && tcb->address_family == AF_INET6) {
|
|
|
|
/* Store given address in TCB */
|
|
|
|
if (memcpy(tcb->local_addr, local_addr, sizeof(tcb->local_addr)) == NULL) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid peer address.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2018-05-11 10:06:14 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-01-02 15:20:20 +01:00
|
|
|
|
|
|
|
if (ipv6_addr_is_unspecified((ipv6_addr_t *) tcb->local_addr)) {
|
|
|
|
tcb->status |= STATUS_ALLOW_ANY_ADDR;
|
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2018-07-10 15:25:02 +02:00
|
|
|
#else
|
2019-09-14 15:47:10 +02:00
|
|
|
/* Suppress Compiler Warnings */
|
2020-01-02 15:20:20 +01:00
|
|
|
(void) remote;
|
|
|
|
(void) local_addr;
|
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 {
|
2020-01-02 15:20:20 +01:00
|
|
|
assert(remote != NULL);
|
|
|
|
|
2018-05-11 10:06:14 +02:00
|
|
|
/* Parse target address and port number into TCB */
|
2016-02-04 14:37:35 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2020-01-02 15:20:20 +01:00
|
|
|
if (tcb->address_family == AF_INET6) {
|
2018-05-11 10:06:14 +02:00
|
|
|
|
2020-01-02 15:20:20 +01:00
|
|
|
/* Store Address information in TCB */
|
|
|
|
if (memcpy(tcb->peer_addr, remote->addr.ipv6, sizeof(tcb->peer_addr)) == NULL) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid peer address.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2018-05-11 10:06:14 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-01-02 15:20:20 +01:00
|
|
|
tcb->ll_iface = remote->netif;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
#endif
|
2020-01-02 15:20:20 +01:00
|
|
|
|
2019-09-14 15:47:10 +02:00
|
|
|
/* Assign port numbers, verification happens in fsm */
|
2016-02-04 14:37:35 +01:00
|
|
|
tcb->local_port = local_port;
|
2020-01-02 15:20:20 +01:00
|
|
|
tcb->peer_port = remote->port;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
/* Setup connection timeout */
|
|
|
|
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Call FSM with event: CALL_OPEN */
|
2020-10-15 10:18:57 +02:00
|
|
|
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
2016-02-04 14:37:35 +01:00
|
|
|
if (ret == -ENOMEM) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ENOMEM: All receive buffers are in use.");
|
2017-02-04 10:19:59 +01:00
|
|
|
}
|
2020-01-02 15:20:20 +01:00
|
|
|
else if (ret == -EADDRINUSE) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EADDRINUSE: local_port is already in use.");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait until a connection was established or closed */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
while (ret >= 0 && state != FSM_STATE_CLOSED && state != FSM_STATE_ESTABLISHED &&
|
|
|
|
state != FSM_STATE_CLOSE_WAIT) {
|
2020-06-08 11:11:31 +02:00
|
|
|
mbox_get(&mbox, &msg);
|
2016-02-04 14:37:35 +01:00
|
|
|
switch (msg.type) {
|
2019-02-04 20:57:30 +01:00
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
2019-02-04 20:57:30 +01:00
|
|
|
|
|
|
|
/* Setup a timeout to be able to revert back to LISTEN state, in case the
|
|
|
|
* send SYN+ACK we received upon entering SYN_RCVD is never acknowledged
|
|
|
|
* by the peer. */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
if ((state == FSM_STATE_SYN_RCVD) && (tcb->status & STATUS_PASSIVE)) {
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&tcb->event_misc);
|
|
|
|
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
2019-02-04 20:57:30 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-02-04 14:37:35 +01:00
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2019-02-04 20:57:30 +01:00
|
|
|
/* The connection establishment attempt timed out:
|
|
|
|
* 1) Active connections return -ETIMEOUT.
|
|
|
|
* 2) Passive connections stop the ongoing retransmissions and repeat the
|
|
|
|
* open call to wait for the next connection attempt. */
|
|
|
|
if (tcb->status & STATUS_PASSIVE) {
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
2019-02-04 20:57:30 +01:00
|
|
|
}
|
|
|
|
else {
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ETIMEDOUT: Connection timed out.");
|
2019-02-04 20:57:30 +01:00
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
}
|
2016-02-04 14:37:35 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("Received unexpected message.");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&tcb->event_misc);
|
2021-04-04 17:31:42 +02:00
|
|
|
if (state == FSM_STATE_CLOSED && ret == 0) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ECONNREFUSED: Connection refused by peer.");
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ECONNREFUSED;
|
|
|
|
}
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* External GNRC TCP API */
|
2020-01-02 15:20:20 +01:00
|
|
|
int gnrc_tcp_ep_init(gnrc_tcp_ep_t *ep, int family, const uint8_t *addr, size_t addr_size,
|
|
|
|
uint16_t port, uint16_t netif)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2020-01-02 15:20:20 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
|
|
|
if (family != AF_INET6) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAFNOSUPPORT: Parameter family is not AF_INET6.")
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr == NULL && addr_size == 0) {
|
|
|
|
ipv6_addr_set_unspecified((ipv6_addr_t *) ep->addr.ipv6);
|
|
|
|
}
|
|
|
|
else if (addr_size == sizeof(ipv6_addr_t)) {
|
|
|
|
memcpy(ep->addr.ipv6, addr, sizeof(ipv6_addr_t));
|
|
|
|
}
|
|
|
|
else {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Parameter addr is invalid.")
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* Suppress Compiler Warnings */
|
|
|
|
(void) addr;
|
|
|
|
(void) addr_size;
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAFNOSUPPORT: No network layer configured.")
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ep->family = family;
|
|
|
|
ep->port = port;
|
|
|
|
ep->netif = netif;
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gnrc_tcp_ep_from_str(gnrc_tcp_ep_t *ep, const char *str)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2020-01-02 15:20:20 +01:00
|
|
|
assert(str);
|
|
|
|
|
|
|
|
unsigned port = 0;
|
|
|
|
unsigned netif = 0;
|
|
|
|
|
|
|
|
/* Examine given string */
|
|
|
|
char *addr_begin = strchr(str, '[');
|
|
|
|
char *addr_end = strchr(str, ']');
|
|
|
|
|
|
|
|
/* 1) Ensure that str contains a single pair of brackets */
|
|
|
|
if (!addr_begin || !addr_end || strchr(addr_begin + 1, '[') || strchr(addr_end + 1, ']')) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* 2) Ensure that the first character is the opening bracket */
|
|
|
|
else if (addr_begin != str) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 3) Examine optional port number */
|
|
|
|
char *port_begin = strchr(addr_end, ':');
|
|
|
|
if (port_begin) {
|
|
|
|
/* 3.1) Ensure that there are characters left to parse after ':'. */
|
|
|
|
if (*(++port_begin) == '\0') {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 3.2) Ensure that port is a number (atol, does not report errors) */
|
|
|
|
for (char *ptr = port_begin; *ptr; ++ptr) {
|
|
|
|
if ((*ptr < '0') || ('9' < *ptr)) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 3.3) Read and verify that given number port is within range */
|
|
|
|
port = atol(port_begin);
|
|
|
|
if (port > 0xFFFF) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 4) Examine optional interface identifier. */
|
|
|
|
char *if_begin = strchr(str, '%');
|
|
|
|
if (if_begin) {
|
|
|
|
/* 4.1) Ensure that the identifier is not empty and within brackets. */
|
|
|
|
if (addr_end <= (++if_begin)) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 4.2) Ensure that the identifier is a number (atol, does not report errors) */
|
|
|
|
for (char *ptr = if_begin; ptr != addr_end; ++ptr) {
|
|
|
|
if ((*ptr < '0') || ('9' < *ptr)) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 4.3) Read and replace addr_end with if_begin. */
|
|
|
|
netif = atol(if_begin);
|
|
|
|
addr_end = if_begin - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MODULE_GNRC_IPV6
|
|
|
|
/* 5) Try to parse IP Address. Construct Endpoint on after success. */
|
|
|
|
char tmp[IPV6_ADDR_MAX_STR_LEN];
|
|
|
|
|
|
|
|
/* 5.1) Verify address length and copy address into temporary buffer.
|
|
|
|
* This is required to preserve constness of input.
|
|
|
|
*/
|
|
|
|
int len = addr_end - (++addr_begin);
|
|
|
|
|
|
|
|
if (0 <= len && len < (int) sizeof(tmp)) {
|
|
|
|
memcpy(tmp, addr_begin, len);
|
|
|
|
tmp[len] = '\0';
|
|
|
|
}
|
|
|
|
else {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 5.2) Try to read address into endpoint. */
|
|
|
|
if (ipv6_addr_from_str((ipv6_addr_t *) ep->addr.ipv6, tmp) == NULL) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ep->family = AF_INET6;
|
|
|
|
#else
|
|
|
|
/* Suppress Compiler Warnings */
|
|
|
|
(void) port;
|
|
|
|
(void) netif;
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: Invalid address string.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ep->port = (uint16_t) port;
|
|
|
|
ep->netif = (uint16_t) netif;
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-04 14:37:35 +01:00
|
|
|
int gnrc_tcp_init(void)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2020-10-15 10:18:57 +02:00
|
|
|
/* Initialize receive buffers */
|
|
|
|
_gnrc_tcp_rcvbuf_init();
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
/* Initialize timers */
|
|
|
|
evtimer_init_mbox(&_tcp_mbox_timer);
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Start TCP processing thread */
|
2020-10-15 10:18:57 +02:00
|
|
|
kernel_pid_t pid = _gnrc_tcp_eventloop_init();
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
|
|
|
return pid;
|
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
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
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
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("Missing network layer. Add module to makefile.");
|
2016-02-04 14:37:35 +01:00
|
|
|
#endif
|
2017-02-01 15:09:46 +01:00
|
|
|
tcb->rtt_var = RTO_UNINITIALIZED;
|
|
|
|
tcb->srtt = RTO_UNINITIALIZED;
|
|
|
|
tcb->rto = RTO_UNINITIALIZED;
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_init(&(tcb->fsm_lock));
|
|
|
|
mutex_init(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2020-01-02 15:20:20 +01:00
|
|
|
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote, uint16_t local_port)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
assert(tcb != NULL);
|
2020-01-02 15:20:20 +01:00
|
|
|
assert(remote != NULL);
|
|
|
|
assert(remote->port != PORT_UNSPEC);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2020-01-02 15:20:20 +01:00
|
|
|
/* Check if given AF-Family in remote is supported */
|
2016-02-04 14:37:35 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2020-01-02 15:20:20 +01:00
|
|
|
if (remote->family != AF_INET6) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAFNOSUPPORT: remote AF-Family not supported.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2017-03-06 08:47:06 +01:00
|
|
|
return -EAFNOSUPPORT;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-03-06 08:47:06 +01:00
|
|
|
#else
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAFNOSUPPORT: AF-Family not supported.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2017-03-06 08:47:06 +01:00
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Check if AF-Family for target address matches internally used AF-Family */
|
2020-01-02 15:20:20 +01:00
|
|
|
if (remote->family != tcb->address_family) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: local and remote AF-Family don't match.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-01-02 15:20:20 +01:00
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Proceed with connection opening */
|
2020-10-09 11:14:42 +02:00
|
|
|
int res = _gnrc_tcp_open(tcb, remote, NULL, local_port, 0);
|
|
|
|
TCP_DEBUG_LEAVE;
|
|
|
|
return res;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2020-01-02 15:20:20 +01:00
|
|
|
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *local)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
assert(tcb != NULL);
|
2020-01-02 15:20:20 +01:00
|
|
|
assert(local != NULL);
|
|
|
|
assert(local->port != PORT_UNSPEC);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2020-01-02 15:20:20 +01:00
|
|
|
/* Check if given AF-Family in local is supported */
|
2016-02-04 14:37:35 +01:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
2020-01-02 15:20:20 +01:00
|
|
|
if (local->family != AF_INET6) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAFNOSUPPORT: AF-Family not supported.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2017-03-06 08:47:06 +01:00
|
|
|
return -EAFNOSUPPORT;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2020-01-02 15:20:20 +01:00
|
|
|
|
|
|
|
/* Check if AF-Family matches internally used AF-Family */
|
|
|
|
if (local->family != tcb->address_family) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EINVAL: AF-Family doesn't match.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Proceed with connection opening */
|
2020-10-09 11:14:42 +02:00
|
|
|
int res = _gnrc_tcp_open(tcb, NULL, local->addr.ipv6, local->port, 1);
|
|
|
|
TCP_DEBUG_LEAVE;
|
|
|
|
return res;
|
2020-01-02 15:20:20 +01:00
|
|
|
#else
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAFNOSUPPORT: AF-Family not supported.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2020-01-02 15:20:20 +01:00
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
#endif
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,
|
2020-07-12 08:22:16 +02:00
|
|
|
const uint32_t timeout_duration_ms)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
assert(tcb != NULL);
|
|
|
|
assert(data != NULL);
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
2020-06-08 11:11:31 +02:00
|
|
|
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
|
|
|
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
2020-07-12 08:22:16 +02:00
|
|
|
evtimer_mbox_event_t event_user_timeout;
|
|
|
|
evtimer_mbox_event_t event_probe_timeout;
|
|
|
|
uint32_t probe_timeout_duration_ms = 0;
|
2017-02-25 18:40:37 +01:00
|
|
|
ssize_t ret = 0;
|
|
|
|
bool probing_mode = false;
|
2021-04-04 17:31:42 +02:00
|
|
|
_gnrc_tcp_fsm_state_t state = 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 */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
if (state != FSM_STATE_ESTABLISHED && state != FSM_STATE_CLOSE_WAIT) {
|
2017-02-04 10:19:59 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ENOTCONN: TCB is not connected.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2017-02-04 10:19:59 +01:00
|
|
|
return -ENOTCONN;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2020-06-08 11:11:31 +02:00
|
|
|
/* Setup messaging */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
2020-06-16 13:26:43 +02:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
/* Setup connection timeout */
|
|
|
|
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
if (timeout_duration_ms > 0) {
|
|
|
|
_sched_mbox(&event_user_timeout, timeout_duration_ms,
|
|
|
|
MSG_TYPE_USER_SPEC_TIMEOUT, &mbox);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop until something was sent and acked */
|
|
|
|
while (ret == 0 || tcb->pkt_retransmit != NULL) {
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
|
2016-02-04 14:37:35 +01:00
|
|
|
/* Check if the connections state is closed. If so, a reset was received */
|
2021-04-04 17:31:42 +02:00
|
|
|
if (state == FSM_STATE_CLOSED) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ECONNRESET: Connection was reset by peer.");
|
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;
|
2020-07-12 08:22:16 +02:00
|
|
|
probe_timeout_duration_ms = tcb->rto;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Setup probe timeout */
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&event_probe_timeout);
|
|
|
|
_sched_mbox(&event_probe_timeout, probe_timeout_duration_ms,
|
|
|
|
MSG_TYPE_PROBE_TIMEOUT, &mbox);
|
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) {
|
2020-10-15 10:18:57 +02:00
|
|
|
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_SEND, NULL, (void *) data, len);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for responses */
|
2020-06-08 11:11:31 +02:00
|
|
|
mbox_get(&mbox, &msg);
|
2016-02-04 14:37:35 +01:00
|
|
|
switch (msg.type) {
|
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ECONNABORTED: Connection timed out.");
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ECONNABORTED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_USER_SPEC_TIMEOUT:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_USER_SPEC_TIMEOUT.");
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ETIMEDOUT: User specified timeout expired.");
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_PROBE_TIMEOUT:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_PROBE_TIMEOUT.");
|
2017-03-06 08:47:06 +01:00
|
|
|
/* Send probe */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_SEND_PROBE, NULL, NULL, 0);
|
2020-07-12 08:22:16 +02:00
|
|
|
probe_timeout_duration_ms += probe_timeout_duration_ms;
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2019-09-14 15:47:10 +02:00
|
|
|
/* Boundary check for time interval between probes */
|
2020-07-12 08:22:16 +02:00
|
|
|
if (probe_timeout_duration_ms < CONFIG_GNRC_TCP_PROBE_LOWER_BOUND_MS) {
|
|
|
|
probe_timeout_duration_ms = CONFIG_GNRC_TCP_PROBE_LOWER_BOUND_MS;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2020-07-12 08:22:16 +02:00
|
|
|
else if (probe_timeout_duration_ms > CONFIG_GNRC_TCP_PROBE_UPPER_BOUND_MS) {
|
|
|
|
probe_timeout_duration_ms = CONFIG_GNRC_TCP_PROBE_UPPER_BOUND_MS;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
2017-02-25 18:40:37 +01:00
|
|
|
|
|
|
|
/* Connection is alive: Reset Connection Timeout */
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&tcb->event_misc);
|
|
|
|
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
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;
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&event_probe_timeout);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("Received unexpected message.");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&tcb->event_misc);
|
|
|
|
_unsched_mbox(&event_probe_timeout);
|
|
|
|
_unsched_mbox(&event_user_timeout);
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t gnrc_tcp_recv(gnrc_tcp_tcb_t *tcb, void *data, const size_t max_len,
|
2020-07-12 08:22:16 +02:00
|
|
|
const uint32_t timeout_duration_ms)
|
2016-02-04 14:37:35 +01:00
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
assert(tcb != NULL);
|
|
|
|
assert(data != NULL);
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
2020-06-08 11:11:31 +02:00
|
|
|
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
|
|
|
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
2020-07-12 08:22:16 +02:00
|
|
|
evtimer_mbox_event_t event_user_timeout;
|
2017-02-25 18:40:37 +01:00
|
|
|
ssize_t ret = 0;
|
2021-04-04 17:31:42 +02:00
|
|
|
_gnrc_tcp_fsm_state_t state = 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 */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
if (state != FSM_STATE_ESTABLISHED && state != FSM_STATE_FIN_WAIT_1 &&
|
|
|
|
state != FSM_STATE_FIN_WAIT_2 && state != FSM_STATE_CLOSE_WAIT) {
|
2017-02-04 10:19:59 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ENOTCONN: TCB is not connected.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2017-02-04 10:19:59 +01:00
|
|
|
return -ENOTCONN;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 10:42:51 +02:00
|
|
|
/* If FIN was received (CLOSE_WAIT), no further data can be received. */
|
|
|
|
/* Copy received data into given buffer and return number of bytes. Can be zero. */
|
2021-04-04 17:31:42 +02:00
|
|
|
if (state == FSM_STATE_CLOSE_WAIT) {
|
2020-10-15 10:18:57 +02:00
|
|
|
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
2019-08-09 10:42:51 +02:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2019-08-09 10:42:51 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
/* If this call is non-blocking (timeout_duration_ms == 0): Try to read data and return */
|
|
|
|
if (timeout_duration_ms == 0) {
|
2020-10-15 10:18:57 +02:00
|
|
|
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
2017-02-04 10:19:59 +01:00
|
|
|
if (ret == 0) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EAGAIN: Not data available, try later again.");
|
2016-02-04 14:37:35 +01:00
|
|
|
ret = -EAGAIN;
|
|
|
|
}
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-06-08 11:11:31 +02:00
|
|
|
/* Setup messaging */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
2020-06-16 13:26:43 +02:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
/* Setup connection timeout */
|
|
|
|
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
|
|
|
|
|
|
|
if (timeout_duration_ms > 0) {
|
|
|
|
_sched_mbox(&event_user_timeout, timeout_duration_ms,
|
|
|
|
MSG_TYPE_USER_SPEC_TIMEOUT, &mbox);
|
|
|
|
}
|
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 */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
if (state == FSM_STATE_CLOSED) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ECONNRESET: Connection was reset by peer.");
|
2017-02-04 10:19:59 +01:00
|
|
|
ret = -ECONNRESET;
|
|
|
|
break;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to read available data */
|
2020-10-15 10:18:57 +02:00
|
|
|
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2019-08-09 10:42:51 +02:00
|
|
|
/* If FIN was received (CLOSE_WAIT), no further data can be received. Leave event loop */
|
2021-04-04 17:31:42 +02:00
|
|
|
if (state == FSM_STATE_CLOSE_WAIT) {
|
2019-08-09 10:42:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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) {
|
2020-06-08 11:11:31 +02:00
|
|
|
mbox_get(&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:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ECONNABORTED: Connection timed out.");
|
2017-02-04 10:19:59 +01:00
|
|
|
ret = -ECONNABORTED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_USER_SPEC_TIMEOUT:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_USER_SPEC_TIMEOUT.");
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ETIMEDOUT: User specified timeout expired.");
|
2017-02-04 10:19:59 +01:00
|
|
|
ret = -ETIMEDOUT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
2017-02-04 10:19:59 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("Received unexpected message.");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&tcb->event_misc);
|
|
|
|
_unsched_mbox(&event_user_timeout);
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
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
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
assert(tcb != NULL);
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
msg_t msg;
|
2020-06-08 11:11:31 +02:00
|
|
|
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
|
|
|
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
2021-04-04 17:31:42 +02:00
|
|
|
_gnrc_tcp_fsm_state_t state = 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));
|
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Return if connection is closed */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
if (state == FSM_STATE_CLOSED) {
|
2017-02-25 18:40:37 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
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
|
|
|
|
2020-06-08 11:11:31 +02:00
|
|
|
/* Setup messaging */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
2020-06-16 13:26:43 +02:00
|
|
|
|
2020-07-12 08:22:16 +02:00
|
|
|
/* Setup connection timeout */
|
|
|
|
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
2016-02-04 14:37:35 +01:00
|
|
|
|
2017-02-25 18:40:37 +01:00
|
|
|
/* Start connection teardown sequence */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_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 */
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
|
|
|
while (state != FSM_STATE_CLOSED) {
|
2020-06-08 11:11:31 +02:00
|
|
|
mbox_get(&mbox, &msg);
|
2017-02-25 18:40:37 +01:00
|
|
|
switch (msg.type) {
|
|
|
|
case MSG_TYPE_CONNECTION_TIMEOUT:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
2017-02-25 18:40:37 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_TYPE_NOTIFY_USER:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
2017-02-25 18:40:37 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("Received unexpected message.");
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
2021-04-04 17:31:42 +02:00
|
|
|
state = _gnrc_tcp_fsm_get_state(tcb);
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
2020-07-12 08:22:16 +02:00
|
|
|
_unsched_mbox(&tcb->event_misc);
|
2016-02-04 14:37:35 +01:00
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2017-05-03 14:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void gnrc_tcp_abort(gnrc_tcp_tcb_t *tcb)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2017-05-03 14:37:51 +02:00
|
|
|
assert(tcb != NULL);
|
|
|
|
|
|
|
|
/* Lock the TCB for this function call */
|
|
|
|
mutex_lock(&(tcb->function_lock));
|
2021-04-04 17:31:42 +02:00
|
|
|
if (_gnrc_tcp_fsm_get_state(tcb) != FSM_STATE_CLOSED) {
|
2017-05-03 14:37:51 +02:00
|
|
|
/* Call FSM ABORT event */
|
2020-10-15 10:18:57 +02:00
|
|
|
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_ABORT, NULL, NULL, 0);
|
2017-05-03 14:37:51 +02:00
|
|
|
}
|
|
|
|
mutex_unlock(&(tcb->function_lock));
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int gnrc_tcp_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_hdr)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
uint16_t csum;
|
|
|
|
|
|
|
|
if ((hdr == NULL) || (pseudo_hdr == NULL)) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EFAULT: hdr or pseudo_hdr is NULL.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
if (hdr->type != GNRC_NETTYPE_TCP) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-EBADMSG: Variable hdr is no TCP header.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
2020-10-15 10:18:57 +02:00
|
|
|
csum = _gnrc_tcp_pkt_calc_csum(hdr, pseudo_hdr, hdr->next);
|
2016-02-04 14:37:35 +01:00
|
|
|
if (csum == 0) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("-ENOENT");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
((tcp_hdr_t *)hdr->data)->checksum = byteorder_htons(csum);
|
2020-10-09 11:14:42 +02:00
|
|
|
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
gnrc_pktsnip_t *gnrc_tcp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src, uint16_t dst)
|
|
|
|
{
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ENTER;
|
2016-02-04 14:37:35 +01:00
|
|
|
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) {
|
2020-10-09 11:14:42 +02:00
|
|
|
TCP_DEBUG_ERROR("pktbuf is full.");
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
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);
|
2020-10-09 11:14:42 +02:00
|
|
|
|
|
|
|
TCP_DEBUG_LEAVE;
|
2016-02-04 14:37:35 +01:00
|
|
|
return res;
|
|
|
|
}
|