diff --git a/sys/include/net/ng_ipv6/netif.h b/sys/include/net/ng_ipv6/netif.h index 693a4440c955d6f73629db6c8768551d0db88816..9cb9cab44372f60569e78bcae007079b17d65d74 100644 --- a/sys/include/net/ng_ipv6/netif.h +++ b/sys/include/net/ng_ipv6/netif.h @@ -396,6 +396,15 @@ ipv6_addr_t *ng_ipv6_netif_match_prefix(kernel_pid_t pid, const ipv6_addr_t *pre * @param[in] dest The destination address you want to find a destination * address for. * + * @todo Rule 4 from RFC 6724 is currently not implemented. Has to updated as + * soon as gnrc supports Mobile IP. + * + * @todo Rule 6 from RFC 6724 is currently not implemented. Has to updated as + * soon as gnrc supports flow labels. + * + * @todo Rule 7 from RFC 6724 is currently not implemented. Has to updated as + * soon as gnrc supports temporary addresses. + * * @return The reference to the found address on the interface. * @return NULL, if no matching address can be found on the interface. * @return NULL, if @p pid is no interface. diff --git a/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c b/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c index 2c4de417a17fda18af595d4f42af8a900b682f47..77efe7f21f1a0b46eb203adb3bae06639f734000 100644 --- a/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c +++ b/sys/net/network_layer/ng_ipv6/netif/ng_ipv6_netif.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Martin Lenders <mlenders@inf.fu-berlin.de> + * Copyright (C) 2015 Oliver Hahm <oliver.hahm@inria.fr> * * 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 @@ -13,6 +14,7 @@ * @file * * @author Martine Lenders <mlenders@inf.fu-berlin.de> + * @author Oliver Hahm <oliver.hahm@inria.fr> */ #include <errno.h> @@ -20,6 +22,8 @@ #include "kernel_types.h" #include "mutex.h" +#include "bitfield.h" + #include "net/eui64.h" #include "net/ipv6/addr.h" #include "net/ng_ndp.h" @@ -38,6 +42,15 @@ #include <inttypes.h> #endif +/* number of "points" assigned to an source address candidate with equal scope + * than destination address */ +#define RULE_2A_PTS (4) +/* number of "points" assigned to an source address candidate with smaller scope + * than destination address */ +#define RULE_2B_PTS (2) +/* number of "points" assigned to an source address candidate in preferred state */ +#define RULE_3_PTS (1) + static ng_ipv6_netif_t ipv6_ifs[NG_NETIF_NUMOF]; #if ENABLE_DEBUG @@ -327,14 +340,18 @@ ipv6_addr_t *ng_ipv6_netif_find_addr(kernel_pid_t pid, const ipv6_addr_t *addr) } static uint8_t _find_by_prefix_unsafe(ipv6_addr_t **res, ng_ipv6_netif_t *iface, - const ipv6_addr_t *addr, bool src_search) + const ipv6_addr_t *addr, uint8_t *only) { uint8_t best_match = 0; for (int i = 0; i < NG_IPV6_NETIF_ADDR_NUMOF; i++) { uint8_t match; - if ((src_search && + if ((only != NULL) && !(bf_isset(only, i))) { + continue; + } + + if (((only != NULL) && ng_ipv6_netif_addr_is_non_unicast(&(iface->addrs[i].addr))) || ipv6_addr_is_unspecified(&(iface->addrs[i].addr))) { continue; @@ -342,7 +359,7 @@ static uint8_t _find_by_prefix_unsafe(ipv6_addr_t **res, ng_ipv6_netif_t *iface, match = ipv6_addr_match_prefix(&(iface->addrs[i].addr), addr); - if (!src_search && !ipv6_addr_is_multicast(addr) && + if ((only == NULL) && !ipv6_addr_is_multicast(addr) && (match < iface->addrs[i].prefix_len)) { /* match but not of same subnet */ continue; @@ -365,14 +382,14 @@ static uint8_t _find_by_prefix_unsafe(ipv6_addr_t **res, ng_ipv6_netif_t *iface, DEBUG("%s by %" PRIu8 " bits (used as source address = %s)\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), best_match, - (src_search) ? "true" : "false"); + (only != NULL) ? "true" : "false"); } else { DEBUG("ipv6 netif: Did not found any address on interface %" PRIkernel_pid " matching %s (used as source address = %s)\n", iface->pid, ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), - (src_search) ? "true" : "false"); + (only != NULL) ? "true" : "false"); } #endif @@ -390,7 +407,7 @@ kernel_pid_t ng_ipv6_netif_find_by_prefix(ipv6_addr_t **out, const ipv6_addr_t * mutex_lock(&(ipv6_ifs[i].mutex)); - match = _find_by_prefix_unsafe(&tmp_res, ipv6_ifs + i, prefix, false); + match = _find_by_prefix_unsafe(&tmp_res, ipv6_ifs + i, prefix, NULL); if (match > best_match) { if (out != NULL) { @@ -422,15 +439,78 @@ kernel_pid_t ng_ipv6_netif_find_by_prefix(ipv6_addr_t **out, const ipv6_addr_t * return res; } -static ipv6_addr_t *_match_prefix(kernel_pid_t pid, const ipv6_addr_t *addr, - bool src_search) +/** + * @brief selects potential source address candidates + * @see <a href="http://tools.ietf.org/html/rfc6724#section-4"> + * RFC6724, section 4 + * </a> + * @param[in] iface the interface used for sending + * @param[in] dst the destination address + * @param[out] candidate_set a bitfield representing all addresses + * configured to @p iface, potential candidates + * will be marked as 1 + * + * @return false if no candidates were found + * @return true otherwise + * + * @pre the interface entry and its set of addresses must not be changed during + * runtime of this function + */ +static int _create_candidate_set(ng_ipv6_netif_t *iface, + const ipv6_addr_t *dst, + uint8_t *candidate_set) +{ + int res = -1; + + DEBUG("gathering candidates\n"); + + /* currently this implementation supports only addresses as source address + * candidates assigned to this interface. Thus we assume all addresses to be + * on interface @p iface */ + (void) dst; + + for (int i = 0; i < NG_IPV6_NETIF_ADDR_NUMOF; i++) { + ng_ipv6_netif_addr_t *iter = &(iface->addrs[i]); + + DEBUG("Checking address: %s\n", + ipv6_addr_to_str(addr_str, &(iter->addr), sizeof(addr_str))); + + /* "In any case, multicast addresses and the unspecified address MUST NOT + * be included in a candidate set." + */ + if (ipv6_addr_is_multicast(&(iter->addr)) || + ipv6_addr_is_unspecified(&(iter->addr))) { + continue; + } + + /* "For all multicast and link-local destination addresses, the set of + * candidate source addresses MUST only include addresses assigned to + * interfaces belonging to the same link as the outgoing interface." + * + * "For site-local unicast destination addresses, the set of candidate + * source addresses MUST only include addresses assigned to interfaces + * belonging to the same site as the outgoing interface." + * -> we should also be fine, since we're only iterating addresses of + * the sending interface + */ + + /* put all other addresses into the candidate set */ + DEBUG("add to candidate set\n"); + bf_set(candidate_set, i); + res = i; + } + + return res; +} + +ipv6_addr_t *ng_ipv6_netif_match_prefix(kernel_pid_t pid, const ipv6_addr_t *prefix) { ipv6_addr_t *res = NULL; ng_ipv6_netif_t *iface = ng_ipv6_netif_get(pid); mutex_lock(&(iface->mutex)); - if (_find_by_prefix_unsafe(&res, iface, addr, src_search) > 0) { + if (_find_by_prefix_unsafe(&res, iface, prefix, NULL) > 0) { mutex_unlock(&(iface->mutex)); return res; } @@ -440,14 +520,174 @@ static ipv6_addr_t *_match_prefix(kernel_pid_t pid, const ipv6_addr_t *addr, return NULL; } -ipv6_addr_t *ng_ipv6_netif_match_prefix(kernel_pid_t pid, const ipv6_addr_t *prefix) +/** + * @brief Determines the scope of the given address. + * + * @param[in] addr The IPv6 address to check. + * @param[in] maybe_multicast False if @p addr is definitely no multicast + * address, true otherwise. + * + * @return The scope of the address. + * + * @pre address is not loopback or unspecified. + * see http://tools.ietf.org/html/rfc6724#section-4 + */ +static uint8_t _get_scope(const ipv6_addr_t *addr, const bool maybe_multicast) +{ + if (maybe_multicast && ipv6_addr_is_multicast(addr)) { + return (addr->u8[1] & 0x0f); + } + else if (ipv6_addr_is_link_local(addr)) { + return IPV6_ADDR_MCAST_SCP_LINK_LOCAL; + } + else if (ipv6_addr_is_site_local(addr)) { + return IPV6_ADDR_MCAST_SCP_SITE_LOCAL; + } + else { + return IPV6_ADDR_MCAST_SCP_GLOBAL; + } +} + +/** @brief Find the best candidate among the configured addresses + * for a certain destination address according to the 8 rules + * specified in RFC 6734, section 5. + * @see <a href="http://tools.ietf.org/html/rfc6724#section-5"> + * RFC6724, section 5 + * </a> + * + * @param[in] iface The interface for sending. + * @param[in] dst The destination IPv6 address. + * @param[in, out] candidate_set The preselected set of candidate addresses as + * a bitfield. + * + * @pre @p dst is not unspecified. + * + * @return The best matching candidate found on @p iface, may be NULL if none + * is found. + */ +static ipv6_addr_t *_source_address_selection(ng_ipv6_netif_t *iface, + const ipv6_addr_t *dst, + uint8_t *candidate_set) { - return _match_prefix(pid, prefix, false); + /* create temporary set for assigning "points" to candidates wining in the + * corresponding rules. + */ + uint8_t winner_set[NG_IPV6_NETIF_ADDR_NUMOF]; + memset(winner_set, 0, NG_IPV6_NETIF_ADDR_NUMOF); + + uint8_t max_pts = 0; + + /* _create_candidate_set() assures that `dest` is not unspecified and if + * `dst` is loopback rule 1 will fire anyway. */ + uint8_t dst_scope = _get_scope(dst, true); + DEBUG("finding the best match within the source address candidates\n"); + + for (int i = 0; i < NG_IPV6_NETIF_ADDR_NUMOF; i++) { + ng_ipv6_netif_addr_t *iter = &(iface->addrs[i]); + DEBUG("Checking address: %s\n", + ipv6_addr_to_str(addr_str, &(iter->addr), sizeof(addr_str))); + /* entries which are not part of the candidate set can be ignored */ + if (!(bf_isset(candidate_set, i))) { + DEBUG("Not part of the candidate set - skipping\n"); + continue; + } + + /* Rule 1: if we have an address configured that equals the destination + * use this one as source */ + if (ipv6_addr_equal(&(iter->addr), dst)) { + DEBUG("Ease one - rule 1\n"); + return &(iter->addr); + } + + /* Rule 2: Prefer appropriate scope. */ + /* both link local */ + uint8_t candidate_scope = _get_scope(&(iter->addr), false); + 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 (iter->preferred > 0) { + 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. + * Does not apply, gnrc does not support Mobile IP. + * TODO: update as soon as gnrc supports Mobile IP + */ + + /* Rule 5: Prefer outgoing interface. + * RFC 6724 says: + * "It is RECOMMENDED that the candidate source addresses be the set of + * unicast addresses assigned to the interface that will be used to + * send to the destination (the "outgoing" interface). On routers, + * the candidate set MAY include unicast addresses assigned to any + * interface that forwards packets, subject to the restrictions + * described below." + * Currently this implementation uses ALWAYS source addresses assigned + * to the outgoing interface. Hence, Rule 5 is always fulfilled. + */ + + /* Rule 6: Prefer matching label. + * Flow labels are currently not supported by gnrc. + * TODO: update as soon as gnrc supports flow labels + */ + + /* Rule 7: Prefer temporary addresses. + * Temporary addresses are currently not supported by gnrc. + * TODO: update as soon as gnrc supports temporary addresses + */ + } + + /* reset candidate set to mark winners */ + memset(candidate_set, 0, (NG_IPV6_NETIF_ADDR_NUMOF / 8) + 1); + /* check if we have a clear winner */ + /* collect candidates with maximum points */ + for (int i = 0; i < NG_IPV6_NETIF_ADDR_NUMOF; i++) { + if (winner_set[i] == max_pts) { + bf_set(candidate_set, i); + } + } + + /* otherwise apply rule 8: Use longest matching prefix. */ + ipv6_addr_t *res = NULL; + _find_by_prefix_unsafe(&res, iface, dst, candidate_set); + return res; } -ipv6_addr_t *ng_ipv6_netif_find_best_src_addr(kernel_pid_t pid, const ipv6_addr_t *dest) +ipv6_addr_t *ng_ipv6_netif_find_best_src_addr(kernel_pid_t pid, const ipv6_addr_t *dst) { - return _match_prefix(pid, dest, true); + ng_ipv6_netif_t *iface = ng_ipv6_netif_get(pid); + ipv6_addr_t *best_src = NULL; + mutex_lock(&(iface->mutex)); + BITFIELD(candidate_set, NG_IPV6_NETIF_ADDR_NUMOF); + memset(candidate_set, 0, sizeof(candidate_set)); + + int first_candidate = _create_candidate_set(iface, dst, candidate_set); + if (first_candidate >= 0) { + best_src = _source_address_selection(iface, dst, candidate_set); + if (best_src == NULL) { + best_src = &(iface->addrs[first_candidate].addr); + } + } + mutex_unlock(&(iface->mutex)); + + return best_src; } void ng_ipv6_netif_init_by_dev(void)