1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-24 14:03:55 +01:00

ng_ndp: fix neighbor advertisement handling

The setting of the states and reset of the retransmission timers is
broken in the current implementation. This patch fixes that behavior and
simplifies the code a little.
This commit is contained in:
Martine Lenders 2015-06-09 18:07:19 +02:00
parent 70abb0c4ed
commit f0134f613c

View File

@ -15,6 +15,7 @@
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#include <errno.h>
#include <string.h>
#include "byteorder.h"
@ -52,10 +53,9 @@ static inline uint32_t _rand(uint32_t min, uint32_t max)
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 bool _handle_tl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt,
ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type,
ng_ndp_opt_t *tl2a_opt, ng_ipv6_addr_t *tgt,
uint8_t adv_flags);
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,
@ -130,14 +130,24 @@ void ng_ndp_nbr_sol_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
return;
}
static inline bool _pkt_has_l2addr(ng_netif_hdr_t *netif_hdr)
{
return (netif_hdr != NULL) && (netif_hdr->src_l2addr_len != 0) &&
(netif_hdr->dst_l2addr_len != 0);
}
void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
ng_ipv6_hdr_t *ipv6, ng_ndp_nbr_adv_t *nbr_adv,
size_t icmpv6_size)
{
uint16_t opt_offset = 0;
uint8_t *buf = ((uint8_t *)nbr_adv) + sizeof(ng_ndp_nbr_adv_t);
bool tl2a_supplied = false;
int l2tgt_len = 0;
uint8_t l2tgt[NG_IPV6_NC_L2_ADDR_MAX];
int sicmpv6_size = (int)icmpv6_size;
ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, &nbr_adv->tgt);
ng_pktsnip_t *netif;
ng_netif_hdr_t *netif_hdr = NULL;
/* check validity */
if ((ipv6->hl != 255) || (nbr_adv->code != 0) ||
@ -148,13 +158,13 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
return;
}
if (ng_ipv6_nc_get(iface, &nbr_adv->tgt) == NULL) {
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(ng_ndp_nbr_adv_t);
while (sicmpv6_size > 0) {
@ -162,14 +172,12 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
switch (opt->type) {
case NG_NDP_OPT_TL2A:
if (!_handle_tl2a_opt(iface, pkt, ipv6, nbr_adv->type, opt,
&nbr_adv->tgt, nbr_adv->flags)) {
if ((l2tgt_len = _handle_tl2a_opt(pkt, ipv6, nbr_adv->type, opt,
l2tgt)) < 0) {
/* invalid target link-layer address option */
return;
}
tl2a_supplied = true;
break;
default:
@ -181,17 +189,87 @@ void ng_ndp_nbr_adv_handle(kernel_pid_t iface, ng_pktsnip_t *pkt,
sicmpv6_size -= (opt->len * 8);
}
if (!tl2a_supplied) {
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_O) {
ng_ipv6_nc_t *nc_entry = ng_ipv6_nc_get(iface, &nbr_adv->tgt);
LL_SEARCH_SCALAR(pkt, netif, type, NG_NETTYPE_NETIF);
if (netif != NULL) {
netif_hdr = netif->data;
}
if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_INCOMPLETE) {
ng_pktqueue_node_t *queued_pkt;
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 & NG_NDP_NBR_ADV_FLAGS_S) {
_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE);
}
else {
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE);
}
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_R) {
nc_entry->flags |= NG_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags &= ~NG_IPV6_NC_IS_ROUTER;
/* TODO: update FIB */
}
while ((queued_pkt = ng_pktqueue_remove_head(&nc_entry->pkts)) != NULL) {
ng_netapi_send(ng_ipv6_pid, queued_pkt->data);
queued_pkt->data = NULL;
}
}
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 & NG_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 & NG_NDP_NBR_ADV_FLAGS_S) {
_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);
}
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_R) {
nc_entry->flags |= NG_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags &= ~NG_IPV6_NC_IS_ROUTER;
/* TODO: update FIB */
}
}
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);
if (nc_entry != NULL) {
if (nbr_adv->flags & NG_NDP_NBR_ADV_FLAGS_S) {
_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE);
}
else {
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE);
}
}
}
}
@ -803,104 +881,44 @@ static bool _handle_sl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt,
}
}
static bool _handle_tl2a_opt(kernel_pid_t iface, ng_pktsnip_t *pkt,
ng_ipv6_hdr_t *ipv6, uint8_t icmpv6_type,
ng_ndp_opt_t *tl2a_opt, ng_ipv6_addr_t *tgt,
uint8_t adv_flags)
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)
{
ng_ipv6_nc_t *nc_entry = NULL;
uint8_t tl2a_len = 0;
uint8_t *tl2a = (uint8_t *)(tl2a_opt + 1);
if ((tl2a_opt->len == 0) || ng_ipv6_addr_is_unspecified(&ipv6->src)) {
DEBUG("ndp: invalid target link-layer address option received\n");
return false;
}
while (pkt) {
if (pkt->type == NG_NETTYPE_NETIF) {
ng_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(ng_ndp_opt_t);
/* ignore all zeroes at the end for length */
for (; tl2a[tl2a_len - 1] == 0x00; tl2a_len--);
return -EINVAL;
}
switch (icmpv6_type) {
case NG_ICMPV6_NBR_ADV:
nc_entry = ng_ipv6_nc_get(iface, tgt);
/* no need to create an entry in the negative case (see RFC 4861) */
if (nc_entry != NULL) {
nc_entry->l2_addr_len = tl2a_len;
if (tl2a_len > 0) {
memcpy(nc_entry->l2_addr, tl2a, tl2a_len);
}
if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_INCOMPLETE) {
ng_pktqueue_node_t *queued_pkt;
if (adv_flags & NG_NDP_NBR_ADV_FLAGS_S) {
_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE);
}
else {
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE);
}
if (adv_flags & NG_NDP_NBR_ADV_FLAGS_R) {
nc_entry->flags |= NG_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags &= ~NG_IPV6_NC_IS_ROUTER;
}
while ((queued_pkt = ng_pktqueue_remove_head(&nc_entry->pkts)) != NULL) {
ng_netapi_send(ng_ipv6_pid, queued_pkt->data);
queued_pkt->data = NULL;
}
}
else {
if (memcmp(tl2a, nc_entry->l2_addr, tl2a_len) != 0) {
if ((adv_flags & NG_NDP_NBR_ADV_FLAGS_O)) {
memcpy(nc_entry->l2_addr, tl2a, tl2a_len);
}
else if (ng_ipv6_nc_get_state(nc_entry) == NG_IPV6_NC_STATE_REACHABLE) {
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE);
}
}
if ((adv_flags & NG_NDP_NBR_ADV_FLAGS_O)) {
if (adv_flags & NG_NDP_NBR_ADV_FLAGS_S) {
_set_state(nc_entry, NG_IPV6_NC_STATE_REACHABLE);
}
else {
_set_state(nc_entry, NG_IPV6_NC_STATE_STALE);
}
if (adv_flags & NG_NDP_NBR_ADV_FLAGS_R) {
nc_entry->flags |= NG_IPV6_NC_IS_ROUTER;
}
else {
nc_entry->flags &= ~NG_IPV6_NC_IS_ROUTER;
}
}
while (pkt) {
if (pkt->type == NG_NETTYPE_NETIF) {
ng_netif_hdr_t *hdr = pkt->data;
tl2a_len = hdr->src_l2addr_len;
break;
}
pkt = pkt->next;
}
return true;
if (tl2a_len == 0) { /* in case there was no source address in l2 */
tl2a_len = (tl2a_opt->len / 8) - sizeof(ng_ndp_opt_t);
/* ignore all zeroes at the end for length */
for (; tl2a[tl2a_len - 1] == 0x00; 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 true;
return 0;
}
}