diff --git a/boards/native/Makefile.dep b/boards/native/Makefile.dep index 81853370f668ac0e3c1452c6ba25d83cb9af1eee..2639777a1d2d82234ff36e87173df57079a83a13 100644 --- a/boards/native/Makefile.dep +++ b/boards/native/Makefile.dep @@ -12,3 +12,9 @@ ifneq (,$(filter can,$(USEMODULE))) CFLAGS += -DCAN_DLL_NUMOF=2 endif endif + +ifneq (,$(filter socket_zep,$(USEMODULE))) + USEMODULE += netdev_ieee802154 + USEMODULE += checksum + USEMODULE += random +endif diff --git a/cpu/native/Makefile b/cpu/native/Makefile index 55c4961b4eda3c688a67bf104ee912352a8110c8..343092dfab8e144008c870a58b88b3f8051a547b 100644 --- a/cpu/native/Makefile +++ b/cpu/native/Makefile @@ -6,6 +6,11 @@ DIRS += vfs ifneq (,$(filter netdev_tap,$(USEMODULE))) DIRS += netdev_tap endif + +ifneq (,$(filter socket_zep,$(USEMODULE))) + DIRS += socket_zep +endif + ifneq (,$(filter mtd_native,$(USEMODULE))) DIRS += mtd endif diff --git a/cpu/native/include/native_internal.h b/cpu/native/include/native_internal.h index 716c526cd54e3328b0407bafdb4aaaf2f7108b8f..e00fbaa87baf78d3f53b662217b5e6ea797d1f4d 100644 --- a/cpu/native/include/native_internal.h +++ b/cpu/native/include/native_internal.h @@ -94,6 +94,7 @@ extern void (*real_srandom)(unsigned int seed); extern int (*real_accept)(int socket, ...); /* The ... is a hack to save includes: */ extern int (*real_bind)(int socket, ...); +extern int (*real_connect)(int socket, ...); extern int (*real_chdir)(const char *path); extern int (*real_close)(int); extern int (*real_fcntl)(int, int, ...); @@ -108,6 +109,7 @@ extern int (*real_fork)(void); extern int (*real_getaddrinfo)(const char *node, ...); extern int (*real_getifaddrs)(struct ifaddrs **ifap); extern int (*real_getpid)(void); +extern int (*real_gettimeofday)(struct timeval *t, ...); extern int (*real_ioctl)(int fildes, int request, ...); extern int (*real_listen)(int socket, int backlog); extern int (*real_open)(const char *path, int oflag, ...); diff --git a/cpu/native/include/socket_zep.h b/cpu/native/include/socket_zep.h new file mode 100644 index 0000000000000000000000000000000000000000..8797f03369b209100be0b26b5c6a873c3c932469 --- /dev/null +++ b/cpu/native/include/socket_zep.h @@ -0,0 +1,84 @@ +/* + * 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 netdev_socket_zep Socket-based ZEP + * @ingroup netdev + * @brief UDP socket-based IEEE 802.15.4 device over ZEP + * @{ + * + * @file + * @brief Socket ZEP definitions + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef SOCKET_ZEP_H +#define SOCKET_ZEP_H + +#include <netdb.h> +#include "net/netdev.h" +#include "net/netdev/ieee802154.h" +#include "net/zep.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 127 - 25 as in at86rf2xx */ +#define SOCKET_ZEP_FRAME_PAYLOAD_LEN (102) /**< maximum possible payload size */ + +/** + * @brief ZEP device state + */ +typedef struct { + netdev_ieee802154_t netdev; /**< netdev internal member */ + int sock_fd; /**< socket fd */ + netdev_event_t last_event; /**< event triggered */ + uint32_t seq; /**< ZEP sequence number */ + /** + * @brief Receive buffer + */ + uint8_t rcv_buf[sizeof(zep_v2_data_hdr_t) + IEEE802154_FRAME_LEN_MAX]; + /** + * @brief Buffer for send header + */ + uint8_t snd_hdr_buf[sizeof(zep_v2_data_hdr_t)]; + uint16_t chksum_buf; /**< buffer for send checksum calculation */ +} socket_zep_t; + +/** + * @brief ZEP device initialization parameters + */ +typedef struct { + char *local_addr; /**< local address string */ + char *local_port; /**< local address string */ + char *remote_addr; /**< remote address string */ + char *remote_port; /**< local address string */ +} socket_zep_params_t; + +/** + * @brief Setup socket_zep_t structure + * + * @param[in] dev the preallocated socket_zep_t device handle to setup + * @param[in] params initialization parameters + */ +void socket_zep_setup(socket_zep_t *dev, const socket_zep_params_t *params); + +/** + * @brief Cleanup socket resources + * + * @param dev the socket_zep device handle to cleanup + */ +void socket_zep_cleanup(socket_zep_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* SOCKET_ZEP_H */ +/** @} */ diff --git a/cpu/native/include/socket_zep_params.h b/cpu/native/include/socket_zep_params.h new file mode 100644 index 0000000000000000000000000000000000000000..b8a73bfeb8be5ffbde7666b9ae216360495a1e87 --- /dev/null +++ b/cpu/native/include/socket_zep_params.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +/** + * @ingroup netdev_socket_zep + * @{ + * + * @file + * @brief Configuration parameters for the @ref netdev_socket_zep driver + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef SOCKET_ZEP_PARAMS_H +#define SOCKET_ZEP_PARAMS_H + +#include "socket_zep.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Number of allocated parameters at @ref socket_zep_params + * + * @note This was desided to only be confiruable on compile-time to be + * more similar to actual boards + */ +#ifndef SOCKET_ZEP_MAX +#define SOCKET_ZEP_MAX (1) +#endif + +/** + * @name Default parameters for native argument parsing + * @{ + */ +#define SOCKET_ZEP_PORT_DEFAULT "17754" /**< default port */ +#define SOCKET_ZEP_LOCAL_ADDR_DEFAULT "::" /**< default local address */ +/** + * @} + */ + +extern socket_zep_params_t socket_zep_params[SOCKET_ZEP_MAX]; + +#ifdef __cplusplus +} +#endif + +#endif /* SOCKET_ZEP_PARAMS_H */ +/** @} */ diff --git a/cpu/native/socket_zep/Makefile b/cpu/native/socket_zep/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7d178b6174522e5864eb1770e5088aa590f02ee4 --- /dev/null +++ b/cpu/native/socket_zep/Makefile @@ -0,0 +1,3 @@ +include $(RIOTBASE)/Makefile.base + +INCLUDES = $(NATIVEINCLUDES) diff --git a/cpu/native/socket_zep/socket_zep.c b/cpu/native/socket_zep/socket_zep.c new file mode 100644 index 0000000000000000000000000000000000000000..d3e51c69ed27079c7efbf567055e11755b1fcb63 --- /dev/null +++ b/cpu/native/socket_zep/socket_zep.c @@ -0,0 +1,443 @@ +/* + * 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 <err.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include "async_read.h" +#include "byteorder.h" +#include "checksum/ucrc16.h" +#include "native_internal.h" +#include "random.h" + +#include "socket_zep.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define _UNIX_NTP_ERA_OFFSET (2208988800U) + +static int _send(netdev_t *netdev, const struct iovec *vector, unsigned n); +static int _recv(netdev_t *netdev, void *buf, size_t n, void *info); +static void _isr(netdev_t *netdev); +static int _init(netdev_t *netdev); +static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len); +static int _set(netdev_t *netdev, netopt_t opt, const void *value, + size_t value_len); + +static const netdev_driver_t socket_zep_driver = { + .send = _send, + .recv = _recv, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, +}; + +static size_t _zep_hdr_fill_v2_data(socket_zep_t *dev, zep_v2_data_hdr_t *hdr, + size_t payload_len) +{ + struct timeval tv; + + real_gettimeofday(&tv, NULL); + hdr->hdr.version = 2; + hdr->type = ZEP_V2_TYPE_DATA; + hdr->chan = dev->netdev.chan; + hdr->dev = byteorder_htons((uint16_t)((((intptr_t)dev)) & 0xffff)); + hdr->lqi_mode = 1; + hdr->lqi_val = 0xff; /* TODO: set */ + hdr->time.seconds = byteorder_htonl(tv.tv_sec + _UNIX_NTP_ERA_OFFSET); + hdr->time.fraction = byteorder_htonl((tv.tv_usec * 1000000) / 232); + hdr->seq = byteorder_htonl(dev->seq); + memset(hdr->resv, 0, sizeof(hdr->resv)); + hdr->length = payload_len; + + return sizeof(zep_v2_data_hdr_t); +} + +static inline size_t _zep_hdr_fill(socket_zep_t *dev, zep_hdr_t *hdr, + size_t payload_len) +{ + hdr->preamble[0] = 'E'; + hdr->preamble[1] = 'X'; + + /* keep possibility for ZEPv1 open */ + return _zep_hdr_fill_v2_data(dev, (zep_v2_data_hdr_t *)hdr, + payload_len); +} + +static size_t _prep_vector(socket_zep_t *dev, const struct iovec *vector, + unsigned n, struct iovec *out) +{ + size_t bytes = 0; + dev->chksum_buf = 0; + + for (unsigned i = 0; i < n; i++) { + bytes += vector[i].iov_len; + } + bytes += sizeof(uint16_t); /* FCS field */ + out[0].iov_base = &dev->snd_hdr_buf; + out[0].iov_len = _zep_hdr_fill(dev, out[0].iov_base, bytes); + for (unsigned i = 0; i < n; i++) { + /* discard const qualifier, we won't change anything. Promise! */ + out[i + 1].iov_base = vector[i].iov_base; + out[i + 1].iov_len = vector[i].iov_len; + dev->chksum_buf = ucrc16_calc_le(out[i + 1].iov_base, out[i + 1].iov_len, + UCRC16_CCITT_POLY_LE, dev->chksum_buf); + } + dev->chksum_buf = byteorder_btols(byteorder_htons(dev->chksum_buf)).u16; + out[n + 1].iov_base = &dev->chksum_buf; + out[n + 1].iov_len = sizeof(uint16_t); + return bytes; +} + +static int _send(netdev_t *netdev, const struct iovec *vector, unsigned n) +{ + socket_zep_t *dev = (socket_zep_t *)netdev; + struct iovec v[n + 2]; + size_t bytes; + int res; + + assert((dev != NULL) && (dev->sock_fd != 0)); + bytes = _prep_vector(dev, vector, n, v); + DEBUG("socket_zep::send(%p, %p, %u)\n", (void *)netdev, (void *)vector, n); + /* simulate TX_STARTED interrupt */ + if (netdev->event_callback) { + dev->last_event = NETDEV_EVENT_TX_STARTED; + netdev->event_callback(netdev, NETDEV_EVENT_ISR); + thread_yield(); + } + res = writev(dev->sock_fd, v, n + 2); + if (res < 0) { + DEBUG("socket_zep::send: error writing packet: %s\n", strerror(errno)); + return res; + } + /* simulate TX_COMPLETE interrupt */ + if (netdev->event_callback) { + dev->last_event = NETDEV_EVENT_TX_COMPLETE; + netdev->event_callback(netdev, NETDEV_EVENT_ISR); + thread_yield(); + } +#ifdef MODULE_NETSTATS_L2 + netdev->stats.tx_bytes += bytes; +#else + (void)bytes; +#endif + + return res - v[0].iov_len - v[n + 1].iov_len; +} + +static void _continue_reading(socket_zep_t *dev) +{ + /* work around lost signals */ + fd_set rfds; + struct timeval t; + memset(&t, 0, sizeof(t)); + FD_ZERO(&rfds); + FD_SET(dev->sock_fd, &rfds); + + _native_in_syscall++; /* no switching here */ + + if (real_select(dev->sock_fd + 1, &rfds, NULL, NULL, &t) == 1) { + int sig = SIGIO; + extern int _sig_pipefd[2]; + extern ssize_t (*real_write)(int fd, const void * buf, size_t count); + real_write(_sig_pipefd[1], &sig, sizeof(int)); + _native_sigpend++; + } + else { + native_async_read_continue(dev->sock_fd); + } + + _native_in_syscall--; +} + +static inline bool _dst_not_me(socket_zep_t *dev, const void *buf) +{ + uint8_t dst_addr[IEEE802154_LONG_ADDRESS_LEN] = { 0 }; + int dst_len; + le_uint16_t dst_pan = { .u16 = 0 }; + + dst_len = ieee802154_get_dst(buf, dst_addr, + &dst_pan); + switch (dst_len) { + case IEEE802154_LONG_ADDRESS_LEN: + return memcmp(dst_addr, dev->netdev.long_addr, dst_len) != 0; + case IEEE802154_SHORT_ADDRESS_LEN: + return (memcmp(dst_addr, ieee802154_addr_bcast, dst_len) != 0) && + (memcmp(dst_addr, dev->netdev.short_addr, dst_len) != 0); + default: + return false; /* better safe than sorry ;-) */ + } +} + +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + socket_zep_t *dev = (socket_zep_t *)netdev; + int size = 0; + + DEBUG("socket_zep::recv(%p, %p, %u, %p)\n", (void *)netdev, buf, + (unsigned)len, (void *)info); + if ((buf == NULL) || (len == 0)) { + int res = real_ioctl(dev->sock_fd, FIONREAD, &size); +#if ENABLE_DEBUG + if (res < 0) { + DEBUG("socket_zep::recv: error reading FIONREAD: %s", + strerror(errno)); + } +#else + (void)res; +#endif + return size; + } + else if (len > 0) { + size = real_read(dev->sock_fd, dev->rcv_buf, sizeof(dev->rcv_buf)); + + if (size > 0) { + zep_hdr_t *tmp = (zep_hdr_t *)&dev->rcv_buf; + + if ((tmp->preamble[0] != 'E') || (tmp->preamble[1] != 'X')) { + DEBUG("socket_zep::recv: invalid ZEP header"); + return -1; + } + switch (tmp->version) { + case 2: { + zep_v2_data_hdr_t *zep = (zep_v2_data_hdr_t *)tmp; + void *payload = &dev->rcv_buf[sizeof(zep_v2_data_hdr_t)]; + + if (zep->type != ZEP_V2_TYPE_DATA) { + DEBUG("socket_zep::recv: unexpect ZEP type\n"); + /* don't support ACK frames for now*/ + return -1; + } + if (((sizeof(zep_v2_data_hdr_t) + zep->length) != (unsigned)size) || + (zep->length > len) || (zep->chan != dev->netdev.chan) || + /* TODO promiscous mode */ + _dst_not_me(dev, payload)) { + /* TODO: check checksum */ + return -1; + } + /* don't hand FCS to stack */ + size = zep->length - sizeof(uint16_t); + if (buf != NULL) { + memcpy(buf, payload, size); + if (info != NULL) { + struct netdev_radio_rx_info *rx_info = info; + rx_info->lqi = zep->lqi_val; + rx_info->rssi = UINT8_MAX; + } + } + break; + } + default: + DEBUG("socket_zep::recv: unexpected ZEP version\n"); + return -1; + } + } + else if (size == 0) { + DEBUG("socket_zep::recv: ignoring null-event\n"); + return -1; + } + else if (size == -1) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + } + else { + err(EXIT_FAILURE, "zep: read"); + } + } + else { + errx(EXIT_FAILURE, "internal error _rx_event"); + } + } + _continue_reading(dev); +#ifdef MODULE_NETSTATS_L2 + netdev->stats.rx_count++; + netdev->stats.rx_bytes += size; +#endif + return size; +} + +static void _isr(netdev_t *netdev) +{ + if (netdev->event_callback) { + socket_zep_t *dev = (socket_zep_t *)netdev; + + DEBUG("socket_zep::isr: firing %u\n", (unsigned)dev->last_event); + netdev->event_callback(netdev, dev->last_event); + } + return; +} + +static void _socket_isr(int fd, void *arg) +{ + (void)fd; + (void)arg; + netdev_t *netdev = (netdev_t *)arg; + + DEBUG("socket_zep::_socket_isr: %d, %p (netdev == %p)\n", + fd, arg, (void *)netdev); + if (netdev == NULL) { + return; + } + if (netdev->event_callback) { + socket_zep_t *dev = (socket_zep_t *)netdev; + + dev->last_event = NETDEV_EVENT_RX_COMPLETE; + netdev->event_callback(netdev, NETDEV_EVENT_ISR); + } +} + +static int _init(netdev_t *netdev) +{ + socket_zep_t *dev = (socket_zep_t *)netdev; + + assert(dev != NULL); + dev->netdev.chan = IEEE802154_DEFAULT_CHANNEL; + dev->netdev.pan = IEEE802154_DEFAULT_PANID; +#ifdef MODULE_GNRC_SIXLOWPAN + dev->netdev.proto = GNRC_NETTYPE_SIXLOWPAN; +#elif MODULE_GNRC + dev->netdev.proto = GNRC_NETTYPE_UNDEF; +#endif + dev->seq = random_uint32(); + + return 0; +} + +static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len) +{ + socket_zep_t *dev = (socket_zep_t *)netdev; + uint16_t *v = value; + + assert((dev != NULL)); + switch (opt) { + case NETOPT_MAX_PACKET_SIZE: + assert(value != NULL); + if (max_len != sizeof(uint16_t)) { + return -EOVERFLOW; + } + *v = SOCKET_ZEP_FRAME_PAYLOAD_LEN; + return sizeof(uint16_t); + default: + return netdev_ieee802154_get(&dev->netdev, opt, value, max_len); + } +} + +static int _set(netdev_t *netdev, netopt_t opt, const void *value, + size_t value_len) +{ + assert(netdev != NULL); + return netdev_ieee802154_set((netdev_ieee802154_t *)netdev, opt, + value, value_len); +} + + +void socket_zep_setup(socket_zep_t *dev, const socket_zep_params_t *params) +{ + static const struct addrinfo hints = { .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM }; + struct addrinfo *ai = NULL, *remote; + int res; + + DEBUG("socket_zep_setup(%p, %p)\n", (void *)dev, (void *)params); + assert((params->local_addr != NULL) && (params->local_port != NULL) && + (params->remote_addr != NULL) && (params->remote_port != NULL)); + memset(dev, 0, sizeof(socket_zep_t)); + dev->netdev.netdev.driver = &socket_zep_driver; + /* bind and connect socket */ + if ((res = real_getaddrinfo(params->local_addr, params->local_port, &hints, + &ai)) < 0) { + errx(EXIT_FAILURE, "ZEP: unable to get local address: %s\n", + gai_strerror(res)); + } + for (struct addrinfo *local = ai; local != NULL; local = local->ai_next) { + if ((res = real_socket(local->ai_family, local->ai_socktype, + local->ai_protocol)) < 0) { + continue; + } + if (real_bind(res, local->ai_addr, local->ai_addrlen) == 0) { + break; /* successfully bound */ + } + } + real_freeaddrinfo(ai); + if (res < 0) { + err(EXIT_FAILURE, "ZEP: Unable to bind socket"); + } + dev->sock_fd = res; + ai = NULL; + if ((res = real_getaddrinfo(params->remote_addr, params->remote_port, &hints, + &ai)) < 0) { + errx(EXIT_FAILURE, "ZEP: unable to get remote address: %s\n", + gai_strerror(res)); + } + for (remote = ai; remote != NULL; remote = remote->ai_next) { + if (real_connect(dev->sock_fd, remote->ai_addr, remote->ai_addrlen) == 0) { + break; /* successfully connected */ + } + } + if (remote == NULL) { + err(EXIT_FAILURE, "ZEP: Unable to connect socket"); + } + real_freeaddrinfo(ai); + + /* generate hardware address from local address */ + uint8_t ss_array[sizeof(struct sockaddr_storage)] = { 0 }; + socklen_t ss_len = sizeof(struct sockaddr_storage); + + if (getpeername(dev->sock_fd, (struct sockaddr *)&ss_array, &ss_len) < 0) { + err(EXIT_FAILURE, "ZEP: Unable to retrieve remote address"); + } + assert(ss_len >= IEEE802154_LONG_ADDRESS_LEN); + if (getsockname(dev->sock_fd, (struct sockaddr *)&ss_array, &ss_len) < 0) { + err(EXIT_FAILURE, "ZEP: Unable to retrieve local address"); + } + assert(ss_len >= IEEE802154_LONG_ADDRESS_LEN); + + /* generate hardware address from socket address and port info */ + dev->netdev.long_addr[1] = 'Z'; /* The "OUI" */ + dev->netdev.long_addr[2] = 'E'; + dev->netdev.long_addr[3] = 'P'; + for (unsigned i = 0; i < ss_len; i++) { /* generate NIC from local source */ + unsigned addr_idx = (i % (IEEE802154_LONG_ADDRESS_LEN / 2)) + + (IEEE802154_LONG_ADDRESS_LEN / 2); + dev->netdev.long_addr[addr_idx] ^= ss_array[i]; + } + dev->netdev.short_addr[0] = dev->netdev.long_addr[6]; + dev->netdev.short_addr[1] = dev->netdev.long_addr[7]; + native_async_read_setup(); + native_async_read_add_handler(dev->sock_fd, dev, _socket_isr); +#ifdef MODULE_NETSTATS_L2 + memset(&dev->netdev.netdev.stats, 0, sizeof(netstats_t)); +#endif +} + +void socket_zep_cleanup(socket_zep_t *dev) +{ + assert(dev != NULL); + /* cleanup signal handling */ + native_async_read_cleanup(); + /* close the socket */ + close(dev->sock_fd); + dev->sock_fd = 0; +} + +/** @} */ diff --git a/cpu/native/startup.c b/cpu/native/startup.c index 35997b9d559b4444d72dc475be1376babb9d2a83..2f0b0173ae304c582aae826c3fab18c3be19be9b 100644 --- a/cpu/native/startup.c +++ b/cpu/native/startup.c @@ -75,12 +75,21 @@ netdev_tap_params_t netdev_tap_params[NETDEV_TAP_MAX]; #include "candev_linux.h" #endif +#ifdef MODULE_SOCKET_ZEP +#include "socket_zep_params.h" + +socket_zep_params_t socket_zep_params[SOCKET_ZEP_MAX]; +#endif + static const char short_opts[] = ":hi:s:deEoc:" #ifdef MODULE_MTD_NATIVE "m:" #endif #ifdef MODULE_CAN_LINUX "n:" +#endif +#ifdef MODULE_SOCKET_ZEP + "z:" #endif ""; @@ -98,6 +107,9 @@ static const struct option long_opts[] = { #endif #ifdef MODULE_CAN_LINUX { "can", required_argument, NULL, 'n' }, +#endif +#ifdef MODULE_SOCKET_ZEP + { "zep", required_argument, NULL, 'z' }, #endif { NULL, 0, NULL, '\0' }, }; @@ -228,8 +240,15 @@ void usage_exit(int status) real_printf(" <tap interface %d>", i + 1); } #endif - real_printf(" [-i <id>] [-d] [-e|-E] [-o] [-c <tty>]\n"); +#if defined(MODULE_SOCKET_ZEP) && (SOCKET_ZEP_MAX > 0) + real_printf(" -z [[<laddr>:<lport>,]<raddr>:<rport>]\n"); + for (int i = 0; i < SOCKET_ZEP_MAX - 1; i++) { + /* for further interfaces the local address must be different so we omit + * the braces (marking them as optional) to be 100% clear on that */ + real_printf(" -z <laddr>:<lport>,<raddr>:<rport>\n"); + } +#endif real_printf(" help: %s -h\n\n", _progname); @@ -253,7 +272,14 @@ void usage_exit(int status) " to socket\n" " -c <tty>, --uart-tty=<tty>\n" " specify TTY device for UART. This argument can be used multiple\n" -" times (up to UART_NUMOF)\n"); +" times (up to UART_NUMOF)\n" +#if defined(MODULE_SOCKET_ZEP) && (SOCKET_ZEP_MAX > 0) +" -z [<laddr>:<lport>,]<raddr>:<rport> --zep=[<laddr>:<lport>,]<raddr>:<rport>\n" +" provide a ZEP interface with local address and port (<laddr>, <lport>)\n" +" and remote address and port (default local: [::]:17754).\n" +" Required to be provided SOCKET_ZEP_MAX times\n" +#endif + ); #ifdef MODULE_MTD_NATIVE real_printf( " -m <mtd>, --mtd=<mtd>\n" @@ -268,6 +294,65 @@ void usage_exit(int status) real_exit(status); } +#ifdef MODULE_SOCKET_ZEP +static void _parse_ep_str(char *ep_str, char **addr, char **port) +{ + /* read endpoint string in reverse, the last chars are the port and decimal + * numbers, then a colon, then the address (potentially containing colons, + * that's why we read in reverse) */ + for (int i = strlen(ep_str) - 1; (i >= 0) && (*port == NULL); i--) { + if (((ep_str[i] < '0') || (ep_str[i] > '9')) && (ep_str[i] != ':')) { + usage_exit(EXIT_FAILURE); + } + if ((ep_str[i] == ':') && (i >= (int)sizeof("[]"))) { + /* found port delimiter, but we need to make sure it isn't delivered + * like :<port>. Two characters for either hostname or IP address + * seems reasonable especially considering, that we need to + * remove the [] around IPv6 addresses */ + *port = &ep_str[i + 1]; + if ((ep_str[0] == '[') && (ep_str[i - 1] == ']')) { + /* addr is in the format [<addr>], strip [] */ + *addr = &ep_str[1]; + ep_str[i - 1] = '\0'; + } + else if ((ep_str[0] == '[') || (ep_str[i - 1] == ']')) { + /* unbalanced brackets */ + usage_exit(EXIT_FAILURE); + } + else { + *addr = ep_str; + } + ep_str[i] = '\0'; + } + } + if (*port == NULL) { + usage_exit(EXIT_FAILURE); + } +} + +static void _zep_params_setup(char *zep_str, int zep) +{ + char *save_ptr, *first_ep, *second_ep; + + if ((first_ep = strtok_r(zep_str, ",", &save_ptr)) == NULL) { + usage_exit(EXIT_FAILURE); + } + second_ep = strtok_r(NULL, ",", &save_ptr); + if (second_ep == NULL) { + socket_zep_params[zep].local_addr = SOCKET_ZEP_LOCAL_ADDR_DEFAULT; + socket_zep_params[zep].local_port = SOCKET_ZEP_PORT_DEFAULT; + _parse_ep_str(first_ep, &socket_zep_params[zep].remote_addr, + &socket_zep_params[zep].remote_port); + } + else { + _parse_ep_str(first_ep, &socket_zep_params[zep].local_addr, + &socket_zep_params[zep].local_port); + _parse_ep_str(second_ep, &socket_zep_params[zep].remote_addr, + &socket_zep_params[zep].remote_port); + } +} +#endif + /** @brief Initialization function pointer type */ typedef void (*init_func_t)(int argc, char **argv, char **envp); #ifdef __APPLE__ @@ -295,6 +380,9 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e _native_id = _native_pid; int c, opt_idx = 0, uart = 0; +#ifdef MODULE_SOCKET_ZEP + unsigned zeps = 0; +#endif bool dmn = false, force_stderr = false; _stdiotype_t stderrtype = _STDIOTYPE_STDIO; _stdiotype_t stdouttype = _STDIOTYPE_STDIO; @@ -360,6 +448,11 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e CAN_MAX_SIZE_INTERFACE_NAME); } break; +#endif +#ifdef MODULE_SOCKET_ZEP + case 'z': + _zep_params_setup(optarg, zeps++); + break; #endif default: usage_exit(EXIT_FAILURE); @@ -374,6 +467,12 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e } } #endif +#ifdef MODULE_SOCKET_ZEP + if (zeps != SOCKET_ZEP_MAX) { + /* not enough ZEPs given */ + usage_exit(EXIT_FAILURE); + } +#endif if (dmn) { filter_daemonize_argv(_native_argv); diff --git a/cpu/native/syscalls.c b/cpu/native/syscalls.c index c4417153c10fe4fa4a5f9c50f803834f93da9d35..765044d9f19aca06054bf042c4f49e69dd61a0e4 100644 --- a/cpu/native/syscalls.c +++ b/cpu/native/syscalls.c @@ -62,9 +62,11 @@ void (*real_freeifaddrs)(struct ifaddrs *ifa); void (*real_srandom)(unsigned int seed); int (*real_accept)(int socket, ...); int (*real_bind)(int socket, ...); +int (*real_connect)(int socket, ...); int (*real_printf)(const char *format, ...); int (*real_getaddrinfo)(const char *node, ...); int (*real_getifaddrs)(struct ifaddrs **ifap); +int (*real_gettimeofday)(struct timeval *t, ...); int (*real_getpid)(void); int (*real_chdir)(const char *path); int (*real_close)(int); @@ -447,11 +449,13 @@ void _native_init_syscalls(void) *(void **)(&real_srandom) = dlsym(RTLD_NEXT, "srandom"); *(void **)(&real_accept) = dlsym(RTLD_NEXT, "accept"); *(void **)(&real_bind) = dlsym(RTLD_NEXT, "bind"); + *(void **)(&real_connect) = dlsym(RTLD_NEXT, "connect"); *(void **)(&real_printf) = dlsym(RTLD_NEXT, "printf"); *(void **)(&real_gai_strerror) = dlsym(RTLD_NEXT, "gai_strerror"); *(void **)(&real_getaddrinfo) = dlsym(RTLD_NEXT, "getaddrinfo"); *(void **)(&real_getifaddrs) = dlsym(RTLD_NEXT, "getifaddrs"); *(void **)(&real_getpid) = dlsym(RTLD_NEXT, "getpid"); + *(void **)(&real_gettimeofday) = dlsym(RTLD_NEXT, "gettimeofday"); *(void **)(&real_pipe) = dlsym(RTLD_NEXT, "pipe"); *(void **)(&real_chdir) = dlsym(RTLD_NEXT, "chdir"); *(void **)(&real_close) = dlsym(RTLD_NEXT, "close"); diff --git a/examples/gnrc_border_router/Makefile b/examples/gnrc_border_router/Makefile index 40e9576cf61d496415238ad9947a42e63403611b..7c6e479f4fe5eb34ffc69c58b1b49fa2841ed83d 100644 --- a/examples/gnrc_border_router/Makefile +++ b/examples/gnrc_border_router/Makefile @@ -29,6 +29,10 @@ ifeq (,$(filter native,$(BOARD))) ETHOS_BAUDRATE ?= 115200 CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE) -DUSE_ETHOS_FOR_STDIO FEATURES_REQUIRED += periph_uart +else + GNRC_NETIF_NUMOF := 2 + TERMFLAGS += -z [::1]:17754 + USEMODULE += socket_zep endif # SLIP legacy compatibility @@ -82,6 +86,14 @@ QUIET ?= 1 TAP ?= tap0 IPV6_PREFIX ?= 2001:db8::/64 +ifeq (native,$(BOARD)) +TERMDEPS += uhcpd-daemon + +.PHONY: uhcpd-daemon + +uhcpd-daemon: host-tools + $(RIOTBASE)/dist/tools/uhcpd/bin/uhcpd $(TAP) $(IPV6_PREFIX) & +else # We override the `make term` command to use ethos TERMPROG ?= sudo sh $(RIOTBASE)/dist/tools/ethos/start_network.sh TERMFLAGS ?= $(PORT) $(TAP) $(IPV6_PREFIX) @@ -89,6 +101,7 @@ TERMFLAGS ?= $(PORT) $(TAP) $(IPV6_PREFIX) # We depend on the ethos host tools to run the border router, we build them # if necessary TERMDEPS += host-tools +endif include $(RIOTBASE)/Makefile.include diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index aa3768f04ff41ce7609a5820b842a801f6163e17..76248d0e40ee048c31ebbcee566208919a49040b 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -215,6 +215,11 @@ void auto_init(void) auto_init_netdev_tap(); #endif +#ifdef MODULE_SOCKET_ZEP + extern void auto_init_socket_zep(void); + auto_init_socket_zep(); +#endif + #ifdef MODULE_NORDIC_SOFTDEVICE_BLE extern void gnrc_nordic_ble_6lowpan_init(void); gnrc_nordic_ble_6lowpan_init(); diff --git a/sys/auto_init/netif/auto_init_socket_zep.c b/sys/auto_init/netif/auto_init_socket_zep.c new file mode 100644 index 0000000000000000000000000000000000000000..b80457798cda422d6fc9d1effdb6a72ae46b5a66 --- /dev/null +++ b/sys/auto_init/netif/auto_init_socket_zep.c @@ -0,0 +1,60 @@ +/* + * 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. + * + */ + +/** + * @ingroup auto_init_gnrc_netif + * @{ + * + * @file + * @brief Auto initialization for @ref netdev_socket_zep devices + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ + +#ifdef MODULE_SOCKET_ZEP + +#include "log.h" +#include "socket_zep.h" +#include "socket_zep_params.h" +#include "net/gnrc/netif/ieee802154.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Define stack parameters for the MAC layer thread + */ +#define SOCKET_ZEP_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE) +#ifndef SOCKET_ZEP_MAC_PRIO +#define SOCKET_ZEP_MAC_PRIO (GNRC_NETIF_PRIO) +#endif + +/** + * @brief Stacks for the MAC layer threads + */ +static char _socket_zep_stacks[SOCKET_ZEP_MAX][SOCKET_ZEP_MAC_STACKSIZE]; +static socket_zep_t _socket_zeps[SOCKET_ZEP_MAX]; + +void auto_init_socket_zep(void) +{ + for (int i = 0; i < SOCKET_ZEP_MAX; i++) { + LOG_DEBUG("[auto_init_netif: initializing socket ZEP device #%u\n", i); + /* setup netdev device */ + socket_zep_setup(&_socket_zeps[i], &socket_zep_params[i]); + gnrc_netif_ieee802154_create(_socket_zep_stacks[i], + SOCKET_ZEP_MAC_STACKSIZE, + SOCKET_ZEP_MAC_PRIO, "socket_zep", + (netdev_t *)&_socket_zeps[i]); + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_SOCKET_ZEP */ +/** @} */ diff --git a/sys/include/net/zep.h b/sys/include/net/zep.h new file mode 100644 index 0000000000000000000000000000000000000000..80a317a40e1d58a8c81b99c92b7d4441052f2c68 --- /dev/null +++ b/sys/include/net/zep.h @@ -0,0 +1,102 @@ +/* + * 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 net_zep ZEP header definitions + * @ingroup net + * @{ + * + * @file + * @brief + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef NET_ZEP_H +#define NET_ZEP_H + +#include <stdint.h> + +#include "byteorder.h" +#include "net/ntp_packet.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZEP_PORT_DEFAULT (17754) /**< default ZEP port */ + +/** + * @brief Type == Data for ZEPv2 header + */ +#define ZEP_V2_TYPE_DATA (1) + +/** + * @brief Type == Ack for ZEPv2 header + */ +#define ZEP_V2_TYPE_ACK (2) + +/** + * @brief Mask for length field + */ +#define ZEP_LENGTH_MASK (0x7f) + +/** + * @brief ZEP header definition + */ +typedef struct __attribute__((packed)) { + char preamble[2]; /**< Preamble code (must be "EX") */ + uint8_t version; /**< Protocol Version (must be 1 or 2) */ +} zep_hdr_t; + +/** + * @brief ZEPv1 header definition + * @extends zep_hdr_t + */ +typedef struct __attribute__((packed)) { + zep_hdr_t hdr; /**< common header fields */ + uint8_t chan; /**< channel ID */ + network_uint16_t dev; /**< device ID */ + uint8_t lqi_mode; /**< CRC/LQI Mode (0: append LQI to frame, 1: append FCS) */ + uint8_t lqi_val; /**< LQI value */ + uint8_t resv[7]; /**< reserved field, must always be 0 */ + uint8_t length; /**< length of the frame */ +} zep_v1_hdr_t; + +/** + * @brief ZEPv2 header definition (type == Data) + * @extends zep_hdr_t + */ +typedef struct __attribute__((packed)) { + zep_hdr_t hdr; /**< common header fields */ + uint8_t type; /**< type (must be @ref ZEP_V2_TYPE_DATA) */ + uint8_t chan; /**< channel ID */ + network_uint16_t dev; /**< device ID */ + uint8_t lqi_mode; /**< CRC/LQI Mode */ + uint8_t lqi_val; /**< LQI value */ + ntp_timestamp_t time; /**< NTP timestamp */ + network_uint32_t seq; /**< Sequence number */ + uint8_t resv[10]; /**< reserved field, must always be 0 */ + uint8_t length; /**< length of the frame */ +} zep_v2_data_hdr_t; + +/** + * @brief ZEPv2 header definition (type == Ack) + * @extends zep_hdr_t + */ +typedef struct __attribute__((packed)) { + zep_hdr_t hdr; /**< common header fields */ + uint8_t type; /**< type (must be @ref ZEP_V2_TYPE_ACK) */ + network_uint32_t seq; /**< Sequence number */ +} zep_v2_ack_hdr_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_ZEP_H */ +/** @} */ diff --git a/tests/socket_zep/Makefile b/tests/socket_zep/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..db55c1d71ec0bdae3ab9d58b4b52a743735d0d2d --- /dev/null +++ b/tests/socket_zep/Makefile @@ -0,0 +1,18 @@ +APPLICATION = socket_zep +include ../Makefile.tests_common + +BOARD_WHITELIST = native # socket_zep is only available on native + +DISABLE_MODULE += auto_init + +USEMODULE += od +USEMODULE += socket_zep + +CFLAGS += -DDEVELHELP + +TERMFLAGS ?= -z [::1]:17754 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/socket_zep/main.c b/tests/socket_zep/main.c new file mode 100644 index 0000000000000000000000000000000000000000..faac4e172ee8ba04a4c1feb5bd1be8f5f6298a16 --- /dev/null +++ b/tests/socket_zep/main.c @@ -0,0 +1,179 @@ +/* + * 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for socket_zep network device driver + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + * + * @} + */ + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> + +#include "byteorder.h" +#include "net/ieee802154.h" +#include "sched.h" +#include "socket_zep.h" +#include "socket_zep_params.h" +#include "msg.h" +#include "od.h" + +#define MSG_QUEUE_SIZE (8) +#define MSG_TYPE_ISR (0x3456) +#define RECVBUF_SIZE (IEEE802154_FRAME_LEN_MAX) + +static uint8_t _recvbuf[RECVBUF_SIZE]; +static msg_t _msg_queue[MSG_QUEUE_SIZE]; +static socket_zep_t _dev; +static kernel_pid_t _main_pid; + +static void _event_cb(netdev_t *dev, netdev_event_t event); +static void _print_info(netdev_t *netdev); + +static void test_init(void) +{ + const socket_zep_params_t *p = &socket_zep_params[0]; + netdev_t *netdev = (netdev_t *)(&_dev); + + printf("Initializing socket ZEP with (local: [%s]:%s, remote: [%s]:%s)\n", + p->local_addr, p->local_port, p->remote_addr, p->remote_port); + socket_zep_setup(&_dev, p); + netdev->event_callback = _event_cb; + assert(netdev->driver->init(netdev) >= 0); + _print_info(netdev); +} + +static void test_send__vector_NULL__count_0(void) +{ + netdev_t *netdev = (netdev_t *)(&_dev); + int res; + + puts("Send zero-length packet"); + res = netdev->driver->send(netdev, NULL, 0); + assert((res < 0) || (res == 0)); + if ((res < 0) && (errno == ECONNREFUSED)) { + puts("No remote socket exists (use scripts in `tests/` to have proper tests)"); + } +} + +static void test_send__vector_not_NULL__count_2(void) +{ + struct iovec vector[] = { { .iov_base = "Hello", .iov_len = sizeof("Hello") }, + { .iov_base = "World", .iov_len = sizeof("World") } }; + netdev_t *netdev = (netdev_t *)(&_dev); + int res; + + puts("Send 'Hello\\0World\\0'"); + res = netdev->driver->send(netdev, vector, + sizeof(vector) / sizeof(struct iovec)); + assert((res < 0) || (res == (sizeof("Hello")) + sizeof("World"))); + if ((res < 0) && (errno == ECONNREFUSED)) { + puts("No remote socket exists (use scripts in `tests/` to have proper tests)"); + } +} + +static void test_recv(void) +{ + puts("Waiting for an incoming message (use `make test`)"); + while (1) { + netdev_t *netdev = (netdev_t *)(&_dev); + msg_t msg; + + msg_receive(&msg); + if (msg.type == MSG_TYPE_ISR) { + netdev->driver->isr(netdev); + } + else { + puts("unexpected message type"); + } + } +} + +int main(void) +{ + puts("Socket ZEP device driver test"); + msg_init_queue(_msg_queue, MSG_QUEUE_SIZE); + _main_pid = sched_active_pid; + + test_init(); + test_send__vector_NULL__count_0(); + test_send__vector_not_NULL__count_2(); + test_recv(); /* does not return */ + puts("ALL TESTS SUCCESSFUL"); + return 0; +} + +static void _recv(netdev_t *dev) +{ + netdev_ieee802154_rx_info_t rx_info; + const int exp_len = dev->driver->recv(dev, NULL, 0, NULL); + int data_len; + + assert(exp_len >= 0); + assert(((unsigned)exp_len) <= sizeof(_recvbuf)); + data_len = dev->driver->recv(dev, _recvbuf, sizeof(_recvbuf), &rx_info); + if (data_len < 0) { + puts("Received invalid packet"); + } + else { + assert(data_len <= exp_len); + printf("RSSI: %u, LQI: %u, Data:\n", rx_info.rssi, rx_info.lqi); + if (data_len > 0) { + od_hex_dump(_recvbuf, data_len, OD_WIDTH_DEFAULT); + } + } +} + +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + if (event == NETDEV_EVENT_ISR) { + msg_t msg; + + msg.type = MSG_TYPE_ISR; + msg.content.ptr = dev; + + if (msg_send(&msg, _main_pid) <= 0) { + puts("possibly lost interrupt."); + } + } + else { + switch (event) { + case NETDEV_EVENT_RX_COMPLETE: + { + _recv(dev); + break; + } + default: + break; + } + } +} + +static void _print_info(netdev_t *netdev) +{ + uint64_t long_addr; + uint16_t short_addr; + + assert(netdev->driver->get(netdev, NETOPT_ADDRESS, &short_addr, + sizeof(short_addr)) == sizeof(uint16_t)); + assert(netdev->driver->get(netdev, NETOPT_ADDRESS_LONG, &long_addr, + sizeof(long_addr)) == sizeof(uint64_t)); + + /* we are on native, so using PRIu* is okay */ + printf("(Hwaddrs: %04" PRIx16 ", %016" PRIx64 ")\n", + byteorder_htons(short_addr).u16, + byteorder_htonll(long_addr).u64); +} diff --git a/tests/socket_zep/tests/01-run.py b/tests/socket_zep/tests/01-run.py new file mode 100755 index 0000000000000000000000000000000000000000..bcbfedd1bfab701601666635e6aa8b4536808e9d --- /dev/null +++ b/tests/socket_zep/tests/01-run.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# 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. + +import os +import sys + +import socket + +IEEE802154_FRAME_LEN_MAX = 127 +ZEP_DATA_HEADER_SIZE = 32 +FCS_LEN = 2 +RCVBUF_LEN = IEEE802154_FRAME_LEN_MAX + ZEP_DATA_HEADER_SIZE + FCS_LEN +zep_params = { + "local_addr": "::", + "local_port": 12345, + "remote_addr": "::1", + "remote_port": 17754, + } +s = None + + +def testfunc(child): + child.expect_exact("Socket ZEP device driver test") + child.expect(r"Initializing socket ZEP with " + + r"\(local: \[(?P<local_addr>[:0-9a-f]+)\]:(?P<local_port>\d+), " + + r"remote: \[(?P<remote_addr>[:0-9a-f]+)\]:(?P<remote_port>\d+)\)") + assert(child.match.group('local_addr') == zep_params['local_addr']) + assert(int(child.match.group('local_port')) == zep_params['local_port']) + assert(child.match.group('remote_addr') == zep_params['remote_addr']) + assert(int(child.match.group('remote_port')) == zep_params['remote_port']) + child.expect(r"\(Hwaddrs: (?P<short_addr>[0-9a-f]{4}), (?P<long_addr>[0-9a-f]{16})\)") + child.expect_exact("Send zero-length packet") + data, addr = s.recvfrom(RCVBUF_LEN) + assert(len(data) == (ZEP_DATA_HEADER_SIZE + FCS_LEN)) + child.expect_exact("Send 'Hello\\0World\\0'") + data, addr = s.recvfrom(RCVBUF_LEN) + assert(len(data) == (ZEP_DATA_HEADER_SIZE + len("Hello\0World\0") + FCS_LEN)) + assert(b"Hello\0World\0" == data[ZEP_DATA_HEADER_SIZE:-2]) + child.expect_exact("Waiting for an incoming message (use `make test`)") + s.sendto(b"\x45\x58\x02\x01\x1a\x44\xe0\x01\xff\xdb\xde\xa6\x1a\x00\x8b" + + b"\xfd\xae\x60\xd3\x21\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + b"\x00\x22\x41\xdc\x02\x23\x00\x38\x30\x00\x0a\x50\x45\x5a\x00" + + b"\x5b\x45\x00\x0a\x50\x45\x5a\x00Hello World\x3a\xf2", + ("::1", zep_params['local_port'])) + child.expect(r"RSSI: \d+, LQI: \d+, Data:") + child.expect_exact(r"00000000 41 DC 02 23 00 38 30 00 0A 50 45 5A 00 5B 45 00") + child.expect_exact(r"00000010 0A 50 45 5A 00 48 65 6C 6C 6F 20 57 6F 72 6C 64") + + +if __name__ == "__main__": + sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner')) + import testrunner + + os.environ['TERMFLAGS'] = "-z [%s]:%d,[%s]:%d" % ( + zep_params['local_addr'], zep_params['local_port'], + zep_params['remote_addr'], zep_params['remote_port']) + s = socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM) + s.bind(("::", zep_params['remote_port'])) + res = testrunner.run(testfunc, timeout=1, echo=True, traceback=True) + s.close() + if (res == 0): + print("Run tests successful") + else: + print("Run tests failed") + sys.exit(res)