if_llatbl.c revision 1.3 1 /* $NetBSD: if_llatbl.c,v 1.3 2015/08/31 12:57:45 pooka 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 <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/syslog.h>
43 #include <sys/sysctl.h>
44 #include <sys/socket.h>
45 #include <sys/kernel.h>
46 #include <sys/lock.h>
47 #include <sys/mutex.h>
48 #include <sys/rwlock.h>
49
50 #ifdef DDB
51 #include <ddb/ddb.h>
52 #endif
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/route.h>
59 #include <netinet/if_inarp.h>
60 #include <netinet6/in6_var.h>
61 #include <netinet6/nd6.h>
62
63 static SLIST_HEAD(, lltable) lltables;
64 krwlock_t lltable_rwlock;
65
66 static void lltable_unlink(struct lltable *llt);
67 static void llentries_unlink(struct lltable *llt, struct llentries *head);
68
69 static void htable_unlink_entry(struct llentry *lle);
70 static void htable_link_entry(struct lltable *llt, struct llentry *lle);
71 static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
72 void *farg);
73
74 /*
75 * Dump lle state for a specific address family.
76 */
77 static int
78 lltable_dump_af(struct lltable *llt, struct sysctl_req *wr)
79 {
80 int error;
81
82 LLTABLE_LOCK_ASSERT();
83
84 if (llt->llt_ifp->if_flags & IFF_LOOPBACK)
85 return (0);
86 error = 0;
87
88 IF_AFDATA_RLOCK(llt->llt_ifp);
89 error = lltable_foreach_lle(llt,
90 (llt_foreach_cb_t *)llt->llt_dump_entry, wr);
91 IF_AFDATA_RUNLOCK(llt->llt_ifp);
92
93 return (error);
94 }
95
96 /*
97 * Dump arp state for a specific address family.
98 */
99 int
100 lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
101 {
102 struct lltable *llt;
103 int error = 0;
104
105 LLTABLE_RLOCK();
106 SLIST_FOREACH(llt, &lltables, llt_link) {
107 if (llt->llt_af == af) {
108 error = lltable_dump_af(llt, wr);
109 if (error != 0)
110 goto done;
111 }
112 }
113 done:
114 LLTABLE_RUNLOCK();
115 return (error);
116 }
117
118 /*
119 * Common function helpers for chained hash table.
120 */
121
122 /*
123 * Runs specified callback for each entry in @llt.
124 * Caller does the locking.
125 *
126 */
127 static int
128 htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
129 {
130 struct llentry *lle, *next;
131 int i, error;
132
133 error = 0;
134
135 for (i = 0; i < llt->llt_hsize; i++) {
136 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
137 error = f(llt, lle, farg);
138 if (error != 0)
139 break;
140 }
141 }
142
143 return (error);
144 }
145
146 static void
147 htable_link_entry(struct lltable *llt, struct llentry *lle)
148 {
149 struct llentries *lleh;
150 uint32_t hashidx;
151
152 if ((lle->la_flags & LLE_LINKED) != 0)
153 return;
154
155 IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
156
157 hashidx = llt->llt_hash(lle, llt->llt_hsize);
158 lleh = &llt->lle_head[hashidx];
159
160 lle->lle_tbl = llt;
161 lle->lle_head = lleh;
162 lle->la_flags |= LLE_LINKED;
163 LIST_INSERT_HEAD(lleh, lle, lle_next);
164 }
165
166 static void
167 htable_unlink_entry(struct llentry *lle)
168 {
169
170 if ((lle->la_flags & LLE_LINKED) != 0) {
171 IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
172 LIST_REMOVE(lle, lle_next);
173 lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
174 #if 0
175 lle->lle_tbl = NULL;
176 lle->lle_head = NULL;
177 #endif
178 }
179 }
180
181 struct prefix_match_data {
182 const struct sockaddr *prefix;
183 const struct sockaddr *mask;
184 struct llentries dchain;
185 u_int flags;
186 };
187
188 static int
189 htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
190 {
191 struct prefix_match_data *pmd;
192
193 pmd = (struct prefix_match_data *)farg;
194
195 if (llt->llt_match_prefix(pmd->prefix, pmd->mask, pmd->flags, lle)) {
196 LLE_WLOCK(lle);
197 LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain);
198 }
199
200 return (0);
201 }
202
203 static void
204 htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
205 const struct sockaddr *mask, u_int flags)
206 {
207 struct llentry *lle, *next;
208 struct prefix_match_data pmd;
209
210 memset(&pmd, 0, sizeof(pmd));
211 pmd.prefix = prefix;
212 pmd.mask = mask;
213 pmd.flags = flags;
214 LIST_INIT(&pmd.dchain);
215
216 IF_AFDATA_WLOCK(llt->llt_ifp);
217 /* Push matching lles to chain */
218 lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd);
219
220 llentries_unlink(llt, &pmd.dchain);
221 IF_AFDATA_WUNLOCK(llt->llt_ifp);
222
223 LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
224 llt->llt_free_entry(llt, lle);
225 }
226
227 static void
228 htable_free_tbl(struct lltable *llt)
229 {
230
231 free(llt->lle_head, M_LLTABLE);
232 free(llt, M_LLTABLE);
233 }
234
235 static void
236 llentries_unlink(struct lltable *llt, struct llentries *head)
237 {
238 struct llentry *lle, *next;
239
240 LIST_FOREACH_SAFE(lle, head, lle_chain, next)
241 llt->llt_unlink_entry(lle);
242 }
243
244 /*
245 * Helper function used to drop all mbufs in hold queue.
246 *
247 * Returns the number of held packets, if any, that were dropped.
248 */
249 size_t
250 lltable_drop_entry_queue(struct llentry *lle)
251 {
252 size_t pkts_dropped;
253 struct mbuf *next;
254
255 LLE_WLOCK_ASSERT(lle);
256
257 pkts_dropped = 0;
258 while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
259 next = lle->la_hold->m_nextpkt;
260 m_freem(lle->la_hold);
261 lle->la_hold = next;
262 lle->la_numheld--;
263 pkts_dropped++;
264 }
265
266 KASSERTMSG(lle->la_numheld == 0,
267 "la_numheld %d > 0, pkts_droped %zd",
268 lle->la_numheld, pkts_dropped);
269
270 return (pkts_dropped);
271 }
272
273 /*
274 * Deletes an address from the address table.
275 * This function is called by the timer functions
276 * such as arptimer() and nd6_llinfo_timer(), and
277 * the caller does the locking.
278 *
279 * Returns the number of held packets, if any, that were dropped.
280 */
281 size_t
282 llentry_free(struct llentry *lle)
283 {
284 struct lltable *llt;
285 size_t pkts_dropped;
286
287 LLE_WLOCK_ASSERT(lle);
288
289 if ((lle->la_flags & LLE_LINKED) != 0) {
290 llt = lle->lle_tbl;
291
292 IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
293 llt->llt_unlink_entry(lle);
294 }
295
296 pkts_dropped = lltable_drop_entry_queue(lle);
297
298 LLE_FREE_LOCKED(lle);
299
300 return (pkts_dropped);
301 }
302
303 /*
304 * (al)locate an llentry for address dst (equivalent to rtalloc for new-arp).
305 *
306 * If found the llentry * is returned referenced and unlocked.
307 */
308 struct llentry *
309 llentry_alloc(struct ifnet *ifp, struct lltable *lt,
310 struct sockaddr_storage *dst)
311 {
312 struct llentry *la;
313
314 IF_AFDATA_RLOCK(ifp);
315 la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
316 IF_AFDATA_RUNLOCK(ifp);
317 if ((la == NULL) &&
318 #ifdef __FreeBSD__
319 (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
320 #else /* XXX */
321 (ifp->if_flags & IFF_NOARP) == 0) {
322 #endif
323 IF_AFDATA_WLOCK(ifp);
324 la = lla_create(lt, 0, (struct sockaddr *)dst);
325 IF_AFDATA_WUNLOCK(ifp);
326 }
327
328 if (la != NULL) {
329 LLE_ADDREF(la);
330 LLE_WUNLOCK(la);
331 }
332
333 return (la);
334 }
335
336 /*
337 * Free all entries from given table and free itself.
338 */
339
340 static int
341 lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
342 {
343 struct llentries *dchain;
344
345 dchain = (struct llentries *)farg;
346
347 LLE_WLOCK(lle);
348 LIST_INSERT_HEAD(dchain, lle, lle_chain);
349
350 return (0);
351 }
352
353 /*
354 * Free all entries from given table and free itself.
355 */
356 void
357 lltable_free(struct lltable *llt)
358 {
359 struct llentry *lle, *next;
360 struct llentries dchain;
361
362 KASSERTMSG(llt != NULL, "llt is NULL");
363
364 lltable_unlink(llt);
365
366 LIST_INIT(&dchain);
367 IF_AFDATA_WLOCK(llt->llt_ifp);
368 /* Push all lles to @dchain */
369 lltable_foreach_lle(llt, lltable_free_cb, &dchain);
370 llentries_unlink(llt, &dchain);
371 IF_AFDATA_WUNLOCK(llt->llt_ifp);
372
373 LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) {
374 if (callout_stop(&lle->la_timer))
375 LLE_REMREF(lle);
376 #if defined(__NetBSD__)
377 /* XXX should have callback? */
378 if (lle->la_rt != NULL)
379 rtfree(lle->la_rt);
380 #endif
381 llentry_free(lle);
382 }
383
384 llt->llt_free_tbl(llt);
385 }
386
387 void
388 lltable_drain(int af)
389 {
390 struct lltable *llt;
391 struct llentry *lle;
392 register int i;
393
394 LLTABLE_RLOCK();
395 SLIST_FOREACH(llt, &lltables, llt_link) {
396 if (llt->llt_af != af)
397 continue;
398
399 for (i=0; i < llt->llt_hsize; i++) {
400 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
401 LLE_WLOCK(lle);
402 lltable_drop_entry_queue(lle);
403 LLE_WUNLOCK(lle);
404 }
405 }
406 }
407 LLTABLE_RUNLOCK();
408 }
409
410 void
411 lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
412 u_int flags)
413 {
414 struct lltable *llt;
415
416 LLTABLE_RLOCK();
417 SLIST_FOREACH(llt, &lltables, llt_link) {
418 if (llt->llt_af != af)
419 continue;
420
421 llt->llt_prefix_free(llt, prefix, mask, flags);
422 }
423 LLTABLE_RUNLOCK();
424 }
425
426 struct lltable *
427 lltable_allocate_htbl(uint32_t hsize)
428 {
429 struct lltable *llt;
430 int i;
431
432 llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK | M_ZERO);
433 llt->llt_hsize = hsize;
434 llt->lle_head = malloc(sizeof(struct llentries) * hsize,
435 M_LLTABLE, M_WAITOK | M_ZERO);
436
437 for (i = 0; i < llt->llt_hsize; i++)
438 LIST_INIT(&llt->lle_head[i]);
439
440 /* Set some default callbacks */
441 llt->llt_link_entry = htable_link_entry;
442 llt->llt_unlink_entry = htable_unlink_entry;
443 llt->llt_prefix_free = htable_prefix_free;
444 llt->llt_foreach_entry = htable_foreach_lle;
445
446 llt->llt_free_tbl = htable_free_tbl;
447
448 return (llt);
449 }
450
451 /*
452 * Links lltable to global llt list.
453 */
454 void
455 lltable_link(struct lltable *llt)
456 {
457
458 LLTABLE_WLOCK();
459 SLIST_INSERT_HEAD(&lltables, llt, llt_link);
460 LLTABLE_WUNLOCK();
461 }
462
463 static void
464 lltable_unlink(struct lltable *llt)
465 {
466
467 LLTABLE_WLOCK();
468 SLIST_REMOVE(&lltables, llt, lltable, llt_link);
469 LLTABLE_WUNLOCK();
470
471 }
472
473 /*
474 * External methods used by lltable consumers
475 */
476
477 int
478 lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
479 {
480
481 return (llt->llt_foreach_entry(llt, f, farg));
482 }
483
484 void
485 lltable_link_entry(struct lltable *llt, struct llentry *lle)
486 {
487
488 llt->llt_link_entry(llt, lle);
489 }
490
491 void
492 lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
493 {
494
495 llt->llt_unlink_entry(lle);
496 }
497
498 void
499 lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
500 {
501 struct lltable *llt;
502
503 llt = lle->lle_tbl;
504 llt->llt_fill_sa_entry(lle, sa);
505 }
506
507 struct ifnet *
508 lltable_get_ifp(const struct lltable *llt)
509 {
510
511 return (llt->llt_ifp);
512 }
513
514 int
515 lltable_get_af(const struct lltable *llt)
516 {
517
518 return (llt->llt_af);
519 }
520
521 /*
522 * Called in route_output when rtm_flags contains RTF_LLDATA.
523 */
524 int
525 lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
526 {
527 const struct sockaddr_dl *dl = satocsdl(info->rti_info[RTAX_GATEWAY]);
528 const struct sockaddr *dst = info->rti_info[RTAX_DST];
529 struct ifnet *ifp;
530 struct lltable *llt;
531 struct llentry *lle;
532 u_int laflags;
533 int error;
534
535 KASSERTMSG(dl != NULL && dl->sdl_family == AF_LINK, "invalid dl");
536
537 ifp = if_byindex(dl->sdl_index);
538 if (ifp == NULL) {
539 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n",
540 __func__, dl->sdl_index);
541 return EINVAL;
542 }
543
544 /* XXX linked list may be too expensive */
545 LLTABLE_RLOCK();
546 SLIST_FOREACH(llt, &lltables, llt_link) {
547 if (llt->llt_af == dst->sa_family &&
548 llt->llt_ifp == ifp)
549 break;
550 }
551 LLTABLE_RUNLOCK();
552 KASSERTMSG(llt != NULL, "Yep, ugly hacks are bad");
553
554 error = 0;
555
556 switch (rtm->rtm_type) {
557 case RTM_ADD:
558 /* Add static LLE */
559 IF_AFDATA_WLOCK(ifp);
560 lle = lla_create(llt, 0, dst);
561 if (lle == NULL) {
562 IF_AFDATA_WUNLOCK(ifp);
563 return (ENOMEM);
564 }
565
566
567 memcpy(&lle->ll_addr, CLLADDR(dl), ifp->if_addrlen);
568 if ((rtm->rtm_flags & RTF_ANNOUNCE))
569 lle->la_flags |= LLE_PUB;
570 lle->la_flags |= LLE_VALID;
571 #ifdef INET6
572 /*
573 * ND6
574 */
575 if (dst->sa_family == AF_INET6)
576 lle->ln_state = ND6_LLINFO_REACHABLE;
577 #endif
578 /*
579 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
580 */
581
582 if (rtm->rtm_rmx.rmx_expire == 0) {
583 lle->la_flags |= LLE_STATIC;
584 lle->la_expire = 0;
585 } else
586 lle->la_expire = rtm->rtm_rmx.rmx_expire;
587 laflags = lle->la_flags;
588 LLE_WUNLOCK(lle);
589 IF_AFDATA_WUNLOCK(ifp);
590 #ifdef INET
591 /* gratuitous ARP */
592 if ((laflags & LLE_PUB) && dst->sa_family == AF_INET)
593 arprequest(ifp,
594 &((const struct sockaddr_in *)dst)->sin_addr,
595 &((const struct sockaddr_in *)dst)->sin_addr,
596 CLLADDR(dl));
597 #endif
598
599 break;
600
601 case RTM_DELETE:
602 IF_AFDATA_WLOCK(ifp);
603 error = lla_delete(llt, 0, dst);
604 IF_AFDATA_WUNLOCK(ifp);
605 return (error == 0 ? 0 : ENOENT);
606
607 default:
608 error = EINVAL;
609 }
610
611 return (error);
612 }
613
614 void
615 lltableinit(void)
616 {
617
618 SLIST_INIT(&lltables);
619 rw_init(&lltable_rwlock);
620 }
621
622 #ifdef __FreeBSD__
623 #ifdef DDB
624 struct llentry_sa {
625 struct llentry base;
626 struct sockaddr l3_addr;
627 };
628
629 static void
630 llatbl_lle_show(struct llentry_sa *la)
631 {
632 struct llentry *lle;
633 uint8_t octet[6];
634
635 lle = &la->base;
636 db_printf("lle=%p\n", lle);
637 db_printf(" lle_next=%p\n", lle->lle_next.le_next);
638 db_printf(" lle_lock=%p\n", &lle->lle_lock);
639 db_printf(" lle_tbl=%p\n", lle->lle_tbl);
640 db_printf(" lle_head=%p\n", lle->lle_head);
641 db_printf(" la_hold=%p\n", lle->la_hold);
642 db_printf(" la_numheld=%d\n", lle->la_numheld);
643 db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire);
644 db_printf(" la_flags=0x%04x\n", lle->la_flags);
645 db_printf(" la_asked=%u\n", lle->la_asked);
646 db_printf(" la_preempt=%u\n", lle->la_preempt);
647 db_printf(" ln_byhint=%u\n", lle->ln_byhint);
648 db_printf(" ln_state=%d\n", lle->ln_state);
649 db_printf(" ln_router=%u\n", lle->ln_router);
650 db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick);
651 db_printf(" lle_refcnt=%d\n", lle->lle_refcnt);
652 memcopy(octet, &lle->ll_addr.mac16, sizeof(octet));
653 db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
654 octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]);
655 db_printf(" lle_timer=%p\n", &lle->lle_timer);
656
657 switch (la->l3_addr.sa_family) {
658 #ifdef INET
659 case AF_INET:
660 {
661 struct sockaddr_in *sin;
662 char l3s[INET_ADDRSTRLEN];
663
664 sin = (struct sockaddr_in *)&la->l3_addr;
665 inet_ntoa_r(sin->sin_addr, l3s);
666 db_printf(" l3_addr=%s\n", l3s);
667 break;
668 }
669 #endif
670 #ifdef INET6
671 case AF_INET6:
672 {
673 struct sockaddr_in6 *sin6;
674 char l3s[INET6_ADDRSTRLEN];
675
676 sin6 = (struct sockaddr_in6 *)&la->l3_addr;
677 ip6_sprintf(l3s, &sin6->sin6_addr);
678 db_printf(" l3_addr=%s\n", l3s);
679 break;
680 }
681 #endif
682 default:
683 db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family);
684 break;
685 }
686 }
687
688 DB_SHOW_COMMAND(llentry, db_show_llentry)
689 {
690
691 if (!have_addr) {
692 db_printf("usage: show llentry <struct llentry *>\n");
693 return;
694 }
695
696 llatbl_lle_show((struct llentry_sa *)addr);
697 }
698
699 static void
700 llatbl_llt_show(struct lltable *llt)
701 {
702 int i;
703 struct llentry *lle;
704
705 db_printf("llt=%p llt_af=%d llt_ifp=%p\n",
706 llt, llt->llt_af, llt->llt_ifp);
707
708 for (i = 0; i < llt->llt_hsize; i++) {
709 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
710
711 llatbl_lle_show((struct llentry_sa *)lle);
712 if (db_pager_quit)
713 return;
714 }
715 }
716 }
717
718 DB_SHOW_COMMAND(lltable, db_show_lltable)
719 {
720
721 if (!have_addr) {
722 db_printf("usage: show lltable <struct lltable *>\n");
723 return;
724 }
725
726 llatbl_llt_show((struct lltable *)addr);
727 }
728
729 DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables)
730 {
731 VNET_ITERATOR_DECL(vnet_iter);
732 struct lltable *llt;
733
734 VNET_FOREACH(vnet_iter) {
735 CURVNET_SET_QUIET(vnet_iter);
736 #ifdef VIMAGE
737 db_printf("vnet=%p\n", curvnet);
738 #endif
739 SLIST_FOREACH(llt, &lltables, llt_link) {
740 db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n",
741 llt, llt->llt_af, llt->llt_ifp,
742 (llt->llt_ifp != NULL) ?
743 llt->llt_ifp->if_xname : "?");
744 if (have_addr && addr != 0) /* verbose */
745 llatbl_llt_show(llt);
746 if (db_pager_quit) {
747 CURVNET_RESTORE();
748 return;
749 }
750 }
751 CURVNET_RESTORE();
752 }
753 }
754 #endif /* DDB */
755 #endif /* __FreeBSD__ */
756