Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 85 additions & 43 deletions sys/net/gnrc/netif/gnrc_netif.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,6 @@ void gnrc_netif_release(gnrc_netif_t *netif)
}

#ifdef MODULE_GNRC_IPV6
static inline bool _addr_anycast(const gnrc_netif_t *netif, unsigned idx);
static int _addr_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr);
static int _group_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr);

Expand All @@ -501,18 +500,14 @@ static unsigned _match_to_len(const gnrc_netif_t *netif,
*
* @param[in] netif the network interface
* @param[in] addr the address to match
* @param[in] filter a bitfield with the bits at the position equal to the
* indexes of the addresses you want to include in the
* search set to one. NULL for all addresses
*
* @return index of the best match for @p addr
* @return -1 if no match was found
*
* @pre `netif != NULL` and `addr != NULL`
*/
static int _match_to_idx(const gnrc_netif_t *netif,
const ipv6_addr_t *addr,
const uint8_t *filter);
const ipv6_addr_t *addr);
/**
* @brief Determines the scope of the given address.
*
Expand Down Expand Up @@ -700,7 +695,7 @@ int gnrc_netif_ipv6_addr_match(gnrc_netif_t *netif,
{
assert((netif != NULL) && (addr != NULL));
gnrc_netif_acquire(netif);
int idx = _match_to_idx(netif, addr, NULL);
int idx = _match_to_idx(netif, addr);
gnrc_netif_release(netif);
return idx;
}
Expand Down Expand Up @@ -818,11 +813,6 @@ int gnrc_netif_ipv6_group_idx(gnrc_netif_t *netif, const ipv6_addr_t *addr)
return idx;
}

static inline bool _addr_anycast(const gnrc_netif_t *netif, unsigned idx)
{
return (netif->ipv6.addrs_flags[idx] & GNRC_NETIF_IPV6_ADDRS_FLAGS_ANYCAST);
}

static int _idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr, bool mcast)
{
if (!ipv6_addr_is_unspecified(addr)) {
Expand Down Expand Up @@ -852,13 +842,12 @@ static unsigned _match_to_len(const gnrc_netif_t *netif,
{
assert((netif != NULL) && (addr != NULL));

int n = _match_to_idx(netif, addr, NULL);
int n = _match_to_idx(netif, addr);
return (n >= 0) ? ipv6_addr_match_prefix(&(netif->ipv6.addrs[n]), addr) : 0;
}

static int _match_to_idx(const gnrc_netif_t *netif,
const ipv6_addr_t *addr,
const uint8_t *filter)
const ipv6_addr_t *addr)
{
assert((netif != NULL) && (addr != NULL));

Expand All @@ -867,15 +856,11 @@ static int _match_to_idx(const gnrc_netif_t *netif,
for (int i = 0; i < GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) {
unsigned match;

if ((netif->ipv6.addrs_flags[i] == 0) ||
((filter != NULL) && _addr_anycast(netif, i)) ||

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See RFC 6724, page 30, point 3: This can be removed without alternative.

/* discard const intentionally */
((filter != NULL) && !(bf_isset((uint8_t *)filter, i)))) {
if (netif->ipv6.addrs_flags[i] == 0) {
continue;
}
match = ipv6_addr_match_prefix(&(netif->ipv6.addrs[i]), addr);
if (((match > 64U) || !ipv6_addr_is_link_local(&(netif->ipv6.addrs[i]))) &&
(match >= best_match)) {
if (match > best_match) {
idx = i;
best_match = match;
}
Expand All @@ -885,17 +870,15 @@ static int _match_to_idx(const gnrc_netif_t *netif,
ipv6_addr_to_str(addr_str, &netif->ipv6.addrs[idx],
sizeof(addr_str)),
netif->pid);
DEBUG("%s by %u bits (used as source address = %s)\n",
DEBUG("%s by %u bits\n",
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)),
best_match,
(filter != NULL) ? "true" : "false");
best_match);
}
else {
DEBUG("gnrc_netif: Did not found any address on interface %" PRIkernel_pid
" matching %s (used as source address = %s)\n",
" matching %s\n",
netif->pid,
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)),
(filter != NULL) ? "true" : "false");
ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)));
}
return idx;
}
Expand Down Expand Up @@ -955,8 +938,19 @@ static int _create_candidate_set(const gnrc_netif_t *netif,
ipv6_addr_to_str(addr_str, tmp, sizeof(addr_str)));
/* "In any case, multicast addresses and the unspecified address MUST NOT
* be included in a candidate set."
*
* flags are set if not unspecfied and multicast addresses are in
* `netif->ipv6.groups` so not considered here.
*/
if ((netif->ipv6.addrs_flags[i] == 0) ||
/* https://tools.ietf.org/html/rfc4862#section-2:
* A tentative address is not considered assigned to an interface
* in the usual sense. An interface discards received packets
* addressed to a tentative address, but accepts Neighbor Discovery
* packets related to Duplicate Address Detection for the tentative
* address.
* (so don't consider tentative addresses for source address
* selection) */
gnrc_netif_ipv6_addr_dad_trans(netif, i)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this line is not part of this PR, but could you clarify why addresses are not considered that have gnrc_netif_ipv6_addr_dad_trans() != 0? According to the doc of this function, the return value is:

the number of duplicate address detection transmissions already performed

How can we deduce that the address is tentative from that result?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number of DAD transmissions is encoded in the tentative state in our DAD implementation (that's why you see TNT[1..3] with the ifconfig command, not just TNT). So a value of 0 for already performed DAD transmissions means the address is not tentative. As the NS for DAD is sent as soon as the address is added, this is also true if you don't have the knowledge of such GNRC internals ;)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also doc for tentative state:

/**
* @brief Tentative states (with encoded DAD retransmissions)
*
* The retransmissions of DAD transmits can be decoded from this state by
* applying it as a mask to the [flags](gnrc_netif_ipv6_t::addrs_flags) of the
* address.
*/
#define GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE (0x07U)

continue;
}
Expand Down Expand Up @@ -993,10 +987,46 @@ static int _create_candidate_set(const gnrc_netif_t *netif,
/* number of "points" assigned to an source address candidate in preferred state */
#define RULE_3_PTS (1)

/**
* @brief Caps the match at a source addresses prefix length
*
* @see https://tools.ietf.org/html/rfc6724#section-2.2
*
* @param[in] netif The network interface @p src was selected from
* @param[in] src A potential source address
* @param[in] match The number of bits matching of @p src with another address
*
* @return @p match or a number lesser than @p match, if @p src has a shorter
* prefix.
*/
static unsigned _cap_match(const gnrc_netif_t *netif, const ipv6_addr_t *src,
unsigned match)
{
unsigned best_prefix = 0;

if (ipv6_addr_is_link_local(src)) {
best_prefix = 64U; /* Link-local prefix is always of length 64 */
}
#ifdef MODULE_GNRC_IPV6_NIB
else {
void *state = NULL;
gnrc_ipv6_nib_pl_t ple;

while (gnrc_ipv6_nib_pl_iter(netif->pid, &state, &ple)) {
if ((ipv6_addr_match_prefix(&ple.pfx, src) > best_prefix)) {
best_prefix = ple.pfx_len;
}
}
}
#endif /* MODULE_GNRC_IPV6_NIB */
return ((best_prefix > 0) && (best_prefix < match)) ? best_prefix : match;
}

static ipv6_addr_t *_src_addr_selection(gnrc_netif_t *netif,
const ipv6_addr_t *dst,
uint8_t *candidate_set)
{
int idx = -1;
/* create temporary set for assigning "points" to candidates winning in the
* corresponding rules.
*/
Expand Down Expand Up @@ -1031,24 +1061,16 @@ static ipv6_addr_t *_src_addr_selection(gnrc_netif_t *netif,
if (candidate_scope == dst_scope) {
DEBUG("winner for rule 2 (same scope) found\n");
winner_set[i] += RULE_2A_PTS;
if (winner_set[i] > max_pts) {
max_pts = RULE_2A_PTS;
}
}
else if (candidate_scope < dst_scope) {
DEBUG("winner for rule 2 (smaller scope) found\n");
winner_set[i] += RULE_2B_PTS;
if (winner_set[i] > max_pts) {
max_pts = winner_set[i];
}
}

/* Rule 3: Avoid deprecated addresses. */
if (_get_state(netif, i) != GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_DEPRECATED) {
DEBUG("winner for rule 3 found\n");
winner_set[i] += RULE_3_PTS;
if (winner_set[i] > max_pts) {
max_pts = winner_set[i];
}
}

/* Rule 4: Prefer home addresses.
Expand Down Expand Up @@ -1077,19 +1099,39 @@ static ipv6_addr_t *_src_addr_selection(gnrc_netif_t *netif,
* Temporary addresses are currently not supported by gnrc.
* TODO: update as soon as gnrc supports temporary addresses
*/

if (winner_set[i] > max_pts) {
idx = i;
max_pts = winner_set[i];
}
}
/* reset candidate set to mark winners */
memset(candidate_set, 0, (GNRC_NETIF_IPV6_ADDRS_NUMOF + 7) / 8);
/* check if we have a clear winner */
/* check if we have a clear winner, otherwise
* rule 8: Use longest matching prefix.*/
uint8_t best_match = 0;
/* collect candidates with maximum points */
for (int i = 0; i < GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) {
if (winner_set[i] == max_pts) {
bf_set(candidate_set, i);
const ipv6_addr_t *addr = &netif->ipv6.addrs[i];
unsigned match = ipv6_addr_match_prefix(addr, dst);

match = _cap_match(netif, addr, match);
/* if match == 0 for all case, it takes above selected idx */
Comment thread
miri64 marked this conversation as resolved.
if (match > best_match) {
idx = i;
best_match = match;
}
}
}
/* otherwise apply rule 8: Use longest matching prefix. */
int idx = _match_to_idx(netif, dst, candidate_set);
return (idx < 0) ? NULL : &netif->ipv6.addrs[idx];
if (idx < 0) {
DEBUG("No winner found\n");
return NULL;
}
else {
DEBUG("Winner is: %s\n", ipv6_addr_to_str(addr_str,
&netif->ipv6.addrs[idx],
sizeof(addr_str)));
return &netif->ipv6.addrs[idx];
}
}
#endif /* MODULE_GNRC_IPV6 */

Expand Down
56 changes: 56 additions & 0 deletions tests/gnrc_netif/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,60 @@ static void test_ipv6_addr_best_src__ula_src_dst(void)
TEST_ASSERT(ipv6_addr_equal(&ula_src, out));
}

