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, lladdr->addr,
126 			 lladdr->len);
127 	net_neighbor_lladdr[avail].lladdr.len = lladdr->len;
128 	net_neighbor_lladdr[avail].lladdr.type = lladdr->type;
129 
130 	nbr->iface = iface;
131 
132 	return 0;
133 }
134 
net_nbr_unlink(struct net_nbr * nbr,struct net_linkaddr * lladdr)135 int net_nbr_unlink(struct net_nbr *nbr, struct net_linkaddr *lladdr)
136 {
137 	ARG_UNUSED(lladdr);
138 
139 	if (nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
140 		return -EALREADY;
141 	}
142 
143 	NET_ASSERT(nbr->idx < CONFIG_NET_IPV6_MAX_NEIGHBORS);
144 	NET_ASSERT(net_neighbor_lladdr[nbr->idx].ref > 0);
145 
146 	net_neighbor_lladdr[nbr->idx].ref--;
147 
148 	if (!net_neighbor_lladdr[nbr->idx].ref) {
149 		(void)memset(net_neighbor_lladdr[nbr->idx].lladdr.addr, 0,
150 			     sizeof(net_neighbor_lladdr[nbr->idx].lladdr.addr));
151 	}
152 
153 	nbr->idx = NET_NBR_LLADDR_UNKNOWN;
154 	nbr->iface = NULL;
155 
156 	return 0;
157 }
158 
net_nbr_lookup(struct net_nbr_table * table,struct net_if * iface,struct net_linkaddr * lladdr)159 struct net_nbr *net_nbr_lookup(struct net_nbr_table *table,
160 			       struct net_if *iface,
161 			       struct net_linkaddr *lladdr)
162 {
163 	int i;
164 
165 	for (i = 0; i < table->nbr_count; i++) {
166 		struct net_nbr *nbr = get_nbr(table->nbr, i);
167 
168 		if (nbr->ref && nbr->iface == iface &&
169 		    net_neighbor_lladdr[nbr->idx].ref &&
170 		    !memcmp(net_neighbor_lladdr[nbr->idx].lladdr.addr,
171 			    lladdr->addr, lladdr->len)) {
172 			return nbr;
173 		}
174 	}
175 
176 	return NULL;
177 }
178 
net_nbr_get_lladdr(uint8_t idx)179 struct net_linkaddr_storage *net_nbr_get_lladdr(uint8_t idx)
180 {
181 	NET_ASSERT(idx < CONFIG_NET_IPV6_MAX_NEIGHBORS,
182 		   "idx %d >= max %d", idx,
183 		   CONFIG_NET_IPV6_MAX_NEIGHBORS);
184 
185 	return &net_neighbor_lladdr[idx].lladdr;
186 }
187 
net_nbr_clear_table(struct net_nbr_table * table)188 void net_nbr_clear_table(struct net_nbr_table *table)
189 {
190 	int i;
191 
192 	for (i = 0; i < table->nbr_count; i++) {
193 		struct net_nbr *nbr = get_nbr(table->nbr, i);
194 		struct net_linkaddr lladdr = {
195 			.addr = net_neighbor_lladdr[i].lladdr.addr,
196 			.len = net_neighbor_lladdr[i].lladdr.len
197 		};
198 
199 		net_nbr_unlink(nbr, &lladdr);
200 	}
201 
202 	if (table->clear) {
203 		table->clear(table);
204 	}
205 }
206 
net_nbr_print(struct net_nbr_table * table)207 void net_nbr_print(struct net_nbr_table *table)
208 {
209 	if (CONFIG_NET_IPV6_NBR_CACHE_LOG_LEVEL >= LOG_LEVEL_DBG) {
210 		int i;
211 
212 		for (i = 0; i < table->nbr_count; i++) {
213 			struct net_nbr *nbr = get_nbr(table->nbr, i);
214 
215 			if (!nbr->ref) {
216 				continue;
217 			}
218 
219 			NET_DBG("[%d] nbr %p data %p ref %d iface %p idx %d "
220 				"ll %s",
221 				i, nbr, nbr->data, nbr->ref, nbr->iface,
222 				nbr->idx,
223 				nbr->idx == NET_NBR_LLADDR_UNKNOWN ?
224 				"<unknown>" :
225 				net_sprint_ll_addr(
226 				   net_neighbor_lladdr[nbr->idx].lladdr.addr,
227 				   net_neighbor_lladdr[nbr->idx].lladdr.len));
228 		}
229 	}
230 }
231