<commit_before>/*
 * Copyright (C) 2015 <NAME> <<EMAIL>>
 *
 * This file is subject to the terms and conditions of the GNU Lesser General
 * Public License v2.1. See the file LICENSE in the top level directory for
 * more details.
 */

/**
 * @ingroup net_gnrc_ndp
 * @{
 *
 * @file
 *
 * @author      <NAME> <<EMAIL>>
 */

#include <errno.h>
#include <string.h>

#include "byteorder.h"
#include "net/fib.h"
#include "net/ieee802154.h"
#include "net/ipv6/ext/rh.h"
#include "net/gnrc/icmpv6.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/sixlowpan/nd.h"
#include "net/gnrc.h"
#include "net/sixlowpan/nd.h"
#include "random.h"
#include "utlist.h"
#include "thread.h"
#include "xtimer.h"

#include "net/gnrc/ndp/internal.h"

#include "net/gnrc/ndp.h"

#define ENABLE_DEBUG    (0)
#include "debug.h"

#if ENABLE_DEBUG
/* For PRIu8 etc. */
#include <inttypes.h>

static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#endif

/* 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_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_aiid(&nbr_adv_dst, iid.uint8);
            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);
}

static inline bool _pkt_has_l2addr(gnrc_netif_hdr_t *netif_hdr)
{
    return (netif_hdr != NULL) && (netif_hdr->src_l2addr_len != 0) &&
           (netif_hdr->dst_l2addr_len != 0);
}

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
    }

    netif = gnrc_pktsnip_search_type(pkt, 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;
}

#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
void gnrc_ndp_rtr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
                             ipv6_hdr_t *ipv6, ndp_rtr_sol_t *rtr_sol,
                             size_t icmpv6_size)
{
    gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);

    if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) {
        int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
        uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
        uint16_t opt_offset = 0;
        uint8_t *buf = (uint8_t *)(rtr_sol + 1);
        /* check validity */
        if ((ipv6->hl != 255) || (rtr_sol->code != 0) ||
            (icmpv6_size < sizeof(ndp_rtr_sol_t))) {
            DEBUG("ndp: router solicitation was invalid\n");
            return;
        }
        sicmpv6_size -= sizeof(ndp_rtr_sol_t);
        while (sicmpv6_size > 0) {
            ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset);

            switch (opt->type) {
                case NDP_OPT_SL2A:
                    l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6,
                                                                  rtr_sol->type,
                                                                  opt, l2src);
                    if (l2src_len < 0) {
                        /* -ENOTSUP can not happen */
                        /* invalid source link-layer address option */
                        return;
                    }
                    _stale_nc(iface, &ipv6->src, l2src, l2src_len);
                    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
        }

        /* send delayed */
        if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV) {
            uint32_t delay;
            uint32_t ms = GNRC_NDP_MAX_RTR_ADV_DELAY;
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
            if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
                ms = GNRC_SIXLOWPAN_ND_MAX_RTR_ADV_DELAY;
            }
#endif
            delay = random_uint32_range(0, ms);
            xtimer_remove(&if_entry->rtr_adv_timer);
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
            /* in case of a 6LBR we have to check if the interface is actually
             * the 6lo interface */
            if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
                gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
                if (nc_entry != NULL) {
                    if_entry->rtr_adv_msg.type = GNRC_NDP_MSG_RTR_ADV_SIXLOWPAN_DELAY;
                    if_entry->rtr_adv_msg.content.ptr = (char *) nc_entry;
                    xtimer_set_msg(&if_entry->rtr_adv_timer, delay, &if_entry->rtr_adv_msg,
                                   gnrc_ipv6_pid);
                }
            }