static void test_ipv6_addr_best_src__global_src_ula_dst(void)
{
static const ipv6_addr_t src = { .u8 = NETIF0_IPV6_G };
static const ipv6_addr_t ula_dst = { .u8 = { ULA1, ULA2, ULA3, ULA4,
ULA5, ULA6, ULA7, ULA8,
0, 0, 0, 0, 0, 0, 0, 1 } };
ipv6_addr_t *out = NULL;
int idx;

test_ipv6_addr_add__success(); /* adds link-local address */
TEST_ASSERT(0 <= (idx = gnrc_netif_ipv6_addr_add_internal(netifs[0], &src, 64U,
GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID)));
TEST_ASSERT_EQUAL_INT(GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID,
netifs[0]->ipv6.addrs_flags[idx]);
TEST_ASSERT(ipv6_addr_equal(&src, &netifs[0]->ipv6.addrs[idx]));

TEST_ASSERT_NOT_NULL((out = gnrc_netif_ipv6_addr_best_src(netifs[0],
&ula_dst,
false)));
TEST_ASSERT(ipv6_addr_equal(&src, out));
}

static void test_ipv6_addr_best_src__deprecated_addr(void)
{
static const ipv6_addr_t src = { .u8 = { LP1, LP2, LP3, LP4,
LP5, LP6, LP7, LP8,
0, 0, 0, 0, 0, 0, 0, 2 } };
static const ipv6_addr_t dst = { .u8 = { LP1, LP2, LP3, LP4,
LP5, LP6, LP7, LP8,
0, 0, 0, 0, 0, 0, 0, 1 } };
ipv6_addr_t *out = NULL;
int idx;
const unsigned exp_match = ipv6_addr_match_prefix(&src, &dst);

test_ipv6_addr_add__success(); /* adds EUI-64 based link-local address */
/* ensure that current addresses have smaller matches */
for (unsigned i = 0; i < GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) {
ipv6_addr_t *addr = &netifs[0]->ipv6.addrs[i];
TEST_ASSERT(exp_match > ipv6_addr_match_prefix(addr, &dst));
}
/* add another link-local address but deprecated */
TEST_ASSERT(0 <= (idx = gnrc_netif_ipv6_addr_add_internal(netifs[0], &src, 64U,
GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_DEPRECATED)));
TEST_ASSERT_EQUAL_INT(GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_DEPRECATED,
netifs[0]->ipv6.addrs_flags[idx]);
TEST_ASSERT(ipv6_addr_equal(&src, &netifs[0]->ipv6.addrs[idx]));

TEST_ASSERT_NOT_NULL((out = gnrc_netif_ipv6_addr_best_src(netifs[0],
&dst,
false)));
/* should be not `src` as it is deprecated */
TEST_ASSERT(!ipv6_addr_equal(&src, out));
}

static void test_get_by_ipv6_addr__empty(void)
{
static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL };
Expand Down Expand Up @@ -1494,6 +1548,8 @@ static Test *embunit_tests_gnrc_netif(void)
new_TestFixture(test_ipv6_addr_best_src__unspecified_addr),
new_TestFixture(test_ipv6_addr_best_src__other_subnet),
new_TestFixture(test_ipv6_addr_best_src__ula_src_dst),
new_TestFixture(test_ipv6_addr_best_src__global_src_ula_dst),
new_TestFixture(test_ipv6_addr_best_src__deprecated_addr),
new_TestFixture(test_get_by_ipv6_addr__empty),
new_TestFixture(test_get_by_ipv6_addr__unspecified_addr),
new_TestFixture(test_get_by_ipv6_addr__success),
Expand Down