mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #16750 from benpicco/gnrc_ipv6_auto_subnets
gnrc/ipv6_auto_subnets: relax topology requirements
This commit is contained in:
commit
a39c0e1010
66
doc/doxygen/src/gnrc_ipv6_auto_subnets-flow.puml
Normal file
66
doc/doxygen/src/gnrc_ipv6_auto_subnets-flow.puml
Normal 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
|
1
doc/doxygen/src/gnrc_ipv6_auto_subnets-flow.svg
Normal file
1
doc/doxygen/src/gnrc_ipv6_auto_subnets-flow.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 17 KiB |
80
doc/doxygen/src/gnrc_ipv6_auto_subnets.puml
Normal file
80
doc/doxygen/src/gnrc_ipv6_auto_subnets.puml
Normal 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
|
1
doc/doxygen/src/gnrc_ipv6_auto_subnets.svg
Normal file
1
doc/doxygen/src/gnrc_ipv6_auto_subnets.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.9 KiB |
@ -1,5 +1,27 @@
|
||||
' to generate SVG run plantuml -tsvg gnrc_ipv6_auto_subnets_simple.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 {
|
||||
|
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 |
@ -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
|
||||
tree topology.
|
||||
This example demonstrates IPv6 subnet auto-configuration for networks on
|
||||
a tree topology.
|
||||
|
||||
This allows to connect multiple links with individual subnets and route
|
||||
between them.
|
||||
Each link can have an arbitrary number of hosts, but there can be only
|
||||
a single router on each link.
|
||||
Each link can have an arbitrary number of hosts and routers.
|
||||
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)
|
||||
|
||||
Routers can still have multiple downstream interfaces but there can be
|
||||
only a single router in each subnet.
|
||||
|
||||
## Setup on native
|
||||
|
||||
To simulate such a network on `native` a `setup_taps.sh` script is provided that
|
||||
|
@ -40,6 +40,8 @@ PSEUDOMODULES += fmt_%
|
||||
PSEUDOMODULES += gcoap_dtls
|
||||
PSEUDOMODULES += fido2_tests
|
||||
PSEUDOMODULES += gnrc_dhcpv6_%
|
||||
PSEUDOMODULES += gnrc_ipv6_auto_subnets_auto_init
|
||||
PSEUDOMODULES += gnrc_ipv6_auto_subnets_simple
|
||||
PSEUDOMODULES += gnrc_ipv6_default
|
||||
PSEUDOMODULES += gnrc_ipv6_ext_frag_stats
|
||||
PSEUDOMODULES += gnrc_ipv6_router
|
||||
|
@ -279,6 +279,11 @@ void auto_init(void)
|
||||
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)) {
|
||||
LOG_DEBUG("auto_init MULTIMEDIA\n");
|
||||
if (IS_USED(MODULE_DFPLAYER)) {
|
||||
|
@ -116,10 +116,19 @@ ifneq (,$(filter gnrc_rpl,$(USEMODULE)))
|
||||
USEMODULE += evtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_auto_subnets_simple,$(USEMODULE)))
|
||||
USEMODULE += gnrc_ipv6_auto_subnets
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_auto_subnets,$(USEMODULE)))
|
||||
USEMODULE += gnrc_ipv6_nib_rtr_adv_pio_cb
|
||||
CFLAGS += -DCONFIG_GNRC_IPV6_NIB_ADV_ROUTER=0
|
||||
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
|
||||
|
||||
ifneq (,$(filter gnrc_netif,$(USEMODULE)))
|
||||
|
@ -14,17 +14,29 @@
|
||||
* 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.
|
||||
*
|
||||
* If a sufficiently large IPv6 prefix (> /64) is provided via Router Advertisements,
|
||||
* a routing node with this module will automatically configure subnets from it
|
||||
* 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
|
||||
* arbitrary number of leaf nodes.
|
||||
* When using the `gnrc_ipv6_auto_subnets_simple` module, there can only be a single
|
||||
* 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
|
||||
* and the process repeats until the bits of the prefix are exhausted.
|
||||
@ -41,8 +53,8 @@
|
||||
* Usage
|
||||
* =====
|
||||
*
|
||||
* Simply add the `gnrc_ipv6_auto_subnets` module to the code of the nodes that
|
||||
* should act as routers in the cascading network.
|
||||
* Simply add the `gnrc_ipv6_auto_subnets` or `gnrc_ipv6_auto_subnets_simple` module
|
||||
* to the nodes that should act as routers in the cascading network.
|
||||
* The upstream network will be automatically chosen as the one that first
|
||||
* receives a router advertisement.
|
||||
*
|
||||
@ -52,14 +64,129 @@
|
||||
* @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/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
|
||||
#include "debug.h"
|
||||
|
||||
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,
|
||||
const ipv6_addr_t *prefix, uint8_t bits,
|
||||
uint8_t idx, uint8_t idx_bits)
|
||||
@ -130,7 +257,7 @@ static bool _remove_old_prefix(gnrc_netif_t *netif,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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.
|
||||
* 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 */
|
||||
_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",
|
||||
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;
|
||||
}
|
||||
|
||||
_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) */
|
||||
/** @} */
|
||||
|
Loading…
Reference in New Issue
Block a user