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) +
66 			  start->size + start->extra_data_size) * idx));
67 }
68 
net_nbr_get(struct net_nbr_table * table)69 struct net_nbr *net_nbr_get(struct net_nbr_table *table)
70 {
71 	int i;
72 
73 	for (i = 0; i < table->nbr_count; i++) {
74 		struct net_nbr *nbr = get_nbr(table->nbr, i);
75 
76 		if (!nbr->ref) {
77 			nbr->data = nbr->__nbr;
78 
79 			return net_nbr_ref(nbr);
80 		}
81 	}
82 
83 	return NULL;
84 }
85 
net_nbr_link(struct net_nbr * nbr,struct net_if * iface,const struct net_linkaddr * lladdr)86 int net_nbr_link(struct net_nbr *nbr, struct net_if *iface,
87 		 const struct net_linkaddr *lladdr)
88 {
89 	int i, avail = -1;
90 
91 	if (nbr->idx != NET_NBR_LLADDR_UNKNOWN) {
92 		return -EALREADY;
93 	}
94 
95 	for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
96 		if (avail < 0 && !net_neighbor_lladdr[i].ref) {
97 			avail = i;
98 		}
99 
100 		if (net_neighbor_lladdr[i].ref &&
101 		    !memcmp(lladdr->addr,
102 			    net_neighbor_lladdr[i].lladdr.addr,
103 			    lladdr->len)) {
104 			/* We found same lladdr in nbr cache so just
105 			 * increase the ref count.
106 			 */
107 			net_neighbor_lladdr[i].ref++;
108 
109 			nbr->idx = i;
110 			nbr->iface = iface;
111 
112 			return 0;
113 		}
114 	}
115 
116 	if (avail < 0) {
117 		return -ENOENT;
118 	}
119 
120 	/* There was no existing entry in the lladdr cache,
121 	 * so allocate one for this lladdr.
122 	 */
123 	net_neighbor_lladdr[avail].ref++;
124 	nbr->idx = avail;
125 
126 	net_linkaddr_set(&net_neighbor_lladdr[avail].lladdr, 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_storage *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 			.addr = net_neighbor_lladdr[i].lladdr.addr,
197 			.len = net_neighbor_lladdr[i].lladdr.len
198 		};
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