emb6: provide sock_udp port

This commit is contained in:
Martine Lenders 2016-10-27 17:49:34 +02:00 committed by Martine Lenders
parent 032c3b6883
commit 20f6ab6daf
9 changed files with 436 additions and 34 deletions

View File

@ -2,6 +2,11 @@ ifneq (,$(filter emb6_conn_udp,$(USEMODULE)))
USEMODULE += emb6_sock
endif
ifneq (,$(filter emb6_sock_%,$(USEMODULE)))
USEMODULE += core_mbox
USEMODULE += emb6_sock
endif
ifneq (,$(filter emb6_%,$(USEMODULE)))
USEMODULE += emb6
endif
@ -19,4 +24,5 @@ ifneq (,$(filter emb6,$(USEMODULE)))
USEMODULE += emb6_rpl
USEMODULE += emb6_sicslowpan
USEMODULE += emb6_utils
USEMODULE += xtimer
endif

View File

@ -65,6 +65,11 @@ ifneq (,$(filter emb6_sock,$(USEMODULE)))
INCLUDES += -I$(EMB6_DIR)/emb6/inc/tport
endif
ifneq (,$(filter emb6_sock_udp,$(USEMODULE)))
DIRS += $(EMB6_CONTRIB)/sock/udp
CFLAGS += -DSOCK_HAS_IPV6
endif
ifneq (,$(filter emb6_utils,$(USEMODULE)))
DIRS += $(EMB6_DIR)/utils/src
INCLUDES += -I$(EMB6_DIR)/utils/inc

View File