#elif defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER)
            if (ipv6_addr_is_unspecified(&ipv6->src)) {
                /* either multicast, if source unspecified */
                if_entry->rtr_adv_msg.type = GNRC_NDP_MSG_RTR_ADV_RETRANS;
                if_entry->rtr_adv_msg.content.ptr = (char *) if_entry;
                xtimer_set_msg(&if_entry->rtr_adv_timer, delay, &if_entry->rtr_adv_msg,
                               gnrc_ipv6_pid);
            }
            else {
                /* or unicast, if source is known */
                /* XXX: can't just use GNRC_NETAPI_MSG_TYPE_SND, since the next retransmission
                 * must also be set. */
                gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
                xtimer_set_msg(&nc_entry->rtr_adv_timer, delay, &nc_entry->rtr_adv_msg,
                               gnrc_ipv6_pid);
            }
#endif
        }
    }
    /* otherwise ignore silently */
}
#endif

static inline void _set_reach_time(gnrc_ipv6_netif_t *if_entry, uint32_t mean)
{
    uint32_t reach_time = random_uint32_range(GNRC_NDP_MIN_RAND, GNRC_NDP_MAX_RAND);

    if_entry->reach_time_base = mean;
    /* to avoid floating point number computation and have higher value entropy, the
     * boundaries for the random value are multiplied by 10 and we need to account for that */
    reach_time = (reach_time * if_entry->reach_time_base) / 10;
    if_entry->reach_time = reach_time;
}

void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6,
                             ndp_rtr_adv_t *rtr_adv, size_t icmpv6_size)
{
    uint8_t *buf = (uint8_t *)(rtr_adv + 1);
    gnrc_ipv6_nc_t *nc_entry = NULL;
    gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);
    uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
#ifdef MODULE_GNRC_SIXLOWPAN_ND
    uint32_t next_rtr_sol = 0;
#endif
    int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
    uint16_t opt_offset = 0;
<commit_msg>gnrc_ndp: unset isRouter flag for neighbor sending RS<commit_after>/*
 * Copyright (C) 2015 <NAME> <<EMAIL>>
 *
 * This file is subject to the terms and conditions of the GNU Lesser General
 * Public License v2.1. See the file LICENSE in the top level directory for
 * more details.
 */

/**
 * @ingroup net_gnrc_ndp
 * @{
 *
 * @file
 *
 * @author      <NAME> <<EMAIL>>
 */

#include <errno.h>
#include <string.h>

#include "byteorder.h"
#include "net/fib.h"
#include "net/ieee802154.h"
#include "net/ipv6/ext/rh.h"
#include "net/gnrc/icmpv6.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/sixlowpan/nd.h"
#include "net/gnrc.h"
#include "net/sixlowpan/nd.h"
#include "random.h"
#include "utlist.h"
#include "thread.h"
#include "xtimer.h"

#include "net/gnrc/ndp/internal.h"

#include "net/gnrc/ndp.h"

#define ENABLE_DEBUG    (0)
#include "debug.h"

#if ENABLE_DEBUG
/* For PRIu8 etc. */
#include <inttypes.h>

static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#endif

/* 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_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_aiid(&nbr_adv_dst, iid.uint8);
            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);
}

static inline bool _pkt_has_l2addr(gnrc_netif_hdr_t *netif_hdr)
{
    return (netif_hdr != NULL) && (netif_hdr->src_l2addr_len != 0) &&
           (netif_hdr->dst_l2addr_len != 0);
}

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
    }

    netif = gnrc_pktsnip_search_type(pkt, 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;
}

#if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER))
void gnrc_ndp_rtr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt,
                             ipv6_hdr_t *ipv6, ndp_rtr_sol_t *rtr_sol,
                             size_t icmpv6_size)
{
    gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);

    if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) {
        gnrc_ipv6_nc_t *nc_entry;
        int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
        uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
        uint16_t opt_offset = 0;
        uint8_t *buf = (uint8_t *)(rtr_sol + 1);
        /* check validity */
        if ((ipv6->hl != 255) || (rtr_sol->code != 0) ||
            (icmpv6_size < sizeof(ndp_rtr_sol_t))) {
            DEBUG("ndp: router solicitation was invalid\n");
            return;
        }
        sicmpv6_size -= sizeof(ndp_rtr_sol_t);
        while (sicmpv6_size > 0) {
            ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset);

            switch (opt->type) {
                case NDP_OPT_SL2A:
                    l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6,
                                                                  rtr_sol->type,
                                                                  opt, l2src);
                    if (l2src_len < 0) {
                        /* -ENOTSUP can not happen */
                        /* invalid source link-layer address option */
                        return;
                    }
                    _stale_nc(iface, &ipv6->src, l2src, l2src_len);
                    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
        }

        /* send delayed */
        if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV) {
            uint32_t delay;
            uint32_t ms = GNRC_NDP_MAX_RTR_ADV_DELAY;
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
            if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
                ms = GNRC_SIXLOWPAN_ND_MAX_RTR_ADV_DELAY;
            }
