1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-27 07:21:18 +01:00

gnrc_tcp: handle link local IPv6 addresses correctly

This commit is contained in:
Simon Brummer 2018-05-11 10:06:14 +02:00
parent 6e484f7aed
commit 686aabaa0a
9 changed files with 144 additions and 56 deletions

View File

@ -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.

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}

View File

@ -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");

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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