mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #4744 from brummer-simon/devel-gnrc_tcp
gnrc_tcp: initial implementation
This commit is contained in:
commit
7f7329ea71
@ -331,6 +331,12 @@ ifneq (,$(filter gnrc_udp,$(USEMODULE)))
|
||||
USEMODULE += udp
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_tcp,$(USEMODULE)))
|
||||
USEMODULE += inet_csum
|
||||
USEMODULE += random
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_nettest,$(USEMODULE)))
|
||||
USEMODULE += gnrc_netapi
|
||||
USEMODULE += gnrc_netreg
|
||||
|
43
examples/gnrc_tcp_cli/Makefile
Normal file
43
examples/gnrc_tcp_cli/Makefile
Normal file
@ -0,0 +1,43 @@
|
||||
# name of your application
|
||||
APPLICATION = gnrc_tcp_cli
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
PORT ?= tap1
|
||||
|
||||
TCP_TARGET_ADDR ?= fe80::5c38:e9ff:fe76:6195
|
||||
TCP_TARGET_PORT ?= 80
|
||||
TCP_TEST_CYCLES ?= 10
|
||||
|
||||
# Mark Boards with insufficient memory
|
||||
BOARD_INSUFFICIENT_MEMORY := airfy-beacon arduino-duemilanove arduino-mega2560\
|
||||
arduino-uno calliope-mini chronos microbit sb-430\
|
||||
sb-430h nrf51dongle nrf6310 nucleo-f030 nucleo-f042\
|
||||
nucleo32-f042 nucleo-f070 nucleo-f072 nucleo32-f303\
|
||||
nucleo-f334 pca10000 pca10005 stm32f0discovery\
|
||||
telosb weio wsn430-v1_3b wsn430-v1_4\
|
||||
yunjia-nrf51822 z1 msb-430 msb-430h
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# Target Address, Target Port and number of Test Cycles
|
||||
CFLAGS += -DTARGET_ADDR=\"$(TCP_TARGET_ADDR)\"
|
||||
CFLAGS += -DTARGET_PORT=$(TCP_TARGET_PORT)
|
||||
CFLAGS += -DCYCLES=$(TCP_TEST_CYCLES)
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
#CFLAGS += -DDEVELHELP
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# Modules to include
|
||||
USEMODULE += gnrc_netdev_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
USEMODULE += gnrc_tcp
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
222
examples/gnrc_tcp_cli/main.c
Normal file
222
examples/gnrc_tcp_cli/main.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "net/af.h"
|
||||
#include "random.h"
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* Number of possible parallel connections */
|
||||
#ifndef CONNS
|
||||
#define CONNS 1
|
||||
#endif
|
||||
|
||||
/* Amount of data to transmit */
|
||||
#ifndef NBYTE
|
||||
#define NBYTE (2048)
|
||||
#endif
|
||||
|
||||
/* Test Pattern used by Client Application */
|
||||
#ifndef TEST_PATERN_CLI
|
||||
#define TEST_PATERN_CLI (0xF0)
|
||||
#endif
|
||||
|
||||
/* Test Pattern used by Server Application */
|
||||
#ifndef TEST_PATERN_SRV
|
||||
#define TEST_PATERN_SRV (0xA7)
|
||||
#endif
|
||||
|
||||
uint8_t bufs[CONNS][NBYTE];
|
||||
uint8_t stacks[CONNS][THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF];
|
||||
|
||||
void *cli_thread(void *arg);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("\nStarting Client Threads. TARGET_ADDR=%s, TARGET_PORT=%d, ", TARGET_ADDR, TARGET_PORT);
|
||||
printf("CONNS=%d, NBYTE=%d, CYCLES=%d\n\n", CONNS, NBYTE, CYCLES );
|
||||
|
||||
/* Start Connection Handling Threads */
|
||||
for (int i = 0; i < CONNS; i += 1) {
|
||||
thread_create((char *)stacks[i], sizeof(stacks[i]), THREAD_PRIORITY_MAIN, 0, cli_thread,
|
||||
(void *)i, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *cli_thread(void *arg)
|
||||
{
|
||||
/* Test Program variables */
|
||||
int tid = (int) arg;
|
||||
uint32_t cycles = 0;
|
||||
uint32_t cycles_ok = 0;
|
||||
uint32_t failed_payload_verifications = 0;
|
||||
|
||||
/* Transmission Control Block */
|
||||
gnrc_tcp_tcb_t tcb;
|
||||
|
||||
/* Target Peer Address Information */
|
||||
ipv6_addr_t target_addr;
|
||||
uint16_t target_port;
|
||||
|
||||
/* Initialize Target Information */
|
||||
ipv6_addr_from_str(&target_addr, TARGET_ADDR);
|
||||
target_port = TARGET_PORT;
|
||||
|
||||
printf("Client running: TID=%d\n", tid);
|
||||
while (cycles < CYCLES) {
|
||||
/* Initialize tcb struct */
|
||||
gnrc_tcp_tcb_init(&tcb);
|
||||
|
||||
/* Connect to Peer */
|
||||
int ret = gnrc_tcp_open_active(&tcb, AF_INET6, (uint8_t *) &target_addr, target_port, 0);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
DEBUG("TID=%d : gnrc_tcp_open_active() : 0 : ok\n", tid);
|
||||
break;
|
||||
|
||||
case -EISCONN:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -EISCONN\n", tid);
|
||||
return 0;
|
||||
|
||||
case -EINVAL:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -EINVAL\n", tid);
|
||||
return 0;
|
||||
|
||||
case -EAFNOSUPPORT:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -EAFNOSUPPORT\n", tid);
|
||||
return 0;
|
||||
|
||||
case -EADDRINUSE:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -EADDRINUSE\n", tid);
|
||||
return 0;
|
||||
|
||||
case -ECONNREFUSED:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -ECONNREFUSED : retry after 10sec\n",
|
||||
tid);
|
||||
xtimer_sleep(10);
|
||||
continue;
|
||||
|
||||
case -ENOMEM:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -ENOMEM\n", tid);
|
||||
return 0;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : -ETIMEDOUT : retry after 10sec\n",
|
||||
tid);
|
||||
xtimer_sleep(10);
|
||||
continue;
|
||||
|
||||
default:
|
||||
printf("TID=%d : gnrc_tcp_open_active() : %d\n", tid, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fill Buffer with a test pattern */
|
||||
for (size_t i=0; i < sizeof(bufs[tid]); ++i){
|
||||
bufs[tid][i] = TEST_PATERN_CLI;
|
||||
}
|
||||
|
||||
/* Send Data, stop if errors were found */
|
||||
for (size_t sent = 0; sent < sizeof(bufs[tid]) && ret >= 0; sent += ret) {
|
||||
ret = gnrc_tcp_send(&tcb, bufs[tid] + sent, sizeof(bufs[tid]) - sent, 0);
|
||||
switch (ret) {
|
||||
case -ENOTCONN:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ENOTCONN\n", tid);
|
||||
break;
|
||||
|
||||
case -ECONNABORTED:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ECONNABORTED\n", tid);
|
||||
break;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ETIMEDOUT\n", tid);
|
||||
break;
|
||||
|
||||
case -ECONNRESET:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ECONNRESET\n", tid);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ret >= 0) {
|
||||
DEBUG("TID=%d : gnrc_tcp_send() : %d Bytes sent\n", tid, ret);
|
||||
}
|
||||
else {
|
||||
printf("TID=%d : gnrc_tcp_send() : %d\n", tid, ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive Data, stop if errors were found */
|
||||
for (size_t rcvd = 0; rcvd < sizeof(bufs[tid]) && ret >= 0; rcvd += ret) {
|
||||
ret = gnrc_tcp_recv(&tcb, (void *)(bufs[tid] + rcvd), sizeof(bufs[tid]) - rcvd,
|
||||
GNRC_TCP_CONNECTION_TIMEOUT_DURATION);
|
||||
switch (ret) {
|
||||
case -ENOTCONN:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ENOTCONN\n", tid);
|
||||
break;
|
||||
|
||||
case -EAGAIN:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -EAGAIN : retry after 10sec\n", tid);
|
||||
ret = 0;
|
||||
xtimer_sleep(10);
|
||||
break;
|
||||
|
||||
case -ECONNABORTED:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ECONNABORTED\n", tid);
|
||||
break;
|
||||
|
||||
case -ECONNRESET:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ECONNRESET\n", tid);
|
||||
break;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ETIMEDOUT\n", tid);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ret >= 0) {
|
||||
DEBUG("TID=%d : gnrc_tcp_rcvd() : %d Bytes read.\n", tid, ret);
|
||||
}
|
||||
else {
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : %d\n", tid, ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there was no error: Check received pattern */
|
||||
for (size_t i=0; i < sizeof(bufs[tid]); ++i) {
|
||||
if (bufs[tid][i] != TEST_PATERN_SRV) {
|
||||
printf("TID=%d : Payload verfication failed\n", tid);
|
||||
failed_payload_verifications += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close Connection */
|
||||
gnrc_tcp_close(&tcb);
|
||||
|
||||
/* Gather Data */
|
||||
cycles += 1;
|
||||
if(ret >= 0) {
|
||||
cycles_ok += 1;
|
||||
}
|
||||
printf("TID=%d : %"PRIi32" test cycles completed. %"PRIi32" ok, %"PRIi32" faulty",
|
||||
tid, cycles, cycles_ok, cycles - cycles_ok);
|
||||
printf(", %"PRIi32" failed payload verifications\n", failed_payload_verifications);
|
||||
}
|
||||
printf("client thread terminating: TID=%d\n", tid);
|
||||
return 0;
|
||||
}
|
42
examples/gnrc_tcp_srv/Makefile
Normal file
42
examples/gnrc_tcp_srv/Makefile
Normal file
@ -0,0 +1,42 @@
|
||||
# name of your application
|
||||
APPLICATION = gnrc_tcp_srv
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
PORT ?= tap0
|
||||
|
||||
TCP_LOCAL_PORT ?= 80
|
||||
|
||||
# Mark Boards with insufficient memory
|
||||
BOARD_INSUFFICIENT_MEMORY := airfy-beacon arduino-duemilanove arduino-mega2560\
|
||||
arduino-uno calliope-mini chronos microbit sb-430\
|
||||
sb-430h nrf51dongle nrf6310 nucleo-f030 nucleo-f042\
|
||||
nucleo32-f042 nucleo-f070 nucleo-f072 nucleo32-f303\
|
||||
nucleo-f334 pca10000 pca10005 stm32f0discovery\
|
||||
telosb weio wsn430-v1_3b wsn430-v1_4\
|
||||
yunjia-nrf51822 z1 msb-430 msb-430h
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# Specify local Port to open
|
||||
CFLAGS += -DLOCAL_PORT=$(TCP_LOCAL_PORT)
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
#CFLAGS += -DDEVELHELP
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# Modules to include
|
||||
USEMODULE += gnrc_netdev_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
USEMODULE += gnrc_tcp
|
||||
|
||||
# include this for printing IP addresses
|
||||
USEMODULE += shell_commands
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
203
examples/gnrc_tcp_srv/main.c
Normal file
203
examples/gnrc_tcp_srv/main.c
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "thread.h"
|
||||
#include "net/af.h"
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* Number of possible parallel connections */
|
||||
#ifndef CONNS
|
||||
#define CONNS 1
|
||||
#endif
|
||||
|
||||
/* Amount of data to transmit */
|
||||
#ifndef NBYTE
|
||||
#define NBYTE (2048)
|
||||
#endif
|
||||
|
||||
/* Test Pattern used by Client Application */
|
||||
#ifndef TEST_PATERN_CLI
|
||||
#define TEST_PATERN_CLI (0xF0)
|
||||
#endif
|
||||
|
||||
/* Test Pattern used by Server Application */
|
||||
#ifndef TEST_PATERN_SRV
|
||||
#define TEST_PATERN_SRV (0xA7)
|
||||
#endif
|
||||
|
||||
uint8_t bufs[CONNS][NBYTE];
|
||||
uint8_t stacks[CONNS][THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF];
|
||||
|
||||
/* ifconfig shell command */
|
||||
extern int _netif_config(int argc, char **argv);
|
||||
|
||||
/* Server Thread */
|
||||
void *srv_thread(void *arg);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
/* Print all configured addresses of the server */
|
||||
printf("\nStarting server: LOCAL_PORT=%d, CONNS=%d, NBYTE=%d\n\n", LOCAL_PORT, CONNS, NBYTE);
|
||||
printf("Printing Servers Network Configuration:\n");
|
||||
_netif_config(0, NULL);
|
||||
|
||||
/* Start Threads to handle each connection */
|
||||
for (int i = 0; i < CONNS; i += 1) {
|
||||
thread_create((char *)stacks[i], sizeof(stacks[i]), THREAD_PRIORITY_MAIN, 0, srv_thread,
|
||||
(void *)i, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *srv_thread(void *arg)
|
||||
{
|
||||
int tid = (int)arg;
|
||||
uint32_t cycles = 0;
|
||||
uint32_t cycles_ok = 0;
|
||||
uint32_t failed_payload_verifications = 0;
|
||||
|
||||
/* Transmission control block */
|
||||
gnrc_tcp_tcb_t tcb;
|
||||
|
||||
/* Connection handling code */
|
||||
printf("Server running: TID=%d\n", tid);
|
||||
while (1) {
|
||||
/* Initialize tcb struct */
|
||||
gnrc_tcp_tcb_init(&tcb);
|
||||
|
||||
/* Connect to Peer */
|
||||
int ret = gnrc_tcp_open_passive(&tcb, AF_INET6, NULL, LOCAL_PORT);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
DEBUG("TID=%d : gnrc_tcp_open_passive() : 0 : ok\n", tid);
|
||||
break;
|
||||
|
||||
case -EISCONN:
|
||||
printf("TID=%d : gnrc_tcp_open_passive() : -EISCONN\n", tid);
|
||||
return 0;
|
||||
|
||||
case -EINVAL:
|
||||
printf("TID=%d : gnrc_tcp_open_passive() : -EINVAL\n", tid);
|
||||
return 0;
|
||||
|
||||
case -EAFNOSUPPORT:
|
||||
printf("TID=%d : gnrc_tcp_open_passive() : -EAFNOSUPPORT\n", tid);
|
||||
return 0;
|
||||
|
||||
case -ENOMEM:
|
||||
printf("TID=%d : gnrc_tcp_open_passive() : -ENOMEM\n", tid);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
printf("TID=%d : gnrc_tcp_open_passive() : %d\n", tid, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive Data, stop if errors were found */
|
||||
for (size_t rcvd = 0; rcvd < sizeof(bufs[tid]) && ret >= 0; rcvd += ret) {
|
||||
ret = gnrc_tcp_recv(&tcb, (void *)(bufs[tid] + rcvd), sizeof(bufs[tid]) - rcvd,
|
||||
GNRC_TCP_CONNECTION_TIMEOUT_DURATION);
|
||||
switch (ret) {
|
||||
case -ENOTCONN:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ENOTCONN\n", tid);
|
||||
break;
|
||||
|
||||
case -EAGAIN:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -EAGAIN : retry after 10sec\n", tid);
|
||||
ret = 0;
|
||||
xtimer_sleep(10);
|
||||
break;
|
||||
|
||||
case -ECONNABORTED:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ECONNABORTED\n", tid);
|
||||
break;
|
||||
|
||||
case -ECONNRESET:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ECONNRESET\n", tid);
|
||||
break;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : -ETIMEDOUT\n", tid);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ret >= 0) {
|
||||
DEBUG("TID=%d : gnrc_tcp_rcvd() : %d Bytes read\n", tid, ret);
|
||||
}
|
||||
else {
|
||||
printf("TID=%d : gnrc_tcp_rcvd() : %d\n", tid, ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check received pattern */
|
||||
for (size_t i=0; i < sizeof(bufs[tid]); ++i) {
|
||||
if (bufs[tid][i] != TEST_PATERN_CLI) {
|
||||
printf("TID=%d : Payload verfication failed\n", tid);
|
||||
failed_payload_verifications += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill Buffer with a test pattern */
|
||||
for (size_t i=0; i < sizeof(bufs[tid]); ++i) {
|
||||
bufs[tid][i] = TEST_PATERN_SRV;
|
||||
}
|
||||
|
||||
/* Send Data, stop if errors were found */
|
||||
for (size_t sent = 0; sent < sizeof(bufs[tid]) && ret >= 0; sent += ret) {
|
||||
ret = gnrc_tcp_send(&tcb, bufs[tid] + sent, sizeof(bufs[tid]) - sent, 0);
|
||||
switch (ret) {
|
||||
case -ENOTCONN:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ENOTCONN\n", tid);
|
||||
break;
|
||||
|
||||
case -ECONNABORTED:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ECONNABORTED\n", tid);
|
||||
break;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ETIMEDOUT\n", tid);
|
||||
break;
|
||||
|
||||
case -ECONNRESET:
|
||||
printf("TID=%d : gnrc_tcp_send() : -ECONNRESET\n", tid);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ret >= 0) {
|
||||
DEBUG("TID=%d : gnrc_tcp_send() : %d Bytes sent.\n", tid, ret);
|
||||
}
|
||||
else {
|
||||
printf("TID=%d : gnrc_tcp_send() : %d\n", tid, ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Close Connection */
|
||||
gnrc_tcp_close(&tcb);
|
||||
|
||||
/* Gather Data */
|
||||
cycles += 1;
|
||||
if(ret >= 0) {
|
||||
cycles_ok += 1;
|
||||
}
|
||||
printf("TID=%d : %"PRIi32" test cycles completed. %"PRIi32" ok, %"PRIi32" faulty",
|
||||
tid, cycles, cycles_ok, cycles - cycles_ok);
|
||||
printf(", %"PRIi32" failed payload verifications\n", failed_payload_verifications);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -76,6 +76,10 @@
|
||||
#include "net/gnrc/udp.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_GNRC_TCP
|
||||
#include "net/gnrc/tcp.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_LWIP
|
||||
#include "lwip.h"
|
||||
#endif
|
||||
@ -152,6 +156,10 @@ void auto_init(void)
|
||||
DEBUG("Auto init UDP module.\n");
|
||||
gnrc_udp_init();
|
||||
#endif
|
||||
#ifdef MODULE_GNRC_TCP
|
||||
DEBUG("Auto init TCP module\n");
|
||||
gnrc_tcp_init();
|
||||
#endif
|
||||
#ifdef MODULE_DHT
|
||||
DEBUG("Auto init DHT devices.\n");
|
||||
extern void dht_auto_init(void);
|
||||
|
231
sys/include/net/gnrc/tcp.h
Normal file
231
sys/include/net/gnrc/tcp.h
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief TCP interface definition
|
||||
*
|
||||
* @author Simon Brummer <simon.brummer@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_H_
|
||||
#define GNRC_TCP_H_
|
||||
|
||||
#include "net/gnrc/netapi.h"
|
||||
#include "net/gnrc/nettype.h"
|
||||
#include "net/gnrc/tcp/hdr.h"
|
||||
#include "net/gnrc/tcp/tcb.h"
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Port unspecified.
|
||||
*
|
||||
* @note PORT 0 is reserved, according to rfc 1700(https://www.ietf.org/rfc/rfc1700.txt)
|
||||
*/
|
||||
#define GNRC_TCP_PORT_UNSPEC 0
|
||||
|
||||
/**
|
||||
* @brief Head of conn linked list.
|
||||
*/
|
||||
extern gnrc_tcp_tcb_t *_list_gnrc_tcp_tcb_head;
|
||||
|
||||
/**
|
||||
* @brief Mutex to protect linked list.
|
||||
*/
|
||||
extern mutex_t _list_gnrc_tcp_tcb_lock;
|
||||
|
||||
/**
|
||||
* @brief Initialize and start TCP
|
||||
*
|
||||
* @return PID of TCP thread on success
|
||||
* @return -1 if thread is already running.
|
||||
* @return -EINVAL, if priority is greater than or equal SCHED_PRIO_LEVELS
|
||||
* @return -EOVERFLOW, if there are too many threads running.
|
||||
*/
|
||||
int gnrc_tcp_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize Transmission Control Block (tcb)
|
||||
* @pre tcb must not be NULL.
|
||||
*
|
||||
* @param[in,out] tcb Transmission that should be initialized.
|
||||
*/
|
||||
void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t* tcb);
|
||||
|
||||
/**
|
||||
* @brief Opens a connection actively.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre tcb must not be NULL
|
||||
* @pre target_addr must not be NULL.
|
||||
* @pre target_port must not be 0.
|
||||
*
|
||||
* @note Blocks until a connection has been established or an error occured.
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] address_family Address Family of @p target_addr.
|
||||
* @param[in] target_addr Pointer to target address.
|
||||
* @param[in] target_port Targets port number.
|
||||
* @param[in] local_port If zero or GNRC_TCP_PORT_UNSPEC, the connections
|
||||
* source port is randomly choosen. If local_port is non-zero
|
||||
* the local_port is used as source port.
|
||||
*
|
||||
* @return Zero on success.
|
||||
* @return -EAFNOSUPPORT if @p address_family is not supported.
|
||||
* @return -EINVAL if @p address_family is not the same the address_family use by the tcb.
|
||||
* @return -EISCONN if transmission control block is already in use.
|
||||
* @return -ENOMEM if the receive buffer for the tcb could not be allocated.
|
||||
* Increase "GNRC_TCP_RCV_BUFFERS".
|
||||
* @return -EADDRINUSE if @p local_port is already used by another connection.
|
||||
* @return -ETIMEDOUT if the connection could not be opened.
|
||||
* @return -ECONNREFUSED if the connection was resetted by the peer.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Opens a connection passively, by waiting for an incomming request.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre tcb must not be NULL.
|
||||
* @pre if local_addr is not NULL, local_addr must be assigned to a network interface.
|
||||
* @pre if local_port is not zero.
|
||||
*
|
||||
* @note Blocks until a connection has been established (incomming connection request
|
||||
* to @p local_port) or an error occured.
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] address_family Address Family of @p local_addr.
|
||||
* If local_addr == NULL, address_family is ignored.
|
||||
* @param[in] local_addr If not NULL the connection is bound to the address in @p local_addr.
|
||||
* If NULL a connection request to every local ip address is valid.
|
||||
* @param[in] local_port Portnumber that should used for incomming connection requests.
|
||||
*
|
||||
* @return Zero on success
|
||||
* @return -EAFNOSUPPORT if local_addr != NULL and @p address_family is not supported.
|
||||
* @return -EINVAL if @p address_family is not the same the address_family use by the tcb.
|
||||
* @return -EISCONN if transmission control block is already in use.
|
||||
* @return -ENOMEM if the receive buffer for the tcb could not be allocated.
|
||||
* Increase "GNRC_TCP_RCV_BUFFERS".
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Transmit Data to Peer.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre tcb must not be NULL.
|
||||
* @pre data must not be NULL.
|
||||
*
|
||||
* @note Blocks until up to @p len bytes were transmitted or an error occured.
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] data Pointer to the data that should be transmitted.
|
||||
* @param[in] len Number of bytes that should be transmitted.
|
||||
* @param[in] user_timeout_duration_us If not zero and there were not data transmitted
|
||||
* successfully, the function call returns after
|
||||
* user_timeout_duration_us. If zero not timeout will be
|
||||
* triggered.
|
||||
*
|
||||
* @return On success, the number of successfully transmitted bytes.
|
||||
* @return -ENOTCONN if connection is not established.
|
||||
* @return -ECONNRESET if connection was resetted by the peer.
|
||||
* @return -ECONNABORTED if the connection was aborted.
|
||||
* @return -ETIMEDOUT if @p user_timeout_duration_us expired.
|
||||
*/
|
||||
ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,
|
||||
const uint32_t user_timeout_duration_us);
|
||||
|
||||
/**
|
||||
* @brief Receive Data from the Peer.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre tcb must not be NULL.
|
||||
* @pre data must not be NULL.
|
||||
*
|
||||
* @note Function blocks if user_timeout_duration_us is not zero.
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[out] data Pointer to the buffer where the received data
|
||||
* should be copied into.
|
||||
* @param[in] max_len Maximum amount to bytes that should be reeived.
|
||||
* Should not exceed size of @p data.
|
||||
* @param[in] user_timeout_duration_us Timeout for receive in microseconds. If zero and no data
|
||||
* is available, the function returns immediately. If not
|
||||
* zero the function block until data is available or
|
||||
* user_timeout_duration_us microseconds have passed.
|
||||
*
|
||||
* @return On success, the number of bytes read into @p data.
|
||||
* @return -ENOTCONN if connection is not established.
|
||||
* @return -EAGAIN if user_timeout_duration_us is zero and no data is available.
|
||||
* @return -ECONNRESET if connection was resetted by the peer.
|
||||
* @return -ECONNABORTED if the connection was aborted.
|
||||
* @return -ETIMEDOUT if @p user_timeout_duration_us expired.
|
||||
*/
|
||||
ssize_t gnrc_tcp_recv(gnrc_tcp_tcb_t *tcb, void *data, const size_t max_len,
|
||||
const uint32_t user_timeout_duration_us);
|
||||
|
||||
/**
|
||||
* @brief Close a tcp connection.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre tcb must not be NULL.
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
*
|
||||
* @return Zero on success.
|
||||
*/
|
||||
int gnrc_tcp_close(gnrc_tcp_tcb_t *tcb);
|
||||
|
||||
/**
|
||||
* @brief Set checksum calculated from tcp and network-layer header in tcp-header.
|
||||
*
|
||||
* @param[in] hdr ng_pktsnip that contains tcp header.
|
||||
* @param[in] pseudo_hdr ng_pktsnip that contains networklayer header.
|
||||
*
|
||||
* @return zero on succeed.
|
||||
* @return -EFAULT if hdr or pseudo_hdr were NULL
|
||||
* @return -EBADMSG if hdr is not of type GNRC_NETTYPE_TCP
|
||||
* @return -ENOENT if pseudo_hdr protocol is unsupported.
|
||||
*/
|
||||
int gnrc_tcp_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_hdr);
|
||||
|
||||
/**
|
||||
* @brief Adds a tcp header to a given payload. Be carefull, leads to huge headers.
|
||||
* Allocates all option bytes
|
||||
*
|
||||
* @param[in] payload payload that follows the tcp header
|
||||
* @param[in] src Source port in host byte order
|
||||
* @param[in] dst Destination port in host byte order
|
||||
*
|
||||
* @return NULL, if paket buffer is full
|
||||
* @return Not NULL on success
|
||||
*/
|
||||
gnrc_pktsnip_t *gnrc_tcp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src, uint16_t dst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_H_ */
|
||||
/** @} */
|
182
sys/include/net/gnrc/tcp/config.h
Normal file
182
sys/include/net/gnrc/tcp/config.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief TCP configuration, includes buffersizes, timeout durations
|
||||
*
|
||||
* @author Simon Brummer <simon.brummer@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_CONFIG_H_
|
||||
#define GNRC_TCP_CONFIG_H_
|
||||
|
||||
#include "timex.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Status Flags for TCP
|
||||
* @{
|
||||
*/
|
||||
#define GNRC_TCP_STATUS_PASSIVE (1 << 0)
|
||||
#define GNRC_TCP_STATUS_ACCEPTED (1 << 1)
|
||||
#define GNRC_TCP_STATUS_ALLOW_ANY_ADDR (1 << 2)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Timeout Duration for user calls. Default 2 minutes
|
||||
*/
|
||||
#ifndef GNRC_TCP_CONNECTION_TIMEOUT_DURATION
|
||||
#define GNRC_TCP_CONNECTION_TIMEOUT_DURATION (120 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum Segment Lifetime. Default 30 secounds
|
||||
*/
|
||||
#ifndef GNRC_TCP_MSL
|
||||
#define GNRC_TCP_MSL (30 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Message queue size for the TCP handling thread
|
||||
*/
|
||||
#ifndef GNRC_TCP_MSG_QUEUE_SIZE
|
||||
#define GNRC_TCP_MSG_QUEUE_SIZE (8U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Priority of the tcp handling thread, must be lower than the applications prio.
|
||||
*/
|
||||
#ifndef GNRC_TCP_PRIO
|
||||
#define GNRC_TCP_PRIO (THREAD_PRIORITY_MAIN - 2U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default stack size for the TCP handling thread
|
||||
*/
|
||||
#ifndef GNRC_TCP_STACK_SIZE
|
||||
#define GNRC_TCP_STACK_SIZE (THREAD_STACKSIZE_DEFAULT)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum Segement Size
|
||||
*/
|
||||
#ifndef GNRC_TCP_MSS
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
#define GNRC_TCP_MSS (1220U) /**< If IPv6 is used. Get MSS = 1280 - IPv6-Hdr - TCP-Hdr = 1220 */
|
||||
#else
|
||||
#define GNRC_TCP_MSS (576U) /**< Default MSS */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MSS Multiplicator = Number of MSS sized packets stored in receive buffer
|
||||
*/
|
||||
#ifndef GNRC_TCP_MSS_MULTIPLICATOR
|
||||
#define GNRC_TCP_MSS_MULTIPLICATOR (1U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default Window Size
|
||||
*/
|
||||
#ifndef GNRC_TCP_DEFAULT_WINDOW
|
||||
#define GNRC_TCP_DEFAULT_WINDOW (GNRC_TCP_MSS * GNRC_TCP_MSS_MULTIPLICATOR)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of preallocated receive buffers
|
||||
*/
|
||||
#ifndef GNRC_TCP_RCV_BUFFERS
|
||||
#define GNRC_TCP_RCV_BUFFERS 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default Receive Buffer Size
|
||||
*/
|
||||
#ifndef GNRC_TCP_RCV_BUF_SIZE
|
||||
#define GNRC_TCP_RCV_BUF_SIZE (GNRC_TCP_DEFAULT_WINDOW)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Lower Bound for RTO = 1 sec (see RFC 6298)
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_LOWER_BOUND
|
||||
#define GNRC_TCP_RTO_LOWER_BOUND (1 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Upper Bound for RTO = 60 sec (see RFC 6298)
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_UPPER_BOUND
|
||||
#define GNRC_TCP_RTO_UPPER_BOUND (60 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Assumes clock granularity for TCP of 10 ms (see RFC 6298)
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_GRANULARITY
|
||||
#define GNRC_TCP_RTO_GRANULARITY (10 * MS_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Alpha value for RTO calculation, default is 1/8
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_A_DIV
|
||||
#define GNRC_TCP_RTO_A_DIV (8U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Beta value for RTO calculation, default is 1/4
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_B_DIV
|
||||
#define GNRC_TCP_RTO_B_DIV (4U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief K value for RTO calculation, default is 4
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_K
|
||||
#define GNRC_TCP_RTO_K (4U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Macro to mark is the time measurement is uninitialized
|
||||
*/
|
||||
#ifndef GNRC_TCP_RTO_UNINITIALIZED
|
||||
#define GNRC_TCP_RTO_UNINITIALIZED (-1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Lower Bound for the duration between probes
|
||||
*/
|
||||
#ifndef GNRC_TCP_PROBE_LOWER_BOUND
|
||||
#define GNRC_TCP_PROBE_LOWER_BOUND (1 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Upper Bound for the duration between probes
|
||||
*/
|
||||
#ifndef GNRC_TCP_PROBE_UPPER_BOUND
|
||||
#define GNRC_TCP_PROBE_UPPER_BOUND (60 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_CONFIG_H_ */
|
||||
/** @} */
|
68
sys/include/net/gnrc/tcp/fsm.h
Normal file
68
sys/include/net/gnrc/tcp/fsm.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definies states and events for TCP finite state maschine
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_FSM_H_
|
||||
#define GNRC_TCP_FSM_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The TCP FSM States.
|
||||
*/
|
||||
typedef enum {
|
||||
GNRC_TCP_FSM_STATE_CLOSED,
|
||||
GNRC_TCP_FSM_STATE_LISTEN,
|
||||
GNRC_TCP_FSM_STATE_SYN_SENT,
|
||||
GNRC_TCP_FSM_STATE_SYN_RCVD,
|
||||
GNRC_TCP_FSM_STATE_ESTABLISHED,
|
||||
GNRC_TCP_FSM_STATE_CLOSE_WAIT,
|
||||
GNRC_TCP_FSM_STATE_LAST_ACK,
|
||||
GNRC_TCP_FSM_STATE_FIN_WAIT_1,
|
||||
GNRC_TCP_FSM_STATE_FIN_WAIT_2,
|
||||
GNRC_TCP_FSM_STATE_CLOSING,
|
||||
GNRC_TCP_FSM_STATE_TIME_WAIT
|
||||
} gnrc_tcp_fsm_state_t;
|
||||
|
||||
/**
|
||||
* @brief Events that trigger translations in TCP FSM.
|
||||
*/
|
||||
typedef enum {
|
||||
GNRC_TCP_FSM_EVENT_CALL_OPEN, /* User function call: open */
|
||||
GNRC_TCP_FSM_EVENT_CALL_SEND, /* User function call: send */
|
||||
GNRC_TCP_FSM_EVENT_CALL_RECV, /* User function call: recv */
|
||||
GNRC_TCP_FSM_EVENT_CALL_CLOSE, /* User function call: close */
|
||||
GNRC_TCP_FSM_EVENT_CALL_ABORT, /* User function call: abort */
|
||||
GNRC_TCP_FSM_EVENT_RCVD_PKT, /* Paket received from peer */
|
||||
GNRC_TCP_FSM_EVENT_TIMEOUT_TIMEWAIT, /* Timeout: Timewait */
|
||||
GNRC_TCP_FSM_EVENT_TIMEOUT_RETRANSMIT, /* Timeout: Retransmit */
|
||||
GNRC_TCP_FSM_EVENT_TIMEOUT_CONNECTION, /* Timeout: Connection */
|
||||
GNRC_TCP_FSM_EVENT_SEND_PROBE, /* Send a Zero Window Probe */
|
||||
GNRC_TCP_FSM_EVENT_CLEAR_RETRANSMIT /* Clear Retransmission Mechanism */
|
||||
} gnrc_tcp_fsm_event_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_FSM_H_ */
|
||||
/** @} */
|
56
sys/include/net/gnrc/tcp/hdr.h
Normal file
56
sys/include/net/gnrc/tcp/hdr.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief TCP Header
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_HDR_H_
|
||||
#define GNRC_TCP_HDR_H_
|
||||
|
||||
#include "byteorder.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TCP Options could contain up to 10 32-Bit values of Information.
|
||||
*/
|
||||
#define TCP_MAX_HDR_OPTIONS 10
|
||||
|
||||
/**
|
||||
* @brief TCP header definition
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t src_port; /**< source port, in network byte order */
|
||||
network_uint16_t dst_port; /**< destination port, in network byte order */
|
||||
network_uint32_t seq_num; /**< sequence number, in network byte order */
|
||||
network_uint32_t ack_num; /**< Acknowledgement number, in network byte order */
|
||||
network_uint16_t off_ctl; /**< Data Offset and control Bits in network byte order */
|
||||
network_uint16_t window; /**< window, in network byte order */
|
||||
network_uint16_t checksum; /**< checksum, in network byte order */
|
||||
network_uint16_t urgent_ptr; /**< urgent pointer, in network byte order */
|
||||
network_uint32_t options[TCP_MAX_HDR_OPTIONS]; /**< Option Fields (Optional) */
|
||||
} tcp_hdr_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_TCB_H_ */
|
||||
/** @} */
|
86
sys/include/net/gnrc/tcp/tcb.h
Normal file
86
sys/include/net/gnrc/tcp/tcb.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Transmission Control Block definition
|
||||
*
|
||||
* @author Simon Brummer <simon.brummer@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_TCB_H_
|
||||
#define GNRC_TCP_TCB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ringbuffer.h>
|
||||
#include <xtimer.h>
|
||||
#include <mutex.h>
|
||||
#include <msg.h>
|
||||
#include "net/gnrc.h"
|
||||
#include "fsm.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief transmission control block of gnrc_tcp
|
||||
*/
|
||||
typedef struct _transmission_control_block {
|
||||
uint8_t address_family; /**< Address Family of local_addr and peer_addr */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
uint8_t local_addr[sizeof(ipv6_addr_t)]; /**< local IP address */
|
||||
uint8_t peer_addr[sizeof(ipv6_addr_t)]; /**< peer IP address */
|
||||
#endif
|
||||
uint16_t local_port; /**< local connections port number */
|
||||
uint16_t peer_port; /**< port connections port number */
|
||||
gnrc_tcp_fsm_state_t state; /**< Connections state */
|
||||
uint8_t status; /**< A connections status flags */
|
||||
uint32_t snd_una; /**< Send Unacknowledged */
|
||||
uint32_t snd_nxt; /**< Send Next */
|
||||
uint16_t snd_wnd; /**< Send Window */
|
||||
uint32_t snd_wl1; /**< SeqNo. Last Windowupdate */
|
||||
uint32_t snd_wl2; /**< AckNo. Last Windowupdate */
|
||||
uint32_t rcv_nxt; /**< Receive Next */
|
||||
uint16_t rcv_wnd; /**< Receive Window */
|
||||
uint32_t iss; /**< Initial Sequence Number */
|
||||
uint32_t irs; /**< Initial Received Sequence Number */
|
||||
uint16_t mss; /**< The peers MSS */
|
||||
uint32_t rtt_start; /**< Timer value for rtt estimation */
|
||||
int32_t rtt_var; /**< Round Trip Time variance */
|
||||
int32_t srtt; /**< Smoothed Round Trip Time */
|
||||
int32_t rto; /**< Retransmission Timeout Duration */
|
||||
uint8_t retries; /**< Number of Retransmissions */
|
||||
xtimer_t tim_tout; /**< Timer struct for timeouts */
|
||||
msg_t msg_tout; /**< Message, sent on timeouts */
|
||||
gnrc_pktsnip_t *pkt_retransmit; /**< Pointer to Packet in "retransmit queue" */
|
||||
kernel_pid_t owner; /**< PID of this connection handling thread */
|
||||
msg_t msg_queue[GNRC_TCP_MSG_QUEUE_SIZE]; /**< Message queue used for asynchronious operation */
|
||||
uint8_t *rcv_buf_raw; /**< Pointer to the receive buffer */
|
||||
ringbuffer_t rcv_buf; /**< Receive Buffer data structure */
|
||||
mutex_t fsm_lock; /**< Mutex for FSM access synchronization */
|
||||
mutex_t function_lock; /**< Mutex for Function call synchronization */
|
||||
struct _transmission_control_block *next; /**< Pointer next TCP connection */
|
||||
} gnrc_tcp_tcb_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* GNRC_TCP_TCB_H_ */
|
||||
/** @} */
|
@ -130,6 +130,9 @@ endif
|
||||
ifneq (,$(filter gnrc_udp,$(USEMODULE)))
|
||||
DIRS += transport_layer/udp
|
||||
endif
|
||||
ifneq (,$(filter gnrc_tcp,$(USEMODULE)))
|
||||
DIRS += transport_layer/tcp
|
||||
endif
|
||||
ifneq (,$(filter gnrc_zep,$(USEMODULE)))
|
||||
DIRS += application_layer/zep
|
||||
endif
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "net/gnrc/icmpv6.h"
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#include "net/gnrc/udp.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
|
||||
#define _INVALID_TYPE(type) (((type) < GNRC_NETTYPE_UNDEF) || ((type) >= GNRC_NETTYPE_NUMOF))
|
||||
|
||||
|
3
sys/net/gnrc/transport_layer/tcp/Makefile
Normal file
3
sys/net/gnrc/transport_layer/tcp/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = gnrc_tcp
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
616
sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c
Normal file
616
sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c
Normal file
@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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
|
||||
* @brief GNRC's TCP implementation
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <utlist.h>
|
||||
#include "msg.h"
|
||||
#include "assert.h"
|
||||
#include "thread.h"
|
||||
#include "byteorder.h"
|
||||
#include "random.h"
|
||||
#include "xtimer.h"
|
||||
#include "mutex.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "net/af.h"
|
||||
#include "net/gnrc/nettype.h"
|
||||
#include "net/gnrc/netapi.h"
|
||||
#include "net/gnrc/netreg.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
|
||||
#include "internal/fsm.h"
|
||||
#include "internal/pkt.h"
|
||||
#include "internal/option.h"
|
||||
#include "internal/helper.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"
|
||||
|
||||
/**
|
||||
* @brief Allocate memory for TCP thread's stack
|
||||
*/
|
||||
#if ENABLE_DEBUG
|
||||
static char _stack[GNRC_TCP_STACK_SIZE + THREAD_EXTRA_STACKSIZE_PRINTF];
|
||||
#else
|
||||
static char _stack[GNRC_TCP_STACK_SIZE];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TCPs eventloop pid, declared externally
|
||||
*/
|
||||
kernel_pid_t _gnrc_tcp_pid = KERNEL_PID_UNDEF;
|
||||
|
||||
/**
|
||||
* @brief Head of liked list of active connections
|
||||
*/
|
||||
gnrc_tcp_tcb_t *_list_gnrc_tcp_tcb_head;
|
||||
|
||||
/**
|
||||
* @brief Mutex to protect the connection list
|
||||
*/
|
||||
mutex_t _list_gnrc_tcp_tcb_lock;
|
||||
|
||||
/**
|
||||
* @brief Establishes a new TCP connection
|
||||
*
|
||||
* @param[in/out] tcb This connections Transmission control block.
|
||||
* @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.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -EISCONN if transmission control block is already in use.
|
||||
* @return -ENOMEM if the receive buffer for the tcb could not be allocated.
|
||||
* Increase "GNRC_TCP_RCV_BUFFERS".
|
||||
* @return -EADDRINUSE if @p local_port is already used by another connection. Only active mode.
|
||||
* @return -ETIMEDOUT if the connection could not be opened. Only active mode.
|
||||
* @return -ECONNREFUSED if the connection was resetted by the peer.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
msg_t msg; /* Message for incomming Messages */
|
||||
msg_t connection_timeout_msg; /* Connection Timeout Message */
|
||||
xtimer_t connection_timeout_timer; /* Connection Timeout Timer */
|
||||
int8_t ret = 0; /* Return Value */
|
||||
|
||||
/* Lock the tcb for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Connection is already connected: Return -EISCONN */
|
||||
if (tcb->state != GNRC_TCP_FSM_STATE_CLOSED) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return -EISCONN;
|
||||
}
|
||||
|
||||
/* Setup connection (common parts) */
|
||||
msg_init_queue(tcb->msg_queue, GNRC_TCP_MSG_QUEUE_SIZE);
|
||||
tcb->owner = thread_getpid();
|
||||
|
||||
/* Setup passive connection */
|
||||
if (passive){
|
||||
/* Set Status Flags */
|
||||
tcb->status |= GNRC_TCP_STATUS_PASSIVE;
|
||||
if (local_addr == NULL) {
|
||||
tcb->status |= GNRC_TCP_STATUS_ALLOW_ANY_ADDR;
|
||||
}
|
||||
/* If local address is specified: Copy it into tcb: only connections to this addr are ok */
|
||||
else {
|
||||
switch (tcb->address_family) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case AF_INET6:
|
||||
memcpy(tcb->local_addr, local_addr, sizeof(ipv6_addr_t));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Assign Port to listen on, to tcb */
|
||||
tcb->local_port = local_port;
|
||||
}
|
||||
/* Setup active connection */
|
||||
else{
|
||||
/* Copy Target Address and Port into tcb structure */
|
||||
if (target_addr != NULL) {
|
||||
switch (tcb->address_family) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case AF_INET6:
|
||||
memcpy(tcb->peer_addr, target_addr, sizeof(ipv6_addr_t));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Copy Port Information, verfication happens in fsm */
|
||||
tcb->local_port = local_port;
|
||||
tcb->peer_port = target_port;
|
||||
|
||||
/* Setup Timeout: If connection could not be established before */
|
||||
/* the timer expired, the connection attempt failed */
|
||||
connection_timeout_msg.type = MSG_TYPE_CONNECTION_TIMEOUT;
|
||||
xtimer_set_msg(&connection_timeout_timer, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
||||
&connection_timeout_msg, tcb->owner);
|
||||
}
|
||||
|
||||
/* Call FSM with Event: CALL_OPEN */
|
||||
ret = _fsm(tcb, GNRC_TCP_FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
||||
if (ret == -ENOMEM) {
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_connect() : Out of receive buffers.\n");
|
||||
} else if(ret == -EADDRINUSE) {
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_connect() : local_port is already in use.\n");
|
||||
}
|
||||
|
||||
/* Wait until a connection was established or closed */
|
||||
while (ret >= 0 && tcb->state != GNRC_TCP_FSM_STATE_CLOSED
|
||||
&& tcb->state != GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
&& tcb->state != GNRC_TCP_FSM_STATE_CLOSE_WAIT
|
||||
) {
|
||||
msg_receive(&msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : CONNECTION_TIMEOUT\n");
|
||||
_fsm(tcb, GNRC_TCP_FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
||||
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 */
|
||||
xtimer_remove(&connection_timeout_timer);
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_CLOSED && ret == 0) {
|
||||
ret = -ECONNREFUSED;
|
||||
}
|
||||
tcb->owner = KERNEL_PID_UNDEF;
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* External GNRC_TCP API */
|
||||
int gnrc_tcp_init(void)
|
||||
{
|
||||
/* Guard: Check if thread is already running */
|
||||
if (_gnrc_tcp_pid != KERNEL_PID_UNDEF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize Mutex for linked-list synchronization */
|
||||
mutex_init(&(_list_gnrc_tcp_tcb_lock));
|
||||
|
||||
/* Initialize Linked-List for connection storage */
|
||||
_list_gnrc_tcp_tcb_head = NULL;
|
||||
|
||||
/* Initialize receive buffers */
|
||||
_rcvbuf_init();
|
||||
|
||||
/* Start TCP processing loop */
|
||||
return thread_create(_stack, sizeof(_stack), GNRC_TCP_PRIO, 0, _event_loop, NULL, "gnrc_tcp");
|
||||
}
|
||||
|
||||
void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
tcb->address_family = AF_INET6;
|
||||
ipv6_addr_set_unspecified((ipv6_addr_t *) tcb->local_addr);
|
||||
ipv6_addr_set_unspecified((ipv6_addr_t *) tcb->peer_addr);
|
||||
#else
|
||||
tcb->address_family = AF_UNSPEC;
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_tcb_init() : Address unspec, add netlayer module to makefile\n");
|
||||
#endif
|
||||
tcb->local_port = GNRC_TCP_PORT_UNSPEC;
|
||||
tcb->peer_port = GNRC_TCP_PORT_UNSPEC;
|
||||
tcb->state = GNRC_TCP_FSM_STATE_CLOSED;
|
||||
tcb->status = 0;
|
||||
tcb->snd_una = 0;
|
||||
tcb->snd_nxt = 0;
|
||||
tcb->snd_wnd = 0;
|
||||
tcb->snd_wl1 = 0;
|
||||
tcb->snd_wl2 = 0;
|
||||
tcb->rcv_nxt = 0;
|
||||
tcb->rcv_wnd = 0;
|
||||
tcb->iss = 0;
|
||||
tcb->irs = 0;
|
||||
tcb->mss = 0;
|
||||
tcb->rtt_start = 0;
|
||||
tcb->rtt_var = GNRC_TCP_RTO_UNINITIALIZED;
|
||||
tcb->srtt = GNRC_TCP_RTO_UNINITIALIZED;
|
||||
tcb->rto = GNRC_TCP_RTO_UNINITIALIZED;
|
||||
tcb->retries = 0;
|
||||
tcb->pkt_retransmit = NULL;
|
||||
tcb->owner = KERNEL_PID_UNDEF;
|
||||
tcb->rcv_buf_raw = NULL;
|
||||
mutex_init(&(tcb->fsm_lock));
|
||||
mutex_init(&(tcb->function_lock));
|
||||
tcb->next = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
assert(target_port != GNRC_TCP_PORT_UNSPEC);
|
||||
|
||||
/* Check AF-Family Support from target_addr */
|
||||
switch (address_family) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case AF_INET6:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
/* Check if AF-Family for Target Address matches internally used AF-Family */
|
||||
if (tcb->address_family != address_family) {
|
||||
return -EINVAL;
|
||||
}
|
||||
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);
|
||||
assert(local_port != GNRC_TCP_PORT_UNSPEC);
|
||||
|
||||
/* Check AF-Family support if local address was supplied */
|
||||
if (local_addr != NULL) {
|
||||
switch (address_family) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case AF_INET6:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
/* Check if AF-Family matches internally used AF-Family */
|
||||
if (tcb->address_family != address_family) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
msg_t msg; /* Message for incomming Messages */
|
||||
msg_t connection_timeout_msg; /* Connection Timeout Message */
|
||||
msg_t probe_timeout_msg; /* Probe Timeout Message */
|
||||
msg_t user_timeout_msg; /* User Specified Timeout Message */
|
||||
xtimer_t connection_timeout_timer; /* Connection Timeout Timer */
|
||||
xtimer_t probe_timeout_timer; /* Probe Timeout Timer */
|
||||
xtimer_t user_timeout_timer; /* User Specified Timeout Timer */
|
||||
uint32_t probe_timeout_duration_us = 0; /* Probe Timeout Duration in microseconds */
|
||||
ssize_t ret = 0; /* Return Value */
|
||||
bool probing = false; /* True if this connection is probing */
|
||||
|
||||
/* Lock the tcb for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Check if connection is in a valid state */
|
||||
if (tcb->state != GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
&& tcb->state != GNRC_TCP_FSM_STATE_CLOSE_WAIT
|
||||
) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
/* Re-init message queue, take ownership. FSM can send Messages to this thread now */
|
||||
msg_init_queue(tcb->msg_queue, GNRC_TCP_MSG_QUEUE_SIZE);
|
||||
tcb->owner = thread_getpid();
|
||||
|
||||
/* Setup Connection Timeout */
|
||||
connection_timeout_msg.type = MSG_TYPE_CONNECTION_TIMEOUT;
|
||||
xtimer_set_msg(&connection_timeout_timer, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
||||
&connection_timeout_msg, tcb->owner);
|
||||
|
||||
/* Setup User specified timeout if timeout_us is greater than zero */
|
||||
if (timeout_duration_us > 0) {
|
||||
user_timeout_msg.type = MSG_TYPE_USER_SPEC_TIMEOUT;
|
||||
xtimer_set_msg(&user_timeout_timer, timeout_duration_us, &user_timeout_msg, tcb->owner);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_CLOSED) {
|
||||
ret = -ECONNRESET;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the send window is closed: Setup Probing */
|
||||
if (tcb->snd_wnd <= 0) {
|
||||
/* If this is the first probe: Setup probing duration */
|
||||
if (!probing) {
|
||||
probing = true;
|
||||
probe_timeout_duration_us = tcb->rto;
|
||||
}
|
||||
/* Initialize Probe Timer */
|
||||
probe_timeout_msg.type = MSG_TYPE_PROBE_TIMEOUT;
|
||||
xtimer_set_msg(&probe_timeout_timer, probe_timeout_duration_us, &probe_timeout_msg,
|
||||
tcb->owner);
|
||||
}
|
||||
|
||||
/* Try to send data in case there nothing has been sent and we are not probing */
|
||||
if (ret == 0 && !probing) {
|
||||
ret = _fsm(tcb, GNRC_TCP_FSM_EVENT_CALL_SEND, NULL, (void *) data, len);
|
||||
}
|
||||
|
||||
/* Wait for responses */
|
||||
msg_receive(&msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : CONNECTION_TIMEOUT\n");
|
||||
_fsm(tcb, GNRC_TCP_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, GNRC_TCP_FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
|
||||
case MSG_TYPE_PROBE_TIMEOUT:
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : PROBE_TIMEOUT\n");
|
||||
/* Send Probe */
|
||||
_fsm(tcb, GNRC_TCP_FSM_EVENT_SEND_PROBE, NULL, NULL, 0);
|
||||
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");
|
||||
/* Connection is alive: Reset Connection Timeout */
|
||||
xtimer_set_msg(&connection_timeout_timer, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
||||
&connection_timeout_msg, tcb->owner);
|
||||
|
||||
/* If the window re-opened and we are probing: Stop it */
|
||||
if (tcb->snd_wnd > 0 && probing) {
|
||||
probing = false;
|
||||
xtimer_remove(&probe_timeout_timer);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_send() : other message type\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
xtimer_remove(&probe_timeout_timer);
|
||||
xtimer_remove(&connection_timeout_timer);
|
||||
xtimer_remove(&user_timeout_timer);
|
||||
tcb->owner = KERNEL_PID_UNDEF;
|
||||
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);
|
||||
|
||||
msg_t msg; /* Message for incomming Messages */
|
||||
msg_t connection_timeout_msg; /* Connection Timeout Message */
|
||||
msg_t user_timeout_msg; /* User Specified Timeout Message */
|
||||
xtimer_t connection_timeout_timer; /* Connection Timeout Timer */
|
||||
xtimer_t user_timeout_timer; /* User Specified Timeout Timer */
|
||||
ssize_t ret = 0; /* Return Value */
|
||||
|
||||
/* Lock the tcb for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Check if connection is in a valid state */
|
||||
if (tcb->state != GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
&& tcb->state != GNRC_TCP_FSM_STATE_FIN_WAIT_1
|
||||
&& tcb->state != GNRC_TCP_FSM_STATE_FIN_WAIT_2
|
||||
&& tcb->state != GNRC_TCP_FSM_STATE_CLOSE_WAIT
|
||||
) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
/* If this call is non-blocking (timeout_duration_us == 0): Try to read data and return */
|
||||
if (timeout_duration_us == 0) {
|
||||
ret = _fsm(tcb, GNRC_TCP_FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
||||
if(ret == 0) {
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If this call is blocking, setup messages and timers */
|
||||
msg_init_queue(tcb->msg_queue, GNRC_TCP_MSG_QUEUE_SIZE);
|
||||
tcb->owner = thread_getpid();
|
||||
|
||||
/* Setup Connection Timeout */
|
||||
connection_timeout_msg.type = MSG_TYPE_CONNECTION_TIMEOUT;
|
||||
xtimer_set_msg(&connection_timeout_timer, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
||||
&connection_timeout_msg, tcb->owner);
|
||||
|
||||
/* Setup User Specified Timeout */
|
||||
user_timeout_msg.type = MSG_TYPE_USER_SPEC_TIMEOUT;
|
||||
xtimer_set_msg(&user_timeout_timer, timeout_duration_us, &user_timeout_msg, tcb->owner);
|
||||
|
||||
/* Processing Loop */
|
||||
while (ret == 0) {
|
||||
/* Check if the connections state is closed. If so, a reset was received */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_CLOSED) {
|
||||
ret = -ECONNRESET;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to read available data */
|
||||
ret = _fsm(tcb, GNRC_TCP_FSM_EVENT_CALL_RECV, NULL, data, max_len);
|
||||
|
||||
/* If there was no data: Wait for next packet or until the timeout fires */
|
||||
if (ret <= 0) {
|
||||
msg_receive(&msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_recv() : CONNECTION_TIMEOUT\n");
|
||||
_fsm(tcb, GNRC_TCP_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, GNRC_TCP_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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
xtimer_remove(&connection_timeout_timer);
|
||||
xtimer_remove(&user_timeout_timer);
|
||||
tcb->owner = KERNEL_PID_UNDEF;
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gnrc_tcp_close(gnrc_tcp_tcb_t *tcb)
|
||||
{
|
||||
assert(tcb != NULL);
|
||||
|
||||
msg_t msg; /* Message for incomming Messages */
|
||||
msg_t connection_timeout_msg; /* Connection Timeout Message */
|
||||
xtimer_t connection_timeout_timer; /* Connection Timeout Timer */
|
||||
|
||||
/* Lock the tcb for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Start connection teardown if the connection was not closed before */
|
||||
if (tcb->state != GNRC_TCP_FSM_STATE_CLOSED) {
|
||||
/* Take ownership */
|
||||
msg_init_queue(tcb->msg_queue, GNRC_TCP_MSG_QUEUE_SIZE);
|
||||
tcb->owner = thread_getpid();
|
||||
|
||||
/* Setup Connection Timeout */
|
||||
connection_timeout_msg.type = MSG_TYPE_CONNECTION_TIMEOUT;
|
||||
xtimer_set_msg(&connection_timeout_timer, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
|
||||
&connection_timeout_msg, tcb->owner);
|
||||
|
||||
/* Start connection teardown sequence */
|
||||
_fsm(tcb, GNRC_TCP_FSM_EVENT_CALL_CLOSE, NULL, NULL, 0);
|
||||
|
||||
/* Loop until the connection has been closed */
|
||||
while (tcb->state != GNRC_TCP_FSM_STATE_CLOSED) {
|
||||
msg_receive(&msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
DEBUG("gnrc_tcp.c : gnrc_tcp_close() : CONNECTION_TIMEOUT\n");
|
||||
_fsm(tcb, GNRC_TCP_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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
xtimer_remove(&connection_timeout_timer);
|
||||
tcb->owner = KERNEL_PID_UNDEF;
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* allocate header */
|
||||
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));
|
||||
|
||||
/* Initialize Header with sane Defaults */
|
||||
hdr->src_port = byteorder_htons(src);
|
||||
hdr->dst_port = byteorder_htons(dst);
|
||||
hdr->checksum = byteorder_htons(0);
|
||||
hdr->off_ctl = byteorder_htons(OPTION_OFFSET_MAX);
|
||||
return res;
|
||||
}
|
275
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_eventloop.c
Normal file
275
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_eventloop.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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
|
||||
* @brief GNRC's TCP event processing loop
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <utlist.h>
|
||||
#include <errno.h>
|
||||
#include "net/af.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
#include "net/gnrc/tcp/hdr.h"
|
||||
#include "internal/pkt.h"
|
||||
#include "internal/fsm.h"
|
||||
#include "internal/helper.h"
|
||||
#include "internal/option.h"
|
||||
#include "internal/eventloop.h"
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief send function, used to pass paket down the network stack
|
||||
*
|
||||
* @param[in] pkt paket to pass down the network stack
|
||||
*
|
||||
* @return zero on success
|
||||
* @return negative value on error
|
||||
* @return -EBADMSG if tcp header is missing
|
||||
*/
|
||||
static int _send(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
/* NOTE: Sending Direction: pkt = nw, nw->next = tcp, tcp->next = payload */
|
||||
gnrc_pktsnip_t *tcp;
|
||||
|
||||
/* Search for tcp header */
|
||||
LL_SEARCH_SCALAR(pkt, tcp, type, GNRC_NETTYPE_TCP);
|
||||
if (tcp == NULL) {
|
||||
DEBUG("gnrc_tcp_eventloop : _send() : tcp header missing.\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* Dispatch to network layer */
|
||||
if (!gnrc_netapi_dispatch_send(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
|
||||
DEBUG("gnrc_tcp_eventloop : _send() : network layer not found\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief recv function, used to call fsm on packet reception
|
||||
*
|
||||
* @param[in] pkt incomming paket to process
|
||||
*
|
||||
* @return zero on success
|
||||
* @return negative value on error
|
||||
* @return -EACCES if not able to get write access to packet
|
||||
* @return -ERANGE if segment offset is less than 5
|
||||
* @return -ENOMSG if paket can't be marked
|
||||
* @return -EINVAL if checksum was invalid
|
||||
* @return -ENOTCONN if no module is interested in this context
|
||||
*/
|
||||
static int _receive(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
/* NOTE: Receiving direction: pkt = payload, payload->next = tcp, tcp->next = nw */
|
||||
uint16_t ctl = 0;
|
||||
uint16_t src = 0;
|
||||
uint16_t dst = 0;
|
||||
uint8_t hdr_size = 0;
|
||||
uint8_t syn = 0;
|
||||
gnrc_pktsnip_t *ip = NULL;
|
||||
gnrc_pktsnip_t *reset = NULL;
|
||||
gnrc_tcp_tcb_t *tcb = NULL;
|
||||
tcp_hdr_t *hdr;
|
||||
|
||||
/* Get write access to the TCP Header */
|
||||
gnrc_pktsnip_t *tcp = gnrc_pktbuf_start_write(pkt);
|
||||
if (tcp == NULL) {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : can't write to packet\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return -EACCES;
|
||||
}
|
||||
pkt = tcp;
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
/* Get IP Header, discard packet if doesn't contain an ip header */
|
||||
LL_SEARCH_SCALAR(pkt, ip, type, GNRC_NETTYPE_IPV6);
|
||||
if (ip == NULL) {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : pkt contains no IP Header\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get TCP Header */
|
||||
LL_SEARCH_SCALAR(pkt, tcp, type, GNRC_NETTYPE_TCP);
|
||||
if (tcp == NULL) {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : pkt contains no TCP Header\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract control bits, src and dst ports and check if SYN is set (not SYN+ACK) */
|
||||
hdr = (tcp_hdr_t *)tcp->data;
|
||||
ctl = byteorder_ntohs(hdr->off_ctl);
|
||||
src = byteorder_ntohs(hdr->src_port);
|
||||
dst = byteorder_ntohs(hdr->dst_port);
|
||||
syn = ((ctl & MSK_SYN_ACK) == MSK_SYN);
|
||||
|
||||
/* Validate Offset */
|
||||
if (GET_OFFSET(ctl) < OPTION_OFFSET_BASE) {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : unexpected Offset Value\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Calculate tcp header size */
|
||||
hdr_size = GET_OFFSET(ctl) * 4;
|
||||
|
||||
/* Mark TCP-Header, if it contains any payload */
|
||||
if ((pkt->type == GNRC_NETTYPE_TCP) && (pkt->size != hdr_size)) {
|
||||
tcp = gnrc_pktbuf_mark(pkt, hdr_size, GNRC_NETTYPE_TCP);
|
||||
if (tcp == NULL) {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : Header marking failed\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return -ENOMSG;
|
||||
}
|
||||
pkt->type = GNRC_NETTYPE_UNDEF;
|
||||
}
|
||||
|
||||
/* Validate Checksum */
|
||||
if (byteorder_ntohs(hdr->checksum) != _pkt_calc_csum(tcp, ip, pkt)) {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : Invalid checksum\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find tcb to de-multiplex this packet to */
|
||||
mutex_lock(&_list_gnrc_tcp_tcb_lock);
|
||||
tcb = _list_gnrc_tcp_tcb_head;
|
||||
while (tcb) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
/* Check if current tcb is fitting for the incomming packet */
|
||||
if (ip->type == GNRC_NETTYPE_IPV6 && tcb->address_family == AF_INET6) {
|
||||
/* If SYN is set, a connection is listening on that port ... */
|
||||
ipv6_addr_t * tmp_addr = NULL;
|
||||
if (syn && tcb->local_port == dst && tcb->state == GNRC_TCP_FSM_STATE_LISTEN) {
|
||||
/* ... and local addr is unspec or preconfigured */
|
||||
tmp_addr = &((ipv6_hdr_t * )ip->data)->dst;
|
||||
if (ipv6_addr_equal((ipv6_addr_t *) tcb->local_addr, (ipv6_addr_t *) tmp_addr)
|
||||
|| ipv6_addr_is_unspecified((ipv6_addr_t *) tcb->local_addr)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If SYN is not set and the ports match ... */
|
||||
if (!syn && tcb->local_port == dst && tcb->peer_port == src) {
|
||||
/* .. and the IP-Addresses match */
|
||||
tmp_addr = &((ipv6_hdr_t * )ip->data)->src;
|
||||
if (ipv6_addr_equal((ipv6_addr_t *) tcb->peer_addr, (ipv6_addr_t *) tmp_addr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Supress compiler warnings if TCP is build without IP-Layer */
|
||||
(void) syn;
|
||||
(void) src;
|
||||
(void) dst;
|
||||
#endif
|
||||
tcb = tcb->next;
|
||||
}
|
||||
mutex_unlock(&_list_gnrc_tcp_tcb_lock);
|
||||
|
||||
/* Call FSM with event RCVD_PKT if a fitting connection was found */
|
||||
if (tcb != NULL) {
|
||||
_fsm(tcb, GNRC_TCP_FSM_EVENT_RCVD_PKT, pkt, NULL, 0);
|
||||
}
|
||||
/* No fitting connection has been found. Respond with reset */
|
||||
else {
|
||||
DEBUG("gnrc_tcp_eventloop.c : _receive() : Can't find fitting connection\n");
|
||||
if ((ctl & MSK_RST) != MSK_RST) {
|
||||
_pkt_build_reset_from_pkt(&reset, pkt);
|
||||
gnrc_netapi_send(_gnrc_tcp_pid, reset);
|
||||
}
|
||||
return -ENOTCONN;
|
||||
}
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *_event_loop(__attribute__((unused)) void *arg)
|
||||
{
|
||||
msg_t msg;
|
||||
msg_t reply;
|
||||
msg_t msg_queue[GNRC_TCP_MSG_QUEUE_SIZE];
|
||||
|
||||
/* Store pid */
|
||||
_gnrc_tcp_pid = thread_getpid();
|
||||
|
||||
/* Setup reply message */
|
||||
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
|
||||
reply.content.value = (uint32_t)-ENOTSUP;
|
||||
|
||||
/* Init message queue*/
|
||||
msg_init_queue(msg_queue, GNRC_TCP_MSG_QUEUE_SIZE);
|
||||
|
||||
/* Register GNRC_tcp in netreg */
|
||||
gnrc_netreg_entry_t entry;
|
||||
gnrc_netreg_entry_init_pid(&entry, GNRC_NETREG_DEMUX_CTX_ALL, _gnrc_tcp_pid);
|
||||
gnrc_netreg_register(GNRC_NETTYPE_TCP, &entry);
|
||||
|
||||
/* dispatch NETAPI Messages */
|
||||
while (1) {
|
||||
msg_receive(&msg);
|
||||
switch (msg.type) {
|
||||
/* Pass Message up the network stack */
|
||||
case GNRC_NETAPI_MSG_TYPE_RCV:
|
||||
DEBUG("gnrc_tcp_eventloop.c : _event_loop() : GNRC_NETAPI_MSG_TYPE_RCV\n");
|
||||
_receive((gnrc_pktsnip_t *)msg.content.ptr);
|
||||
break;
|
||||
|
||||
/* Pass Message down the network stack */
|
||||
case GNRC_NETAPI_MSG_TYPE_SND:
|
||||
DEBUG("gnrc_tcp_eventloop.c : _event_loop() : GNRC_NETAPI_MSG_TYPE_SND\n");
|
||||
_send((gnrc_pktsnip_t *)msg.content.ptr);
|
||||
break;
|
||||
|
||||
/* Option Set and Get Messages*/
|
||||
case GNRC_NETAPI_MSG_TYPE_SET:
|
||||
case GNRC_NETAPI_MSG_TYPE_GET:
|
||||
msg_reply(&msg, &reply);
|
||||
break;
|
||||
|
||||
/* Retransmission Timer expired -> Call FSM with retransmission event */
|
||||
case MSG_TYPE_RETRANSMISSION:
|
||||
DEBUG("gnrc_tcp_eventloop.c : _event_loop() : MSG_TYPE_RETRANSMISSION\n");
|
||||
_fsm((gnrc_tcp_tcb_t *)msg.content.ptr, GNRC_TCP_FSM_EVENT_TIMEOUT_RETRANSMIT,
|
||||
NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
/* Time Wait Timer expired -> Call FSM with timewait event */
|
||||
case MSG_TYPE_TIMEWAIT:
|
||||
DEBUG("gnrc_tcp_eventloop.c : _event_loop() : MSG_TYPE_TIMEWAIT\n");
|
||||
_fsm((gnrc_tcp_tcb_t *)msg.content.ptr, GNRC_TCP_FSM_EVENT_TIMEOUT_TIMEWAIT,
|
||||
NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG("gnrc_tcp_eventloop.c : _event_loop() : received expected message\n");
|
||||
}
|
||||
}
|
||||
/* never reached */
|
||||
return NULL;
|
||||
}
|
934
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c
Normal file
934
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c
Normal file
@ -0,0 +1,934 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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
|
||||
* @brief GNRC's TCP finite state maschine
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
#include "random.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "net/af.h"
|
||||
|
||||
#include "internal/fsm.h"
|
||||
#include "internal/pkt.h"
|
||||
#include "internal/option.h"
|
||||
#include "internal/helper.h"
|
||||
#include "internal/rcvbuf.h"
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
#include "net/gnrc/ipv6.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief Checks if a given portnumber is currently used by a tcb as local_port.
|
||||
*
|
||||
* @param[in] portnumber Portnumber that should be checked
|
||||
*
|
||||
* @note Must be called from a context where the tcb list ist locked.
|
||||
*
|
||||
* @return Zero if @p portnumber is currently not used.
|
||||
* @return 1 if @p portnumber is used by an tcb.
|
||||
*/
|
||||
static int _is_local_port_in_use(const uint16_t portnumber)
|
||||
{
|
||||
gnrc_tcp_tcb_t *iter = NULL;
|
||||
LL_FOREACH(_list_gnrc_tcp_tcb_head, iter) {
|
||||
if (iter->local_port == portnumber) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random, currently unused local port above the well-known ports (> 1024)
|
||||
*
|
||||
* @return The generated port number
|
||||
*/
|
||||
static uint16_t _get_random_local_port(void)
|
||||
{
|
||||
uint16_t ret = 0;
|
||||
do {
|
||||
ret = random_uint32();
|
||||
if (ret < 1024) {
|
||||
continue;
|
||||
}
|
||||
} while(_is_local_port_in_use(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief clears retransmit queue
|
||||
*
|
||||
* @param[in/out] conn TCP Connection, where the retransmit should be cleared
|
||||
*
|
||||
* @return zero on success
|
||||
*/
|
||||
static int _clear_retransmit(gnrc_tcp_tcb_t *tcb)
|
||||
{
|
||||
if (tcb->pkt_retransmit != NULL) {
|
||||
gnrc_pktbuf_release(tcb->pkt_retransmit);
|
||||
xtimer_remove(&(tcb->tim_tout));
|
||||
tcb->pkt_retransmit = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief restarts time wait timer
|
||||
*
|
||||
* @param[in/out] conn TCP Connection, where the timewait_timer should be restarted
|
||||
*
|
||||
* @return Zero on success
|
||||
*/
|
||||
static int _restart_timewait_timer(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
xtimer_remove(&tcb->tim_tout);
|
||||
tcb->msg_tout.type = MSG_TYPE_TIMEWAIT;
|
||||
tcb->msg_tout.content.ptr = (void *)tcb;
|
||||
xtimer_set_msg(&tcb->tim_tout, 2 * GNRC_TCP_MSL, &tcb->msg_tout, _gnrc_tcp_pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief translates fsm into another state
|
||||
*
|
||||
* @param[in/out] tcb tcb, that specifies connection
|
||||
* @param[in] state state to translate in
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return zero on success
|
||||
*/
|
||||
static int _transition_to(gnrc_tcp_tcb_t* tcb, gnrc_tcp_fsm_state_t state, bool *notify_owner)
|
||||
{
|
||||
gnrc_tcp_tcb_t *iter = NULL;
|
||||
uint8_t found = 0;
|
||||
|
||||
switch (state) {
|
||||
case GNRC_TCP_FSM_STATE_CLOSED:
|
||||
/* Free Packets in Retransmit queue */
|
||||
_clear_retransmit(tcb);
|
||||
|
||||
/* Remove from Connection from active connections */
|
||||
mutex_lock(&_list_gnrc_tcp_tcb_lock);
|
||||
LL_FOREACH(_list_gnrc_tcp_tcb_head, iter) {
|
||||
if (iter == tcb) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
LL_DELETE(_list_gnrc_tcp_tcb_head, iter);
|
||||
}
|
||||
mutex_unlock(&_list_gnrc_tcp_tcb_lock);
|
||||
|
||||
/* Free potencially allocated Receive Buffer */
|
||||
_rcvbuf_release_buffer(tcb);
|
||||
*notify_owner = true;
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_LISTEN:
|
||||
/* Clear Adress Info */
|
||||
switch (tcb->address_family) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case AF_INET6:
|
||||
if (tcb->status & GNRC_TCP_STATUS_ALLOW_ANY_ADDR) {
|
||||
ipv6_addr_set_unspecified((ipv6_addr_t *) tcb->local_addr);
|
||||
}
|
||||
ipv6_addr_set_unspecified((ipv6_addr_t *) tcb->peer_addr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
DEBUG("gnrc_tcp_fsm.c : _transition_to() : Undefined Addresses\n");
|
||||
break;
|
||||
}
|
||||
tcb->peer_port = GNRC_TCP_PORT_UNSPEC;
|
||||
|
||||
/* Allocate rcv Buffer */
|
||||
if (_rcvbuf_get_buffer(tcb) == -ENOMEM) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Add to Connection to active connections (if not already active) */
|
||||
mutex_lock(&_list_gnrc_tcp_tcb_lock);
|
||||
LL_FOREACH(_list_gnrc_tcp_tcb_head, iter) {
|
||||
if (iter == tcb) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
LL_APPEND(_list_gnrc_tcp_tcb_head, tcb);
|
||||
}
|
||||
mutex_unlock(&_list_gnrc_tcp_tcb_lock);
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_SYN_SENT:
|
||||
/* Allocate rcv Buffer */
|
||||
if (_rcvbuf_get_buffer(tcb) == -ENOMEM) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Add to Connections to active connection (if not already active) */
|
||||
mutex_lock(&_list_gnrc_tcp_tcb_lock);
|
||||
LL_FOREACH(_list_gnrc_tcp_tcb_head, iter) {
|
||||
if (iter == tcb) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
/* If not already active: Apped tcb but check portnumber first */
|
||||
if (!found) {
|
||||
/* Check if Port Number is not in use */
|
||||
if (tcb->local_port != GNRC_TCP_PORT_UNSPEC ) {
|
||||
|
||||
/* If Portnumber is used: return error and release buffer */
|
||||
if (_is_local_port_in_use(tcb->local_port)) {
|
||||
mutex_unlock(&_list_gnrc_tcp_tcb_lock);
|
||||
_rcvbuf_release_buffer(tcb);
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
}
|
||||
/* Pick Random Port */
|
||||
else {
|
||||
tcb->local_port = _get_random_local_port();
|
||||
}
|
||||
LL_APPEND(_list_gnrc_tcp_tcb_head, tcb);
|
||||
}
|
||||
mutex_unlock(&_list_gnrc_tcp_tcb_lock);
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_ESTABLISHED:
|
||||
*notify_owner = true;
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_CLOSE_WAIT:
|
||||
*notify_owner = true;
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_TIME_WAIT:
|
||||
_restart_timewait_timer(tcb);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
tcb->state = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for active and passive open
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return zero on success
|
||||
* @return -ENOMEM Can't allocate receive buffer.
|
||||
* @return -EADDRINUSE Given local port is already in use
|
||||
*/
|
||||
static int _fsm_call_open(gnrc_tcp_tcb_t* tcb, bool *notify_owner)
|
||||
{
|
||||
gnrc_pktsnip_t *out_pkt = NULL; /* Outgoing packet */
|
||||
uint16_t seq_con = 0; /* Sequence number consumption (out_pkt) */
|
||||
int ret = 0; /* Return value */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_call_open()\n");
|
||||
tcb->rcv_wnd = GNRC_TCP_DEFAULT_WINDOW;
|
||||
|
||||
if (tcb->status & GNRC_TCP_STATUS_PASSIVE) {
|
||||
/* Passive Open, T: CLOSED -> LISTEN */
|
||||
if (_transition_to(tcb, GNRC_TCP_FSM_STATE_LISTEN, notify_owner) == -ENOMEM){
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Active Open, init tcb values, send SYN, T: CLOSED -> SYN_SENT */
|
||||
tcb->iss = random_uint32();
|
||||
tcb->snd_nxt = tcb->iss;
|
||||
tcb->snd_una = tcb->iss;
|
||||
|
||||
/* Translate to SYN_SENT */
|
||||
ret = _transition_to(tcb, GNRC_TCP_FSM_STATE_SYN_SENT, notify_owner);
|
||||
if ( ret < 0) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send SYN */
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_SYN, tcb->iss, 0, NULL, 0);
|
||||
_pkt_setup_retransmit(tcb, out_pkt, false);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for sending data.
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[in/out] buf buffer containing data to send.
|
||||
* @param[in] nByte Maximum Number of Bytes to send.
|
||||
*
|
||||
* @return number of bytes that was sent.
|
||||
*/
|
||||
static int _fsm_call_send(gnrc_tcp_tcb_t* tcb, void *buf, size_t nByte)
|
||||
{
|
||||
gnrc_pktsnip_t *out_pkt = NULL; /* Outgoing packet */
|
||||
uint16_t seq_con = 0; /* Sequence number consumption (out_pkt) */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_call_send()\n");
|
||||
size_t payload = (tcb->snd_una + tcb->snd_wnd) - tcb->snd_nxt;
|
||||
|
||||
/* We are allowed to send further bytes if window is open */
|
||||
if (payload > 0 && tcb->snd_wnd > 0 && tcb->pkt_retransmit == NULL) {
|
||||
/* Calculate segment size */
|
||||
payload = (payload < GNRC_TCP_MSS ? payload : GNRC_TCP_MSS);
|
||||
payload = (payload < tcb->mss) ? payload : tcb->mss;
|
||||
payload = (payload < nByte) ? payload : nByte;
|
||||
|
||||
/* Calculate payload size for this segment */
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt, buf, payload);
|
||||
_pkt_setup_retransmit(tcb, out_pkt, false);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
return payload;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for receiving data.
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[in/out] buf buffer to store received data into.
|
||||
* @param[in] nByte Maximum Number of Bytes to receive.
|
||||
*
|
||||
* @return number of bytes that was received.
|
||||
*/
|
||||
static int _fsm_call_recv(gnrc_tcp_tcb_t* tcb, void *buf, size_t nByte)
|
||||
{
|
||||
gnrc_pktsnip_t *out_pkt = NULL; /* Outgoing packet */
|
||||
uint16_t seq_con = 0; /* Sequence number consumption (out_pkt) */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_call_recv()\n");
|
||||
if (ringbuffer_empty(&tcb->rcv_buf)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read up to the requesed amount of data */
|
||||
size_t rcvd = ringbuffer_get(&(tcb->rcv_buf), buf, nByte);
|
||||
|
||||
/* If the buffer can store more than the GNRC_TCP_MSS: open Window to available buffersize */
|
||||
if (ringbuffer_get_free(&tcb->rcv_buf) >= GNRC_TCP_MSS) {
|
||||
tcb->rcv_wnd = ringbuffer_get_free(&(tcb->rcv_buf));
|
||||
|
||||
/* Send ACK to update window on reopening */
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt, 0, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
return rcvd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for initiating a teardown.
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return zero on success.
|
||||
*/
|
||||
static int _fsm_call_close(gnrc_tcp_tcb_t* tcb, bool *notify_owner)
|
||||
{
|
||||
gnrc_pktsnip_t *out_pkt = NULL; /* Outgoing packet */
|
||||
uint16_t seq_con = 0; /* Sequence number consumption (out_pkt) */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_call_close()\n");
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_SYN_RCVD
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_CLOSE_WAIT
|
||||
) {
|
||||
/* Send FIN packet */
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_FIN_ACK, tcb->snd_nxt, tcb->rcv_nxt, NULL, 0);
|
||||
_pkt_setup_retransmit(tcb, out_pkt, false);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
switch (tcb->state) {
|
||||
case GNRC_TCP_FSM_STATE_LISTEN:
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_SYN_RCVD:
|
||||
case GNRC_TCP_FSM_STATE_ESTABLISHED:
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_FIN_WAIT_1, notify_owner);
|
||||
break;
|
||||
|
||||
case GNRC_TCP_FSM_STATE_CLOSE_WAIT:
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_LAST_ACK, notify_owner);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for forcefull teardown
|
||||
*
|
||||
* @return -EOPNOTSUPP, because function is currently not implemented
|
||||
*/
|
||||
static int _fsm_call_abort(void)
|
||||
{
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_call_abort()\n");
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_call_abort() : ABORT not implemented\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for processing of a received packet
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[in] in_pkt Packet that should be processed.
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return zero on success.
|
||||
* @return -ENOMEM Can't allocate receive buffer.
|
||||
*/
|
||||
static int _fsm_rcvd_pkt(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t *in_pkt, bool *notify_owner)
|
||||
{
|
||||
gnrc_pktsnip_t *out_pkt = NULL; /* Outgoing packet */
|
||||
uint16_t seq_con = 0; /* Sequence number consumption (out_pkt) */
|
||||
gnrc_pktsnip_t *snp = NULL; /* Temporary Packet Snip */
|
||||
gnrc_tcp_tcb_t *lst = NULL; /* Temporary tcb pointer */
|
||||
uint16_t ctl = 0; /* Received control bits */
|
||||
uint32_t seg_seq = 0; /* Received sequence number */
|
||||
uint32_t seg_ack = 0; /* Received acknowledgment number */
|
||||
uint32_t seg_len = 0; /* Segment length */
|
||||
uint32_t pay_len = 0; /* Payload length */
|
||||
uint32_t seg_wnd = 0; /* Segment window */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt()\n");
|
||||
/* Search TCP header. */
|
||||
LL_SEARCH_SCALAR(in_pkt, snp, type, GNRC_NETTYPE_TCP);
|
||||
tcp_hdr_t *tcp_hdr = (tcp_hdr_t *) snp->data;
|
||||
|
||||
/* Verify packet options, return if they were faulty */
|
||||
if (_option_parse(tcb, tcp_hdr) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract header values */
|
||||
ctl = byteorder_ntohs(tcp_hdr->off_ctl);
|
||||
seg_seq = byteorder_ntohl(tcp_hdr->seq_num);
|
||||
seg_ack = byteorder_ntohl(tcp_hdr->ack_num);
|
||||
seg_wnd = byteorder_ntohs(tcp_hdr->window);
|
||||
|
||||
/* Extract IPv6-Header */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
LL_SEARCH_SCALAR(in_pkt, snp, type, GNRC_NETTYPE_IPV6);
|
||||
if (snp == NULL) {
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt() : incomming packet had no ip header\n");
|
||||
return 0;
|
||||
}
|
||||
void *ip = snp->data;
|
||||
#endif
|
||||
|
||||
/* Handle state LISTEN */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_LISTEN) {
|
||||
/* 1) Check RST: if set, return */
|
||||
if (ctl & MSK_RST) {
|
||||
return 0;
|
||||
}
|
||||
/* 2) Check ACK: if set, send reset with seq_no = ack_no, return */
|
||||
if (ctl & MSK_ACK) {
|
||||
_pkt_build_reset_from_pkt(&out_pkt, in_pkt);
|
||||
_pkt_send(tcb, out_pkt, 0, false);
|
||||
return 0;
|
||||
}
|
||||
/* 3) Check SYN: Setup incoming connection*/
|
||||
if (ctl & MSK_SYN) {
|
||||
uint16_t src = byteorder_ntohs(tcp_hdr->src_port);
|
||||
uint16_t dst = byteorder_ntohs(tcp_hdr->dst_port);
|
||||
|
||||
/* Check if SYN Request is handled by another connection */
|
||||
lst = _list_gnrc_tcp_tcb_head;
|
||||
while (lst) {
|
||||
/* Compare Portnumbers and Network Layer Adresses */
|
||||
/* Note: Packets without ip-header were discarded earlier */
|
||||
if (lst->local_port == dst && lst->peer_port == src) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (snp->type == GNRC_NETTYPE_IPV6 && lst->address_family == AF_INET6) {
|
||||
ipv6_addr_t *dst_addr = &((ipv6_hdr_t *)ip)->dst;
|
||||
ipv6_addr_t *src_addr = &((ipv6_hdr_t *)ip)->src;
|
||||
|
||||
if (ipv6_addr_equal((ipv6_addr_t *)lst->local_addr, dst_addr)
|
||||
&& ipv6_addr_equal((ipv6_addr_t *)lst->peer_addr, src_addr)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
lst = lst->next;
|
||||
}
|
||||
/* Return if connection is already handled (port and addresses match) */
|
||||
if (lst != NULL) {
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt() : Connection already handled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SYN Request is valid, fill connection struct with connection information */
|
||||
/* Note: Packets without ipv6-header were discarded earlier */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (snp->type == GNRC_NETTYPE_IPV6 && tcb->address_family == AF_INET6) {
|
||||
memcpy(tcb->local_addr, &((ipv6_hdr_t *)ip)->dst, sizeof(ipv6_addr_t));
|
||||
memcpy(tcb->peer_addr, &((ipv6_hdr_t *)ip)->src, sizeof(ipv6_addr_t));
|
||||
}
|
||||
#else
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt() : Received Address was not stored\n");
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
tcb->local_port = dst;
|
||||
tcb->peer_port = src;
|
||||
tcb->irs = byteorder_ntohl(tcp_hdr->seq_num);
|
||||
tcb->rcv_nxt = tcb->irs + 1;
|
||||
tcb->iss = random_uint32();
|
||||
tcb->snd_una = tcb->iss;
|
||||
tcb->snd_nxt = tcb->iss;
|
||||
tcb->snd_wnd = seg_wnd;
|
||||
|
||||
/* Send SYN+ACK: seq_no = iss, ack_no = rcv_nxt, T: LISTEN -> SYN_RCVD */
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_SYN_ACK, tcb->iss, tcb->rcv_nxt, NULL, 0);
|
||||
_pkt_setup_retransmit(tcb, out_pkt, false);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_SYN_RCVD, notify_owner);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Handle state SYN_SENT */
|
||||
else if (tcb->state == GNRC_TCP_FSM_STATE_SYN_SENT) {
|
||||
/* 1) Check ACK */
|
||||
if (ctl & MSK_ACK) {
|
||||
/* If ACK is not acceptable ...*/
|
||||
if (seg_ack <= tcb->iss || seg_ack > tcb->snd_nxt) {
|
||||
/* ... send Reset if RST is not set else return */
|
||||
if ((ctl & MSK_RST) != MSK_RST) {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_RST, seg_ack, 0, NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* 2) Check RST: If RST set ... */
|
||||
if (ctl & MSK_RST) {
|
||||
/* ... and ACK: Translate to CLOSED, if not return */
|
||||
if (ctl & MSK_ACK) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* 3) Check SYN: Set TCB values accordingly */
|
||||
if (ctl & MSK_SYN) {
|
||||
tcb->rcv_nxt = seg_seq + 1;
|
||||
tcb->irs = seg_seq;
|
||||
if (ctl & MSK_ACK) {
|
||||
tcb->snd_una = seg_ack;
|
||||
_pkt_acknowledge(tcb, seg_ack);
|
||||
}
|
||||
/* Set the local address accordingly */
|
||||
/* Note: Packets without ipv6-header were discarded earlier */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (snp->type == GNRC_NETTYPE_IPV6 && tcb->address_family == AF_INET6) {
|
||||
memcpy(tcb->local_addr, &((ipv6_hdr_t *)ip)->dst, sizeof(ipv6_addr_t));
|
||||
}
|
||||
#else
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt() : Received Address was not stored\n");
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* SYN has been ACKed, reply pure ACK, T: SYN_SENT -> ESTABLISHED */
|
||||
if (tcb->snd_una > tcb->iss) {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt,
|
||||
NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_ESTABLISHED, notify_owner);
|
||||
}
|
||||
/* Simultaneous SYN received send SYN+ACK, T: SYN_SENT -> SYN_RCVD */
|
||||
else {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_SYN_ACK, tcb->iss, tcb->rcv_nxt,
|
||||
NULL, 0);
|
||||
_pkt_setup_retransmit(tcb, out_pkt, false);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_SYN_RCVD, notify_owner);
|
||||
}
|
||||
tcb->snd_wnd = seg_wnd;
|
||||
tcb->snd_wl1 = seg_seq;
|
||||
tcb->snd_wl2 = seg_ack;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Handle other states */
|
||||
else {
|
||||
seg_len = _pkt_get_seg_len(in_pkt);
|
||||
pay_len = _pkt_get_pay_len(in_pkt);
|
||||
/* 1) Verify Sequence Number ... */
|
||||
if (!_pkt_chk_seq_num(tcb, seg_seq, pay_len)) {
|
||||
/* ... if invalid, and RST not set, reply with pure ACK, return */
|
||||
if ((ctl & MSK_RST) != MSK_RST) {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt,
|
||||
NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* 2) Check RST: If RST is set ... */
|
||||
if (ctl & MSK_RST) {
|
||||
/* .. and State is SYN_RCVD and passive Open: SYN_RCVD -> LISTEN */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_SYN_RCVD
|
||||
&& (tcb->status & GNRC_TCP_STATUS_PASSIVE)
|
||||
) {
|
||||
if (_transition_to(tcb, GNRC_TCP_FSM_STATE_LISTEN, notify_owner) == -ENOMEM) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* 3) Check SYN: If SYN is set ... */
|
||||
if (ctl & MSK_SYN) {
|
||||
/* ... send RST, seq_no = snd_nxt, ack_no = rcv_nxt */
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_RST, tcb->snd_nxt, tcb->rcv_nxt, NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return 0;
|
||||
}
|
||||
/* 4) Check ACK */
|
||||
if (!(ctl & MSK_ACK)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_SYN_RCVD) {
|
||||
if (LSS_32_BIT(tcb->snd_una, seg_ack) && LEQ_32_BIT(seg_ack, tcb->snd_nxt)) {
|
||||
tcb->snd_wnd = seg_wnd;
|
||||
tcb->snd_wl1 = seg_seq;
|
||||
tcb->snd_wl2 = seg_ack;
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_ESTABLISHED, notify_owner);
|
||||
}
|
||||
else {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_RST, seg_ack, 0, NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
}
|
||||
/* Acknowledgment processing */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_1
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_2
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_CLOSE_WAIT
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_CLOSING
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_LAST_ACK
|
||||
) {
|
||||
/* Sent data has been acknowledged */
|
||||
if (LSS_32_BIT(tcb->snd_una, seg_ack) && LEQ_32_BIT(seg_ack, tcb->snd_nxt)) {
|
||||
tcb->snd_una = seg_ack;
|
||||
_pkt_acknowledge(tcb, seg_ack);
|
||||
}
|
||||
/* ACK received for something not yet sent: Reply with pure ACK */
|
||||
else if (LSS_32_BIT(tcb->snd_nxt, seg_ack)) {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt,
|
||||
NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
return 0;
|
||||
}
|
||||
/* Update Window */
|
||||
if (LEQ_32_BIT(tcb->snd_una, seg_ack) && LEQ_32_BIT(seg_ack, tcb->snd_nxt)) {
|
||||
if (LSS_32_BIT(tcb->snd_wl1, seg_seq) || (tcb->snd_wl1 == seg_seq
|
||||
&& LEQ_32_BIT(tcb->snd_wl2, seg_ack))
|
||||
) {
|
||||
tcb->snd_wnd = seg_wnd;
|
||||
tcb->snd_wl1 = seg_seq;
|
||||
tcb->snd_wl2 = seg_ack;
|
||||
|
||||
/* Signal User after Window Update */
|
||||
*notify_owner = true;
|
||||
}
|
||||
}
|
||||
/* Additional processing */
|
||||
/* Check additionaly if previous our sent FIN has been acknowledged */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_1) {
|
||||
if (tcb->pkt_retransmit == NULL) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_FIN_WAIT_2, notify_owner);
|
||||
}
|
||||
}
|
||||
/* If retransmission queue is empty, acknowledge close operation */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_2) {
|
||||
if (tcb->pkt_retransmit == NULL) {
|
||||
/* Optional: Unblock user close operation */
|
||||
}
|
||||
}
|
||||
/* If our FIN has been acknowledged: Translate to TIME_WAIT */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_CLOSING) {
|
||||
if (tcb->pkt_retransmit == NULL) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_TIME_WAIT, notify_owner);
|
||||
}
|
||||
}
|
||||
/* If our FIN has been acknowledged: last ACK received, close connection */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_LAST_ACK) {
|
||||
if (tcb->pkt_retransmit == NULL) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 5) Check URG */
|
||||
/* NOTE: Add Urgent Pointer Processing here ... */
|
||||
/* 6) Process Payload, if existing */
|
||||
if (pay_len > 0) {
|
||||
/* Check if State is valid */
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_1
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_2
|
||||
) {
|
||||
/* Search for begin of payload "chain" */
|
||||
LL_SEARCH_SCALAR(in_pkt, snp, type, GNRC_NETTYPE_UNDEF);
|
||||
|
||||
/* Add only Data that is expected, to be received */
|
||||
if (tcb->rcv_nxt == seg_seq) {
|
||||
/* Copy contents in to buffer */
|
||||
while (snp && snp->type == GNRC_NETTYPE_UNDEF) {
|
||||
tcb->rcv_nxt += ringbuffer_add(&(tcb->rcv_buf), snp->data, snp->size);
|
||||
snp = snp->next;
|
||||
}
|
||||
/* Shrink Receive Window */
|
||||
tcb->rcv_wnd = ringbuffer_get_free(&(tcb->rcv_buf));
|
||||
/* Notify Owner because new data is available */
|
||||
*notify_owner = true;
|
||||
}
|
||||
/* Send pure ACK, if FIN doesn't this already */
|
||||
/* NOTE: this is the place to add piggybagging in the future */
|
||||
if (!(ctl & MSK_FIN)) {
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt,
|
||||
NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 7) Check FIN */
|
||||
if (ctl & MSK_FIN) {
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_CLOSED
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_LISTEN
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_SYN_SENT
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
/* Advance rcv_nxt over FIN bit. */
|
||||
tcb->rcv_nxt = seg_seq + seg_len;
|
||||
_pkt_build(tcb, &out_pkt, &seq_con, MSK_ACK, tcb->snd_nxt, tcb->rcv_nxt, NULL, 0);
|
||||
_pkt_send(tcb, out_pkt, seq_con, false);
|
||||
|
||||
if (tcb->state == GNRC_TCP_FSM_STATE_SYN_RCVD
|
||||
|| tcb->state == GNRC_TCP_FSM_STATE_ESTABLISHED
|
||||
) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSE_WAIT, notify_owner);
|
||||
}
|
||||
else if (tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_1) {
|
||||
if (tcb->pkt_retransmit == NULL) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_TIME_WAIT, notify_owner);
|
||||
}
|
||||
else {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSING, notify_owner);
|
||||
}
|
||||
}
|
||||
else if (tcb->state == GNRC_TCP_FSM_STATE_FIN_WAIT_2) {
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_TIME_WAIT, notify_owner);
|
||||
}
|
||||
else if (tcb->state == GNRC_TCP_FSM_STATE_TIME_WAIT) {
|
||||
_restart_timewait_timer(tcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for timewait timeout handling
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return zero on success.
|
||||
*/
|
||||
static int _fsm_timeout_timewait(gnrc_tcp_tcb_t* tcb, bool *notify_owner)
|
||||
{
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_timeout_timewait()\n");
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for retransmissions
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
*
|
||||
* @return zero on success.
|
||||
*/
|
||||
static int _fsm_timeout_retransmit(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_timeout_retransmit()\n");
|
||||
if(tcb->pkt_retransmit != NULL){
|
||||
_pkt_setup_retransmit(tcb, tcb->pkt_retransmit, true);
|
||||
_pkt_send(tcb, tcb->pkt_retransmit, 0, true);
|
||||
}
|
||||
else {
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_timeout_retransmit() : Retransmit queue is empty\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for connection timeout handling
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return zero on success.
|
||||
*/
|
||||
static int _fsm_timeout_connection(gnrc_tcp_tcb_t* tcb, bool *notify_owner)
|
||||
{
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_timeout_connection()\n");
|
||||
_transition_to(tcb, GNRC_TCP_FSM_STATE_CLOSED, notify_owner);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for probe sending
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
*
|
||||
* @return zero on success.
|
||||
*/
|
||||
static int _fsm_send_probe(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
gnrc_pktsnip_t *out_pkt = NULL; /* Outgoing packet */
|
||||
uint8_t probe_pay[] = { 1 }; /* Probe Payload */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_send_probe()\n");
|
||||
/* The Probe sends a already acknowledged Sequence No. with a garbage byte */
|
||||
_pkt_build(tcb, &out_pkt, NULL, MSK_ACK, tcb->snd_una - 1, tcb->rcv_nxt, probe_pay,
|
||||
sizeof(probe_pay));
|
||||
_pkt_send(tcb, out_pkt, 0, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FSM Handling Function for clearing the retransmit queue.
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
*
|
||||
* @return zero on success.
|
||||
*/
|
||||
static int _fsm_clear_retransmit(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_clear_retransmit()\n");
|
||||
_clear_retransmit(tcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief real fsm: needs to be protected from the outside
|
||||
*
|
||||
* @param[in/out] tcb Specifies tcb to use fsm on.
|
||||
* @param[in] event current event that triggers fsm translation
|
||||
* @param[in] in_pkt packet that triggered fsm event. Only in case of RCVD_PKT
|
||||
* @param[in/out] buf buffer for send and receive functions
|
||||
* @param[in] nByte number of bytes to send or receive atmost
|
||||
* @param[out] notify_owner non-negative if the tcb owner should be notified
|
||||
*
|
||||
* @return TODO zero on success
|
||||
* @return -ENOMEM Can't allocate receive buffer.
|
||||
* @return -EADDRINUSE Given local port is already in use
|
||||
* @return -EOPNOTSUPP If event is not implemented
|
||||
*/
|
||||
static int _fsm_unprotected(gnrc_tcp_tcb_t* tcb, gnrc_tcp_fsm_event_t event,
|
||||
gnrc_pktsnip_t *in_pkt, void *buf, size_t nByte, bool *notify_owner)
|
||||
{
|
||||
int ret = 0; /* Return Value */
|
||||
|
||||
DEBUG("gnrc_tcp_fsm.c : _fsm_unprotected()\n");
|
||||
switch (event) {
|
||||
case GNRC_TCP_FSM_EVENT_CALL_OPEN :
|
||||
ret = _fsm_call_open(tcb, notify_owner);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_CALL_SEND :
|
||||
ret = _fsm_call_send(tcb, buf, nByte);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_CALL_RECV :
|
||||
ret = _fsm_call_recv(tcb, buf, nByte);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_CALL_CLOSE :
|
||||
ret = _fsm_call_close(tcb, notify_owner);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_CALL_ABORT :
|
||||
ret = _fsm_call_abort();
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_RCVD_PKT :
|
||||
ret = _fsm_rcvd_pkt(tcb, in_pkt, notify_owner);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_TIMEOUT_TIMEWAIT :
|
||||
ret = _fsm_timeout_timewait(tcb, notify_owner);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_TIMEOUT_RETRANSMIT :
|
||||
ret = _fsm_timeout_retransmit(tcb);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_TIMEOUT_CONNECTION :
|
||||
ret = _fsm_timeout_connection(tcb, notify_owner);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_SEND_PROBE :
|
||||
ret = _fsm_send_probe(tcb);
|
||||
break;
|
||||
case GNRC_TCP_FSM_EVENT_CLEAR_RETRANSMIT :
|
||||
ret = _fsm_clear_retransmit(tcb);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _fsm(gnrc_tcp_tcb_t* tcb, gnrc_tcp_fsm_event_t event, gnrc_pktsnip_t *in_pkt, void *buf,
|
||||
size_t nByte)
|
||||
{
|
||||
msg_t msg;
|
||||
int32_t result;
|
||||
bool notify_owner;
|
||||
|
||||
/* Lock FSM */
|
||||
mutex_lock(&(tcb->fsm_lock));
|
||||
notify_owner = false;
|
||||
result = _fsm_unprotected(tcb, event, in_pkt, buf, nByte, ¬ify_owner);
|
||||
|
||||
/* Notify owner if something interesting happend */
|
||||
if (notify_owner && tcb->owner != KERNEL_PID_UNDEF) {
|
||||
msg.type = MSG_TYPE_NOTIFY_USER;
|
||||
msg_send(&msg, tcb->owner);
|
||||
}
|
||||
/* Unlock FSM */
|
||||
mutex_unlock(&(tcb->fsm_lock));
|
||||
return result;
|
||||
}
|
90
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_option.c
Normal file
90
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_option.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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
|
||||
* @brief GNRC's TCP option handling related functions
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
#include "assert.h"
|
||||
#include "internal/option.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
uint32_t _option_build_mss(uint16_t mss)
|
||||
{
|
||||
return (((uint32_t )OPT_KIND_MSS) << 24) | (((uint32_t) OPT_LENGTH_MSS) << 16) | mss;
|
||||
}
|
||||
|
||||
uint16_t _option_build_offset_control(uint16_t nopts, uint16_t ctl)
|
||||
{
|
||||
assert(OPTION_OFFSET_BASE <= nopts && nopts <= OPTION_OFFSET_MAX);
|
||||
return (nopts << 12) | ctl;
|
||||
}
|
||||
|
||||
int _option_parse(gnrc_tcp_tcb_t* tcb, tcp_hdr_t *hdr)
|
||||
{
|
||||
uint8_t word_idx = 0;
|
||||
uint8_t byte_idx = 0;
|
||||
uint8_t word_end = 0;
|
||||
uint16_t off_ctl = byteorder_ntohs(hdr->off_ctl);
|
||||
|
||||
word_end = GET_OFFSET(off_ctl) - OPTION_OFFSET_BASE;
|
||||
|
||||
while (word_idx < word_end) {
|
||||
uint32_t word = byteorder_ntohl(hdr->options[word_idx]);
|
||||
|
||||
/* If byte index is not aligned to word index. Fill word with bytes from next word. */
|
||||
if (byte_idx) {
|
||||
word >>= (byte_idx * 8);
|
||||
word |= (byteorder_ntohl(hdr->options[word_idx + 1]) << ((sizeof(word) - byte_idx) * 8));
|
||||
}
|
||||
|
||||
/* Option handling */
|
||||
switch (OPT_GET_KIND(word)) {
|
||||
case OPT_KIND_EOL:
|
||||
DEBUG("gnrc_tcp_option.c : _option_parse() : Option eol\n");
|
||||
return 0;
|
||||
|
||||
case OPT_KIND_NOP:
|
||||
byte_idx += 1;
|
||||
DEBUG("gnrc_tcp_option.c : _option_parse() : Option nop\n");
|
||||
break;
|
||||
|
||||
case OPT_KIND_MSS:
|
||||
DEBUG("gnrc_tcp_option.c : _option_parse() : Option mss\n");
|
||||
if (OPT_GET_LENGTH(word) == OPT_LENGTH_MSS) {
|
||||
tcb->mss = OPT_GET_VAL_2B(word);
|
||||
byte_idx += 4;
|
||||
}
|
||||
else {
|
||||
DEBUG("gnrc_tcp_option.c : _option_parse() : invalid MSS Option length.\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Add options support HERE */
|
||||
default:
|
||||
DEBUG("gnrc_tcp_option.c : _option_parse() : Unsupported option received\n");
|
||||
byte_idx += 1;
|
||||
}
|
||||
|
||||
/* Update index */
|
||||
if (byte_idx >= 4) {
|
||||
word_idx += 1;
|
||||
byte_idx -= 4;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
439
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_pkt.c
Normal file
439
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_pkt.c
Normal file
@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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
|
||||
* @brief GNRC's TCP paket related functions
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <utlist.h>
|
||||
#include <errno.h>
|
||||
#include "msg.h"
|
||||
#include "net/inet_csum.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
#include "internal/pkt.h"
|
||||
#include "internal/helper.h"
|
||||
#include "internal/option.h"
|
||||
#include "internal/eventloop.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* Check if a sequence number, falls into the receive window */
|
||||
#define INSIDE_WND(l_ed, seq_num, r_ed) (LEQ_32_BIT(l_ed, seq_num) && LSS_32_BIT(seq_num, r_ed))
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum of two unsigned numbers
|
||||
*
|
||||
* @param [in] x First comparrison value
|
||||
* @param [in] y Second comparrison value
|
||||
*
|
||||
* @return x if x is larger than y, if not y is returned.
|
||||
*/
|
||||
static inline uint32_t _max(const uint32_t x, const uint32_t y)
|
||||
{
|
||||
return (x > y) ? x : y;
|
||||
}
|
||||
|
||||
int _pkt_build_reset_from_pkt(gnrc_pktsnip_t **out_pkt, gnrc_pktsnip_t *in_pkt)
|
||||
{
|
||||
tcp_hdr_t tcp_hdr_out;
|
||||
|
||||
/* Extract headers */
|
||||
gnrc_pktsnip_t *tcp_snp;
|
||||
LL_SEARCH_SCALAR(in_pkt, tcp_snp, type, GNRC_NETTYPE_TCP);
|
||||
tcp_hdr_t *tcp_hdr_in = (tcp_hdr_t *)tcp_snp->data;
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
gnrc_pktsnip_t *ip6_snp;
|
||||
LL_SEARCH_SCALAR(in_pkt, ip6_snp, type, GNRC_NETTYPE_IPV6);
|
||||
ipv6_hdr_t *ip6_hdr = (ipv6_hdr_t *)ip6_snp->data;
|
||||
#endif
|
||||
|
||||
/* Setup Header information */
|
||||
tcp_hdr_out.src_port = tcp_hdr_in->dst_port;
|
||||
tcp_hdr_out.dst_port = tcp_hdr_in->src_port;
|
||||
tcp_hdr_out.checksum = byteorder_htons(0);
|
||||
tcp_hdr_out.window = byteorder_htons(0);
|
||||
tcp_hdr_out.urgent_ptr = byteorder_htons(0);
|
||||
|
||||
/* Seq/Ackno and control flags depend on inputs ACK-Flag */
|
||||
uint16_t ctl = byteorder_ntohs(tcp_hdr_in->off_ctl);
|
||||
if (ctl & MSK_ACK) {
|
||||
tcp_hdr_out.off_ctl = byteorder_htons((OPTION_OFFSET_BASE << 12) | MSK_RST);
|
||||
tcp_hdr_out.seq_num = tcp_hdr_in->ack_num;
|
||||
tcp_hdr_out.ack_num = byteorder_htonl(0);
|
||||
}
|
||||
else {
|
||||
uint8_t seq_no = 0;
|
||||
tcp_hdr_out.off_ctl = byteorder_htons((OPTION_OFFSET_BASE << 12) | MSK_RST_ACK);
|
||||
tcp_hdr_out.seq_num = byteorder_htonl(0);
|
||||
if (ctl & MSK_SYN) {
|
||||
seq_no += 1;
|
||||
}
|
||||
if (ctl & MSK_FIN) {
|
||||
seq_no += 1;
|
||||
}
|
||||
uint32_t tmp = byteorder_ntohl(tcp_hdr_in->seq_num);
|
||||
tcp_hdr_out.ack_num = byteorder_htonl(seq_no + tmp + _pkt_get_pay_len(in_pkt));
|
||||
}
|
||||
|
||||
/* Allocate new tcp header */
|
||||
tcp_snp = gnrc_pktbuf_add(NULL, &tcp_hdr_out, OPTION_OFFSET_BASE * 4, GNRC_NETTYPE_TCP);
|
||||
if (tcp_snp == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Can't alloc buffer for TCP Header\n.");
|
||||
*(out_pkt) = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
*out_pkt = tcp_snp;
|
||||
|
||||
/* Build new network layer header */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
ip6_snp = gnrc_ipv6_hdr_build(tcp_snp, &(ip6_hdr->dst), &(ip6_hdr->src));
|
||||
if (ip6_snp == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Can't alloc buffer for IPv6 Header.\n");
|
||||
gnrc_pktbuf_release(tcp_snp);
|
||||
*(out_pkt) = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
*out_pkt = ip6_snp;
|
||||
#else
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Network Layer Module Missing\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _pkt_build(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t **out_pkt, uint16_t *seq_con,
|
||||
const uint16_t ctl, const uint32_t seq_num, const uint32_t ack_num,
|
||||
void *payload, const size_t payload_len)
|
||||
{
|
||||
gnrc_pktsnip_t *pay_snp = NULL;
|
||||
gnrc_pktsnip_t *tcp_snp = NULL;
|
||||
tcp_hdr_t tcp_hdr;
|
||||
uint8_t nopts = 0;
|
||||
|
||||
/* Add payload, if supplied */
|
||||
if (payload != NULL && payload_len > 0) {
|
||||
pay_snp = gnrc_pktbuf_add(pay_snp, payload, payload_len, GNRC_NETTYPE_UNDEF);
|
||||
if (pay_snp == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build() : Can't allocate buffer for payload\n.");
|
||||
*(out_pkt) = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill tcp-header */
|
||||
tcp_hdr.src_port = byteorder_htons(tcb->local_port);
|
||||
tcp_hdr.dst_port = byteorder_htons(tcb->peer_port);
|
||||
tcp_hdr.checksum = byteorder_htons(0);
|
||||
tcp_hdr.seq_num = byteorder_htonl(seq_num);
|
||||
tcp_hdr.ack_num = byteorder_htonl(ack_num);
|
||||
tcp_hdr.window = byteorder_htons(tcb->rcv_wnd);
|
||||
tcp_hdr.urgent_ptr = byteorder_htons(0);
|
||||
|
||||
/* tcp option handling */
|
||||
/* If this is a syn-message, send mss option */
|
||||
if (ctl & MSK_SYN) {
|
||||
/* NOTE: MSS usually based on lower layers MTU */
|
||||
tcp_hdr.options[nopts] = byteorder_htonl(_option_build_mss(GNRC_TCP_MSS));
|
||||
nopts += 1;
|
||||
}
|
||||
/* Set offset and control bit accordingly */
|
||||
tcp_hdr.off_ctl = byteorder_htons(_option_build_offset_control(OPTION_OFFSET_BASE + nopts, ctl));
|
||||
|
||||
/* allocate tcp header */
|
||||
tcp_snp = gnrc_pktbuf_add(pay_snp, &tcp_hdr, (OPTION_OFFSET_BASE + nopts) * 4, GNRC_NETTYPE_TCP);
|
||||
if (tcp_snp == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build() : Can't allocate buffer for TCP Header\n.");
|
||||
gnrc_pktbuf_release(pay_snp);
|
||||
*(out_pkt) = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
else {
|
||||
*(out_pkt) = tcp_snp;
|
||||
}
|
||||
|
||||
/* Build network layer header */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
gnrc_pktsnip_t* ip6_snp = gnrc_ipv6_hdr_build(tcp_snp, NULL, (ipv6_addr_t *) tcb->peer_addr);
|
||||
if (ip6_snp == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build() : Can't allocate buffer for IPv6 Header.\n");
|
||||
gnrc_pktbuf_release(tcp_snp);
|
||||
*(out_pkt) = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
else {
|
||||
*(out_pkt) = ip6_snp;
|
||||
}
|
||||
#else
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Network Layer Module Missing\n");
|
||||
#endif
|
||||
|
||||
/* Calculate Sequence Space Number Consumption for this packet */
|
||||
if (seq_con != NULL) {
|
||||
*seq_con = 0;
|
||||
if (ctl & MSK_SYN) {
|
||||
*seq_con += 1;
|
||||
}
|
||||
if (ctl & MSK_FIN) {
|
||||
*seq_con += 1;
|
||||
}
|
||||
*seq_con += payload_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _pkt_send(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t *out_pkt, const uint16_t seq_con,
|
||||
const bool retransmit)
|
||||
{
|
||||
if (out_pkt == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_send() : Packet to send is null\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If this is no retransmission, advance sequence number and measure time */
|
||||
if (!retransmit) {
|
||||
tcb->retries = 0;
|
||||
tcb->snd_nxt += seq_con;
|
||||
tcb->rtt_start = xtimer_now().ticks32;
|
||||
}
|
||||
else {
|
||||
tcb->retries += 1;
|
||||
}
|
||||
|
||||
/* Pass packet down the network stack */
|
||||
gnrc_netapi_send(_gnrc_tcp_pid, out_pkt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _pkt_chk_seq_num(const gnrc_tcp_tcb_t* tcb, const uint32_t seq_num, const uint32_t seg_len)
|
||||
{
|
||||
uint32_t l_edge = tcb->rcv_nxt;
|
||||
uint32_t r_edge = tcb->rcv_nxt + tcb->rcv_wnd;
|
||||
uint32_t last_seq = seq_num + seg_len - 1;
|
||||
|
||||
/* Possible case 1 */
|
||||
/* Segment contains no payload and Receive window is closed and */
|
||||
/* Sequence Number is next expected number */
|
||||
if (seg_len == 0 && tcb->rcv_wnd == 0 && l_edge == seq_num ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Possible case 2 */
|
||||
/* Segment contains no payload and Receive window is open and */
|
||||
/* Sequence number falls inside the receive window */
|
||||
if (seg_len == 0 && tcb->rcv_wnd > 0 && INSIDE_WND(l_edge, seq_num, r_edge)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Possible case 3 */
|
||||
/* Segment contains Payload and Receive window is open and */
|
||||
/* Sequence Number overlaps with receive window */
|
||||
if (seg_len > 0 && tcb->rcv_wnd > 0
|
||||
&& (INSIDE_WND(l_edge, seq_num, r_edge) || INSIDE_WND(l_edge, last_seq, r_edge))
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Everthing else is not acceptable */
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t _pkt_get_seg_len(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
uint32_t seq = 0;
|
||||
uint16_t ctl = 0;
|
||||
gnrc_pktsnip_t *snp = NULL;
|
||||
|
||||
LL_SEARCH_SCALAR(pkt, snp, type, GNRC_NETTYPE_TCP);
|
||||
tcp_hdr_t *hdr = (tcp_hdr_t *) snp->data;
|
||||
ctl = byteorder_ntohs(hdr->off_ctl);
|
||||
seq = _pkt_get_pay_len(pkt);
|
||||
if (ctl & MSK_SYN) {
|
||||
seq += 1;
|
||||
}
|
||||
if (ctl & MSK_FIN) {
|
||||
seq += 1;
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
uint32_t _pkt_get_pay_len(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
uint32_t seg_len = 0;
|
||||
gnrc_pktsnip_t *snp = NULL;
|
||||
|
||||
LL_SEARCH_SCALAR(pkt, snp, type, GNRC_NETTYPE_UNDEF);
|
||||
while (snp && snp->type == GNRC_NETTYPE_UNDEF) {
|
||||
seg_len += snp->size;
|
||||
snp = snp->next;
|
||||
}
|
||||
return seg_len;
|
||||
}
|
||||
|
||||
int _pkt_setup_retransmit(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t *pkt, const bool retransmit)
|
||||
{
|
||||
gnrc_pktsnip_t *snp = NULL;
|
||||
uint32_t ctl = 0;
|
||||
uint32_t len = 0;
|
||||
|
||||
/* No packet received */
|
||||
if (pkt == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_setup_retransmit() : pkt=NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if retransmit queue is full and pkt is not already in retransmit queue */
|
||||
if (tcb->pkt_retransmit != NULL && tcb->pkt_retransmit != pkt) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_setup_retransmit() : Nothing to do\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Extract control bits and segment length */
|
||||
LL_SEARCH_SCALAR(pkt, snp, type, GNRC_NETTYPE_TCP);
|
||||
ctl = byteorder_ntohs(((tcp_hdr_t *) snp->data)->off_ctl);
|
||||
len = _pkt_get_pay_len(pkt);
|
||||
|
||||
/* Check if pkt contains reset or is a pure ACK, return */
|
||||
if ((ctl & MSK_RST) || (((ctl & MSK_SYN_FIN_ACK) == MSK_ACK) && len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assign pkt and increase users: every send attempt consumes a user */
|
||||
tcb->pkt_retransmit = pkt;
|
||||
gnrc_pktbuf_hold(pkt, 1);
|
||||
|
||||
/* RTO Adjustment */
|
||||
if (!retransmit) {
|
||||
/* If this is the first transmission: rto is 1 sec (Lower Bound) */
|
||||
if (tcb->srtt == GNRC_TCP_RTO_UNINITIALIZED || tcb->rtt_var == GNRC_TCP_RTO_UNINITIALIZED) {
|
||||
tcb->rto = GNRC_TCP_RTO_LOWER_BOUND;
|
||||
}
|
||||
else {
|
||||
tcb->rto = tcb->srtt + _max(GNRC_TCP_RTO_GRANULARITY, GNRC_TCP_RTO_K * tcb->rtt_var);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If this is a retransmission: Double the rto (Timer Backoff) */
|
||||
tcb->rto *= 2;
|
||||
|
||||
/* If the transmission has been tried five times, we assume srtt and rtt_var are bogus */
|
||||
/* New measurements must be taken */
|
||||
if (tcb->retries >= 5) {
|
||||
tcb->srtt = GNRC_TCP_RTO_UNINITIALIZED;
|
||||
tcb->rtt_var = GNRC_TCP_RTO_UNINITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform Boundrychecks on current RTO before usage */
|
||||
if (tcb->rto < (int32_t) GNRC_TCP_RTO_LOWER_BOUND) {
|
||||
tcb->rto = GNRC_TCP_RTO_LOWER_BOUND;
|
||||
}
|
||||
else if (tcb->rto > (int32_t) GNRC_TCP_RTO_UPPER_BOUND) {
|
||||
tcb->rto = GNRC_TCP_RTO_UPPER_BOUND;
|
||||
}
|
||||
|
||||
/* Setup retransmission timer, msg to TCP thread with ptr to tcb */
|
||||
tcb->msg_tout.type = MSG_TYPE_RETRANSMISSION;
|
||||
tcb->msg_tout.content.ptr = (void *)tcb;
|
||||
xtimer_set_msg(&tcb->tim_tout, tcb->rto, &tcb->msg_tout, _gnrc_tcp_pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _pkt_acknowledge(gnrc_tcp_tcb_t* tcb, const uint32_t ack)
|
||||
{
|
||||
uint32_t seg = 0;
|
||||
gnrc_pktsnip_t *snp = NULL;
|
||||
tcp_hdr_t *hdr;
|
||||
|
||||
/* Retransmission Queue is empty. Nothing to ACK there */
|
||||
if (tcb->pkt_retransmit == NULL) {
|
||||
DEBUG("gnrc_tcp_pkt.c : _pkt_acknowledge() : There is no packet to ack\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
LL_SEARCH_SCALAR(tcb->pkt_retransmit, snp, type, GNRC_NETTYPE_TCP);
|
||||
hdr = (tcp_hdr_t *) snp->data;
|
||||
|
||||
/* There must be a packet, waiting to be acknowledged. */
|
||||
seg = byteorder_ntohl(hdr->seq_num) + _pkt_get_seg_len(tcb->pkt_retransmit) - 1;
|
||||
|
||||
/* If segment can be acknowledged -> stop timer, release packet from pktbuf and update rto. */
|
||||
if (LSS_32_BIT(seg, ack)) {
|
||||
xtimer_remove(&(tcb->tim_tout));
|
||||
gnrc_pktbuf_release(tcb->pkt_retransmit);
|
||||
tcb->pkt_retransmit = NULL;
|
||||
|
||||
/* Measure Round Trip Time */
|
||||
int32_t rtt = xtimer_now().ticks32 - tcb->rtt_start;
|
||||
|
||||
/* Use sample only if ther was no timeroverflow and no retransmission (Karns Alogrithm) */
|
||||
if (tcb->retries == 0 && rtt > 0) {
|
||||
/* If this is the first sample taken */
|
||||
if (tcb->srtt == GNRC_TCP_RTO_UNINITIALIZED
|
||||
&& tcb->rtt_var == GNRC_TCP_RTO_UNINITIALIZED
|
||||
) {
|
||||
tcb->srtt = rtt;
|
||||
tcb->rtt_var = (rtt >> 1);
|
||||
}
|
||||
/* If this is a subsequent sample */
|
||||
else {
|
||||
tcb->rtt_var = (tcb->rtt_var / GNRC_TCP_RTO_B_DIV) * (GNRC_TCP_RTO_B_DIV-1);
|
||||
tcb->rtt_var += abs(tcb->srtt - rtt) / GNRC_TCP_RTO_B_DIV;
|
||||
tcb->srtt = (tcb->srtt / GNRC_TCP_RTO_A_DIV) * (GNRC_TCP_RTO_A_DIV-1);
|
||||
tcb->srtt += rtt / GNRC_TCP_RTO_A_DIV;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t _pkt_calc_csum(const gnrc_pktsnip_t *hdr,
|
||||
const gnrc_pktsnip_t *pseudo_hdr,
|
||||
const gnrc_pktsnip_t *payload)
|
||||
{
|
||||
uint16_t csum = 0;
|
||||
uint16_t len = (uint16_t) hdr->size;
|
||||
|
||||
if(pseudo_hdr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process payload */
|
||||
while (payload && payload != hdr) {
|
||||
csum = inet_csum(csum, (uint8_t *)payload->data, payload->size);
|
||||
len += (uint16_t)payload->size;
|
||||
payload = payload->next;
|
||||
}
|
||||
|
||||
/* Process tcp-header, before checksum field(Byte 16 to 18) */
|
||||
csum = inet_csum(csum, (uint8_t *)hdr->data, 16);
|
||||
/* Process tcp-header, after checksum field */
|
||||
csum = inet_csum(csum, ((uint8_t *)hdr->data) + 18, hdr->size - 18);
|
||||
|
||||
/* Process Network layer Header */
|
||||
switch (pseudo_hdr->type) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case GNRC_NETTYPE_IPV6:
|
||||
csum = ipv6_hdr_inet_csum(csum, pseudo_hdr->data, PROTNUM_TCP, len);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return ~csum;
|
||||
}
|
||||
|
||||
/* Cleanup, defines */
|
||||
#undef INSIDE_WND
|
83
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_rcvbuf.c
Normal file
83
sys/net/gnrc/transport_layer/tcp/gnrc_tcp_rcvbuf.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Simon Brummer <brummer.simon@googlemail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of tcp_internal/rcvbuf.h
|
||||
*
|
||||
* @author Brummer Simon <brummer.simon@googlemail.com>
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include "internal/rcvbuf.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
rcvbuf_t _static_buf; /**< Staticly allocated receive buffers */
|
||||
|
||||
void _rcvbuf_init(void)
|
||||
{
|
||||
DEBUG("gnrc_tcp_rcvbuf.c : _rcvbuf_init() : Entry\n");
|
||||
mutex_init(&(_static_buf.lock));
|
||||
for (int i=0; i<GNRC_TCP_RCV_BUFFERS; i++) {
|
||||
_static_buf.entries[i].used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void* _rcvbuf_alloc(void)
|
||||
{
|
||||
void *result = NULL;
|
||||
DEBUG("gnrc_tcp_rcvbuf.c : _rcvbuf_alloc() : Entry\n");
|
||||
mutex_lock(&(_static_buf.lock));
|
||||
for (int i=0; i<GNRC_TCP_RCV_BUFFERS; i++) {
|
||||
if (_static_buf.entries[i].used == 0) {
|
||||
_static_buf.entries[i].used = 1;
|
||||
result = (void *)(_static_buf.entries[i].buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&(_static_buf.lock));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void _rcvbuf_free(void * const buf)
|
||||
{
|
||||
DEBUG("gnrc_tcp_rcvbuf.c : _rcvbuf_free() : Entry\n");
|
||||
mutex_lock(&(_static_buf.lock));
|
||||
for (int i=0; i<GNRC_TCP_RCV_BUFFERS; i++) {
|
||||
if (_static_buf.entries[i].used == 1 && buf == _static_buf.entries[i].buffer) {
|
||||
_static_buf.entries[i].used = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&(_static_buf.lock));
|
||||
}
|
||||
|
||||
int _rcvbuf_get_buffer(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
if (tcb->rcv_buf_raw == NULL) {
|
||||
tcb->rcv_buf_raw = _rcvbuf_alloc();
|
||||
if (tcb->rcv_buf_raw == NULL) {
|
||||
DEBUG("gnrc_tcp_rcvbuf.c : _rcvbuf_get_buffer() : Can't allocate rcv_buf_raw\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
else {
|
||||
ringbuffer_init(&tcb->rcv_buf, (char *) tcb->rcv_buf_raw, GNRC_TCP_RCV_BUF_SIZE);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _rcvbuf_release_buffer(gnrc_tcp_tcb_t* tcb)
|
||||
{
|
||||
if (tcb->rcv_buf_raw != NULL) {
|
||||
_rcvbuf_free(tcb->rcv_buf_raw);
|
||||
tcb->rcv_buf_raw = NULL;
|
||||
}
|
||||
}
|
48
sys/net/gnrc/transport_layer/tcp/internal/eventloop.h
Normal file
48
sys/net/gnrc/transport_layer/tcp/internal/eventloop.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definition for gnrc tcp event processing loop
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_INTERNAL_EVENTLOOP_H_
|
||||
#define GNRC_TCP_INTERNAL_EVENTLOOP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PID of tcp event handling thread
|
||||
*/
|
||||
extern kernel_pid_t _gnrc_tcp_pid;
|
||||
|
||||
/**
|
||||
* @brief TCP's mein processing thread.
|
||||
*
|
||||
* @param[in] arg arguments, unused
|
||||
*
|
||||
* @return Never returns, its an endless loop
|
||||
*/
|
||||
void *_event_loop(__attribute__((unused)) void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_INTERNAL_EVENTLOOP_H_ */
|
||||
/** @} */
|
61
sys/net/gnrc/transport_layer/tcp/internal/fsm.h
Normal file
61
sys/net/gnrc/transport_layer/tcp/internal/fsm.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definies function to manipulate a connections state
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_INTERNAL_FSM_H_
|
||||
#define GNRC_TCP_INTERNAL_FSM_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
#include "net/gnrc/tcp/fsm.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PID of tcp event handling thread
|
||||
*/
|
||||
extern kernel_pid_t _gnrc_tcp_pid;
|
||||
|
||||
/**
|
||||
* @brief TCP finite state maschine
|
||||
*
|
||||
* @param[in,out] tcb specifies connection to use fsm on.
|
||||
* @param[in] event current event that triggers fsm translation
|
||||
* @param[in] in_pkt packet that triggered fsm event. Only in case of RCVD_PKT
|
||||
* @param[in,out] buf buffer for send and receive functions
|
||||
* @param[in] nByte number of bytes to send or receive atmost
|
||||
*
|
||||
* @return Zero on success
|
||||
* @return Positive Number, number of bytes sent from or copied into buf.
|
||||
* @return -ENOSYS if event is not implemented
|
||||
*/
|
||||
int _fsm(gnrc_tcp_tcb_t* tcb, gnrc_tcp_fsm_event_t event, gnrc_pktsnip_t *in_pkt, void *buf,
|
||||
size_t nByte);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_INTERNAL_FSM_H_ */
|
||||
/** @} */
|
79
sys/net/gnrc/transport_layer/tcp/internal/helper.h
Normal file
79
sys/net/gnrc/transport_layer/tcp/internal/helper.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Helperfunctions and defines
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_INTERNAL_HELPER_H_
|
||||
#define GNRC_TCP_INTERNAL_HELPER_H_
|
||||
|
||||
#include "net/gnrc/netapi.h"
|
||||
#include "net/gnrc/tcp/hdr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Bitmasks for control bit handling
|
||||
* @{
|
||||
*/
|
||||
#define MSK_FIN 0x0001
|
||||
#define MSK_SYN 0x0002
|
||||
#define MSK_RST 0x0004
|
||||
#define MSK_PSH 0x0008
|
||||
#define MSK_ACK 0x0010
|
||||
#define MSK_URG 0x0020
|
||||
#define MSK_FIN_ACK 0x0011
|
||||
#define MSK_SYN_ACK 0x0012
|
||||
#define MSK_RST_ACK 0x0014
|
||||
#define MSK_SYN_FIN_ACK 0x0013
|
||||
#define MSK_FIN_ACK_PSH 0x0019
|
||||
#define MSK_CTL 0x003F
|
||||
#define MSK_OFFSET 0xF000
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Type field values for TCP internal Message Passing.
|
||||
* @{
|
||||
*/
|
||||
#define MSG_TYPE_CONNECTION_TIMEOUT (GNRC_NETAPI_MSG_TYPE_ACK + 101)
|
||||
#define MSG_TYPE_PROBE_TIMEOUT (GNRC_NETAPI_MSG_TYPE_ACK + 102)
|
||||
#define MSG_TYPE_USER_SPEC_TIMEOUT (GNRC_NETAPI_MSG_TYPE_ACK + 103)
|
||||
#define MSG_TYPE_RETRANSMISSION (GNRC_NETAPI_MSG_TYPE_ACK + 104)
|
||||
#define MSG_TYPE_TIMEWAIT (GNRC_NETAPI_MSG_TYPE_ACK + 105)
|
||||
#define MSG_TYPE_NOTIFY_USER (GNRC_NETAPI_MSG_TYPE_ACK + 106)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Overflow tolerant comparision operators for sequence and
|
||||
acknowledgement number comparision
|
||||
* @{
|
||||
*/
|
||||
#define LSS_32_BIT(x, y) (((int32_t) (x)) - ((int32_t) (y)) < 0)
|
||||
#define LEQ_32_BIT(x, y) (((int32_t) (x)) - ((int32_t) (y)) <= 0)
|
||||
#define GRT_32_BIT(x, y) (!LEQ_32_BIT(x, y))
|
||||
#define GEQ_32_BIT(x, y) (!LSS_32_BOT(x, y))
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_INTERNAL_HELPER_H_ */
|
||||
/** @} */
|
112
sys/net/gnrc/transport_layer/tcp/internal/option.h
Normal file
112
sys/net/gnrc/transport_layer/tcp/internal/option.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Defines and Macros for TCP option handling
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_INTERNAL_OPTION_H_
|
||||
#define GNRC_TCP_INTERNAL_OPTION_H_
|
||||
|
||||
#include "helper.h"
|
||||
#include "net/gnrc/tcp.h"
|
||||
#include "net/gnrc/tcp/hdr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TCP Option field boundries
|
||||
* @{
|
||||
*/
|
||||
#define OPTION_OFFSET_BASE (0x5)
|
||||
#define OPTION_OFFSET_MAX (0xF)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Extract offset value from offet and ctl bit field.
|
||||
*/
|
||||
#define GET_OFFSET( x ) (((x) & MSK_OFFSET) >> 12)
|
||||
|
||||
/**
|
||||
* @brief TCP Option Kind Field Values
|
||||
* @{
|
||||
*/
|
||||
#define OPT_KIND_EOL (00) /**< End of List */
|
||||
#define OPT_KIND_NOP (01) /**< No Operatrion */
|
||||
#define OPT_KIND_MSS (02) /**< Maximum Segment Size */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief TCP Option Length Field Values
|
||||
* @{
|
||||
*/
|
||||
#define OPT_LENGTH_MSS (04) /**< MSS Option Size is 4 byte */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Extract kind field value from 4-byte option field
|
||||
*/
|
||||
#define OPT_GET_KIND( x ) (((x) & 0xFF000000) >> 24)
|
||||
|
||||
/**
|
||||
* @brief Extract length field value from 4-byte option field
|
||||
*/
|
||||
#define OPT_GET_LENGTH( x ) (((x) & 0x00FF0000) >> 16)
|
||||
|
||||
/**
|
||||
* @brief Extract two byte option value from 4-byte option field
|
||||
*/
|
||||
#define OPT_GET_VAL_2B( x ) (((x) & 0x0000FFFF))
|
||||
|
||||
/**
|
||||
* @brief Helper Function to build the MSS Option
|
||||
*
|
||||
* @param[in] mss tcp header to be checked
|
||||
*
|
||||
* @return Valid MSS Option.
|
||||
*/
|
||||
uint32_t _option_build_mss(uint16_t mss);
|
||||
|
||||
/**
|
||||
* @brief Helper Function to build the combined option and control flag field
|
||||
*
|
||||
* @param[in] nopts Number of Options
|
||||
* @param[in] ctl Control Flags
|
||||
*
|
||||
* @return Valid option size and control field.
|
||||
*/
|
||||
uint16_t _option_build_offset_control(uint16_t nopts, uint16_t ctl);
|
||||
|
||||
/**
|
||||
* @brief Parses options of a given tcp-header pktsnip.
|
||||
*
|
||||
* @param[out] tcb transmission control block to memorize options.
|
||||
* @param[in] hdr tcp header to be checked
|
||||
*
|
||||
* @return Zero on success
|
||||
* @return A negative value on error
|
||||
*/
|
||||
int _option_parse(gnrc_tcp_tcb_t* tcb, tcp_hdr_t *hdr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_INTERNAL_OPTION_H_*/
|
||||
/** @} */
|
150
sys/net/gnrc/transport_layer/tcp/internal/pkt.h
Normal file
150
sys/net/gnrc/transport_layer/tcp/internal/pkt.h
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Functions for TCP's paket handling
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef GNRC_TCP_INTERNAL_PKT_H_
|
||||
#define GNRC_TCP_INTERNAL_PKT_H_
|
||||
|
||||
#include "net/conn/tcp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Build a reset packet from an incomming packet.
|
||||
*
|
||||
* This function builds a reset from an incomming packet
|
||||
* for cases where the connection has not been established
|
||||
*
|
||||
* @param[out] out_pkt outgoing reset packet
|
||||
* @param[in] in_pkt incomming packet
|
||||
*
|
||||
* @return Zero on success
|
||||
* @return -ENOMEM if pktbuf is full.
|
||||
*/
|
||||
int _pkt_build_reset_from_pkt(gnrc_pktsnip_t **out_pkt, gnrc_pktsnip_t *in_pkt);
|
||||
|
||||
/**
|
||||
* @brief Build and allocate a tcp paket in paketbuffer, conn stores pointer to new paket.
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[out] out_pkt Pointer to paket to build
|
||||
* @param[out] seq_con Number of Bytes, the packet will consume in sequence number spce
|
||||
* @param[in] ctl control bits to set in pkt
|
||||
* @param[in] seq_num sequence number to use in new paket
|
||||
* @param[in] ack_num acknowledgment number to use in new paket
|
||||
* @param[in] payload pointer to payload buffer
|
||||
* @param[in] payload_len payload size
|
||||
*
|
||||
* @return Zero on success.
|
||||
* @return -ENOMEM if pktbuf is full.
|
||||
*/
|
||||
int _pkt_build(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t **out_pkt, uint16_t *seq_con,
|
||||
const uint16_t ctl, const uint32_t seq_num, const uint32_t ack_num,
|
||||
void *payload, const size_t payload_len);
|
||||
|
||||
/**
|
||||
* @brief Sends a packet to the peer
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] out_pkt pointer to paket to send
|
||||
* @param[in] seq_con sequence number consumption of the paket to send
|
||||
* @param[in] retransmit is this a retransmission ?
|
||||
*
|
||||
* @return Zero on success.
|
||||
* @return -EINVAL if out_pkt was NULL
|
||||
*/
|
||||
int _pkt_send(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t *out_pkt, const uint16_t seq_con,
|
||||
const bool retransmit);
|
||||
|
||||
/**
|
||||
* @brief Checks sequence number
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] seq_num sequence number from the segment
|
||||
* @param[in] seg_len length of a segments payload
|
||||
*
|
||||
* @return Zero if the sequence number is invalid
|
||||
* @return Non-zero if the sequence number is acceptable
|
||||
*/
|
||||
int _pkt_chk_seq_num(const gnrc_tcp_tcb_t* tcb, const uint32_t seq_num, const uint32_t seg_len);
|
||||
|
||||
/**
|
||||
* @brief Extracts the length of a segment
|
||||
*
|
||||
* @param[in] pkt Packet to calculate payload length
|
||||
*
|
||||
* @return number consumption in sequence number space
|
||||
*/
|
||||
uint32_t _pkt_get_seg_len(gnrc_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Calculates a segments payload length
|
||||
*
|
||||
* @param[in] pkt Packet to calculate payload length
|
||||
*
|
||||
* @return the segments payload length in bytes
|
||||
*/
|
||||
uint32_t _pkt_get_pay_len(gnrc_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Adds a paket to the retransmission mechanism
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] pkt paket to add to the retransmission mechanism
|
||||
* @param[in] retransmit is this a retransmission ?
|
||||
*
|
||||
* @return Zero on success
|
||||
* @return -ENOMEM if the retransmission queue is full
|
||||
* @return -EINVAL if pkt is null
|
||||
*/
|
||||
int _pkt_setup_retransmit(gnrc_tcp_tcb_t* tcb, gnrc_pktsnip_t *pkt, const bool retransmit);
|
||||
|
||||
/**
|
||||
* @brief Acknowledges and removes packet from the retransmission mechanism
|
||||
*
|
||||
* @param[in,out] tcb This connections Transmission control block.
|
||||
* @param[in] ack Acknowldegment number used to acknowledge packets
|
||||
*
|
||||
* @return Zero on success
|
||||
* @return -ENODATA if there is nothing to acknowledge
|
||||
*/
|
||||
int _pkt_acknowledge(gnrc_tcp_tcb_t* tcb, const uint32_t ack);
|
||||
|
||||
/**
|
||||
* @brief Calculates checksum over payload, tcp-header and network layer header
|
||||
*
|
||||
* @param[in] hdr gnrc_pktsnip_t to tcp-header
|
||||
* @param[in] pseudo_hdr gnrc_pktsnip_t to network layer header
|
||||
* @param[in] payload gnrc_pktsnip_t to payload
|
||||
*
|
||||
* @return non zero checksum if given network layer is supported
|
||||
* @return zero if given network layer is not supported
|
||||
*/
|
||||
uint16_t _pkt_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_hdr,
|
||||
const gnrc_pktsnip_t *payload);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_INTERNAL_PKT_H_ */
|
||||
/** @} */
|
81
sys/net/gnrc/transport_layer/tcp/internal/rcvbuf.h
Normal file
81
sys/net/gnrc/transport_layer/tcp/internal/rcvbuf.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Simon Brummer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_tcp TCP
|
||||
* @ingroup net_gnrc
|
||||
* @brief RIOT's tcp implementation for the gnrc stack
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Functions for allocating and freeing the receive buffer
|
||||
*
|
||||
* @author Simon Brummer <brummer.simon@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
#ifndef GNRC_TCP_INTERNAL_RCVBUF_H_
|
||||
#define GNRC_TCP_INTERNAL_RCVBUF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mutex.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "net/gnrc/tcp/config.h"
|
||||
#include "net/gnrc/tcp/tcb.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Struct for a single connections receive buffer
|
||||
* @internal
|
||||
*/
|
||||
typedef struct rcvbuf_entry {
|
||||
uint8_t used; /**< Is entry currently in use */
|
||||
uint8_t buffer[GNRC_TCP_RCV_BUF_SIZE]; /**< Raw Buffer Data */
|
||||
} rcvbuf_entry_t;
|
||||
|
||||
/**
|
||||
* @brief Stuct holding receive buffers
|
||||
* @internal
|
||||
*/
|
||||
typedef struct rcvbuf {
|
||||
mutex_t lock; /**< Lock for synchronization */
|
||||
rcvbuf_entry_t entries[GNRC_TCP_RCV_BUFFERS]; /**< Number of receive buffers */
|
||||
} rcvbuf_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes global receive Buffer
|
||||
* @internal
|
||||
*/
|
||||
void _rcvbuf_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initializes and assigns receive Buffer to tcb.
|
||||
*
|
||||
* @param[in] tcb Transmission control block that should hold the buffer.
|
||||
*
|
||||
* @return zero on success
|
||||
* @return -ENOMEM If receive buffer is out of memory.
|
||||
*/
|
||||
int _rcvbuf_get_buffer(gnrc_tcp_tcb_t* tcb);
|
||||
|
||||
/**
|
||||
* @brief Free allocated receive buffer
|
||||
*
|
||||
* @param[in] tcb Transmission control block that buffer should be freed.
|
||||
*/
|
||||
void _rcvbuf_release_buffer(gnrc_tcp_tcb_t* tcb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GNRC_TCP_INTERNAL_RCVBUF_H_ */
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user