mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #16459 from brummer-simon/gnrc_tcp-rewrite_passive_open
gnrc_tcp: rewrite passive open
This commit is contained in:
commit
79ee4fd489
@ -17,13 +17,17 @@
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
|
||||
#define TCB_QUEUE_SIZE 1
|
||||
#define ACCEPT_TIMEOUT_MS 60000
|
||||
|
||||
static uint32_t demux = GNRC_NETREG_DEMUX_CTX_ALL;
|
||||
static gnrc_nettype_t ntype = GNRC_NETTYPE_TCP;
|
||||
static gnrc_tcp_tcb_queue_t queue = GNRC_TCP_TCB_QUEUE_INIT;
|
||||
static gnrc_tcp_tcb_t tcbs[TCB_QUEUE_SIZE];
|
||||
|
||||
static void *tcploop(void *arg)
|
||||
{
|
||||
mutex_t *tcpmtx = arg;
|
||||
gnrc_tcp_tcb_t tcb;
|
||||
gnrc_tcp_ep_t ep;
|
||||
|
||||
if (gnrc_tcp_ep_from_str(&ep, "[" SERVER_ADDR "]")) {
|
||||
@ -31,16 +35,26 @@ static void *tcploop(void *arg)
|
||||
}
|
||||
ep.port = SERVER_PORT;
|
||||
|
||||
for (;;) {
|
||||
gnrc_tcp_tcb_init(&tcb);
|
||||
mutex_unlock(tcpmtx);
|
||||
for (unsigned i = 0; i < TCB_QUEUE_SIZE; ++i) {
|
||||
gnrc_tcp_tcb_init(&tcbs[i]);
|
||||
}
|
||||
mutex_unlock(tcpmtx);
|
||||
|
||||
int ret = gnrc_tcp_open_passive(&tcb, &ep);
|
||||
if (!ret) {
|
||||
errx(EXIT_FAILURE, "gnrc_tcp_open_passive failed: %d\n", ret);
|
||||
int ret = gnrc_tcp_listen(&queue, tcbs, ARRAY_SIZE(tcbs), &ep);
|
||||
if (ret) {
|
||||
errx(EXIT_FAILURE, "gnrc_tcp_listen failed: %d\n", ret);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
gnrc_tcp_tcb_t *tcp = NULL;
|
||||
ret = gnrc_tcp_accept(&queue, &tcp, ACCEPT_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
errx(EXIT_FAILURE, "gnrc_tcp_accept failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Never reached but, for clean programming sake */
|
||||
gnrc_tcp_stop_listen(&queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -102,57 +102,78 @@ int gnrc_tcp_init(void);
|
||||
void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb);
|
||||
|
||||
/**
|
||||
* @brief Opens a connection actively.
|
||||
* @brief Opens a connection.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre @p tcb must not be NULL
|
||||
* @pre @p remote must not be NULL.
|
||||
* @pre @p remote->port must not be 0.
|
||||
*
|
||||
* @note Blocks until a connection has been established or an error occurred.
|
||||
* @note Blocks until a connection was established or an error occurred.
|
||||
*
|
||||
* @param[in,out] tcb TCB holding the connection information.
|
||||
* @param[in] remote Remote endpoint of the host to connect to.
|
||||
* @param[in,out] tcb TCB for this connection.
|
||||
* @param[in] remote Remote endpoint to connect to.
|
||||
* @param[in] local_port If zero or PORT_UNSPEC, the connections source port
|
||||
* is randomly chosen. If local_port is non-zero
|
||||
* the local_port is used as source port.
|
||||
* is randomly selected. If local_port is non-zero
|
||||
* it is used as source port.
|
||||
*
|
||||
* @return 0 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 -EAFNOSUPPORT if @p remote address_family is not supported.
|
||||
* @return -EINVAL if @p remote and @p tcb address_family do not match
|
||||
* or @p target_addr is invalid.
|
||||
* @return -EISCONN if TCB is already in use.
|
||||
* @return -ENOMEM if the receive buffer for the TCB could not be allocated.
|
||||
* @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 reset by the peer.
|
||||
* @return -EISCONN if @p tcb is already connected.
|
||||
* @return -ENOMEM if there are no receive buffer left to use for @p tcb.
|
||||
* @return -EADDRINUSE if @p local_port is already in use.
|
||||
* @return -ETIMEDOUT if the connection attempt timed out.
|
||||
* @return -ECONNREFUSED if the connection attempt was reset by the peer.
|
||||
*/
|
||||
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote,
|
||||
uint16_t local_port);
|
||||
int gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote, uint16_t local_port);
|
||||
|
||||
/**
|
||||
* @brief Opens a connection passively, by waiting for an incoming request.
|
||||
* @brief Configures a sequence of TCBs to wait for incoming connections.
|
||||
*
|
||||
* @pre gnrc_tcp_tcb_init() must have been successfully called.
|
||||
* @pre @p tcb must not be NULL.
|
||||
* @pre @p local must not be NULL.
|
||||
* @pre port in @p local must not be zero.
|
||||
* @pre All TCBs behind @p tcbs must have been initialized via gnrc_tcp_tcb_init().
|
||||
* @pre @p queue must not be NULL.
|
||||
* @pre @p tcbs must not be NULL.
|
||||
* @pre @p tcbs_len must be greater 0.
|
||||
* @pre @p local len must be NULL.
|
||||
* @pre @p local->port must not be 0.
|
||||
*
|
||||
* @note Blocks until a connection has been established (incoming connection request
|
||||
* to @p local_port) or an error occurred.
|
||||
* @param[in,out] queue Listening queue for incoming connections.
|
||||
* @param[in] tcbs TCBs associated with @p queue.
|
||||
* @param[in] tcbs_len Number of TCBs behind @p tcbs.
|
||||
* @param[in] local Endpoint specifying address and port to listen on.
|
||||
*
|
||||
* @param[in,out] tcb TCB holding the connection information.
|
||||
* @param[in] local Endpoint specifying the port and address used to wait for
|
||||
* incoming connections.
|
||||
*
|
||||
* @return 0 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 used in TCB.
|
||||
* or the address in @p local is invalid.
|
||||
* @return -EISCONN if TCB is already in use.
|
||||
* @return -ENOMEM if the receive buffer for the TCB could not be allocated.
|
||||
* Hint: Increase "CONFIG_GNRC_TCP_RCV_BUFFERS".
|
||||
* @returns 0 on success.
|
||||
* @return -EAFNOSUPPORT given address family in @p local is not supported.
|
||||
* @return -EINVAL address_family in @p tcbs and @p local do not match.
|
||||
* @return -EISCONN a TCB in @p tcbs is already connected.
|
||||
* @return -ENOMEM all available receive buffers are in use.
|
||||
* Increase GNRC_TCP_RCV_BUFFERS.
|
||||
*/
|
||||
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *local);
|
||||
int gnrc_tcp_listen(gnrc_tcp_tcb_queue_t *queue, gnrc_tcp_tcb_t *tcbs, size_t tcbs_len,
|
||||
const gnrc_tcp_ep_t *local);
|
||||
|
||||
/**
|
||||
* @brief Accept TCP connection from listening queue.
|
||||
*
|
||||
* @pre @p queue must not be NULL
|
||||
* @pre @p tcb must not be NULL
|
||||
*
|
||||
* @note Function blocks if user_timeout_duration_us is not zero.
|
||||
*
|
||||
* @param[in] queue Listening queue to accept connection from.
|
||||
* @param[out] tcb Pointer to TCB associated with a established connection.
|
||||
* @param[in] user_timeout_duration_ms User specified timeout in milliseconds.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -ENOMEM if all connection in @p queue were already accepted.
|
||||
* @return -EAGAIN if @p user_timeout_duration_ms was 0 and no connection is ready to accept.
|
||||
* @return -ETIMEDOUT if @p user_timeout_duration_ms was not 0 and no connection
|
||||
* could be established.
|
||||
*/
|
||||
int gnrc_tcp_accept(gnrc_tcp_tcb_queue_t *queue, gnrc_tcp_tcb_t **tcb,
|
||||
uint32_t user_timeout_duration_ms);
|
||||
|
||||
/**
|
||||
* @brief Transmit data to connected peer.
|
||||
@ -230,6 +251,18 @@ void gnrc_tcp_close(gnrc_tcp_tcb_t *tcb);
|
||||
*/
|
||||
void gnrc_tcp_abort(gnrc_tcp_tcb_t *tcb);
|
||||
|
||||
/**
|
||||
* @brief Close connections and stop listening on TCB queue
|
||||
*
|
||||
* @pre @p queue must not be NULL
|
||||
*
|
||||
* @note: Blocks until all currently opened connections maintained
|
||||
* by @p queue were closed.
|
||||
*
|
||||
* @param[in,out] queue TCB queue to stop listening
|
||||
*/
|
||||
void gnrc_tcp_stop_listen(gnrc_tcp_tcb_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Calculate and set checksum in TCP header.
|
||||
*
|
||||
|
@ -68,6 +68,7 @@ typedef struct _transmission_control_block {
|
||||
int32_t rto; /**< Retransmission timeout duration */
|
||||
uint8_t retries; /**< Number of retransmissions */
|
||||
evtimer_msg_event_t event_retransmit; /**< Retransmission event */
|
||||
evtimer_msg_event_t event_timeout; /**< Timeout event */
|
||||
evtimer_mbox_event_t event_misc; /**< General purpose event */
|
||||
gnrc_pktsnip_t *pkt_retransmit; /**< Pointer to packet in "retransmit queue" */
|
||||
mbox_t *mbox; /**< TCB mbox for synchronization */
|
||||
@ -78,6 +79,20 @@ typedef struct _transmission_control_block {
|
||||
struct _transmission_control_block *next; /**< Pointer next TCB */
|
||||
} gnrc_tcp_tcb_t;
|
||||
|
||||
/**
|
||||
* @brief Transmission control block queue.
|
||||
*/
|
||||
typedef struct _transmission_control_block_queue {
|
||||
mutex_t lock; /**< Mutex for access synchronization */
|
||||
gnrc_tcp_tcb_t *tcbs; /**< Pointer to TCB sequence */
|
||||
size_t tcbs_len; /**< Number of TCBs behind member tcbs */
|
||||
} gnrc_tcp_tcb_queue_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for type gnrc_tcp_tcb_queue_t
|
||||
*/
|
||||
#define GNRC_TCP_TCB_QUEUE_INIT { MUTEX_INIT, NULL, 0 }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -32,8 +32,8 @@ extern "C" {
|
||||
* @brief TCP offset value boundaries.
|
||||
* @{
|
||||
*/
|
||||
#define TCP_HDR_OFFSET_MIN (0x05)
|
||||
#define TCP_HDR_OFFSET_MAX (0x0F)
|
||||
#define TCP_HDR_OFFSET_MIN (0x05) /**< Header offset minimum value */
|
||||
#define TCP_HDR_OFFSET_MAX (0x0F) /**< Header offset maximum value */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -49,7 +49,7 @@ extern "C" {
|
||||
* @brief TCP option "length"-field values.
|
||||
* @{
|
||||
*/
|
||||
#define TCP_OPTION_LENGTH_MIN (2U) /**< Minimum amount of bytes needed for an option with a length field */
|
||||
#define TCP_OPTION_LENGTH_MIN (2U) /**< Minimum option field size in bytes */
|
||||
#define TCP_OPTION_LENGTH_MSS (0x04) /**< MSS Option Size always 4 */
|
||||
/** @} */
|
||||
|
||||
|
@ -74,144 +74,43 @@ static void _unsched_mbox(evtimer_mbox_event_t *event)
|
||||
TCP_DEBUG_LEAVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Establishes a new TCP connection
|
||||
*
|
||||
* @param[in,out] tcb TCB holding the connection information.
|
||||
* @param[in] target_addr Target address to connect to, if this is a active connection.
|
||||
* @param[in] target_port Target port to connect to, if this is a active connection.
|
||||
* @param[in] local_addr Local address to bind on, if this is a passive connection.
|
||||
* @param[in] local_port Local port to bind on, if this is a passive connection.
|
||||
* @param[in] passive Flag to indicate if this is a active or passive open.
|
||||
*
|
||||
* @returns Zero on success.
|
||||
* -EISCONN if TCB is already connected.
|
||||
* -ENOMEM if the receive buffer for the TCB could not be allocated.
|
||||
* -EADDRINUSE if @p local_port is already in use.
|
||||
* -ETIMEDOUT if the connection opening timed out.
|
||||
* -ECONNREFUSED if the connection was reset by the peer.
|
||||
*/
|
||||
static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote,
|
||||
const uint8_t *local_addr, uint16_t local_port, int passive)
|
||||
static void _close(gnrc_tcp_tcb_t *tcb)
|
||||
{
|
||||
TCP_DEBUG_ENTER;
|
||||
|
||||
msg_t msg;
|
||||
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
||||
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
||||
int ret = 0;
|
||||
_gnrc_tcp_fsm_state_t state = 0;
|
||||
|
||||
/* Lock the TCB for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* TCB is already connected: Return -EISCONN */
|
||||
/* Return if connection is closed */
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
if (state != FSM_STATE_CLOSED) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
TCP_DEBUG_ERROR("-EISCONN: TCB already connected.");
|
||||
if (state == FSM_STATE_CLOSED) {
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EISCONN;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setup messaging */
|
||||
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
||||
|
||||
/* Setup passive connection */
|
||||
if (passive) {
|
||||
/* Mark connection as passive opend */
|
||||
tcb->status |= STATUS_PASSIVE;
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
/* If local address is specified: Copy it into TCB */
|
||||
if (local_addr && tcb->address_family == AF_INET6) {
|
||||
/* Store given address in TCB */
|
||||
if (memcpy(tcb->local_addr, local_addr, sizeof(tcb->local_addr)) == NULL) {
|
||||
TCP_DEBUG_ERROR("-EINVAL: Invalid peer address.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Setup connection timeout */
|
||||
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
||||
|
||||
if (ipv6_addr_is_unspecified((ipv6_addr_t *) tcb->local_addr)) {
|
||||
tcb->status |= STATUS_ALLOW_ANY_ADDR;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Suppress Compiler Warnings */
|
||||
(void) remote;
|
||||
(void) local_addr;
|
||||
#endif
|
||||
/* Set port number to listen on */
|
||||
tcb->local_port = local_port;
|
||||
}
|
||||
/* Setup active connection */
|
||||
else {
|
||||
assert(remote != NULL);
|
||||
/* Start connection teardown sequence */
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_CLOSE, NULL, NULL, 0);
|
||||
|
||||
/* Parse target address and port number into TCB */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (tcb->address_family == AF_INET6) {
|
||||
|
||||
/* Store Address information in TCB */
|
||||
if (memcpy(tcb->peer_addr, remote->addr.ipv6, sizeof(tcb->peer_addr)) == NULL) {
|
||||
TCP_DEBUG_ERROR("-EINVAL: Invalid peer address.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EINVAL;
|
||||
}
|
||||
tcb->ll_iface = remote->netif;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Assign port numbers, verification happens in fsm */
|
||||
tcb->local_port = local_port;
|
||||
tcb->peer_port = remote->port;
|
||||
|
||||
/* Setup connection timeout */
|
||||
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
||||
}
|
||||
|
||||
/* Call FSM with event: CALL_OPEN */
|
||||
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
||||
if (ret == -ENOMEM) {
|
||||
TCP_DEBUG_ERROR("-ENOMEM: All receive buffers are in use.");
|
||||
}
|
||||
else if (ret == -EADDRINUSE) {
|
||||
TCP_DEBUG_ERROR("-EADDRINUSE: local_port is already in use.");
|
||||
}
|
||||
|
||||
/* Wait until a connection was established or closed */
|
||||
/* Loop until the connection has been closed */
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
while (ret >= 0 && state != FSM_STATE_CLOSED && state != FSM_STATE_ESTABLISHED &&
|
||||
state != FSM_STATE_CLOSE_WAIT) {
|
||||
while ((state != FSM_STATE_CLOSED) && (state != FSM_STATE_LISTEN)) {
|
||||
mbox_get(&mbox, &msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_NOTIFY_USER:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
||||
|
||||
/* Setup a timeout to be able to revert back to LISTEN state, in case the
|
||||
* send SYN+ACK we received upon entering SYN_RCVD is never acknowledged
|
||||
* by the peer. */
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
if ((state == FSM_STATE_SYN_RCVD) && (tcb->status & STATUS_PASSIVE)) {
|
||||
_unsched_mbox(&tcb->event_misc);
|
||||
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
/* The connection establishment attempt timed out:
|
||||
* 1) Active connections return -ETIMEOUT.
|
||||
* 2) Passive connections stop the ongoing retransmissions and repeat the
|
||||
* open call to wait for the next connection attempt. */
|
||||
if (tcb->status & STATUS_PASSIVE) {
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
||||
}
|
||||
else {
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
||||
TCP_DEBUG_ERROR("-ETIMEDOUT: Connection timed out.");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
case MSG_TYPE_NOTIFY_USER:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -223,13 +122,17 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote,
|
||||
/* Cleanup */
|
||||
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
||||
_unsched_mbox(&tcb->event_misc);
|
||||
if (state == FSM_STATE_CLOSED && ret == 0) {
|
||||
TCP_DEBUG_ERROR("-ECONNREFUSED: Connection refused by peer.");
|
||||
ret = -ECONNREFUSED;
|
||||
}
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
TCP_DEBUG_LEAVE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void _abort(gnrc_tcp_tcb_t *tcb)
|
||||
{
|
||||
TCP_DEBUG_ENTER;
|
||||
if (_gnrc_tcp_fsm_get_state(tcb) != FSM_STATE_CLOSED) {
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_ABORT, NULL, NULL, 0);
|
||||
}
|
||||
TCP_DEBUG_LEAVE;
|
||||
}
|
||||
|
||||
/* External GNRC TCP API */
|
||||
@ -421,14 +324,16 @@ void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb)
|
||||
TCP_DEBUG_LEAVE;
|
||||
}
|
||||
|
||||
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote, uint16_t local_port)
|
||||
|
||||
int gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote, uint16_t local_port)
|
||||
{
|
||||
/* Sanity checking */
|
||||
TCP_DEBUG_ENTER;
|
||||
assert(tcb != NULL);
|
||||
assert(remote != NULL);
|
||||
assert(remote->port != PORT_UNSPEC);
|
||||
|
||||
/* Check if given AF-Family in remote is supported */
|
||||
/* Verify remote ep */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (remote->family != AF_INET6) {
|
||||
TCP_DEBUG_ERROR("-EAFNOSUPPORT: remote AF-Family not supported.");
|
||||
@ -441,50 +346,298 @@ int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote, uint1
|
||||
return -EAFNOSUPPORT;
|
||||
#endif
|
||||
|
||||
/* Protect TCB against usage in other TCP functions */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Check if AF-Family for target address matches internally used AF-Family */
|
||||
if (remote->family != tcb->address_family) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
TCP_DEBUG_ERROR("-EINVAL: local and remote AF-Family don't match.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Proceed with connection opening */
|
||||
int res = _gnrc_tcp_open(tcb, remote, NULL, local_port, 0);
|
||||
/* TCB is already connected: Return -EISCONN */
|
||||
_gnrc_tcp_fsm_state_t state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
if (state != FSM_STATE_CLOSED) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
TCP_DEBUG_ERROR("-EISCONN: TCB already connected.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EISCONN;
|
||||
}
|
||||
|
||||
/* Setup messaging */
|
||||
msg_t msg;
|
||||
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
||||
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
||||
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
||||
|
||||
/* Parse target address and port number into TCB */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (tcb->address_family == AF_INET6) {
|
||||
|
||||
/* Store Address information in TCB */
|
||||
if (memcpy(tcb->peer_addr, remote->addr.ipv6, sizeof(tcb->peer_addr)) == NULL) {
|
||||
TCP_DEBUG_ERROR("-EINVAL: Invalid peer address.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EINVAL;
|
||||
}
|
||||
tcb->ll_iface = remote->netif;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Assign port numbers, verification happens during connection establishment. */
|
||||
tcb->local_port = local_port;
|
||||
tcb->peer_port = remote->port;
|
||||
|
||||
/* Setup connection timeout */
|
||||
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
||||
|
||||
/* Call FSM with event: CALL_OPEN */
|
||||
int ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
||||
if (ret == -ENOMEM) {
|
||||
TCP_DEBUG_ERROR("-ENOMEM: All receive buffers are in use.");
|
||||
}
|
||||
else if (ret == -EADDRINUSE) {
|
||||
TCP_DEBUG_ERROR("-EADDRINUSE: local_port is already in use.");
|
||||
}
|
||||
|
||||
/* Wait until a connection was established or closed */
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
while (ret >= 0 && state != FSM_STATE_CLOSED && state != FSM_STATE_ESTABLISHED &&
|
||||
state != FSM_STATE_CLOSE_WAIT) {
|
||||
mbox_get(&mbox, &msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_NOTIFY_USER:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
||||
break;
|
||||
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
||||
TCP_DEBUG_ERROR("-ETIMEDOUT: Connection timed out.");
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
|
||||
default:
|
||||
TCP_DEBUG_ERROR("Received unexpected message.");
|
||||
}
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
_unsched_mbox(&tcb->event_misc);
|
||||
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
if (state == FSM_STATE_CLOSED && ret == 0) {
|
||||
TCP_DEBUG_ERROR("-ECONNREFUSED: Connection was refused by peer.");
|
||||
ret = -ECONNREFUSED;
|
||||
}
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
TCP_DEBUG_LEAVE;
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *local)
|
||||
int gnrc_tcp_listen(gnrc_tcp_tcb_queue_t *queue, gnrc_tcp_tcb_t *tcbs, size_t tcbs_len,
|
||||
const gnrc_tcp_ep_t *local)
|
||||
{
|
||||
TCP_DEBUG_ENTER;
|
||||
assert(tcb != NULL);
|
||||
/* Sanity checks */
|
||||
assert(queue != NULL);
|
||||
assert(tcbs != NULL);
|
||||
assert(tcbs_len > 0);
|
||||
assert(local != NULL);
|
||||
assert(local->port != PORT_UNSPEC);
|
||||
|
||||
/* Check if given AF-Family in local is supported */
|
||||
/* Verfiy given endpoint */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (local->family != AF_INET6) {
|
||||
TCP_DEBUG_ERROR("-EAFNOSUPPORT: AF-Family not supported.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
/* Check if AF-Family matches internally used AF-Family */
|
||||
if (local->family != tcb->address_family) {
|
||||
TCP_DEBUG_ERROR("-EINVAL: AF-Family doesn't match.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Proceed with connection opening */
|
||||
int res = _gnrc_tcp_open(tcb, NULL, local->addr.ipv6, local->port, 1);
|
||||
TCP_DEBUG_LEAVE;
|
||||
return res;
|
||||
#else
|
||||
TCP_DEBUG_ERROR("-EAFNOSUPPORT: AF-Family not supported.");
|
||||
TCP_DEBUG_LEAVE;
|
||||
return -EAFNOSUPPORT;
|
||||
#endif
|
||||
|
||||
/* Protect TCP Data structures against usage in other TCP function*/
|
||||
mutex_lock(&queue->lock);
|
||||
|
||||
for (size_t i = 0; i < tcbs_len; ++i) {
|
||||
mutex_lock(&(tcbs[i].function_lock));
|
||||
}
|
||||
|
||||
/* Setup and verify each TCB */
|
||||
int ret = 0;
|
||||
for (size_t i = 0; i < tcbs_len; ++i) {
|
||||
gnrc_tcp_tcb_t *tcb = &(tcbs[i]);
|
||||
|
||||
/* Verify current TCB */
|
||||
if (tcb->address_family != local->family) {
|
||||
TCP_DEBUG_ERROR("-EINVAL: local and remote AF-Family don't match.");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
else if (_gnrc_tcp_fsm_get_state(tcb) != FSM_STATE_CLOSED) {
|
||||
TCP_DEBUG_ERROR("-EISCONN: tcb is already connected.");
|
||||
ret = -EISCONN;
|
||||
}
|
||||
|
||||
/* Setup TCB for incoming connections attempts */
|
||||
if (!ret)
|
||||
{
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (tcb->address_family == AF_INET6) {
|
||||
memcpy(tcb->local_addr, local->addr.ipv6, sizeof(tcb->local_addr));
|
||||
|
||||
if (ipv6_addr_is_unspecified((ipv6_addr_t *) tcb->local_addr)) {
|
||||
tcb->status |= STATUS_ALLOW_ANY_ADDR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tcb->local_port = local->port;
|
||||
tcb->status |= STATUS_LISTENING;
|
||||
|
||||
/* Open connection */
|
||||
ret = _gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/* If anything goes wrong, discard all potentially opened connections. */
|
||||
if (ret) {
|
||||
for (size_t j = 0; j <= i; ++j) {
|
||||
tcb->status &= ~(STATUS_LISTENING);
|
||||
_abort(tcb);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If everything went well: setup queue and unlock all TCBs */
|
||||
if (!ret) {
|
||||
queue->tcbs = tcbs;
|
||||
queue->tcbs_len = tcbs_len;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tcbs_len; ++i) {
|
||||
mutex_unlock(&(tcbs[i].function_lock));
|
||||
}
|
||||
mutex_unlock(&queue->lock);
|
||||
TCP_DEBUG_LEAVE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gnrc_tcp_accept(gnrc_tcp_tcb_queue_t *queue, gnrc_tcp_tcb_t **tcb,
|
||||
uint32_t user_timeout_duration_ms)
|
||||
{
|
||||
TCP_DEBUG_ENTER;
|
||||
assert(queue != NULL);
|
||||
assert(tcb != NULL);
|
||||
|
||||
int ret = 0;
|
||||
int avail_tcbs = 0;
|
||||
msg_t msg;
|
||||
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
||||
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
||||
evtimer_mbox_event_t event_user_timeout;
|
||||
gnrc_tcp_tcb_t *tmp = NULL;
|
||||
_gnrc_tcp_fsm_state_t state = 0;
|
||||
|
||||
/* Search for non-accepted established connections */
|
||||
*tcb = NULL;
|
||||
mutex_lock(&queue->lock);
|
||||
for (size_t i = 0; i < queue->tcbs_len; ++i) {
|
||||
tmp = &(queue->tcbs[i]);
|
||||
|
||||
if (tmp->status & STATUS_ACCEPTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
state = _gnrc_tcp_fsm_get_state(tmp);
|
||||
if (state == FSM_STATE_ESTABLISHED || state == FSM_STATE_CLOSE_WAIT) {
|
||||
tmp->status |= STATUS_ACCEPTED;
|
||||
*tcb = tmp;
|
||||
break;
|
||||
}
|
||||
++avail_tcbs;
|
||||
}
|
||||
|
||||
/* Return if a connection was found, accept was called as non-blocking or all
|
||||
* TCBs were already accepted.
|
||||
*/
|
||||
if ((*tcb) || (user_timeout_duration_ms == 0) || (avail_tcbs == 0)) {
|
||||
if (*tcb) {
|
||||
TCP_DEBUG_INFO("Accepting connection.");
|
||||
ret = 0;
|
||||
}
|
||||
else if (avail_tcbs == 0) {
|
||||
TCP_DEBUG_ERROR("-ENOMEM: All TCBs are currently accepted.");
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
else if (user_timeout_duration_ms == 0) {
|
||||
TCP_DEBUG_ERROR("-EAGAIN: Would block. Try again.");
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
mutex_unlock(&queue->lock);
|
||||
TCP_DEBUG_LEAVE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup TCBs for message exchange with the FSM */
|
||||
for (size_t i = 0; i < queue->tcbs_len; ++i) {
|
||||
tmp = &(queue->tcbs[i]);
|
||||
|
||||
/* Setup only not accepted TCBS */
|
||||
if (!(tmp->status & STATUS_ACCEPTED)) {
|
||||
mutex_lock(&(tmp->function_lock));
|
||||
tmp->status |= STATUS_LOCKED;
|
||||
_gnrc_tcp_fsm_set_mbox(tmp, &mbox);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup User specified Timeout */
|
||||
_sched_mbox(&event_user_timeout, user_timeout_duration_ms,
|
||||
MSG_TYPE_USER_SPEC_TIMEOUT, &mbox);
|
||||
|
||||
/* Wait until a connection was established */
|
||||
while (ret >= 0 && *tcb == NULL) {
|
||||
mbox_get(&mbox, &msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_NOTIFY_USER:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
||||
|
||||
tmp = (gnrc_tcp_tcb_t *) msg.content.ptr;
|
||||
state = _gnrc_tcp_fsm_get_state(tmp);
|
||||
if (state == FSM_STATE_ESTABLISHED || state == FSM_STATE_CLOSE_WAIT) {
|
||||
tmp->status |= STATUS_ACCEPTED;
|
||||
*tcb = tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_TYPE_USER_SPEC_TIMEOUT:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_USER_SPEC_TIMEOUT.");
|
||||
TCP_DEBUG_ERROR("-ETIMEDOUT: User specified timeout expired.");
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
|
||||
default:
|
||||
TCP_DEBUG_ERROR("Received unexpected message.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
_unsched_mbox(&event_user_timeout);
|
||||
for (size_t i = 0; i < queue->tcbs_len; ++i) {
|
||||
tmp = &(queue->tcbs[i]);
|
||||
|
||||
if (tmp->status & STATUS_LOCKED) {
|
||||
tmp->status &= ~(STATUS_LOCKED);
|
||||
_gnrc_tcp_fsm_set_mbox(tmp, NULL);
|
||||
mutex_unlock(&(tmp->function_lock));
|
||||
}
|
||||
}
|
||||
mutex_unlock(&queue->lock);
|
||||
TCP_DEBUG_LEAVE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,
|
||||
@ -736,55 +889,10 @@ void gnrc_tcp_close(gnrc_tcp_tcb_t *tcb)
|
||||
TCP_DEBUG_ENTER;
|
||||
assert(tcb != NULL);
|
||||
|
||||
msg_t msg;
|
||||
msg_t msg_queue[TCP_MSG_QUEUE_SIZE];
|
||||
mbox_t mbox = MBOX_INIT(msg_queue, TCP_MSG_QUEUE_SIZE);
|
||||
_gnrc_tcp_fsm_state_t state = 0;
|
||||
|
||||
/* Lock the TCB for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Return if connection is closed */
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
if (state == FSM_STATE_CLOSED) {
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
TCP_DEBUG_LEAVE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setup messaging */
|
||||
_gnrc_tcp_fsm_set_mbox(tcb, &mbox);
|
||||
|
||||
/* Setup connection timeout */
|
||||
_sched_connection_timeout(&tcb->event_misc, &mbox);
|
||||
|
||||
/* Start connection teardown sequence */
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_CLOSE, NULL, NULL, 0);
|
||||
|
||||
/* Loop until the connection has been closed */
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
while (state != FSM_STATE_CLOSED) {
|
||||
mbox_get(&mbox, &msg);
|
||||
switch (msg.type) {
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
case MSG_TYPE_NOTIFY_USER:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_NOTIFY_USER.");
|
||||
break;
|
||||
|
||||
default:
|
||||
TCP_DEBUG_ERROR("Received unexpected message.");
|
||||
}
|
||||
state = _gnrc_tcp_fsm_get_state(tcb);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
_gnrc_tcp_fsm_set_mbox(tcb, NULL);
|
||||
_unsched_mbox(&tcb->event_misc);
|
||||
_close(tcb);
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
|
||||
TCP_DEBUG_LEAVE;
|
||||
}
|
||||
|
||||
@ -793,13 +901,35 @@ void gnrc_tcp_abort(gnrc_tcp_tcb_t *tcb)
|
||||
TCP_DEBUG_ENTER;
|
||||
assert(tcb != NULL);
|
||||
|
||||
/* Lock the TCB for this function call */
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
if (_gnrc_tcp_fsm_get_state(tcb) != FSM_STATE_CLOSED) {
|
||||
/* Call FSM ABORT event */
|
||||
_gnrc_tcp_fsm(tcb, FSM_EVENT_CALL_ABORT, NULL, NULL, 0);
|
||||
}
|
||||
_abort(tcb);
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
|
||||
TCP_DEBUG_LEAVE;
|
||||
}
|
||||
|
||||
void gnrc_tcp_stop_listen(gnrc_tcp_tcb_queue_t *queue)
|
||||
{
|
||||
TCP_DEBUG_ENTER;
|
||||
assert(queue != NULL);
|
||||
|
||||
/* Close all connections associated with the given queue */
|
||||
mutex_lock(&(queue->lock));
|
||||
for (size_t i = 0; i < queue->tcbs_len; ++i) {
|
||||
gnrc_tcp_tcb_t *tcb = &(queue->tcbs[i]);
|
||||
mutex_lock(&(tcb->function_lock));
|
||||
|
||||
/* Clear LISTENING status causing re-opening on close */
|
||||
tcb->status &= ~(STATUS_LISTENING);
|
||||
_close(tcb);
|
||||
|
||||
mutex_unlock(&(tcb->function_lock));
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
queue->tcbs = NULL;
|
||||
queue->tcbs_len = 0;
|
||||
mutex_unlock(&(queue->lock));
|
||||
TCP_DEBUG_LEAVE;
|
||||
}
|
||||
|
||||
|
@ -330,6 +330,16 @@ static void *_eventloop(__attribute__((unused)) void *arg)
|
||||
FSM_EVENT_TIMEOUT_TIMEWAIT, NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
/* A connection opening attempt from a TCB in listening mode failed.
|
||||
* Clear retransmission and re-open for next attempt */
|
||||
case MSG_TYPE_CONNECTION_TIMEOUT:
|
||||
TCP_DEBUG_INFO("Received MSG_TYPE_CONNECTION_TIMEOUT.");
|
||||
_gnrc_tcp_fsm((gnrc_tcp_tcb_t *)msg.content.ptr,
|
||||
FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
|
||||
_gnrc_tcp_fsm((gnrc_tcp_tcb_t *)msg.content.ptr,
|
||||
FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
TCP_DEBUG_ERROR("Received unexpected message.");
|
||||
}
|
||||
|
@ -111,8 +111,8 @@ static int _clear_retransmit(gnrc_tcp_tcb_t *tcb)
|
||||
static int _restart_timewait_timer(gnrc_tcp_tcb_t *tcb)
|
||||
{
|
||||
TCP_DEBUG_ENTER;
|
||||
_gnrc_tcp_eventloop_unsched(&tcb->event_retransmit);
|
||||
_gnrc_tcp_eventloop_sched(&tcb->event_retransmit, 2 * CONFIG_GNRC_TCP_MSL_MS,
|
||||
_gnrc_tcp_eventloop_unsched(&tcb->event_timeout);
|
||||
_gnrc_tcp_eventloop_sched(&tcb->event_timeout, 2 * CONFIG_GNRC_TCP_MSL_MS,
|
||||
MSG_TYPE_TIMEWAIT, tcb);
|
||||
TCP_DEBUG_LEAVE;
|
||||
return 0;
|
||||
@ -139,17 +139,32 @@ static int _transition_to(gnrc_tcp_tcb_t *tcb, _gnrc_tcp_fsm_state_t state)
|
||||
/* Clear retransmit queue */
|
||||
_clear_retransmit(tcb);
|
||||
|
||||
/* Remove connection from active connections */
|
||||
mutex_lock(&list->lock);
|
||||
LL_DELETE(list->head, tcb);
|
||||
mutex_unlock(&list->lock);
|
||||
/* Close connection if not listenng */
|
||||
if (!(tcb->status & STATUS_LISTENING))
|
||||
{
|
||||
/* Remove connection from active connections */
|
||||
mutex_lock(&list->lock);
|
||||
LL_DELETE(list->head, tcb);
|
||||
mutex_unlock(&list->lock);
|
||||
|
||||
/* Free potentially allocated receive buffer */
|
||||
_gnrc_tcp_rcvbuf_release_buffer(tcb);
|
||||
/* Free potentially allocated receive buffer */
|
||||
_gnrc_tcp_rcvbuf_release_buffer(tcb);
|
||||
TCP_DEBUG_INFO("Connection closed");
|
||||
}
|
||||
/* Re-open connection as listenng */
|
||||
else
|
||||
{
|
||||
TCP_DEBUG_INFO("Connection reopend");
|
||||
state = FSM_STATE_LISTEN;
|
||||
_transition_to(tcb, state);
|
||||
}
|
||||
tcb->status |= STATUS_NOTIFY_USER;
|
||||
break;
|
||||
|
||||
case FSM_STATE_LISTEN:
|
||||
/* Clear Accepted Status */
|
||||
tcb->status &= ~(STATUS_ACCEPTED);
|
||||
|
||||
/* Clear address info */
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if (tcb->address_family == AF_INET6) {
|
||||
@ -196,8 +211,20 @@ static int _transition_to(gnrc_tcp_tcb_t *tcb, _gnrc_tcp_fsm_state_t state)
|
||||
break;
|
||||
|
||||
case FSM_STATE_SYN_RCVD:
|
||||
/* Setup timeout for listening TCBs */
|
||||
if (tcb->status & STATUS_LISTENING) {
|
||||
_gnrc_tcp_eventloop_sched(&tcb->event_timeout,
|
||||
CONFIG_GNRC_TCP_CONNECTION_TIMEOUT_DURATION_MS,
|
||||
MSG_TYPE_CONNECTION_TIMEOUT, tcb);
|
||||
}
|
||||
break;
|
||||
|
||||
case FSM_STATE_ESTABLISHED:
|
||||
case FSM_STATE_CLOSE_WAIT:
|
||||
/* Stop timeout for listening TCBs */
|
||||
if (tcb->status & STATUS_LISTENING) {
|
||||
_gnrc_tcp_eventloop_unsched(&tcb->event_timeout);
|
||||
}
|
||||
tcb->status |= STATUS_NOTIFY_USER;
|
||||
break;
|
||||
|
||||
@ -236,7 +263,7 @@ static int _fsm_call_open(gnrc_tcp_tcb_t *tcb)
|
||||
|
||||
tcb->rcv_wnd = CONFIG_GNRC_TCP_DEFAULT_WINDOW;
|
||||
|
||||
if (tcb->status & STATUS_PASSIVE) {
|
||||
if (tcb->status & STATUS_LISTENING) {
|
||||
/* Passive open, T: CLOSED -> LISTEN */
|
||||
_transition_to(tcb, FSM_STATE_LISTEN);
|
||||
}
|
||||
@ -629,7 +656,7 @@ static int _fsm_rcvd_pkt(gnrc_tcp_tcb_t *tcb, gnrc_pktsnip_t *in_pkt)
|
||||
/* 2) Check RST: If RST is set ... */
|
||||
if (ctl & MSK_RST) {
|
||||
/* .. and state is SYN_RCVD and the connection is passive: SYN_RCVD -> LISTEN */
|
||||
if (tcb->state == FSM_STATE_SYN_RCVD && (tcb->status & STATUS_PASSIVE)) {
|
||||
if (tcb->state == FSM_STATE_SYN_RCVD && (tcb->status & STATUS_LISTENING)) {
|
||||
_transition_to(tcb, FSM_STATE_LISTEN);
|
||||
}
|
||||
else {
|
||||
|
@ -42,9 +42,11 @@ extern "C" {
|
||||
* @brief TCB status flags
|
||||
* @{
|
||||
*/
|
||||
#define STATUS_PASSIVE (1 << 0)
|
||||
#define STATUS_LISTENING (1 << 0)
|
||||
#define STATUS_ALLOW_ANY_ADDR (1 << 1)
|
||||
#define STATUS_NOTIFY_USER (1 << 2)
|
||||
#define STATUS_ACCEPTED (1 << 3)
|
||||
#define STATUS_LOCKED (1 << 4)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,14 @@ in the tests directory.
|
||||
7) 07-endpoint_construction.py
|
||||
This test ensures the correctness of the endpoint construction.
|
||||
|
||||
8) 08-return_codes.py
|
||||
This test tries to trigger all documented return codes from GNRC_TCPs functions.
|
||||
|
||||
9) 09-listen_accept_cycle.py
|
||||
This test verifies that connection establishment via listen and accept can be repeated multiple
|
||||
times.
|
||||
|
||||
|
||||
Setup
|
||||
==========
|
||||
The test requires a tap-device setup. This can be achieved by running 'dist/tools/tapsetup/tapsetup'
|
||||
|
@ -15,10 +15,13 @@
|
||||
#include "net/gnrc/tcp.h"
|
||||
|
||||
#define MAIN_QUEUE_SIZE (8)
|
||||
#define TCB_QUEUE_SIZE (1)
|
||||
#define BUFFER_SIZE (2049)
|
||||
|
||||
static msg_t main_msg_queue[MAIN_QUEUE_SIZE];
|
||||
static gnrc_tcp_tcb_t tcb;
|
||||
static gnrc_tcp_tcb_t tcbs[TCB_QUEUE_SIZE];
|
||||
static gnrc_tcp_tcb_t *tcb = tcbs;
|
||||
static gnrc_tcp_tcb_queue_t queue = GNRC_TCP_TCB_QUEUE_INIT;
|
||||
static char buffer[BUFFER_SIZE];
|
||||
|
||||
void dump_args(int argc, char **argv)
|
||||
@ -121,11 +124,17 @@ int gnrc_tcp_ep_from_str_cmd(int argc, char **argv)
|
||||
int gnrc_tcp_tcb_init_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
gnrc_tcp_tcb_init(&tcb);
|
||||
|
||||
// Initialize all given TCBs
|
||||
for (int i = 0; i < TCB_QUEUE_SIZE; ++i)
|
||||
{
|
||||
gnrc_tcp_tcb_init(&(tcbs[i]));
|
||||
}
|
||||
printf("%s: returns 0\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gnrc_tcp_open_active_cmd(int argc, char **argv)
|
||||
int gnrc_tcp_open_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
|
||||
@ -133,7 +142,7 @@ int gnrc_tcp_open_active_cmd(int argc, char **argv)
|
||||
gnrc_tcp_ep_from_str(&remote, argv[1]);
|
||||
uint16_t local_port = atol(argv[2]);
|
||||
|
||||
int err = gnrc_tcp_open_active(&tcb, &remote, local_port);
|
||||
int err = gnrc_tcp_open(tcb, &remote, local_port);
|
||||
switch (err) {
|
||||
case -EAFNOSUPPORT:
|
||||
printf("%s: returns -EAFNOSUPPORT\n", argv[0]);
|
||||
@ -169,14 +178,14 @@ int gnrc_tcp_open_active_cmd(int argc, char **argv)
|
||||
return err;
|
||||
}
|
||||
|
||||
int gnrc_tcp_open_passive_cmd(int argc, char **argv)
|
||||
int gnrc_tcp_listen_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
|
||||
gnrc_tcp_ep_t local;
|
||||
gnrc_tcp_ep_from_str(&local, argv[1]);
|
||||
|
||||
int err = gnrc_tcp_open_passive(&tcb, &local);
|
||||
int err = gnrc_tcp_listen(&queue, tcbs, ARRAY_SIZE(tcbs), &local);
|
||||
switch (err) {
|
||||
case -EAFNOSUPPORT:
|
||||
printf("%s: returns -EAFNOSUPPORT\n", argv[0]);
|
||||
@ -200,6 +209,37 @@ int gnrc_tcp_open_passive_cmd(int argc, char **argv)
|
||||
return err;
|
||||
}
|
||||
|
||||
int gnrc_tcp_accept_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
|
||||
gnrc_tcp_tcb_t *tmp = NULL;
|
||||
int timeout = atol(argv[1]);
|
||||
int err = gnrc_tcp_accept(&queue, &tmp, timeout);
|
||||
switch (err) {
|
||||
case -EAGAIN:
|
||||
printf("%s: returns -EAGAIN\n", argv[0]);
|
||||
break;
|
||||
|
||||
case -ENOMEM:
|
||||
printf("%s: returns -ENOMEM\n", argv[0]);
|
||||
break;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
printf("%s: returns -ETIMEDOUT\n", argv[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("%s: returns %d\n", argv[0], err);
|
||||
}
|
||||
|
||||
if (tmp) {
|
||||
tcb = tmp;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int gnrc_tcp_send_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
@ -209,7 +249,7 @@ int gnrc_tcp_send_cmd(int argc, char **argv)
|
||||
size_t sent = 0;
|
||||
|
||||
while (sent < to_send) {
|
||||
int ret = gnrc_tcp_send(&tcb, buffer + sent, to_send - sent, timeout);
|
||||
int ret = gnrc_tcp_send(tcb, buffer + sent, to_send - sent, timeout);
|
||||
switch (ret) {
|
||||
case -ENOTCONN:
|
||||
printf("%s: returns -ENOTCONN\n", argv[0]);
|
||||
@ -243,7 +283,7 @@ int gnrc_tcp_recv_cmd(int argc, char **argv)
|
||||
size_t rcvd = 0;
|
||||
|
||||
while (rcvd < to_receive) {
|
||||
int ret = gnrc_tcp_recv(&tcb, buffer + rcvd, to_receive - rcvd,
|
||||
int ret = gnrc_tcp_recv(tcb, buffer + rcvd, to_receive - rcvd,
|
||||
timeout);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
@ -280,14 +320,24 @@ int gnrc_tcp_recv_cmd(int argc, char **argv)
|
||||
int gnrc_tcp_close_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
gnrc_tcp_close(&tcb);
|
||||
gnrc_tcp_close(tcb);
|
||||
printf("%s: returns\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gnrc_tcp_abort_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
gnrc_tcp_abort(&tcb);
|
||||
gnrc_tcp_abort(tcb);
|
||||
printf("%s: returns\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gnrc_tcp_stop_listen_cmd(int argc, char **argv)
|
||||
{
|
||||
dump_args(argc, argv);
|
||||
gnrc_tcp_stop_listen(&queue);
|
||||
printf("%s: returns\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -295,11 +345,14 @@ int gnrc_tcp_abort_cmd(int argc, char **argv)
|
||||
static const shell_command_t shell_commands[] = {
|
||||
{ "gnrc_tcp_ep_from_str", "Build endpoint from string",
|
||||
gnrc_tcp_ep_from_str_cmd },
|
||||
{ "gnrc_tcp_tcb_init", "gnrc_tcp: init tcb", gnrc_tcp_tcb_init_cmd },
|
||||
{ "gnrc_tcp_open_active", "gnrc_tcp: open active connection",
|
||||
gnrc_tcp_open_active_cmd },
|
||||
{ "gnrc_tcp_open_passive", "gnrc_tcp: open passive connection",
|
||||
gnrc_tcp_open_passive_cmd },
|
||||
{ "gnrc_tcp_tcb_init", "gnrc_tcp: init tcb",
|
||||
gnrc_tcp_tcb_init_cmd },
|
||||
{ "gnrc_tcp_open", "gnrc_tcp: open connection",
|
||||
gnrc_tcp_open_cmd },
|
||||
{ "gnrc_tcp_listen", "gnrc_tcp: listen for connection",
|
||||
gnrc_tcp_listen_cmd },
|
||||
{ "gnrc_tcp_accept", "gnrc_tcp: accept connection",
|
||||
gnrc_tcp_accept_cmd },
|
||||
{ "gnrc_tcp_send", "gnrc_tcp: send data to connected peer",
|
||||
gnrc_tcp_send_cmd },
|
||||
{ "gnrc_tcp_recv", "gnrc_tcp: recv data from connected peer",
|
||||
@ -308,11 +361,16 @@ static const shell_command_t shell_commands[] = {
|
||||
gnrc_tcp_close_cmd },
|
||||
{ "gnrc_tcp_abort", "gnrc_tcp: close connection forcefully",
|
||||
gnrc_tcp_abort_cmd },
|
||||
{ "buffer_init", "init internal buffer", buffer_init_cmd },
|
||||
{ "gnrc_tcp_stop_listen", "gnrc_tcp: stop listening",
|
||||
gnrc_tcp_stop_listen_cmd },
|
||||
{ "buffer_init", "init internal buffer",
|
||||
buffer_init_cmd },
|
||||
{ "buffer_get_max_size", "get max size of internal buffer",
|
||||
buffer_get_max_size_cmd },
|
||||
{ "buffer_write", "write data into internal buffer", buffer_write_cmd },
|
||||
{ "buffer_read", "read data from internal buffer", buffer_read_cmd },
|
||||
{ "buffer_write", "write data into internal buffer",
|
||||
buffer_write_cmd },
|
||||
{ "buffer_read", "read data from internal buffer",
|
||||
buffer_read_cmd },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -32,8 +32,9 @@ def testfunc(child):
|
||||
|
||||
# Setup RIOT Node to connect to host systems TCP Server
|
||||
child.sendline('gnrc_tcp_tcb_init')
|
||||
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open_active: returns 0')
|
||||
|
||||
child.sendline('gnrc_tcp_open [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open: returns 0')
|
||||
|
||||
# Close connection and verify that pktbuf is cleared
|
||||
shutdown_event.set()
|
||||
|
@ -8,50 +8,35 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import threading
|
||||
|
||||
from testrunner import run
|
||||
from shared_func import generate_port_number, get_host_tap_device, get_riot_ll_addr, \
|
||||
verify_pktbuf_empty, sudo_guard
|
||||
from shared_func import Runner, RiotTcpServer, HostTcpClient, generate_port_number, \
|
||||
sudo_guard
|
||||
|
||||
|
||||
def tcp_client(addr, port, shutdown_event):
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
addr_info = socket.getaddrinfo(addr + '%' + get_host_tap_device(), port, type=socket.SOCK_STREAM)
|
||||
|
||||
sock.connect(addr_info[0][-1])
|
||||
|
||||
shutdown_event.wait()
|
||||
|
||||
sock.close()
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
port = generate_port_number()
|
||||
shutdown_event = threading.Event()
|
||||
|
||||
client_handle = threading.Thread(target=tcp_client, args=(get_riot_ll_addr(child), port, shutdown_event))
|
||||
|
||||
# Setup RIOT Node wait for incoming connections from host system
|
||||
child.sendline('gnrc_tcp_tcb_init')
|
||||
child.sendline('gnrc_tcp_open_passive [::]:{}'.format(str(port)))
|
||||
|
||||
client_handle.start()
|
||||
child.expect_exact('gnrc_tcp_open_passive: returns 0')
|
||||
|
||||
# Close connection and verify that pktbuf is cleared
|
||||
shutdown_event.set()
|
||||
child.sendline('gnrc_tcp_close')
|
||||
client_handle.join()
|
||||
|
||||
verify_pktbuf_empty(child)
|
||||
|
||||
print(os.path.basename(sys.argv[0]) + ': success')
|
||||
@Runner(timeout=10)
|
||||
def test_lifecycle_as_server(child):
|
||||
""" Open/close a single connection as tcp server """
|
||||
# Setup RIOT as server
|
||||
with RiotTcpServer(child, generate_port_number()) as riot_srv:
|
||||
# Setup Host as client
|
||||
with HostTcpClient(riot_srv.addr, riot_srv.listen_port):
|
||||
# Accept and close connection
|
||||
riot_srv.accept(timeout_ms=1000)
|
||||
riot_srv.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sudo_guard()
|
||||
sys.exit(run(testfunc, timeout=7, echo=False, traceback=True))
|
||||
|
||||
# Read and run all test functions.
|
||||
script = sys.modules[__name__]
|
||||
tests = [getattr(script, t) for t in script.__dict__
|
||||
if type(getattr(script, t)).__name__ == 'function'
|
||||
and t.startswith('test_')]
|
||||
|
||||
for test in tests:
|
||||
res = test()
|
||||
if (res != 0):
|
||||
sys.exit(res)
|
||||
|
||||
print(os.path.basename(sys.argv[0]) + ': success\n')
|
||||
|
@ -40,8 +40,8 @@ def testfunc(child):
|
||||
|
||||
# Setup RIOT Node to connect to host systems TCP Server
|
||||
child.sendline('gnrc_tcp_tcb_init')
|
||||
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open_active: returns 0')
|
||||
child.sendline('gnrc_tcp_open [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open: returns 0')
|
||||
|
||||
# Send data from RIOT Node to Linux
|
||||
write_data_to_internal_buffer(child, data)
|
||||
|
@ -40,8 +40,8 @@ def testfunc(child):
|
||||
|
||||
# Setup RIOT Node to connect to Hostsystems TCP Server
|
||||
child.sendline('gnrc_tcp_tcb_init')
|
||||
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open_active: returns 0')
|
||||
child.sendline('gnrc_tcp_open [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open: returns 0')
|
||||
|
||||
# Accept Data sent by the host system
|
||||
child.sendline('gnrc_tcp_recv 1000000 ' + str(data_len))
|
||||
|
@ -33,12 +33,14 @@ def testfunc(func):
|
||||
# Setup RIOT Node wait for incoming connections from host system
|
||||
child.sendline('gnrc_tcp_tcb_init')
|
||||
child.expect_exact('gnrc_tcp_tcb_init: argc=1, argv[0] = gnrc_tcp_tcb_init')
|
||||
child.sendline('gnrc_tcp_open_passive [::]:{}'.format(port))
|
||||
child.expect(r'gnrc_tcp_open_passive: argc=2, '
|
||||
r'argv\[0\] = gnrc_tcp_open_passive, '
|
||||
child.sendline('gnrc_tcp_listen [::]:{}'.format(port))
|
||||
child.expect(r'gnrc_tcp_listen: argc=2, '
|
||||
r'argv\[0\] = gnrc_tcp_listen, '
|
||||
r'argv\[1\] = \[::\]:(\d+)\r\n')
|
||||
assert int(child.match.group(1)) == port
|
||||
|
||||
child.sendline('gnrc_tcp_accept 15000')
|
||||
|
||||
try:
|
||||
print("- {} ".format(func.__name__), end="")
|
||||
if child.logfile == sys.stdout:
|
||||
@ -53,6 +55,7 @@ def testfunc(func):
|
||||
raise e
|
||||
finally:
|
||||
child.sendline('gnrc_tcp_close')
|
||||
child.sendline('gnrc_tcp_stop_listen')
|
||||
|
||||
return runner
|
||||
|
||||
@ -65,7 +68,7 @@ def test_short_payload(child, src_if, src_ll, dst_if, dst_l2, dst_ll, dst_port):
|
||||
addr_info = socket.getaddrinfo(dst_ll + '%' + src_if, dst_port,
|
||||
type=socket.SOCK_STREAM)
|
||||
sock.connect(addr_info[0][-1])
|
||||
child.expect_exact('gnrc_tcp_open_passive: returns 0')
|
||||
child.expect_exact('gnrc_tcp_accept: returns 0')
|
||||
child.sendline('gnrc_tcp_recv 1000000 1')
|
||||
child.expect_exact('gnrc_tcp_recv: argc=3, '
|
||||
'argv[0] = gnrc_tcp_recv, '
|
||||
@ -90,7 +93,7 @@ def test_short_header(child, src_if, src_ll, dst_if, dst_l2, dst_ll, dst_port):
|
||||
addr_info = socket.getaddrinfo(dst_ll + '%' + src_if, dst_port,
|
||||
type=socket.SOCK_STREAM)
|
||||
sock.connect(addr_info[0][-1])
|
||||
child.expect_exact('gnrc_tcp_open_passive: returns 0')
|
||||
child.expect_exact('gnrc_tcp_accept: returns 0')
|
||||
verify_pktbuf_empty(child)
|
||||
|
||||
|
||||
@ -122,7 +125,7 @@ def test_send_ack_instead_of_syn(child, src_if, src_ll,
|
||||
addr_info = socket.getaddrinfo(dst_ll + '%' + src_if, dst_port,
|
||||
type=socket.SOCK_STREAM)
|
||||
sock.connect(addr_info[0][-1])
|
||||
child.expect_exact('gnrc_tcp_open_passive: returns 0')
|
||||
child.expect_exact('gnrc_tcp_accept: returns 0')
|
||||
verify_pktbuf_empty(child)
|
||||
|
||||
|
||||
@ -140,7 +143,7 @@ def test_option_parsing_term(child, src_if, src_ll,
|
||||
addr_info = socket.getaddrinfo(dst_ll + '%' + src_if, dst_port,
|
||||
type=socket.SOCK_STREAM)
|
||||
sock.connect(addr_info[0][-1])
|
||||
child.expect_exact('gnrc_tcp_open_passive: returns 0')
|
||||
child.expect_exact('gnrc_tcp_accept: returns 0')
|
||||
verify_pktbuf_empty(child)
|
||||
|
||||
|
||||
|
@ -42,8 +42,8 @@ def testfunc(child):
|
||||
|
||||
# Setup RIOT Node to connect to Hostsystems TCP Server
|
||||
child.sendline('gnrc_tcp_tcb_init')
|
||||
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open_active: returns 0')
|
||||
child.sendline('gnrc_tcp_open [{}]:{} 0'.format(target_addr, str(port)))
|
||||
child.expect_exact('gnrc_tcp_open: returns 0')
|
||||
|
||||
# Initiate connection teardown from test host
|
||||
shutdown_event.set()
|
||||
|
@ -132,14 +132,19 @@ def main(child):
|
||||
if type(getattr(script, t)).__name__ == "function"
|
||||
and t.startswith("test_")]
|
||||
|
||||
res = 0
|
||||
|
||||
for test in tests:
|
||||
try:
|
||||
test(child)
|
||||
print('- {} SUCCESS'.format(test.__name__))
|
||||
|
||||
except Exception:
|
||||
res = -1
|
||||
print('- {} FAILED'.format(test.__name__))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sudo_guard()
|
||||
|
69
tests/gnrc_tcp/tests-as-root/08-return_codes.py
Executable file
69
tests/gnrc_tcp/tests-as-root/08-return_codes.py
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2021 Simon Brummer <simon.brummer@posteo.de>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from shared_func import Runner, RiotTcpServer, HostTcpClient, generate_port_number, \
|
||||
sudo_guard
|
||||
|
||||
|
||||
@Runner(timeout=0.5)
|
||||
def test_gnrc_tcp_accept_returns_EAGAIN(child):
|
||||
""" gnrc_tcp_accept must return with -EAGAIN
|
||||
if no connection is ready and timeout is 0
|
||||
"""
|
||||
riot_srv = RiotTcpServer(child, generate_port_number())
|
||||
riot_srv.listen()
|
||||
|
||||
child.sendline('gnrc_tcp_accept 0')
|
||||
child.expect_exact('gnrc_tcp_accept: returns -EAGAIN')
|
||||
|
||||
|
||||
@Runner(timeout=1.5)
|
||||
def test_gnrc_tcp_accept_returns_ETIMEDOUT(child):
|
||||
""" gnrc_tcp_accept must return with -ETIMEDOUT
|
||||
if no connection is ready and timeout is not 0
|
||||
"""
|
||||
riot_srv = RiotTcpServer(child, generate_port_number())
|
||||
riot_srv.listen()
|
||||
|
||||
child.sendline('gnrc_tcp_accept 1000')
|
||||
child.expect_exact('gnrc_tcp_accept: returns -ETIMEDOUT')
|
||||
|
||||
|
||||
@Runner(timeout=1)
|
||||
def test_gnrc_tcp_accept_returns_ENOMEM(child):
|
||||
""" gnrc_tcp_accept must return with -ENOMEM
|
||||
if all TCBs already handle a connection
|
||||
"""
|
||||
with RiotTcpServer(child, generate_port_number()) as riot_srv:
|
||||
# Establish connection to ensure that all TCBs are in use
|
||||
with HostTcpClient(riot_srv.addr, riot_srv.listen_port):
|
||||
riot_srv.accept(timeout_ms=0)
|
||||
|
||||
# Faulty accept should return immediately despite a huge timeout.
|
||||
child.sendline('gnrc_tcp_accept 100000000')
|
||||
child.expect_exact('gnrc_tcp_accept: returns -ENOMEM')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sudo_guard()
|
||||
|
||||
# Read and run all test functions.
|
||||
script = sys.modules[__name__]
|
||||
tests = [getattr(script, t) for t in script.__dict__
|
||||
if type(getattr(script, t)).__name__ == 'function'
|
||||
and t.startswith('test_')]
|
||||
|
||||
for test in tests:
|
||||
res = test()
|
||||
if (res != 0):
|
||||
sys.exit(res)
|
||||
|
||||
print(os.path.basename(sys.argv[0]) + ': success\n')
|
78
tests/gnrc_tcp/tests-as-root/09-listen_accept_cycle.py
Executable file
78
tests/gnrc_tcp/tests-as-root/09-listen_accept_cycle.py
Executable file
@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2021 Simon Brummer <simon.brummer@posteo.de>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import random
|
||||
|
||||
from shared_func import Runner, RiotTcpServer, HostTcpClient, generate_port_number, \
|
||||
sudo_guard
|
||||
|
||||
|
||||
@Runner(timeout=10)
|
||||
def test_listen_accept_cycle(child, iterations=10):
|
||||
""" This test verifies gnrc_tcp in a typical server role by
|
||||
accepting a connection, exchange data, teardown connection, handle the next one
|
||||
"""
|
||||
# Setup RIOT Node as server
|
||||
with RiotTcpServer(child, generate_port_number()) as riot_srv:
|
||||
# Establish multiple connections iterativly
|
||||
for i in range(iterations):
|
||||
print(' Running iteration {}'.format(i))
|
||||
|
||||
with HostTcpClient(riot_srv.addr, riot_srv.listen_port) as host_cli:
|
||||
# Accept connection from host system
|
||||
riot_srv.accept(timeout_ms=0)
|
||||
|
||||
# Send data from host to RIOT
|
||||
data = '0123456789'
|
||||
host_cli.send(payload_to_send=data)
|
||||
riot_srv.receive(timeout_ms=500, sent_payload=data)
|
||||
|
||||
# Send data from RIOT to host
|
||||
riot_srv.send(timeout_ms=500, payload_to_send=data)
|
||||
host_cli.receive(sent_payload=data)
|
||||
|
||||
# Randomize connection teardown: The connections
|
||||
# can't be either closed or aborted from either
|
||||
# side. Regardless type of the connection teardown
|
||||
# riot_srv must be able to accept the next connection
|
||||
# Note: python sockets don't offer abort...
|
||||
dice_throw = random.randint(0, 3)
|
||||
if dice_throw == 0:
|
||||
riot_srv.close()
|
||||
host_cli.close()
|
||||
|
||||
elif dice_throw == 1:
|
||||
riot_srv.abort()
|
||||
host_cli.close()
|
||||
|
||||
elif dice_throw == 2:
|
||||
host_cli.close()
|
||||
riot_srv.close()
|
||||
|
||||
elif dice_throw == 3:
|
||||
host_cli.close()
|
||||
riot_srv.abort()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sudo_guard()
|
||||
|
||||
# Read and run all test functions.
|
||||
script = sys.modules[__name__]
|
||||
tests = [getattr(script, t) for t in script.__dict__
|
||||
if type(getattr(script, t)).__name__ == 'function'
|
||||
and t.startswith('test_')]
|
||||
|
||||
for test in tests:
|
||||
res = test()
|
||||
if (res != 0):
|
||||
sys.exit(res)
|
||||
|
||||
print(os.path.basename(sys.argv[0]) + ': success\n')
|
@ -10,6 +10,31 @@ import os
|
||||
import re
|
||||
import socket
|
||||
import random
|
||||
import testrunner
|
||||
|
||||
|
||||
class Runner:
|
||||
def __init__(self, timeout, echo=False, skip=False):
|
||||
self.timeout = timeout
|
||||
self.echo = echo
|
||||
self.skip = skip
|
||||
|
||||
def __call__(self, fn):
|
||||
if self.skip:
|
||||
print('- Test "{}": SKIPPED'.format(fn.__name__))
|
||||
return 0
|
||||
|
||||
res = -1
|
||||
try:
|
||||
res = testrunner.run(fn, self.timeout, self.echo)
|
||||
|
||||
finally:
|
||||
if res == 0:
|
||||
print('- Test "{}": SUCCESS'.format(fn.__name__))
|
||||
else:
|
||||
print('- Test "{}": FAILED'.format(fn.__name__))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class TcpServer:
|
||||
@ -37,6 +62,143 @@ class TcpServer:
|
||||
return self.conn.recv(number_of_bytes, socket.MSG_WAITALL).decode('utf-8')
|
||||
|
||||
|
||||
class HostTcpNode:
|
||||
def __init__(self):
|
||||
self.opened = False
|
||||
self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.interface = get_host_tap_device()
|
||||
|
||||
def send(self, payload_to_send):
|
||||
self.sock.send(payload_to_send.encode('utf-8'))
|
||||
|
||||
def receive(self, sent_payload):
|
||||
total_bytes = len(sent_payload)
|
||||
assert self.sock.recv(total_bytes, socket.MSG_WAITALL).decode('utf-8') == sent_payload
|
||||
|
||||
def close(self):
|
||||
self.sock.close()
|
||||
self.opened = False
|
||||
|
||||
|
||||
class HostTcpClient(HostTcpNode):
|
||||
def __init__(self, target_addr, target_port):
|
||||
super().__init__()
|
||||
self.target_addr = target_addr
|
||||
self.target_port = target_port
|
||||
|
||||
def __enter__(self):
|
||||
if not self.opened:
|
||||
self.open()
|
||||
return self
|
||||
|
||||
def __exit__(self, _1, _2, _3):
|
||||
if self.opened:
|
||||
self.close()
|
||||
|
||||
def open(self):
|
||||
addrinfo = socket.getaddrinfo(
|
||||
self.target_addr + '%' + self.interface,
|
||||
self.target_port,
|
||||
type=socket.SOCK_STREAM
|
||||
)
|
||||
self.sock.connect(addrinfo[0][-1])
|
||||
self.opened = True
|
||||
|
||||
|
||||
class RiotTcpNode:
|
||||
def __init__(self, child):
|
||||
self.child = child
|
||||
self.opened = False
|
||||
|
||||
# Query local address of RIOT Node
|
||||
self.addr = get_riot_ll_addr(self.child)
|
||||
|
||||
def tcb_init(self):
|
||||
self.child.sendline('gnrc_tcp_tcb_init')
|
||||
self.child.expect_exact('gnrc_tcp_tcb_init: returns')
|
||||
|
||||
def send(self, timeout_ms, payload_to_send):
|
||||
total_bytes = len(payload_to_send)
|
||||
|
||||
# Verify that internal buffer can hold the given amount of data
|
||||
assert setup_internal_buffer(self.child) >= total_bytes
|
||||
|
||||
# Write data to RIOT nodes internal buffer
|
||||
write_data_to_internal_buffer(self.child, payload_to_send)
|
||||
|
||||
# Send buffer contents via tcp
|
||||
self.child.sendline('gnrc_tcp_send {}'.format(str(timeout_ms)))
|
||||
self.child.expect_exact('gnrc_tcp_send: sent {}'.format(str(total_bytes)))
|
||||
|
||||
# Verify that packet buffer is empty
|
||||
verify_pktbuf_empty(self.child)
|
||||
|
||||
def receive(self, timeout_ms, sent_payload):
|
||||
total_bytes = len(sent_payload)
|
||||
|
||||
# Verify that internal Buffer can hold the test data
|
||||
assert setup_internal_buffer(self.child) >= total_bytes
|
||||
|
||||
# Receive Data
|
||||
self.child.sendline('gnrc_tcp_recv {} {}'.format(timeout_ms, str(total_bytes)))
|
||||
self.child.expect_exact('gnrc_tcp_recv: received ' + str(total_bytes), timeout=20)
|
||||
|
||||
# Readout internal buffer content of RIOT Note
|
||||
assert read_data_from_internal_buffer(self.child, total_bytes) == sent_payload
|
||||
|
||||
# Verify that packet buffer is empty
|
||||
verify_pktbuf_empty(self.child)
|
||||
|
||||
def close(self):
|
||||
self.child.sendline('gnrc_tcp_close')
|
||||
self.child.expect_exact('gnrc_tcp_close: returns')
|
||||
verify_pktbuf_empty(self.child)
|
||||
self.opened = False
|
||||
|
||||
def abort(self):
|
||||
self.child.sendline('gnrc_tcp_abort')
|
||||
self.child.expect_exact('gnrc_tcp_abort: returns')
|
||||
verify_pktbuf_empty(self.child)
|
||||
self.opened = False
|
||||
|
||||
|
||||
class RiotTcpServer(RiotTcpNode):
|
||||
def __init__(self, child, listen_port, listen_addr='[::]'):
|
||||
super().__init__(child)
|
||||
|
||||
self.listening = False
|
||||
self.listen_port = str(listen_port)
|
||||
self.listen_addr = str(listen_addr)
|
||||
|
||||
self.tcb_init()
|
||||
|
||||
def __enter__(self):
|
||||
if not self.listening:
|
||||
self.listen()
|
||||
self.listening = True
|
||||
return self
|
||||
|
||||
def __exit__(self, _1, _2, _3):
|
||||
if self.listening:
|
||||
self.stop_listen()
|
||||
self.listening = False
|
||||
|
||||
def listen(self):
|
||||
self.child.sendline('gnrc_tcp_listen {}:{}'.format(self.listen_addr, self.listen_port))
|
||||
self.child.expect_exact('gnrc_tcp_listen: returns 0')
|
||||
|
||||
def accept(self, timeout_ms):
|
||||
self.child.sendline('gnrc_tcp_accept {}'.format(str(timeout_ms)))
|
||||
self.child.expect_exact('gnrc_tcp_accept: returns 0')
|
||||
self.opened = True
|
||||
|
||||
def stop_listen(self):
|
||||
self.child.sendline('gnrc_tcp_stop_listen')
|
||||
self.child.expect_exact('gnrc_tcp_stop_listen: returns')
|
||||
verify_pktbuf_empty(self.child)
|
||||
|
||||
|
||||
def generate_port_number():
|
||||
return random.randint(1024, 65535)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user