void gnrc_ndp_internal_send_nbr_sol(kernel_pid_t iface, ipv6_addr_t *src, ipv6_addr_t *tgt, ipv6_addr_t *dst) { #ifdef MODULE_GNRC_SIXLOWPAN_ND gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface); assert(ipv6_iface != NULL); #endif gnrc_pktsnip_t *hdr, *pkt = NULL; /* both suppressions, since they are needed in the MODULE_GNRC_SIXLOWPAN_ND branch */ /* cppcheck-suppress variableScope */ uint8_t l2src[8]; /* cppcheck-suppress variableScope */ size_t l2src_len = 0; DEBUG("ndp internal: send neighbor solicitation (iface: %" PRIkernel_pid ", src: %s, ", iface, ipv6_addr_to_str(addr_str, src, sizeof(addr_str))); DEBUG(" tgt: %s, ", ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); DEBUG("dst: %s)\n", ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); /* check if there is a fitting source address to target */ if (src == NULL) { src = gnrc_ipv6_netif_find_best_src_addr(iface, tgt); } if (src != NULL) { l2src_len = _get_l2src(iface, l2src, sizeof(l2src)); if (l2src_len > 0) { /* add source address link-layer address option */ pkt = gnrc_ndp_opt_sl2a_build(l2src, l2src_len, NULL); if (pkt == NULL) { DEBUG("ndp internal: error allocating Source Link-layer address option.\n"); gnrc_pktbuf_release(pkt); return; } } } #ifdef MODULE_GNRC_SIXLOWPAN_ND if (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { if (l2src_len != sizeof(eui64_t)) { l2src_len = (uint16_t)gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, l2src, sizeof(l2src)); if (l2src_len != sizeof(eui64_t)) { DEBUG("ndp internal: can't get EUI-64 of the interface\n"); gnrc_pktbuf_release(pkt); return; } } hdr = gnrc_sixlowpan_nd_opt_ar_build(0, GNRC_SIXLOWPAN_ND_AR_LTIME, (eui64_t *)l2src, pkt); if (hdr == NULL) { DEBUG("ndp internal: error allocatin Address Registration option.\n"); gnrc_pktbuf_release(pkt); return; } pkt = hdr; } #endif hdr = gnrc_ndp_nbr_sol_build(tgt, pkt); if (hdr == NULL) { DEBUG("ndp internal: error allocating Neighbor solicitation.\n"); gnrc_pktbuf_release(pkt); return; } pkt = hdr; hdr = _build_headers(iface, pkt, dst, src); if (hdr == NULL) { DEBUG("ndp internal: error adding lower-layer headers.\n"); gnrc_pktbuf_release(pkt); return; } else if (gnrc_netapi_send(gnrc_ipv6_pid, hdr) < 1) { DEBUG("ndp internal: unable to send neighbor solicitation\n"); gnrc_pktbuf_release(hdr); } }
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, nbr_adv_dst; gnrc_pktsnip_t *nbr_adv_opts = NULL; #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER ndp_opt_t *sl2a_opt = NULL; sixlowpan_nd_opt_ar_t *ar_opt = NULL; #endif 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)) || ipv6_addr_is_multicast(&nbr_sol->tgt) || (ipv6_addr_is_unspecified(&ipv6->src) && ipv6_addr_is_solicited_node(&ipv6->dst))) { DEBUG("ndp: neighbor solicitation was invalid.\n"); /* 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 ((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; } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER sl2a_opt = opt; break; case NDP_OPT_AR: /* actually handling at the end of the function (see below) */ ar_opt = (sixlowpan_nd_opt_ar_t *)opt; #endif break; default: /* silently discard all other options */ break; } opt_offset += (opt->len * 8); sicmpv6_size -= (opt->len * 8); #if ENABLE_DEBUG if (sicmpv6_size < 0) { DEBUG("ndp: Option parsing out of sync.\n"); } #endif } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface); assert(ipv6_iface != NULL); if ((sl2a_opt != NULL) && (ar_opt != NULL) && (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { uint8_t status = gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6, nbr_sol->type, &ipv6->src, ar_opt, l2src, l2src_len); /* check for multihop DAD return */ nbr_adv_opts = gnrc_sixlowpan_nd_opt_ar_build(status, GNRC_SIXLOWPAN_ND_AR_LTIME, &ar_opt->eui64, NULL); if (status == 0) { memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t)); } else { /* see https://tools.ietf.org/html/rfc6775#section-6.5.2 */ eui64_t iid; ieee802154_get_iid(&iid, ar_opt->eui64.uint8, sizeof(eui64_t)); ipv6_addr_set_iid(&nbr_adv_dst, iid.uint64.u64); ipv6_addr_set_link_local_prefix(&nbr_adv_dst); } } else { /* gnrc_sixlowpan_nd_opt_ar_handle updates neighbor cache */ _stale_nc(iface, &ipv6->src, l2src, l2src_len); memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t)); } #else _stale_nc(iface, &ipv6->src, l2src, l2src_len); memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t)); #endif gnrc_ndp_internal_send_nbr_adv(iface, tgt, &nbr_adv_dst, ipv6_addr_is_multicast(&ipv6->dst), nbr_adv_opts); }