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:
parent
f2329252b8
commit
10872d9a85
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
==========
|
||||
|
@ -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 },
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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')
|
||||
|
150
tests/gnrc_tcp/tests/07-endpoint_construction.py
Executable file
150
tests/gnrc_tcp/tests/07-endpoint_construction.py
Executable 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")
|
Loading…
Reference in New Issue
Block a user