From 9b8417fbb9df0dc54bb485be619cca56a25e8bc6 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Sun, 15 Nov 2015 21:00:07 +0100 Subject: [PATCH] tests: intial import of lwIP test application --- tests/lwip/Makefile | 49 +++++++ tests/lwip/common.c | 57 ++++++++ tests/lwip/common.h | 81 +++++++++++ tests/lwip/ip.c | 160 ++++++++++++++++++++++ tests/lwip/main.c | 75 +++++++++++ tests/lwip/tests/01-run.py | 266 +++++++++++++++++++++++++++++++++++++ tests/lwip/udp.c | 168 +++++++++++++++++++++++ 7 files changed, 856 insertions(+) create mode 100644 tests/lwip/Makefile create mode 100644 tests/lwip/common.c create mode 100644 tests/lwip/common.h create mode 100644 tests/lwip/ip.c create mode 100644 tests/lwip/main.c create mode 100755 tests/lwip/tests/01-run.py create mode 100644 tests/lwip/udp.c diff --git a/tests/lwip/Makefile b/tests/lwip/Makefile new file mode 100644 index 0000000000..2e1c844b49 --- /dev/null +++ b/tests/lwip/Makefile @@ -0,0 +1,49 @@ +APPLICATION = lwip + +BOARD ?= iotlab-m3 + +RIOTBASE ?= $(CURDIR)/../.. + +BOARD_BLACKLIST := arduino-mega2560 msb-430h z1 +BOARD_INSUFFICIENT_MEMORY := airfy-beacon arduino-mega2560 msb-430h nrf6310 \ + nucleo-f334 pca10005 stm32f0discovery weio \ + yunjia-nrf51822 z1 + +USEMODULE += lwip lwip_ipv6_autoconfig lwip_conn_ip lwip_netdev2 +USEMODULE += lwip_udp lwip_conn_udp +USEMODULE += ipv6_addr +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += od + +# use the at86rf231 as fallback device +DRIVER := at86rf231 + +# define the driver to be used for selected boards +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif +ifneq (,$(filter mulle,$(BOARD))) + DRIVER := at86rf212b +endif +ifneq (,$(filter native,$(BOARD))) + DRIVER := netdev2_tap + USEMODULE += lwip_ethernet +endif + +ifneq (,$(filter at86rf2%,$(DRIVER))) + FEATURES_REQUIRED = periph_spi periph_gpio +endif + +USEMODULE += $(DRIVER) + +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/lwip/common.c b/tests/lwip/common.c new file mode 100644 index 0000000000..ec50688980 --- /dev/null +++ b/tests/lwip/common.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 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 + */ + +#include + +#include "common.h" + +size_t hex2ints(uint8_t *out, const char *in) +{ + bool upper = true; + size_t out_size = 0; + + while (*in != '\0') { + char c; + if ((*in >= '0') && (*in <= '9')) { + c = '0'; + } + else if ((*in >= 'a') && (*in <= 'f')) { + c = 'a' - 10; + } + else if ((*in >= 'A') && (*in <= 'F')) { + c = 'A' - 10; + } + else { + in++; + continue; + } + if (upper) { + *out = (char)(*in - c) << 4; + } + else { + *out |= (char)(*in - c); + out++; + out_size++; + } + upper = !upper; + in++; + } + if (!upper) { + out_size++; + } + return out_size; +} + +/** @} */ diff --git a/tests/lwip/common.h b/tests/lwip/common.h new file mode 100644 index 0000000000..635f1dae94 --- /dev/null +++ b/tests/lwip/common.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Martine Lenders + * + * 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 Definitions for tests/lwip/ + * + * @author Martine Lenders + */ +#ifndef MAIN_H_ +#define MAIN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Application configuration + * @{ + */ +#define CONN_INBUF_SIZE (256) +#define SERVER_MSG_QUEUE_SIZE (8) +#define SERVER_BUFFER_SIZE (64) +/** + * @} + */ + +/** + * @brief Converts hex string to byte array. + * + * @param[out] out Resulting byte array + * @param[in] in `\0` terminated string. Non-hex characters (all except 0-9, a-f, A-F) + * will be ignored. + * + * @return Length of @p out. + */ +size_t hex2ints(uint8_t *out, const char *in); + +#ifdef MODULE_CONN_IP +/** + * @brief Raw IP shell command + * + * @param[in] argc number of arguments + * @param[in] argv array of arguments + * + * @return 0 on success + * @return other on error + */ +int ip_cmd(int argc, char **argv); +#endif + +#ifdef MODULE_CONN_UDP +/** + * @brief UDP IP shell command + * + * @param[in] argc number of arguments + * @param[in] argv array of arguments + * + * @return 0 on success + * @return other on error + */ +int udp_cmd(int argc, char **argv); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_H_ */ +/** @} */ diff --git a/tests/lwip/ip.c b/tests/lwip/ip.c new file mode 100644 index 0000000000..dac3059e46 --- /dev/null +++ b/tests/lwip/ip.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 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 examples + * @{ + * + * @file + * @brief Demonstrating the sending and receiving of UDP data over POSIX sockets. + * + * @author Martine Lenders + * + * @} + */ + +#include +#include +#include + +#include "common.h" +#include "od.h" +#include "net/af.h" +#include "net/conn/ip.h" +#include "net/ipv6.h" +#include "thread.h" +#include "xtimer.h" + +#ifdef MODULE_CONN_IP +static char conn_inbuf[CONN_INBUF_SIZE]; +static bool server_running; +static conn_ip_t server_conn; +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; + uint8_t protocol; + + msg_init_queue(server_msg_queue, SERVER_MSG_QUEUE_SIZE); + /* parse protocol */ + protocol = (uint8_t)atoi((char *)args); + if (conn_ip_create(&server_conn, &server_addr, sizeof(server_addr), AF_INET6, protocol) < 0) { + return NULL; + } + server_running = true; + printf("Success: started IP server on protocol %u\n", protocol); + while (1) { + int res; + ipv6_addr_t src; + size_t src_len = sizeof(ipv6_addr_t); + if ((res = conn_ip_recvfrom(&server_conn, conn_inbuf, sizeof(conn_inbuf), &src, + &src_len)) < 0) { + puts("Error on receive"); + } + else if (res == 0) { + puts("No data received"); + } + else { + od_hex_dump(conn_inbuf, res, 0); + } + } + return NULL; +} + +static int ip_send(char *addr_str, char *port_str, char *data, unsigned int num, + unsigned int delay) +{ + ipv6_addr_t src = IPV6_ADDR_UNSPECIFIED, dst; + uint8_t protocol; + uint8_t byte_data[strlen(data) / 2]; + size_t data_len; + + /* parse destination address */ + if (ipv6_addr_from_str(&dst, addr_str) == NULL) { + puts("Error: unable to parse destination address"); + return 1; + } + /* parse protocol */ + protocol = (uint8_t)atoi(port_str); + data_len = hex2ints(byte_data, data); + for (unsigned int i = 0; i < num; i++) { + if (conn_ip_sendto(byte_data, data_len, &src, sizeof(src), (struct sockaddr *)&dst, + sizeof(dst), AF_INET6, protocol) < 0) { + puts("could not send"); + } + else { + printf("Success: send %u byte to %s (next header: %u)\n", + (unsigned)data_len, addr_str, protocol); + } + xtimer_usleep(delay); + } + return 0; +} + +static int ip_start_server(char *port_str) +{ + if (thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _server_thread, port_str, + "IP server") <= KERNEL_PID_UNDEF) { + return 1; + } + return 0; +} + +int ip_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s [send|server]\n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "send") == 0) { + uint32_t num = 1; + uint32_t delay = 1000000; + if (argc < 5) { + printf("usage: %s send [ []]\n", + argv[0]); + return 1; + } + if (argc > 5) { + num = (uint32_t)atoi(argv[5]); + } + if (argc > 6) { + delay = (uint32_t)atoi(argv[6]); + } + return ip_send(argv[2], argv[3], argv[4], num, delay); + } + else if (strcmp(argv[1], "server") == 0) { + if (argc < 3) { + printf("usage: %s server [start|stop]\n", argv[0]); + return 1; + } + if (strcmp(argv[2], "start") == 0) { + if (argc < 4) { + printf("usage %s server start \n", argv[0]); + return 1; + } + return ip_start_server(argv[3]); + } + else { + puts("error: invalid command"); + return 1; + } + } + else { + puts("error: invalid command"); + return 1; + } +} +#else +typedef int dont_be_pedantic; +#endif + +/** @} */ diff --git a/tests/lwip/main.c b/tests/lwip/main.c new file mode 100644 index 0000000000..4a26afa2c3 --- /dev/null +++ b/tests/lwip/main.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 examples + * @{ + * + * @file + * @brief Test for raw IPv6 connections + * + * @author Martine Lenders + * + * This test application tests the gnrc_conn_ip module. If you select protocol 58 you can also + * test if gnrc is able to deal with multiple subscribers to ICMPv6 (gnrc_icmpv6 and this + * application). + * + * @} + */ + +#include +#include + +#include "common.h" +#include "lwip.h" +#include "lwip/netif.h" +#include "net/ipv6/addr.h" +#include "shell.h" + +static int ifconfig(int argc, char **argv) +{ + (void)argc; + (void)argv; + for (struct netif *iface = netif_list; iface != NULL; iface = iface->next) { + printf("%s_%02u: ", iface->name, iface->num); +#ifdef MODULE_LWIP_IPV6 + char addrstr[IPV6_ADDR_MAX_STR_LEN]; + for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (!ipv6_addr_is_unspecified((ipv6_addr_t *)&iface->ip6_addr[i])) { + printf(" inet6 %s\n", ipv6_addr_to_str(addrstr, (ipv6_addr_t *)&iface->ip6_addr[i], + sizeof(addrstr))); + } + } +#endif + puts(""); + } + return 0; +} + +static const shell_command_t shell_commands[] = { +#ifdef MODULE_CONN_IP + { "ip", "Send IP packets and listen for packets of certain type", ip_cmd }, +#endif +#ifdef MODULE_CONN_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]; + +int main(void) +{ + puts("RIOT lwip test application"); + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should be never reached */ + return 0; +} diff --git a/tests/lwip/tests/01-run.py b/tests/lwip/tests/01-run.py new file mode 100755 index 0000000000..2490f502d1 --- /dev/null +++ b/tests/lwip/tests/01-run.py @@ -0,0 +1,266 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright © 2016 Martine Lenders +# +# Distributed under terms of the MIT license. + +from __future__ import print_function +import argparse +import os, sys +import random +import pexpect +import subprocess +import time +import types + +DEFAULT_TIMEOUT = 5 + +class Strategy(object): + def __init__(self, func=None): + if func != None: + if sys.version_info < (3,): + self.__class__.execute = types.MethodType(func, self, self.__class__) + else: + self.__class__.execute = types.MethodType(func, self) + + def execute(self, *args, **kwargs): + raise NotImplementedError() + +class ApplicationStrategy(Strategy): + def __init__(self, app_dir=os.getcwd(), func=None): + super(ApplicationStrategy, self).__init__(func) + self.app_dir = app_dir + +class BoardStrategy(Strategy): + def __init__(self, board, func=None): + super(BoardStrategy, self).__init__(func) + self.board = board + + def __run_make(self, application, make_targets, env=None): + env = os.environ.copy() + if env != None: + env.update(env) + env.update(self.board.to_env()) + cmd = ("make", "-C", application) + make_targets + print(' '.join(cmd)) + print(subprocess.check_output(cmd, env=env)) + + def execute(self, application): + super(BoardStrategy, self).execute(application) + +class CleanStrategy(BoardStrategy): + def execute(self, application, env=None): + super(CleanStrategy, self).__run_make(application, ("-B", "clean"), env) + +class BuildStrategy(BoardStrategy): + def execute(self, application, env=None): + super(BuildStrategy, self).__run_make(application, ("all",), env) + +class FlashStrategy(BoardStrategy): + def execute(self, application, env=None): + super(FlashStrategy, self).__run_make(application, ("all",), env) + +class ResetStrategy(BoardStrategy): + def execute(self, application, env=None): + super(ResetStrategy, self).__run_make(application, ("reset",), env) + +class Board(object): + def __init__(self, name, port=None, serial=None, clean=None, + build=None, flash=None, + reset=None, term=None): + def _reset_native_execute(obj, application, env=None, *args, **kwargs): + pass + + if (name == "native") and (reset == None): + reset = _reset_native_execute + + self.name = name + self.port = port + self.serial = serial + self.clean_strategy = CleanStrategy(self, clean) + self.build_strategy = BuildStrategy(self, build) + self.flash_strategy = FlashStrategy(self, flash) + self.reset_strategy = ResetStrategy(self, reset) + + def __len__(self): + return 1 + + def __iter__(self): + return self + + def next(self): + raise StopIteration() + + def __repr__(self): + return ("" % + (repr(self.name), repr(self.port), repr(self.serial))) + + def to_env(self): + env = {} + if self.name: + env['BOARD'] = self.name + if self.port: + env['PORT'] = self.port + if self.serial: + env['SERIAL'] = self.serial + return env + + def clean(self, application=os.getcwd(), env=None): + self.build_strategy.execute(application, env) + + def build(self, application=os.getcwd(), env=None): + self.build_strategy.execute(application, env) + + def flash(self, application=os.getcwd(), env=None): + self.flash_strategy.execute(application, env) + + def reset(self, application=os.getcwd(), env=None): + self.reset_strategy.execute(application, env) + +class BoardGroup(object): + def __init__(self, boards): + self.boards = boards + + def __len__(self): + return len(self.boards) + + def __iter__(self): + return iter(self.boards) + + def __repr__(self): + return str(self.boards) + + def clean(self, application=os.getcwd(), env=None): + for board in self.boards: + board.clean(application, env) + + def build(self, application=os.getcwd(), env=None): + for board in self.boards: + board.build(application, env) + + def flash(self, application=os.getcwd(), env=None): + for board in self.boards: + board.flash(application, env) + + def reset(self, application=os.getcwd(), env=None): + for board in self.boards: + board.reset(application, env) + +def default_test_case(board_group, application, env=None): + for board in board_group: + env = os.environ.copy() + if env != None: + env.update(env) + env.update(board.to_env()) + with pexpect.spawn("make", ["-C", application, "term"], env=env, + timeout=DEFAULT_TIMEOUT, + logfile=sys.stdout) as spawn: + spawn.expect("TEST: SUCCESS") + +class TestStrategy(ApplicationStrategy): + def execute(self, board_groups, test_cases=[default_test_case], + timeout=DEFAULT_TIMEOUT, env=None): + for board_group in board_groups: + print("Testing for %s: " % board_group) + for test_case in test_cases: + board_group.reset() + test_case(board_group, self.app_dir, env=None) + sys.stdout.write('.') + sys.stdout.flush() + print() + +def get_ipv6_address(spawn): + spawn.sendline(u"ifconfig") + spawn.expect(u"[A-Za-z0-9]{2}[0-9]+: inet6 (fe80::[0-9a-f:]+)") + return spawn.match.group(1) + +def test_ipv6_send(board_group, application, env=None): + env_sender = os.environ.copy() + if env != None: + env_sender.update(env) + env_sender.update(board_group.boards[0].to_env()) + env_receiver = os.environ.copy() + if env != None: + env_receiver.update(env) + env_receiver.update(board_group.boards[1].to_env()) + with pexpect.spawn("make", ["-C", application, "term"], env=env_sender, + timeout=DEFAULT_TIMEOUT) as sender, \ + pexpect.spawn("make", ["-C", application, "term"], env=env_receiver, + timeout=DEFAULT_TIMEOUT) as receiver: + ipprot = random.randint(0x00, 0xff) + receiver_ip = get_ipv6_address(receiver) + + receiver.sendline(u"ip server start %d" % ipprot) + # wait for neighbor discovery to be done + time.sleep(5) + sender.sendline(u"ip send %s %d 01:23:45:67:89:ab:cd:ef" % (receiver_ip, ipprot)) + sender.expect_exact(u"Success: send 8 byte to %s (next header: %d)" % + (receiver_ip, ipprot)) + receiver.expect(u"000000 60 00 00 00 00 08 %s ff fe 80 00 00 00 00 00 00" % hex(ipprot)[2:]) + receiver.expect(u"000010( [0-9a-f]{2}){8} fe 80 00 00 00 00 00 00") + receiver.expect(u"000020( [0-9a-f]{2}){8} 01 23 45 67 89 ab cd ef") + +def test_udpv6_send(board_group, application, env=None): + env_sender = os.environ.copy() + if env != None: + env_sender.update(env) + env_sender.update(board_group.boards[0].to_env()) + env_receiver = os.environ.copy() + if env != None: + env_receiver.update(env) + env_receiver.update(board_group.boards[1].to_env()) + with pexpect.spawn("make", ["-C", application, "term"], env=env_sender, + timeout=DEFAULT_TIMEOUT) as sender, \ + pexpect.spawn("make", ["-C", application, "term"], env=env_receiver, + timeout=DEFAULT_TIMEOUT) as receiver: + port = random.randint(0x0000, 0xffff) + receiver_ip = get_ipv6_address(receiver) + + receiver.sendline(u"udp server start %d" % port) + # wait for neighbor discovery to be done + time.sleep(5) + sender.sendline(u"udp send %s %d ab:cd:ef" % (receiver_ip, port)) + sender.expect_exact(u"Success: send 3 byte to [%s]:%d" % + (receiver_ip, port)) + receiver.expect(u"000000 ab cd ef") + +def test_dual_send(board_group, application, env=None): + env_sender = os.environ.copy() + if env != None: + env_sender.update(env) + env_sender.update(board_group.boards[0].to_env()) + env_receiver = os.environ.copy() + if env != None: + env_receiver.update(env) + env_receiver.update(board_group.boards[1].to_env()) + with pexpect.spawn("make", ["-C", application, "term"], env=env_sender, + timeout=DEFAULT_TIMEOUT) as sender, \ + pexpect.spawn("make", ["-C", application, "term"], env=env_receiver, + timeout=DEFAULT_TIMEOUT) as receiver: + port = random.randint(0x0000, 0xffff) + ipprot = random.randint(0x00, 0xff) + receiver_ip = get_ipv6_address(receiver) + + receiver.sendline(u"ip server start %d" % ipprot) + receiver.sendline(u"udp server start %d" % port) + # wait for neighbor discovery to be done + time.sleep(5) + sender.sendline(u"udp send %s %d 01:23" % (receiver_ip, port)) + sender.expect_exact(u"Success: send 2 byte to [%s]:%d" % + (receiver_ip, port)) + receiver.expect(u"000000 01 23") + + sender.sendline(u"ip send %s %d 01:02:03:04" % (receiver_ip, ipprot)) + sender.expect_exact(u"Success: send 4 byte to %s (next header: %d)" % + (receiver_ip, ipprot)) + receiver.expect(u"000000 60 00 00 00 00 04 %s ff fe 80 00 00 00 00 00 00" % hex(ipprot)[2:]) + receiver.expect(u"000010( [0-9a-f]{2}){8} fe 80 00 00 00 00 00 00") + receiver.expect(u"000020( [0-9a-f]{2}){8} 01 02 03 04") + +if __name__ == "__main__": + del os.environ['TERMFLAGS'] + TestStrategy().execute([BoardGroup((Board("native", "tap0"), \ + Board("native", "tap1")))], \ + [test_ipv6_send, test_udpv6_send, test_dual_send]) diff --git a/tests/lwip/udp.c b/tests/lwip/udp.c new file mode 100644 index 0000000000..748110cc6e --- /dev/null +++ b/tests/lwip/udp.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2015 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 examples + * @{ + * + * @file + * @brief Demonstrating the sending and receiving of UDP data over POSIX sockets. + * + * @author Martine Lenders + * + * @} + */ + +#include +#include +#include + +#include "common.h" +#include "od.h" +#include "net/af.h" +#include "net/conn/udp.h" +#include "net/ipv6.h" +#include "thread.h" +#include "xtimer.h" + +#ifdef MODULE_CONN_UDP +static char conn_inbuf[CONN_INBUF_SIZE]; +static bool server_running; +static conn_udp_t server_conn; +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) { + printf("Unable to open UDP server on port %" PRIu16 " (error code %d)\n", + port, -res); + return NULL; + } + 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) { + puts("Error on receive"); + } + else if (res == 0) { + puts("No data received"); + } + 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); + } + } + return NULL; +} + +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]; + size_t data_len; + + /* parse destination address */ + if (ipv6_addr_from_str(&dst, addr_str) == NULL) { + puts("Error: unable to parse destination address"); + return 1; + } + /* parse port */ + port = (uint16_t)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) { + puts("could not send"); + } + else { + printf("Success: send %u byte to [%s]:%" PRIu16 ")\n", + (unsigned)data_len, addr_str, port); + } + xtimer_usleep(delay); + } + return 0; +} + +static int udp_start_server(char *port_str) +{ + if (thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _server_thread, port_str, + "UDP server") <= KERNEL_PID_UNDEF) { + return 1; + } + return 0; +} + +int udp_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s [send|server]\n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "send") == 0) { + uint32_t num = 1; + uint32_t delay = 1000000; + if (argc < 5) { + printf("usage: %s send [ []]\n", + argv[0]); + return 1; + } + if (argc > 5) { + num = (uint32_t)atoi(argv[5]); + } + if (argc > 6) { + delay = (uint32_t)atoi(argv[6]); + } + return udp_send(argv[2], argv[3], argv[4], num, delay); + } + else if (strcmp(argv[1], "server") == 0) { + if (argc < 3) { + printf("usage: %s server [start|stop]\n", argv[0]); + return 1; + } + if (strcmp(argv[2], "start") == 0) { + if (argc < 4) { + printf("usage %s server start \n", argv[0]); + return 1; + } + return udp_start_server(argv[3]); + } + else { + puts("error: invalid command"); + return 1; + } + } + else { + puts("error: invalid command"); + return 1; + } +} +#else +typedef int dont_be_pedantic; +#endif + +/** @} */