|
83 | 83 | * The upstream network will be automatically chosen as the one that first |
84 | 84 | * receives a router advertisement. |
85 | 85 | * |
| 86 | + * If only a single level of downstream routers exists and a sufficiently small |
| 87 | + * upstream prefix is provided, we can skip the synchronisation and instead derive |
| 88 | + * the *prefix* from the EUI of the downstream interface. |
| 89 | + * |
| 90 | + * e.g. given a prefix `fd12::/16` a router with a downstream interface with the |
| 91 | + * layer 2 address `12:84:0C:87:1F:B7` would create the prefix `fd12:1284:c87:1fb7::/64` |
| 92 | + * for the downstream network. |
| 93 | + * |
| 94 | + * To enable this behavior, chose the `gnrc_ipv6_auto_subnets_eui` module. |
| 95 | + * |
86 | 96 | * @{ |
87 | 97 | * |
88 | 98 | * @file |
89 | 99 | * @author Benjamin Valentin <benjamin.valentin@ml-pa.com> |
90 | 100 | */ |
91 | 101 |
|
92 | 102 | #include "compiler_hints.h" |
| 103 | +#include "macros/utils.h" |
93 | 104 | #include "net/gnrc/ipv6.h" |
94 | 105 | #include "net/gnrc/netif.h" |
95 | 106 | #include "net/gnrc/netif/hdr.h" |
@@ -305,6 +316,39 @@ static void _init_sub_prefix(ipv6_addr_t *out, |
305 | 316 | out->u8[bytes] |= idx << shift; |
306 | 317 | } |
307 | 318 |
|
| 319 | +static uint8_t _init_sub_prefix_eui(ipv6_addr_t *out, |
| 320 | + const ipv6_addr_t *prefix, uint8_t bits, |
| 321 | + const uint8_t *eui, uint8_t eui_len) |
| 322 | +{ |
| 323 | + assert(eui_len <= sizeof(uint64_t)); |
| 324 | + |
| 325 | + /* If the EUI is too large, discard most significant bits as |
| 326 | + those are typically manufacturer ID */ |
| 327 | + uint64_t mask = UINT64_MAX >> bits; |
| 328 | + |
| 329 | + union { |
| 330 | + uint64_t u64; |
| 331 | + uint8_t u8[8]; |
| 332 | + } eui64 = {}; |
| 333 | + uint64_t pfx = byteorder_ntohll(prefix->u64[0]); |
| 334 | + |
| 335 | + /* If EUI is small, we want to preserve leftover unused bits at the end */ |
| 336 | + uint8_t bits_total = bits + 8 * eui_len; |
| 337 | + uint8_t shift = bits_total < 64 |
| 338 | + ? 64 - bits_total |
| 339 | + : 0; |
| 340 | + |
| 341 | + /* treat EUI as a EUI-64 with unused bytes set to 0 */ |
| 342 | + memcpy(&eui64.u8[sizeof(uint64_t) - eui_len], eui, eui_len); |
| 343 | + eui64.u64 = ntohll(eui64.u64) & mask; |
| 344 | + |
| 345 | + /* create downstream prefix from upstream prefix + masked EUI64 */ |
| 346 | + out->u64[0] = byteorder_htonll(pfx | (eui64.u64 << shift)); |
| 347 | + |
| 348 | + /* we don't create prefixes that longer than 64 bits */ |
| 349 | + return MIN(64, bits_total); |
| 350 | +} |
| 351 | + |
308 | 352 | /* returns true if a new prefix was added, false if nothing changed */ |
309 | 353 | static bool _remove_old_prefix(gnrc_netif_t *netif, |
310 | 354 | const ipv6_addr_t *pfx, uint8_t pfx_len, |
@@ -393,7 +437,19 @@ static void _configure_subnets(uint8_t subnets, uint8_t start_idx, gnrc_netif_t |
393 | 437 | } |
394 | 438 |
|
395 | 439 | /* create subnet from upstream prefix */ |
396 | | - _init_sub_prefix(&new_prefix, prefix, prefix_len, ++start_idx, subnet_len); |
| 440 | + if (IS_USED(MODULE_GNRC_IPV6_AUTO_SUBNETS_EUI)) { |
| 441 | + uint8_t hwaddr[GNRC_NETIF_L2ADDR_MAXLEN]; |
| 442 | + int hwaddr_len = netif_get_opt(&downstream->netif, NETOPT_ADDRESS, 0, |
| 443 | + hwaddr, sizeof(hwaddr)); |
| 444 | + if (hwaddr_len <= 0) { |
| 445 | + DEBUG("auto_subnets: can't get l2 address from netif %u\n", downstream->pid); |
| 446 | + continue; |
| 447 | + } |
| 448 | + new_prefix_len = _init_sub_prefix_eui(&new_prefix, prefix, prefix_len, hwaddr, hwaddr_len); |
| 449 | + new_prefix_len = MAX(new_prefix_len, CONFIG_GNRC_IPV6_AUTO_SUBNETS_PREFIX_MIN_LEN); |
| 450 | + } else { |
| 451 | + _init_sub_prefix(&new_prefix, prefix, prefix_len, ++start_idx, subnet_len); |
| 452 | + } |
397 | 453 |
|
398 | 454 | DEBUG("auto_subnets: configure prefix %s/%u on %u\n", |
399 | 455 | ipv6_addr_to_str(addr_str, &new_prefix, sizeof(addr_str)), |
@@ -462,6 +518,7 @@ void gnrc_ipv6_nib_rtr_adv_pio_cb(gnrc_netif_t *upstream, const ndp_opt_pi_t *pi |
462 | 518 | /* if we are the only router on this bus, we can directly choose a prefix */ |
463 | 519 | _configure_subnets(subnets, 0, upstream, pio, src); |
464 | 520 | #else |
| 521 | + (void)src; |
465 | 522 |
|
466 | 523 | /* store PIO information for later use */ |
467 | 524 | if (!_store_pio(pio)) { |
|
0 commit comments