1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

gnrc_tcp: Add Endpoints for connection specification

This commit is contained in:
Simon Brummer 2020-01-02 15:20:20 +01:00
parent f2329252b8
commit 10872d9a85
11 changed files with 480 additions and 141 deletions

View File

@ -34,13 +34,62 @@
extern "C" {
#endif
/**
* @brief Address information for a single TCP connection endpoint.
* @extends sock_tcp_ep_t
*/
typedef struct {
int family; /**< IP address family. */
union {
#ifdef MODULE_GNRC_IPV6
uint8_t ipv6[sizeof(ipv6_addr_t)]; /**< IPv6 address storage */
#endif
uint8_t dummy; /**< Enable build without network module */
} addr; /**< IP address storage */
uint16_t netif; /**< Network interface ID */
uint16_t port; /**< Port number (in host byte order) */
} gnrc_tcp_ep_t;
/**
* @brief Initialize TCP connection endpoint.
*
* @param[in,out] ep Endpoint to initialize.
* @param[in] family Address family of @p addr.
* @param[in] addr Address for endpoint.
* @param[in] addr_size Size of @p addr.
* @param[in] port Port number for endpoint.
* @param[in] netif Network inferface to use.
*
* @return 0 on success.
* @return -EAFNOSUPPORT if @p address_family is not supported.
* @return -EINVAL if @p addr_size does not match @p family.
*/
int gnrc_tcp_ep_init(gnrc_tcp_ep_t *ep, int family, const uint8_t *addr, size_t addr_size,
uint16_t port, uint16_t netif);
/**
* @brief Construct TCP connection endpoint from string.
* @note This function expects @p str in the IPv6 "URL" notation.
* The following strings specify a valid endpoint:
* - [fe80::0a00:27ff:fe9f:7a5b%5]:8080 (with Port and Interface)
* - [2001::0200:f8ff:fe21:67cf]:8080 (with Port)
* - [2001::0200:f8ff:fe21:67cf] (addr only)
*
* @param[in,out] ep Endpoint to initialize.
* @param[in] str String containing IPv6-Address to parse.
*
* @return 0 on success.
* @return -EINVAL if parsing of @p str failed.
*/
int gnrc_tcp_ep_from_str(gnrc_tcp_ep_t *ep, const char *str);
/**
* @brief Initialize TCP
*
* @returns PID of TCP thread on success
* -1 if TCB is already running.
* -EINVAL, if priority is greater than or equal SCHED_PRIO_LEVELS
* -EOVERFLOW, if there are too many threads running.
* @return PID of TCP thread on success
* @return -1 if TCB 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);
@ -57,31 +106,27 @@ void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb);
*
* @pre gnrc_tcp_tcb_init() must have been successfully called.
* @pre @p tcb must not be NULL
* @pre @p target_addr must not be NULL.
* @pre @p target_port must not be 0.
* @pre @p remote must not be NULL.
*
* @note Blocks until a connection has been established or an error occurred.
*
* @param[in,out] tcb TCB holding the connection information.
* @param[in] address_family Address family of @p target_addr.
* @param[in] target_addr Pointer to target address.
* @param[in] target_port Target port number.
* @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.
* @param[in,out] tcb TCB holding the connection information.
* @param[in] remote Remote endpoint of the host 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.
*
* @returns 0 on success.
* -EAFNOSUPPORT if @p address_family is not supported.
* -EINVAL if @p address_family is not the same the address_family use by the TCB.
* @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.
* or @p target_addr is invalid.
* -EISCONN if TCB is already in use.
* -ENOMEM if the receive buffer for the TCB could not be allocated.
* -EADDRINUSE if @p local_port is already used by another connection.
* -ETIMEDOUT if the connection could not be opened.
* -ECONNREFUSED if the connection was reset by the peer.
* @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.
*/
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
char *target_addr, uint16_t target_port,
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote,
uint16_t local_port);
/**
@ -89,30 +134,25 @@ int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
*
* @pre gnrc_tcp_tcb_init() must have been successfully called.
* @pre @p 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.
* @pre @p local must not be NULL.
* @pre port in @p local must not be zero.
*
* @note Blocks until a connection has been established (incoming connection request
* to @p local_port) or an error occurred.
*
* @param[in,out] tcb TCB holding the connection information.
* @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 @p local_addr.
* If NULL a connection request to all local ip
* addresses is valid.
* @param[in] local_port Port number 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.
*
* @returns 0 on success.
* -EAFNOSUPPORT if local_addr != NULL and @p address_family is not supported.
* -EINVAL if @p address_family is not the same the address_family used in TCB.
* or @p target_addr is invalid.
* -EISCONN if TCB is already in use.
* -ENOMEM if the receive buffer for the TCB could not be allocated.
* @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 "GNRC_TCP_RCV_BUFFERS".
*/
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
const char *local_addr, uint16_t local_port);
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *local);
/**
* @brief Transmit data to connected peer.
@ -130,11 +170,11 @@ int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
* the function returns after user_timeout_duration_us.
* If zero, no timeout will be triggered.
*
* @returns The number of successfully transmitted bytes.
* -ENOTCONN if connection is not established.
* -ECONNRESET if connection was reset by the peer.
* -ECONNABORTED if the connection was aborted.
* -ETIMEDOUT if @p user_timeout_duration_us expired.
* @return The number of successfully transmitted bytes.
* @return -ENOTCONN if connection is not established.
* @return -ECONNRESET if connection was reset 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);
@ -159,13 +199,13 @@ ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,
* blocks until data is available or
* @p user_timeout_duration_us microseconds passed.
*
* @returns The number of bytes read into @p data.
* 0, if the connection is closing and no further data can be read.
* -ENOTCONN if connection is not established.
* -EAGAIN if user_timeout_duration_us is zero and no data is available.
* -ECONNRESET if connection was reset by the peer.
* -ECONNABORTED if the connection was aborted.
* -ETIMEDOUT if @p user_timeout_duration_us expired.
* @return The number of bytes read into @p data.
* @return 0, if the connection is closing and no further data can be read.
* @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 reset 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);
@ -196,10 +236,10 @@ void gnrc_tcp_abort(gnrc_tcp_tcb_t *tcb);
* @param[in] hdr Gnrc_pktsnip that contains TCP header.
* @param[in] pseudo_hdr Gnrc_pktsnip that contains network layer header.
*
* @returns 0 on success.
* -EFAULT if @p hdr or pseudo_hdr were NULL
* -EBADMSG if @p hdr is not of type GNRC_NETTYPE_TCP
* -ENOENT if @p pseudo_hdr protocol is unsupported.
* @return 0 on success.
* @return -EFAULT if @p hdr or pseudo_hdr were NULL
* @return -EBADMSG if @p hdr is not of type GNRC_NETTYPE_TCP
* @return -ENOENT if @p pseudo_hdr protocol is unsupported.
*/
int gnrc_tcp_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_hdr);
@ -210,8 +250,8 @@ int gnrc_tcp_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_h
* @param[in] src Source port number.
* @param[in] dst Destination port number.
*
* @returns Not NULL on success.
* NULL if TCP header was not allocated.
* @return Not NULL on success.
* @return NULL if TCP header was not allocated.
*/
gnrc_pktsnip_t *gnrc_tcp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src, uint16_t dst);

