From 54eedd94ba9a2ccc6d89c1528d9fc3f83ca0abf9 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 13 Aug 2020 17:30:47 +0200 Subject: [PATCH 1/4] tools/zep_dispatch: add simple ZEP dispatcher --- dist/tools/Makefile | 2 +- dist/tools/zep_dispatch/Makefile | 16 +++ dist/tools/zep_dispatch/main.c | 121 +++++++++++++++++++++++ dist/tools/zep_dispatch/start_network.sh | 99 +++++++++++++++++++ 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 dist/tools/zep_dispatch/Makefile create mode 100644 dist/tools/zep_dispatch/main.c create mode 100755 dist/tools/zep_dispatch/start_network.sh diff --git a/dist/tools/Makefile b/dist/tools/Makefile index aed03d3a00..b19355f1a6 100644 --- a/dist/tools/Makefile +++ b/dist/tools/Makefile @@ -1,4 +1,4 @@ -HOST_TOOLS=ethos uhcpd sliptty +HOST_TOOLS=ethos uhcpd sliptty zep_dispatch .PHONY: all $(HOST_TOOLS) diff --git a/dist/tools/zep_dispatch/Makefile b/dist/tools/zep_dispatch/Makefile new file mode 100644 index 0000000000..c6f881c198 --- /dev/null +++ b/dist/tools/zep_dispatch/Makefile @@ -0,0 +1,16 @@ +CFLAGS?=-g -O3 -Wall -Wextra + +BINARY := bin/zep_dispatch +all: bin $(BINARY) + +bin: + mkdir bin + +RIOTBASE:=../../.. +RIOT_INCLUDE=$(RIOTBASE)/core/include +SRCS:=$(wildcard *.c) +$(BINARY): $(SRCS) + $(CC) $(CFLAGS) $(CFLAGS_EXTRA) -I$(RIOT_INCLUDE) $(SRCS) -o $@ + +clean: + rm -f $(BINARY) diff --git a/dist/tools/zep_dispatch/main.c b/dist/tools/zep_dispatch/main.c new file mode 100644 index 0000000000..00432556a0 --- /dev/null +++ b/dist/tools/zep_dispatch/main.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Benjamin Valentin + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#ifndef ZEP_DISPATCH_PDU +#define ZEP_DISPATCH_PDU 256 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "kernel_defines.h" + +typedef struct { + list_node_t node; + struct sockaddr_in6 addr; +} zep_client_t; + +static void dispatch_loop(int sock) +{ + list_node_t head = { .next = NULL }; + + puts("entering loop…"); + while (1) { + char addr_str[INET6_ADDRSTRLEN]; + uint8_t buffer[ZEP_DISPATCH_PDU]; + struct sockaddr_in6 src_addr; + socklen_t addr_len = sizeof(src_addr); + + /* receive incoming packet */ + ssize_t bytes_in = recvfrom(sock, buffer, sizeof(buffer), 0, + (struct sockaddr*)&src_addr, &addr_len); + + if (bytes_in <= 0 || addr_len != sizeof(src_addr)) { + continue; + } + + /* send packet to all other clients */ + bool known_node = false; + list_node_t *prev = &head; + for (list_node_t* n = head.next; n; n = n->next) { + struct sockaddr_in6 *addr = &container_of(n, zep_client_t, node)->addr; + + /* don't echo packet back to sender */ + if (memcmp(&src_addr, addr, addr_len) == 0) { + known_node = true; + /* remove client if sending fails */ + } else if (sendto(sock, buffer, bytes_in, 0, (struct sockaddr*)addr, addr_len) < 0) { + inet_ntop(AF_INET6, &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN); + printf("removing [%s]:%d\n", addr_str, ntohs(addr->sin6_port)); + prev->next = n->next; + free(n); + continue; + } + + prev = n; + } + + /* if the client new, add it to the broadcast list */ + if (!known_node) { + inet_ntop(AF_INET6, &src_addr.sin6_addr, addr_str, INET6_ADDRSTRLEN); + printf("adding [%s]:%d\n", addr_str, ntohs(src_addr.sin6_port)); + zep_client_t *client = malloc(sizeof(zep_client_t)); + memcpy(&client->addr, &src_addr, addr_len); + list_add(&head, &client->node); + } + } +} + +int main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "usage: %s
\n", + argv[0]); + exit(1); + } + + struct addrinfo hint = { + .ai_family = AF_INET6, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, + .ai_flags = AI_NUMERICHOST, + }; + + struct addrinfo *server_addr; + int res = getaddrinfo(argv[1], argv[2], + &hint, &server_addr); + if (res != 0) { + perror("getaddrinfo()"); + exit(1); + } + + int sock = socket(server_addr->ai_family, server_addr->ai_socktype, + server_addr->ai_protocol); + if (sock < 0) { + perror("socket() failed"); + exit(1); + } + + if (bind(sock, server_addr->ai_addr, server_addr->ai_addrlen) < 0) { + perror("bind() failed"); + exit(1); + } + + freeaddrinfo(server_addr); + + dispatch_loop(sock); + + close(sock); + + return 0; +} diff --git a/dist/tools/zep_dispatch/start_network.sh b/dist/tools/zep_dispatch/start_network.sh new file mode 100755 index 0000000000..f98cd93b87 --- /dev/null +++ b/dist/tools/zep_dispatch/start_network.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +ZEP_DISPATCH_DIR="$(cd "$(dirname "$0")" && pwd -P)" +UHCPD="$(cd "${ZEP_DISPATCH_DIR}/../uhcpd/bin" && pwd -P)/uhcpd" +DHCPD="$(cd "${ZEP_DISPATCH_DIR}/../dhcpv6-pd_ia/" && pwd -P)/dhcpv6-pd_ia.py" +ZEP_DISPATCH="${ZEP_DISPATCH_DIR}/bin/zep_dispatch" + +TAP_GLB="fdea:dbee:f::1/64" + +NOSUDO="sudo -u ${SUDO_USER}" + +create_tap() { + ip tuntap add "${TAP}" mode tap user "${SUDO_USER}" + sysctl -w net.ipv6.conf."${TAP}".forwarding=1 + sysctl -w net.ipv6.conf."${TAP}".accept_ra=0 + ip link set "${TAP}" up + ip a a fe80::1/64 dev "${TAP}" + ip a a ${TAP_GLB} dev "${TAP}" + ip route add "${PREFIX}" via fe80::2 dev "${TAP}" +} + +remove_tap() { + ip tuntap del "${TAP}" mode tap +} + +cleanup() { + echo "Cleaning up..." + remove_tap + if [ -n "${UHCPD_PID}" ]; then + kill "${UHCPD_PID}" + fi + if [ -n "${ZEP_DISPATCH_PID}" ]; then + kill "${ZEP_DISPATCH_PID}" + fi + if [ -n "${DHCPD_PIDFILE}" ]; then + kill "$(cat "${DHCPD_PIDFILE}")" + rm "${DHCPD_PIDFILE}" + fi + trap "" INT QUIT TERM EXIT +} + +start_uhcpd() { + ${UHCPD} "${TAP}" "${PREFIX}" > /dev/null & + UHCPD_PID=$! +} + +start_dhcpd() { + DHCPD_PIDFILE=$(mktemp) + ${DHCPD} -d -p "${DHCPD_PIDFILE}" "${TAP}" "${PREFIX}" 2> /dev/null +} + +start_zep_dispatch() { + ${ZEP_DISPATCH} :: "${ZEP_PORT_BASE}" > /dev/null & + ZEP_DISPATCH_PID=$! +} + +if [ "$1" = "-d" ] || [ "$1" = "--use-dhcpv6" ]; then + USE_DHCPV6=1 + shift 1 +else + USE_DHCPV6=0 +fi + +if [ "$1" = "-z" ] || [ "$1" = "--use-zep-dispatch" ]; then + USE_ZEP_DISPATCH=1 + ZEP_PORT_BASE=$2 + shift 2 +else + USE_ZEP_DISPATCH=0 +fi + +ELFFILE=$1 +PREFIX=$2 +shift 2 + +# tap will be the last argument +for TAP in "$@"; do :; done + +[[ -z "${ELFFILE}" || -z "${PREFIX}" || -z "${TAP}" ]] && { + echo "usage: $0 [-d|--use-dhcp] [-z|--use-zep ] " \ + " [elf args]" + exit 1 +} + +trap "cleanup" INT QUIT TERM EXIT + +create_tap + +if [ ${USE_ZEP_DISPATCH} -eq 1 ]; then + start_zep_dispatch +fi + +if [ ${USE_DHCPV6} -eq 1 ]; then + start_dhcpd +else + start_uhcpd +fi + +${NOSUDO} "${ELFFILE}" "${TAP}" "$@" From 90f3c150846673942f2a154c7c5adf9c4e4fbf80 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 29 Oct 2020 18:37:05 +0100 Subject: [PATCH 2/4] socket_zep: send dummy HELLO packet on connect --- cpu/native/include/socket_zep.h | 33 ++++++++++++++++++++++++++++++ cpu/native/socket_zep/socket_zep.c | 23 ++++++++++++++++++++- makefiles/pseudomodules.inc.mk | 1 + 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/cpu/native/include/socket_zep.h b/cpu/native/include/socket_zep.h index 01bbad616c..7529e7d8ef 100644 --- a/cpu/native/include/socket_zep.h +++ b/cpu/native/include/socket_zep.h @@ -13,6 +13,39 @@ * * @see @ref net_zep for protocol definitions * + * This ZEP implementation can send a dummy HELLO packet on startup. + * This is used to make dispatchers aware of the node. + * To enable this behavior, add + * + * ``` + * USEMODULE += socket_zep_hello + * ``` + * + * to your Makefile. + * + * A ZEP dispatcher can just drop those packets (ZEP type 0xFF) if it + * chooses to parse the ZEP header. + * + * The header of the HELLO packet will look like this: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Preamble (EX) | Version (2) | Type (255) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Reserved (0) + + * | | + * + + + * | | + * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | 'H' | 'E' | 'L' | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 'L' | 'O' | 0 | 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0 | 0 | 0 | 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * * @{ * * @file diff --git a/cpu/native/socket_zep/socket_zep.c b/cpu/native/socket_zep/socket_zep.c index 487a145393..f88b2c3608 100644 --- a/cpu/native/socket_zep/socket_zep.c +++ b/cpu/native/socket_zep/socket_zep.c @@ -40,6 +40,9 @@ * (https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/basedefs/time.h.html) */ #define TV_USEC_PER_SEC (1000000L) +/* dummy packet to register with ZEP dispatcher */ +#define SOCKET_ZEP_V2_TYPE_HELLO (255) + static size_t _zep_hdr_fill_v2_data(socket_zep_t *dev, zep_v2_data_hdr_t *hdr, size_t payload_len) { @@ -390,6 +393,21 @@ static int _connect_remote(socket_zep_t *dev, const socket_zep_params_t *params) return res; } +static void _send_zep_hello(socket_zep_t *dev) +{ + if (IS_USED(MODULE_SOCKET_ZEP_HELLO)) { + /* dummy packet */ + zep_v2_data_hdr_t hdr = { + .hdr.preamble = "EX", + .hdr.version = 2, + .type = SOCKET_ZEP_V2_TYPE_HELLO, + .resv = "HELLO", + }; + + real_write(dev->sock_fd, &hdr, sizeof(hdr)); + } +} + void socket_zep_setup(socket_zep_t *dev, const socket_zep_params_t *params) { int res; @@ -408,7 +426,10 @@ void socket_zep_setup(socket_zep_t *dev, const socket_zep_params_t *params) dev->sock_fd = res; } - _connect_remote(dev, params); + if (_connect_remote(dev, params) == 0) { + /* send dummy data to connect to dispatcher */ + _send_zep_hello(dev); + } /* generate hardware address from local address */ uint8_t ss_array[sizeof(struct sockaddr_storage)] = { 0 }; diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 388dcf4dc4..4e29d69dc9 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -116,6 +116,7 @@ PSEUDOMODULES += sock_dtls PSEUDOMODULES += sock_ip PSEUDOMODULES += sock_tcp PSEUDOMODULES += sock_udp +PSEUDOMODULES += socket_zep_hello PSEUDOMODULES += soft_uart_modecfg PSEUDOMODULES += stdin PSEUDOMODULES += stdio_cdc_acm From ecd40ff824f77cc126c61e91254ca95f70b44de8 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 13 Aug 2020 15:49:58 +0200 Subject: [PATCH 3/4] examples/gnrc_networking: add port configuration for socket_zep Set the socket_zep port so that native will connect to the default border router configuration. --- examples/gnrc_networking/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/gnrc_networking/Makefile b/examples/gnrc_networking/Makefile index 610d0bc501..139a202658 100644 --- a/examples/gnrc_networking/Makefile +++ b/examples/gnrc_networking/Makefile @@ -36,6 +36,17 @@ USEMODULE += netstats_rpl # development process: DEVELHELP ?= 1 +# Instead of simulating an Ethernet connection, we can also simulate +# an IEEE 802.15.4 radio using ZEP +USE_ZEP ?= 0 + +# set the ZEP port for native +ZEP_PORT_BASE ?= 17754 +ifeq (1,$(USE_ZEP)) + TERMFLAGS += -z [::1]:$(ZEP_PORT_BASE) + USEMODULE += socket_zep +endif + # Uncomment the following 2 lines to specify static link lokal IPv6 address # this might be useful for testing, in cases where you cannot or do not want to # run a shell with ifconfig to get the real link lokal address. From 0ffe5a09f40622ea845f96feab3e040e71972b4c Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 13 Aug 2020 18:43:31 +0200 Subject: [PATCH 4/4] examples/gnrc_border_router: automatically start ZEP dispatcher --- .../gnrc_border_router/Makefile.board.dep | 1 + .../gnrc_border_router/Makefile.native.conf | 20 ++++++++++++------- examples/gnrc_border_router/README.md | 13 ++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/examples/gnrc_border_router/Makefile.board.dep b/examples/gnrc_border_router/Makefile.board.dep index 32fc82bde2..ada0bf3938 100644 --- a/examples/gnrc_border_router/Makefile.board.dep +++ b/examples/gnrc_border_router/Makefile.board.dep @@ -14,4 +14,5 @@ ifeq (,$(filter native,$(BOARD))) endif else USEMODULE += socket_zep + USEMODULE += socket_zep_hello endif diff --git a/examples/gnrc_border_router/Makefile.native.conf b/examples/gnrc_border_router/Makefile.native.conf index 89b8528d25..013bc5d88d 100644 --- a/examples/gnrc_border_router/Makefile.native.conf +++ b/examples/gnrc_border_router/Makefile.native.conf @@ -9,11 +9,17 @@ CFLAGS += -DASYNC_READ_NUMOF=$(shell expr $(ZEP_DEVICES) + 1) # Set CFLAGS if not being set via Kconfig CFLAGS += $(if $(CONFIG_KCONFIG_MODULE_DHCPV6),,-DCONFIG_DHCPV6_CLIENT_PFX_LEASE_MAX=$(ZEP_DEVICES)) -# -z [::1]:$PORT for each ZEP device -TERMFLAGS += $(patsubst %,-z [::1]:%, $(shell seq $(ZEP_PORT_BASE) $(ZEP_PORT_MAX))) - -ifneq (1,$(USE_DHCPV6)) - # We don't need to start ethos so just start the UHCPD daemon in the - # background - TERMDEPS += uhcpd-daemon +ifeq (1,$(USE_DHCPV6)) + FLAGS_EXTRAS += --use-dhcpv6 endif + +# enable the ZEP dispatcher +FLAGS_EXTRAS += -z $(ZEP_PORT_BASE) + +# Configure terminal parameters +TERMDEPS += host-tools +TERMPROG_FLAGS = $(FLAGS_EXTRAS) $(ELFFILE) $(IPV6_PREFIX) +TERMPROG ?= sudo $(RIOTTOOLS)/zep_dispatch/start_network.sh $(TERMPROG_FLAGS) + +# -z [::1]:$PORT for each ZEP device +TERMFLAGS ?= $(patsubst %,-z [::1]:%, $(shell seq $(ZEP_PORT_BASE) $(ZEP_PORT_MAX))) diff --git a/examples/gnrc_border_router/README.md b/examples/gnrc_border_router/README.md index 9403554865..4e82c22d6f 100644 --- a/examples/gnrc_border_router/README.md +++ b/examples/gnrc_border_router/README.md @@ -24,6 +24,8 @@ This example comes with support for three uplink types pre-configured: For `native` the host-facing [`netdev_tap`](https://doc.riot-os.org/netdev__tap_8h.html) device is configured, providing connectivity via a TAP interface to the RIOT instance. +On the node-facing side [`socket_zep`](https://doc.riot-os.org/group__drivers__socket__zep.html) +is used to simulate a IEEE 802.15.4 network. To select an uplink, set the UPLINK environment variable. For instance, use `UPLINK=slip` for a SLIP uplink. @@ -143,6 +145,17 @@ On this example, such address can be pinged from 6lo motes: Thus far, IPv6 communication with between your PC and your motes is enabled. +### Simulated network with native + +On native a IEEE 802.15.4 network is simulated by encapsulating 802.15.4 frames +inside UDP packets. For this the `socket_zep` modules is used both on the border +router and on the virtual mote. + +The UDP packets are sent to a dispatcher which forwards them to all other nodes. +By default a simple dispatcher is provided that will forward every packet to +every node (perfect broadcast), but it can be replaced by the user with alternative +dispatchers to simulate more advanced topologies. + # gnrc_border_router with manual config You can use `ethos` as a standalone driver, if you want to setup the BR manually.