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}" "$@"