View File

@ -115,18 +115,18 @@ static void _setup_timeout(xtimer_t *timer, const uint32_t duration, const xtime
* -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, char *target_addr, uint16_t target_port,
const char *local_addr, uint16_t local_port, uint8_t passive)
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)
{
msg_t msg;
xtimer_t connection_timeout;
cb_arg_t connection_timeout_arg = {MSG_TYPE_CONNECTION_TIMEOUT, &(tcb->mbox)};
int8_t ret = 0;
int ret = 0;
/* Lock the TCB for this function call */
mutex_lock(&(tcb->function_lock));
/* Connection is already connected: Return -EISCONN */
/* TCB is already connected: Return -EISCONN */
if (tcb->state != FSM_STATE_CLOSED) {
mutex_unlock(&(tcb->function_lock));
return -EISCONN;
@ -143,46 +143,47 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, char *target_addr, uint16_t targe
if (passive) {
/* Mark connection as passive opend */
tcb->status |= STATUS_PASSIVE;
if (local_addr == NULL) {
tcb->status |= STATUS_ALLOW_ANY_ADDR;
}
#ifdef MODULE_GNRC_IPV6
/* If local address is specified: Copy it into TCB */
else if (tcb->address_family == AF_INET6) {
if (ipv6_addr_from_str((ipv6_addr_t *) tcb->local_addr, local_addr) == NULL) {
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) {
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : Invalid peer addr\n");
return -EINVAL;
}
if (ipv6_addr_is_unspecified((ipv6_addr_t *) tcb->local_addr)) {
tcb->status |= STATUS_ALLOW_ANY_ADDR;
}
}
#else
/* Suppress Compiler Warnings */
(void) target_addr;
(void) remote;
(void) local_addr;
#endif
/* Set port number to listen on */
tcb->local_port = local_port;
}
/* Setup active connection */
else {
assert(remote != NULL);
/* Parse target address and port number into TCB */
#ifdef MODULE_GNRC_IPV6
if ((target_addr != NULL) && (tcb->address_family == AF_INET6)) {
if (tcb->address_family == AF_INET6) {
/* Extract interface (optional) specifier from target address */
char *ll_iface = ipv6_addr_split_iface(target_addr);
if (ipv6_addr_from_str((ipv6_addr_t *) tcb->peer_addr, target_addr) == NULL) {
/* Store Address information in TCB */
if (memcpy(tcb->peer_addr, remote->addr.ipv6, sizeof(tcb->peer_addr)) == NULL) {
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : Invalid peer addr\n");
return -EINVAL;
}
/* In case the given address is link-local: Memorize the interface Id if existing. */
if ((ll_iface) && ipv6_addr_is_link_local((ipv6_addr_t *) tcb->peer_addr)) {
tcb->ll_iface = atoi(ll_iface);
}
tcb->ll_iface = remote->netif;
}
#endif
/* Assign port numbers, verification happens in fsm */
tcb->local_port = local_port;
tcb->peer_port = target_port;
tcb->peer_port = remote->port;
/* Setup connection timeout: Put timeout message in TCBs mbox on expiration */
_setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
@ -194,7 +195,7 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, char *target_addr, uint16_t targe
if (ret == -ENOMEM) {
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : Out of receive buffers.\n");
}
else if(ret == -EADDRINUSE) {
else if (ret == -EADDRINUSE) {
DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : local_port is already in use.\n");
}
@ -248,6 +249,132 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, char *target_addr, uint16_t targe
}
/* External GNRC TCP API */
int gnrc_tcp_ep_init(gnrc_tcp_ep_t *ep, int family, const uint8_t *addr, size_t addr_size,
uint16_t port, uint16_t netif)
{
#ifdef MODULE_GNRC_IPV6
if (family != AF_INET6) {
return -EAFNOSUPPORT;
}
if (addr == NULL && addr_size == 0) {
ipv6_addr_set_unspecified((ipv6_addr_t *) ep->addr.ipv6);
}
else if (addr_size == sizeof(ipv6_addr_t)) {
memcpy(ep->addr.ipv6, addr, sizeof(ipv6_addr_t));
}
else {
return -EINVAL;
}
#else
/* Suppress Compiler Warnings */
(void) addr;
(void) addr_size;
return -EAFNOSUPPORT;
#endif
ep->family = family;
ep->port = port;
ep->netif = netif;
return 0;
}
int gnrc_tcp_ep_from_str(gnrc_tcp_ep_t *ep, const char *str)
{
assert(str);
unsigned port = 0;
unsigned netif = 0;
/* Examine given string */
char *addr_begin = strchr(str, '[');
char *addr_end = strchr(str, ']');
/* 1) Ensure that str contains a single pair of brackets */
if (!addr_begin || !addr_end || strchr(addr_begin + 1, '[') || strchr(addr_end + 1, ']')) {
return -EINVAL;
}
/* 2) Ensure that the first character is the opening bracket */
else if (addr_begin != str) {
return -EINVAL;
}
/* 3) Examine optional port number */
char *port_begin = strchr(addr_end, ':');
if (port_begin) {
/* 3.1) Ensure that there are characters left to parse after ':'. */
if (*(++port_begin) == '\0') {
return -EINVAL;
}
/* 3.2) Ensure that port is a number (atol, does not report errors) */
for (char *ptr = port_begin; *ptr; ++ptr) {
if ((*ptr < '0') || ('9' < *ptr)) {
return -EINVAL;
}
}
/* 3.3) Read and verify that given number port is within range */
port = atol(port_begin);
if (port > 0xFFFF) {
return -EINVAL;
}
}
/* 4) Examine optional interface identifier. */
char *if_begin = strchr(str, '%');
if (if_begin) {
/* 4.1) Ensure that the identifier is not empty and within brackets. */
if (addr_end <= (++if_begin)) {
return -EINVAL;
}
/* 4.2) Ensure that the identifier is a number (atol, does not report errors) */
for (char *ptr = if_begin; ptr != addr_end; ++ptr) {
if ((*ptr < '0') || ('9' < *ptr)) {
return -EINVAL;
}
}
/* 4.3) Read and replace addr_end with if_begin. */
netif = atol(if_begin);
addr_end = if_begin - 1;
}
#ifdef MODULE_GNRC_IPV6
/* 5) Try to parse IP Address. Construct Endpoint on after success. */
char tmp[IPV6_ADDR_MAX_STR_LEN];
/* 5.1) Verify address length and copy address into temporary buffer.
* This is required to preserve constness of input.
*/
int len = addr_end - (++addr_begin);
if (0 <= len && len < (int) sizeof(tmp)) {
memcpy(tmp, addr_begin, len);
tmp[len] = '\0';
}
else {
return -EINVAL;
}
/* 5.2) Try to read address into endpoint. */
if (ipv6_addr_from_str((ipv6_addr_t *) ep->addr.ipv6, tmp) == NULL) {
return -EINVAL;
}
ep->family = AF_INET6;
#else
/* Suppress Compiler Warnings */
(void) port;
(void) netif;
return -EINVAL;
#endif
ep->port = (uint16_t) port;
ep->netif = (uint16_t) netif;
return 0;
}
int gnrc_tcp_init(void)
{
/* Guard: Check if thread is already running */
@ -284,17 +411,15 @@ void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb)
mutex_init(&(tcb->function_lock));
}
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
char *target_addr, uint16_t target_port,
uint16_t local_port)
int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *remote, uint16_t local_port)
{
assert(tcb != NULL);
assert(target_addr != NULL);
assert(target_port != PORT_UNSPEC);
assert(remote != NULL);
assert(remote->port != PORT_UNSPEC);
/* Check if AF-Family of target_addr is supported */
/* Check if given AF-Family in remote is supported */
#ifdef MODULE_GNRC_IPV6
if (address_family != AF_INET6) {
if (remote->family != AF_INET6) {
return -EAFNOSUPPORT;
}
#else
@ -302,35 +427,36 @@ int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
#endif
/* Check if AF-Family for target address matches internally used AF-Family */
if (tcb->address_family != address_family) {
if (remote->family != tcb->address_family) {
return -EINVAL;
}
/* Proceed with connection opening */
return _gnrc_tcp_open(tcb, target_addr, target_port, NULL, local_port, 0);
return _gnrc_tcp_open(tcb, remote, NULL, local_port, 0);
}
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, uint8_t address_family,
const char *local_addr, uint16_t local_port)
int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const gnrc_tcp_ep_t *local)
{
assert(tcb != NULL);
assert(local_port != PORT_UNSPEC);
assert(local != NULL);
assert(local->port != PORT_UNSPEC);
/* Check AF-Family support if local address was supplied */
if (local_addr != NULL) {
/* Check if given AF-Family in local is supported */
#ifdef MODULE_GNRC_IPV6
if (address_family != AF_INET6) {
return -EAFNOSUPPORT;
}
#else
if (local->family != AF_INET6) {
return -EAFNOSUPPORT;
#endif
/* Check if AF-Family matches internally used AF-Family */
if (tcb->address_family != address_family) {
return -EINVAL;
}
}
/* Check if AF-Family matches internally used AF-Family */
if (local->family != tcb->address_family) {
return -EINVAL;
}
/* Proceed with connection opening */
return _gnrc_tcp_open(tcb, NULL, 0, local_addr, local_port, 1);
return _gnrc_tcp_open(tcb, NULL, local->addr.ipv6, local->port, 1);
#else
return -EAFNOSUPPORT;
#endif
}
ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,

View File

@ -25,7 +25,10 @@ in the tests directory.
6) 06-receive_data_closed_conn.py
This test covers accessing received data after receiving a FIN packet. If the connection was closed
by the peer, a call to gnrc_tcp_recv must return directly with all currently received data
or zero if there is no data. The function must return immediatly dispite any given timeout.
or zero if there is no data. The function must return immediately despite any given timeout.
7) 07-endpoint_construction.py
This test ensures the correctness of the endpoint construction.
Setup
==========

View File

@ -31,17 +31,6 @@ void dump_args(int argc, char **argv)
printf("\n");
}
int get_af_family(char *family)
{
if (memcmp(family, "AF_INET6", sizeof("AF_INET6")) == 0) {
return AF_INET6;
}
else if (memcmp(family, "AF_INET", sizeof("AF_INET")) == 0) {
return AF_INET;
}
return AF_UNSPEC;
}
/* API Export for test script */
int buffer_init_cmd(int argc, char **argv)
{
@ -91,6 +80,44 @@ int buffer_read_cmd(int argc, char **argv)
return 0;
}
int gnrc_tcp_ep_from_str_cmd(int argc, char **argv)
{
dump_args(argc, argv);
gnrc_tcp_ep_t ep;
int err = gnrc_tcp_ep_from_str(&ep, argv[1]);
switch (err) {
case -EINVAL:
printf("%s: returns -EINVAL\n", argv[0]);
break;
default:
printf("%s: returns %d\n", argv[0], err);
}
if (err == 0) {
char addr_as_str[IPV6_ADDR_MAX_STR_LEN];
switch (ep.family) {
case AF_INET6:
printf("Family: AF_INET6\n");
ipv6_addr_to_str(addr_as_str, (ipv6_addr_t *) ep.addr.ipv6,
sizeof(addr_as_str));
printf("Addr: %s\n", addr_as_str);
break;
case AF_INET:
printf("Family: AF_INET\n");
break;
default:
printf("Family: %d\n", ep.family);
}
printf("Port: %d\n", ep.port);
printf("Netif: %d\n", ep.netif);
}
return err;
}
int gnrc_tcp_tcb_init_cmd(int argc, char **argv)
{
dump_args(argc, argv);
@ -101,13 +128,12 @@ int gnrc_tcp_tcb_init_cmd(int argc, char **argv)
int gnrc_tcp_open_active_cmd(int argc, char **argv)
{
dump_args(argc, argv);
int af_family = get_af_family(argv[1]);
char *target_addr = argv[2];
uint16_t target_port = atol(argv[3]);
uint16_t local_port = atol(argv[4]);
int err = gnrc_tcp_open_active(&tcb, af_family, target_addr, target_port,
local_port);
gnrc_tcp_ep_t remote;
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);
switch (err) {
case -EAFNOSUPPORT:
printf("%s: returns -EAFNOSUPPORT\n", argv[0]);
@ -146,19 +172,11 @@ int gnrc_tcp_open_active_cmd(int argc, char **argv)
int gnrc_tcp_open_passive_cmd(int argc, char **argv)
{
dump_args(argc, argv);
int af_family = get_af_family(argv[1]);
char *local_addr = NULL;
uint16_t local_port = 0;
if (argc == 3) {
local_port = atol(argv[2]);
}
else if (argc == 4) {
local_addr = argv[2];
local_port = atol(argv[3]);
}
gnrc_tcp_ep_t local;
gnrc_tcp_ep_from_str(&local, argv[1]);
int err = gnrc_tcp_open_passive(&tcb, af_family, local_addr, local_port);
int err = gnrc_tcp_open_passive(&tcb, &local);
switch (err) {
case -EAFNOSUPPORT:
printf("%s: returns -EAFNOSUPPORT\n", argv[0]);
@ -275,6 +293,8 @@ int gnrc_tcp_abort_cmd(int argc, char **argv)
/* Exporting GNRC TCP Api to for shell usage */
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 },

View File

@ -32,7 +32,7 @@ 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 AF_INET6 ' + target_addr + ' ' + str(port) + ' 0')
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
child.expect_exact('gnrc_tcp_open_active: returns 0')
# Close connection and verify that pktbuf is cleared

View File

@ -37,7 +37,7 @@ def testfunc(child):
# Setup RIOT Node wait for incoming connections from host system
child.sendline('gnrc_tcp_tcb_init')
child.sendline('gnrc_tcp_open_passive AF_INET6 ' + str(port))
child.sendline('gnrc_tcp_open_passive [::]:{}'.format(str(port)))
client_handle.start()
child.expect_exact('gnrc_tcp_open_passive: returns 0')

View File

@ -40,7 +40,7 @@ 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 AF_INET6 ' + target_addr + ' ' + str(port) + ' 0')
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
child.expect_exact('gnrc_tcp_open_active: returns 0')
# Send data from RIOT Node to Linux

View File

@ -40,7 +40,7 @@ def testfunc(child):
# Setup RIOT Node to connect to Hostsystems TCP Server
child.sendline('gnrc_tcp_tcb_init')
child.sendline('gnrc_tcp_open_active AF_INET6 ' + target_addr + " " + str(port) + ' 0')
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
child.expect_exact('gnrc_tcp_open_active: returns 0')
# Accept Data sent by the host system

View File

@ -32,10 +32,10 @@ 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 AF_INET6 {}'.format(port))
child.expect(r'gnrc_tcp_open_passive: argc=3, '
child.sendline('gnrc_tcp_open_passive [::]:{}'.format(port))
child.expect(r'gnrc_tcp_open_passive: argc=2, '
r'argv\[0\] = gnrc_tcp_open_passive, '
r'argv\[1\] = AF_INET6, argv\[2\] = (\d+)\r\n')
r'argv\[1\] = \[::\]:(\d+)\r\n')
assert int(child.match.group(1)) == port
try:

View File

@ -42,7 +42,7 @@ def testfunc(child):
# Setup RIOT Node to connect to Hostsystems TCP Server
child.sendline('gnrc_tcp_tcb_init')
child.sendline('gnrc_tcp_open_active AF_INET6 ' + target_addr + " " + str(port) + ' 0')
child.sendline('gnrc_tcp_open_active [{}]:{} 0'.format(target_addr, str(port)))
child.expect_exact('gnrc_tcp_open_active: returns 0')
# Initiate connection teardown from test host
@ -60,7 +60,7 @@ def testfunc(child):
child.expect_exact('gnrc_tcp_recv: received ' + str(half_data_len))
assert read_data_from_internal_buffer(child, half_data_len) == data[half_data_len:]
# Buffer should have been read entirly and the connection was closed, there can be no new data.
# Buffer should have been read entirely and the connection was closed, there can be no new data.
# Reading with a timeout must return 0 not -ETIMEOUT
child.sendline('gnrc_tcp_recv 1000000 ' + str(half_data_len))
child.expect_exact('gnrc_tcp_recv: returns 0')

View File

@ -0,0 +1,150 @@
#!/usr/bin/env python3
# Copyright (C) 2020 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 testrunner import run
from shared_func import sudo_guard
def test_build_unspec(child):
valid = '[::]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(valid))
child.expect_exact('gnrc_tcp_ep_from_str: returns 0')
child.expect_exact('Family: AF_INET6')
child.expect_exact('Addr: ::')
child.expect_exact('Port: 0')
child.expect_exact('Netif: 0')
def test_build_unspec_with_port(child):
valid = '[::]:8080'
child.sendline('gnrc_tcp_ep_from_str {}'.format(valid))
child.expect_exact('gnrc_tcp_ep_from_str: returns 0')
child.expect_exact('Family: AF_INET6')
child.expect_exact('Addr: ::')
child.expect_exact('Port: 8080')
child.expect_exact('Netif: 0')
def test_build_unspec_with_interface_and_port(child):
valid = '[::%5]:8080'
child.sendline('gnrc_tcp_ep_from_str {}'.format(valid))
child.expect_exact('gnrc_tcp_ep_from_str: returns 0')
child.expect_exact('Family: AF_INET6')
child.expect_exact('Addr: ::')
child.expect_exact('Port: 8080')
child.expect_exact('Netif: 5')
def test_build_addr_with_interface_and_port(child):
valid = '[fe80::68bf:dbff:fe05:c35e%5]:8080'
child.sendline('gnrc_tcp_ep_from_str {}'.format(valid))
child.expect_exact('gnrc_tcp_ep_from_str: returns 0')
child.expect_exact('Family: AF_INET6')
child.expect_exact('Addr: fe80::68bf:dbff:fe05:c35e')
child.expect_exact('Port: 8080')
child.expect_exact('Netif: 5')
def test_einval_no_brackets(child):
invalid = '::'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_much_brackets(child):
invalid = '[:]:]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_swaped_brackets(child):
invalid = ']::['
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_no_addr_in_brackets(child):
invalid = '[]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_first_char_no_open_bracket(child):
invalid = 'a[]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_port_token_but_no_number(child):
invalid = '[::]:'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_port_inval_char(child):
invalid = '[::]:103f2'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_port_out_of_range(child):
invalid = '[::]:65536'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_interface_id_after_bracket(child):
invalid = '[::]%5'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_interface_id_token_but_no_number(child):
invalid = '[::%]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_interface_id_inval_char(child):
invalid = '[::%5a]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def test_einval_interface_addr_to_short(child):
invalid = '[:%5]'
child.sendline('gnrc_tcp_ep_from_str {}'.format(invalid))
child.expect_exact('gnrc_tcp_ep_from_str: returns -EINVAL')
def main(child):
# 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:
try:
test(child)
print('- {} SUCCESS'.format(test.__name__))
except Exception:
print('- {} FAILED'.format(test.__name__))
if __name__ == '__main__':
sudo_guard()
res = run(main, timeout=0.5, echo=False)
if res != 0:
sys.exit(res)
print(os.path.basename(sys.argv[0]) + ": success\n")