diff --git a/sys/include/net/gnrc/tcp.h b/sys/include/net/gnrc/tcp.h index 797ea1efd4..dfaf53e3aa 100644 --- a/sys/include/net/gnrc/tcp.h +++ b/sys/include/net/gnrc/tcp.h @@ -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); diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c index bfca39f258..3a16ac899f 100644 --- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c +++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c @@ -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, diff --git a/tests/gnrc_tcp/README.md b/tests/gnrc_tcp/README.md index 12e881ce3a..98c1565db6 100644 --- a/tests/gnrc_tcp/README.md +++ b/tests/gnrc_tcp/README.md @@ -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 ========== diff --git a/tests/gnrc_tcp/main.c b/tests/gnrc_tcp/main.c index 79b741edbe..cbcdfe377c 100644 --- a/tests/gnrc_tcp/main.c +++ b/tests/gnrc_tcp/main.c @@ -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 }, diff --git a/tests/gnrc_tcp/tests/01-conn_lifecycle_as_client.py b/tests/gnrc_tcp/tests/01-conn_lifecycle_as_client.py index d96c9e7ea9..7191d757d7 100755 --- a/tests/gnrc_tcp/tests/01-conn_lifecycle_as_client.py +++ b/tests/gnrc_tcp/tests/01-conn_lifecycle_as_client.py @@ -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 diff --git a/tests/gnrc_tcp/tests/02-conn_lifecycle_as_server.py b/tests/gnrc_tcp/tests/02-conn_lifecycle_as_server.py index b5b757f689..bf8a608638 100755 --- a/tests/gnrc_tcp/tests/02-conn_lifecycle_as_server.py +++ b/tests/gnrc_tcp/tests/02-conn_lifecycle_as_server.py @@ -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') diff --git a/tests/gnrc_tcp/tests/03-send_data.py b/tests/gnrc_tcp/tests/03-send_data.py index 01e221c43b..1083bd2876 100755 --- a/tests/gnrc_tcp/tests/03-send_data.py +++ b/tests/gnrc_tcp/tests/03-send_data.py @@ -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 diff --git a/tests/gnrc_tcp/tests/04-receive_data.py b/tests/gnrc_tcp/tests/04-receive_data.py index f67d46fd4d..829f8be4f4 100755 --- a/tests/gnrc_tcp/tests/04-receive_data.py +++ b/tests/gnrc_tcp/tests/04-receive_data.py @@ -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 diff --git a/tests/gnrc_tcp/tests/05-garbage-pkts.py b/tests/gnrc_tcp/tests/05-garbage-pkts.py index 7b31826571..ce92a22c63 100755 --- a/tests/gnrc_tcp/tests/05-garbage-pkts.py +++ b/tests/gnrc_tcp/tests/05-garbage-pkts.py @@ -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: diff --git a/tests/gnrc_tcp/tests/06-receive_data_closed_conn.py b/tests/gnrc_tcp/tests/06-receive_data_closed_conn.py index 885f080d27..063a380add 100755 --- a/tests/gnrc_tcp/tests/06-receive_data_closed_conn.py +++ b/tests/gnrc_tcp/tests/06-receive_data_closed_conn.py @@ -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') diff --git a/tests/gnrc_tcp/tests/07-endpoint_construction.py b/tests/gnrc_tcp/tests/07-endpoint_construction.py new file mode 100755 index 0000000000..7bb402c8ec --- /dev/null +++ b/tests/gnrc_tcp/tests/07-endpoint_construction.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 Simon Brummer +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +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")