diff --git a/sys/net/application_layer/sock_dodtls/sock_dodtls.c b/sys/net/application_layer/sock_dodtls/sock_dodtls.c index 421638304e..2af5e68d5e 100644 --- a/sys/net/application_layer/sock_dodtls/sock_dodtls.c +++ b/sys/net/application_layer/sock_dodtls/sock_dodtls.c @@ -96,6 +96,7 @@ int sock_dodtls_query(const char *domain_name, void *addr_out, int family) timeout -= send_duration; if (res <= 0) { _sleep_ms(timeout); + continue; } res = sock_dtls_recv(&_dtls_sock, &_server_session, _dns_buf, sizeof(_dns_buf), timeout); diff --git a/tests/gnrc_sock_dodtls/Makefile b/tests/gnrc_sock_dodtls/Makefile new file mode 100644 index 0000000000..5c65a19505 --- /dev/null +++ b/tests/gnrc_sock_dodtls/Makefile @@ -0,0 +1,47 @@ +include ../Makefile.tests_common + +RIOTBASE ?= $(CURDIR)/../.. + +export TAP ?= tap0 + +# TinyDTLS only has support for 32-bit architectures ATM +FEATURES_REQUIRED += arch_32bit + +USEMODULE += auto_init_gnrc_netif +USEMODULE += ipv4_addr +USEMODULE += ipv6_addr +USEMODULE += sock_dodtls +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_netif_single # Only one interface used and it makes + # shell commands easier +USEMODULE += posix_inet +# tinydtls needs crypto secure PRNG +USEMODULE += prng_sha1prng +USEMODULE += shell +USEMODULE += shell_commands + +USEPKG += tinydtls + +# use Ethernet as link-layer protocol +ifeq (native,$(BOARD)) + TERMFLAGS ?= $(TAP) +else + ETHOS_BAUDRATE ?= 115200 + CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE) + TERMDEPS += ethos + TERMPROG ?= sudo $(RIOTTOOLS)/ethos/ethos + TERMFLAGS ?= $(TAP) $(PORT) $(ETHOS_BAUDRATE) +endif + +CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\) + +# The test requires some setup and to be run as root +# So it cannot currently be run +TEST_ON_CI_BLACKLIST += all + +.PHONY: ethos + +ethos: + $(Q)env -u CC -u CFLAGS $(MAKE) -C $(RIOTTOOLS)/ethos + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sock_dodtls/Makefile.board.dep b/tests/gnrc_sock_dodtls/Makefile.board.dep new file mode 100644 index 0000000000..b595b8605c --- /dev/null +++ b/tests/gnrc_sock_dodtls/Makefile.board.dep @@ -0,0 +1,6 @@ +# Put board specific dependencies here +ifeq (native,$(BOARD)) + USEMODULE += netdev_tap +else + USEMODULE += stdio_ethos +endif diff --git a/tests/gnrc_sock_dodtls/Makefile.ci b/tests/gnrc_sock_dodtls/Makefile.ci new file mode 100644 index 0000000000..342ddb97e0 --- /dev/null +++ b/tests/gnrc_sock_dodtls/Makefile.ci @@ -0,0 +1,50 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega328p \ + atmega328p-xplained-mini \ + atxmega-a3bu-xplained \ + blackpill \ + bluepill \ + bluepill-stm32f030c8 \ + derfmega128 \ + hifive1 \ + hifive1b \ + i-nucleo-lrwan1 \ + im880b \ + mega-xplained \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + saml10-xpro \ + saml11-xpro \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + stm32f7508-dk \ + stm32g0316-disco \ + stm32mp157c-dk2 \ + telosb \ + thingy52 \ + waspmote-pro \ + z1 \ + zigduino \ + # diff --git a/tests/gnrc_sock_dodtls/README.md b/tests/gnrc_sock_dodtls/README.md new file mode 100644 index 0000000000..b59df9c8ef --- /dev/null +++ b/tests/gnrc_sock_dodtls/README.md @@ -0,0 +1,118 @@ +# Overview + +This folder contains a test application for RIOT's sock-based DNS over DTLS +client. + +# How to test with native + +## Setting up a tap interface + +1. Create a tap interface with a valid IPv6 address + + ```console + $ sudo ip tuntap add dev tap0 mode tap user $(id -u -n) + $ sudo ip a a 2001:db8::1/64 dev tap0 + $ sudo ip link set up dev tap0 + $ ip addr show dev tap0 + 4: tap0: mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 + link/ether e2:bc:7d:6b:8b:08 brd ff:ff:ff:ff:ff:ff + inet6 2001:db8::1/64 scope global + valid_lft forever preferred_lft forever + inet6 fe80::e0bc:7dff:fe6b:8b08/64 scope link + valid_lft forever preferred_lft forever + ``` + + **Note down** the link-local address. + +2. Run the test application + + ```console + $ make flash -j term + ``` + + And copy the link-local address using `ifconfig`: + + ``` + > ifconfig + ifconfig + Iface 5 HWaddr: E2:BC:7D:6B:8B:09 + L2-PDU:1500 MTU:1500 HL:64 Source address length: 6 + Link type: wired + inet6 addr: fe80::e0bc:7dff:fe6b:8b09 scope: link VAL + inet6 group: ff02::1 + inet6 group: ff02::1:ff6b:8b09 + + ``` + +3. Use it to configure a route to the `native` device (replace `2001:db8::/64` if you used a + different prefix in 1.): + + ```console + $ sudo ip route add 2001:db8::/64 via fe80::e0bc:7dff:fe6b:8b09 dev tap0 + ``` + +4. Run `make term` again to configure the global address for the `native` device and the route to + the host from the `native` device: + + ```console + > ifconfig 5 add 2001:db8::2 + ifconfig 5 add 2001:db8::2 + success: added 2001:db8::2/64 to interface 5 + > nib route add 5 default fe80::e0bc:7dff:fe6b:8b08 + nib route add 5 default fe80::e0bc:7dff:fe6b:8b08 + ``` + + **Keep the `native` instance open for [2.3](#configure-dns-over-dtls-client-and-query-a-name)** + +## Install and run a test server + +1. In a new terminal install `aiodnsprox` as your test server: + + ```console + $ sudo pip install git+https://github.com/anr-bmbf-pivot/aiodnsprox/ + ``` + +2. Provide a minimal configuration file containing the `TLS_PSK_WITH_AES_128_CCM_8` pre-shared key + credentials for the DTLS server: + + ```console + $ cat << EOF > test.yaml + dtls_credentials: + client_identity: Client_identity + psk: secretPSK + EOF + ``` + +3. Run the DNS server with a DTLS transport bound to the `tap0` interface (`-d 2001:db8::1`; replace + the address if you used a different one in [2.1](#setting-up-a-tap-interface)'s step 1), using a + public DNS server as upstream (`-U 9.9.9.9`). `sudo` is required to be able to bind to the + DNS over DTLS port 853: + + ```console + $ sudo aiodns-proxy -C test.yaml -U 9.9.9.9 -d 2001:db8::1 + ``` + +## Configure DNS over DTLS client and query a name + +Use the RIOT shell you kept open in [2.1](#setting-up-a-tap-interface) to configure the DNS over +DTLS server and request `example.org` from it + +1. Provide the DNS over DTLS server address, port (optional), credential tag (5853), + `TLS_PSK_WITH_AES_128_CCM_8` client identity (`Client_identity`) and + `TLS_PSK_WITH_AES_128_CCM_8` secret key (`secretPSK`) to the DNS over DTLS client: + + ```console + > dodtls server [2001:db8::1]:853 5853 Client_identity secretPSK + > dodtls server + DNS over DTLS server: [2001:db8::1]:853 + ``` + +2. Now you should be able to query a name: + ```console + > dodtls request example.org inet6 + dodtls request example.org inet6 + example.org resolves to 2606:2800:220:1:248:1893:25c8:1946 + > dodtls request example.org inet + dodtls request example.org inet + example.org resolves to 93.184.216.34 + ``` diff --git a/tests/gnrc_sock_dodtls/main.c b/tests/gnrc_sock_dodtls/main.c new file mode 100644 index 0000000000..26f28ede03 --- /dev/null +++ b/tests/gnrc_sock_dodtls/main.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * Copyright (C) 2021 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 sock DNS over DTLS client test application + * + * @author Kaspar Schleiser + * @author Martine S. Lenders + * + * @} + */ + +#include +#include + +#include + +#include "net/credman.h" +#include "net/sock/dodtls.h" +#include "net/sock/util.h" +#include "shell.h" + +#define MAIN_QUEUE_SIZE 8U +#define PSK_ID_LEN 32U +#define PSK_LEN 32U + +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +static int _dodtls(int argc, char **argv); + +static const shell_command_t _shell_commands[] = { + { "dodtls", "configures and requests a DNS server", _dodtls }, + { NULL, NULL, NULL }, +}; +static char _shell_buffer[SHELL_DEFAULT_BUFSIZE]; +static char _psk_id[PSK_ID_LEN]; +static char _psk[PSK_LEN]; +static credman_credential_t _credential = { + .type = CREDMAN_TYPE_PSK, + .params = { + .psk = { + .id = { .s = _psk_id, .len = 0U, }, + .key = { .s = _psk, .len = 0U, }, + } + } +}; + +static void _usage(char *cmd) +{ + printf("usage: %s server " + " \n", cmd); + printf("usage: %s server -d\n", cmd); + printf(" %s request []\n", cmd); +} + +static int _dodtls_server(int argc, char **argv) +{ + sock_udp_ep_t server; + int res; + + if ((argc == 3) && (strcmp(argv[2], "-d") == 0)) { + sock_dodtls_set_server(NULL, NULL); + } + else if ((argc > 1) && (argc < 6)) { + _usage(argv[0]); + return 1; + } + else { + if (sock_udp_str2ep(&server, argv[2]) < 0) { + _usage(argv[0]); + return 1; + } + if (server.port == 0) { + server.port = SOCK_DODTLS_PORT; + } + if (server.netif == SOCK_ADDR_ANY_NETIF) { + netif_t *netif = netif_iter(NULL); + /* we only have one interface so take that one, otherwise + * TinyDTLS is not able to identify the peer */ + server.netif = netif_get_id(netif); + } + if ((_credential.tag = atoi(argv[3])) == 0) { + _usage(argv[0]); + return 1; + } + if ((_credential.params.psk.id.len = strlen(argv[4])) >= PSK_ID_LEN) { + printf("PSK ID too long (max. %u bytes allowed)", PSK_ID_LEN); + return 1; + } + if ((_credential.params.psk.key.len = strlen(argv[5])) >= PSK_LEN) { + printf("PSK too long (max. %u bytes allowed)", PSK_LEN); + return 1; + } + strcpy((char *)_credential.params.psk.id.s, argv[4]); + strcpy((char *)_credential.params.psk.key.s, argv[5]); + if ((res = sock_dodtls_set_server(&server, &_credential)) < 0) { + errno = -res; + perror("Unable to establish session with server"); + } + } + if (sock_dodtls_get_server(&server) == 0) { + char addrstr[INET6_ADDRSTRLEN + 8U]; /* + 8 for port + colons + [] */ + uint16_t port; + + sock_udp_ep_fmt(&server, addrstr, &port); + printf("DNS server: [%s]:%u\n", addrstr, port); + } + else { + puts("DNS server: -"); + } + return 0; +} + +static int _dodtls_request(int argc, char **argv) +{ + uint8_t addr[16] = {0}; + int res, family = AF_UNSPEC; + + if (argc > 3) { + if (strcmp(argv[3], "inet") == 0) { + family = AF_INET; + } + else if (strcmp(argv[3], "inet6") == 0) { + family = AF_INET6; + } + else { + _usage(argv[0]); + } + } + + res = sock_dodtls_query(argv[2], addr, family); + + if (res > 0) { + char addrstr[INET6_ADDRSTRLEN]; + + if (inet_ntop(res == 4 ? AF_INET : AF_INET6, addr, addrstr, + sizeof(addrstr))) { + printf("%s resolves to %s\n", argv[2], addrstr); + } + else { + printf("unable to print resolved address for %s.\n", argv[2]); + printf("Maybe address module is missing\n"); + return 1; + } + } + else { + printf("error resolving %s: ", argv[2]); + errno = -res; + perror(""); + return 1; + } + return 0; +} + +static int _dodtls(int argc, char **argv) +{ + if ((argc > 1) && (strcmp(argv[1], "server") == 0)) { + return _dodtls_server(argc, argv); + } + else if ((argc > 2) && (strcmp(argv[1], "request") == 0)) { + return _dodtls_request(argc, argv); + } + else { + _usage(argv[0]); + return 1; + } +} + +int main(void) +{ + /* we need a message queue for the thread running the shell in order to + * receive potentially fast incoming networking packets */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + + /* start shell */ + shell_run(_shell_commands, _shell_buffer, sizeof(_shell_buffer)); + return 0; +}