1/* $NetBSD: if_llatbl.c,v 1.15 2016/10/11 12:32:30 roy Exp $ */
2/*
3 * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
4 * Copyright (c) 2004-2008 Qing Li. All rights reserved.
5 * Copyright (c) 2008 Kip Macy. All rights reserved.
6 * Copyright (c) 2015 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30#include <sys/cdefs.h>
31
32#ifdef _KERNEL_OPT
33#include "opt_ddb.h"
34#include "opt_inet.h"
35#include "opt_inet6.h"
36#endif
37
38#include "arp.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/malloc.h>
43#include <sys/mbuf.h>
44#include <sys/syslog.h>
45#include <sys/sysctl.h>
46#include <sys/socket.h>
47#include <sys/socketvar.h>
48#include <sys/kernel.h>
49#include <sys/lock.h>
50#include <sys/mutex.h>
51#include <sys/rwlock.h>
52
53#ifdef DDB
54#include <ddb/ddb.h>
55#endif
56
57#include <netinet/in.h>
58#include <net/if_llatbl.h>
59#include <net/if.h>
60#include <net/if_dl.h>
61#include <net/route.h>
62#include <netinet/if_inarp.h>
63#include <netinet/in_var.h>
64#include <netinet6/in6_var.h>
65#include <netinet6/nd6.h>
66
67static SLIST_HEAD(, lltable) lltables;
68krwlock_t lltable_rwlock;
69
70static void lltable_unlink(struct lltable *llt);
71static void llentries_unlink(struct lltable *llt, struct llentries *head);
72
73static void htable_unlink_entry(struct llentry *lle);
74static void htable_link_entry(struct lltable *llt, struct llentry *lle);
75static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
76 void *farg);
77
78int
79lltable_dump_entry(struct lltable *llt, struct llentry *lle,
80 struct rt_walkarg *w, struct sockaddr *sa)
81{
82 struct ifnet *ifp = llt->llt_ifp;
83 int error;
84 void *a;
85 struct sockaddr_dl sdl;
86 int size;
87 struct rt_addrinfo info;
88
89 memset(&info, 0, sizeof(info));
90 info.rti_info[RTAX_DST] = sa;
91
92 a = (lle->la_flags & LLE_VALID) == LLE_VALID ? &lle->ll_addr : NULL;
93 if (sockaddr_dl_init(&sdl, sizeof(sdl), ifp->if_index, ifp->if_type,
94 NULL, 0, a, ifp->if_addrlen) == NULL)
95 return EINVAL;
96
97 info.rti_info[RTAX_GATEWAY] = sstocsa(&sdl);
98 if (sa->sa_family == AF_INET && lle->la_flags & LLE_PUB) {
99 struct sockaddr_inarp *sin;
100 sin = (struct sockaddr_inarp *)sa;
101 sin->sin_other = SIN_PROXY;
102 }
103 if ((error = rt_msg3(RTM_GET, &info, 0, w, &size)))
104 return error;
105 if (w->w_where && w->w_tmem && w->w_needed <= 0) {
106 struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem;
107
108 /* Need to copy by myself */
109 rtm->rtm_rmx.rmx_expire =
110 (lle->la_flags & LLE_STATIC) ? 0 : lle->la_expire;
111 rtm->rtm_flags |= RTF_HOST; /* For ndp */
112 rtm->rtm_flags |= (lle->la_flags & LLE_STATIC) ? RTF_STATIC : 0;
113 if (lle->la_flags & LLE_PUB)
114 rtm->rtm_flags |= RTF_ANNOUNCE;
115 rtm->rtm_addrs = info.rti_addrs;
116 if ((error = copyout(rtm, w->w_where, size)) != 0)
117 w->w_where = NULL;
118 else
119 w->w_where = (char *)w->w_where + size;
120 }
121
122 return error;
123}
124
125/*
126 * Dump lle state for a specific address family.
127 */
128static int
129lltable_dump_af(struct lltable *llt, struct rt_walkarg *w)
130{
131 int error;
132
133 LLTABLE_LOCK_ASSERT();
134
135 if (llt->llt_ifp->if_flags & IFF_LOOPBACK)
136 return (0);
137 error = 0;
138
139 IF_AFDATA_RLOCK(llt->llt_ifp);
140 error = lltable_foreach_lle(llt,
141 (llt_foreach_cb_t *)llt->llt_dump_entry, w);
142 IF_AFDATA_RUNLOCK(llt->llt_ifp);
143
144 return (error);
145}
146
147/*
148 * Dump arp state for a specific address family.
149 */
150int
151lltable_sysctl_dumparp(int af, struct rt_walkarg *w)
152{
153 struct lltable *llt;
154 int error = 0;
155
156 LLTABLE_RLOCK();
157 SLIST_FOREACH(llt, &lltables, llt_link) {
158 if (llt->llt_af == af) {
159 error = lltable_dump_af(llt, w);
160 if (error != 0)
161 goto done;
162 }
163 }
164done:
165 LLTABLE_RUNLOCK();
166 return (error);
167}
168
169/*
170 * Common function helpers for chained hash table.
171 */
172
173/*
174 * Runs specified callback for each entry in @llt.
175 * Caller does the locking.
176 *
177 */
178static int
179htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
180{
181 struct llentry *lle, *next;
182 int i, error;
183
184 error = 0;
185
186 for (i = 0; i < llt->llt_hsize; i++) {
187 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
188 error = f(llt, lle, farg);
189 if (error != 0)
190 break;
191 }
192 }
193
194 return (error);
195}
196
197static void
198htable_link_entry(struct lltable *llt, struct llentry *lle)
199{
200 struct llentries *lleh;
201 uint32_t hashidx;
202
203 if ((lle->la_flags & LLE_LINKED) != 0)
204 return;
205
206 IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
207
208 hashidx = llt->llt_hash(lle, llt->llt_hsize);
209 lleh = &llt->lle_head[hashidx];
210
211 lle->lle_tbl = llt;
212 lle->lle_head = lleh;
213 lle->la_flags |= LLE_LINKED;
214 LIST_INSERT_HEAD(lleh, lle, lle_next);
215
216 llt->llt_lle_count++;
217}
218
219static void
220htable_unlink_entry(struct llentry *lle)
221{
222
223 if ((lle->la_flags & LLE_LINKED) != 0) {
224 IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
225 LIST_REMOVE(lle, lle_next);
226 lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
227#if 0
228 lle->lle_tbl = NULL;
229 lle->lle_head = NULL;
230#endif
231 KASSERT(lle->lle_tbl->llt_lle_count != 0);
232 lle->lle_tbl->llt_lle_count--;
233 }
234}
235
236struct prefix_match_data {
237 const struct sockaddr *prefix;
238 const struct sockaddr *mask;
239 struct llentries dchain;
240 u_int flags;
241};
242
243static int
244htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
245{
246 struct prefix_match_data *pmd;
247
248 pmd = (struct prefix_match_data *)farg;
249
250 if (llt->llt_match_prefix(pmd->prefix, pmd->mask, pmd->flags, lle)) {
251 LLE_WLOCK(lle);
252 LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain);
253 }
254
255 return (0);
256}
257
258static void
259htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
260 const struct sockaddr *mask, u_int flags)
261{
262 struct llentry *lle, *next;
263 struct prefix_match_data pmd;
264
265 memset(&pmd, 0, sizeof(pmd));
266 pmd.prefix = prefix;
267 pmd.mask = mask;
268 pmd.flags = flags;
269 LIST_INIT(&pmd.dchain);
270
271 IF_AFDATA_WLOCK(llt->llt_ifp);
272 /* Push matching lles to chain */
273 lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd);
274
275 llentries_unlink(llt, &pmd.dchain);
276 IF_AFDATA_WUNLOCK(llt->llt_ifp);
277
278 LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
279 llt->llt_free_entry(llt, lle);
280}
281
282static void
283htable_free_tbl(struct lltable *llt)
284{
285
286 free(llt->lle_head, M_LLTABLE);
287 free(llt, M_LLTABLE);
288}
289
290static void
291llentries_unlink(struct lltable *llt, struct llentries *head)
292{
293 struct llentry *lle, *next;
294
295 LIST_FOREACH_SAFE(lle, head, lle_chain, next)
296 llt->llt_unlink_entry(lle);
297}
298
299/*
300 * Helper function used to drop all mbufs in hold queue.
301 *
302 * Returns the number of held packets, if any, that were dropped.
303 */
304size_t
305lltable_drop_entry_queue(struct llentry *lle)
306{
307 size_t pkts_dropped;
308 struct mbuf *next;
309
310 LLE_WLOCK_ASSERT(lle);
311
312 pkts_dropped = 0;
313 while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
314 next = lle->la_hold->m_nextpkt;
315 m_freem(lle->la_hold);
316 lle->la_hold = next;
317 lle->la_numheld--;
318 pkts_dropped++;
319 }
320
321 KASSERTMSG(lle->la_numheld == 0,
322 "la_numheld %d > 0, pkts_droped %zd",
323 lle->la_numheld, pkts_dropped);
324
325 return (pkts_dropped);
326}
327
328/*
329 * Deletes an address from the address table.
330 * This function is called by the timer functions
331 * such as arptimer() and nd6_llinfo_timer(), and
332 * the caller does the locking.
333 *
334 * Returns the number of held packets, if any, that were dropped.
335 */
336size_t
337llentry_free(struct llentry *lle)
338{
339 struct lltable *llt;
340 size_t pkts_dropped;
341
342 LLE_WLOCK_ASSERT(lle);
343
344 if ((lle->la_flags & LLE_LINKED) != 0) {
345 llt = lle->lle_tbl;
346
347 IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
348 llt->llt_unlink_entry(lle);
349 }
350
351 pkts_dropped = lltable_drop_entry_queue(lle);
352
353 LLE_FREE_LOCKED(lle);
354
355 return (pkts_dropped);
356}
357
358/*
359 * (al)locate an llentry for address dst (equivalent to rtalloc for new-arp).
360 *
361 * If found the llentry * is returned referenced and unlocked.
362 */
363struct llentry *
364llentry_alloc(struct ifnet *ifp, struct lltable *lt,
365 struct sockaddr_storage *dst)
366{
367 struct llentry *la;
368
369 IF_AFDATA_RLOCK(ifp);
370 la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
371 IF_AFDATA_RUNLOCK(ifp);
372 if ((la == NULL) &&
373#ifdef __FreeBSD__
374 (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
375#else /* XXX */
376 (ifp->if_flags & IFF_NOARP) == 0) {
377#endif
378 IF_AFDATA_WLOCK(ifp);
379 la = lla_create(lt, 0, (struct sockaddr *)dst);
380 IF_AFDATA_WUNLOCK(ifp);
381 }
382
383 if (la != NULL) {
384 LLE_ADDREF(la);
385 LLE_WUNLOCK(la);
386 }
387
388 return (la);
389}
390
391/*
392 * Free all entries from given table and free itself.
393 */
394
395static int
396lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
397{
398 struct llentries *dchain;
399
400 dchain = (struct llentries *)farg;
401
402 LLE_WLOCK(lle);
403 LIST_INSERT_HEAD(dchain, lle, lle_chain);
404
405 return (0);
406}
407
408/*
409 * Free all entries from given table.
410 */
411void
412lltable_purge_entries(struct lltable *llt)
413{
414 struct llentry *lle, *next;
415 struct llentries dchain;
416
417 KASSERTMSG(llt != NULL, "llt is NULL");
418
419 LIST_INIT(&dchain);
420 IF_AFDATA_WLOCK(llt->llt_ifp);
421 /* Push all lles to @dchain */
422 lltable_foreach_lle(llt, lltable_free_cb, &dchain);
423 llentries_unlink(llt, &dchain);
424 IF_AFDATA_WUNLOCK(llt->llt_ifp);
425
426 LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) {
427 if (callout_halt(&lle->la_timer, &lle->lle_lock))
428 LLE_REMREF(lle);
429 llentry_free(lle);
430 }
431
432}
433
434/*
435 * Free all entries from given table and free itself.
436 */
437void
438lltable_free(struct lltable *llt)
439{
440
441 KASSERTMSG(llt != NULL, "llt is NULL");
442
443 lltable_unlink(llt);
444 lltable_purge_entries(llt);
445 llt->llt_free_tbl(llt);
446}
447
448void
449lltable_drain(int af)
450{
451 struct lltable *llt;
452 struct llentry *lle;
453 register int i;
454
455 LLTABLE_RLOCK();
456 SLIST_FOREACH(llt, &lltables, llt_link) {
457 if (llt->llt_af != af)
458 continue;
459
460 for (i=0; i < llt->llt_hsize; i++) {
461 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
462 LLE_WLOCK(lle);
463 lltable_drop_entry_queue(lle);
464 LLE_WUNLOCK(lle);
465 }
466 }
467 }
468 LLTABLE_RUNLOCK();
469}
470
471void
472lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
473 u_int flags)
474{
475 struct lltable *llt;
476
477 LLTABLE_RLOCK();
478 SLIST_FOREACH(llt, &lltables, llt_link) {
479 if (llt->llt_af != af)
480 continue;
481
482 llt->llt_prefix_free(llt, prefix, mask, flags);
483 }
484 LLTABLE_RUNLOCK();
485}
486
487struct lltable *
488lltable_allocate_htbl(uint32_t hsize)
489{
490 struct lltable *llt;
491 int i;
492
493 llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK | M_ZERO);
494 llt->llt_hsize = hsize;
495 llt->lle_head = malloc(sizeof(struct llentries) * hsize,
496 M_LLTABLE, M_WAITOK | M_ZERO);
497
498 for (i = 0; i < llt->llt_hsize; i++)
499 LIST_INIT(&llt->lle_head[i]);
500
501 /* Set some default callbacks */
502 llt->llt_link_entry = htable_link_entry;
503 llt->llt_unlink_entry = htable_unlink_entry;
504 llt->llt_prefix_free = htable_prefix_free;
505 llt->llt_foreach_entry = htable_foreach_lle;
506
507 llt->llt_free_tbl = htable_free_tbl;
508
509 return (llt);
510}
511
512/*
513 * Links lltable to global llt list.
514 */
515void
516lltable_link(struct lltable *llt)
517{
518
519 LLTABLE_WLOCK();
520 SLIST_INSERT_HEAD(&lltables, llt, llt_link);
521 LLTABLE_WUNLOCK();
522}
523
524static void
525lltable_unlink(struct lltable *llt)
526{
527
528 LLTABLE_WLOCK();
529 SLIST_REMOVE(&lltables, llt, lltable, llt_link);
530 LLTABLE_WUNLOCK();
531
532}
533
534/*
535 * External methods used by lltable consumers
536 */
537
538int
539lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
540{
541
542 return (llt->llt_foreach_entry(llt, f, farg));
543}
544
545void
546lltable_link_entry(struct lltable *llt, struct llentry *lle)
547{
548
549 llt->llt_link_entry(llt, lle);
550}
551
552void
553lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
554{
555
556 llt->llt_unlink_entry(lle);
557}
558
559void
560lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
561{
562 struct lltable *llt;
563
564 llt = lle->lle_tbl;
565 llt->llt_fill_sa_entry(lle, sa);
566}
567
568struct ifnet *
569lltable_get_ifp(const struct lltable *llt)
570{
571
572 return (llt->llt_ifp);
573}
574
575int
576lltable_get_af(const struct lltable *llt)
577{
578
579 return (llt->llt_af);
580}
581
582/*
583 * Called in route_output when rtm_flags contains RTF_LLDATA.
584 */
585int
586lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expire,
587 struct rt_addrinfo *info, int sdl_index)
588{
589 const struct sockaddr_dl *dl = satocsdl(info->rti_info[RTAX_GATEWAY]);
590 const struct sockaddr *dst = info->rti_info[RTAX_DST];
591 struct ifnet *ifp;
592 struct lltable *llt;
593 struct llentry *lle;
594 u_int laflags;
595 int error;
596 struct psref psref;
597 int bound;
598
599 KASSERTMSG(dl != NULL && dl->sdl_family == AF_LINK, "invalid dl");
600
601 bound = curlwp_bind();
602 if (sdl_index != 0)
603 ifp = if_get_byindex(sdl_index, &psref);
604 else
605 ifp = if_get_byindex(dl->sdl_index, &psref);
606 if (ifp == NULL) {
607 curlwp_bindx(bound);
608 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n",
609 __func__, sdl_index != 0 ? sdl_index : dl->sdl_index);
610 return EINVAL;
611 }
612
613 /* XXX linked list may be too expensive */
614 LLTABLE_RLOCK();
615 SLIST_FOREACH(llt, &lltables, llt_link) {
616 if (llt->llt_af == dst->sa_family &&
617 llt->llt_ifp == ifp)
618 break;
619 }
620 LLTABLE_RUNLOCK();
621 KASSERTMSG(llt != NULL, "Yep, ugly hacks are bad");
622
623 error = 0;
624
625 switch (rtm_type) {
626 case RTM_ADD:
627 /* Add static LLE */
628 IF_AFDATA_WLOCK(ifp);
629 lle = lla_lookup(llt, 0, dst);
630
631 /* Cannot overwrite an existing static entry */
632 if (lle != NULL &&
633 (lle->la_flags & LLE_STATIC || lle->la_expire == 0)) {
634 LLE_RUNLOCK(lle);
635 IF_AFDATA_WUNLOCK(ifp);
636 error = EEXIST;
637 goto out;
638 }
639 if (lle != NULL)
640 LLE_RUNLOCK(lle);
641
642 lle = lla_create(llt, 0, dst);
643 if (lle == NULL) {
644 IF_AFDATA_WUNLOCK(ifp);
645 error = ENOMEM;
646 goto out;
647 }
648
649 KASSERT(ifp->if_addrlen <= sizeof(lle->ll_addr));
650 memcpy(&lle->ll_addr, CLLADDR(dl), ifp->if_addrlen);
651 if ((rtm_flags & RTF_ANNOUNCE))
652 lle->la_flags |= LLE_PUB;
653 lle->la_flags |= LLE_VALID;
654#ifdef INET6
655 /*
656 * ND6
657 */
658 if (dst->sa_family == AF_INET6)
659 lle->ln_state = ND6_LLINFO_REACHABLE;
660#endif
661 /*
662 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
663 */
664
665 if (rtm_expire == 0) {
666 lle->la_flags |= LLE_STATIC;
667 lle->la_expire = 0;
668 } else
669 lle->la_expire = rtm_expire;
670 laflags = lle->la_flags;
671 LLE_WUNLOCK(lle);
672 IF_AFDATA_WUNLOCK(ifp);
673#if defined(INET) && NARP > 0
674 /* gratuitous ARP */
675 if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
676 const struct sockaddr_in *sin;
677 struct in_ifaddr *ia;
678 struct psref _psref;
679
680 sin = satocsin(dst);
681 ia = in_get_ia_on_iface_psref(sin->sin_addr,
682 ifp, &_psref);
683 if (ia != NULL) {
684 arpannounce(ifp, &ia->ia_ifa, CLLADDR(dl));
685 ia4_release(ia, &_psref);
686 }
687 }
688#else
689 (void)laflags;
690#endif
691
692 break;
693
694 case RTM_DELETE:
695 IF_AFDATA_WLOCK(ifp);
696 error = lla_delete(llt, 0, dst);
697 IF_AFDATA_WUNLOCK(ifp);
698 error = (error == 0 ? 0 : ENOENT);
699 break;
700
701 default:
702 error = EINVAL;
703 }
704
705out:
706 if_put(ifp, &psref);
707 curlwp_bindx(bound);
708 return (error);
709}
710
711void
712lltableinit(void)
713{
714
715 SLIST_INIT(&lltables);
716 rw_init(&lltable_rwlock);
717}
718
719#ifdef __FreeBSD__
720#ifdef DDB
721struct llentry_sa {
722 struct llentry base;
723 struct sockaddr l3_addr;
724};
725
726static void
727llatbl_lle_show(struct llentry_sa *la)
728{
729 struct llentry *lle;
730 uint8_t octet[6];
731
732 lle = &la->base;
733 db_printf("lle=%p\n", lle);
734 db_printf(" lle_next=%p\n", lle->lle_next.le_next);
735 db_printf(" lle_lock=%p\n", &lle->lle_lock);
736 db_printf(" lle_tbl=%p\n", lle->lle_tbl);
737 db_printf(" lle_head=%p\n", lle->lle_head);
738 db_printf(" la_hold=%p\n", lle->la_hold);
739 db_printf(" la_numheld=%d\n", lle->la_numheld);
740 db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire);
741 db_printf(" la_flags=0x%04x\n", lle->la_flags);
742 db_printf(" la_asked=%u\n", lle->la_asked);
743 db_printf(" la_preempt=%u\n", lle->la_preempt);
744 db_printf(" ln_byhint=%u\n", lle->ln_byhint);
745 db_printf(" ln_state=%d\n", lle->ln_state);
746 db_printf(" ln_router=%u\n", lle->ln_router);
747 db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick);
748 db_printf(" lle_refcnt=%d\n", lle->lle_refcnt);
749 memcopy(octet, &lle->ll_addr.mac16, sizeof(octet));
750 db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
751 octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]);
752 db_printf(" lle_timer=%p\n", &lle->lle_timer);
753
754 switch (la->l3_addr.sa_family) {
755#ifdef INET
756 case AF_INET:
757 {
758 struct sockaddr_in *sin;
759 char l3s[INET_ADDRSTRLEN];
760
761 sin = (struct sockaddr_in *)&la->l3_addr;
762 inet_ntoa_r(sin->sin_addr, l3s);
763 db_printf(" l3_addr=%s\n", l3s);
764 break;
765 }
766#endif
767#ifdef INET6
768 case AF_INET6:
769 {
770 struct sockaddr_in6 *sin6;
771 char l3s[INET6_ADDRSTRLEN];
772
773 sin6 = (struct sockaddr_in6 *)&la->l3_addr;
774 ip6_sprintf(l3s, &sin6->sin6_addr);
775 db_printf(" l3_addr=%s\n", l3s);
776 break;
777 }
778#endif
779 default:
780 db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family);
781 break;
782 }
783}
784
785DB_SHOW_COMMAND(llentry, db_show_llentry)
786{
787
788 if (!have_addr) {
789 db_printf("usage: show llentry <struct llentry *>\n");
790 return;
791 }
792
793 llatbl_lle_show((struct llentry_sa *)addr);
794}
795
796static void
797llatbl_llt_show(struct lltable *llt)
798{
799 int i;
800 struct llentry *lle;
801
802 db_printf("llt=%p llt_af=%d llt_ifp=%p\n",
803 llt, llt->llt_af, llt->llt_ifp);
804
805 for (i = 0; i < llt->llt_hsize; i++) {
806 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
807
808 llatbl_lle_show((struct llentry_sa *)lle);
809 if (db_pager_quit)
810 return;
811 }
812 }
813}
814
815DB_SHOW_COMMAND(lltable, db_show_lltable)
816{
817
818 if (!have_addr) {
819 db_printf("usage: show lltable <struct lltable *>\n");
820 return;
821 }
822
823 llatbl_llt_show((struct lltable *)addr);
824}
825
826DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables)
827{
828 VNET_ITERATOR_DECL(vnet_iter);
829 struct lltable *llt;
830
831 VNET_FOREACH(vnet_iter) {
832 CURVNET_SET_QUIET(vnet_iter);
833#ifdef VIMAGE
834 db_printf("vnet=%p\n", curvnet);
835#endif
836 SLIST_FOREACH(llt, &lltables, llt_link) {
837 db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n",
838 llt, llt->llt_af, llt->llt_ifp,
839 (llt->llt_ifp != NULL) ?
840 llt->llt_ifp->if_xname : "?");
841 if (have_addr && addr != 0) /* verbose */
842 llatbl_llt_show(llt);
843 if (db_pager_quit) {
844 CURVNET_RESTORE();
845 return;
846 }
847 }
848 CURVNET_RESTORE();
849 }
850}
851#endif /* DDB */
852#endif /* __FreeBSD__ */
853