1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /* |
---|
4 | * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved. |
---|
5 | * Copyright (c) 2004-2008 Qing Li. All rights reserved. |
---|
6 | * Copyright (c) 2008 Kip Macy. All rights reserved. |
---|
7 | * |
---|
8 | * Redistribution and use in source and binary forms, with or without |
---|
9 | * modification, are permitted provided that the following conditions |
---|
10 | * are met: |
---|
11 | * 1. Redistributions of source code must retain the above copyright |
---|
12 | * notice, this list of conditions and the following disclaimer. |
---|
13 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
14 | * notice, this list of conditions and the following disclaimer in the |
---|
15 | * documentation and/or other materials provided with the distribution. |
---|
16 | * |
---|
17 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
20 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
27 | * SUCH DAMAGE. |
---|
28 | */ |
---|
29 | #include <sys/cdefs.h> |
---|
30 | __FBSDID("$FreeBSD$"); |
---|
31 | |
---|
32 | #include <rtems/bsd/local/opt_ddb.h> |
---|
33 | #include <rtems/bsd/local/opt_inet.h> |
---|
34 | #include <rtems/bsd/local/opt_inet6.h> |
---|
35 | |
---|
36 | #include <sys/param.h> |
---|
37 | #include <sys/systm.h> |
---|
38 | #include <sys/malloc.h> |
---|
39 | #include <sys/mbuf.h> |
---|
40 | #include <sys/syslog.h> |
---|
41 | #include <sys/sysctl.h> |
---|
42 | #include <sys/socket.h> |
---|
43 | #include <sys/kernel.h> |
---|
44 | #include <sys/lock.h> |
---|
45 | #include <sys/mutex.h> |
---|
46 | #include <sys/rwlock.h> |
---|
47 | |
---|
48 | #ifdef DDB |
---|
49 | #include <ddb/ddb.h> |
---|
50 | #endif |
---|
51 | |
---|
52 | #include <vm/uma.h> |
---|
53 | |
---|
54 | #include <netinet/in.h> |
---|
55 | #include <net/if_llatbl.h> |
---|
56 | #include <net/if.h> |
---|
57 | #include <net/if_dl.h> |
---|
58 | #include <net/if_var.h> |
---|
59 | #include <net/route.h> |
---|
60 | #include <net/vnet.h> |
---|
61 | #include <netinet/if_ether.h> |
---|
62 | #include <netinet6/in6_var.h> |
---|
63 | #include <netinet6/nd6.h> |
---|
64 | |
---|
65 | MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables"); |
---|
66 | |
---|
67 | static VNET_DEFINE(SLIST_HEAD(, lltable), lltables) = |
---|
68 | SLIST_HEAD_INITIALIZER(lltables); |
---|
69 | #define V_lltables VNET(lltables) |
---|
70 | |
---|
71 | static struct rwlock lltable_list_lock; |
---|
72 | RW_SYSINIT(lltable_list_lock, &lltable_list_lock, "lltable_list_lock"); |
---|
73 | #define LLTABLE_LIST_RLOCK() rw_rlock(&lltable_list_lock) |
---|
74 | #define LLTABLE_LIST_RUNLOCK() rw_runlock(&lltable_list_lock) |
---|
75 | #define LLTABLE_LIST_WLOCK() rw_wlock(&lltable_list_lock) |
---|
76 | #define LLTABLE_LIST_WUNLOCK() rw_wunlock(&lltable_list_lock) |
---|
77 | #define LLTABLE_LIST_LOCK_ASSERT() rw_assert(&lltable_list_lock, RA_LOCKED) |
---|
78 | |
---|
79 | static void lltable_unlink(struct lltable *llt); |
---|
80 | static void llentries_unlink(struct lltable *llt, struct llentries *head); |
---|
81 | |
---|
82 | static void htable_unlink_entry(struct llentry *lle); |
---|
83 | static void htable_link_entry(struct lltable *llt, struct llentry *lle); |
---|
84 | static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, |
---|
85 | void *farg); |
---|
86 | |
---|
87 | /* |
---|
88 | * Dump lle state for a specific address family. |
---|
89 | */ |
---|
90 | static int |
---|
91 | lltable_dump_af(struct lltable *llt, struct sysctl_req *wr) |
---|
92 | { |
---|
93 | int error; |
---|
94 | |
---|
95 | LLTABLE_LIST_LOCK_ASSERT(); |
---|
96 | |
---|
97 | if (llt->llt_ifp->if_flags & IFF_LOOPBACK) |
---|
98 | return (0); |
---|
99 | error = 0; |
---|
100 | |
---|
101 | IF_AFDATA_RLOCK(llt->llt_ifp); |
---|
102 | error = lltable_foreach_lle(llt, |
---|
103 | (llt_foreach_cb_t *)llt->llt_dump_entry, wr); |
---|
104 | IF_AFDATA_RUNLOCK(llt->llt_ifp); |
---|
105 | |
---|
106 | return (error); |
---|
107 | } |
---|
108 | |
---|
109 | /* |
---|
110 | * Dump arp state for a specific address family. |
---|
111 | */ |
---|
112 | int |
---|
113 | lltable_sysctl_dumparp(int af, struct sysctl_req *wr) |
---|
114 | { |
---|
115 | struct lltable *llt; |
---|
116 | int error = 0; |
---|
117 | |
---|
118 | LLTABLE_LIST_RLOCK(); |
---|
119 | SLIST_FOREACH(llt, &V_lltables, llt_link) { |
---|
120 | if (llt->llt_af == af) { |
---|
121 | error = lltable_dump_af(llt, wr); |
---|
122 | if (error != 0) |
---|
123 | goto done; |
---|
124 | } |
---|
125 | } |
---|
126 | done: |
---|
127 | LLTABLE_LIST_RUNLOCK(); |
---|
128 | return (error); |
---|
129 | } |
---|
130 | |
---|
131 | /* |
---|
132 | * Common function helpers for chained hash table. |
---|
133 | */ |
---|
134 | |
---|
135 | /* |
---|
136 | * Runs specified callback for each entry in @llt. |
---|
137 | * Caller does the locking. |
---|
138 | * |
---|
139 | */ |
---|
140 | static int |
---|
141 | htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg) |
---|
142 | { |
---|
143 | struct llentry *lle, *next; |
---|
144 | int i, error; |
---|
145 | |
---|
146 | error = 0; |
---|
147 | |
---|
148 | for (i = 0; i < llt->llt_hsize; i++) { |
---|
149 | LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { |
---|
150 | error = f(llt, lle, farg); |
---|
151 | if (error != 0) |
---|
152 | break; |
---|
153 | } |
---|
154 | } |
---|
155 | |
---|
156 | return (error); |
---|
157 | } |
---|
158 | |
---|
159 | static void |
---|
160 | htable_link_entry(struct lltable *llt, struct llentry *lle) |
---|
161 | { |
---|
162 | struct llentries *lleh; |
---|
163 | uint32_t hashidx; |
---|
164 | |
---|
165 | if ((lle->la_flags & LLE_LINKED) != 0) |
---|
166 | return; |
---|
167 | |
---|
168 | IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp); |
---|
169 | |
---|
170 | hashidx = llt->llt_hash(lle, llt->llt_hsize); |
---|
171 | lleh = &llt->lle_head[hashidx]; |
---|
172 | |
---|
173 | lle->lle_tbl = llt; |
---|
174 | lle->lle_head = lleh; |
---|
175 | lle->la_flags |= LLE_LINKED; |
---|
176 | LIST_INSERT_HEAD(lleh, lle, lle_next); |
---|
177 | } |
---|
178 | |
---|
179 | static void |
---|
180 | htable_unlink_entry(struct llentry *lle) |
---|
181 | { |
---|
182 | |
---|
183 | if ((lle->la_flags & LLE_LINKED) != 0) { |
---|
184 | IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp); |
---|
185 | LIST_REMOVE(lle, lle_next); |
---|
186 | lle->la_flags &= ~(LLE_VALID | LLE_LINKED); |
---|
187 | #if 0 |
---|
188 | lle->lle_tbl = NULL; |
---|
189 | lle->lle_head = NULL; |
---|
190 | #endif |
---|
191 | } |
---|
192 | } |
---|
193 | |
---|
194 | struct prefix_match_data { |
---|
195 | const struct sockaddr *addr; |
---|
196 | const struct sockaddr *mask; |
---|
197 | struct llentries dchain; |
---|
198 | u_int flags; |
---|
199 | }; |
---|
200 | |
---|
201 | static int |
---|
202 | htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg) |
---|
203 | { |
---|
204 | struct prefix_match_data *pmd; |
---|
205 | |
---|
206 | pmd = (struct prefix_match_data *)farg; |
---|
207 | |
---|
208 | if (llt->llt_match_prefix(pmd->addr, pmd->mask, pmd->flags, lle)) { |
---|
209 | LLE_WLOCK(lle); |
---|
210 | LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain); |
---|
211 | } |
---|
212 | |
---|
213 | return (0); |
---|
214 | } |
---|
215 | |
---|
216 | static void |
---|
217 | htable_prefix_free(struct lltable *llt, const struct sockaddr *addr, |
---|
218 | const struct sockaddr *mask, u_int flags) |
---|
219 | { |
---|
220 | struct llentry *lle, *next; |
---|
221 | struct prefix_match_data pmd; |
---|
222 | |
---|
223 | bzero(&pmd, sizeof(pmd)); |
---|
224 | pmd.addr = addr; |
---|
225 | pmd.mask = mask; |
---|
226 | pmd.flags = flags; |
---|
227 | LIST_INIT(&pmd.dchain); |
---|
228 | |
---|
229 | IF_AFDATA_WLOCK(llt->llt_ifp); |
---|
230 | /* Push matching lles to chain */ |
---|
231 | lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd); |
---|
232 | |
---|
233 | llentries_unlink(llt, &pmd.dchain); |
---|
234 | IF_AFDATA_WUNLOCK(llt->llt_ifp); |
---|
235 | |
---|
236 | LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next) |
---|
237 | lltable_free_entry(llt, lle); |
---|
238 | } |
---|
239 | |
---|
240 | static void |
---|
241 | htable_free_tbl(struct lltable *llt) |
---|
242 | { |
---|
243 | |
---|
244 | free(llt->lle_head, M_LLTABLE); |
---|
245 | free(llt, M_LLTABLE); |
---|
246 | } |
---|
247 | |
---|
248 | static void |
---|
249 | llentries_unlink(struct lltable *llt, struct llentries *head) |
---|
250 | { |
---|
251 | struct llentry *lle, *next; |
---|
252 | |
---|
253 | LIST_FOREACH_SAFE(lle, head, lle_chain, next) |
---|
254 | llt->llt_unlink_entry(lle); |
---|
255 | } |
---|
256 | |
---|
257 | /* |
---|
258 | * Helper function used to drop all mbufs in hold queue. |
---|
259 | * |
---|
260 | * Returns the number of held packets, if any, that were dropped. |
---|
261 | */ |
---|
262 | size_t |
---|
263 | lltable_drop_entry_queue(struct llentry *lle) |
---|
264 | { |
---|
265 | size_t pkts_dropped; |
---|
266 | struct mbuf *next; |
---|
267 | |
---|
268 | LLE_WLOCK_ASSERT(lle); |
---|
269 | |
---|
270 | pkts_dropped = 0; |
---|
271 | while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) { |
---|
272 | next = lle->la_hold->m_nextpkt; |
---|
273 | m_freem(lle->la_hold); |
---|
274 | lle->la_hold = next; |
---|
275 | lle->la_numheld--; |
---|
276 | pkts_dropped++; |
---|
277 | } |
---|
278 | |
---|
279 | KASSERT(lle->la_numheld == 0, |
---|
280 | ("%s: la_numheld %d > 0, pkts_droped %zd", __func__, |
---|
281 | lle->la_numheld, pkts_dropped)); |
---|
282 | |
---|
283 | return (pkts_dropped); |
---|
284 | } |
---|
285 | |
---|
286 | void |
---|
287 | lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle, |
---|
288 | const char *linkhdr, size_t linkhdrsize, int lladdr_off) |
---|
289 | { |
---|
290 | |
---|
291 | memcpy(lle->r_linkdata, linkhdr, linkhdrsize); |
---|
292 | lle->r_hdrlen = linkhdrsize; |
---|
293 | lle->ll_addr = &lle->r_linkdata[lladdr_off]; |
---|
294 | lle->la_flags |= LLE_VALID; |
---|
295 | lle->r_flags |= RLLE_VALID; |
---|
296 | } |
---|
297 | |
---|
298 | /* |
---|
299 | * Tries to update @lle link-level address. |
---|
300 | * Since update requires AFDATA WLOCK, function |
---|
301 | * drops @lle lock, acquires AFDATA lock and then acquires |
---|
302 | * @lle lock to maintain lock order. |
---|
303 | * |
---|
304 | * Returns 1 on success. |
---|
305 | */ |
---|
306 | int |
---|
307 | lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, |
---|
308 | const char *linkhdr, size_t linkhdrsize, int lladdr_off) |
---|
309 | { |
---|
310 | |
---|
311 | /* Perform real LLE update */ |
---|
312 | /* use afdata WLOCK to update fields */ |
---|
313 | LLE_WLOCK_ASSERT(lle); |
---|
314 | LLE_ADDREF(lle); |
---|
315 | LLE_WUNLOCK(lle); |
---|
316 | IF_AFDATA_WLOCK(ifp); |
---|
317 | LLE_WLOCK(lle); |
---|
318 | |
---|
319 | /* |
---|
320 | * Since we droppped LLE lock, other thread might have deleted |
---|
321 | * this lle. Check and return |
---|
322 | */ |
---|
323 | if ((lle->la_flags & LLE_DELETED) != 0) { |
---|
324 | IF_AFDATA_WUNLOCK(ifp); |
---|
325 | LLE_FREE_LOCKED(lle); |
---|
326 | return (0); |
---|
327 | } |
---|
328 | |
---|
329 | /* Update data */ |
---|
330 | lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off); |
---|
331 | |
---|
332 | IF_AFDATA_WUNLOCK(ifp); |
---|
333 | |
---|
334 | LLE_REMREF(lle); |
---|
335 | |
---|
336 | return (1); |
---|
337 | } |
---|
338 | |
---|
339 | /* |
---|
340 | * Helper function used to pre-compute full/partial link-layer |
---|
341 | * header data suitable for feeding into if_output(). |
---|
342 | */ |
---|
343 | int |
---|
344 | lltable_calc_llheader(struct ifnet *ifp, int family, char *lladdr, |
---|
345 | char *buf, size_t *bufsize, int *lladdr_off) |
---|
346 | { |
---|
347 | struct if_encap_req ereq; |
---|
348 | int error; |
---|
349 | |
---|
350 | bzero(buf, *bufsize); |
---|
351 | bzero(&ereq, sizeof(ereq)); |
---|
352 | ereq.buf = buf; |
---|
353 | ereq.bufsize = *bufsize; |
---|
354 | ereq.rtype = IFENCAP_LL; |
---|
355 | ereq.family = family; |
---|
356 | ereq.lladdr = lladdr; |
---|
357 | ereq.lladdr_len = ifp->if_addrlen; |
---|
358 | error = ifp->if_requestencap(ifp, &ereq); |
---|
359 | if (error == 0) { |
---|
360 | *bufsize = ereq.bufsize; |
---|
361 | *lladdr_off = ereq.lladdr_off; |
---|
362 | } |
---|
363 | |
---|
364 | return (error); |
---|
365 | } |
---|
366 | |
---|
367 | /* |
---|
368 | * Update link-layer header for given @lle after |
---|
369 | * interface lladdr was changed. |
---|
370 | */ |
---|
371 | static int |
---|
372 | llentry_update_ifaddr(struct lltable *llt, struct llentry *lle, void *farg) |
---|
373 | { |
---|
374 | struct ifnet *ifp; |
---|
375 | u_char linkhdr[LLE_MAX_LINKHDR]; |
---|
376 | size_t linkhdrsize; |
---|
377 | u_char *lladdr; |
---|
378 | int lladdr_off; |
---|
379 | |
---|
380 | ifp = (struct ifnet *)farg; |
---|
381 | |
---|
382 | lladdr = lle->ll_addr; |
---|
383 | |
---|
384 | LLE_WLOCK(lle); |
---|
385 | if ((lle->la_flags & LLE_VALID) == 0) { |
---|
386 | LLE_WUNLOCK(lle); |
---|
387 | return (0); |
---|
388 | } |
---|
389 | |
---|
390 | if ((lle->la_flags & LLE_IFADDR) != 0) |
---|
391 | lladdr = IF_LLADDR(ifp); |
---|
392 | |
---|
393 | linkhdrsize = sizeof(linkhdr); |
---|
394 | lltable_calc_llheader(ifp, llt->llt_af, lladdr, linkhdr, &linkhdrsize, |
---|
395 | &lladdr_off); |
---|
396 | memcpy(lle->r_linkdata, linkhdr, linkhdrsize); |
---|
397 | LLE_WUNLOCK(lle); |
---|
398 | |
---|
399 | return (0); |
---|
400 | } |
---|
401 | |
---|
402 | /* |
---|
403 | * Update all calculated headers for given @llt |
---|
404 | */ |
---|
405 | void |
---|
406 | lltable_update_ifaddr(struct lltable *llt) |
---|
407 | { |
---|
408 | |
---|
409 | if (llt->llt_ifp->if_flags & IFF_LOOPBACK) |
---|
410 | return; |
---|
411 | |
---|
412 | IF_AFDATA_WLOCK(llt->llt_ifp); |
---|
413 | lltable_foreach_lle(llt, llentry_update_ifaddr, llt->llt_ifp); |
---|
414 | IF_AFDATA_WUNLOCK(llt->llt_ifp); |
---|
415 | } |
---|
416 | |
---|
417 | /* |
---|
418 | * |
---|
419 | * Performs generic cleanup routines and frees lle. |
---|
420 | * |
---|
421 | * Called for non-linked entries, with callouts and |
---|
422 | * other AF-specific cleanups performed. |
---|
423 | * |
---|
424 | * @lle must be passed WLOCK'ed |
---|
425 | * |
---|
426 | * Returns the number of held packets, if any, that were dropped. |
---|
427 | */ |
---|
428 | size_t |
---|
429 | llentry_free(struct llentry *lle) |
---|
430 | { |
---|
431 | size_t pkts_dropped; |
---|
432 | |
---|
433 | LLE_WLOCK_ASSERT(lle); |
---|
434 | |
---|
435 | KASSERT((lle->la_flags & LLE_LINKED) == 0, ("freeing linked lle")); |
---|
436 | |
---|
437 | pkts_dropped = lltable_drop_entry_queue(lle); |
---|
438 | |
---|
439 | LLE_FREE_LOCKED(lle); |
---|
440 | |
---|
441 | return (pkts_dropped); |
---|
442 | } |
---|
443 | |
---|
444 | /* |
---|
445 | * (al)locate an llentry for address dst (equivalent to rtalloc for new-arp). |
---|
446 | * |
---|
447 | * If found the llentry * is returned referenced and unlocked. |
---|
448 | */ |
---|
449 | struct llentry * |
---|
450 | llentry_alloc(struct ifnet *ifp, struct lltable *lt, |
---|
451 | struct sockaddr_storage *dst) |
---|
452 | { |
---|
453 | struct llentry *la, *la_tmp; |
---|
454 | |
---|
455 | IF_AFDATA_RLOCK(ifp); |
---|
456 | la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst); |
---|
457 | IF_AFDATA_RUNLOCK(ifp); |
---|
458 | |
---|
459 | if (la != NULL) { |
---|
460 | LLE_ADDREF(la); |
---|
461 | LLE_WUNLOCK(la); |
---|
462 | return (la); |
---|
463 | } |
---|
464 | |
---|
465 | if ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { |
---|
466 | la = lltable_alloc_entry(lt, 0, (struct sockaddr *)dst); |
---|
467 | if (la == NULL) |
---|
468 | return (NULL); |
---|
469 | IF_AFDATA_WLOCK(ifp); |
---|
470 | LLE_WLOCK(la); |
---|
471 | /* Prefer any existing LLE over newly-created one */ |
---|
472 | la_tmp = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst); |
---|
473 | if (la_tmp == NULL) |
---|
474 | lltable_link_entry(lt, la); |
---|
475 | IF_AFDATA_WUNLOCK(ifp); |
---|
476 | if (la_tmp != NULL) { |
---|
477 | lltable_free_entry(lt, la); |
---|
478 | la = la_tmp; |
---|
479 | } |
---|
480 | LLE_ADDREF(la); |
---|
481 | LLE_WUNLOCK(la); |
---|
482 | } |
---|
483 | |
---|
484 | return (la); |
---|
485 | } |
---|
486 | |
---|
487 | /* |
---|
488 | * Free all entries from given table and free itself. |
---|
489 | */ |
---|
490 | |
---|
491 | static int |
---|
492 | lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg) |
---|
493 | { |
---|
494 | struct llentries *dchain; |
---|
495 | |
---|
496 | dchain = (struct llentries *)farg; |
---|
497 | |
---|
498 | LLE_WLOCK(lle); |
---|
499 | LIST_INSERT_HEAD(dchain, lle, lle_chain); |
---|
500 | |
---|
501 | return (0); |
---|
502 | } |
---|
503 | |
---|
504 | /* |
---|
505 | * Free all entries from given table and free itself. |
---|
506 | */ |
---|
507 | void |
---|
508 | lltable_free(struct lltable *llt) |
---|
509 | { |
---|
510 | struct llentry *lle, *next; |
---|
511 | struct llentries dchain; |
---|
512 | |
---|
513 | KASSERT(llt != NULL, ("%s: llt is NULL", __func__)); |
---|
514 | |
---|
515 | lltable_unlink(llt); |
---|
516 | |
---|
517 | LIST_INIT(&dchain); |
---|
518 | IF_AFDATA_WLOCK(llt->llt_ifp); |
---|
519 | /* Push all lles to @dchain */ |
---|
520 | lltable_foreach_lle(llt, lltable_free_cb, &dchain); |
---|
521 | llentries_unlink(llt, &dchain); |
---|
522 | IF_AFDATA_WUNLOCK(llt->llt_ifp); |
---|
523 | |
---|
524 | LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) { |
---|
525 | if (callout_stop(&lle->lle_timer) > 0) |
---|
526 | LLE_REMREF(lle); |
---|
527 | llentry_free(lle); |
---|
528 | } |
---|
529 | |
---|
530 | llt->llt_free_tbl(llt); |
---|
531 | } |
---|
532 | |
---|
533 | #if 0 |
---|
534 | void |
---|
535 | lltable_drain(int af) |
---|
536 | { |
---|
537 | struct lltable *llt; |
---|
538 | struct llentry *lle; |
---|
539 | register int i; |
---|
540 | |
---|
541 | LLTABLE_LIST_RLOCK(); |
---|
542 | SLIST_FOREACH(llt, &V_lltables, llt_link) { |
---|
543 | if (llt->llt_af != af) |
---|
544 | continue; |
---|
545 | |
---|
546 | for (i=0; i < llt->llt_hsize; i++) { |
---|
547 | LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { |
---|
548 | LLE_WLOCK(lle); |
---|
549 | if (lle->la_hold) { |
---|
550 | m_freem(lle->la_hold); |
---|
551 | lle->la_hold = NULL; |
---|
552 | } |
---|
553 | LLE_WUNLOCK(lle); |
---|
554 | } |
---|
555 | } |
---|
556 | } |
---|
557 | LLTABLE_LIST_RUNLOCK(); |
---|
558 | } |
---|
559 | #endif |
---|
560 | |
---|
561 | /* |
---|
562 | * Deletes an address from given lltable. |
---|
563 | * Used for userland interaction to remove |
---|
564 | * individual entries. Skips entries added by OS. |
---|
565 | */ |
---|
566 | int |
---|
567 | lltable_delete_addr(struct lltable *llt, u_int flags, |
---|
568 | const struct sockaddr *l3addr) |
---|
569 | { |
---|
570 | struct llentry *lle; |
---|
571 | struct ifnet *ifp; |
---|
572 | |
---|
573 | ifp = llt->llt_ifp; |
---|
574 | IF_AFDATA_WLOCK(ifp); |
---|
575 | lle = lla_lookup(llt, LLE_EXCLUSIVE, l3addr); |
---|
576 | |
---|
577 | if (lle == NULL) { |
---|
578 | IF_AFDATA_WUNLOCK(ifp); |
---|
579 | return (ENOENT); |
---|
580 | } |
---|
581 | if ((lle->la_flags & LLE_IFADDR) != 0 && (flags & LLE_IFADDR) == 0) { |
---|
582 | IF_AFDATA_WUNLOCK(ifp); |
---|
583 | LLE_WUNLOCK(lle); |
---|
584 | return (EPERM); |
---|
585 | } |
---|
586 | |
---|
587 | lltable_unlink_entry(llt, lle); |
---|
588 | IF_AFDATA_WUNLOCK(ifp); |
---|
589 | |
---|
590 | llt->llt_delete_entry(llt, lle); |
---|
591 | |
---|
592 | return (0); |
---|
593 | } |
---|
594 | |
---|
595 | void |
---|
596 | lltable_prefix_free(int af, struct sockaddr *addr, struct sockaddr *mask, |
---|
597 | u_int flags) |
---|
598 | { |
---|
599 | struct lltable *llt; |
---|
600 | |
---|
601 | LLTABLE_LIST_RLOCK(); |
---|
602 | SLIST_FOREACH(llt, &V_lltables, llt_link) { |
---|
603 | if (llt->llt_af != af) |
---|
604 | continue; |
---|
605 | |
---|
606 | llt->llt_prefix_free(llt, addr, mask, flags); |
---|
607 | } |
---|
608 | LLTABLE_LIST_RUNLOCK(); |
---|
609 | } |
---|
610 | |
---|
611 | struct lltable * |
---|
612 | lltable_allocate_htbl(uint32_t hsize) |
---|
613 | { |
---|
614 | struct lltable *llt; |
---|
615 | int i; |
---|
616 | |
---|
617 | llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK | M_ZERO); |
---|
618 | llt->llt_hsize = hsize; |
---|
619 | llt->lle_head = malloc(sizeof(struct llentries) * hsize, |
---|
620 | M_LLTABLE, M_WAITOK | M_ZERO); |
---|
621 | |
---|
622 | for (i = 0; i < llt->llt_hsize; i++) |
---|
623 | LIST_INIT(&llt->lle_head[i]); |
---|
624 | |
---|
625 | /* Set some default callbacks */ |
---|
626 | llt->llt_link_entry = htable_link_entry; |
---|
627 | llt->llt_unlink_entry = htable_unlink_entry; |
---|
628 | llt->llt_prefix_free = htable_prefix_free; |
---|
629 | llt->llt_foreach_entry = htable_foreach_lle; |
---|
630 | llt->llt_free_tbl = htable_free_tbl; |
---|
631 | |
---|
632 | return (llt); |
---|
633 | } |
---|
634 | |
---|
635 | /* |
---|
636 | * Links lltable to global llt list. |
---|
637 | */ |
---|
638 | void |
---|
639 | lltable_link(struct lltable *llt) |
---|
640 | { |
---|
641 | |
---|
642 | LLTABLE_LIST_WLOCK(); |
---|
643 | SLIST_INSERT_HEAD(&V_lltables, llt, llt_link); |
---|
644 | LLTABLE_LIST_WUNLOCK(); |
---|
645 | } |
---|
646 | |
---|
647 | static void |
---|
648 | lltable_unlink(struct lltable *llt) |
---|
649 | { |
---|
650 | |
---|
651 | LLTABLE_LIST_WLOCK(); |
---|
652 | SLIST_REMOVE(&V_lltables, llt, lltable, llt_link); |
---|
653 | LLTABLE_LIST_WUNLOCK(); |
---|
654 | |
---|
655 | } |
---|
656 | |
---|
657 | /* |
---|
658 | * External methods used by lltable consumers |
---|
659 | */ |
---|
660 | |
---|
661 | int |
---|
662 | lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg) |
---|
663 | { |
---|
664 | |
---|
665 | return (llt->llt_foreach_entry(llt, f, farg)); |
---|
666 | } |
---|
667 | |
---|
668 | struct llentry * |
---|
669 | lltable_alloc_entry(struct lltable *llt, u_int flags, |
---|
670 | const struct sockaddr *l3addr) |
---|
671 | { |
---|
672 | |
---|
673 | return (llt->llt_alloc_entry(llt, flags, l3addr)); |
---|
674 | } |
---|
675 | |
---|
676 | void |
---|
677 | lltable_free_entry(struct lltable *llt, struct llentry *lle) |
---|
678 | { |
---|
679 | |
---|
680 | llt->llt_free_entry(llt, lle); |
---|
681 | } |
---|
682 | |
---|
683 | void |
---|
684 | lltable_link_entry(struct lltable *llt, struct llentry *lle) |
---|
685 | { |
---|
686 | |
---|
687 | llt->llt_link_entry(llt, lle); |
---|
688 | } |
---|
689 | |
---|
690 | void |
---|
691 | lltable_unlink_entry(struct lltable *llt, struct llentry *lle) |
---|
692 | { |
---|
693 | |
---|
694 | llt->llt_unlink_entry(lle); |
---|
695 | } |
---|
696 | |
---|
697 | void |
---|
698 | lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa) |
---|
699 | { |
---|
700 | struct lltable *llt; |
---|
701 | |
---|
702 | llt = lle->lle_tbl; |
---|
703 | llt->llt_fill_sa_entry(lle, sa); |
---|
704 | } |
---|
705 | |
---|
706 | struct ifnet * |
---|
707 | lltable_get_ifp(const struct lltable *llt) |
---|
708 | { |
---|
709 | |
---|
710 | return (llt->llt_ifp); |
---|
711 | } |
---|
712 | |
---|
713 | int |
---|
714 | lltable_get_af(const struct lltable *llt) |
---|
715 | { |
---|
716 | |
---|
717 | return (llt->llt_af); |
---|
718 | } |
---|
719 | |
---|
720 | /* |
---|
721 | * Called in route_output when rtm_flags contains RTF_LLDATA. |
---|
722 | */ |
---|
723 | int |
---|
724 | lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) |
---|
725 | { |
---|
726 | struct sockaddr_dl *dl = |
---|
727 | (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; |
---|
728 | struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST]; |
---|
729 | struct ifnet *ifp; |
---|
730 | struct lltable *llt; |
---|
731 | struct llentry *lle, *lle_tmp; |
---|
732 | uint8_t linkhdr[LLE_MAX_LINKHDR]; |
---|
733 | size_t linkhdrsize; |
---|
734 | int lladdr_off; |
---|
735 | u_int laflags = 0; |
---|
736 | int error; |
---|
737 | |
---|
738 | KASSERT(dl != NULL && dl->sdl_family == AF_LINK, |
---|
739 | ("%s: invalid dl\n", __func__)); |
---|
740 | |
---|
741 | ifp = ifnet_byindex(dl->sdl_index); |
---|
742 | if (ifp == NULL) { |
---|
743 | log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", |
---|
744 | __func__, dl->sdl_index); |
---|
745 | return EINVAL; |
---|
746 | } |
---|
747 | |
---|
748 | /* XXX linked list may be too expensive */ |
---|
749 | LLTABLE_LIST_RLOCK(); |
---|
750 | SLIST_FOREACH(llt, &V_lltables, llt_link) { |
---|
751 | if (llt->llt_af == dst->sa_family && |
---|
752 | llt->llt_ifp == ifp) |
---|
753 | break; |
---|
754 | } |
---|
755 | LLTABLE_LIST_RUNLOCK(); |
---|
756 | KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n")); |
---|
757 | |
---|
758 | error = 0; |
---|
759 | |
---|
760 | switch (rtm->rtm_type) { |
---|
761 | case RTM_ADD: |
---|
762 | /* Add static LLE */ |
---|
763 | laflags = 0; |
---|
764 | if (rtm->rtm_rmx.rmx_expire == 0) |
---|
765 | laflags = LLE_STATIC; |
---|
766 | lle = lltable_alloc_entry(llt, laflags, dst); |
---|
767 | if (lle == NULL) |
---|
768 | return (ENOMEM); |
---|
769 | |
---|
770 | linkhdrsize = sizeof(linkhdr); |
---|
771 | if (lltable_calc_llheader(ifp, dst->sa_family, LLADDR(dl), |
---|
772 | linkhdr, &linkhdrsize, &lladdr_off) != 0) |
---|
773 | return (EINVAL); |
---|
774 | lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, |
---|
775 | lladdr_off); |
---|
776 | if ((rtm->rtm_flags & RTF_ANNOUNCE)) |
---|
777 | lle->la_flags |= LLE_PUB; |
---|
778 | lle->la_expire = rtm->rtm_rmx.rmx_expire; |
---|
779 | |
---|
780 | laflags = lle->la_flags; |
---|
781 | |
---|
782 | /* Try to link new entry */ |
---|
783 | lle_tmp = NULL; |
---|
784 | IF_AFDATA_WLOCK(ifp); |
---|
785 | LLE_WLOCK(lle); |
---|
786 | lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, dst); |
---|
787 | if (lle_tmp != NULL) { |
---|
788 | /* Check if we are trying to replace immutable entry */ |
---|
789 | if ((lle_tmp->la_flags & LLE_IFADDR) != 0) { |
---|
790 | IF_AFDATA_WUNLOCK(ifp); |
---|
791 | LLE_WUNLOCK(lle_tmp); |
---|
792 | lltable_free_entry(llt, lle); |
---|
793 | return (EPERM); |
---|
794 | } |
---|
795 | /* Unlink existing entry from table */ |
---|
796 | lltable_unlink_entry(llt, lle_tmp); |
---|
797 | } |
---|
798 | lltable_link_entry(llt, lle); |
---|
799 | IF_AFDATA_WUNLOCK(ifp); |
---|
800 | |
---|
801 | if (lle_tmp != NULL) { |
---|
802 | EVENTHANDLER_INVOKE(lle_event, lle_tmp,LLENTRY_EXPIRED); |
---|
803 | lltable_free_entry(llt, lle_tmp); |
---|
804 | } |
---|
805 | |
---|
806 | /* |
---|
807 | * By invoking LLE handler here we might get |
---|
808 | * two events on static LLE entry insertion |
---|
809 | * in routing socket. However, since we might have |
---|
810 | * other subscribers we need to generate this event. |
---|
811 | */ |
---|
812 | EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED); |
---|
813 | LLE_WUNLOCK(lle); |
---|
814 | #ifdef INET |
---|
815 | /* gratuitous ARP */ |
---|
816 | if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) |
---|
817 | arprequest(ifp, |
---|
818 | &((struct sockaddr_in *)dst)->sin_addr, |
---|
819 | &((struct sockaddr_in *)dst)->sin_addr, |
---|
820 | (u_char *)LLADDR(dl)); |
---|
821 | #endif |
---|
822 | |
---|
823 | break; |
---|
824 | |
---|
825 | case RTM_DELETE: |
---|
826 | return (lltable_delete_addr(llt, 0, dst)); |
---|
827 | |
---|
828 | default: |
---|
829 | error = EINVAL; |
---|
830 | } |
---|
831 | |
---|
832 | return (error); |
---|
833 | } |
---|
834 | |
---|
835 | #ifdef DDB |
---|
836 | struct llentry_sa { |
---|
837 | struct llentry base; |
---|
838 | struct sockaddr l3_addr; |
---|
839 | }; |
---|
840 | |
---|
841 | static void |
---|
842 | llatbl_lle_show(struct llentry_sa *la) |
---|
843 | { |
---|
844 | struct llentry *lle; |
---|
845 | uint8_t octet[6]; |
---|
846 | |
---|
847 | lle = &la->base; |
---|
848 | db_printf("lle=%p\n", lle); |
---|
849 | db_printf(" lle_next=%p\n", lle->lle_next.le_next); |
---|
850 | db_printf(" lle_lock=%p\n", &lle->lle_lock); |
---|
851 | db_printf(" lle_tbl=%p\n", lle->lle_tbl); |
---|
852 | db_printf(" lle_head=%p\n", lle->lle_head); |
---|
853 | db_printf(" la_hold=%p\n", lle->la_hold); |
---|
854 | db_printf(" la_numheld=%d\n", lle->la_numheld); |
---|
855 | db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire); |
---|
856 | db_printf(" la_flags=0x%04x\n", lle->la_flags); |
---|
857 | db_printf(" la_asked=%u\n", lle->la_asked); |
---|
858 | db_printf(" la_preempt=%u\n", lle->la_preempt); |
---|
859 | db_printf(" ln_state=%d\n", lle->ln_state); |
---|
860 | db_printf(" ln_router=%u\n", lle->ln_router); |
---|
861 | db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick); |
---|
862 | db_printf(" lle_refcnt=%d\n", lle->lle_refcnt); |
---|
863 | bcopy(lle->ll_addr, octet, sizeof(octet)); |
---|
864 | db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n", |
---|
865 | octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]); |
---|
866 | db_printf(" lle_timer=%p\n", &lle->lle_timer); |
---|
867 | |
---|
868 | switch (la->l3_addr.sa_family) { |
---|
869 | #ifdef INET |
---|
870 | case AF_INET: |
---|
871 | { |
---|
872 | struct sockaddr_in *sin; |
---|
873 | char l3s[INET_ADDRSTRLEN]; |
---|
874 | |
---|
875 | sin = (struct sockaddr_in *)&la->l3_addr; |
---|
876 | inet_ntoa_r(sin->sin_addr, l3s); |
---|
877 | db_printf(" l3_addr=%s\n", l3s); |
---|
878 | break; |
---|
879 | } |
---|
880 | #endif |
---|
881 | #ifdef INET6 |
---|
882 | case AF_INET6: |
---|
883 | { |
---|
884 | struct sockaddr_in6 *sin6; |
---|
885 | char l3s[INET6_ADDRSTRLEN]; |
---|
886 | |
---|
887 | sin6 = (struct sockaddr_in6 *)&la->l3_addr; |
---|
888 | ip6_sprintf(l3s, &sin6->sin6_addr); |
---|
889 | db_printf(" l3_addr=%s\n", l3s); |
---|
890 | break; |
---|
891 | } |
---|
892 | #endif |
---|
893 | default: |
---|
894 | db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family); |
---|
895 | break; |
---|
896 | } |
---|
897 | } |
---|
898 | |
---|
899 | DB_SHOW_COMMAND(llentry, db_show_llentry) |
---|
900 | { |
---|
901 | |
---|
902 | if (!have_addr) { |
---|
903 | db_printf("usage: show llentry <struct llentry *>\n"); |
---|
904 | return; |
---|
905 | } |
---|
906 | |
---|
907 | llatbl_lle_show((struct llentry_sa *)addr); |
---|
908 | } |
---|
909 | |
---|
910 | static void |
---|
911 | llatbl_llt_show(struct lltable *llt) |
---|
912 | { |
---|
913 | int i; |
---|
914 | struct llentry *lle; |
---|
915 | |
---|
916 | db_printf("llt=%p llt_af=%d llt_ifp=%p\n", |
---|
917 | llt, llt->llt_af, llt->llt_ifp); |
---|
918 | |
---|
919 | for (i = 0; i < llt->llt_hsize; i++) { |
---|
920 | LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { |
---|
921 | |
---|
922 | llatbl_lle_show((struct llentry_sa *)lle); |
---|
923 | if (db_pager_quit) |
---|
924 | return; |
---|
925 | } |
---|
926 | } |
---|
927 | } |
---|
928 | |
---|
929 | DB_SHOW_COMMAND(lltable, db_show_lltable) |
---|
930 | { |
---|
931 | |
---|
932 | if (!have_addr) { |
---|
933 | db_printf("usage: show lltable <struct lltable *>\n"); |
---|
934 | return; |
---|
935 | } |
---|
936 | |
---|
937 | llatbl_llt_show((struct lltable *)addr); |
---|
938 | } |
---|
939 | |
---|
940 | DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables) |
---|
941 | { |
---|
942 | VNET_ITERATOR_DECL(vnet_iter); |
---|
943 | struct lltable *llt; |
---|
944 | |
---|
945 | VNET_FOREACH(vnet_iter) { |
---|
946 | CURVNET_SET_QUIET(vnet_iter); |
---|
947 | #ifdef VIMAGE |
---|
948 | db_printf("vnet=%p\n", curvnet); |
---|
949 | #endif |
---|
950 | SLIST_FOREACH(llt, &V_lltables, llt_link) { |
---|
951 | db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n", |
---|
952 | llt, llt->llt_af, llt->llt_ifp, |
---|
953 | (llt->llt_ifp != NULL) ? |
---|
954 | llt->llt_ifp->if_xname : "?"); |
---|
955 | if (have_addr && addr != 0) /* verbose */ |
---|
956 | llatbl_llt_show(llt); |
---|
957 | if (db_pager_quit) { |
---|
958 | CURVNET_RESTORE(); |
---|
959 | return; |
---|
960 | } |
---|
961 | } |
---|
962 | CURVNET_RESTORE(); |
---|
963 | } |
---|
964 | } |
---|
965 | #endif |
---|