source: rtems-libbsd/freebsd/sys/net/if_llatbl.c @ 6d9d7b1

55-freebsd-126-freebsd-12
Last change on this file since 6d9d7b1 was 3c967ca, checked in by Sebastian Huber <sebastian.huber@…>, on 06/08/17 at 11:15:12

Use <sys/lock.h> provided by Newlib

  • Property mode set to 100644
File size: 21.2 KB
Line 
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
65MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables");
66
67static VNET_DEFINE(SLIST_HEAD(, lltable), lltables) =
68    SLIST_HEAD_INITIALIZER(lltables);
69#define V_lltables      VNET(lltables)
70
71static struct rwlock lltable_list_lock;
72RW_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
79static void lltable_unlink(struct lltable *llt);
80static void llentries_unlink(struct lltable *llt, struct llentries *head);
81
82static void htable_unlink_entry(struct llentry *lle);
83static void htable_link_entry(struct lltable *llt, struct llentry *lle);
84static 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 */
90static int
91lltable_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 */
112int
113lltable_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        }
126done:
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 */
140static int
141htable_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
159static void
160htable_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
179static void
180htable_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
194struct prefix_match_data {
195        const struct sockaddr *addr;
196        const struct sockaddr *mask;
197        struct llentries dchain;
198        u_int flags;
199};
200
201static int
202htable_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
216static void
217htable_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
240static void
241htable_free_tbl(struct lltable *llt)
242{
243
244        free(llt->lle_head, M_LLTABLE);
245        free(llt, M_LLTABLE);
246}
247
248static void
249llentries_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 */
262size_t
263lltable_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
286void
287lltable_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 */
306int
307lltable_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 */
343int
344lltable_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 */
371static int
372llentry_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 */
405void
406lltable_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 */
428size_t
429llentry_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 */
449struct llentry *
450llentry_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
491static int
492lltable_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 */
507void
508lltable_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
534void
535lltable_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 */
566int
567lltable_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
595void
596lltable_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
611struct lltable *
612lltable_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 */
638void
639lltable_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
647static void
648lltable_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
661int
662lltable_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
668struct llentry *
669lltable_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
676void
677lltable_free_entry(struct lltable *llt, struct llentry *lle)
678{
679
680        llt->llt_free_entry(llt, lle);
681}
682
683void
684lltable_link_entry(struct lltable *llt, struct llentry *lle)
685{
686
687        llt->llt_link_entry(llt, lle);
688}
689
690void
691lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
692{
693
694        llt->llt_unlink_entry(lle);
695}
696
697void
698lltable_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
706struct ifnet *
707lltable_get_ifp(const struct lltable *llt)
708{
709
710        return (llt->llt_ifp);
711}
712
713int
714lltable_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 */
723int
724lla_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
836struct llentry_sa {
837        struct llentry          base;
838        struct sockaddr         l3_addr;
839};
840
841static void
842llatbl_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
899DB_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
910static void
911llatbl_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
929DB_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
940DB_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
Note: See TracBrowser for help on using the repository browser.