ipv6_addr_t *gnrc_ndp_internal_default_router(void) { gnrc_ipv6_nc_t *router = gnrc_ipv6_nc_get_next_router(NULL); /* first look if there is any reachable router */ while (router != NULL) { if ((gnrc_ipv6_nc_get_state(router) != GNRC_IPV6_NC_STATE_INCOMPLETE) && (gnrc_ipv6_nc_get_state(router) != GNRC_IPV6_NC_STATE_UNREACHABLE)) { _last_router = NULL; return &router->ipv6_addr; } router = gnrc_ipv6_nc_get_next_router(router); } /* else take the first one, but keep round-robin in further selections */ router = gnrc_ipv6_nc_get_next_router(_last_router); if (router == NULL) { /* end of router list or there is none => wrap around */ router = gnrc_ipv6_nc_get_next_router(router); if (router == NULL) { /* still nothing found => no router in list */ return NULL; } } _last_router = router; return &router->ipv6_addr; }
gnrc_ipv6_nc_t *gnrc_ipv6_nc_still_reachable(const ipv6_addr_t *ipv6_addr) { gnrc_ipv6_nc_t *entry = gnrc_ipv6_nc_get(KERNEL_PID_UNDEF, ipv6_addr); if (entry == NULL) { DEBUG("ipv6_nc: No entry found for %s\n", ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str))); return NULL; } if ((gnrc_ipv6_nc_get_state(entry) != GNRC_IPV6_NC_STATE_INCOMPLETE) && (gnrc_ipv6_nc_get_state(entry) != GNRC_IPV6_NC_STATE_UNMANAGED)) { #if defined(MODULE_GNRC_IPV6_NETIF) && defined(MODULE_VTIMER) && defined(MODULE_GNRC_IPV6) gnrc_ipv6_netif_t *iface = gnrc_ipv6_netif_get(entry->iface); timex_t t = iface->reach_time; vtimer_remove(&entry->nbr_sol_timer); vtimer_set_msg(&entry->nbr_sol_timer, t, gnrc_ipv6_pid, GNRC_NDP_MSG_NC_STATE_TIMEOUT, entry); #endif DEBUG("ipv6_nc: Marking entry %s as reachable\n", ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str))); entry->flags &= ~(GNRC_IPV6_NC_STATE_MASK >> GNRC_IPV6_NC_STATE_POS); entry->flags |= (GNRC_IPV6_NC_STATE_REACHABLE >> GNRC_IPV6_NC_STATE_POS); }
/* 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) { #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER /* tentative type see https://tools.ietf.org/html/rfc6775#section-6.3 */ gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface); if ((ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { if ((nc_entry = gnrc_ipv6_nc_add(iface, ipaddr, l2addr, (uint16_t)l2addr_len, GNRC_IPV6_NC_STATE_STALE | GNRC_IPV6_NC_TYPE_TENTATIVE)) != NULL) { xtimer_set_msg(&nc_entry->type_timeout, (GNRC_SIXLOWPAN_ND_TENTATIVE_NCE_LIFETIME * SEC_IN_USEC), &nc_entry->type_timeout_msg, gnrc_ipv6_pid); } return; } #endif gnrc_ipv6_nc_add(iface, ipaddr, l2addr, (uint16_t)l2addr_len, GNRC_IPV6_NC_STATE_STALE); } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER /* unreachable set in gnrc_ndp_retrans_nbr_sol() will just be staled */ else if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_UNREACHABLE) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); } #endif 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_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, ndp_nbr_adv_t *nbr_adv, size_t icmpv6_size) { uint16_t opt_offset = 0; uint8_t *buf = ((uint8_t *)nbr_adv) + sizeof(ndp_nbr_adv_t); int l2tgt_len = 0; uint8_t l2tgt[GNRC_IPV6_NC_L2_ADDR_MAX]; int sicmpv6_size = (int)icmpv6_size; gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt); gnrc_pktsnip_t *netif; gnrc_netif_hdr_t *netif_hdr = NULL; DEBUG("ndp: received neighbor advertisement (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_adv->tgt, sizeof(addr_str))); /* check validity */ if ((ipv6->hl != 255) || (nbr_adv->code != 0) || (icmpv6_size < sizeof(ndp_nbr_adv_t)) || ipv6_addr_is_multicast(&nbr_adv->tgt)) { DEBUG("ndp: neighbor advertisement was invalid.\n"); /* ipv6 releases */ return; } if (nc_entry == NULL) { /* see https://tools.ietf.org/html/rfc4861#section-7.2.5 */ DEBUG("ndp: no neighbor cache entry found for advertisement's target\n"); /* ipv6 releases */ return; } sicmpv6_size -= sizeof(ndp_nbr_adv_t); while (sicmpv6_size > 0) { ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset); switch (opt->type) { case NDP_OPT_TL2A: if ((l2tgt_len = gnrc_ndp_internal_tl2a_opt_handle(pkt, ipv6, nbr_adv->type, opt, l2tgt)) < 0) { /* invalid target link-layer address option */ return; } break; #ifdef MODULE_GNRC_SIXLOWPAN_ND case NDP_OPT_AR: /* address registration option is always ignored when invalid */ gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6, nbr_adv->type, &nbr_adv->tgt, (sixlowpan_nd_opt_ar_t *)opt, NULL, 0); break; #endif 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 } LL_SEARCH_SCALAR(pkt, netif, type, GNRC_NETTYPE_NETIF); if (netif != NULL) { netif_hdr = netif->data; } if (l2tgt_len != -ENOTSUP) { #ifdef MODULE_GNRC_SIXLOWPAN_ND /* check if entry wasn't removed by ARO, ideally there should not be any TL2A in here */ nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt); if (nc_entry == NULL) { return; } #endif if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) { 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 state of neighbor as router in FIB? */ } #ifdef MODULE_GNRC_NDP_NODE gnrc_pktqueue_t *queued_pkt; while ((queued_pkt = gnrc_pktqueue_remove_head(&nc_entry->pkts)) != NULL) { if (gnrc_netapi_send(gnrc_ipv6_pid, queued_pkt->pkt) < 1) { DEBUG("ndp: unable to send queued packet\n"); gnrc_pktbuf_release(queued_pkt->pkt); } queued_pkt->pkt = NULL; } #endif } 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); } } } return; }
gnrc_ipv6_nc_t *gnrc_ipv6_nc_add(kernel_pid_t iface, const ipv6_addr_t *ipv6_addr, const void *l2_addr, size_t l2_addr_len, uint8_t flags) { gnrc_ipv6_nc_t *free_entry = NULL; if (ipv6_addr == NULL) { DEBUG("ipv6_nc: address was NULL\n"); return NULL; } if ((l2_addr_len > GNRC_IPV6_NC_L2_ADDR_MAX) || ipv6_addr_is_unspecified(ipv6_addr)) { DEBUG("ipv6_nc: invalid parameters\n"); return NULL; } for (int i = 0; i < GNRC_IPV6_NC_SIZE; i++) { if (ipv6_addr_equal(&(ncache[i].ipv6_addr), ipv6_addr)) { DEBUG("ipv6_nc: Address %s already registered.\n", ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str))); if ((l2_addr != NULL) && (l2_addr_len > 0)) { DEBUG("ipv6_nc: Update to L2 address %s", gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), l2_addr, l2_addr_len)); memcpy(&(ncache[i].l2_addr), l2_addr, l2_addr_len); ncache[i].l2_addr_len = l2_addr_len; ncache[i].flags = flags; DEBUG(" with flags = 0x%0x\n", flags); } return &ncache[i]; } if (ipv6_addr_is_unspecified(&(ncache[i].ipv6_addr)) && !free_entry) { /* found the first free entry */ free_entry = &ncache[i]; } } if (!free_entry) { /* reached end of NC without finding updateable or free entry */ DEBUG("ipv6_nc: neighbor cache full.\n"); return NULL; } /* Otherwise, fill free entry with your fresh information */ free_entry->iface = iface; #ifdef MODULE_GNRC_NDP_NODE free_entry->pkts = NULL; #endif memcpy(&(free_entry->ipv6_addr), ipv6_addr, sizeof(ipv6_addr_t)); DEBUG("ipv6_nc: Register %s for interface %" PRIkernel_pid, ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), iface); if ((l2_addr != NULL) && (l2_addr_len > 0)) { DEBUG(" to L2 address %s", gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), l2_addr, l2_addr_len)); memcpy(&(free_entry->l2_addr), l2_addr, l2_addr_len); free_entry->l2_addr_len = l2_addr_len; } free_entry->flags = flags; DEBUG(" with flags = 0x%0x\n", flags); if (gnrc_ipv6_nc_get_state(free_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) { DEBUG("ipv6_nc: Set remaining probes to %" PRIu8 "\n", (uint8_t) GNRC_NDP_MAX_MC_NBR_SOL_NUMOF); free_entry->probes_remaining = GNRC_NDP_MAX_MC_NBR_SOL_NUMOF; } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER free_entry->type_timeout_msg.type = GNRC_SIXLOWPAN_ND_MSG_AR_TIMEOUT; free_entry->type_timeout_msg.content.ptr = (char *) free_entry; #endif free_entry->rtr_timeout_msg.type = GNRC_NDP_MSG_RTR_TIMEOUT; free_entry->rtr_timeout_msg.content.ptr = (char *) free_entry; #if defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER) free_entry->rtr_adv_msg.type = GNRC_NDP_MSG_RTR_ADV_DELAY; free_entry->rtr_adv_msg.content.ptr = (char *) free_entry; #endif free_entry->nbr_sol_msg.content.ptr = (char *) free_entry; return free_entry; }
kernel_pid_t gnrc_sixlowpan_nd_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, kernel_pid_t iface, ipv6_addr_t *dst) { ipv6_addr_t *next_hop = NULL; gnrc_ipv6_nc_t *nc_entry = 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 = 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 ((next_hop == NULL) && !ipv6_addr_is_link_local(dst)) { size_t next_hop_size = sizeof(ipv6_addr_t); uint32_t next_hop_flags = 0; if ((next_hop == 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 = &next_hop_actual; } } #endif #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER /* next hop determination: https://tools.ietf.org/html/rfc6775#section-6.5.4 */ nc_entry = gnrc_ipv6_nc_get(iface, dst); /* if NCE found */ if (nc_entry != NULL) { gnrc_ipv6_netif_t *ipv6_if = gnrc_ipv6_netif_get(nc_entry->iface); /* and interface is not 6LoWPAN */ if (!(ipv6_if->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) || /* or entry is registered */ (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_REGISTERED)) { next_hop = dst; } } #endif /* next hop determination according to: https://tools.ietf.org/html/rfc6775#section-5.6 */ if ((next_hop == NULL) && ipv6_addr_is_link_local(dst)) { /* prefix is "on-link" */ /* multicast is not handled here anyway so we don't need to check that */ next_hop = dst; } else if (next_hop == NULL) { /* prefix is off-link */ next_hop = gnrc_ndp_internal_default_router(); } /* address resolution of next_hop: https://tools.ietf.org/html/rfc6775#section-5.7 */ if ((nc_entry == NULL) || (next_hop != dst)) { /* get if not gotten from previous check */ nc_entry = gnrc_ipv6_nc_get(iface, next_hop); } /* If a (non-tentative) NCE for this destination exist, we can use even for * link-local addresses. This should be only the case for 6LBRs. */ if ((ipv6_addr_is_link_local(next_hop)) && ((nc_entry == NULL) || (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_TENTATIVE))) { /* in case of a border router there is no sensible way for address resolution * if the interface is not given */ #ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER /* if no interface is specified it is impossible to resolve the * link-layer address for a link-local address on a 6LBR */ if (iface == KERNEL_PID_UNDEF) { return KERNEL_PID_UNDEF; } #endif kernel_pid_t ifs[GNRC_NETIF_NUMOF]; size_t ifnum = gnrc_netif_get(ifs); /* we don't need address resolution, the EUI-64 is in next_hop's IID */ *l2addr_len = sizeof(eui64_t); memcpy(l2addr, &next_hop->u8[8], sizeof(eui64_t)); _revert_iid(l2addr); if (iface == KERNEL_PID_UNDEF) { for (unsigned i = 0; i < ifnum; i++) { gnrc_ipv6_netif_t *ipv6_if = gnrc_ipv6_netif_get(ifs[i]); if ((ipv6_if != NULL) && (ipv6_if->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { /* always take the first 6LoWPAN interface we can find */ return ifs[i]; } } } return iface; } if ((nc_entry == NULL) || (!gnrc_ipv6_nc_is_reachable(nc_entry)) || (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_TENTATIVE)) { return KERNEL_PID_UNDEF; } else { 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; 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); } } return nc_entry->iface; }