@ -0,0 +1,3 @@
MODULE := emb6_sock_udp
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,326 @@
/*
* Copyright (C) 2016 Freie Universität Berlin
*
* 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.
*/
/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include "byteorder.h"
#include "evproc.h"
#include "msg.h"
#include "mutex.h"
#include "net/af.h"
#include "net/sock/udp.h"
#include "net/ipv6/hdr.h"
#include "sched.h"
#include "uip.h"
#include "xtimer.h"
#define _MSG_TYPE_CLOSE (0x4123)
#define _MSG_TYPE_TIMEOUT (0x4124)
#define _MSG_TYPE_RCV (0x4125)
/* struct to describe a sendto command for emb6 thread */
typedef struct {
mutex_t block;
struct udp_socket *sock;
const sock_udp_ep_t *remote;
int res;
const void *data;
size_t len;
} _send_cmd_t;
extern uint16_t uip_slen;
static bool send_registered = false;
static void _timeout_callback(void *arg);
static void _input_callback(struct udp_socket *c, void *ptr,
const uip_ipaddr_t *src_addr, uint16_t src_port,
const uip_ipaddr_t *dst_addr, uint16_t dst_port,
const uint8_t *data, uint16_t datalen);
static void _output_callback(c_event_t c_event, p_data_t p_data);
static int _reg(struct udp_socket *c, void *ptr, udp_socket_input_callback_t cb,
const sock_udp_ep_t *local, const sock_udp_ep_t *remote)
{
if (((local != NULL) && (local->family != AF_INET6)) ||
((remote != NULL) && (remote->family != AF_INET6))) {
return -EAFNOSUPPORT;
}
if (udp_socket_register(c, ptr, cb) < 0) {
return -ENOMEM;
}
if (local != NULL) {
if (udp_socket_bind(c, local->port) < 0) {
udp_socket_close(c);
return -EADDRINUSE;
}
}
if (remote != NULL) {
/* check of return value not necessary, since neither c nor
* c->udp_conn is NULL (only error case) at this point */
udp_socket_connect(c, (uip_ipaddr_t *)&remote->addr, remote->port);
}
return 0;
}
int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local,
const sock_udp_ep_t *remote, uint16_t flags)
{
int res;
(void)flags;
assert((sock != NULL));
assert((local == NULL) || (local->port != 0));
assert((remote == NULL) || (remote->port != 0));
if (sock->sock.input_callback != NULL) {
sock_udp_close(sock);
}
mutex_init(&sock->mutex);
mutex_lock(&sock->mutex);
mbox_init(&sock->mbox, sock->mbox_queue, SOCK_MBOX_SIZE);
atomic_flag_clear(&sock->receivers);
if ((res = _reg(&sock->sock, sock, _input_callback, local, remote)) < 0) {
sock->sock.input_callback = NULL;
}
mutex_unlock(&sock->mutex);
return res;
}
void sock_udp_close(sock_udp_t *sock)
{
assert(sock != NULL);
if (sock->sock.input_callback != NULL) {
while (atomic_fetch_sub(&sock->receivers, 1) > 0) {
msg_t msg = { .type = _MSG_TYPE_CLOSE };
mbox_put(&sock->mbox, &msg);
}
mutex_lock(&sock->mutex);
udp_socket_close(&sock->sock);
sock->sock.input_callback = NULL;
mutex_unlock(&sock->mutex);
}
}
int sock_udp_get_local(sock_udp_t *sock, sock_udp_ep_t *ep)
{
assert((sock != NULL) && (ep != NULL));
if ((sock->sock.input_callback != NULL) &&
(sock->sock.udp_conn->lport != 0)) {
mutex_lock(&sock->mutex);
/* local UDP endpoints do not have addresses in emb6 */
memset(&ep->addr, 0, sizeof(ipv6_addr_t));
ep->port = ntohs(sock->sock.udp_conn->lport);
mutex_unlock(&sock->mutex);
return sizeof(ipv6_addr_t);
}
return -EADDRNOTAVAIL;
}
int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep)
{
assert((sock != NULL) && (ep != NULL));
if ((sock->sock.input_callback != NULL) &&
(sock->sock.udp_conn->rport != 0)) {
mutex_lock(&sock->mutex);
memcpy(&ep->addr, &sock->sock.udp_conn->ripaddr, sizeof(ipv6_addr_t));
ep->port = ntohs(sock->sock.udp_conn->rport);
mutex_unlock(&sock->mutex);
return sizeof(ipv6_addr_t);
}
return -ENOTCONN;
}
int sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len,
uint32_t timeout, sock_udp_ep_t *remote)
{
xtimer_t timeout_timer;
int blocking = BLOCKING;
int res = -EIO;
msg_t msg;
assert((sock != NULL) && (data != NULL) && (max_len > 0));
if (sock->sock.input_callback == NULL) {
return -EADDRNOTAVAIL;
}
if (timeout == 0) {
blocking = NON_BLOCKING;
}
else if (timeout != SOCK_NO_TIMEOUT) {
timeout_timer.callback = _timeout_callback;
timeout_timer.arg = &sock->mbox;
xtimer_set(&timeout_timer, timeout);
}
atomic_fetch_add(&sock->receivers, 1);
if (_mbox_get(&sock->mbox, &msg, blocking) == 0) {
/* do not need to remove xtimer, since we only get here in non-blocking
* mode (timeout > 0) */
return -EAGAIN;
}
switch (msg.type) {
case _MSG_TYPE_CLOSE:
res = -EADDRNOTAVAIL;
break;
case _MSG_TYPE_TIMEOUT:
res = -ETIMEDOUT;
break;
case _MSG_TYPE_RCV:
mutex_lock(&sock->mutex);
if (max_len < sock->recv_info.datalen) {
res = -ENOBUFS;
mutex_unlock(&sock->mutex);
break;
}
memcpy(data, sock->recv_info.data, sock->recv_info.datalen);
if (remote != NULL) {
remote->family = AF_INET6;
remote->netif = SOCK_ADDR_ANY_NETIF;
memcpy(&remote->addr, &sock->recv_info.src, sizeof(ipv6_addr_t));
remote->port = sock->recv_info.src_port;
}
res = (int)sock->recv_info.datalen;
mutex_unlock(&sock->mutex);
break;
}
atomic_fetch_sub(&sock->receivers, 1);
return res;
}
int sock_udp_send(sock_udp_t *sock, const void *data, size_t len,
const sock_udp_ep_t *remote)
{
struct udp_socket tmp;
_send_cmd_t send_cmd = { .block = MUTEX_INIT,
.remote = remote,
.data = data,
.len = len };
assert((sock != NULL) || (remote != NULL));
assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */
/* we want the send in the uip thread (which udp_socket_send does not offer)
* so we need to do it manually */
if (!send_registered) {
if (evproc_regCallback(EVENT_TYPE_CONN_SEND, _output_callback) != E_SUCCESS) {
return -ENOMEM;
}
else {
send_registered = true;
}
}
if ((len > (UIP_BUFSIZE - (UIP_LLH_LEN + UIP_IPUDPH_LEN))) ||
(len > UINT16_MAX)) {
return -ENOMEM;
}
if (remote != NULL) {
if (remote->family != AF_INET6) {
return -EAFNOSUPPORT;
}
if (remote->port == 0) {
return -EINVAL;
}
send_cmd.remote = remote;
}
else if (sock->sock.udp_conn->rport == 0) {
return -ENOTCONN;
}
/* cppcheck-supress nullPointerRedundantCheck
* remote == NULL implies that sock != NULL (see assert at start of
* function) * that's why it is okay in the if-statement above to check
* sock->... without checking (sock != NULL) first => this check afterwards
* isn't redundant */
if (sock == NULL) {
int res;
if ((res = _reg(&tmp, NULL, NULL, NULL, NULL)) < 0) {
return res;
}
send_cmd.sock = &tmp;
}
else {
send_cmd.sock = &sock->sock;
}
mutex_lock(&send_cmd.block);
/* change to emb6 thread context */
if (evproc_putEvent(E_EVPROC_TAIL, EVENT_TYPE_CONN_SEND, &send_cmd) == E_SUCCESS) {
/* block thread until data was sent */
mutex_lock(&send_cmd.block);
}
else {
/* most likely error: event queue was full */
send_cmd.res = -ENOMEM;
}
if (send_cmd.sock == &tmp) {
udp_socket_close(&tmp);
}
mutex_unlock(&send_cmd.block);
return send_cmd.res;
}
static void _timeout_callback(void *arg)
{
msg_t msg = { .type = _MSG_TYPE_TIMEOUT };
mbox_t *mbox = arg;
/* should be safe, because otherwise if mbox were filled this callback is
* senseless */
mbox_try_put(mbox, &msg);
}
static void _input_callback(struct udp_socket *c, void *ptr,
const uip_ipaddr_t *src_addr, uint16_t src_port,
const uip_ipaddr_t *dst_addr, uint16_t dst_port,
const uint8_t *data, uint16_t datalen)
{
msg_t msg = { .type = _MSG_TYPE_RCV };
sock_udp_t *sock = ptr;
(void)dst_addr;
(void)dst_port;
mutex_lock(&sock->mutex);
sock->recv_info.src_port = src_port;
sock->recv_info.src = (const ipv6_addr_t *)src_addr;
sock->recv_info.data = data;
sock->recv_info.datalen = datalen - sizeof(ipv6_hdr_t);
mutex_unlock(&sock->mutex);
mbox_put(&sock->mbox, &msg);
}
static void _output_callback(c_event_t c_event, p_data_t p_data)
{
if ((c_event != EVENT_TYPE_CONN_SEND) || (p_data == NULL)) {
return;
}
_send_cmd_t *send_cmd = (_send_cmd_t *)p_data;
if (send_cmd->remote != NULL) {
/* send_cmd->len was previously checked */
send_cmd->res = udp_socket_sendto(send_cmd->sock, send_cmd->data,
(uint16_t)send_cmd->len,
(uip_ipaddr_t *)&send_cmd->remote->addr,
send_cmd->remote->port);
}
else {
/* send_cmd->len was previously checked */
send_cmd->res = udp_socket_send(send_cmd->sock, send_cmd->data,
(uint16_t)send_cmd->len);
}
send_cmd->res = (send_cmd->res < 0) ? -EHOSTUNREACH : send_cmd->res;
/* notify notify waiting thread */
mutex_unlock(&send_cmd->block);
}
/** @} */

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2016 Freie Universität Berlin
*
* 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.
*/
/**
* @defgroup pkg_emb6_sock emb6-specific implementation of the sock API
* @ingroup pkg_emb6
* @brief
* @{
*
* @file
* @brief
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#ifndef SOCK_TYPES_H_
#define SOCK_TYPES_H_
#include <stdatomic.h>
#include "mbox.h"
#include "mutex.h"
#include "net/ipv6/addr.h"
#include "uip.h"
#include "udp-socket.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef SOCK_MBOX_SIZE
#define SOCK_MBOX_SIZE (2)
#endif
#ifndef SOCK_HAS_IPV6
#error "emb6 only runs with IPv6 support"
#endif
/**
* @brief @ref net_sock_udp definition for emb6
*/
struct sock_udp {
struct udp_socket sock; /**< emb6 internal socket */
mutex_t mutex; /**< mutex for the connection */
mbox_t mbox; /**< mbox for receiving */
msg_t mbox_queue[SOCK_MBOX_SIZE]; /**< queue for mbox */
atomic_int receivers; /**< current number of recv calls */
struct {
const ipv6_addr_t *src; /**< source address */
const void *data; /**< data of received packet */
size_t datalen; /**< length of received packet data */
uint16_t src_port; /**< source port */
} recv_info; /**< info on received packet */
};
#ifdef __cplusplus
}
#endif
#endif /* SOCK_TYPES_H_ */
/** @} */

