From 686aabaa0a0ae0190de664517f9a88ead046381e Mon Sep 17 00:00:00 2001 From: Simon Brummer <simon.brummer@posteo.de> Date: Fri, 11 May 2018 10:06:14 +0200 Subject: [PATCH] gnrc_tcp: handle link local IPv6 addresses correctly --- sys/include/net/gnrc/tcp.h | 66 ++++++++++--------- sys/include/net/gnrc/tcp/tcb.h | 1 + sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c | 38 +++++++---- .../transport_layer/tcp/gnrc_tcp_eventloop.c | 20 +++++- .../gnrc/transport_layer/tcp/gnrc_tcp_fsm.c | 13 ++++ .../gnrc/transport_layer/tcp/gnrc_tcp_pkt.c | 42 ++++++++++++ tests/gnrc_tcp_client/Makefile | 3 +- tests/gnrc_tcp_client/main.c | 16 ++--- tests/gnrc_tcp_server/Makefile | 1 + 9 files changed, 144 insertions(+), 56 deletions(-) diff --git a/sys/include/net/gnrc/tcp.h b/sys/include/net/gnrc/tcp.h index 0902e8d836..6b86c0cf4b 100644 --- a/sys/include/net/gnrc/tcp.h +++ b/sys/include/net/gnrc/tcp.h @@ -52,36 +52,37 @@ int gnrc_tcp_init(void); */ void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb); - /** - * @brief Opens a connection actively. - * - * @pre gnrc_tcp_tcb_init() must have been successfully called. - * @pre @p tcb must not be NULL - * @pre @p target_addr must not be NULL. - * @pre @p target_port must not be 0. - * - * @note Blocks until a connection has been established or an error occured. - * - * @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. - * - * @returns Zero 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. - * -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 resetted by the peer. - */ -int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const uint8_t address_family, - const uint8_t *target_addr, const uint16_t target_port, - const uint16_t local_port); +/** + * @brief Opens a connection actively. + * + * @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. + * + * @note Blocks until a connection has been established or an error occured. + * + * @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. + * + * @returns Zero 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. + * 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 resetted by the peer. + */ +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); /** * @brief Opens a connection passively, by waiting for an incomming request. @@ -105,12 +106,13 @@ int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const uint8_t address_family, * @returns Zero 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. * Hint: Increase "GNRC_TCP_RCV_BUFFERS". */ -int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const uint8_t address_family, - const uint8_t *local_addr, const uint16_t local_port); +int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, uint8_t address_family, + const char *local_addr, uint16_t local_port); /** * @brief Transmit data to connected peer. diff --git a/sys/include/net/gnrc/tcp/tcb.h b/sys/include/net/gnrc/tcp/tcb.h index eb54672ed6..dadb1b7952 100644 --- a/sys/include/net/gnrc/tcp/tcb.h +++ b/sys/include/net/gnrc/tcp/tcb.h @@ -53,6 +53,7 @@ typedef struct _transmission_control_block { #ifdef MODULE_GNRC_IPV6 uint8_t local_addr[sizeof(ipv6_addr_t)]; /**< Local IP address */ uint8_t peer_addr[sizeof(ipv6_addr_t)]; /**< Peer IP address */ + int8_t ll_iface; /**< Link layer interface id to use. */ #endif uint16_t local_port; /**< Local connections port number */ uint16_t peer_port; /**< Peer connections port number */ diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c index b72304a826..273d3b3703 100644 --- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c +++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c @@ -112,8 +112,8 @@ 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 resetted by the peer. */ -static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const uint8_t *target_addr, uint16_t target_port, - const uint8_t *local_addr, uint16_t local_port, uint8_t passive) +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) { msg_t msg; xtimer_t connection_timeout; @@ -146,7 +146,10 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const uint8_t *target_addr, uint1 #ifdef MODULE_GNRC_IPV6 /* If local address is specified: Copy it into TCB */ else if (tcb->address_family == AF_INET6) { - memcpy(tcb->local_addr, local_addr, sizeof(ipv6_addr_t)); + if (ipv6_addr_from_str((ipv6_addr_t *) tcb->local_addr, local_addr) == NULL) { + DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : Invalid peer addr\n"); + return -EINVAL; + } } #endif /* Set port number to listen on */ @@ -154,10 +157,21 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const uint8_t *target_addr, uint1 } /* Setup active connection */ else { - /* Copy target address and port number into TCB */ + /* Parse target address and port number into TCB */ #ifdef MODULE_GNRC_IPV6 if ((target_addr != NULL) && (tcb->address_family == AF_INET6)) { - memcpy(tcb->peer_addr, target_addr, sizeof(ipv6_addr_t)); + + /* Extract interface (optional) specifier from target address */ + int ll_iface = ipv6_addr_split_iface(target_addr); + if (ipv6_addr_from_str((ipv6_addr_t *) tcb->peer_addr, target_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 > 0) && ipv6_addr_is_link_local((ipv6_addr_t *) tcb->peer_addr)) { + tcb->ll_iface = ll_iface; + } } #endif /* Assign port numbers, verfication happens in fsm */ @@ -172,10 +186,10 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, const uint8_t *target_addr, uint1 /* Call FSM with event: CALL_OPEN */ ret = _fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0); if (ret == -ENOMEM) { - DEBUG("gnrc_tcp.c : gnrc_tcp_connect() : Out of receive buffers.\n"); + DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : Out of receive buffers.\n"); } else if(ret == -EADDRINUSE) { - DEBUG("gnrc_tcp.c : gnrc_tcp_connect() : local_port is already in use.\n"); + DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : local_port is already in use.\n"); } /* Wait until a connection was established or closed */ @@ -245,9 +259,9 @@ 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, const uint8_t address_family, - const uint8_t *target_addr, const uint16_t target_port, - const uint16_t local_port) +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) { assert(tcb != NULL); assert(target_addr != NULL); @@ -270,8 +284,8 @@ int gnrc_tcp_open_active(gnrc_tcp_tcb_t *tcb, const uint8_t address_family, return _gnrc_tcp_open(tcb, target_addr, target_port, NULL, local_port, 0); } -int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, const uint8_t address_family, - const uint8_t *local_addr, const uint16_t local_port) +int gnrc_tcp_open_passive(gnrc_tcp_tcb_t *tcb, uint8_t address_family, + const char *local_addr, uint16_t local_port) { assert(tcb != NULL); assert(local_port != PORT_UNSPEC); diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_eventloop.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_eventloop.c index da38da1394..85769f45a7 100644 --- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_eventloop.c +++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_eventloop.c @@ -46,19 +46,35 @@ static msg_t _eventloop_msg_queue[TCP_EVENTLOOP_MSG_QUEUE_SIZE]; */ static int _send(gnrc_pktsnip_t *pkt) { + assert(pkt != NULL); + /* NOTE: In sending direction: pkt = nw, nw->next = tcp, tcp->next = payload */ - gnrc_pktsnip_t *tcp; + gnrc_pktsnip_t *tcp = NULL; + gnrc_pktsnip_t *nw = NULL; /* Search for TCP header */ LL_SEARCH_SCALAR(pkt, tcp, type, GNRC_NETTYPE_TCP); + /* cppcheck-suppress knownConditionTrueFalse */ if (tcp == NULL) { DEBUG("gnrc_tcp_eventloop : _send() : tcp header missing.\n"); gnrc_pktbuf_release(pkt); return -EBADMSG; } + /* Search for network layer */ +#ifdef MODULE_GNRC_IPV6 + /* Get IPv6 header, discard packet if doesn't contain an ipv6 header */ + LL_SEARCH_SCALAR(pkt, nw, type, GNRC_NETTYPE_IPV6); + if (nw == NULL) { + DEBUG("gnrc_tcp_eventloop.c : _send() : pkt contains no ipv6 layer header\n"); + gnrc_pktbuf_release(pkt); + return -EBADMSG; + } +#endif + /* Dispatch packet to network layer */ - if (!gnrc_netapi_dispatch_send(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) { + assert(nw != NULL); + if (!gnrc_netapi_dispatch_send(nw->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) { DEBUG("gnrc_tcp_eventloop : _send() : network layer not found\n"); gnrc_pktbuf_release(pkt); } diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c index ddb4598936..c877aa92fc 100644 --- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c +++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c @@ -474,6 +474,19 @@ static int _fsm_rcvd_pkt(gnrc_tcp_tcb_t *tcb, gnrc_pktsnip_t *in_pkt) if (snp->type == GNRC_NETTYPE_IPV6 && tcb->address_family == AF_INET6) { memcpy(tcb->local_addr, &((ipv6_hdr_t *)ip)->dst, sizeof(ipv6_addr_t)); memcpy(tcb->peer_addr, &((ipv6_hdr_t *)ip)->src, sizeof(ipv6_addr_t)); + + /* In case peer_addr is link local: Store interface Id in tcb */ + if (ipv6_addr_is_link_local((ipv6_addr_t *) tcb->peer_addr)) { + gnrc_pktsnip_t *tmp = NULL; + LL_SEARCH_SCALAR(in_pkt, tmp, type, GNRC_NETTYPE_NETIF); + /* cppcheck-suppress knownConditionTrueFalse */ + if (tmp == NULL) { + DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt() :\ + incomming packet had no netif header\n"); + return 0; + } + tcb->ll_iface = ((gnrc_netif_hdr_t *)tmp->data)->if_pid; + } } #else DEBUG("gnrc_tcp_fsm.c : _fsm_rcvd_pkt() : Received address was not stored\n"); diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_pkt.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_pkt.c index d3857ea307..b1f1f1c407 100644 --- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_pkt.c +++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_pkt.c @@ -109,6 +109,30 @@ int _pkt_build_reset_from_pkt(gnrc_pktsnip_t **out_pkt, gnrc_pktsnip_t *in_pkt) return -ENOMEM; } *out_pkt = ip6_snp; + + /* Add netif header in case the receiver addr sent from a link local address */ + if (ipv6_addr_is_link_local(&ip6_hdr->src)) { + + /* Search for netif header in received packet */ + gnrc_pktsnip_t *net_snp; + LL_SEARCH_SCALAR(in_pkt, net_snp, type, GNRC_NETTYPE_NETIF); + gnrc_netif_hdr_t *net_hdr = (gnrc_netif_hdr_t *)net_snp->data; + + /* Allocate new header and set interface id */ + net_snp = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + if (net_snp == NULL) { + DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() :\ + Can't alloc buffer for netif Header.\n"); + gnrc_pktbuf_release(ip6_snp); + *(out_pkt) = NULL; + return -ENOMEM; + } + else { + ((gnrc_netif_hdr_t *)net_snp->data)->if_pid = net_hdr->if_pid; + LL_PREPEND(ip6_snp, net_snp); + *(out_pkt) = net_snp; + } + } #else DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Network Layer Module Missing\n"); #endif @@ -191,6 +215,22 @@ int _pkt_build(gnrc_tcp_tcb_t *tcb, gnrc_pktsnip_t **out_pkt, uint16_t *seq_con, else { *(out_pkt) = ip6_snp; } + + /* Prepend network interface header if an interface id was specified */ + if (tcb->ll_iface > 0) { + gnrc_pktsnip_t *net_snp = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + if (net_snp == NULL) { + DEBUG("gnrc_tcp_pkt.c : _pkt_build() : Can't allocate buffer for netif header.\n"); + gnrc_pktbuf_release(ip6_snp); + *(out_pkt) = NULL; + return -ENOMEM; + } + else { + ((gnrc_netif_hdr_t *)net_snp->data)->if_pid = (kernel_pid_t)tcb->ll_iface; + LL_PREPEND(ip6_snp, net_snp); + *(out_pkt) = net_snp; + } + } #else DEBUG("gnrc_tcp_pkt.c : _pkt_build_reset_from_pkt() : Network Layer Module Missing\n"); #endif @@ -441,6 +481,8 @@ uint16_t _pkt_calc_csum(const gnrc_pktsnip_t *hdr, const gnrc_pktsnip_t *pseudo_ break; #endif default: + /* Suppress compiler warnings */ + (void) len; return 0; } return ~csum; diff --git a/tests/gnrc_tcp_client/Makefile b/tests/gnrc_tcp_client/Makefile index f48c9d3e34..6415f2647b 100644 --- a/tests/gnrc_tcp_client/Makefile +++ b/tests/gnrc_tcp_client/Makefile @@ -4,7 +4,7 @@ include ../Makefile.tests_common BOARD ?= native PORT ?= tap1 -TCP_TARGET_ADDR ?= fe80::affe +TCP_TARGET_ADDR ?= fe80::affe%5 TCP_TARGET_PORT ?= 80 TCP_TEST_CYCLES ?= 3 @@ -21,6 +21,7 @@ BOARD_INSUFFICIENT_MEMORY := airfy-beacon arduino-duemilanove arduino-mega2560 \ CFLAGS += -DTARGET_ADDR=\"$(TCP_TARGET_ADDR)\" CFLAGS += -DTARGET_PORT=$(TCP_TARGET_PORT) CFLAGS += -DCYCLES=$(TCP_TEST_CYCLES) +CFLAGS += -DGNRC_NETIF_IPV6_GROUPS_NUMOF=3 # Modules to include USEMODULE += gnrc_netdev_default diff --git a/tests/gnrc_tcp_client/main.c b/tests/gnrc_tcp_client/main.c index 3d0dba905d..90b2f8d3b2 100644 --- a/tests/gnrc_tcp_client/main.c +++ b/tests/gnrc_tcp_client/main.c @@ -65,21 +65,19 @@ void *cli_thread(void *arg) /* Transmission control block */ gnrc_tcp_tcb_t tcb; - /* Target peer address information */ - ipv6_addr_t target_addr; - uint16_t target_port; - - /* Initialize target information */ - ipv6_addr_from_str(&target_addr, TARGET_ADDR); - target_port = TARGET_PORT; - printf("Client running: TID=%d\n", tid); while (cycles < CYCLES) { + + /* Copy peer address information. NOTE: This test uses link-local addresses + * -> The Device identifier is removed from target_addr in each iteration! */ + char target_addr[] = TARGET_ADDR; + uint16_t target_port = TARGET_PORT; + /* Initialize TCB */ gnrc_tcp_tcb_init(&tcb); /* Connect to peer */ - int ret = gnrc_tcp_open_active(&tcb, AF_INET6, (uint8_t *) &target_addr, target_port, 0); + int ret = gnrc_tcp_open_active(&tcb, AF_INET6, target_addr, target_port, 0); switch (ret) { case 0: DEBUG("TID=%d : gnrc_tcp_open_active() : 0 : ok\n", tid); diff --git a/tests/gnrc_tcp_server/Makefile b/tests/gnrc_tcp_server/Makefile index 931775aeec..340a838f5a 100644 --- a/tests/gnrc_tcp_server/Makefile +++ b/tests/gnrc_tcp_server/Makefile @@ -24,6 +24,7 @@ RIOTBASE ?= $(CURDIR)/../.. CFLAGS += -DLOCAL_ADDR=\"$(TCP_LOCAL_ADDR)\" CFLAGS += -DLOCAL_PORT=$(TCP_LOCAL_PORT) CFLAGS += -DCYCLES=$(TCP_TEST_CYCLES) +CFLAGS += -DGNRC_NETIF_IPV6_GROUPS_NUMOF=3 # Modules to include USEMODULE += gnrc_netdev_default -- GitLab