kernel_pid_t gnrc_ipv6_netif_find_by_addr(ipv6_addr_t **out, const ipv6_addr_t *addr) { for (int i = 0; i < GNRC_NETIF_NUMOF; i++) { if (out != NULL) { *out = gnrc_ipv6_netif_find_addr(ipv6_ifs[i].pid, addr); if (*out != NULL) { DEBUG("ipv6 netif: Found %s on interface %" PRIkernel_pid "\n", ipv6_addr_to_str(addr_str, *out, sizeof(addr_str)), ipv6_ifs[i].pid); return ipv6_ifs[i].pid; } } else { if (gnrc_ipv6_netif_find_addr(ipv6_ifs[i].pid, addr) != NULL) { DEBUG("ipv6 netif: Found :: on interface %" PRIkernel_pid "\n", ipv6_ifs[i].pid); return ipv6_ifs[i].pid; } } } if (out != NULL) { *out = NULL; } return KERNEL_PID_UNDEF; }
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, 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; } 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); }
/* functions for receiving */ static inline bool _pkt_not_for_me(kernel_pid_t *iface, ipv6_hdr_t *hdr) { if (ipv6_addr_is_loopback(&hdr->dst)) { return false; } else if (*iface == KERNEL_PID_UNDEF) { *iface = gnrc_ipv6_netif_find_by_addr(NULL, &hdr->dst); return (*iface == KERNEL_PID_UNDEF); } else { return (gnrc_ipv6_netif_find_addr(*iface, &hdr->dst) == NULL); } }
bool gnrc_ndp_internal_pi_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type, ndp_opt_pi_t *pi_opt) { ipv6_addr_t *prefix; gnrc_ipv6_netif_addr_t *netif_addr; if ((pi_opt->len != NDP_OPT_MTU_LEN)) { DEBUG("ndp: invalid MTU option received\n"); return false; } if (icmpv6_type != ICMPV6_RTR_ADV || ipv6_addr_is_link_local(&pi_opt->prefix)) { /* else discard silently */ return true; } prefix = gnrc_ipv6_netif_find_addr(iface, &pi_opt->prefix); if (((prefix == NULL) || (gnrc_ipv6_netif_addr_get(prefix)->prefix_len != pi_opt->prefix_len)) && (pi_opt->valid_ltime.u32 != 0)) { prefix = gnrc_ipv6_netif_add_addr(iface, &pi_opt->prefix, pi_opt->prefix_len, pi_opt->flags & NDP_OPT_PI_FLAGS_MASK); if (prefix == NULL) { DEBUG("ndp: could not add prefix to interface %d\n", iface); return false; } } netif_addr = gnrc_ipv6_netif_addr_get(prefix); if (pi_opt->valid_ltime.u32 == 0) { if (prefix != NULL) { gnrc_ipv6_netif_remove_addr(iface, &netif_addr->addr); } return true; } netif_addr->valid = byteorder_ntohl(pi_opt->valid_ltime); netif_addr->preferred = byteorder_ntohl(pi_opt->pref_ltime); vtimer_remove(&netif_addr->valid_timeout); if (netif_addr->valid != UINT32_MAX) { vtimer_set_msg(&netif_addr->valid_timeout, timex_set(byteorder_ntohl(pi_opt->valid_ltime), 0), thread_getpid(), GNRC_NDP_MSG_ADDR_TIMEOUT, &netif_addr->addr); } /* TODO: preferred lifetime for address auto configuration */ /* on-link flag MUST stay set if it was */ netif_addr->flags &= ~NDP_OPT_PI_FLAGS_A; netif_addr->flags |= (pi_opt->flags & NDP_OPT_PI_FLAGS_MASK); return true; }
/* functions for receiving */ static inline bool _pkt_not_for_me(kernel_pid_t *iface, ipv6_hdr_t *hdr) { if (ipv6_addr_is_loopback(&hdr->dst)) { return false; } else if ((!ipv6_addr_is_link_local(&hdr->dst)) || (*iface == KERNEL_PID_UNDEF)) { kernel_pid_t if_pid = gnrc_ipv6_netif_find_by_addr(NULL, &hdr->dst); if (*iface == KERNEL_PID_UNDEF) { *iface = if_pid; /* Use original interface for reply if * existent */ } return (if_pid == KERNEL_PID_UNDEF); } else { return (gnrc_ipv6_netif_find_addr(*iface, &hdr->dst) == NULL); } }
static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr) { kernel_pid_t iface = KERNEL_PID_UNDEF; gnrc_pktsnip_t *ipv6, *payload; ipv6_addr_t *tmp; ipv6_hdr_t *hdr; /* get IPv6 snip and (if present) generic interface header */ if (pkt->type == GNRC_NETTYPE_NETIF) { /* If there is already a netif header (routing protocols and * neighbor discovery might add them to preset sending interface) */ iface = ((gnrc_netif_hdr_t *)pkt->data)->if_pid; /* seize payload as temporary variable */ ipv6 = gnrc_pktbuf_start_write(pkt); /* write protect for later removal * in _send_unicast() */ if (ipv6 == NULL) { DEBUG("ipv6: unable to get write access to netif header, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } pkt = ipv6; /* Reset pkt from temporary variable */ ipv6 = pkt->next; } else { ipv6 = pkt; } /* seize payload as temporary variable */ payload = gnrc_pktbuf_start_write(ipv6); if (payload == NULL) { DEBUG("ipv6: unable to get write access to IPv6 header, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } if (ipv6 != pkt) { /* in case packet has netif header */ pkt->next = payload;/* pkt is already write-protected so we can do that */ } else { pkt = payload; /* pkt is the IPv6 header so we just write-protected it */ } ipv6 = payload; /* Reset ipv6 from temporary variable */ hdr = ipv6->data; payload = ipv6->next; if (ipv6_addr_is_multicast(&hdr->dst)) { _send_multicast(iface, pkt, ipv6, payload, prep_hdr); } else if ((ipv6_addr_is_loopback(&hdr->dst)) || /* dst is loopback address */ ((iface == KERNEL_PID_UNDEF) && /* or dst registered to any local interface */ ((iface = gnrc_ipv6_netif_find_by_addr(&tmp, &hdr->dst)) != KERNEL_PID_UNDEF)) || ((iface != KERNEL_PID_UNDEF) && /* or dst registered to given interface */ (gnrc_ipv6_netif_find_addr(iface, &hdr->dst) != NULL))) { uint8_t *rcv_data; gnrc_pktsnip_t *ptr = ipv6, *rcv_pkt; if (prep_hdr) { if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) { /* error on filling up header */ gnrc_pktbuf_release(pkt); return; } } rcv_pkt = gnrc_pktbuf_add(NULL, NULL, gnrc_pkt_len(ipv6), GNRC_NETTYPE_IPV6); if (rcv_pkt == NULL) { DEBUG("ipv6: error on generating loopback packet\n"); gnrc_pktbuf_release(pkt); return; } rcv_data = rcv_pkt->data; /* "reverse" packet (by making it one snip as if received from NIC) */ while (ptr != NULL) { memcpy(rcv_data, ptr->data, ptr->size); rcv_data += ptr->size; ptr = ptr->next; } gnrc_pktbuf_release(pkt); DEBUG("ipv6: packet is addressed to myself => loopback\n"); if (gnrc_netapi_receive(gnrc_ipv6_pid, rcv_pkt) < 1) { DEBUG("ipv6: unable to deliver packet\n"); gnrc_pktbuf_release(rcv_pkt); } } else { 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); if (iface == KERNEL_PID_UNDEF) { DEBUG("ipv6: error determining next hop's link layer address\n"); gnrc_pktbuf_release(pkt); return; } if (prep_hdr) { if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) { /* error on filling up header */ gnrc_pktbuf_release(pkt); return; } } _send_unicast(iface, l2addr, l2addr_len, pkt); } }
bool gnrc_ndp_internal_pi_opt_handle(kernel_pid_t iface, uint8_t icmpv6_type, ndp_opt_pi_t *pi_opt) { ipv6_addr_t *prefix; gnrc_ipv6_netif_addr_t *netif_addr; if ((pi_opt->len != NDP_OPT_PI_LEN)) { DEBUG("ndp: invalid PI option received\n"); return false; } if (icmpv6_type != ICMPV6_RTR_ADV || ipv6_addr_is_link_local(&pi_opt->prefix)) { /* else discard silently */ return true; } #ifdef MODULE_GNRC_SIXLOWPAN_ND if ((gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (pi_opt->flags & NDP_OPT_PI_FLAGS_L)) { /* ignore: see https://tools.ietf.org/html/rfc6775#section-5.4 */ return true; } #endif prefix = gnrc_ipv6_netif_find_addr(iface, &pi_opt->prefix); if (((prefix == NULL) || (gnrc_ipv6_netif_addr_get(prefix)->prefix_len != pi_opt->prefix_len)) && (pi_opt->valid_ltime.u32 != 0)) { ipv6_addr_t pref_addr; if ((gnrc_netapi_get(iface, NETOPT_IPV6_IID, 0, &pref_addr.u64[1], sizeof(eui64_t)) < 0)) { DEBUG("ndp: could not get IID from interface %d\n", iface); return false; } ipv6_addr_init_prefix(&pref_addr, &pi_opt->prefix, pi_opt->prefix_len); prefix = gnrc_ipv6_netif_add_addr(iface, &pref_addr, pi_opt->prefix_len, pi_opt->flags & NDP_OPT_PI_FLAGS_MASK); if (prefix == NULL) { DEBUG("ndp: could not add prefix to interface %d\n", iface); return false; } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER gnrc_sixlowpan_nd_router_set_rtr_adv(gnrc_ipv6_netif_get(iface), true); #endif } netif_addr = gnrc_ipv6_netif_addr_get(prefix); if (pi_opt->valid_ltime.u32 == 0) { if (prefix != NULL) { gnrc_ipv6_netif_remove_addr(iface, &netif_addr->addr); } return true; } netif_addr->valid = byteorder_ntohl(pi_opt->valid_ltime); netif_addr->preferred = byteorder_ntohl(pi_opt->pref_ltime); if (netif_addr->valid != UINT32_MAX) { xtimer_set_msg(&netif_addr->valid_timeout, (byteorder_ntohl(pi_opt->valid_ltime) * SEC_IN_USEC), &netif_addr->valid_timeout_msg, thread_getpid()); } /* TODO: preferred lifetime for address auto configuration */ /* on-link flag MUST stay set if it was */ netif_addr->flags &= ~NDP_OPT_PI_FLAGS_A; netif_addr->flags |= (pi_opt->flags & NDP_OPT_PI_FLAGS_MASK); return true; }
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); }