Skip to content

Commit

Permalink
gnrc_tcp: Add Endpoints for connection specification
Browse files Browse the repository at this point in the history
  • Loading branch information
brummer-simon committed Jan 4, 2020
1 parent 68210fe commit 65db70d
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 68 deletions.
70 changes: 47 additions & 23 deletions sys/include/net/gnrc/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,39 @@
extern "C" {
#endif


/**
* @brief Address information for a single TCP connection endpoint.
* @note Must be compatible to sock_tcp_ep_t.
*/
typedef struct {
int family; /**< IP Address family. GNRC supports currently only IPv6. */

union {
#ifdef MODULE_GNRC_IPV6
uint8_t ipv6[sizeof(ipv6_addr_t)]; /**< IPv6 Address storage */
#endif
} addr;

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] address_family Address family of @p address.
* @param[in] address Address information for endpoint.
* @param[in] port Port number for endpoint.
*
* @returns 0 on success.
* -EAFNOSUPPORT if @p address_family is not supported.
* -EINVAL if parsing of @p address failed.
*/
int gnrc_tcp_ep_init(gnrc_tcp_ep_t *ep, int address_family, char *address, uint16_t port);

/**
* @brief Initialize TCP
*
Expand All @@ -57,18 +90,15 @@ 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.
Expand All @@ -80,39 +110,33 @@ void gnrc_tcp_tcb_init(gnrc_tcp_tcb_t *tcb);
* -ETIMEDOUT if the connection could not be opened.
* -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);

/**
* @brief Opens a connection passively, by waiting for an incoming request.
*
* @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
* incomming 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.
* or the address in @p local 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, 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.
Expand Down
108 changes: 66 additions & 42 deletions sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -143,20 +143,22 @@ 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;
#endif
/* Set port number to listen on */
tcb->local_port = local_port;
Expand All @@ -165,24 +167,19 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, char *target_addr, uint16_t targe
else {
/* Parse target address and port number into TCB */
#ifdef MODULE_GNRC_IPV6
if ((target_addr != NULL) && (tcb->address_family == AF_INET6)) {
if ((remote != NULL) && (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,
Expand Down Expand Up @@ -248,6 +245,35 @@ 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 address_family, char *address, uint16_t port)
{
if (address_family != AF_INET6) {
return -EAFNOSUPPORT;
}

ep->family = address_family;
ep->port = port;
ep->netif = 0;

#ifdef MODULE_GNRC_IPV6
if (address) {
char *netif = ipv6_addr_split_iface(address);

if (ipv6_addr_from_str((ipv6_addr_t *) ep->addr.ipv6, address) == NULL) {
return -EINVAL;
}

if (netif) {
ep->netif = atol(netif);
}
}
else {
ipv6_addr_set_unspecified((ipv6_addr_t *) ep->addr.ipv6);
}
#endif
return 0;
}

int gnrc_tcp_init(void)
{
/* Guard: Check if thread is already running */
Expand Down Expand Up @@ -284,53 +310,51 @@ 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
return -EAFNOSUPPORT;
#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;
}
if (local->family != AF_INET6) {
return -EAFNOSUPPORT;
}
#else
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);
}

ssize_t gnrc_tcp_send(gnrc_tcp_tcb_t *tcb, const void *data, const size_t len,
Expand Down
11 changes: 8 additions & 3 deletions tests/gnrc_tcp/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@ int gnrc_tcp_open_active_cmd(int argc, char **argv)
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_init(&remote, af_family, target_addr, target_port);

int err = gnrc_tcp_open_active(&tcb, &remote, local_port);
switch (err) {
case -EAFNOSUPPORT:
printf("%s: returns -EAFNOSUPPORT\n", argv[0]);
Expand Down Expand Up @@ -158,7 +160,10 @@ int gnrc_tcp_open_passive_cmd(int argc, char **argv)
local_port = atol(argv[3]);
}

int err = gnrc_tcp_open_passive(&tcb, af_family, local_addr, local_port);
gnrc_tcp_ep_t local;
gnrc_tcp_ep_init(&local, 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]);
Expand Down

0 comments on commit 65db70d

Please sign in to comment.