View File

@ -5,6 +5,9 @@ include ../Makefile.tests_common
FEATURES_REQUIRED = periph_gpio periph_spi # for at86rf231
# MSP-430 doesn't support C11's atomic functionality yet
BOARD_BLACKLIST := msb-430 msb-430h telosb wsn430-v1_3b wsn430-v1_4 z1
BOARD_INSUFFICIENT_MEMORY := msb-430 msb-430h nucleo32-l031 nucleo32-f031 \
nucleo32-f042 nucleo-l053 stm32f0discovery telosb \
weio wsn430-v1_3b wsn430-v1_4 z1
@ -12,7 +15,7 @@ BOARD_INSUFFICIENT_MEMORY := msb-430 msb-430h nucleo32-l031 nucleo32-f031 \
USEPKG += emb6
USEMODULE += emb6_router
USEMODULE += emb6_conn_udp
USEMODULE += emb6_sock_udp
USEMODULE += ipv6_addr
USEMODULE += shell
USEMODULE += shell_commands

View File

@ -11,7 +11,7 @@
* @{
*
* @file
* @brief Definitions for tests/lwip/
* @brief Definitions for tests/emb6/
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
@ -29,7 +29,7 @@ extern "C" {
* @brief Application configuration
* @{
*/
#define CONN_INBUF_SIZE (256)
#define SOCK_INBUF_SIZE (256)
#define SERVER_MSG_QUEUE_SIZE (8)
#define SERVER_BUFFER_SIZE (64)
/**
@ -58,7 +58,7 @@ size_t hex2ints(uint8_t *out, const char *in);
*/
int ping_cmd(int argc, char **argv);
#ifdef MODULE_CONN_UDP
#ifdef MODULE_EMB6_SOCK_UDP
/**
* @brief UDP IP shell command
*

View File

@ -78,15 +78,14 @@ static void *_emb6_thread(void *args)
static const shell_command_t shell_commands[] = {
{ "ping6", "Send pings and receive pongs", ping_cmd },
#ifdef MODULE_CONN_UDP
#ifdef MODULE_EMB6_SOCK_UDP
{ "udp", "Send UDP messages and listen for messages on UDP port", udp_cmd },
#endif
{ "ifconfig", "Shows assigned IPv6 addresses", ifconfig },
{ NULL, NULL, NULL }
};
static char line_buf[SHELL_DEFAULT_BUFSIZE];
char conn_inbuf[CONN_INBUF_SIZE];
static char line_buf[SHELL_DEFAULT_BUFSIZE];
int main(void)
{

View File

@ -25,29 +25,26 @@
#include "common.h"
#include "od.h"
#include "net/af.h"
#include "net/conn/udp.h"
#include "net/sock/udp.h"
#include "net/ipv6.h"
#include "thread.h"
#include "xtimer.h"
#ifdef MODULE_CONN_UDP
static char conn_inbuf[CONN_INBUF_SIZE];
#ifdef MODULE_EMB6_SOCK_UDP
static char sock_inbuf[SOCK_INBUF_SIZE];
static bool server_running;
static conn_udp_t server_conn;
static sock_udp_t server_sock;
static char server_stack[THREAD_STACKSIZE_DEFAULT];
static msg_t server_msg_queue[SERVER_MSG_QUEUE_SIZE];
static void *_server_thread(void *args)
{
ipv6_addr_t server_addr = IPV6_ADDR_UNSPECIFIED;
uint16_t port;
int res;
msg_init_queue(server_msg_queue, SERVER_MSG_QUEUE_SIZE);
/* parse port */
port = (uint16_t)atoi((char *)args);
if ((res = conn_udp_create(&server_conn, &server_addr,
sizeof(server_addr), AF_INET6, port)) < 0) {
uint16_t port = atoi(args);
const sock_udp_ep_t server_addr = { .family = AF_INET6,
.port = port };
if ((res = sock_udp_create(&server_sock, &server_addr, NULL, 0)) < 0) {
printf("Unable to open UDP server on port %" PRIu16 " (error code %d)\n",
port, -res);
return NULL;
@ -55,12 +52,10 @@ static void *_server_thread(void *args)
server_running = true;
printf("Success: started UDP server on port %" PRIu16 "\n", port);
while (1) {
int res;
ipv6_addr_t src;
size_t src_len = sizeof(ipv6_addr_t);
uint16_t sport;
if ((res = conn_udp_recvfrom(&server_conn, conn_inbuf, sizeof(conn_inbuf), &src,
&src_len, &sport)) < 0) {
sock_udp_ep_t client_addr;
if ((res = sock_udp_recv(&server_sock, sock_inbuf, sizeof(sock_inbuf),
SOCK_NO_TIMEOUT, &client_addr)) < 0) {
puts("Error on receive");
}
else if (res == 0) {
@ -68,9 +63,10 @@ static void *_server_thread(void *args)
}
else {
char addrstr[IPV6_ADDR_MAX_STR_LEN];
printf("Received from [%s]:%" PRIu16 ":\n", ipv6_addr_to_str(addrstr, &src,
sizeof(addrstr)), sport);
od_hex_dump(conn_inbuf, res, 0);
printf("Received from [%s]:%" PRIu16 ":\n",
ipv6_addr_to_str(addrstr, (ipv6_addr_t *)&client_addr.addr,
sizeof(addrstr)), client_addr.port);
od_hex_dump(sock_inbuf, res, 0);
}
}
return NULL;
@ -79,27 +75,25 @@ static void *_server_thread(void *args)
static int udp_send(char *addr_str, char *port_str, char *data, unsigned int num,
unsigned int delay)
{
ipv6_addr_t src = IPV6_ADDR_UNSPECIFIED, dst;
uint16_t port;
uint8_t byte_data[strlen(data) / 2];
sock_udp_ep_t dst = { .family = AF_INET6 };
size_t data_len;
/* parse destination address */
if (ipv6_addr_from_str(&dst, addr_str) == NULL) {
if (ipv6_addr_from_str((ipv6_addr_t *)&dst.addr, addr_str) == NULL) {
puts("Error: unable to parse destination address");
return 1;
}
/* parse port */
port = (uint16_t)atoi(port_str);
dst.port = atoi(port_str);
data_len = hex2ints(byte_data, data);
for (unsigned int i = 0; i < num; i++) {
if (conn_udp_sendto(byte_data, data_len, &src, sizeof(src), (struct sockaddr *)&dst,
sizeof(dst), AF_INET6, port, port) < 0) {
if (sock_udp_send(NULL, byte_data, data_len, &dst) < 0) {
puts("could not send");
}
else {
printf("Success: send %u byte to [%s]:%" PRIu16 ")\n",
(unsigned)data_len, addr_str, port);
(unsigned)data_len, addr_str, dst.port);
}
xtimer_usleep(delay);
}