#endif
            delay = random_uint32_range(0, ms);
            xtimer_remove(&if_entry->rtr_adv_timer);
#ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER
            /* in case of a 6LBR we have to check if the interface is actually
             * the 6lo interface */
            if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
                gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
                if (nc_entry != NULL) {
                    if_entry->rtr_adv_msg.type = GNRC_NDP_MSG_RTR_ADV_SIXLOWPAN_DELAY;
                    if_entry->rtr_adv_msg.content.ptr = (char *) nc_entry;
                    xtimer_set_msg(&if_entry->rtr_adv_timer, delay, &if_entry->rtr_adv_msg,
                                   gnrc_ipv6_pid);
                }
            }
#elif defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER)
            if (ipv6_addr_is_unspecified(&ipv6->src)) {
                /* either multicast, if source unspecified */
                if_entry->rtr_adv_msg.type = GNRC_NDP_MSG_RTR_ADV_RETRANS;
                if_entry->rtr_adv_msg.content.ptr = (char *) if_entry;
                xtimer_set_msg(&if_entry->rtr_adv_timer, delay, &if_entry->rtr_adv_msg,
                               gnrc_ipv6_pid);
            }
            else {
                /* or unicast, if source is known */
                /* XXX: can't just use GNRC_NETAPI_MSG_TYPE_SND, since the next retransmission
                 * must also be set. */
                nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
                xtimer_set_msg(&nc_entry->rtr_adv_timer, delay, &nc_entry->rtr_adv_msg,
                               gnrc_ipv6_pid);
            }
#endif
        }
        nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src);
        if (nc_entry != NULL) {
            /* unset isRouter flag
             * (https://tools.ietf.org/html/rfc4861#section-6.2.6) */
            nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER;
        }
    }
    /* otherwise ignore silently */
}
#endif

static inline void _set_reach_time(gnrc_ipv6_netif_t *if_entry, uint32_t mean)
{
    uint32_t reach_time = random_uint32_range(GNRC_NDP_MIN_RAND, GNRC_NDP_MAX_RAND);

    if_entry->reach_time_base = mean;
    /* to avoid floating point number computation and have higher value entropy, the
     * boundaries for the random value are multiplied by 10 and we need to account for that */
    reach_time = (reach_time * if_entry->reach_time_base) / 10;
    if_entry->reach_time = reach_time;
}

void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6,
                             ndp_rtr_adv_t *rtr_adv, size_t icmpv6_size)
{
    uint8_t *buf = (uint8_t *)(rtr_adv + 1);
    gnrc_ipv6_nc_t *nc_entry = NULL;
    gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface);
    uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX];
#ifdef MODULE_GNRC_SIXLOWPAN_ND
    uint32_t next_rtr_sol = 0;
#endif
    int sicmpv6_size = (int)icmpv6_size, l2src_len = 0;
    uint16_t opt_offset = 0;

{'fraction_non_alphanumeric': 0.10121179788125904, 'fraction_numerical': 0.020933363818814624, 'mean_word_length': 1.8584706992956213, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 229, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 62, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '812029', 'n_tokens_mistral': 14729, 'n_tokens_neox': 14308, 'n_words': 2424}