Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ndp: separate & clean next hop & l2 determination #3622

Closed
wants to merge 11 commits into from
5 changes: 5 additions & 0 deletions sys/include/net/gnrc/ipv6/netif.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ extern "C" {
*/
#define GNRC_IPV6_NETIF_FLAGS_ADV_CUR_HL (0x0010)

/**
* @brief Flag to indicate if the interface is operating over a wired link
*/
#define GNRC_IPV6_NETIF_FLAGS_IS_WIRED (0x2000)

/**
* @brief Flag to indicate that the interface has other address
* configuration.
Expand Down
27 changes: 23 additions & 4 deletions sys/include/net/gnrc/ndp/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,33 @@ extern "C" {
#endif

/**
* @brief Get link-layer address and interface for next hop to destination
* IPv6 address.
* @brief Next hop determination for a destination address that is not on-link.
*
* @param[out] l2addr The link-layer for the next hop to @p dst.
* @see <a href="https://tools.ietf.org/html/rfc4861#section-3">
* RFC 4861, section 3
* </a>
*
* @param[out] next_hop_ip Will be filled with IPv6 address of the next hop
* or NULL if no next hop could be determined.
* @param[in,out] iface Will be filled with the interface id that has to be
* used for reaching the next hop.
* @param[in] dst The destination IPv6 address.
*
* @return true, if a next hop could be determined.
* @return false otherwise
*/
bool gnrc_ndp_node_next_hop_ipv6_addr(ipv6_addr_t *next_hop_ip,
kernel_pid_t *iface, ipv6_addr_t *dst);

/**
* @brief Get link-layer address a given IPv6 address.
*
* @param[out] l2addr The link-layer of @p dst.
* @param[out] l2addr_len Length of @p l2addr.
* @param[in] iface The interface to search the next hop on.
* May be @ref KERNEL_PID_UNDEF if not specified.
* @param[in] dst An IPv6 address to search the next hop for.
* @param[in] dst The IPv6 address to search the link-layer
* address hop for.
* @param[in] pkt Packet to send to @p dst. Leave NULL if you
* just want to get the addresses.
*
Expand Down
11 changes: 11 additions & 0 deletions sys/include/net/netopt.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@ typedef enum {
NETOPT_TX_END_IRQ,
NETOPT_AUTOCCA, /**< en/disable to check automatically
* before sending the channel is clear. */

/**
* @brief read-only check for a wired interface.
*
* If the interface is wireless this function will return -ENOTSUP, a
* positive value otherwise.
*
* @note Setting this option will always return -EONOTSUP.
*/
NETOPT_IS_WIRED,

/* add more options if needed */

/**
Expand Down
4 changes: 4 additions & 0 deletions sys/net/gnrc/link_layer/netdev_eth/gnrc_netdev_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ static int _get(gnrc_netdev_t *dev, netopt_t opt, void *value, size_t max_len)
DEBUG("promiscous mode\n");
return _get_promiscousmode((gnrc_netdev_eth_t *)dev, value, max_len);

case NETOPT_IS_WIRED:
DEBUG("is wired\n");
return 1;

default:
DEBUG("[not supported: %d]\n", opt);
return -ENOTSUP;
Expand Down
15 changes: 14 additions & 1 deletion sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,23 @@ static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr)
gnrc_netapi_receive(gnrc_ipv6_pid, rcv_pkt);
}
else {
/* forwarding packet */
ipv6_addr_t next_hop_ip;
/* determining next hop's IP address */
if (ipv6_addr_is_link_local(&(hdr->dst))) {
memcpy(&next_hop_ip, &(hdr->dst), sizeof(ipv6_addr_t));
}
else if (!gnrc_ndp_node_next_hop_ipv6_addr(&next_hop_ip, &iface,
&(hdr->dst))) {
DEBUG("ipv6: error determining next hop's IPv6 address\n");
return;
}

/* determining next hop's layer 2 address */
uint8_t l2addr_len = GNRC_IPV6_NC_L2_ADDR_MAX;
uint8_t l2addr[l2addr_len];

iface = _next_hop_l2addr(l2addr, &l2addr_len, iface, &hdr->dst, pkt);
iface = _next_hop_l2addr(l2addr, &l2addr_len, iface, &next_hop_ip, pkt);

if (iface == KERNEL_PID_UNDEF) {
DEBUG("ipv6: error determining next hop's link layer address\n");
Expand Down
15 changes: 11 additions & 4 deletions sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ void gnrc_ipv6_netif_init_by_dev(void)
for (size_t i = 0; i < ifnum; i++) {
ipv6_addr_t addr;
eui64_t iid;
uint16_t mtu;
uint16_t tmp;
gnrc_ipv6_netif_t *ipv6_if = gnrc_ipv6_netif_get(ifs[i]);

if (ipv6_if == NULL) {
Expand Down Expand Up @@ -743,15 +743,22 @@ void gnrc_ipv6_netif_init_by_dev(void)
_add_addr_to_entry(ipv6_if, &addr, 64, 0);

/* set link MTU */
if ((gnrc_netapi_get(ifs[i], NETOPT_MAX_PACKET_SIZE, 0, &mtu,
if ((gnrc_netapi_get(ifs[i], NETOPT_MAX_PACKET_SIZE, 0, &tmp,
sizeof(uint16_t)) >= 0)) {
if (mtu >= IPV6_MIN_MTU) {
ipv6_if->mtu = mtu;
if (tmp >= IPV6_MIN_MTU) {
ipv6_if->mtu = tmp;
}
/* otherwise leave at GNRC_IPV6_NETIF_DEFAULT_MTU as initialized in
* gnrc_ipv6_netif_add() */
}

if (gnrc_netapi_get(ifs[i], NETOPT_IS_WIRED, 0, &tmp, sizeof(int)) > 0) {
ipv6_if->flags = GNRC_IPV6_NETIF_FLAGS_IS_WIRED;
}
else {
ipv6_if->flags = 0;
}

mutex_unlock(&ipv6_if->mutex);
}
}
Expand Down
106 changes: 52 additions & 54 deletions sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,68 +55,66 @@ static gnrc_pktqueue_t *_alloc_pkt_node(gnrc_pktsnip_t *pkt)
return NULL;
}

kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len,
kernel_pid_t iface, ipv6_addr_t *dst,
gnrc_pktsnip_t *pkt)
bool gnrc_ndp_node_next_hop_ipv6_addr(ipv6_addr_t *next_hop_ip,
kernel_pid_t *iface,
ipv6_addr_t *dst)
{
gnrc_ipv6_nc_t *nc_entry;
ipv6_addr_t *next_hop_ip = NULL, *prefix = NULL;

#ifdef MODULE_GNRC_IPV6_EXT_RH
ipv6_hdr_t *hdr;
gnrc_pktsnip_t *ipv6;
LL_SEARCH_SCALAR(pkt, ipv6, type, GNRC_NETTYPE_IPV6);
assert(ipv6);
hdr = ipv6->data;
next_hop_ip = ipv6_ext_rh_next_hop(hdr);
#endif
#ifdef MODULE_FIB
ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */
/* don't look-up link local addresses in FIB */
if (!ipv6_addr_is_link_local(dst)) {
size_t next_hop_size = sizeof(ipv6_addr_t);
uint32_t next_hop_flags = 0;

if ((next_hop_ip == NULL) &&
(fib_get_next_hop(gnrc_ipv6_fib_table, &iface, next_hop_actual.u8, &next_hop_size,
&next_hop_flags, (uint8_t *)dst,
sizeof(ipv6_addr_t), 0) >= 0) &&
(next_hop_size == sizeof(ipv6_addr_t))) {
next_hop_ip = &next_hop_actual;
}
next_hop_ip = gnrc_ipv6_ext_rh_next_hop(hdr);
Copy link
Contributor

Choose a reason for hiding this comment

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

This only modify a local pointer variable. We should memcpy like this.

ipv6_addr *tmp = gnrc_ipv6_ext_rh_next_hop(hdr);

if (tmp != NULL) {
    memcpy(next_hop_ip, tmp, sizeof(ipv6_addr_t));
    return true;
}

Alternatively, change signature like this:

bool gnrc_ndp_node_next_hop_ipv6_addr(ipv6_addr_t **next_hop_ip, ...)
{
    *next_hop_ip = gnrc_ipv6_ext_rh_next_hop(hdr);
    ...
}

call site:

  ipv6_addr_t *next_hop_ip;

  gnrc_ndp_node_next_hop_ipv6_addr(&next_hop_ip, ...);

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the hint. In fact this PR is very outdated and I just left it open to remind me of re-introducing it as a new one anytime soon.

if (next_hop_ip != NULL) {
return true;
}
#endif

if ((next_hop_ip == NULL)) { /* no route to host */
if (iface == KERNEL_PID_UNDEF) {
/* gnrc_ipv6_netif_t doubles as prefix list */
iface = gnrc_ipv6_netif_find_by_prefix(&prefix, dst);
}
else {
/* gnrc_ipv6_netif_t doubles as prefix list */
prefix = gnrc_ipv6_netif_match_prefix(iface, dst);
}
/* for wired interfaces "perform a longest prefix match against the Prefix
List to determine whether the packet's destination is on- or off-link."
according to RFC 4861, section 5.2 */
ipv6_addr_t *prefix;

if ((prefix != NULL) && /* prefix is on-link */
(gnrc_ipv6_netif_addr_get(prefix)->flags &
GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)) {
next_hop_ip = dst;
}
/* determine the longest matching prefix */
if (*iface == KERNEL_PID_UNDEF) {
/* gnrc_ipv6_netif_t doubles as prefix list */
*iface = gnrc_ipv6_netif_find_by_prefix(&prefix, dst);
}

/* dst has not an on-link prefix */
if (next_hop_ip == NULL) {
next_hop_ip = gnrc_ndp_internal_default_router();
else {
/* gnrc_ipv6_netif_t doubles as prefix list */
prefix = gnrc_ipv6_netif_match_prefix(*iface, dst);
}

/* interface is wired and prefix is on-link */
if ((gnrc_ipv6_netif_get(*iface)->flags & GNRC_IPV6_NETIF_FLAGS_IS_WIRED) &&
(prefix != NULL) &&
(gnrc_ipv6_netif_addr_get(prefix)->flags & GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)) {
next_hop_ip = dst;
}

if (next_hop_ip == NULL) {
next_hop_ip = dst; /* Just look if it's in the neighbor cache
* (aka on-link but not registered in prefix list as such) */
#ifdef MODULE_FIB
size_t next_hop_size = sizeof(ipv6_addr_t);
uint32_t next_hop_flags = 0;
ipv6_addr_t next_hop_actual;

if ((fib_get_next_hop(gnrc_ipv6_fib_table, iface, next_hop_actual.u8,
&next_hop_size, &next_hop_flags, (uint8_t *)dst,
sizeof(ipv6_addr_t), 0) >= 0) &&
(next_hop_size == sizeof(ipv6_addr_t))) {
memcpy(next_hop_ip, &next_hop_actual, sizeof(ipv6_addr_t));
return true;
}
#endif
next_hop_ip = gnrc_ndp_internal_default_router();
if (next_hop_ip != NULL) {
return true;
}
return false;
}

kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len,
kernel_pid_t iface, ipv6_addr_t *dst,
gnrc_pktsnip_t *pkt)
{
assert(dst != NULL);

/* start address resolution */
nc_entry = gnrc_ipv6_nc_get(iface, next_hop_ip);
gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, dst);

if ((nc_entry != NULL) && gnrc_ipv6_nc_is_reachable(nc_entry)) {
DEBUG("ndp node: found reachable neighbor (%s => ",
Expand All @@ -138,7 +136,7 @@ kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len,
gnrc_pktqueue_t *pkt_node;
ipv6_addr_t dst_sol;

nc_entry = gnrc_ipv6_nc_add(iface, next_hop_ip, NULL, 0,
nc_entry = gnrc_ipv6_nc_add(iface, dst, NULL, 0,
GNRC_IPV6_NC_STATE_INCOMPLETE << GNRC_IPV6_NC_STATE_POS);

if (nc_entry == NULL) {
Expand All @@ -158,15 +156,15 @@ kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len,
}

/* address resolution */
ipv6_addr_set_solicited_nodes(&dst_sol, next_hop_ip);
ipv6_addr_set_solicited_nodes(&dst_sol, dst);

if (iface == KERNEL_PID_UNDEF) {
timex_t t = { 0, GNRC_NDP_RETRANS_TIMER };
kernel_pid_t ifs[GNRC_NETIF_NUMOF];
size_t ifnum = gnrc_netif_get(ifs);

for (size_t i = 0; i < ifnum; i++) {
gnrc_ndp_internal_send_nbr_sol(ifs[i], next_hop_ip, &dst_sol);
gnrc_ndp_internal_send_nbr_sol(ifs[i], dst, &dst_sol);
}

vtimer_remove(&nc_entry->nbr_sol_timer);
Expand All @@ -176,7 +174,7 @@ kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len,
else {
gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface);

gnrc_ndp_internal_send_nbr_sol(iface, next_hop_ip, &dst_sol);
gnrc_ndp_internal_send_nbr_sol(iface, dst, &dst_sol);

mutex_lock(&ipv6_iface->mutex);
vtimer_remove(&nc_entry->nbr_sol_timer);
Expand Down
4 changes: 4 additions & 0 deletions sys/shell/commands/sc_netif.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ static void _netif_list(kernel_pid_t dev)
printf("6LO ");
}
linebreak = true;

printf("Link type: %s ", (entry->flags & GNRC_IPV6_NETIF_FLAGS_IS_WIRED) ?
"wired" : "wireless");
linebreak= true;
}
#endif

Expand Down