Merge pull request #16750 from benpicco/gnrc_ipv6_auto_subnets

gnrc/ipv6_auto_subnets: relax topology requirements
This commit is contained in:
benpicco 2021-09-28 19:07:21 +02:00 committed by GitHub
commit a39c0e1010
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 559 additions and 16 deletions

View File

@ -0,0 +1,66 @@
' to generate SVG run plantuml -tsvg gnrc_ipv6_auto_subnets-flow.puml
@startuml
<style>
participant {
FontColor #white
BackGroundColor #275a4b
LineColor #3fa687
LineThickness 2.0
}
actor {
FontColor #white
BackGroundColor #275a4b
LineColor #3fa687
LineThickness 2.0
}
note {
FontColor white
BackGroundColor #30735f
LineColor #388d73
}
arrow {
LineColor #3fa687
LineThickness 2.0
}
</style>
skinparam sequence {
LifeLineBorderColor #275a4b
LifeLineBorderThickness 2
}
participant "**A**\n2e:a3:9e:a9:68:<i>23</i>" as A
participant "**B**\n2e:a3:9e:a9:68:<i>42</i>" as B
participant "**C**\n2e:a3:9e:a9:68:<i>f6</i>" as C
note across: <i>Address of **A** < Address of **B** < Address of **C**</i>
note over A: index: 0\nlocal subnets: 2
/ note over C: index: 0\nlocal subnets: 1
/ note over B: index: 0\nlocal subnets: 1
A -> C: I want to create **2** local subnets
A -> B: I want to create **2** local subnets
note over C: index: **2**\ntotal subnets: **3**
/ note over B: index: **2**\ntotal subnets: **3**
C -> A: I want to create **1** local subnet
C -> B: I want to create **1** local subnet
note over A: index: 0\ntotal subnets: **3**
/ note over B: index: 2\ntotal subnets: **4**
B -> C: I want to create **1** local subnet
B -> A: I want to create **1** local subnet
note over A: index: 0 local: 2\ntotal subnets: **4**
/ note over C: index: **3** local: 1\ntotal subnets: **4**
/ note over B: index: 2 local: 1\ntotal subnets: 4
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,80 @@
' to generate SVG run plantuml -tsvg gnrc_ipv6_auto_subnets.puml
@startuml
<style>
nwdiagDiagram {
network {
BackGroundColor #275a4b
LineColor #3fa687
LineThickness 2.0
}
server {
BackGroundColor #275a4b
LineColor #3fa687
FontColor #white
LineThickness 2.0
}
arrow {
LineColor #3fa687
LineThickness 2.0
}
}
</style>
nwdiag {
network level1 {
address = "2001:db8::/60";
router_a [address = "2001:db8::<color:#8a8a8a>c8f4:13ff:fece:3f43", description = "1st level router #1"];
router_b [address = "2001:db8::<color:#8a8a8a>804b:fcff:feb6:43fb", description = "1st level router #2"];
}
network level2_1 {
address = "2001:db8:0:4::/62";
description = "level 2.1"
router_b [address = "2001:db8:0:4:<color:#8a8a8a>2ca3:9eff:fea9:68f7"];
router_e [address = "2001:db8:0:4:<color:#8a8a8a>5075:35ff:fefa:30bb", description = "2nd level router #3"];
router_f [address = "2001:db8:0:4:<color:#8a8a8a>14c4:7bff:fe63:c449", description = "2nd level router #4"];
}
network level2_2 {
address = "2001:db8:0:8::/62";
description = "level 2.2"
router_a [address = "2001:db8:0:8:<color:#8a8a8a>3c27:6dff:fe25:e95d"];
router_c [address = "2001:db8:0:8:<color:#8a8a8a>c8f4:13ff:fece:3f43", description = "2nd level router #1"];
router_d [address = "2001:db8:0:8:<color:#8a8a8a>a440:e4ff:fe55:a059", description = "2nd level router #2"];
}
network level3_1 {
address = "2001:db8:0:9::/64";
description = "level 3.1"
router_c [address = "2001:db8:0:9:<color:#8a8a8a>48f7:1cf:74cc:3f13"];
}
network level3_2 {
address = "2001:db8:0:a::/64";
description = "level 3.2"
router_d [address = "2001:db8:0:a:<color:#8a8a8a>a8d9:e1ff:feab:d543"];
}
network level3_3 {
address = "2001:db8:0:5::/64";
description = "level 3.3"
router_e [address = "2001:db8:0:5:<color:#8a8a8a>1848:79ff:fe20:cf59"];
}
network level3_4 {
address = "2001:db8:0:6::/64";
description = "level 3.4"
router_f [address = "2001:db8:0:6:<color:#8a8a8a>8cbf:adff:fef0:4092"];
}
}
@enduml

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -1,5 +1,27 @@
' to generate SVG run plantuml -tsvg gnrc_ipv6_auto_subnets_simple.puml ' to generate SVG run plantuml -tsvg gnrc_ipv6_auto_subnets_simple.puml
@startuml @startuml
<style>
nwdiagDiagram {
network {
BackGroundColor #275a4b
LineColor #3fa687
LineThickness 2.0
}
server {
BackGroundColor #275a4b
LineColor #3fa687
FontColor #white
LineThickness 2.0
}
arrow {
LineColor #3fa687
LineThickness 2.0
}
}
</style>
nwdiag { nwdiag {
network level1 { network level1 {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -1,16 +1,24 @@
# Auto-configuration for nested subnets on a (simple) tree topology # Auto-configuration for nested subnets on a tree topology
This example demonstrates IPv6 subnet auto-configuration for networks on a This example demonstrates IPv6 subnet auto-configuration for networks on
tree topology. a tree topology.
This allows to connect multiple links with individual subnets and route This allows to connect multiple links with individual subnets and route
between them. between them.
Each link can have an arbitrary number of hosts, but there can be only Each link can have an arbitrary number of hosts and routers.
a single router on each link.
Routers can have multiple interfaces to connect different downlinks. Routers can have multiple interfaces to connect different downlinks.
![](../../doc/doxygen/src/gnrc_ipv6_auto_subnets.svg)
If you can ensure there is only a single router on each link, you can
skip the coordination protocol and save some resources by enabling
the `gnrc_ipv6_auto_subnets_simple` module.
![](../../doc/doxygen/src/gnrc_ipv6_auto_subnets_simple.svg) ![](../../doc/doxygen/src/gnrc_ipv6_auto_subnets_simple.svg)
Routers can still have multiple downstream interfaces but there can be
only a single router in each subnet.
## Setup on native ## Setup on native
To simulate such a network on `native` a `setup_taps.sh` script is provided that To simulate such a network on `native` a `setup_taps.sh` script is provided that

View File

@ -40,6 +40,8 @@ PSEUDOMODULES += fmt_%
PSEUDOMODULES += gcoap_dtls PSEUDOMODULES += gcoap_dtls
PSEUDOMODULES += fido2_tests PSEUDOMODULES += fido2_tests
PSEUDOMODULES += gnrc_dhcpv6_% PSEUDOMODULES += gnrc_dhcpv6_%
PSEUDOMODULES += gnrc_ipv6_auto_subnets_auto_init
PSEUDOMODULES += gnrc_ipv6_auto_subnets_simple
PSEUDOMODULES += gnrc_ipv6_default PSEUDOMODULES += gnrc_ipv6_default
PSEUDOMODULES += gnrc_ipv6_ext_frag_stats PSEUDOMODULES += gnrc_ipv6_ext_frag_stats
PSEUDOMODULES += gnrc_ipv6_router PSEUDOMODULES += gnrc_ipv6_router

View File

@ -279,6 +279,11 @@ void auto_init(void)
gnrc_dhcpv6_client_simple_pd_init(); gnrc_dhcpv6_client_simple_pd_init();
} }
if (IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_AUTO_INIT)) {
extern void gnrc_ipv6_auto_subnets_init(void);
gnrc_ipv6_auto_subnets_init();
}
if (IS_USED(MODULE_AUTO_INIT_MULTIMEDIA)) { if (IS_USED(MODULE_AUTO_INIT_MULTIMEDIA)) {
LOG_DEBUG("auto_init MULTIMEDIA\n"); LOG_DEBUG("auto_init MULTIMEDIA\n");
if (IS_USED(MODULE_DFPLAYER)) { if (IS_USED(MODULE_DFPLAYER)) {

View File

@ -116,10 +116,19 @@ ifneq (,$(filter gnrc_rpl,$(USEMODULE)))
USEMODULE += evtimer USEMODULE += evtimer
endif endif
ifneq (,$(filter gnrc_ipv6_auto_subnets_simple,$(USEMODULE)))
USEMODULE += gnrc_ipv6_auto_subnets
endif
ifneq (,$(filter gnrc_ipv6_auto_subnets,$(USEMODULE))) ifneq (,$(filter gnrc_ipv6_auto_subnets,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_rtr_adv_pio_cb USEMODULE += gnrc_ipv6_nib_rtr_adv_pio_cb
CFLAGS += -DCONFIG_GNRC_IPV6_NIB_ADV_ROUTER=0 CFLAGS += -DCONFIG_GNRC_IPV6_NIB_ADV_ROUTER=0
CFLAGS += -DCONFIG_GNRC_IPV6_NIB_ADD_RIO_IN_LAST_RA=1 CFLAGS += -DCONFIG_GNRC_IPV6_NIB_ADD_RIO_IN_LAST_RA=1
ifeq (,$(filter gnrc_ipv6_auto_subnets_simple,$(USEMODULE)))
DEFAULT_MODULE += gnrc_ipv6_auto_subnets_auto_init
USEMODULE += gnrc_udp
endif
endif endif
ifneq (,$(filter gnrc_netif,$(USEMODULE))) ifneq (,$(filter gnrc_netif,$(USEMODULE)))

View File

@ -14,17 +14,29 @@
* About * About
* ===== * =====
* *
* This module provides an automatic configuration for networks with a simple * This module provides an automatic configuration for networks with a (simple)
* tree topology. * tree topology.
* *
* If a sufficiently large IPv6 prefix (> /64) is provided via Router Advertisements, * If a sufficiently large IPv6 prefix (> /64) is provided via Router Advertisements,
* a routing node with this module will automatically configure subnets from it * a routing node with this module will automatically configure subnets from it
* by dividing it into sub-prefixes for each downstream interface. * by dividing it into sub-prefixes for each downstream interface.
* *
* There can only be a single routing node on each level of the network but an * When using the `gnrc_ipv6_auto_subnets_simple` module, there can only be a single
* arbitrary number of leaf nodes. * routing node on each level of the network but an arbitrary number of leaf nodes.
* *
* ![Example Topology](gnrc_ipv6_auto_subnets_simple.svg) * !['Skinny Tree' Example Topology](gnrc_ipv6_auto_subnets_simple.svg)
*
* If there are multiple routing nodes on the same link, coordination between the
* routers is required.
* For this the `gnrc_ipv6_auto_subnets` implements a simple UDP based synchronisation
* protocol where each router announces the number of subnets they want to create.
*
* ![Synchronisation Algorithm](gnrc_ipv6_auto_subnets-flow.svg)
*
* The layer 2 address of the sender then determines the order in which the prefixes
* are assigned.
*
* ![Example Topology](gnrc_ipv6_auto_subnets.svg)
* *
* The downstream network(s) receive the sub-prefix via Router Advertisements * The downstream network(s) receive the sub-prefix via Router Advertisements
* and the process repeats until the bits of the prefix are exhausted. * and the process repeats until the bits of the prefix are exhausted.
@ -41,8 +53,8 @@
* Usage * Usage
* ===== * =====
* *
* Simply add the `gnrc_ipv6_auto_subnets` module to the code of the nodes that * Simply add the `gnrc_ipv6_auto_subnets` or `gnrc_ipv6_auto_subnets_simple` module
* should act as routers in the cascading network. * to the nodes that should act as routers in the cascading network.
* The upstream network will be automatically chosen as the one that first * The upstream network will be automatically chosen as the one that first
* receives a router advertisement. * receives a router advertisement.
* *
@ -52,14 +64,129 @@
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com> * @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*/ */
#include "net/gnrc/ipv6.h"
#include "net/gnrc/netif.h"
#include "net/gnrc/netif/hdr.h"
#include "net/gnrc/udp.h"
#include "net/gnrc/ipv6/nib.h" #include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/ndp.h" #include "net/gnrc/ndp.h"
#include "random.h"
#include "xtimer.h"
/**
* @brief Port for the custom UDP sync protocol
*/
#ifndef CONFIG_GNRC_IPV6_AUTO_SUBNETS_PORT
#define CONFIG_GNRC_IPV6_AUTO_SUBNETS_PORT (16179)
#endif
/**
* @brief Max number of other routers on the same link
*/
#ifndef CONFIG_GNRC_IPV6_AUTO_SUBNETS_PEERS_MAX
#define CONFIG_GNRC_IPV6_AUTO_SUBNETS_PEERS_MAX (4)
#endif
/**
* @brief How often the number subnets should be announced by the routers
*/
#ifndef CONFIG_GNRC_IPV6_AUTO_SUBNETS_TX_PER_PERIOD
#define CONFIG_GNRC_IPV6_AUTO_SUBNETS_TX_PER_PERIOD (3)
#endif
/**
* @brief How long to wait for other routers annoucements before resending
* or creating subnets when the retry counter is exhausted
*/
#ifndef CONFIG_GNRC_IPV6_AUTO_SUBNETS_TIMEOUT_MS
#define CONFIG_GNRC_IPV6_AUTO_SUBNETS_TIMEOUT_MS (50)
#endif
#define SERVER_THREAD_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define SERVER_MSG_QUEUE_SIZE (CONFIG_GNRC_IPV6_AUTO_SUBNETS_PEERS_MAX)
#define SERVER_MSG_TYPE_TIMEOUT (0x8fae)
#define ENABLE_DEBUG 0 #define ENABLE_DEBUG 0
#include "debug.h" #include "debug.h"
static char addr_str[IPV6_ADDR_MAX_STR_LEN]; static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#if !IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_SIMPLE)
/**
* @brief Custom UDP sync protocol
*/
typedef struct __attribute__((packed)) {
uint8_t version; /**< version number, must be 0 */
uint8_t num_subnets; /**< number of subnets a host wants to create */
} _auto_subnets_request_v0_t;
/* keep a copy of PIO information in memory */
static gnrc_netif_t *_upstream;
static ndp_opt_pi_t _pio_cache;
static char auto_subnets_stack[SERVER_THREAD_STACKSIZE];
static msg_t server_queue[SERVER_MSG_QUEUE_SIZE];
/* store neighbor routers l2 address to ignore duplicate packets */
static uint8_t l2addrs[CONFIG_GNRC_IPV6_AUTO_SUBNETS_PEERS_MAX]
[CONFIG_GNRC_IPV6_NIB_L2ADDR_MAX_LEN];
/* PID of the event thread */
static kernel_pid_t _server_pid;
static int _send_udp(gnrc_netif_t *netif, const ipv6_addr_t *addr,
uint16_t port, const void *data, size_t len)
{
gnrc_pktsnip_t *payload, *udp, *ip;
/* allocate payload */
payload = gnrc_pktbuf_add(NULL, data, len, GNRC_NETTYPE_UNDEF);
if (payload == NULL) {
DEBUG("auto_subnets: unable to copy data to packet buffer\n");
return -ENOBUFS;
}
/* allocate UDP header, set source port := destination port */
udp = gnrc_udp_hdr_build(payload, port, port);
if (udp == NULL) {
DEBUG("auto_subnets: unable to allocate UDP header\n");
gnrc_pktbuf_release(payload);
return -ENOBUFS;
}
/* allocate IPv6 header */
ip = gnrc_ipv6_hdr_build(udp, NULL, addr);
if (ip == NULL) {
DEBUG("auto_subnets: unable to allocate IPv6 header\n");
gnrc_pktbuf_release(udp);
return -ENOBUFS;
}
/* add netif header, if interface was given */
if (netif != NULL) {
gnrc_pktsnip_t *netif_hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
if (netif_hdr == NULL) {
DEBUG("auto_subnets: unable to allocate netif header\n");
gnrc_pktbuf_release(ip);
return -ENOBUFS;
}
gnrc_netif_hdr_set_netif(netif_hdr->data, netif);
ip = gnrc_pkt_prepend(ip, netif_hdr);
}
/* send packet */
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP,
GNRC_NETREG_DEMUX_CTX_ALL, ip)) {
DEBUG("auto_subnets: unable to locate UDP thread\n");
gnrc_pktbuf_release(ip);
return -ENETUNREACH;
}
return 0;
}
#endif /* !IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_SIMPLE) */
static void _init_sub_prefix(ipv6_addr_t *out, static void _init_sub_prefix(ipv6_addr_t *out,
const ipv6_addr_t *prefix, uint8_t bits, const ipv6_addr_t *prefix, uint8_t bits,
uint8_t idx, uint8_t idx_bits) uint8_t idx, uint8_t idx_bits)
@ -130,7 +257,7 @@ static bool _remove_old_prefix(gnrc_netif_t *netif,
return false; return false;
} }
static void _configure_subnets(uint8_t subnets, gnrc_netif_t *upstream, static void _configure_subnets(uint8_t subnets, uint8_t start_idx, gnrc_netif_t *upstream,
const ndp_opt_pi_t *pio) const ndp_opt_pi_t *pio)
{ {
gnrc_netif_t *downstream = NULL; gnrc_netif_t *downstream = NULL;
@ -141,7 +268,7 @@ static void _configure_subnets(uint8_t subnets, gnrc_netif_t *upstream,
const uint8_t prefix_len = pio->prefix_len; const uint8_t prefix_len = pio->prefix_len;
uint8_t new_prefix_len, subnet_len; uint8_t new_prefix_len, subnet_len;
DEBUG("auto_subnets: create %u subnets\n", subnets); DEBUG("auto_subnets: create %u subnets, start with %u\n", subnets, start_idx);
/* Calculate remaining prefix length. /* Calculate remaining prefix length.
* For n subnets we consume floor(log_2 n) + 1 bits. * For n subnets we consume floor(log_2 n) + 1 bits.
@ -165,7 +292,7 @@ static void _configure_subnets(uint8_t subnets, gnrc_netif_t *upstream,
} }
/* create subnet from upstream prefix */ /* create subnet from upstream prefix */
_init_sub_prefix(&new_prefix, prefix, prefix_len, subnets--, subnet_len); _init_sub_prefix(&new_prefix, prefix, prefix_len, ++start_idx, subnet_len);
DEBUG("auto_subnets: configure prefix %s/%u on %u\n", DEBUG("auto_subnets: configure prefix %s/%u on %u\n",
ipv6_addr_to_str(addr_str, &new_prefix, sizeof(addr_str)), ipv6_addr_to_str(addr_str, &new_prefix, sizeof(addr_str)),
@ -213,6 +340,228 @@ void gnrc_ipv6_nib_rtr_adv_pio_cb(gnrc_netif_t *upstream, const ndp_opt_pi_t *pi
return; return;
} }
_configure_subnets(subnets, upstream, pio); #if IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_SIMPLE)
/* if we are the only router on this bus, we can directly choose a prefix */
_configure_subnets(subnets, 0, upstream, pio);
#else
/* store PIO information for later use */
_pio_cache = *pio;
_upstream = upstream;
/* start advertising by sending timeout message to the server thread */
msg_t m = {
.type = SERVER_MSG_TYPE_TIMEOUT
};
msg_send(&m, _server_pid);
#endif /* !IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_SIMPLE) */
} }
#if !IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_SIMPLE)
/**
* @brief Check if memory region is set to 0
*
* @param[in] The memory array to check
* @param[in] The size of the memory array
*
* @return true if all bytes are set to 0
*/
static bool _all_zero(const uint8_t *addr, size_t len)
{
for (const uint8_t *end = addr + len; addr != end; ++addr) {
if (*addr) {
return false;
}
}
return true;
}
/**
* @brief Allocates a l2 address in the `l2addrs` array
*
* @param[in] addr The l2 address to insert
* @param[in] len l2 address length
*
* @return 1 if the address was added to the `l2addrs` array
* 0 if the address was already in the array
* -1 if there was no more space in the `l2addrs` array
*/
static int _alloc_l2addr_entry(const void *addr, size_t len)
{
int empty = -1;
for (unsigned i = 0; i < CONFIG_GNRC_IPV6_AUTO_SUBNETS_PEERS_MAX; ++i) {
if (_all_zero(l2addrs[i], len)) {
empty = i;
continue;
}
if (memcmp(addr, l2addrs[i], len) == 0) {
return 0;
}
}
if (empty < 0) {
return -1;
}
memcpy(l2addrs[empty], addr, len);
return 1;
}
/**
* @brief Compare the l2 address of the received packet with the l2 address of the
* interface it was received on.
*
* Only the first packet from a host generates a comparison, all subsequent
* packets will be ignored until the `l2addrs` array is reset.
*
* @param[in] upstream interface, ignore if the source does not match
* @param[in] pkt a received packet
*
* @return 1 if the sender l2 address is in order before the local l2 address
* @return 0 if the order could not be determined or a packet from the sender
* was already processed
* @return -1 if the sender l2 address is in order behind the local l2 address
*/
static int _get_my_l2addr_rank(gnrc_netif_t *iface, gnrc_pktsnip_t *pkt)
{
const void *src_addr;
gnrc_pktsnip_t *netif_hdr;
gnrc_netif_hdr_t *hdr;
if (iface == NULL) {
return 0;
}
netif_hdr = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF);
if (netif_hdr == NULL) {
return 0;
}
/* ignore packet if it was received on the wrong interface */
hdr = netif_hdr->data;
if (iface->pid != hdr->if_pid) {
return 0;
}
/* ignore packets without source address */
src_addr = gnrc_netif_hdr_get_src_addr(hdr);
if (src_addr == NULL) {
return 0;
}
/* check if we have seen the host before */
if (_alloc_l2addr_entry(src_addr, iface->l2addr_len) == 0) {
return 0;
}
return memcmp(iface->l2addr, src_addr, iface->l2addr_len);
}
static void _receive_announce(gnrc_pktsnip_t *pkt, uint8_t *subnets, uint8_t *idx_start)
{
_auto_subnets_request_v0_t *request = pkt->data;
/* Check if we already got an announcement from that host, */
/* in this case, res will be 0. */
int res = _get_my_l2addr_rank(_upstream, pkt);
if (res) {
/* calculate total number of subnets */
*subnets += request->num_subnets;
DEBUG("auto_subnets: %u new remote subnets, total %u\n",
request->num_subnets, *subnets);
/* If other host is before us in order of MAC addresses, add
* their subnets to our offset */
if (res > 0) {
*idx_start += request->num_subnets;
}
}
gnrc_pktbuf_release(pkt);
}
static void _send_announce(uint8_t local_subnets, xtimer_t *timer, msg_t *msg)
{
uint32_t timeout_us;
_auto_subnets_request_v0_t request = {
.num_subnets = local_subnets,
};
/* broadcast the number of subnets we want to create */
_send_udp(_upstream, &ipv6_addr_all_routers_link_local,
CONFIG_GNRC_IPV6_AUTO_SUBNETS_PORT,
&request, sizeof(request));
/* configure timeout for resend */
timeout_us = random_uint32_range(
CONFIG_GNRC_IPV6_AUTO_SUBNETS_TIMEOUT_MS * US_PER_MS / 2,
CONFIG_GNRC_IPV6_AUTO_SUBNETS_TIMEOUT_MS * US_PER_MS);
xtimer_set_msg(timer, timeout_us, msg, _server_pid);
DEBUG("auto_subnets: announce sent, next timeout in %" PRIu32 " µs\n", timeout_us);
}
static void *_eventloop(void *arg)
{
(void)arg;
xtimer_t timeout_timer;
msg_t msg, timeout_msg = { .type = SERVER_MSG_TYPE_TIMEOUT };
gnrc_netreg_entry_t server = GNRC_NETREG_ENTRY_INIT_PID(0, KERNEL_PID_UNDEF);
const uint8_t local_subnets = gnrc_netif_numof() - 1;
uint8_t idx_start = 0;
uint8_t subnets = local_subnets;
uint8_t tx_period = CONFIG_GNRC_IPV6_AUTO_SUBNETS_TX_PER_PERIOD;
DEBUG("auto_subnets: %u local subnets\n", subnets);
if (subnets == 0) {
return NULL;
}
/* setup the message queue */
msg_init_queue(server_queue, SERVER_MSG_QUEUE_SIZE);
/* register server to receive messages from given port */
gnrc_netreg_entry_init_pid(&server, CONFIG_GNRC_IPV6_AUTO_SUBNETS_PORT, thread_getpid());
gnrc_netreg_register(GNRC_NETTYPE_UDP, &server);
while (1) {
msg_receive(&msg);
switch (msg.type) {
case GNRC_NETAPI_MSG_TYPE_RCV:
_receive_announce(msg.content.ptr, &subnets, &idx_start);
break;
case SERVER_MSG_TYPE_TIMEOUT:
if (tx_period--) {
/* send subnet announcement */
_send_announce(local_subnets, &timeout_timer, &timeout_msg);
} else {
/* config round done, configure subnets */
_configure_subnets(subnets, idx_start, _upstream, &_pio_cache);
/* start a new round of counting */
tx_period = CONFIG_GNRC_IPV6_AUTO_SUBNETS_TX_PER_PERIOD;
memset(l2addrs, 0, sizeof(l2addrs));
idx_start = 0;
subnets = local_subnets;
}
break;
}
}
/* never reached */
return NULL;
}
void gnrc_ipv6_auto_subnets_init(void)
{
/* initiate auto_subnets thread */
_server_pid = thread_create(auto_subnets_stack, sizeof(auto_subnets_stack),
THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST,
_eventloop, NULL, "auto_subnets");
}
#endif /* !IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_SIMPLE) */
/** @} */ /** @} */