]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - sys/net/if_llatbl.c
Merge r238990 (manually resolving absence of r237263):
[FreeBSD/stable/9.git] / sys / net / if_llatbl.c
1 /*
2  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
3  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
4  * Copyright (c) 2008 Kip Macy. All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 
15  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_ddb.h"
31 #include "opt_inet.h"
32 #include "opt_inet6.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/syslog.h>
39 #include <sys/sysctl.h>
40 #include <sys/socket.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/rwlock.h>
45
46 #ifdef DDB
47 #include <ddb/ddb.h>
48 #endif
49
50 #include <vm/uma.h>
51
52 #include <netinet/in.h>
53 #include <net/if_llatbl.h>
54 #include <net/if.h>
55 #include <net/if_dl.h>
56 #include <net/if_var.h>
57 #include <net/route.h>
58 #include <net/vnet.h>
59 #include <netinet/if_ether.h>
60 #include <netinet6/in6_var.h>
61 #include <netinet6/nd6.h>
62
63 MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables");
64
65 static VNET_DEFINE(SLIST_HEAD(, lltable), lltables);
66 #define V_lltables      VNET(lltables)
67
68 extern void arprequest(struct ifnet *, struct in_addr *, struct in_addr *,
69         u_char *);
70
71 static void vnet_lltable_init(void);
72
73 struct rwlock lltable_rwlock;
74 RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock");
75
76 /*
77  * Dump arp state for a specific address family.
78  */
79 int
80 lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
81 {
82         struct lltable *llt;
83         int error = 0;
84
85         LLTABLE_RLOCK();
86         SLIST_FOREACH(llt, &V_lltables, llt_link) {
87                 if (llt->llt_af == af) {
88                         error = llt->llt_dump(llt, wr);
89                         if (error != 0)
90                                 goto done;
91                 }
92         }
93 done:
94         LLTABLE_RUNLOCK();
95         return (error);
96 }
97
98 /*
99  * Deletes an address from the address table.
100  * This function is called by the timer functions
101  * such as arptimer() and nd6_llinfo_timer(), and
102  * the caller does the locking.
103  *
104  * Returns the number of held packets, if any, that were dropped.
105  */
106 size_t
107 llentry_free(struct llentry *lle)
108 {
109         size_t pkts_dropped;
110         struct mbuf *next;
111
112         IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
113         LLE_WLOCK_ASSERT(lle);
114
115         /* XXX: guard against race with other llentry_free(). */
116         if (!(lle->la_flags & LLE_LINKED)) {
117                 LLE_FREE_LOCKED(lle);
118                 return (0);
119         }
120
121         LIST_REMOVE(lle, lle_next);
122         lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
123
124         pkts_dropped = 0;
125         while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
126                 next = lle->la_hold->m_nextpkt;
127                 m_freem(lle->la_hold);
128                 lle->la_hold = next;
129                 lle->la_numheld--;
130                 pkts_dropped++;
131         }
132
133         KASSERT(lle->la_numheld == 0,
134                 ("%s: la_numheld %d > 0, pkts_droped %zd", __func__,
135                  lle->la_numheld, pkts_dropped));
136
137         LLE_FREE_LOCKED(lle);
138
139         return (pkts_dropped);
140 }
141
142 /*
143  * (al)locate an llentry for address dst (equivalent to rtalloc for new-arp).
144  *
145  * If found the llentry * is returned referenced and unlocked.
146  */
147 struct llentry *
148 llentry_alloc(struct ifnet *ifp, struct lltable *lt,
149     struct sockaddr_storage *dst)
150 {
151         struct llentry *la;
152
153         IF_AFDATA_RLOCK(ifp);
154         la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
155         IF_AFDATA_RUNLOCK(ifp);
156         if ((la == NULL) &&
157             (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
158                 IF_AFDATA_WLOCK(ifp);
159                 la = lla_lookup(lt, (LLE_CREATE | LLE_EXCLUSIVE),
160                     (struct sockaddr *)dst);
161                 IF_AFDATA_WUNLOCK(ifp);
162         }
163
164         if (la != NULL) {
165                 LLE_ADDREF(la);
166                 LLE_WUNLOCK(la);
167         }
168
169         return (la);
170 }
171
172 /*
173  * Free all entries from given table and free itself.
174  */
175 void
176 lltable_free(struct lltable *llt)
177 {
178         struct llentry *lle, *next;
179         int i;
180
181         KASSERT(llt != NULL, ("%s: llt is NULL", __func__));
182
183         LLTABLE_WLOCK();
184         SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
185         LLTABLE_WUNLOCK();
186
187         IF_AFDATA_WLOCK(llt->llt_ifp);
188         for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
189                 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
190                         LLE_WLOCK(lle);
191                         if (callout_stop(&lle->la_timer))
192                                 LLE_REMREF(lle);
193                         llentry_free(lle);
194                 }
195         }
196         IF_AFDATA_WUNLOCK(llt->llt_ifp);
197
198         free(llt, M_LLTABLE);
199 }
200
201 #if 0
202 void
203 lltable_drain(int af)
204 {
205         struct lltable  *llt;
206         struct llentry  *lle;
207         register int i;
208
209         LLTABLE_RLOCK();
210         SLIST_FOREACH(llt, &V_lltables, llt_link) {
211                 if (llt->llt_af != af)
212                         continue;
213
214                 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
215                         LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
216                                 LLE_WLOCK(lle);
217                                 if (lle->la_hold) {
218                                         m_freem(lle->la_hold);
219                                         lle->la_hold = NULL;
220                                 }
221                                 LLE_WUNLOCK(lle);
222                         }
223                 }
224         }
225         LLTABLE_RUNLOCK();
226 }
227 #endif
228
229 void
230 lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
231     u_int flags)
232 {
233         struct lltable *llt;
234
235         LLTABLE_RLOCK();
236         SLIST_FOREACH(llt, &V_lltables, llt_link) {
237                 if (llt->llt_af != af)
238                         continue;
239
240                 llt->llt_prefix_free(llt, prefix, mask, flags);
241         }
242         LLTABLE_RUNLOCK();
243 }
244
245
246
247 /*
248  * Create a new lltable.
249  */
250 struct lltable *
251 lltable_init(struct ifnet *ifp, int af)
252 {
253         struct lltable *llt;
254         register int i;
255
256         llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK);
257
258         llt->llt_af = af;
259         llt->llt_ifp = ifp;
260         for (i = 0; i < LLTBL_HASHTBL_SIZE; i++)
261                 LIST_INIT(&llt->lle_head[i]);
262
263         LLTABLE_WLOCK();
264         SLIST_INSERT_HEAD(&V_lltables, llt, llt_link);
265         LLTABLE_WUNLOCK();
266
267         return (llt);
268 }
269
270 /*
271  * Called in route_output when adding/deleting a route to an interface.
272  */
273 int
274 lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
275 {
276         struct sockaddr_dl *dl =
277             (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY];
278         struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST];
279         struct ifnet *ifp;
280         struct lltable *llt;
281         struct llentry *lle;
282         u_int laflags = 0, flags = 0;
283         int error = 0;
284
285         if (dl == NULL || dl->sdl_family != AF_LINK) {
286                 log(LOG_INFO, "%s: invalid dl\n", __func__);
287                 return EINVAL;
288         }
289         ifp = ifnet_byindex(dl->sdl_index);
290         if (ifp == NULL) {
291                 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n",
292                     __func__, dl->sdl_index);
293                 return EINVAL;
294         }
295
296         switch (rtm->rtm_type) {
297         case RTM_ADD:
298                 if (rtm->rtm_flags & RTF_ANNOUNCE) {
299                         flags |= LLE_PUB;
300 #ifdef INET
301                         if (dst->sa_family == AF_INET &&
302                             ((struct sockaddr_inarp *)dst)->sin_other != 0) {
303                                 struct rtentry *rt;
304                                 ((struct sockaddr_inarp *)dst)->sin_other = 0;
305                                 rt = rtalloc1(dst, 0, 0);
306                                 if (rt == NULL || !(rt->rt_flags & RTF_HOST)) {
307                                         log(LOG_INFO, "%s: RTM_ADD publish "
308                                             "(proxy only) is invalid\n",
309                                             __func__);
310                                         if (rt)
311                                                 RTFREE_LOCKED(rt);
312                                         return EINVAL;
313                                 }
314                                 RTFREE_LOCKED(rt);
315
316                                 flags |= LLE_PROXY;
317                         }
318 #endif
319                 }
320                 flags |= LLE_CREATE;
321                 break;
322
323         case RTM_DELETE:
324                 flags |= LLE_DELETE;
325                 break;
326
327         case RTM_CHANGE:
328                 break;
329
330         default:
331                 return EINVAL; /* XXX not implemented yet */
332         }
333
334         /* XXX linked list may be too expensive */
335         LLTABLE_RLOCK();
336         SLIST_FOREACH(llt, &V_lltables, llt_link) {
337                 if (llt->llt_af == dst->sa_family &&
338                     llt->llt_ifp == ifp)
339                         break;
340         }
341         LLTABLE_RUNLOCK();
342         KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n"));
343
344         if (flags & LLE_CREATE)
345                 flags |= LLE_EXCLUSIVE;
346
347         IF_AFDATA_LOCK(ifp);
348         lle = lla_lookup(llt, flags, dst);
349         IF_AFDATA_UNLOCK(ifp);
350         if (LLE_IS_VALID(lle)) {
351                 if (flags & LLE_CREATE) {
352                         /*
353                          * If we delay the delete, then a subsequent
354                          * "arp add" should look up this entry, reset the
355                          * LLE_DELETED flag, and reset the expiration timer
356                          */
357                         bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
358                         lle->la_flags |= (flags & (LLE_PUB | LLE_PROXY));
359                         lle->la_flags |= LLE_VALID;
360                         lle->la_flags &= ~LLE_DELETED;
361 #ifdef INET6
362                         /*
363                          * ND6
364                          */
365                         if (dst->sa_family == AF_INET6)
366                                 lle->ln_state = ND6_LLINFO_REACHABLE;
367 #endif
368                         /*
369                          * NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
370                          */
371
372                         if (rtm->rtm_rmx.rmx_expire == 0) {
373                                 lle->la_flags |= LLE_STATIC;
374                                 lle->la_expire = 0;
375                         } else
376                                 lle->la_expire = rtm->rtm_rmx.rmx_expire;
377                         laflags = lle->la_flags;
378                         LLE_WUNLOCK(lle);
379 #ifdef INET
380                         /*  gratuitous ARP */
381                         if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
382                                 arprequest(ifp,
383                                     &((struct sockaddr_in *)dst)->sin_addr,
384                                     &((struct sockaddr_in *)dst)->sin_addr,
385                                     ((laflags & LLE_PROXY) ?
386                                         (u_char *)IF_LLADDR(ifp) :
387                                         (u_char *)LLADDR(dl)));
388                         }
389 #endif
390                 } else {
391                         if (flags & LLE_EXCLUSIVE)
392                                 LLE_WUNLOCK(lle);
393                         else
394                                 LLE_RUNLOCK(lle);
395                 }
396         } else if ((lle == NULL) && (flags & LLE_DELETE))
397                 error = EINVAL;
398
399
400         return (error);
401 }
402
403 static void
404 vnet_lltable_init()
405 {
406
407         SLIST_INIT(&V_lltables);
408 }
409 VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST,
410     vnet_lltable_init, NULL);
411
412 #ifdef DDB
413 struct llentry_sa {
414         struct llentry          base;
415         struct sockaddr         l3_addr;
416 };
417
418 static void
419 llatbl_lle_show(struct llentry_sa *la)
420 {
421         struct llentry *lle;
422         uint8_t octet[6];
423
424         lle = &la->base;
425         db_printf("lle=%p\n", lle);
426         db_printf(" lle_next=%p\n", lle->lle_next.le_next);
427         db_printf(" lle_lock=%p\n", &lle->lle_lock);
428         db_printf(" lle_tbl=%p\n", lle->lle_tbl);
429         db_printf(" lle_head=%p\n", lle->lle_head);
430         db_printf(" la_hold=%p\n", lle->la_hold);
431         db_printf(" la_numheld=%d\n", lle->la_numheld);
432         db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire);
433         db_printf(" la_flags=0x%04x\n", lle->la_flags);
434         db_printf(" la_asked=%u\n", lle->la_asked);
435         db_printf(" la_preempt=%u\n", lle->la_preempt);
436         db_printf(" ln_byhint=%u\n", lle->ln_byhint);
437         db_printf(" ln_state=%d\n", lle->ln_state);
438         db_printf(" ln_router=%u\n", lle->ln_router);
439         db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick);
440         db_printf(" lle_refcnt=%d\n", lle->lle_refcnt);
441         bcopy(&lle->ll_addr.mac16, octet, sizeof(octet));
442         db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
443             octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]);
444         db_printf(" la_timer=%p\n", &lle->la_timer);
445
446         switch (la->l3_addr.sa_family) {
447 #ifdef INET
448         case AF_INET:
449         {
450                 struct sockaddr_in *sin;
451                 char l3s[INET_ADDRSTRLEN];
452
453                 sin = (struct sockaddr_in *)&la->l3_addr;
454                 inet_ntoa_r(sin->sin_addr, l3s);
455                 db_printf(" l3_addr=%s\n", l3s);
456                 break;
457         }
458 #endif
459 #ifdef INET6
460         case AF_INET6:
461         {
462                 struct sockaddr_in6 *sin6;
463                 char l3s[INET6_ADDRSTRLEN];
464
465                 sin6 = (struct sockaddr_in6 *)&la->l3_addr;
466                 ip6_sprintf(l3s, &sin6->sin6_addr);
467                 db_printf(" l3_addr=%s\n", l3s);
468                 break;
469         }
470 #endif
471         default:
472                 db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family);
473                 break;
474         }
475 }
476
477 DB_SHOW_COMMAND(llentry, db_show_llentry)
478 {
479
480         if (!have_addr) {
481                 db_printf("usage: show llentry <struct llentry *>\n");
482                 return;
483         }
484
485         llatbl_lle_show((struct llentry_sa *)addr);
486 }
487
488 static void
489 llatbl_llt_show(struct lltable *llt)
490 {
491         int i;
492         struct llentry *lle;
493
494         db_printf("llt=%p llt_af=%d llt_ifp=%p\n",
495             llt, llt->llt_af, llt->llt_ifp);
496
497         for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
498                 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
499
500                         llatbl_lle_show((struct llentry_sa *)lle);
501                         if (db_pager_quit)
502                                 return;
503                 }
504         }
505 }
506
507 DB_SHOW_COMMAND(lltable, db_show_lltable)
508 {
509
510         if (!have_addr) {
511                 db_printf("usage: show lltable <struct lltable *>\n");
512                 return;
513         }
514
515         llatbl_llt_show((struct lltable *)addr);
516 }
517
518 DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables)
519 {
520         VNET_ITERATOR_DECL(vnet_iter);
521         struct lltable *llt;
522
523         VNET_FOREACH(vnet_iter) {
524                 CURVNET_SET_QUIET(vnet_iter);
525 #ifdef VIMAGE
526                 db_printf("vnet=%p\n", curvnet);
527 #endif
528                 SLIST_FOREACH(llt, &V_lltables, llt_link) {
529                         db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n",
530                             llt, llt->llt_af, llt->llt_ifp,
531                             (llt->llt_ifp != NULL) ?
532                                 llt->llt_ifp->if_xname : "?");
533                         if (have_addr && addr != 0) /* verbose */
534                                 llatbl_llt_show(llt);
535                         if (db_pager_quit) {
536                                 CURVNET_RESTORE();
537                                 return;
538                         }
539                 }
540                 CURVNET_RESTORE();
541         }
542 }
543 #endif