232 lines
4.9 KiB
C
232 lines
4.9 KiB
C
/* nbr.c - Neighbor table management */
|
|
|
|
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(net_nbr, CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL);
|
|
|
|
#include <errno.h>
|
|
|
|
#include <net/net_core.h>
|
|
|
|
#include "net_private.h"
|
|
|
|
#include "nbr.h"
|
|
|
|
NET_NBR_LLADDR_INIT(net_neighbor_lladdr, CONFIG_NET_IPV6_MAX_NEIGHBORS);
|
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
|
|
void net_nbr_unref_debug(struct net_nbr *nbr, const char *caller, int line)
|
|
#define net_nbr_unref(nbr) net_nbr_unref_debug(nbr, __func__, __LINE__)
|
|
#else
|
|
void net_nbr_unref(struct net_nbr *nbr)
|
|
#endif
|
|
{
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
|
|
NET_DBG("nbr %p ref %u (%s():%d)", nbr, nbr->ref - 1, caller, line);
|
|
#else
|
|
NET_DBG("nbr %p ref %u", nbr, nbr->ref - 1);
|
|
#endif
|
|
if (--nbr->ref) {
|
|
return;
|
|
}
|
|
|
|
if (nbr->remove) {
|
|
nbr->remove(nbr);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
|
|
struct net_nbr *net_nbr_ref_debug(struct net_nbr *nbr, const char *caller,
|
|
int line)
|
|
#else
|
|
struct net_nbr *net_nbr_ref(struct net_nbr *nbr)
|
|
#endif
|
|
{
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
|
|
NET_DBG("nbr %p ref %u (%s():%d)", nbr, nbr->ref + 1, caller, line);
|
|
#else
|
|
NET_DBG("nbr %p ref %u", nbr, nbr->ref + 1);
|
|
#endif
|
|
nbr->ref++;
|
|
|
|
return nbr;
|
|
}
|
|
|
|
static inline struct net_nbr *get_nbr(struct net_nbr *start, int idx)
|
|
{
|
|
NET_ASSERT(idx < CONFIG_NET_IPV6_MAX_NEIGHBORS);
|
|
|
|
return (struct net_nbr *)((uint8_t *)start +
|
|
((sizeof(struct net_nbr) +
|
|
start->size + start->extra_data_size) * idx));
|
|
}
|
|
|
|
struct net_nbr *net_nbr_get(struct net_nbr_table *table)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < table->nbr_count; i++) {
|
|
struct net_nbr *nbr = get_nbr(table->nbr, i);
|
|
|
|
if (!nbr->ref) {
|
|
nbr->data = nbr->__nbr;
|
|
|
|
return net_nbr_ref(nbr);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int net_nbr_link(struct net_nbr *nbr, struct net_if *iface,
|
|
const struct net_linkaddr *lladdr)
|
|
{
|
|
int i, avail = -1;
|
|
|
|
if (nbr->idx != NET_NBR_LLADDR_UNKNOWN) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
if (avail < 0 && !net_neighbor_lladdr[i].ref) {
|
|
avail = i;
|
|
}
|
|
|
|
if (net_neighbor_lladdr[i].ref &&
|
|
!memcmp(lladdr->addr,
|
|
net_neighbor_lladdr[i].lladdr.addr,
|
|
lladdr->len)) {
|
|
/* We found same lladdr in nbr cache so just
|
|
* increase the ref count.
|
|
*/
|
|
net_neighbor_lladdr[i].ref++;
|
|
|
|
nbr->idx = i;
|
|
nbr->iface = iface;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (avail < 0) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* There was no existing entry in the lladdr cache,
|
|
* so allocate one for this lladdr.
|
|
*/
|
|
net_neighbor_lladdr[avail].ref++;
|
|
nbr->idx = avail;
|
|
|
|
net_linkaddr_set(&net_neighbor_lladdr[avail].lladdr, lladdr->addr,
|
|
lladdr->len);
|
|
net_neighbor_lladdr[avail].lladdr.len = lladdr->len;
|
|
net_neighbor_lladdr[avail].lladdr.type = lladdr->type;
|
|
|
|
nbr->iface = iface;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_nbr_unlink(struct net_nbr *nbr, struct net_linkaddr *lladdr)
|
|
{
|
|
ARG_UNUSED(lladdr);
|
|
|
|
if (nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
NET_ASSERT(nbr->idx < CONFIG_NET_IPV6_MAX_NEIGHBORS);
|
|
NET_ASSERT(net_neighbor_lladdr[nbr->idx].ref > 0);
|
|
|
|
net_neighbor_lladdr[nbr->idx].ref--;
|
|
|
|
if (!net_neighbor_lladdr[nbr->idx].ref) {
|
|
(void)memset(net_neighbor_lladdr[nbr->idx].lladdr.addr, 0,
|
|
sizeof(net_neighbor_lladdr[nbr->idx].lladdr.addr));
|
|
}
|
|
|
|
nbr->idx = NET_NBR_LLADDR_UNKNOWN;
|
|
nbr->iface = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct net_nbr *net_nbr_lookup(struct net_nbr_table *table,
|
|
struct net_if *iface,
|
|
struct net_linkaddr *lladdr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < table->nbr_count; i++) {
|
|
struct net_nbr *nbr = get_nbr(table->nbr, i);
|
|
|
|
if (nbr->ref && nbr->iface == iface &&
|
|
net_neighbor_lladdr[nbr->idx].ref &&
|
|
!memcmp(net_neighbor_lladdr[nbr->idx].lladdr.addr,
|
|
lladdr->addr, lladdr->len)) {
|
|
return nbr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct net_linkaddr_storage *net_nbr_get_lladdr(uint8_t idx)
|
|
{
|
|
NET_ASSERT(idx < CONFIG_NET_IPV6_MAX_NEIGHBORS,
|
|
"idx %d >= max %d", idx,
|
|
CONFIG_NET_IPV6_MAX_NEIGHBORS);
|
|
|
|
return &net_neighbor_lladdr[idx].lladdr;
|
|
}
|
|
|
|
void net_nbr_clear_table(struct net_nbr_table *table)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < table->nbr_count; i++) {
|
|
struct net_nbr *nbr = get_nbr(table->nbr, i);
|
|
struct net_linkaddr lladdr = {
|
|
.addr = net_neighbor_lladdr[i].lladdr.addr,
|
|
.len = net_neighbor_lladdr[i].lladdr.len
|
|
};
|
|
|
|
net_nbr_unlink(nbr, &lladdr);
|
|
}
|
|
|
|
if (table->clear) {
|
|
table->clear(table);
|
|
}
|
|
}
|
|
|
|
void net_nbr_print(struct net_nbr_table *table)
|
|
{
|
|
if (CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL >= LOG_LEVEL_DBG) {
|
|
int i;
|
|
|
|
for (i = 0; i < table->nbr_count; i++) {
|
|
struct net_nbr *nbr = get_nbr(table->nbr, i);
|
|
|
|
if (!nbr->ref) {
|
|
continue;
|
|
}
|
|
|
|
NET_DBG("[%d] nbr %p data %p ref %d iface %p idx %d "
|
|
"ll %s",
|
|
i, nbr, nbr->data, nbr->ref, nbr->iface,
|
|
nbr->idx,
|
|
nbr->idx == NET_NBR_LLADDR_UNKNOWN ?
|
|
"<unknown>" :
|
|
log_strdup(net_sprint_ll_addr(
|
|
net_neighbor_lladdr[nbr->idx].lladdr.addr,
|
|
net_neighbor_lladdr[nbr->idx].lladdr.len)));
|
|
}
|
|
}
|
|
}
|