From a980155250623be93c753b1df987d7973701e6d3 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 14 Aug 2015 14:13:09 +0200 Subject: [PATCH 1/2] ng_ndp: add support for address-less link-layers --- sys/include/net/gnrc/ndp/internal.h | 22 +-- sys/net/gnrc/network_layer/ndp/gnrc_ndp.c | 163 ++++++++++-------- .../ndp/internal/gnrc_ndp_internal.c | 40 ++--- .../network_layer/ndp/node/gnrc_ndp_node.c | 6 +- 4 files changed, 114 insertions(+), 117 deletions(-) diff --git a/sys/include/net/gnrc/ndp/internal.h b/sys/include/net/gnrc/ndp/internal.h index def878163f..52b42dd00b 100644 --- a/sys/include/net/gnrc/ndp/internal.h +++ b/sys/include/net/gnrc/ndp/internal.h @@ -19,8 +19,8 @@ * * @author Martine Lenders */ -#ifndef INTERNAL_H_ -#define INTERNAL_H_ +#ifndef GNRC_NDP_INTERNAL_H_ +#define GNRC_NDP_INTERNAL_H_ #include "net/ipv6/addr.h" #include "net/ipv6/hdr.h" @@ -91,23 +91,22 @@ void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_a /** * @brief Handles a SL2A option. * - * @param[in] iface Interface the option was received on. * @param[in] pkt Packet the option was received in. * @param[in] ipv6 IPv6 header of @p pkt * @param[in] icmpv6_type ICMPv6 type of the message carrying the option. - * @param[in] sl2a_opt The SL2A option. + * @param[in] tl2a_opt The TL2A option. + * @param[out] l2addr The L2 address carried in the SL2A option. * - * @return true, on success. - * @return false, if SL2A was not valid. + * @return length of the L2 address, on success. + * @return -EINVAL, if SL2A was not valid. + * @return -ENOTSUP, if node should silently ignore the option. */ -bool gnrc_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, - ipv6_hdr_t *ipv6, uint8_t icmpv6_type, - ndp_opt_t *sl2a_opt); +int gnrc_ndp_internal_sl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + ndp_opt_t *sl2a_opt, uint8_t *l2addr); /** * @brief Handles a TL2A option. * - * @param[in] iface Interface the option was received on. * @param[in] pkt Packet the option was received in. * @param[in] ipv6 IPv6 header of @p pkt * @param[in] icmpv6_type ICMPv6 type of the message carrying the option. @@ -116,6 +115,7 @@ bool gnrc_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, * * @return length of the L2 address, on success. * @return -EINVAL, if TL2A was not valid. + * @return -ENOTSUP, if node should silently ignore the option. */ int gnrc_ndp_internal_tl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, ndp_opt_t *tl2a_opt, @@ -125,5 +125,5 @@ int gnrc_ndp_internal_tl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, } #endif -#endif /* INTERNAL_H_ */ +#endif /* GNRC_NDP_INTERNAL_H_ */ /** @} */ diff --git a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c index 84dc909da0..e79cd12a7b 100644 --- a/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c +++ b/sys/net/gnrc/network_layer/ndp/gnrc_ndp.c @@ -43,23 +43,42 @@ static char addr_str[IPV6_ADDR_MAX_STR_LEN]; #endif -/* random helper function */ +/* sets an entry to stale if its l2addr differs from the given one or creates it stale if it + * does not exist */ +static void _stale_nc(kernel_pid_t iface, ipv6_addr_t *ipaddr, uint8_t *l2addr, + int l2addr_len) +{ + if (l2addr_len != -ENOTSUP) { + gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, ipaddr); + if (nc_entry == NULL) { + gnrc_ipv6_nc_add(iface, ipaddr, l2addr, (uint16_t)l2addr_len, + GNRC_IPV6_NC_STATE_STALE); + } + else if (((uint16_t)l2addr_len != nc_entry->l2_addr_len) || + (memcmp(l2addr, nc_entry->l2_addr, l2addr_len) != 0)) { + /* if entry exists but l2 address differs: set */ + nc_entry->l2_addr_len = (uint16_t)l2addr_len; + memcpy(nc_entry->l2_addr, l2addr, l2addr_len); + gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); + } + } +} + void gnrc_ndp_nbr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, ndp_nbr_sol_t *nbr_sol, size_t icmpv6_size) { uint16_t opt_offset = 0; + uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX]; uint8_t *buf = ((uint8_t *)nbr_sol) + sizeof(ndp_nbr_sol_t); ipv6_addr_t *tgt; - int sicmpv6_size = (int)icmpv6_size; - + int sicmpv6_size = (int)icmpv6_size, l2src_len = 0; DEBUG("ndp: received neighbor solicitation (src: %s, ", ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); DEBUG("dst: %s, ", ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str))); DEBUG("tgt: %s)\n", ipv6_addr_to_str(addr_str, &nbr_sol->tgt, sizeof(addr_str))); - /* check validity */ if ((ipv6->hl != 255) || (nbr_sol->code != 0) || (icmpv6_size < sizeof(ndp_nbr_sol_t)) || @@ -70,41 +89,37 @@ void gnrc_ndp_nbr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, /* ipv6 releases */ return; } - if ((tgt = gnrc_ipv6_netif_find_addr(iface, &nbr_sol->tgt)) == NULL) { DEBUG("ndp: Target address is not to interface %" PRIkernel_pid "\n", iface); /* ipv6 releases */ return; } - sicmpv6_size -= sizeof(ndp_nbr_sol_t); - while (sicmpv6_size > 0) { ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset); - switch (opt->type) { case NDP_OPT_SL2A: - if (!gnrc_ndp_internal_sl2a_opt_handle(iface, pkt, ipv6, nbr_sol->type, opt)) { - /* invalid source link-layer address option */ + if ((l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6, nbr_sol->type, opt, + l2src)) < 0) { + /* -ENOTSUP can not happen, since the function only returns this for invalid + * message types containing the SL2A. Neighbor solicitations are not an + * invalid message type for SL2A. According to that, we don't need to watch + * out for that here, but regardless, the source link-layer address option + * is invalid. */ return; } - break; - default: /* silently discard all other options */ break; } - opt_offset += (opt->len * 8); sicmpv6_size -= (opt->len * 8); } - + _stale_nc(iface, &ipv6->src, l2src, l2src_len); gnrc_ndp_internal_send_nbr_adv(iface, tgt, &ipv6->src, ipv6_addr_is_multicast(&ipv6->dst), NULL); - - return; } static inline bool _pkt_has_l2addr(gnrc_netif_hdr_t *netif_hdr) @@ -178,66 +193,24 @@ void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, netif_hdr = netif->data; } - if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) { - gnrc_pktqueue_t *queued_pkt; + if (l2tgt_len != -ENOTSUP) { + if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) { + gnrc_pktqueue_t *queued_pkt; - if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len == 0)) { - /* link-layer has addresses, but no TLLAO supplied: discard silently - * (see https://tools.ietf.org/html/rfc4861#section-7.2.5) */ - return; - } - - nc_entry->iface = iface; - nc_entry->l2_addr_len = l2tgt_len; - memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); - - if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) { - gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE); - } - else { - gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); - } - - if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) { - nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER; - } - else { - nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER; - /* TODO: update FIB */ - } - - while ((queued_pkt = gnrc_pktqueue_remove_head(&nc_entry->pkts)) != NULL) { - gnrc_netapi_send(gnrc_ipv6_pid, queued_pkt->pkt); - queued_pkt->pkt = NULL; - } - } - else { - /* first or-term: no link-layer, but nc_entry has l2addr, - * second or-term: different l2addr cached */ - bool l2tgt_changed = false; - - if ((!_pkt_has_l2addr(netif_hdr)) && (l2tgt_len == 0)) { - /* there was previously a L2 address registered */ - l2tgt_changed = (nc_entry->l2_addr_len != 0); - } - /* link-layer has addresses and TLLAO with different address */ - else if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len != 0)) { - l2tgt_changed = (!(l2tgt_len == nc_entry->l2_addr_len)) && - (memcmp(nc_entry->l2_addr, l2tgt, l2tgt_len) == 0); - } - - if ((nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) || !l2tgt_changed || - (l2tgt_len == 0)) { - if (l2tgt_len != 0) { - nc_entry->iface = iface; - nc_entry->l2_addr_len = l2tgt_len; - memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); + if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len == 0)) { + /* link-layer has addresses, but no TLLAO supplied: discard silently + * (see https://tools.ietf.org/html/rfc4861#section-7.2.5) */ + return; } + nc_entry->iface = iface; + nc_entry->l2_addr_len = l2tgt_len; + memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); + if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE); } - else if (l2tgt_changed && (l2tgt_len != 0)) { + else { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); } @@ -246,13 +219,55 @@ void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, } else { nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER; - /* TODO: update FIB */ + /* TODO: update state of neighbor as router in FIB? */ + } + + while ((queued_pkt = gnrc_pktqueue_remove_head(&nc_entry->pkts)) != NULL) { + gnrc_netapi_send(gnrc_ipv6_pid, queued_pkt->pkt); + queued_pkt->pkt = NULL; } } - else if (l2tgt_changed) { - if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_REACHABLE) { - gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); + else { + /* first or-term: no link-layer, but nc_entry has l2addr, + * second or-term: different l2addr cached */ + bool l2tgt_changed = false; + if ((!_pkt_has_l2addr(netif_hdr)) && (l2tgt_len == 0)) { + /* there was previously a L2 address registered */ + l2tgt_changed = (nc_entry->l2_addr_len != 0); + } + /* link-layer has addresses and TLLAO with different address */ + else if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len != 0)) { + l2tgt_changed = (!(l2tgt_len == nc_entry->l2_addr_len)) && + (memcmp(nc_entry->l2_addr, l2tgt, l2tgt_len) == 0); + } + + if ((nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) || !l2tgt_changed || + (l2tgt_len == 0)) { + if (l2tgt_len != 0) { + nc_entry->iface = iface; + nc_entry->l2_addr_len = l2tgt_len; + memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); + } + + if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) { + gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE); + } + else if (l2tgt_changed) { + gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); + } + + if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) { + nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER; + } + else { + nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER; + /* TODO: update state of neighbor as router in FIB? */ + } + } + else if (l2tgt_changed && + gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_REACHABLE) { + gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); } } } diff --git a/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c b/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c index 2a0ecf2285..5e1650e486 100644 --- a/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c +++ b/sys/net/gnrc/network_layer/ndp/internal/gnrc_ndp_internal.c @@ -311,17 +311,15 @@ void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *tgt, gnrc_netapi_send(gnrc_ipv6_pid, pkt); } -bool gnrc_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, - ipv6_hdr_t *ipv6, uint8_t icmpv6_type, - ndp_opt_t *sl2a_opt) +int gnrc_ndp_internal_sl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, + ndp_opt_t *sl2a_opt, uint8_t *l2src) { - gnrc_ipv6_nc_t *nc_entry = NULL; - uint8_t sl2a_len = 0; + int sl2a_len = 0; uint8_t *sl2a = (uint8_t *)(sl2a_opt + 1); if ((sl2a_opt->len == 0) || ipv6_addr_is_unspecified(&ipv6->src)) { DEBUG("ndp: invalid source link-layer address option received\n"); - return false; + return -EINVAL; } while (pkt) { @@ -333,41 +331,25 @@ bool gnrc_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, pkt = pkt->next; } - if (sl2a_len == 0) { /* in case there was no source address in l2 */ - sl2a_len = (sl2a_opt->len / 8) - sizeof(ndp_opt_t); - - /* ignore all zeroes at the end for length */ - for (; sl2a[sl2a_len - 1] == 0x00; sl2a_len--); - } - DEBUG("ndp: received SL2A (link-layer address: %s)\n", gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), sl2a, sl2a_len)); switch (icmpv6_type) { case ICMPV6_NBR_SOL: - nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src); + if (sl2a_len == 0) { /* in case there was no source address in l2 */ + sl2a_len = (sl2a_opt->len / 8) - sizeof(ndp_opt_t); - if (nc_entry != NULL) { - if ((sl2a_len != nc_entry->l2_addr_len) || - (memcmp(sl2a, nc_entry->l2_addr, sl2a_len) != 0)) { - /* if entry exists but l2 address differs: set */ - nc_entry->l2_addr_len = sl2a_len; - memcpy(nc_entry->l2_addr, sl2a, sl2a_len); - - gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); - } - } - else { - gnrc_ipv6_nc_add(iface, &ipv6->src, sl2a, sl2a_len, - GNRC_IPV6_NC_STATE_STALE); + /* ignore all zeroes at the end for length */ + for (; sl2a[sl2a_len - 1] == 0x00; sl2a_len--); } - return true; + memcpy(l2src, sl2a, sl2a_len); + return sl2a_len; default: /* wrong encapsulating message: silently discard */ DEBUG("ndp: silently discard sl2a_opt for ICMPv6 message type %" PRIu8 "\n", icmpv6_type); - return true; + return -ENOTSUP; } } diff --git a/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c b/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c index 150899ae52..ea0d28145e 100644 --- a/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c +++ b/sys/net/gnrc/network_layer/ndp/node/gnrc_ndp_node.c @@ -109,7 +109,6 @@ kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, next_hop_ip = gnrc_ndp_internal_default_router(); } - 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) */ @@ -128,8 +127,9 @@ kernel_pid_t gnrc_ndp_node_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_STALE) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_DELAY); } - - memcpy(l2addr, nc_entry->l2_addr, nc_entry->l2_addr_len); + if (nc_entry->l2_addr_len > 0) { + memcpy(l2addr, nc_entry->l2_addr, nc_entry->l2_addr_len); + } *l2addr_len = nc_entry->l2_addr_len; /* TODO: unreachability check */ return nc_entry->iface; From 6e0a44705bea1448791d53dfbeb5619687de9539 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 1 Sep 2015 19:32:57 +0200 Subject: [PATCH 2/2] sc_ipv6_nc: add capability for address-less link-layers --- sys/shell/commands/sc_ipv6_nc.c | 70 +++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/sys/shell/commands/sc_ipv6_nc.c b/sys/shell/commands/sc_ipv6_nc.c index bffe8c088b..6db6654901 100644 --- a/sys/shell/commands/sc_ipv6_nc.c +++ b/sys/shell/commands/sc_ipv6_nc.c @@ -127,20 +127,59 @@ static int _ipv6_nc_list(void) return 0; } -static int _ipv6_nc_add(kernel_pid_t iface, char *ipv6_addr_str, - char *l2_addr_str) +static inline kernel_pid_t _get_iface(void) { + kernel_pid_t ifs[GNRC_NETIF_NUMOF]; + size_t ifnum = gnrc_netif_get(ifs); + return (ifnum > 1) ? KERNEL_PID_UNDEF : ifs[0]; +} + +static int _ipv6_nc_add(int argc, char **argv) +{ + char *ipv6_addr_str; + char *l2_addr_str = NULL; + kernel_pid_t iface = KERNEL_PID_UNDEF; ipv6_addr_t ipv6_addr; uint8_t l2_addr[MAX_L2_ADDR_LEN]; size_t l2_addr_len; + switch (argc) { + case 1: + iface = _get_iface(); + ipv6_addr_str = argv[0]; /* store for output */ + l2_addr_len = 0; + break; + case 2: + ipv6_addr_str = argv[0]; /* store for output */ + if (ipv6_addr_from_str(&ipv6_addr, ipv6_addr_str) == NULL) { + iface = atoi(argv[0]); + ipv6_addr_str = argv[1]; + l2_addr_len = 0; + } + else { + iface = _get_iface(); + l2_addr_str = argv[1]; + } + break; + default: + iface = atoi(argv[0]); + ipv6_addr_str = argv[1]; + l2_addr_str = argv[2]; + break; + } + + if (!_is_iface(iface)) { + puts("error: invalid interface given."); + return 1; + } + if (ipv6_addr_from_str(&ipv6_addr, ipv6_addr_str) == NULL) { puts("error: unable to parse IPv6 address."); return 1; } - if ((l2_addr_len = gnrc_netif_addr_from_str(l2_addr, sizeof(l2_addr), - l2_addr_str)) == 0) { + if ((l2_addr_str != NULL) && (l2_addr_len = gnrc_netif_addr_from_str(l2_addr, sizeof(l2_addr), + argv[1])) == 0) { puts("error: unable to parse link-layer address."); return 1; } @@ -198,31 +237,12 @@ int _ipv6_nc_manage(int argc, char **argv) } if (argc > 1) { - if ((argc == 4) && (strcmp("add", argv[1]) == 0)) { - kernel_pid_t ifs[GNRC_NETIF_NUMOF]; - size_t ifnum = gnrc_netif_get(ifs); - if (ifnum > 1) { - puts("error: multiple interfaces exist."); - return 1; - } - - return _ipv6_nc_add(ifs[0], argv[2], argv[3]); + if (strcmp("add", argv[1]) == 0) { + return _ipv6_nc_add(argc - 2, &argv[2]); } - else if ((argc > 4) && (strcmp("add", argv[1]) == 0)) { - kernel_pid_t iface = (kernel_pid_t)atoi(argv[2]); - - if (!_is_iface(iface)) { - puts("error: invalid interface given."); - return 1; - } - - return _ipv6_nc_add(iface, argv[3], argv[4]); - } - if (strcmp("del", argv[1]) == 0) { return _ipv6_nc_del(argv[2]); } - if (strcmp("reset", argv[1]) == 0) { return _ipv6_nc_reset(); }