Merge pull request #13307 from miri64/dhcpv6-client-6lbr/feat/init
gnrc_dhcpv6_client_6lbr: initial import of a 6LBR DHCPv6 client
This commit is contained in:
commit
030e7ebc06
@ -90,6 +90,10 @@ ifneq (,$(filter gnrc_dhcpv6_client,$(USEMODULE)))
|
|||||||
USEMODULE += gnrc_sock_udp
|
USEMODULE += gnrc_sock_udp
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter gnrc_dhcpv6_client_6lbr,$(USEMODULE)))
|
||||||
|
USEMODULE += gnrc_dhcpv6_client
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter gnrc_uhcpc,$(USEMODULE)))
|
ifneq (,$(filter gnrc_uhcpc,$(USEMODULE)))
|
||||||
DEFAULT_MODULE += auto_init_gnrc_uhcpc
|
DEFAULT_MODULE += auto_init_gnrc_uhcpc
|
||||||
USEMODULE += uhcpc
|
USEMODULE += uhcpc
|
||||||
|
|||||||
@ -643,4 +643,10 @@ void auto_init(void)
|
|||||||
extern void dhcpv6_client_auto_init(void);
|
extern void dhcpv6_client_auto_init(void);
|
||||||
dhcpv6_client_auto_init();
|
dhcpv6_client_auto_init();
|
||||||
#endif /* MODULE_AUTO_INIT_DHCPV6_CLIENT */
|
#endif /* MODULE_AUTO_INIT_DHCPV6_CLIENT */
|
||||||
|
|
||||||
|
#ifdef MODULE_GNRC_DHCPV6_CLIENT_6LBR
|
||||||
|
DEBUG("auto_init 6LoWPAN border router DHCPv6 client");
|
||||||
|
extern void gnrc_dhcpv6_client_6lbr_init(void);
|
||||||
|
gnrc_dhcpv6_client_6lbr_init();
|
||||||
|
#endif /* MODULE_GNRC_DHCPV6_CLIENT_6LBR */
|
||||||
}
|
}
|
||||||
|
|||||||
62
sys/include/net/gnrc/dhcpv6/client/6lbr.h
Normal file
62
sys/include/net/gnrc/dhcpv6/client/6lbr.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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_dhcpv6_client_6lbr DHCPv6 client for 6LoWPAN border routers
|
||||||
|
* @ingroup net_dhcpv6_client
|
||||||
|
* @brief DHCPv6 client bootstrapping for 6LoWPAN border routers
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief DHCPv6 client on 6LoWPAN border router definitions
|
||||||
|
*
|
||||||
|
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
#ifndef NET_GNRC_DHCPV6_CLIENT_6LBR_H
|
||||||
|
#define NET_GNRC_DHCPV6_CLIENT_6LBR_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Identifier of the upstream interface
|
||||||
|
*
|
||||||
|
* Leave 0 (default) to let the client pick the first non-6LoWPAN interface it
|
||||||
|
* finds
|
||||||
|
*/
|
||||||
|
#ifndef CONFIG_GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM
|
||||||
|
#define CONFIG_GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Use static routes to upstream router
|
||||||
|
*
|
||||||
|
* If set the border router will be configured to have a default route via
|
||||||
|
* `fe80::1`. The link-local address `fe80::2` will be added so that the
|
||||||
|
* upstream router can set a static route for the delegated prefix via that
|
||||||
|
* address. It is recommended to increase at least @ref
|
||||||
|
* CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF to that end.
|
||||||
|
*/
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
#define CONFIG_GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the DHCPv6 client for 6LoWPAN border router
|
||||||
|
*
|
||||||
|
* @note Called by `auto_init` when included
|
||||||
|
*/
|
||||||
|
void gnrc_dhcpv6_client_6lbr_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NET_GNRC_DHCPV6_CLIENT_6LBR_H */
|
||||||
|
/** @} */
|
||||||
@ -7,6 +7,7 @@
|
|||||||
menu "GNRC Network stack"
|
menu "GNRC Network stack"
|
||||||
depends on MODULE_GNRC
|
depends on MODULE_GNRC
|
||||||
|
|
||||||
|
rsource "application_layer/dhcpv6/Kconfig"
|
||||||
rsource "link_layer/lorawan/Kconfig"
|
rsource "link_layer/lorawan/Kconfig"
|
||||||
rsource "netif/Kconfig"
|
rsource "netif/Kconfig"
|
||||||
rsource "network_layer/ipv6/Kconfig"
|
rsource "network_layer/ipv6/Kconfig"
|
||||||
|
|||||||
26
sys/net/gnrc/application_layer/dhcpv6/Kconfig
Normal file
26
sys/net/gnrc/application_layer/dhcpv6/Kconfig
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
menuconfig KCONFIG_MODULE_GNRC_DHCPV6
|
||||||
|
bool "Configure GNRC-part of DHCPv6"
|
||||||
|
depends on MODULE_GNRC_DHCPV6
|
||||||
|
help
|
||||||
|
Configure GNRC-part of DHCPv6 via Kconfig.
|
||||||
|
|
||||||
|
if KCONFIG_MODULE_GNRC_DHCPV6
|
||||||
|
|
||||||
|
if MODULE_GNRC_DHCPV6_CLIENT_6LBR
|
||||||
|
config GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM
|
||||||
|
int "Identifier for the upstream interface of the 6LoWPAN border router"
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Leave 0 to let the client pick the first non-6LoWPAN interface it finds
|
||||||
|
|
||||||
|
config GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE
|
||||||
|
bool "Use static routes to upstream interface"
|
||||||
|
help
|
||||||
|
If set to 1 the border router will be configured to have a default
|
||||||
|
route via `fe80::1`. The link-local address `fe80::2` will be added so
|
||||||
|
that the upstream router can set a static route for the delegated
|
||||||
|
prefix via that address. It is recommended to increase at least @ref
|
||||||
|
CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF to that end.
|
||||||
|
endif # MODULE_GNRC_DHCPV6_CLIENT_6LBR
|
||||||
|
|
||||||
|
endif # KCONFIG_MODULE_GNRC_DHCPV6
|
||||||
139
sys/net/gnrc/application_layer/dhcpv6/client_6lbr.c
Normal file
139
sys/net/gnrc/application_layer/dhcpv6/client_6lbr.c
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018-20 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 S. Lenders <m.lenders@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "net/dhcpv6/client.h"
|
||||||
|
#include "net/ipv6/addr.h"
|
||||||
|
#include "net/gnrc.h"
|
||||||
|
#include "net/gnrc/ipv6/nib/ft.h"
|
||||||
|
#include "net/gnrc/netif/internal.h"
|
||||||
|
|
||||||
|
#include "net/gnrc/dhcpv6/client/6lbr.h"
|
||||||
|
|
||||||
|
#if IS_USED(MODULE_AUTO_INIT_DHCPV6_CLIENT)
|
||||||
|
#error "Module `gnrc_dhcpv6_client_6lbr` is mutually exclusive to \
|
||||||
|
`auto_init_dhcpv6_client`"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static char _stack[DHCPV6_CLIENT_STACK_SIZE];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find upstream network interface
|
||||||
|
*
|
||||||
|
* Either the one network interface configured at compile-time with @ref
|
||||||
|
* CONFIG_GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM is picked or the first network
|
||||||
|
* interface that is not a 6LoWPAN interfaces if
|
||||||
|
* `CONFIG_GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM` is 0.
|
||||||
|
*
|
||||||
|
* @return The upstream network interface.
|
||||||
|
*/
|
||||||
|
static gnrc_netif_t *_find_upstream_netif(void)
|
||||||
|
{
|
||||||
|
gnrc_netif_t *netif = NULL;
|
||||||
|
|
||||||
|
if (CONFIG_GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM) {
|
||||||
|
return gnrc_netif_get_by_pid(CONFIG_GNRC_DHCPV6_CLIENT_6LBR_UPSTREAM);
|
||||||
|
}
|
||||||
|
while ((netif = gnrc_netif_iter(netif))) {
|
||||||
|
if (!gnrc_netif_is_6lo(netif)) {
|
||||||
|
LOG_WARNING("DHCPv6: Selecting interface %d as upstream\n",
|
||||||
|
netif->pid);
|
||||||
|
return netif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure upstream netif to be in line with configuration script
|
||||||
|
*
|
||||||
|
* Set route and link-local address in accordance to
|
||||||
|
* `dist/tools/ethos/setup_network.sh`.
|
||||||
|
*
|
||||||
|
* @note This might not be necessary with a properly set-up DHCPv6 server
|
||||||
|
* (automatically configures a route for the delegated prefix) and
|
||||||
|
* upstream router (sends periodic router advertisements).
|
||||||
|
*
|
||||||
|
* @param[in] upstream_netif The upstream netif The upstream netif
|
||||||
|
*/
|
||||||
|
static void _configure_upstream_netif(gnrc_netif_t *upstream_netif)
|
||||||
|
{
|
||||||
|
if (IS_ACTIVE(CONFIG_GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE)) {
|
||||||
|
ipv6_addr_t addr = {
|
||||||
|
.u8 = { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* set default route to host machine (as set-up in setup_network.sh) */
|
||||||
|
gnrc_ipv6_nib_ft_add(NULL, 0, &addr, upstream_netif->pid, 0);
|
||||||
|
/* set additional link-local address to provide a well-known next hop
|
||||||
|
* for static route configuration on the host machine */
|
||||||
|
addr.u8[15] = 2;
|
||||||
|
gnrc_netif_ipv6_addr_add(upstream_netif, &addr, 64, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configures all 6LoWPAN interfaces to request a 64-bit prefix
|
||||||
|
*/
|
||||||
|
static void _configure_dhcpv6_client(void)
|
||||||
|
{
|
||||||
|
gnrc_netif_t *netif = NULL;
|
||||||
|
while ((netif = gnrc_netif_iter(netif))) {
|
||||||
|
if (gnrc_netif_is_6lo(netif)) {
|
||||||
|
dhcpv6_client_req_ia_pd(netif->pid, 64U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The DHCPv6 client thread
|
||||||
|
*/
|
||||||
|
static void *_dhcpv6_cl_6lbr_thread(void *args)
|
||||||
|
{
|
||||||
|
event_queue_t event_queue;
|
||||||
|
gnrc_netif_t *upstream_netif = _find_upstream_netif();
|
||||||
|
|
||||||
|
(void)args;
|
||||||
|
if (upstream_netif == NULL) {
|
||||||
|
LOG_ERROR("DHCPv6: No upstream interface found!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_configure_upstream_netif(upstream_netif);
|
||||||
|
/* initialize client event queue */
|
||||||
|
event_queue_init(&event_queue);
|
||||||
|
/* initialize DHCPv6 client on border interface */
|
||||||
|
dhcpv6_client_init(&event_queue, upstream_netif->pid);
|
||||||
|
/* configure client to request prefix delegation for WPAN interfaces */
|
||||||
|
_configure_dhcpv6_client();
|
||||||
|
/* start DHCPv6 client */
|
||||||
|
dhcpv6_client_start();
|
||||||
|
/* start event loop of DHCPv6 client */
|
||||||
|
event_loop(&event_queue); /* never returns */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gnrc_dhcpv6_client_6lbr_init(void)
|
||||||
|
{
|
||||||
|
/* start DHCPv6 client thread to request prefix for WPAN */
|
||||||
|
thread_create(_stack, DHCPV6_CLIENT_STACK_SIZE,
|
||||||
|
DHCPV6_CLIENT_PRIORITY,
|
||||||
|
THREAD_CREATE_STACKTEST,
|
||||||
|
_dhcpv6_cl_6lbr_thread, NULL, "dhcpv6-client");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
10
tests/gnrc_dhcpv6_client_6lbr/Kconfig
Normal file
10
tests/gnrc_dhcpv6_client_6lbr/Kconfig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
if MODULE_ETHOS
|
||||||
|
config GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE
|
||||||
|
default y
|
||||||
|
depends on MODULE_GNRC_DHCPV6_CLIENT_6LBR && KCONFIG_MODULE_GNRC_DHCPV6
|
||||||
|
config GNRC_NETIF_IPV6_ADDRS_NUMOF
|
||||||
|
# CONFIG_GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE=1 requires one more address
|
||||||
|
# for `fe80::2`.
|
||||||
|
default 3
|
||||||
|
depends on KCONFIG_MODULE_GNRC_NETIF
|
||||||
|
endif # MODULE_ETHOS
|
||||||
52
tests/gnrc_dhcpv6_client_6lbr/Makefile
Normal file
52
tests/gnrc_dhcpv6_client_6lbr/Makefile
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
DEVELHELP := 1
|
||||||
|
include $(CURDIR)/../Makefile.tests_common
|
||||||
|
|
||||||
|
export TAP ?= tap0
|
||||||
|
GNRC_NETIF_NUMOF := 2
|
||||||
|
|
||||||
|
USEMODULE += auto_init_gnrc_netif
|
||||||
|
USEMODULE += gnrc_dhcpv6_client_6lbr
|
||||||
|
USEMODULE += gnrc_netdev_default
|
||||||
|
USEMODULE += gnrc_pktdump
|
||||||
|
USEMODULE += gnrc_sixlowpan_border_router_default
|
||||||
|
USEMODULE += ps
|
||||||
|
USEMODULE += shell
|
||||||
|
USEMODULE += shell_commands
|
||||||
|
|
||||||
|
# use Ethernet as link-layer protocol
|
||||||
|
ifeq (native,$(BOARD))
|
||||||
|
TERMFLAGS += -z [::1]:17754
|
||||||
|
else
|
||||||
|
ETHOS_BAUDRATE ?= 115200
|
||||||
|
CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE)
|
||||||
|
TERMDEPS += ethos
|
||||||
|
TERMPROG ?= sudo $(RIOTTOOLS)/ethos/ethos
|
||||||
|
TERMFLAGS ?= $(TAP) $(PORT) $(ETHOS_BAUDRATE)
|
||||||
|
STATIC_ROUTES ?= 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
# The test requires some setup and to be run as root
|
||||||
|
# So it cannot currently be run on CI
|
||||||
|
TEST_ON_CI_BLACKLIST += all
|
||||||
|
|
||||||
|
# As there is an 'app.config' we want to explicitly disable Kconfig by setting
|
||||||
|
# the variable to empty
|
||||||
|
SHOULD_RUN_KCONFIG ?=
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
||||||
|
|
||||||
|
ifndef CONFIG_GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE
|
||||||
|
ifeq (1,$(STATIC_ROUTES))
|
||||||
|
CFLAGS += -DCONFIG_GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE=1
|
||||||
|
# CONFIG_GNRC_DHCPV6_CLIENT_6LBR_STATIC_ROUTE=1 requires one more address for
|
||||||
|
# `fe80::2`.
|
||||||
|
CFLAGS += -DCONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF=3
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (,$(filter native,$(BOARD)))
|
||||||
|
.PHONY: ethos
|
||||||
|
|
||||||
|
ethos:
|
||||||
|
$(Q)env -u CC -u CFLAGS make -C $(RIOTBASE)/dist/tools/ethos
|
||||||
|
endif
|
||||||
6
tests/gnrc_dhcpv6_client_6lbr/Makefile.board.dep
Normal file
6
tests/gnrc_dhcpv6_client_6lbr/Makefile.board.dep
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Put board specific dependencies here
|
||||||
|
ifeq (native,$(BOARD))
|
||||||
|
USEMODULE += socket_zep
|
||||||
|
else
|
||||||
|
USEMODULE += stdio_ethos
|
||||||
|
endif
|
||||||
56
tests/gnrc_dhcpv6_client_6lbr/Makefile.ci
Normal file
56
tests/gnrc_dhcpv6_client_6lbr/Makefile.ci
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
BOARD_INSUFFICIENT_MEMORY := \
|
||||||
|
airfy-beacon \
|
||||||
|
arduino-duemilanove \
|
||||||
|
arduino-leonardo \
|
||||||
|
arduino-mega2560 \
|
||||||
|
arduino-nano \
|
||||||
|
arduino-uno \
|
||||||
|
atmega1284p \
|
||||||
|
atmega328p \
|
||||||
|
b-l072z-lrwan1 \
|
||||||
|
blackpill-128kib \
|
||||||
|
blackpill \
|
||||||
|
bluepill-128kib \
|
||||||
|
bluepill \
|
||||||
|
calliope-mini \
|
||||||
|
cc2650-launchpad \
|
||||||
|
cc2650stk \
|
||||||
|
derfmega128 \
|
||||||
|
hifive1 \
|
||||||
|
hifive1b \
|
||||||
|
i-nucleo-lrwan1 \
|
||||||
|
lsn50 \
|
||||||
|
maple-mini \
|
||||||
|
mega-xplained \
|
||||||
|
microbit \
|
||||||
|
microduino-corerf \
|
||||||
|
msb-430 \
|
||||||
|
msb-430h \
|
||||||
|
nrf51dongle \
|
||||||
|
nrf6310 \
|
||||||
|
nucleo-f030r8 \
|
||||||
|
nucleo-f031k6 \
|
||||||
|
nucleo-f042k6 \
|
||||||
|
nucleo-f070rb \
|
||||||
|
nucleo-f072rb \
|
||||||
|
nucleo-f103rb \
|
||||||
|
nucleo-f302r8 \
|
||||||
|
nucleo-f303k8 \
|
||||||
|
nucleo-f334r8 \
|
||||||
|
nucleo-l031k6 \
|
||||||
|
nucleo-l053r8 \
|
||||||
|
nucleo-l073rz \
|
||||||
|
opencm904 \
|
||||||
|
saml10-xpro \
|
||||||
|
saml11-xpro \
|
||||||
|
spark-core \
|
||||||
|
stm32f030f4-demo \
|
||||||
|
stm32f0discovery \
|
||||||
|
stm32l0538-disco \
|
||||||
|
telosb \
|
||||||
|
waspmote-pro \
|
||||||
|
wsn430-v1_3b \
|
||||||
|
wsn430-v1_4 \
|
||||||
|
yunjia-nrf51822 \
|
||||||
|
z1 \
|
||||||
|
#
|
||||||
30
tests/gnrc_dhcpv6_client_6lbr/README.md
Normal file
30
tests/gnrc_dhcpv6_client_6lbr/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# `gnrc_dhcpv6_client_6lbr` test
|
||||||
|
|
||||||
|
This test utilizes [scapy] to test the DHCPv6 client configuration for a 6LoWPAN
|
||||||
|
border router.
|
||||||
|
|
||||||
|
To test, compile and flash the application to any board of your liking (since
|
||||||
|
`ethos` is used to communicate with non-native boards it really doesn't matter
|
||||||
|
as long as the application fits).
|
||||||
|
|
||||||
|
```
|
||||||
|
make flash
|
||||||
|
```
|
||||||
|
|
||||||
|
And run the tests using
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that root privileges are required since `scapy` needs to construct Ethernet
|
||||||
|
frames to properly communicate over the TAP interface.
|
||||||
|
|
||||||
|
The tests succeeds if you see the string `SUCCESS`.
|
||||||
|
|
||||||
|
If any problems are encountered (i.e. if the test prints the sting `FAILED`),
|
||||||
|
set the echo parameter in the `run()` function at the bottom of the test script
|
||||||
|
(tests/01-run.py) to `True`. The test script will then offer a more detailed
|
||||||
|
output.
|
||||||
|
|
||||||
|
[scapy]: https://scapy.readthedocs.io/en/latest/
|
||||||
27
tests/gnrc_dhcpv6_client_6lbr/main.c
Normal file
27
tests/gnrc_dhcpv6_client_6lbr/main.c
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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 "shell.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||||
|
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||||
|
|
||||||
|
/* should be never reached */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
173
tests/gnrc_dhcpv6_client_6lbr/tests/01-run.py
Executable file
173
tests/gnrc_dhcpv6_client_6lbr/tests/01-run.py
Executable file
@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright (C) 2018 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 pexpect
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from scapy.all import AsyncSniffer, sendp, Ether, IPv6, UDP
|
||||||
|
from scapy.all import DHCP6_Solicit, DHCP6_Advertise, DHCP6_Request, DHCP6_Reply
|
||||||
|
from scapy.all import DHCP6OptClientId, DHCP6OptServerId, DHCP6OptIA_PD
|
||||||
|
from scapy.all import DUID_LL, DHCP6OptIAPrefix
|
||||||
|
from testrunner import run
|
||||||
|
|
||||||
|
|
||||||
|
TIMEOUT = 1
|
||||||
|
|
||||||
|
|
||||||
|
def get_upstream_netif(child):
|
||||||
|
child.sendline("ifconfig")
|
||||||
|
child.sendline("help") # workaround to spot end of ifconfig output
|
||||||
|
candidate = None
|
||||||
|
while True: # Search for an interface that does _not_ contain 6LO flag
|
||||||
|
if candidate is None:
|
||||||
|
child.expect(r"Iface\s+([^\s]+)\s+")
|
||||||
|
candidate = child.match.group(1)
|
||||||
|
res = child.expect([r"\b6LO\b", r"Iface\s+([^\s]+)\s+", "Command",
|
||||||
|
pexpect.TIMEOUT], timeout=.2)
|
||||||
|
if res > 0:
|
||||||
|
break
|
||||||
|
candidate = None
|
||||||
|
# wait for a line in "help"
|
||||||
|
child.expect("reboot")
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
|
||||||
|
def get_downstream_netif(child):
|
||||||
|
child.sendline("ifconfig")
|
||||||
|
child.sendline("help") # workaround to spot end of ifconfig output
|
||||||
|
candidate = None
|
||||||
|
while True: # Search for an interface that does _not_ contain 6LO flag
|
||||||
|
if candidate is None:
|
||||||
|
child.expect(r"Iface\s+([^\s]+)\s+")
|
||||||
|
candidate = child.match.group(1)
|
||||||
|
res = child.expect([r"\b6LO\b", r"Iface\s+([^\s]+)\s+", "Command",
|
||||||
|
pexpect.TIMEOUT], timeout=.2)
|
||||||
|
if res == 0:
|
||||||
|
break
|
||||||
|
elif res == 1:
|
||||||
|
candidate = child.match.group(1)
|
||||||
|
elif res == 2:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
candidate = None
|
||||||
|
# wait for a line in "help"
|
||||||
|
child.expect("reboot")
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
|
||||||
|
def get_hwaddrs(child, netif):
|
||||||
|
hwaddrs = []
|
||||||
|
child.sendline("ifconfig {}".format(netif))
|
||||||
|
child.expect(r"HWaddr:\s+(([A-Fa-f0-9]{2}:?)+)\s")
|
||||||
|
hwaddrs.append(child.match.group(1).lower())
|
||||||
|
if len(hwaddrs[0]) == 5: # short address
|
||||||
|
res = child.expect([pexpect.TIMEOUT,
|
||||||
|
r"Long HWaddr:\s+(([A-Fa-f0-9]{2}:?)+)\s"])
|
||||||
|
if res > 0:
|
||||||
|
hwaddrs.append(child.match.group(1).lower())
|
||||||
|
return hwaddrs
|
||||||
|
|
||||||
|
|
||||||
|
def start_sniffer(iface, count=None, stop_filter=None):
|
||||||
|
sniffer = AsyncSniffer(
|
||||||
|
iface=iface,
|
||||||
|
filter="udp and dst port 547",
|
||||||
|
count=count,
|
||||||
|
stop_filter=stop_filter,
|
||||||
|
)
|
||||||
|
sniffer.start()
|
||||||
|
return sniffer
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_dhcpv6_pkt(iface, sniffer=None, timeout=5):
|
||||||
|
if sniffer is None:
|
||||||
|
sniffer = start_sniffer(iface, count=1)
|
||||||
|
sniffer.join(timeout=timeout)
|
||||||
|
if sniffer.results is None:
|
||||||
|
raise TimeoutError("Sniffing for DHCPv6 traffic timed out")
|
||||||
|
return [p for p in sniffer.results
|
||||||
|
# filter out packets only belonging to stop_filter if it existed
|
||||||
|
if sniffer.kwargs.get("stop_filter") is None or
|
||||||
|
sniffer.kwargs["stop_filter"](p)][-1]
|
||||||
|
|
||||||
|
|
||||||
|
def build_reply_headers(pkt):
|
||||||
|
src_ether = pkt[Ether].src
|
||||||
|
src_ip = pkt[IPv6].src
|
||||||
|
sport = pkt[UDP].sport
|
||||||
|
dport = pkt[UDP].dport
|
||||||
|
return Ether(dst=src_ether) / IPv6(dst=src_ip) / \
|
||||||
|
UDP(sport=dport, dport=sport)
|
||||||
|
|
||||||
|
|
||||||
|
def testfunc(child):
|
||||||
|
iface = os.environ["TAP"]
|
||||||
|
|
||||||
|
pkt = wait_for_dhcpv6_pkt(iface)
|
||||||
|
# the packet was a solicit
|
||||||
|
assert DHCP6_Solicit in pkt
|
||||||
|
# check if the sender is the upstream interface of the node
|
||||||
|
upstream_netif = get_upstream_netif(child)
|
||||||
|
print(upstream_netif)
|
||||||
|
upstream_hwaddrs = get_hwaddrs(child, upstream_netif)
|
||||||
|
assert DHCP6OptClientId in pkt and DUID_LL in pkt[DHCP6OptClientId].duid
|
||||||
|
assert pkt[DHCP6OptClientId].duid[DUID_LL].lladdr in upstream_hwaddrs
|
||||||
|
# and it is asking for a prefix delegation
|
||||||
|
assert DHCP6OptIA_PD in pkt
|
||||||
|
|
||||||
|
# reply to solicit with advertise and a prefix provided
|
||||||
|
trid = pkt[DHCP6_Solicit].trid
|
||||||
|
srv_duid = "aa:bb:cc:dd:ee:ff"
|
||||||
|
cli_id = DHCP6OptClientId(duid=pkt[DHCP6OptClientId].duid)
|
||||||
|
srv_id = DHCP6OptServerId(duid=DUID_LL(lladdr=srv_duid))
|
||||||
|
prefix = "2001:db8:{:x}:{:x}::".format(
|
||||||
|
random.randint(0, 0xffff),
|
||||||
|
random.randint(0, 0xffff)
|
||||||
|
)
|
||||||
|
ia_pd = DHCP6OptIA_PD(T1=12000, T2=13000, iaid=pkt[DHCP6OptIA_PD].iaid,
|
||||||
|
iapdopt=[
|
||||||
|
DHCP6OptIAPrefix(preflft=14000, validlft=15000,
|
||||||
|
prefix=prefix, plen=64)])
|
||||||
|
# start sniffer to catch incoming request
|
||||||
|
sniffer = start_sniffer(iface,
|
||||||
|
stop_filter=lambda pkt: DHCP6_Request in pkt)
|
||||||
|
sendp(build_reply_headers(pkt) / DHCP6_Advertise(trid=trid) /
|
||||||
|
cli_id / srv_id / ia_pd, iface=iface, verbose=False)
|
||||||
|
|
||||||
|
# wait for request
|
||||||
|
pkt = wait_for_dhcpv6_pkt(iface, sniffer)
|
||||||
|
# the packet was indeed a request
|
||||||
|
assert DHCP6_Request in pkt
|
||||||
|
# and from the client
|
||||||
|
assert DHCP6OptClientId in pkt and DUID_LL in pkt[DHCP6OptClientId].duid
|
||||||
|
assert pkt[DHCP6OptClientId].duid[DUID_LL].lladdr in upstream_hwaddrs
|
||||||
|
# and it is trying to talk to this server
|
||||||
|
assert DHCP6OptServerId in pkt and DUID_LL in pkt[DHCP6OptServerId].duid
|
||||||
|
assert pkt[DHCP6OptServerId].duid[DUID_LL].lladdr == srv_duid
|
||||||
|
# and is still asking for a prefix delegation
|
||||||
|
assert DHCP6OptIA_PD in pkt
|
||||||
|
|
||||||
|
# reply to request with reply and a prefix provided
|
||||||
|
trid = pkt[DHCP6_Request].trid
|
||||||
|
sendp(build_reply_headers(pkt) / DHCP6_Reply(trid=trid) /
|
||||||
|
cli_id / srv_id / ia_pd, iface=iface, verbose=False)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# check if global address was configured
|
||||||
|
child.sendline("ifconfig {}".format(get_downstream_netif(child)))
|
||||||
|
# remove one trailing ':' from prefix just to be safe ;-)
|
||||||
|
child.expect(r"inet6 addr:\s+{}[0-9a-fA-F:]+\s"
|
||||||
|
.format(prefix[:-1]))
|
||||||
|
print("SUCCESS")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(run(testfunc, timeout=TIMEOUT, echo=True))
|
||||||
Loading…
x
Reference in New Issue
Block a user