|
|
|
@ -19,6 +19,7 @@
|
|
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
#include "byteorder.h" |
|
|
|
|
#include "net/fib.h" |
|
|
|
|
#include "net/ng_icmpv6.h" |
|
|
|
|
#include "net/ng_ipv6.h" |
|
|
|
|
#include "net/ng_ipv6/ext/rh.h" |
|
|
|
@ -28,8 +29,9 @@
|
|
|
|
|
#include "thread.h" |
|
|
|
|
#include "vtimer.h" |
|
|
|
|
|
|
|
|
|
#include "net/ng_ndp/internal.h" |
|
|
|
|
|
|
|
|
|
#include "net/ng_ndp.h" |
|
|
|
|
#include "net/fib.h" |
|
|
|
|
|
|
|
|
|
#define ENABLE_DEBUG (0) |
|
|
|
|
#include "debug.h" |
|
|
|
@ -41,43 +43,7 @@
|
|
|
|
|
static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN]; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static ng_pktqueue_t _pkt_nodes[NG_IPV6_NC_SIZE * 2]; |
|
|
|
|
static ng_ipv6_nc_t *_last_router = NULL; /* last router chosen as default
|
|
|
|
|
* router. Only used if reachability |
|
|
|
|
* is suspect (i. e. incomplete or |
|
|
|
|
* not at all) */ |
|
|
|
|
|
|
|
|
|
/* random helper function */ |
|
|
|
|
static inline uint32_t _rand(uint32_t min, uint32_t max) |
|
|
|
|
{ |
|
|
|
|
return (genrand_uint32() % (max - min)) + min; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool _handle_sl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt, |
|
|
|
|
ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type, |
|
|
|
|
ng_ndp_opt_t *sl2a_opt); |
|
|
|
|
static int _handle_tl2a_opt(ng_pktsnip_t *pkt, ng_ipv6_hdr_t *ipv6, |
|
|
|
|
uint8_t icmpv6_type, ng_ndp_opt_t *tl2a_opt, |
|
|
|
|
uint8_t *l2addr); |
|
|
|
|
|
|
|
|
|
/* send address resolution messages */ |
|
|
|
|
static void _send_nbr_sol(kernel_pid_t iface, ng_ipv6_addr_t *tgt, |
|
|
|
|
ng_ipv6_addr_t *dst); |
|
|
|
|
static void _send_nbr_adv(kernel_pid_t iface, ng_ipv6_addr_t *tgt, |
|
|
|
|
ng_ipv6_addr_t *dst, bool supply_tl2a); |
|
|
|
|
|
|
|
|
|
static void _set_state(ng_ipv6_nc_t *nc_entry, uint8_t state); |
|
|
|
|
|
|
|
|
|
/* special netapi helper */ |
|
|
|
|
static inline void _send_delayed(vtimer_t *t, timex_t interval, ng_pktsnip_t *pkt) |
|
|
|
|
{ |
|
|
|
|
vtimer_remove(t); |
|
|
|
|
vtimer_set_msg(t, interval, ng_ipv6_pid, NG_NETAPI_MSG_TYPE_SND, pkt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* packet queue node allocation */ |
|
|
|
|
static ng_pktqueue_t *_alloc_pkt_node(ng_pktsnip_t *pkt); |
|
|
|
|
|
|
|
|
|
void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt, |
|
|
|
|
ng_ipv6_hdr_t *ipv6, ng_ndp_nbr_sol_t *nbr_sol, |
|
|
|
|
size_t icmpv6_size) |
|
|
|
@ -119,7 +85,7 @@ void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
|
|
|
|
|
|
|
|
|
|
switch (opt->type) { |
|
|
|
|
case NG_NDP_OPT_SL2A: |
|
|
|
|
if (!_handle_sl2a_opt(iface, pkt, ipv6, nbr_sol->type, opt)) { |
|
|
|
|
if (!ng_ndp_internal_sl2a_opt_handle(iface, pkt, ipv6, nbr_sol->type, opt)) { |
|
|
|
|
/* invalid source link-layer address option */ |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
@ -135,8 +101,8 @@ void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
|
|
|
|
|
sicmpv6_size -= (opt->len * 8); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_send_nbr_adv(iface, tgt, &ipv6->src, |
|
|
|
|
ng_ipv6_addr_is_multicast(&ipv6->dst)); |
|
|
|
|
ng_ndp_internal_send_nbr_adv(iface, tgt, &ipv6->src, |
|
|
|
|
ng_ipv6_addr_is_multicast(&ipv6->dst)); |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
@ -190,8 +156,7 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
|
|
|
|
|
|
|
|
|
|
switch (opt->type) { |
|
|
|
|
case NG_NDP_OPT_TL2A: |
|
|
|
|
if ((l2tgt_len = _handle_tl2a_opt(pkt, ipv6, nbr_adv->type, opt, |
|
|
|
|
l2tgt)) < 0) { |
|
|
|
|
if ((l2tgt_len = ng_ndp_internal_tl2a_opt_handle(pkt, ipv6, nbr_adv->type, opt, l2tgt)) < 0) { |
|
|
|
|
/* invalid target link-layer address option */ |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
@ -227,10 +192,10 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
|
|
|
|
|
memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); |
|
|
|
|
|
|
|
|
|
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_S) { |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_R) { |
|
|
|
@ -270,10 +235,10 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_S) { |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE); |
|
|
|
|
} |
|
|
|
|
else if (l2tgt_changed && (l2tgt_len != 0)) { |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_R) { |
|
|
|
@ -286,7 +251,7 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
|
|
|
|
|
} |
|
|
|
|
else if (l2tgt_changed) { |
|
|
|
|
if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_REACHABLE) { |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -322,7 +287,7 @@ void ng_ndp_retrans_nbr_sol(ng_ipv6_nc_t *nc_entry)
|
|
|
|
|
size_t ifnum = ng_netif_get(ifs); |
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ifnum; i++) { |
|
|
|
|
_send_nbr_sol(ifs[i], &nc_entry->ipv6_addr, &dst); |
|
|
|
|
ng_ndp_internal_send_nbr_sol(ifs[i], &nc_entry->ipv6_addr, &dst); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vtimer_remove(&nc_entry->nbr_sol_timer); |
|
|
|
@ -332,7 +297,7 @@ void ng_ndp_retrans_nbr_sol(ng_ipv6_nc_t *nc_entry)
|
|
|
|
|
else { |
|
|
|
|
ng_ipv6_netif_t *ipv6_iface = ng_ipv6_netif_get(nc_entry->iface); |
|
|
|
|
|
|
|
|
|
_send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, &dst); |
|
|
|
|
ng_ndp_internal_send_nbr_sol(nc_entry->iface, &nc_entry->ipv6_addr, &dst); |
|
|
|
|
|
|
|
|
|
mutex_lock(&ipv6_iface->mutex); |
|
|
|
|
vtimer_remove(&nc_entry->nbr_sol_timer); |
|
|
|
@ -359,11 +324,11 @@ void ng_ndp_state_timeout(ng_ipv6_nc_t *nc_entry)
|
|
|
|
|
{ |
|
|
|
|
switch (ng_ipv6_nc_get_state(nc_entry)) { |
|
|
|
|
case NG_IPV6_NC_STATE_REACHABLE: |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_STALE); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case NG_IPV6_NC_STATE_DELAY: |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_PROBE); |
|
|
|
|
ng_ndp_internal_set_state(nc_entry, NG_IPV6_NC_STATE_PROBE); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
@ -373,7 +338,7 @@ void ng_ndp_state_timeout(ng_ipv6_nc_t *nc_entry)
|
|
|
|
|
|
|
|
|
|
void ng_ndp_netif_add(ng_ipv6_netif_t *iface) |
|
|
|
|
{ |
|
|
|
|
uint32_t reach_time = _rand(NG_NDP_MIN_RAND, NG_NDP_MAX_RAND); |
|
|
|
|
uint32_t reach_time = genrand_uint32_range(NG_NDP_MIN_RAND, NG_NDP_MAX_RAND); |
|
|
|
|
|
|
|
|
|
/* set default values */ |
|
|
|
|
mutex_lock(&iface->mutex); |
|
|
|
@ -392,173 +357,6 @@ void ng_ndp_netif_remove(ng_ipv6_netif_t *iface)
|
|
|
|
|
(void) iface; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static ng_ipv6_addr_t *_default_router(void) |
|
|
|
|
{ |
|
|
|
|
ng_ipv6_nc_t *router = ng_ipv6_nc_get_next_router(NULL); |
|
|
|
|
|
|
|
|
|
/* first look if there is any reachable router */ |
|
|
|
|
while (router != NULL) { |
|
|
|
|
if ((ng_ipv6_nc_get_state(router) != NG_IPV6_NC_STATE_INCOMPLETE) && |
|
|
|
|
(ng_ipv6_nc_get_state(router) != NG_IPV6_NC_STATE_UNREACHABLE)) { |
|
|
|
|
_last_router = NULL; |
|
|
|
|
|
|
|
|
|
return &router->ipv6_addr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
router = ng_ipv6_nc_get_next_router(router); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* else take the first one, but keep round-robin in further selections */ |
|
|
|
|
router = ng_ipv6_nc_get_next_router(_last_router); |
|
|
|
|
|
|
|
|
|
if (router == NULL) { /* end of router list or there is none => wrap around */ |
|
|
|
|
router = ng_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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
kernel_pid_t ng_ndp_next_hop_l2addr(uint8_t *l2addr, uint8_t *l2addr_len, |
|
|
|
|
kernel_pid_t iface, ng_ipv6_addr_t *dst, |
|
|
|
|
ng_pktsnip_t *pkt) |
|
|
|
|
{ |
|
|
|
|
ng_ipv6_addr_t *next_hop_ip = NULL, *prefix = NULL; |
|
|
|
|
|
|
|
|
|
#ifdef MODULE_NG_IPV6_EXT_RH |
|
|
|
|
next_hop_ip = ng_ipv6_ext_rh_next_hop(hdr); |
|
|
|
|
#endif |
|
|
|
|
#ifdef MODULE_FIB |
|
|
|
|
size_t next_hop_size = sizeof(ng_ipv6_addr_t); |
|
|
|
|
uint32_t next_hop_flags = 0; |
|
|
|
|
ng_ipv6_addr_t next_hop_actual; /* FIB copies address into this variable */ |
|
|
|
|
|
|
|
|
|
if ((next_hop_ip == NULL) && |
|
|
|
|
(fib_get_next_hop(&iface, next_hop_actual.u8, &next_hop_size, |
|
|
|
|
&next_hop_flags, (uint8_t *)dst, |
|
|
|
|
sizeof(ng_ipv6_addr_t), 0) >= 0) && |
|
|
|
|
(next_hop_size == sizeof(ng_ipv6_addr_t))) { |
|
|
|
|
next_hop_ip = &next_hop_actual; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
if (next_hop_ip == NULL) { /* no route to host */ |
|
|
|
|
if (iface == KERNEL_PID_UNDEF) { |
|
|
|
|
/* ng_ipv6_netif_t doubles as prefix list */ |
|
|
|
|
iface = ng_ipv6_netif_find_by_prefix(&prefix, dst); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
/* ng_ipv6_netif_t doubles as prefix list */ |
|
|
|
|
prefix = ng_ipv6_netif_match_prefix(iface, dst); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ((prefix != NULL) && /* prefix is on-link */ |
|
|
|
|
(ng_ipv6_netif_addr_get(prefix)->flags & |
|
|
|
|
NG_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK)) { |
|
|
|
|
next_hop_ip = dst; |
|
|
|
|
#ifdef MODULE_FIB |
|
|
|
|
/* We don't care if FIB is full, this is just for efficiency
|
|
|
|
|
* for later sends */ |
|
|
|
|
fib_add_entry(iface, (uint8_t *)dst, sizeof(ng_ipv6_addr_t), 0, |
|
|
|
|
(uint8_t *)next_hop_ip, sizeof(ng_ipv6_addr_t), 0, |
|
|
|
|
FIB_LIFETIME_NO_EXPIRE); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (next_hop_ip == NULL) { |
|
|
|
|
next_hop_ip = _default_router(); |
|
|
|
|
#ifdef MODULE_FIB |
|
|
|
|
/* We don't care if FIB is full, this is just for efficiency for later
|
|
|
|
|
* sends */ |
|
|
|
|
fib_add_entry(iface, (uint8_t *)dst, sizeof(ng_ipv6_addr_t), 0, |
|
|
|
|
(uint8_t *)next_hop_ip, sizeof(ng_ipv6_addr_t), 0, |
|
|
|
|
FIB_LIFETIME_NO_EXPIRE); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (next_hop_ip != NULL) { |
|
|
|
|
ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, next_hop_ip); |
|
|
|
|
|
|
|
|
|
if ((nc_entry != NULL) && ng_ipv6_nc_is_reachable(nc_entry)) { |
|
|
|
|
DEBUG("ndp: found reachable neighbor (%s => ", |
|
|
|
|
ng_ipv6_addr_to_str(addr_str, &nc_entry->ipv6_addr, sizeof(addr_str))); |
|
|
|
|
DEBUG("%s)\n", |
|
|
|
|
ng_netif_addr_to_str(addr_str, sizeof(addr_str), |
|
|
|
|
nc_entry->l2_addr, nc_entry->l2_addr_len)); |
|
|
|
|
|
|
|
|
|
if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_STALE) { |
|
|
|
|
_set_state(nc_entry, NG_IPV6_NC_STATE_DELAY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
else if (nc_entry == NULL) { |
|
|
|
|
ng_pktqueue_t *pkt_node; |
|
|
|
|
ng_ipv6_addr_t dst_sol; |
|
|
|
|
|
|
|
|
|
nc_entry = ng_ipv6_nc_add(iface, next_hop_ip, NULL, 0, |
|
|
|
|
NG_IPV6_NC_STATE_INCOMPLETE << NG_IPV6_NC_STATE_POS); |
|
|
|
|
|
|
|
|
|
if (nc_entry == NULL) { |
|
|
|
|
DEBUG("ndp: could not create neighbor cache entry\n"); |
|
|
|
|
return KERNEL_PID_UNDEF; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkt_node = _alloc_pkt_node(pkt); |
|
|
|
|
|
|
|
|
|
if (pkt_node == NULL) { |
|
|
|
|
DEBUG("ndp: could not add packet to packet queue\n"); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
/* prevent packet from being released by IPv6 */ |
|
|
|
|
ng_pktbuf_hold(pkt_node->pkt, 1); |
|
|
|
|
ng_pktqueue_add(&nc_entry->pkts, pkt_node); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* address resolution */ |
|
|
|
|
ng_ipv6_addr_set_solicited_nodes(&dst_sol, next_hop_ip); |
|
|
|
|
|
|
|
|
|
if (iface == KERNEL_PID_UNDEF) { |
|
|
|
|
timex_t t = { 0, NG_NDP_RETRANS_TIMER }; |
|
|
|
|
kernel_pid_t ifs[NG_NETIF_NUMOF]; |
|
|
|
|
size_t ifnum = ng_netif_get(ifs); |
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ifnum; i++) { |
|
|
|
|
_send_nbr_sol(ifs[i], next_hop_ip, &dst_sol); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vtimer_remove(&nc_entry->nbr_sol_timer); |
|
|
|
|
vtimer_set_msg(&nc_entry->nbr_sol_timer, t, ng_ipv6_pid, |
|
|
|
|
NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
ng_ipv6_netif_t *ipv6_iface = ng_ipv6_netif_get(iface); |
|
|
|
|
|
|
|
|
|
_send_nbr_sol(iface, next_hop_ip, &dst_sol); |
|
|
|
|
|
|
|
|
|
mutex_lock(&ipv6_iface->mutex); |
|
|
|
|
vtimer_remove(&nc_entry->nbr_sol_timer); |
|
|
|
|
vtimer_set_msg(&nc_entry->nbr_sol_timer, |
|
|
|
|
ipv6_iface->retrans_timer, ng_ipv6_pid, |
|
|
|
|
NG_NDP_MSG_NBR_SOL_RETRANS, nc_entry); |
|
|
|
|
mutex_unlock(&ipv6_iface->mutex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return KERNEL_PID_UNDEF; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ng_pktsnip_t *ng_ndp_nbr_sol_build(ng_ipv6_addr_t *tgt, ng_pktsnip_t *options) |
|
|
|
|
{ |
|
|
|
|
ng_pktsnip_t *pkt; |
|
|
|
@ -631,199 +429,6 @@ ng_pktsnip_t *ng_ndp_opt_build(uint8_t type, size_t size, ng_pktsnip_t *next)
|
|
|
|
|
return pkt; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static uint16_t _get_l2src(uint8_t *l2src, size_t l2src_size, kernel_pid_t iface) |
|
|
|
|
{ |
|
|
|
|
bool try_long = false; |
|
|
|
|
int res; |
|
|
|
|
uint16_t l2src_len; |
|
|
|
|
/* maximum address length that fits into a minimum length (8) S/TL2A option */ |
|
|
|
|
const uint16_t max_short_len = 6; |
|
|
|
|
|
|
|
|
|
/* try getting source address */ |
|
|
|
|
if ((ng_netapi_get(iface, NETOPT_SRC_LEN, 0, &l2src_len, |
|
|
|
|
sizeof(l2src_len)) >= 0) && |
|
|
|
|
(l2src_len > max_short_len)) { |
|
|
|
|
try_long = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (try_long && ((res = ng_netapi_get(iface, NETOPT_ADDRESS_LONG, 0, |
|
|
|
|
l2src, l2src_size)) > max_short_len)) { |
|
|
|
|
l2src_len = (uint16_t)res; |
|
|
|
|
} |
|
|
|
|
else if ((res = ng_netapi_get(iface, NETOPT_ADDRESS, 0, l2src, |
|
|
|
|
l2src_size)) >= 0) { |
|
|
|
|
l2src_len = (uint16_t)res; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
DEBUG("ndp: no link-layer address found.\n"); |
|
|
|
|
l2src_len = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return l2src_len; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void _send_nbr_sol(kernel_pid_t iface, ng_ipv6_addr_t *tgt, |
|
|
|
|
ng_ipv6_addr_t *dst) |
|
|
|
|
{ |
|
|
|
|
ng_pktsnip_t *hdr, *pkt = NULL; |
|
|
|
|
ng_ipv6_addr_t *src = NULL; |
|
|
|
|
size_t src_len = 0; |
|
|
|
|
|
|
|
|
|
DEBUG("ndp: send neighbor solicitation (iface: %" PRIkernel_pid ", tgt: %s, ", |
|
|
|
|
iface, ng_ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); |
|
|
|
|
DEBUG("dst: %s)\n", ng_ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); |
|
|
|
|
|
|
|
|
|
/* check if there is a fitting source address to target */ |
|
|
|
|
if ((src = ng_ipv6_netif_find_best_src_addr(iface, tgt)) != NULL) { |
|
|
|
|
uint8_t l2src[8]; |
|
|
|
|
uint16_t l2src_len; |
|
|
|
|
src_len = sizeof(ng_ipv6_addr_t); |
|
|
|
|
l2src_len = _get_l2src(l2src, sizeof(l2src), iface); |
|
|
|
|
|
|
|
|
|
if (l2src_len > 0) { |
|
|
|
|
/* add source address link-layer address option */ |
|
|
|
|
pkt = ng_ndp_opt_sl2a_build(l2src, l2src_len, NULL); |
|
|
|
|
|
|
|
|
|
if (pkt == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating Source Link-layer address option.\n"); |
|
|
|
|
ng_pktbuf_release(pkt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hdr = ng_ndp_nbr_sol_build(tgt, pkt); |
|
|
|
|
|
|
|
|
|
if (hdr == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating Neighbor solicitation.\n"); |
|
|
|
|
ng_pktbuf_release(pkt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkt = hdr; |
|
|
|
|
hdr = ng_ipv6_hdr_build(pkt, (uint8_t *)src, src_len, (uint8_t *)dst, |
|
|
|
|
sizeof(ng_ipv6_addr_t)); |
|
|
|
|
|
|
|
|
|
if (hdr == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating IPv6 header.\n"); |
|
|
|
|
ng_pktbuf_release(pkt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
((ng_ipv6_hdr_t *)hdr->data)->hl = 255; |
|
|
|
|
|
|
|
|
|
pkt = hdr; |
|
|
|
|
/* add netif header for send interface specification */ |
|
|
|
|
hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); |
|
|
|
|
|
|
|
|
|
if (hdr == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating netif header.\n"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
((ng_netif_hdr_t *)hdr->data)->if_pid = iface; |
|
|
|
|
|
|
|
|
|
LL_PREPEND(pkt, hdr); |
|
|
|
|
|
|
|
|
|
ng_netapi_send(ng_ipv6_pid, pkt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void _send_nbr_adv(kernel_pid_t iface, ng_ipv6_addr_t *tgt, |
|
|
|
|
ng_ipv6_addr_t *dst, bool supply_tl2a) |
|
|
|
|
{ |
|
|
|
|
ng_pktsnip_t *hdr, *pkt = NULL; |
|
|
|
|
uint8_t adv_flags = 0; |
|
|
|
|
|
|
|
|
|
DEBUG("ndp: send neighbor advertisement (iface: %" PRIkernel_pid ", tgt: %s, ", |
|
|
|
|
iface, ng_ipv6_addr_to_str(addr_str, tgt, sizeof(addr_str))); |
|
|
|
|
DEBUG("dst: %s, supply_tl2a: %d)\n", |
|
|
|
|
ng_ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), supply_tl2a); |
|
|
|
|
|
|
|
|
|
if (ng_ipv6_netif_get(iface)->flags & NG_IPV6_NETIF_FLAGS_ROUTER) { |
|
|
|
|
adv_flags |= NG_NDP_NBR_ADV_FLAGS_R; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ng_ipv6_addr_is_unspecified(dst)) { |
|
|
|
|
ng_ipv6_addr_set_all_nodes_multicast(dst, |
|
|
|
|
NG_IPV6_ADDR_MCAST_SCP_LINK_LOCAL); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
adv_flags |= NG_NDP_NBR_ADV_FLAGS_S; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (supply_tl2a) { |
|
|
|
|
uint8_t l2src[8]; |
|
|
|
|
uint16_t l2src_len; |
|
|
|
|
/* we previously checked if we are the target, so we can take our L2src */ |
|
|
|
|
l2src_len = _get_l2src(l2src, sizeof(l2src), iface); |
|
|
|
|
|
|
|
|
|
if (l2src_len > 0) { |
|
|
|
|
/* add target address link-layer address option */ |
|
|
|
|
pkt = ng_ndp_opt_tl2a_build(l2src, l2src_len, NULL); |
|
|
|
|
|
|
|
|
|
if (pkt == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating Target Link-layer address option.\n"); |
|
|
|
|
ng_pktbuf_release(pkt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* TODO: also check if the node provides proxy servies for tgt */ |
|
|
|
|
if ((pkt != NULL) && !ng_ipv6_netif_addr_is_non_unicast(tgt)) { |
|
|
|
|
/* TL2A is not supplied and tgt is not anycast */ |
|
|
|
|
adv_flags |= NG_NDP_NBR_ADV_FLAGS_O; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hdr = ng_ndp_nbr_adv_build(adv_flags, tgt, pkt); |
|
|
|
|
|
|
|
|
|
if (hdr == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating Neighbor advertisement.\n"); |
|
|
|
|
ng_pktbuf_release(pkt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkt = hdr; |
|
|
|
|
hdr = ng_ipv6_hdr_build(pkt, NULL, 0, (uint8_t *)dst, |
|
|
|
|
sizeof(ng_ipv6_addr_t)); |
|
|
|
|
|
|
|
|
|
if (hdr == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating IPv6 header.\n"); |
|
|
|
|
ng_pktbuf_release(pkt); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
((ng_ipv6_hdr_t *)hdr->data)->hl = 255; |
|
|
|
|
|
|
|
|
|
pkt = hdr; |
|
|
|
|
/* add netif header for send interface specification */ |
|
|
|
|
hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); |
|
|
|
|
|
|
|
|
|
if (hdr == NULL) { |
|
|
|
|
DEBUG("ndp: error allocating netif header.\n"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
((ng_netif_hdr_t *)hdr->data)->if_pid = iface; |
|
|
|
|
|
|
|
|
|
LL_PREPEND(pkt, hdr); |
|
|
|
|
|
|
|
|
|
if (ng_ipv6_netif_addr_is_non_unicast(tgt)) { |
|
|
|
|
/* avoid collision for anycast addresses
|
|
|
|
|
* (see https://tools.ietf.org/html/rfc4861#section-7.2.7) */
|
|
|
|
|
timex_t delay = { _rand(0, NG_NDP_MAX_AC_TGT_DELAY), 0 }; |
|
|
|
|
ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, tgt); |
|
|
|
|
DEBUG("ndp: delay neighbor advertisement for %" PRIu32 " sec.", |
|
|
|
|
delay.seconds); |
|
|
|
|
|
|
|
|
|
/* nc_entry must be set so no need to check it */ |
|
|
|
|
_send_delayed(&nc_entry->nbr_adv_timer, delay, pkt); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
ng_netapi_send(ng_ipv6_pid, pkt); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static inline ng_pktsnip_t *_opt_l2a_build(uint8_t type, const uint8_t *l2addr, |
|
|
|
|
uint8_t l2addr_len, ng_pktsnip_t *next) |
|
|
|
|
{ |
|
|
|
@ -858,188 +463,6 @@ ng_pktsnip_t *ng_ndp_opt_tl2a_build(const uint8_t *l2addr, uint8_t l2addr_len,
|
|
|
|
|
return _opt_l2a_build(NG_NDP_OPT_TL2A, l2addr, l2addr_len, next); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* internal functions */ |
|
|
|
|
/* packet queue node allocation */ |
|
|
|
|
static ng_pktqueue_t *_alloc_pkt_node(ng_pktsnip_t *pkt) |
|
|
|
|
{ |
|
|
|
|
for (size_t i = 0; i < sizeof(_pkt_nodes) / sizeof(ng_pktqueue_t); i++) { |
|
|
|
|
if ((_pkt_nodes[i].pkt == NULL) && (_pkt_nodes[i].next == NULL)) { |
|
|
|
|
_pkt_nodes[i].pkt = pkt; |
|
|
|
|
|
|
|
|
|
return &(_pkt_nodes[i]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool _handle_sl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt, |
|
|
|
|
ng_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) || ng_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 NG_ICMPV6_NBR_SOL: |
|
|
|
|
nc_entry = ng_ipv6_nc_get(iface, &ipv6->src); |
|
|
|
|
|
|
|
|
|
if (nc_entry != NULL) { |
|
|
|
|
if ((sl2a_len != nc_entry->l2_addr_len) || |
|
|