gnrc_sixlowpan_nd_router_abr_t *gnrc_sixlowpan_nd_router_abr_get(void) { if (ipv6_addr_is_unspecified(&_abrs[0].addr)) { return NULL; } return _abrs; }
int gnrc_netif_ipv6_group_join_internal(gnrc_netif_t *netif, const ipv6_addr_t *addr) { unsigned idx = UINT_MAX; gnrc_netif_acquire(netif); for (unsigned i = 0; i < GNRC_NETIF_IPV6_GROUPS_NUMOF; i++) { if (ipv6_addr_equal(&netif->ipv6.groups[i], addr)) { gnrc_netif_release(netif); return i; } if ((idx == UINT_MAX) && (ipv6_addr_is_unspecified(&netif->ipv6.groups[i]))) { idx = i; } } if (idx == UINT_MAX) { gnrc_netif_release(netif); return -ENOMEM; } memcpy(&netif->ipv6.groups[idx], addr, sizeof(netif->ipv6.groups[idx])); /* TODO: * - MLD action */ gnrc_netif_release(netif); return idx; }
bool gnrc_conn6_set_local_addr(uint8_t *conn_addr, const ipv6_addr_t *addr) { ipv6_addr_t *tmp; if (!ipv6_addr_is_unspecified(addr) && !ipv6_addr_is_loopback(addr) && gnrc_ipv6_netif_find_by_addr(&tmp, addr) == KERNEL_PID_UNDEF) { return false; } else if (ipv6_addr_is_loopback(addr) || ipv6_addr_is_unspecified(addr)) { ipv6_addr_set_unspecified((ipv6_addr_t *)conn_addr); } else { memcpy(conn_addr, addr, sizeof(ipv6_addr_t)); } return true; }
bool ng_ndp_internal_sl2a_opt_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, ng_ndp_opt_t *sl2a_opt) { ng_ipv6_nc_t *nc_entry = NULL; uint8_t 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; } while (pkt) { if (pkt->type == NG_NETTYPE_NETIF) { ng_netif_hdr_t *hdr = pkt->data; sl2a_len = hdr->src_l2addr_len; break; } pkt = pkt->next; } if (sl2a_len == 0) { /* in case there was no source address in l2 */ sl2a_len = (sl2a_opt->len / 8) - sizeof(ng_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", ng_netif_addr_to_str(addr_str, sizeof(addr_str), sl2a, sl2a_len)); switch (icmpv6_type) { case ICMPV6_NBR_SOL: nc_entry = ng_ipv6_nc_get(iface, &ipv6->src); 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); ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); } } else { ng_ipv6_nc_add(iface, &ipv6->src, sl2a, sl2a_len, NG_IPV6_NC_STATE_STALE); } return true; default: /* wrong encapsulating message: silently discard */ DEBUG("ndp: silently discard sl2a_opt for ICMPv6 message type %" PRIu8 "\n", icmpv6_type); return true; } }
static int _fill_ipv6_hdr(kernel_pid_t iface, gnrc_pktsnip_t *ipv6, gnrc_pktsnip_t *payload) { int res; ipv6_hdr_t *hdr = ipv6->data; hdr->len = byteorder_htons(gnrc_pkt_len(payload)); DEBUG("ipv6: set payload length to %u (network byteorder %04" PRIx16 ")\n", (unsigned) gnrc_pkt_len(payload), hdr->len.u16); /* check if e.g. extension header was not already marked */ if (hdr->nh == PROTNUM_RESERVED) { hdr->nh = gnrc_nettype_to_protnum(payload->type); /* if still reserved: mark no next header */ if (hdr->nh == PROTNUM_RESERVED) { hdr->nh = PROTNUM_IPV6_NONXT; } } DEBUG("ipv6: set next header to %u\n", hdr->nh); if (hdr->hl == 0) { if (iface == KERNEL_PID_UNDEF) { hdr->hl = GNRC_IPV6_NETIF_DEFAULT_HL; } else { hdr->hl = gnrc_ipv6_netif_get(iface)->cur_hl; } } if (ipv6_addr_is_unspecified(&hdr->src)) { if (ipv6_addr_is_loopback(&hdr->dst)) { ipv6_addr_set_loopback(&hdr->src); } else { ipv6_addr_t *src = gnrc_ipv6_netif_find_best_src_addr(iface, &hdr->dst, false); if (src != NULL) { DEBUG("ipv6: set packet source to %s\n", ipv6_addr_to_str(addr_str, src, sizeof(addr_str))); memcpy(&hdr->src, src, sizeof(ipv6_addr_t)); } /* Otherwise leave unspecified */ } } DEBUG("ipv6: calculate checksum for upper header.\n"); if ((res = gnrc_netreg_calc_csum(payload, ipv6)) < 0) { if (res != -ENOENT) { /* if there is no checksum we are okay */ DEBUG("ipv6: checksum calculation failed.\n"); return res; } } return 0; }
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); }
static uint8_t _find_by_prefix_unsafe(ipv6_addr_t **res, gnrc_ipv6_netif_t *iface, const ipv6_addr_t *addr, uint8_t *only) { uint8_t best_match = 0; for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { uint8_t match; if ((only != NULL) && !(bf_isset(only, i))) { continue; } if (((only != NULL) && gnrc_ipv6_netif_addr_is_non_unicast(&(iface->addrs[i].addr))) || ipv6_addr_is_unspecified(&(iface->addrs[i].addr))) { continue; } match = ipv6_addr_match_prefix(&(iface->addrs[i].addr), addr); if ((only == NULL) && !ipv6_addr_is_multicast(addr) && (match < iface->addrs[i].prefix_len)) { /* match but not of same subnet */ continue; } if (match > best_match) { if (res != NULL) { *res = &(iface->addrs[i].addr); } best_match = match; } } #if ENABLE_DEBUG if (*res != NULL) { DEBUG("ipv6 netif: Found %s on interface %" PRIkernel_pid " matching ", ipv6_addr_to_str(addr_str, *res, sizeof(addr_str)), iface->pid); DEBUG("%s by %" PRIu8 " bits (used as source address = %s)\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), best_match, (only != NULL) ? "true" : "false"); } else { DEBUG("ipv6 netif: Did not found any address on interface %" PRIkernel_pid " matching %s (used as source address = %s)\n", iface->pid, ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), (only != NULL) ? "true" : "false"); } #endif return best_match; }
static ipv6_addr_t *_add_addr_to_entry(gnrc_ipv6_netif_t *entry, const ipv6_addr_t *addr, uint8_t prefix_len, uint8_t flags) { gnrc_ipv6_netif_addr_t *tmp_addr = NULL; for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { if (ipv6_addr_equal(&(entry->addrs[i].addr), addr)) { return &(entry->addrs[i].addr); } if (ipv6_addr_is_unspecified(&(entry->addrs[i].addr)) && !tmp_addr) { tmp_addr = &(entry->addrs[i]); } } if (!tmp_addr) { DEBUG("ipv6 netif: couldn't add %s/%" PRIu8 " to interface %" PRIkernel_pid "\n: No space left.", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), prefix_len, entry->pid); return NULL; } memcpy(&(tmp_addr->addr), addr, sizeof(ipv6_addr_t)); DEBUG("ipv6 netif: Added %s/%" PRIu8 " to interface %" PRIkernel_pid "\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), prefix_len, entry->pid); tmp_addr->prefix_len = prefix_len; tmp_addr->flags = flags; if (ipv6_addr_is_multicast(addr)) { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST; } else { ipv6_addr_t sol_node; if (!ipv6_addr_is_link_local(addr)) { /* add also corresponding link-local address */ ipv6_addr_t ll_addr; ll_addr.u64[1] = addr->u64[1]; ipv6_addr_set_link_local_prefix(&ll_addr); _add_addr_to_entry(entry, &ll_addr, 64, flags | GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK); } else { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK; } ipv6_addr_set_solicited_nodes(&sol_node, addr); _add_addr_to_entry(entry, &sol_node, IPV6_ADDR_BIT_LEN, 0); } return &(tmp_addr->addr); }
gnrc_ipv6_nc_t *_find_free_entry(void) { for (int i = 0; i < GNRC_IPV6_NC_SIZE; i++) { if (ipv6_addr_is_unspecified(&(ncache[i].ipv6_addr))) { return ncache + i; } } return NULL; }
static int _addr_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr) { if (!ipv6_addr_is_unspecified(addr)) { for (unsigned i = 0; i < GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) { if (ipv6_addr_equal(&netif->ipv6.addrs[i], addr)) { return i; } } } return -1; }
static void test_ipv6_netif_find_best_src_addr__multicast_input(void) { ipv6_addr_t mc_addr = IPV6_ADDR_ALL_ROUTERS_LINK_LOCAL; ipv6_addr_t *out = NULL; /* Adds DEFAULT_TEST_NETIF as interface and to it fe80::1, fe80::2 and ff02::1 */ test_ipv6_netif_find_best_src_addr__success(); TEST_ASSERT_NOT_NULL((out = ng_ipv6_netif_find_best_src_addr(DEFAULT_TEST_NETIF, &mc_addr))); TEST_ASSERT_EQUAL_INT(false, ipv6_addr_equal(&mc_addr, out)); TEST_ASSERT_EQUAL_INT(false, ipv6_addr_is_unspecified(out)); }
/** * @brief selects potential source address candidates * @see <a href="http://tools.ietf.org/html/rfc6724#section-4"> * RFC6724, section 4 * </a> * @param[in] iface the interface used for sending * @param[in] dst the destination address * @param[out] candidate_set a bitfield representing all addresses * configured to @p iface, potential candidates * will be marked as 1 * * @return false if no candidates were found * @return true otherwise * * @pre the interface entry and its set of addresses must not be changed during * runtime of this function */ static int _create_candidate_set(gnrc_ipv6_netif_t *iface, const ipv6_addr_t *dst, uint8_t *candidate_set, bool link_local_only) { int res = -1; DEBUG("gathering candidates\n"); /* currently this implementation supports only addresses as source address * candidates assigned to this interface. Thus we assume all addresses to be * on interface @p iface */ (void) dst; for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { gnrc_ipv6_netif_addr_t *iter = &(iface->addrs[i]); DEBUG("Checking address: %s\n", ipv6_addr_to_str(addr_str, &(iter->addr), sizeof(addr_str))); /* "In any case, multicast addresses and the unspecified address MUST NOT * be included in a candidate set." */ if (ipv6_addr_is_multicast(&(iter->addr)) || ipv6_addr_is_unspecified(&(iter->addr))) { continue; } /* Check if we only want link local addresses */ if (link_local_only && !ipv6_addr_is_link_local(&(iter->addr))) { continue; } /* "For all multicast and link-local destination addresses, the set of * candidate source addresses MUST only include addresses assigned to * interfaces belonging to the same link as the outgoing interface." * * "For site-local unicast destination addresses, the set of candidate * source addresses MUST only include addresses assigned to interfaces * belonging to the same site as the outgoing interface." * -> we should also be fine, since we're only iterating addresses of * the sending interface */ /* put all other addresses into the candidate set */ DEBUG("add to candidate set\n"); bf_set(candidate_set, i); res = i; } return res; }
void uhcp_handle_prefix(uint8_t *prefix, uint8_t prefix_len, uint16_t lifetime, uint8_t *src, uhcp_iface_t iface) { (void)prefix_len; (void)lifetime; (void)src; eui64_t iid; if (!gnrc_wireless_interface) { LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): received prefix, but don't know any wireless interface\n"); return; } if ((kernel_pid_t)iface != gnrc_border_interface) { LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): received prefix from unexpected interface\n"); return; } if (gnrc_netapi_get(gnrc_wireless_interface, NETOPT_IPV6_IID, 0, &iid, sizeof(eui64_t)) >= 0) { ipv6_addr_set_aiid((ipv6_addr_t*)prefix, iid.uint8); } else { LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): cannot get IID of wireless interface\n"); return; } if (ipv6_addr_equal(&_prefix, (ipv6_addr_t*)prefix)) { LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): got same prefix again\n"); return; } gnrc_ipv6_netif_add_addr(gnrc_wireless_interface, (ipv6_addr_t*)prefix, 64, GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST | GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_AUTO); gnrc_ipv6_netif_remove_addr(gnrc_wireless_interface, &_prefix); print_str("gnrc_uhcpc: uhcp_handle_prefix(): configured new prefix "); ipv6_addr_print((ipv6_addr_t*)prefix); puts("/64"); if (!ipv6_addr_is_unspecified(&_prefix)) { gnrc_ipv6_netif_remove_addr(gnrc_wireless_interface, &_prefix); print_str("gnrc_uhcpc: uhcp_handle_prefix(): removed old prefix "); ipv6_addr_print(&_prefix); puts("/64"); } memcpy(&_prefix, prefix, 16); }
static gnrc_sixlowpan_nd_router_abr_t *_get_abr(ipv6_addr_t *addr) { gnrc_sixlowpan_nd_router_abr_t *abr = NULL; for (int i = 0; i < GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF; i++) { if (ipv6_addr_equal(&_abrs[i].addr, addr)) { return &_abrs[i]; } if ((abr == NULL) && ipv6_addr_is_unspecified(&_abrs[i].addr)) { abr = &_abrs[i]; } } return abr; }
void gnrc_sixlowpan_nd_uc_rtr_sol(gnrc_ipv6_nc_t *nce) { assert(gnrc_ipv6_netif_get(nce->iface)->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN); /* neighbor is not a router anymore */ if (!(nce->flags & GNRC_IPV6_NC_IS_ROUTER) || ipv6_addr_is_unspecified(&nce->ipv6_addr)) { /* and there are no routers anymore */ if (gnrc_ipv6_nc_get_next_router(NULL) == NULL) { /* start search for routers */ gnrc_sixlowpan_nd_init(gnrc_ipv6_netif_get(nce->iface)); } /* otherwise ignore this call */ return; } /* next RS is rescheduled by RA handle function */ gnrc_ndp_internal_send_rtr_sol(nce->iface, &nce->ipv6_addr); }
static void *_ipv6_fwd_eventloop(void *arg) { (void)arg; msg_t msg, msg_q[8]; gnrc_netreg_entry_t me_reg; msg_init_queue(msg_q, 8); me_reg.demux_ctx = GNRC_NETREG_DEMUX_CTX_ALL; me_reg.pid = thread_getpid(); gnrc_netreg_register(GNRC_NETTYPE_SIXLOWPAN, &me_reg); while(1) { msg_receive(&msg); gnrc_pktsnip_t *pkt = msg.content.ptr; if(msg.type == GNRC_NETAPI_MSG_TYPE_SND) { gnrc_pktsnip_t *ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); ipv6 = ipv6->data; ipv6_hdr_t *ipv6_hdr =(ipv6_hdr_t *)ipv6; /* get the first IPv6 interface and prints its address */ kernel_pid_t ifs[GNRC_NETIF_NUMOF]; gnrc_netif_get(ifs); gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(ifs[0]); for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { if ( (!ipv6_addr_is_link_local(&entry->addrs[i].addr)) && (!ipv6_addr_is_link_local(&ipv6_hdr->src)) && (!ipv6_addr_is_link_local(&ipv6_hdr->dst)) && !(entry->addrs[i].flags & GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST) && (!ipv6_addr_is_unspecified(&entry->addrs[i].addr)) ) { if(!ipv6_addr_equal(&entry->addrs[i].addr, &(ipv6_hdr->src))){ char addr_str[IPV6_ADDR_MAX_STR_LEN]; printf("IPv6 ROUTER: forward from src = %s ", ipv6_addr_to_str(addr_str, &(ipv6_hdr->src), sizeof(addr_str)) ); printf("to dst = %s\n",ipv6_addr_to_str(addr_str, &(ipv6_hdr->dst), sizeof(addr_str))); } } } } gnrc_pktbuf_release(pkt); } /* never reached */ return NULL; }
static gnrc_sixlowpan_nd_router_prf_t *_get_free_prefix(ipv6_addr_t *prefix, size_t prefix_len) { gnrc_sixlowpan_nd_router_prf_t *prf = NULL; for (int i = 0; i < GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF; i++) { if ((ipv6_addr_match_prefix(&_prefixes[i].prefix->addr, prefix) >= prefix_len) && (_prefixes[i].prefix->prefix_len == prefix_len)) { return &_prefixes[i]; } if ((prf == NULL) && ipv6_addr_is_unspecified(&_prefixes[i].prefix->addr)) { prf = &_prefixes[i]; } } return prf; }
static bool _pio_from_iface_addr(gnrc_pktsnip_t **res, gnrc_ipv6_netif_addr_t *addr, gnrc_pktsnip_t *next) { if (((addr->prefix_len - 1U) > 127U) && /* 0 < prefix_len < 128 */ !ipv6_addr_is_unspecified(&addr->addr) && !ipv6_addr_is_link_local(&addr->addr) && !gnrc_ipv6_netif_addr_is_non_unicast(&addr->addr)) { DEBUG(" - PIO for %s/%" PRIu8 "\n", ipv6_addr_to_str(addr_str, &addr->addr, sizeof(addr_str)), addr->prefix_len); *res = gnrc_ndp_opt_pi_build(addr->prefix_len, (addr->flags & (GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_AUTO | GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)), addr->valid, addr->preferred, &addr->addr, next); return true; } return false; }
gnrc_ipv6_nc_t *gnrc_ipv6_nc_get_next(gnrc_ipv6_nc_t *prev) { if (prev == NULL) { prev = ncache; } else { prev++; /* get next entry */ } while (prev < (ncache + GNRC_IPV6_NC_SIZE)) { /* while not reached end */ if (!ipv6_addr_is_unspecified(&(prev->ipv6_addr))) { return prev; } prev++; } return NULL; }
ipv6_addr_t *gnrc_ipv6_netif_add_addr(kernel_pid_t pid, const ipv6_addr_t *addr, uint8_t prefix_len, uint8_t flags) { gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(pid); ipv6_addr_t *res; if ((entry == NULL) || (addr == NULL) || (ipv6_addr_is_unspecified(addr)) || ((prefix_len - 1) > 127)) { /* prefix_len < 1 || prefix_len > 128 */ return NULL; } mutex_lock(&entry->mutex); res = _add_addr_to_entry(entry, addr, prefix_len, flags); mutex_unlock(&entry->mutex); return res; }
int gnrc_ndp_internal_tl2a_opt_handle(gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, ndp_opt_t *tl2a_opt, uint8_t *l2addr) { uint8_t tl2a_len = 0; uint8_t *tl2a = (uint8_t *)(tl2a_opt + 1); if ((tl2a_opt->len == 0) || ipv6_addr_is_unspecified(&ipv6->src)) { DEBUG("ndp: invalid target link-layer address option received\n"); return -EINVAL; } switch (icmpv6_type) { case ICMPV6_NBR_ADV: while (pkt) { if (pkt->type == GNRC_NETTYPE_NETIF) { gnrc_netif_hdr_t *hdr = pkt->data; tl2a_len = hdr->src_l2addr_len; break; } pkt = pkt->next; } if (tl2a_len == 0) { /* in case there was no source address in l2 */ tl2a_len = (tl2a_opt->len / 8) - sizeof(ndp_opt_t); /* ignore all zeroes at the end for length */ for (; tl2a[tl2a_len - 1] == 0x00; tl2a_len--); } DEBUG("ndp: received TL2A (link-layer address: %s)\n", gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), tl2a, tl2a_len)); memcpy(l2addr, tl2a, tl2a_len); return (int)tl2a_len; default: /* wrong encapsulating message: silently discard */ DEBUG("ndp: silently discard tl2a_opt for ICMPv6 message type %" PRIu8 "\n", icmpv6_type); return 0; } }
gnrc_ipv6_nc_t *gnrc_ipv6_nc_get(kernel_pid_t iface, const ipv6_addr_t *ipv6_addr) { if ((ipv6_addr == NULL) || (ipv6_addr_is_unspecified(ipv6_addr))) { DEBUG("ipv6_nc: address was NULL or ::\n"); return NULL; } for (int i = 0; i < GNRC_IPV6_NC_SIZE; i++) { if (((ncache[i].iface == KERNEL_PID_UNDEF) || (iface == KERNEL_PID_UNDEF) || (iface == ncache[i].iface)) && ipv6_addr_equal(&(ncache[i].ipv6_addr), ipv6_addr)) { DEBUG("ipv6_nc: Found entry for %s on interface %" PRIkernel_pid " (0 = all interfaces) [%p]\n", ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)), iface, (void *)(ncache + i)); return ncache + i; } } return NULL; }
static void _send_rtr_adv(gnrc_ipv6_netif_t *iface, ipv6_addr_t *dst) { bool fin; uint32_t interval; mutex_lock(&iface->mutex); fin = (iface->adv_ltime == 0); assert((iface->min_adv_int != 0) && (iface->max_adv_int != 0)); interval = genrand_uint32_range(iface->min_adv_int, iface->max_adv_int); if (!fin && !((iface->flags | GNRC_IPV6_NETIF_FLAGS_ROUTER) && (iface->flags | GNRC_IPV6_NETIF_FLAGS_RTR_ADV))) { DEBUG("ndp rtr: interface %" PRIkernel_pid " is not an advertising interface\n", iface->pid); return; } if (iface->rtr_adv_count > 1) { /* regard for off-by-one error */ iface->rtr_adv_count--; if (!fin && (interval > GNRC_NDP_MAX_INIT_RTR_ADV_INT)) { interval = GNRC_NDP_MAX_INIT_RTR_ADV_INT; } } if (!fin || (iface->rtr_adv_count > 1)) { /* regard for off-by-one-error */ /* reset timer for next router advertisement */ xtimer_remove(&iface->rtr_adv_timer); iface->rtr_adv_msg.type = GNRC_NDP_MSG_RTR_ADV_RETRANS; iface->rtr_adv_msg.content.ptr = (char *) iface; xtimer_set_msg(&iface->rtr_adv_timer, interval * SEC_IN_USEC, &iface->rtr_adv_msg, gnrc_ipv6_pid); } mutex_unlock(&iface->mutex); for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { ipv6_addr_t *src = &iface->addrs[i].addr; if (!ipv6_addr_is_unspecified(src) && ipv6_addr_is_link_local(src) && !gnrc_ipv6_netif_addr_is_non_unicast(src)) { /* send one for every link local address (ideally there is only one) */ gnrc_ndp_internal_send_rtr_adv(iface->pid, src, dst, fin); } } }
static void _netif_list(kernel_pid_t dev) { uint8_t hwaddr[MAX_ADDR_LEN]; uint16_t u16; int16_t i16; int res; netopt_state_t state; netopt_enable_t enable; bool linebreak = false; #ifdef MODULE_GNRC_IPV6_NETIF gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(dev); char ipv6_addr[IPV6_ADDR_MAX_STR_LEN]; #endif printf("Iface %2d ", dev); res = gnrc_netapi_get(dev, NETOPT_ADDRESS, 0, hwaddr, sizeof(hwaddr)); if (res >= 0) { char hwaddr_str[res * 3]; printf(" HWaddr: "); printf("%s", gnrc_netif_addr_to_str(hwaddr_str, sizeof(hwaddr_str), hwaddr, res)); printf(" "); } res = gnrc_netapi_get(dev, NETOPT_CHANNEL, 0, &u16, sizeof(u16)); if (res >= 0) { printf(" Channel: %" PRIu16 " ", u16); } res = gnrc_netapi_get(dev, NETOPT_NID, 0, &u16, sizeof(u16)); if (res >= 0) { printf(" NID: 0x%" PRIx16 " ", u16); } res = gnrc_netapi_get(dev, NETOPT_TX_POWER, 0, &i16, sizeof(i16)); if (res >= 0) { printf(" TX-Power: %" PRIi16 "dBm ", i16); } res = gnrc_netapi_get(dev, NETOPT_STATE, 0, &state, sizeof(state)); if (res >= 0) { printf(" State: "); _print_netopt_state(state); } printf("\n "); res = gnrc_netapi_get(dev, NETOPT_ADDRESS_LONG, 0, hwaddr, sizeof(hwaddr)); if (res >= 0) { char hwaddr_str[res * 3]; printf("Long HWaddr: "); printf("%s", gnrc_netif_addr_to_str(hwaddr_str, sizeof(hwaddr_str), hwaddr, res)); printf("\n "); } res = gnrc_netapi_get(dev, NETOPT_PROMISCUOUSMODE, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("PROMISC "); linebreak = true; } res = gnrc_netapi_get(dev, NETOPT_AUTOACK, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("AUTOACK "); linebreak = true; } res = gnrc_netapi_get(dev, NETOPT_PRELOADING, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("PRELOAD "); linebreak = true; } res = gnrc_netapi_get(dev, NETOPT_RAWMODE, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("RAWMODE "); linebreak = true; } #ifdef MODULE_GNRC_IPV6_NETIF if ((entry != NULL) && (entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { printf("6LO "); linebreak = true; } #endif #if defined(MODULE_GNRC_SIXLOWPAN_NETIF) && defined(MODULE_GNRC_SIXLOWPAN_IPHC) gnrc_sixlowpan_netif_t *sixlo_entry = gnrc_sixlowpan_netif_get(dev); if ((sixlo_entry != NULL) && (sixlo_entry->iphc_enabled)) { printf("IPHC "); linebreak = true; } #endif if (linebreak) { printf("\n "); } res = gnrc_netapi_get(dev, NETOPT_SRC_LEN, 0, &u16, sizeof(u16)); if (res >= 0) { printf("Source address length: %" PRIu16 "\n ", u16); } #ifdef MODULE_GNRC_IPV6_NETIF for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { if (!ipv6_addr_is_unspecified(&entry->addrs[i].addr)) { printf("inet6 addr: "); if (ipv6_addr_to_str(ipv6_addr, &entry->addrs[i].addr, IPV6_ADDR_MAX_STR_LEN)) { printf("%s/%" PRIu8 " scope: ", ipv6_addr, entry->addrs[i].prefix_len); if ((ipv6_addr_is_link_local(&entry->addrs[i].addr))) { printf("local"); } else { printf("global"); } if (entry->addrs[i].flags & GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST) { if (ipv6_addr_is_multicast(&entry->addrs[i].addr)) { printf(" [multicast]"); } else { printf(" [anycast]"); } } } else { printf("error in conversion"); } printf("\n "); } } #endif puts(""); }
void rpl_send_DAO(rpl_dodag_t *my_dodag, ipv6_addr_t *destination, uint8_t lifetime, bool default_lifetime, uint8_t start_index) { #if RPL_DEFAULT_MOP == RPL_MOP_NON_STORING_MODE (void) start_index; #endif #if ENABLE_DEBUG if (destination) { DEBUGF("Send DAO to %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, destination)); } #endif if (i_am_root) { return; } if (my_dodag == NULL) { DEBUGF("send_DAO: I have no my_dodag\n"); return; } if (destination == NULL) { #if RPL_DEFAULT_MOP == RPL_MOP_NON_STORING_MODE destination = &my_dodag->dodag_id; #else if (my_dodag->my_preferred_parent == NULL) { DEBUGF("send_DAO: my_dodag has no my_preferred_parent\n"); return; } destination = &my_dodag->my_preferred_parent->addr; #endif } if (default_lifetime) { lifetime = my_dodag->default_lifetime; } icmp_send_buf = get_rpl_send_icmpv6_buf(ipv6_ext_hdr_len); icmp_send_buf->type = ICMPV6_TYPE_RPL_CONTROL; icmp_send_buf->code = ICMP_CODE_DAO; rpl_send_dao_buf = get_rpl_send_dao_buf(); memset(rpl_send_dao_buf, 0, sizeof(*rpl_send_dao_buf)); rpl_send_dao_buf->rpl_instanceid = my_dodag->instance->id; rpl_send_dao_buf->k_d_flags = 0x00; rpl_send_dao_buf->dao_sequence = my_dodag->dao_seq; uint16_t opt_len = 0; rpl_send_opt_target_buf = get_rpl_send_opt_target_buf(DAO_BASE_LEN); #if RPL_DEFAULT_MOP != RPL_MOP_NON_STORING_MODE /* add all targets from routing table as targets */ uint8_t entries = 0; uint8_t continue_index = 0; for (uint8_t i = start_index; i < rpl_max_routing_entries; i++) { if (rpl_get_routing_table()[i].used) { rpl_send_opt_target_buf->type = RPL_OPT_TARGET; rpl_send_opt_target_buf->length = RPL_OPT_TARGET_LEN; rpl_send_opt_target_buf->flags = 0x00; rpl_send_opt_target_buf->prefix_length = RPL_DODAG_ID_LEN; memcpy(&rpl_send_opt_target_buf->target, &rpl_get_routing_table()[i].address, sizeof(ipv6_addr_t)); opt_len += RPL_OPT_TARGET_LEN_WITH_OPT_LEN; rpl_send_opt_transit_buf = get_rpl_send_opt_transit_buf(DAO_BASE_LEN + opt_len); rpl_send_opt_transit_buf->type = RPL_OPT_TRANSIT; rpl_send_opt_transit_buf->length = (RPL_OPT_TRANSIT_LEN - sizeof(ipv6_addr_t)); rpl_send_opt_transit_buf->e_flags = 0x00; rpl_send_opt_transit_buf->path_control = 0x00; /* not used */ rpl_send_opt_transit_buf->path_sequence = 0x00; /* not used */ rpl_send_opt_transit_buf->path_lifetime = lifetime; opt_len += (RPL_OPT_TRANSIT_LEN_WITH_OPT_LEN - sizeof(ipv6_addr_t)); rpl_send_opt_target_buf = get_rpl_send_opt_target_buf(DAO_BASE_LEN + opt_len); entries++; } /* Split DAO, so packages don't get too big. * The value 5 is based on experience. */ if (entries >= 5) { continue_index = i + 1; break; } } #endif /* add own address */ rpl_send_opt_target_buf->type = RPL_OPT_TARGET; rpl_send_opt_target_buf->length = RPL_OPT_TARGET_LEN; rpl_send_opt_target_buf->flags = 0x00; rpl_send_opt_target_buf->prefix_length = RPL_DODAG_ID_LEN; if (!ipv6_addr_is_unspecified(&my_dodag->prefix)) { ipv6_addr_t tmp; ipv6_addr_set_by_eui64(&tmp, rpl_if_id, &my_dodag->prefix); memcpy(&rpl_send_opt_target_buf->target, &tmp, sizeof(ipv6_addr_t)); } else { memcpy(&rpl_send_opt_target_buf->target, &my_address, sizeof(ipv6_addr_t)); } opt_len += RPL_OPT_TARGET_LEN_WITH_OPT_LEN; rpl_send_opt_transit_buf = get_rpl_send_opt_transit_buf(DAO_BASE_LEN + opt_len); rpl_send_opt_transit_buf->type = RPL_OPT_TRANSIT; rpl_send_opt_transit_buf->e_flags = 0x00; rpl_send_opt_transit_buf->path_control = 0x00; rpl_send_opt_transit_buf->path_sequence = 0x00; rpl_send_opt_transit_buf->path_lifetime = lifetime; #if RPL_DEFAULT_MOP == RPL_MOP_NON_STORING_MODE rpl_send_opt_transit_buf->length = RPL_OPT_TRANSIT_LEN; memcpy(&rpl_send_opt_transit_buf->parent, &my_dodag->my_preferred_parent->addr, sizeof(ipv6_addr_t)); opt_len += RPL_OPT_TRANSIT_LEN_WITH_OPT_LEN; #else rpl_send_opt_transit_buf->length = (RPL_OPT_TRANSIT_LEN - sizeof(ipv6_addr_t)); opt_len += (RPL_OPT_TRANSIT_LEN_WITH_OPT_LEN - sizeof(ipv6_addr_t)); #endif uint16_t plen = ICMPV6_HDR_LEN + DAO_BASE_LEN + opt_len; rpl_send(destination, (uint8_t *)icmp_send_buf, plen, IPV6_PROTO_NUM_ICMPV6); #if RPL_DEFAULT_MOP != RPL_MOP_NON_STORING_MODE if (continue_index > 1) { rpl_send_DAO(my_dodag, destination, lifetime, default_lifetime, continue_index); } #endif }
void rpl_send_DIO(rpl_dodag_t *mydodag, ipv6_addr_t *destination) { #if ENABLE_DEBUG if (destination) { DEBUGF("Send DIO to %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, destination)); } #endif icmp_send_buf = get_rpl_send_icmpv6_buf(ipv6_ext_hdr_len); if (mydodag == NULL) { DEBUGF("Error - trying to send DIO without being part of a dodag.\n"); return; } icmp_send_buf->type = ICMPV6_TYPE_RPL_CONTROL; icmp_send_buf->code = ICMP_CODE_DIO; rpl_send_dio_buf = get_rpl_send_dio_buf(); memset(rpl_send_dio_buf, 0, sizeof(*rpl_send_dio_buf)); DEBUGF("Sending DIO with "); rpl_send_dio_buf->rpl_instanceid = mydodag->instance->id; DEBUG("instance %02X ", rpl_send_dio_buf->rpl_instanceid); rpl_send_dio_buf->version_number = mydodag->version; rpl_send_dio_buf->rank = byteorder_htons(mydodag->my_rank); DEBUG("rank %04X\n", byteorder_ntohs(rpl_send_dio_buf->rank)); rpl_send_dio_buf->g_mop_prf = (mydodag->grounded << RPL_GROUNDED_SHIFT) | (mydodag->mop << RPL_MOP_SHIFT) | mydodag->prf; rpl_send_dio_buf->dtsn = mydodag->dtsn; rpl_send_dio_buf->flags = 0; rpl_send_dio_buf->reserved = 0; rpl_send_dio_buf->dodagid = mydodag->dodag_id; int opt_hdr_len = 0; /* DODAG configuration option */ rpl_send_opt_dodag_conf_buf = get_rpl_send_opt_dodag_conf_buf(DIO_BASE_LEN); rpl_send_opt_dodag_conf_buf->type = RPL_OPT_DODAG_CONF; rpl_send_opt_dodag_conf_buf->length = RPL_OPT_DODAG_CONF_LEN; rpl_send_opt_dodag_conf_buf->flags_a_pcs = 0; rpl_send_opt_dodag_conf_buf->DIOIntDoubl = mydodag->dio_interval_doubling; rpl_send_opt_dodag_conf_buf->DIOIntMin = mydodag->dio_min; rpl_send_opt_dodag_conf_buf->DIORedun = mydodag->dio_redundancy; rpl_send_opt_dodag_conf_buf->MaxRankIncrease = byteorder_htons(mydodag->maxrankincrease); rpl_send_opt_dodag_conf_buf->MinHopRankIncrease = byteorder_htons(mydodag->minhoprankincrease); rpl_send_opt_dodag_conf_buf->ocp = byteorder_htons(mydodag->of->ocp); rpl_send_opt_dodag_conf_buf->reserved = 0; rpl_send_opt_dodag_conf_buf->default_lifetime = mydodag->default_lifetime; rpl_send_opt_dodag_conf_buf->lifetime_unit = byteorder_htons(mydodag->lifetime_unit); opt_hdr_len += RPL_OPT_DODAG_CONF_LEN_WITH_OPT_LEN; if (!ipv6_addr_is_unspecified(&mydodag->prefix)) { rpl_send_opt_prefix_information_buf = get_rpl_send_opt_prefix_information_buf(DIO_BASE_LEN + opt_hdr_len); rpl_send_opt_prefix_information_buf->type = RPL_OPT_PREFIX_INFO; rpl_send_opt_prefix_information_buf->length = RPL_OPT_PREFIX_INFO_LEN; rpl_send_opt_prefix_information_buf->flags = mydodag->prefix_flags; rpl_send_opt_prefix_information_buf->prefix = mydodag->prefix; rpl_send_opt_prefix_information_buf->prefix_length = mydodag->prefix_length; rpl_send_opt_prefix_information_buf->preferred_lifetime = byteorder_htonl(mydodag->prefix_preferred_lifetime); rpl_send_opt_prefix_information_buf->valid_lifetime = byteorder_htonl(mydodag->prefix_valid_lifetime); opt_hdr_len += RPL_OPT_PREFIX_INFO_LEN_WITH_OPT_LEN; } uint16_t plen = ICMPV6_HDR_LEN + DIO_BASE_LEN + opt_hdr_len; rpl_send(destination, (uint8_t *)icmp_send_buf, plen, IPV6_PROTO_NUM_ICMPV6); }
static ipv6_addr_t *_add_addr_to_entry(gnrc_ipv6_netif_t *entry, const ipv6_addr_t *addr, uint8_t prefix_len, uint8_t flags) { gnrc_ipv6_netif_addr_t *tmp_addr = NULL; for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { if (ipv6_addr_equal(&(entry->addrs[i].addr), addr)) { return &(entry->addrs[i].addr); } if (ipv6_addr_is_unspecified(&(entry->addrs[i].addr)) && !tmp_addr) { tmp_addr = &(entry->addrs[i]); } } if (!tmp_addr) { DEBUG("ipv6 netif: couldn't add %s/%" PRIu8 " to interface %" PRIkernel_pid "\n: No space left.", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), prefix_len, entry->pid); return NULL; } memcpy(&(tmp_addr->addr), addr, sizeof(ipv6_addr_t)); DEBUG("ipv6 netif: Added %s/%" PRIu8 " to interface %" PRIkernel_pid "\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), prefix_len, entry->pid); tmp_addr->prefix_len = prefix_len; tmp_addr->flags = flags; #ifdef MODULE_GNRC_SIXLOWPAN_ND if (!ipv6_addr_is_multicast(&(tmp_addr->addr)) && (entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { ipv6_addr_t *router = gnrc_ndp_internal_default_router(); if (router != NULL) { mutex_unlock(&entry->mutex); /* function below relocks mutex */ gnrc_ndp_internal_send_nbr_sol(entry->pid, &tmp_addr->addr, router, router); mutex_lock(&entry->mutex); /* relock mutex */ } /* otherwise there is no default router to register to */ } #endif if (ipv6_addr_is_multicast(addr)) { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST; } else { if (!ipv6_addr_is_link_local(addr)) { #ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER tmp_addr->valid = 0xFFFF; gnrc_sixlowpan_nd_router_abr_t *abr = gnrc_sixlowpan_nd_router_abr_get(); if (gnrc_sixlowpan_nd_router_abr_add_prf(abr, entry, tmp_addr) < 0) { DEBUG("ipv6_netif: error adding prefix to 6LoWPAN-ND management\n"); } #endif #if defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER) if ((entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) && (entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV)) { mutex_unlock(&entry->mutex); /* function below relocks mutex */ #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER if (entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { gnrc_ndp_internal_send_rtr_adv(entry->pid, NULL, NULL, false); } #endif #ifdef MODULE_GNRC_NDP_ROUTER /* New prefixes MAY allow the router to retransmit up to * GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF unsolicited RA * (see https://tools.ietf.org/html/rfc4861#section-6.2.4) */ if (!(entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { entry->rtr_adv_count = GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF; gnrc_ndp_router_retrans_rtr_adv(entry); } #endif mutex_lock(&entry->mutex); /* relock mutex */ } #endif } else { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK; } #if defined(MODULE_GNRC_NDP_NODE) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER) /* add solicited-nodes multicast address for new address if interface is not a * 6LoWPAN host interface (see: https://tools.ietf.org/html/rfc6775#section-5.2) */ if (!(entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) || (entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { ipv6_addr_t sol_node; ipv6_addr_set_solicited_nodes(&sol_node, addr); _add_addr_to_entry(entry, &sol_node, IPV6_ADDR_BIT_LEN, 0); } #endif /* TODO: send NS with ARO on 6LoWPAN interfaces, but not so many and only for the new * source address. */ } return &(tmp_addr->addr); }
void gnrc_ndp_internal_send_nbr_adv(kernel_pid_t iface, ipv6_addr_t *tgt, ipv6_addr_t *dst, bool supply_tl2a, gnrc_pktsnip_t *ext_opts) { gnrc_pktsnip_t *hdr, *pkt = ext_opts; uint8_t adv_flags = 0; DEBUG("ndp internal: send neighbor advertisement (iface: %" PRIkernel_pid ", tgt: %s, ", iface, ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); DEBUG("dst: %s, supply_tl2a: %d)\n", ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), supply_tl2a); if ((gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) && (gnrc_ipv6_netif_get(iface)->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV)) { adv_flags |= NDP_NBR_ADV_FLAGS_R; } if (ipv6_addr_is_unspecified(dst)) { ipv6_addr_set_all_nodes_multicast(dst, IPV6_ADDR_MCAST_SCP_LINK_LOCAL); } else { adv_flags |= NDP_NBR_ADV_FLAGS_S; } if (supply_tl2a) { uint8_t l2src[8]; size_t l2src_len; /* we previously checked if we are the target, so we can take our L2src */ l2src_len = _get_l2src(iface, l2src, sizeof(l2src)); if (l2src_len > 0) { /* add target address link-layer address option */ pkt = gnrc_ndp_opt_tl2a_build(l2src, l2src_len, pkt); if (pkt == NULL) { DEBUG("ndp internal: error allocating Target Link-layer address option.\n"); gnrc_pktbuf_release(ext_opts); return; } } } /* TODO: also check if the node provides proxy servies for tgt */ if ((pkt != NULL) && !gnrc_ipv6_netif_addr_is_non_unicast(tgt)) { /* TL2A is not supplied and tgt is not anycast */ adv_flags |= NDP_NBR_ADV_FLAGS_O; } hdr = gnrc_ndp_nbr_adv_build(adv_flags, tgt, pkt); if (hdr == NULL) { DEBUG("ndp internal: error allocating Neighbor advertisement.\n"); gnrc_pktbuf_release(pkt); return; } pkt = hdr; hdr = _build_headers(iface, pkt, dst, NULL); if (hdr == NULL) { DEBUG("ndp internal: error adding lower-layer headers.\n"); gnrc_pktbuf_release(pkt); return; } if (gnrc_ipv6_netif_addr_is_non_unicast(tgt)) { /* avoid collision for anycast addresses * (see https://tools.ietf.org/html/rfc4861#section-7.2.7) */ uint32_t delay = genrand_uint32_range(0, GNRC_NDP_MAX_AC_TGT_DELAY * SEC_IN_USEC); gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, dst); DEBUG("ndp internal: delay neighbor advertisement for %" PRIu32 " sec.", (delay / SEC_IN_USEC)); /* nc_entry must be set so no need to check it */ assert(nc_entry); _send_delayed(&nc_entry->nbr_adv_timer, &nc_entry->nbr_adv_msg, delay, hdr); } else if (gnrc_netapi_send(gnrc_ipv6_pid, hdr) < 1) { DEBUG("ndp internal: unable to send neighbor advertisement\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); }
uint8_t gnrc_sixlowpan_nd_opt_ar_handle(kernel_pid_t iface, ipv6_hdr_t *ipv6, uint8_t icmpv6_type, sixlowpan_nd_opt_ar_t *ar_opt, uint8_t *sl2a, size_t sl2a_len) { eui64_t eui64; gnrc_ipv6_netif_t *ipv6_iface; gnrc_ipv6_nc_t *nc_entry; uint8_t status = 0; (void)sl2a; (void)sl2a_len; if (ar_opt->len != SIXLOWPAN_ND_OPT_AR_LEN) { /* discard silently: see https://tools.ietf.org/html/rfc6775#section-5.5.2 */ return 0; } if (gnrc_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, &eui64, sizeof(eui64)) < 0) { /* discard silently: see https://tools.ietf.org/html/rfc6775#section-5.5.2 */ return 0; } ipv6_iface = gnrc_ipv6_netif_get(iface); nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src); switch (icmpv6_type) { case ICMPV6_NBR_ADV: if (!(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { DEBUG("6lo nd: interface not a 6LoWPAN interface\n"); return 0; } if (eui64.uint64.u64 != ar_opt->eui64.uint64.u64) { /* discard silently: see https://tools.ietf.org/html/rfc6775#section-5.5.2 */ return 0; } switch (ar_opt->status) { case SIXLOWPAN_ND_STATUS_SUCCESS: DEBUG("6lo nd: address registration successful\n"); mutex_lock(&ipv6_iface->mutex); /* reschedule 1 minute before lifetime expires */ timex_t t = { (uint32_t)(byteorder_ntohs(ar_opt->ltime) - 1) * 60, 0 }; vtimer_remove(&nc_entry->nbr_sol_timer); vtimer_set_msg(&nc_entry->nbr_sol_timer, t, gnrc_ipv6_pid, GNRC_NDP_MSG_NBR_SOL_RETRANS, nc_entry); mutex_unlock(&ipv6_iface->mutex); break; case SIXLOWPAN_ND_STATUS_DUP: DEBUG("6lo nd: address registration determined duplicated\n"); /* TODO: handle DAD failed case */ gnrc_ipv6_netif_remove_addr(iface, &ipv6->dst); /* address should not be used anymore */ break; case SIXLOWPAN_ND_STATUS_NC_FULL: DEBUG("6lo nd: neighbor cache on router is full\n"); gnrc_ipv6_nc_remove(iface, &ipv6->src); /* try to find another router */ gnrc_sixlowpan_nd_init(ipv6_iface); break; default: DEBUG("6lo nd: unknown status for registration received\n"); break; } break; #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER case ICMPV6_NBR_SOL: if (!(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && !(ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { DEBUG("6lo nd: interface not a 6LoWPAN or forwarding interface\n"); return 0; } if ((ar_opt->status != 0) || ipv6_addr_is_unspecified(&ipv6->src)) { /* discard silently */ return 0; } /* TODO multihop DAD */ if ((nc_entry != NULL) && ((gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_REGISTERED) || (gnrc_ipv6_nc_get_type(nc_entry) == GNRC_IPV6_NC_TYPE_TENTATIVE)) && ((nc_entry->eui64.uint64.u64 != 0) && (ar_opt->eui64.uint64.u64 != nc_entry->eui64.uint64.u64))) { /* there is already another node with this address */ DEBUG("6lo nd: duplicate address detected\n"); status = SIXLOWPAN_ND_STATUS_DUP; } else if ((nc_entry != NULL) && (ar_opt->ltime.u16 == 0)) { gnrc_ipv6_nc_remove(iface, &ipv6->src); /* TODO, notify routing protocol */ } else if (ar_opt->ltime.u16 != 0) { /* TODO: multihop DAD behavior */ uint16_t reg_ltime; if (nc_entry == NULL) { if ((nc_entry = gnrc_ipv6_nc_add(iface, &ipv6->src, sl2a, sl2a_len, GNRC_IPV6_NC_STATE_STALE)) == NULL) { DEBUG("6lo nd: neighbor cache is full\n"); return SIXLOWPAN_ND_STATUS_NC_FULL; } nc_entry->eui64 = ar_opt->eui64; } nc_entry->flags &= ~GNRC_IPV6_NC_TYPE_MASK; nc_entry->flags |= GNRC_IPV6_NC_TYPE_REGISTERED; reg_ltime = byteorder_ntohs(ar_opt->ltime); /* TODO: notify routing protocol */ vtimer_remove(&nc_entry->type_timeout); vtimer_set_msg(&nc_entry->type_timeout, timex_set(reg_ltime * 60, 0), gnrc_ipv6_pid, GNRC_SIXLOWPAN_ND_MSG_AR_TIMEOUT, nc_entry); } break; #endif default: break; } return status; }