1 /* nbr.c - Neighbor table management */
2
3 /*
4 * Copyright (c) 2016 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(net_nbr, CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL);
11
12 #include <errno.h>
13
14 #include <zephyr/net/net_core.h>
15
16 #include "net_private.h"
17
18 #include "nbr.h"
19
20 NET_NBR_LLADDR_INIT(net_neighbor_lladdr, CONFIG_NET_IPV6_MAX_NEIGHBORS);
21
22 #if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
net_nbr_unref_debug(struct net_nbr * nbr,const char * caller,int line)23 void net_nbr_unref_debug(struct net_nbr *nbr, const char *caller, int line)
24 #define net_nbr_unref(nbr) net_nbr_unref_debug(nbr, __func__, __LINE__)
25 #else
26 void net_nbr_unref(struct net_nbr *nbr)
27 #endif
28 {
29 #if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
30 NET_DBG("nbr %p ref %u (%s():%d)", nbr, nbr->ref - 1, caller, line);
31 #else
32 NET_DBG("nbr %p ref %u", nbr, nbr->ref - 1);
33 #endif
34 if (--nbr->ref) {
35 return;
36 }
37
38 if (nbr->remove) {
39 nbr->remove(nbr);
40 }
41 }
42
43 #if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
net_nbr_ref_debug(struct net_nbr * nbr,const char * caller,int line)44 struct net_nbr *net_nbr_ref_debug(struct net_nbr *nbr, const char *caller,
45 int line)
46 #else
47 struct net_nbr *net_nbr_ref(struct net_nbr *nbr)
48 #endif
49 {
50 #if defined(CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL_DBG)
51 NET_DBG("nbr %p ref %u (%s():%d)", nbr, nbr->ref + 1, caller, line);
52 #else
53 NET_DBG("nbr %p ref %u", nbr, nbr->ref + 1);
54 #endif
55 nbr->ref++;
56
57 return nbr;
58 }
59
get_nbr(struct net_nbr * start,int idx)60 static inline struct net_nbr *get_nbr(struct net_nbr *start, int idx)
61 {
62 NET_ASSERT(idx < CONFIG_NET_IPV6_MAX_NEIGHBORS);
63
64 return (struct net_nbr *)((uint8_t *)start +
65 ((sizeof(struct net_nbr) + start->size) * idx));
66 }
67
net_nbr_get(struct net_nbr_table * table)68 struct net_nbr *net_nbr_get(struct net_nbr_table *table)
69 {
70 int i;
71
72 for (i = 0; i < table->nbr_count; i++) {
73 struct net_nbr *nbr = get_nbr(table->nbr, i);
74
75 if (!nbr->ref) {
76 nbr->data = nbr->__nbr;
77
78 return net_nbr_ref(nbr);
79 }
80 }
81
82 return NULL;
83 }
84
net_nbr_link(struct net_nbr * nbr,struct net_if * iface,const struct net_linkaddr * lladdr)85 int net_nbr_link(struct net_nbr *nbr, struct net_if *iface,
86 const struct net_linkaddr *lladdr)
87 {
88 int i, avail = -1;
89
90 if (nbr->idx != NET_NBR_LLADDR_UNKNOWN) {
91 return -EALREADY;
92 }
93
94 for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
95 if (avail < 0 && !net_neighbor_lladdr[i].ref) {
96 avail = i;
97 }
98
99 if (net_neighbor_lladdr[i].ref &&
100 !memcmp(lladdr->addr,
101 net_neighbor_lladdr[i].lladdr.addr,
102 lladdr->len)) {
103 /* We found same lladdr in nbr cache so just
104 * increase the ref count.
105 */
106 net_neighbor_lladdr[i].ref++;
107
108 nbr->idx = i;
109 nbr->iface = iface;
110
111 return 0;
112 }
113 }
114
115 if (avail < 0) {
116 return -ENOENT;
117 }
118
119 /* There was no existing entry in the lladdr cache,
120 * so allocate one for this lladdr.
121 */
122 net_neighbor_lladdr[avail].ref++;
123 nbr->idx = avail;
124
125 net_linkaddr_set(&net_neighbor_lladdr[avail].lladdr,
126 (uint8_t *)lladdr->addr,
127 lladdr->len);
128 net_neighbor_lladdr[avail].lladdr.len = lladdr->len;
129 net_neighbor_lladdr[avail].lladdr.type = lladdr->type;
130
131 nbr->iface = iface;
132
133 return 0;
134 }
135
net_nbr_unlink(struct net_nbr * nbr,struct net_linkaddr * lladdr)136 int net_nbr_unlink(struct net_nbr *nbr, struct net_linkaddr *lladdr)
137 {
138 ARG_UNUSED(lladdr);
139
140 if (nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
141 return -EALREADY;
142 }
143
144 NET_ASSERT(nbr->idx < CONFIG_NET_IPV6_MAX_NEIGHBORS);
145 NET_ASSERT(net_neighbor_lladdr[nbr->idx].ref > 0);
146
147 net_neighbor_lladdr[nbr->idx].ref--;
148
149 if (!net_neighbor_lladdr[nbr->idx].ref) {
150 (void)memset(net_neighbor_lladdr[nbr->idx].lladdr.addr, 0,
151 sizeof(net_neighbor_lladdr[nbr->idx].lladdr.addr));
152 }
153
154 nbr->idx = NET_NBR_LLADDR_UNKNOWN;
155 nbr->iface = NULL;
156
157 return 0;
158 }
159
net_nbr_lookup(struct net_nbr_table * table,struct net_if * iface,struct net_linkaddr * lladdr)160 struct net_nbr *net_nbr_lookup(struct net_nbr_table *table,
161 struct net_if *iface,
162 struct net_linkaddr *lladdr)
163 {
164 int i;
165
166 for (i = 0; i < table->nbr_count; i++) {
167 struct net_nbr *nbr = get_nbr(table->nbr, i);
168
169 if (nbr->ref && nbr->iface == iface &&
170 net_neighbor_lladdr[nbr->idx].ref &&
171 !memcmp(net_neighbor_lladdr[nbr->idx].lladdr.addr,
172 lladdr->addr, lladdr->len)) {
173 return nbr;
174 }
175 }
176
177 return NULL;
178 }
179
net_nbr_get_lladdr(uint8_t idx)180 struct net_linkaddr *net_nbr_get_lladdr(uint8_t idx)
181 {
182 NET_ASSERT(idx < CONFIG_NET_IPV6_MAX_NEIGHBORS,
183 "idx %d >= max %d", idx,
184 CONFIG_NET_IPV6_MAX_NEIGHBORS);
185
186 return &net_neighbor_lladdr[idx].lladdr;
187 }
188
net_nbr_clear_table(struct net_nbr_table * table)189 void net_nbr_clear_table(struct net_nbr_table *table)
190 {
191 int i;
192
193 for (i = 0; i < table->nbr_count; i++) {
194 struct net_nbr *nbr = get_nbr(table->nbr, i);
195 struct net_linkaddr lladdr;
196
197 (void)net_linkaddr_set(&lladdr, net_neighbor_lladdr[i].lladdr.addr,
198 net_neighbor_lladdr[i].lladdr.len);
199
200 net_nbr_unlink(nbr, &lladdr);
201 }
202
203 if (table->clear) {
204 table->clear(table);
205 }
206 }
207
net_nbr_print(struct net_nbr_table * table)208 void net_nbr_print(struct net_nbr_table *table)
209 {
210 if (CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL >= LOG_LEVEL_DBG) {
211 int i;
212
213 for (i = 0; i < table->nbr_count; i++) {
214 struct net_nbr *nbr = get_nbr(table->nbr, i);
215
216 if (!nbr->ref) {
217 continue;
218 }
219
220 NET_DBG("[%d] nbr %p data %p ref %d iface %p idx %d "
221 "ll %s",
222 i, nbr, nbr->data, nbr->ref, nbr->iface,
223 nbr->idx,
224 nbr->idx == NET_NBR_LLADDR_UNKNOWN ?
225 "<unknown>" :
226 net_sprint_ll_addr(
227 net_neighbor_lladdr[nbr->idx].lladdr.addr,
228 net_neighbor_lladdr[nbr->idx].lladdr.len));
229 }
230 }
231 }
232