]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/if_llatbl.c
MFV: Update zlib to 1.2.7.
[FreeBSD/FreeBSD.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 static void vnet_lltable_init(void);
69
70 struct rwlock lltable_rwlock;
71 RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock");
72
73 /*
74  * Dump arp state for a specific address family.
75  */
76 int
77 lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
78 {
79         struct lltable *llt;
80         int error = 0;
81
82         LLTABLE_RLOCK();
83         SLIST_FOREACH(llt, &V_lltables, llt_link) {
84                 if (llt->llt_af == af) {
85                         error = llt->llt_dump(llt, wr);
86                         if (error != 0)
87                                 goto done;
88                 }
89         }
90 done:
91         LLTABLE_RUNLOCK();
92         return (error);
93 }
94
95 /*
96  * Deletes an address from the address table.
97  * This function is called by the timer functions
98  * such as arptimer() and nd6_llinfo_timer(), and
99  * the caller does the locking.
100  *
101  * Returns the number of held packets, if any, that were dropped.
102  */
103 size_t
104 llentry_free(struct llentry *lle)
105 {
106         size_t pkts_dropped;
107         struct mbuf *next;
108
109         pkts_dropped = 0;
110         LLE_WLOCK_ASSERT(lle);
111         LIST_REMOVE(lle, lle_next);
112
113         while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
114                 next = lle->la_hold->m_nextpkt;
115                 m_freem(lle->la_hold);
116                 lle->la_hold = next;
117                 lle->la_numheld--;
118                 pkts_dropped++;
119         }
120
121         KASSERT(lle->la_numheld == 0, 
122                 ("%s: la_numheld %d > 0, pkts_droped %zd", __func__, 
123                  lle->la_numheld, pkts_dropped));
124
125         lle->la_flags &= ~LLE_VALID;
126         LLE_FREE_LOCKED(lle);
127
128         return (pkts_dropped);
129 }
130
131 /*
132  * Update an llentry for address dst (equivalent to rtalloc for new-arp)
133  * Caller must pass in a valid struct llentry * (or NULL)
134  *
135  * if found the llentry * is returned referenced and unlocked
136  */
137 int
138 llentry_update(struct llentry **llep, struct lltable *lt,
139     struct sockaddr_storage *dst, struct ifnet *ifp)
140 {
141         struct llentry *la;
142
143         IF_AFDATA_RLOCK(ifp);   
144         la = lla_lookup(lt, LLE_EXCLUSIVE,
145             (struct sockaddr *)dst);
146         IF_AFDATA_RUNLOCK(ifp);
147         if ((la == NULL) && 
148             (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
149                 IF_AFDATA_WLOCK(ifp);
150                 la = lla_lookup(lt,
151                     (LLE_CREATE | LLE_EXCLUSIVE),
152                     (struct sockaddr *)dst);
153                 IF_AFDATA_WUNLOCK(ifp); 
154         }
155         if (la != NULL && (*llep != la)) {
156                 if (*llep != NULL)
157                         LLE_FREE(*llep);
158                 LLE_ADDREF(la);
159                 LLE_WUNLOCK(la);
160                 *llep = la;
161         } else if (la != NULL)
162                 LLE_WUNLOCK(la);
163
164         if (la == NULL)
165                 return (ENOENT);
166
167         return (0);
168 }
169
170 /*
171  * Free all entries from given table and free itself.
172  */
173 void
174 lltable_free(struct lltable *llt)
175 {
176         struct llentry *lle, *next;
177         int i;
178
179         KASSERT(llt != NULL, ("%s: llt is NULL", __func__));
180
181         LLTABLE_WLOCK();
182         SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
183         LLTABLE_WUNLOCK();
184
185         for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
186                 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
187                         int canceled;
188
189                         canceled = callout_drain(&lle->la_timer);
190                         LLE_WLOCK(lle);
191                         if (canceled)
192                                 LLE_REMREF(lle);
193                         llentry_free(lle);
194                 }
195         }
196
197         free(llt, M_LLTABLE);
198 }
199
200 #if 0
201 void
202 lltable_drain(int af)
203 {
204         struct lltable  *llt;
205         struct llentry  *lle;
206         register int i;
207
208         LLTABLE_RLOCK();
209         SLIST_FOREACH(llt, &V_lltables, llt_link) {
210                 if (llt->llt_af != af)
211                         continue;
212
213                 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
214                         LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
215                                 LLE_WLOCK(lle);
216                                 if (lle->la_hold) {
217                                         m_freem(lle->la_hold);
218                                         lle->la_hold = NULL;
219                                 }
220                                 LLE_WUNLOCK(lle);
221                         }
222                 }
223         }
224         LLTABLE_RUNLOCK();
225 }
226 #endif
227
228 void
229 lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
230             u_int flags)
231 {
232         struct lltable *llt;
233
234         LLTABLE_RLOCK();
235         SLIST_FOREACH(llt, &V_lltables, llt_link) {
236                 if (llt->llt_af != af)
237                         continue;
238
239                 llt->llt_prefix_free(llt, prefix, mask, flags);
240         }
241         LLTABLE_RUNLOCK();
242 }
243
244
245
246 /*
247  * Create a new lltable.
248  */
249 struct lltable *
250 lltable_init(struct ifnet *ifp, int af)
251 {
252         struct lltable *llt;
253         register int i;
254
255         llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK);
256
257         llt->llt_af = af;
258         llt->llt_ifp = ifp;
259         for (i = 0; i < LLTBL_HASHTBL_SIZE; i++)
260                 LIST_INIT(&llt->lle_head[i]);
261
262         LLTABLE_WLOCK();
263         SLIST_INSERT_HEAD(&V_lltables, llt, llt_link);
264         LLTABLE_WUNLOCK();
265
266         return (llt);
267 }
268
269 /*
270  * Called in route_output when adding/deleting a route to an interface.
271  */
272 int
273 lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
274 {
275         struct sockaddr_dl *dl =
276             (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY];
277         struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST];
278         struct ifnet *ifp;
279         struct lltable *llt;
280         struct llentry *lle;
281         u_int laflags = 0, flags = 0;
282         int error = 0;
283
284         if (dl == NULL || dl->sdl_family != AF_LINK) {
285                 log(LOG_INFO, "%s: invalid dl\n", __func__);
286                 return EINVAL;
287         }
288         ifp = ifnet_byindex(dl->sdl_index);
289         if (ifp == NULL) {
290                 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n",
291                     __func__, dl->sdl_index);
292                 return EINVAL;
293         }
294
295         switch (rtm->rtm_type) {
296         case RTM_ADD:
297                 if (rtm->rtm_flags & RTF_ANNOUNCE) {
298                         flags |= LLE_PUB;
299 #ifdef INET
300                         if (dst->sa_family == AF_INET && 
301                             ((struct sockaddr_inarp *)dst)->sin_other != 0) {
302                                 struct rtentry *rt;
303                                 ((struct sockaddr_inarp *)dst)->sin_other = 0;
304                                 rt = rtalloc1(dst, 0, 0);
305                                 if (rt == NULL || !(rt->rt_flags & RTF_HOST)) {
306                                         log(LOG_INFO, "%s: RTM_ADD publish "
307                                             "(proxy only) is invalid\n",
308                                             __func__);
309                                         if (rt)
310                                                 RTFREE_LOCKED(rt);
311                                         return EINVAL;
312                                 }
313                                 RTFREE_LOCKED(rt);
314
315                                 flags |= LLE_PROXY;
316                         }
317 #endif
318                 }
319                 flags |= LLE_CREATE;
320                 break;
321
322         case RTM_DELETE:
323                 flags |= LLE_DELETE;
324                 break;
325
326         case RTM_CHANGE:
327                 break;
328
329         default:
330                 return EINVAL; /* XXX not implemented yet */
331         }
332
333         /* XXX linked list may be too expensive */
334         LLTABLE_RLOCK();
335         SLIST_FOREACH(llt, &V_lltables, llt_link) {
336                 if (llt->llt_af == dst->sa_family &&
337                     llt->llt_ifp == ifp)
338                         break;
339         }
340         LLTABLE_RUNLOCK();
341         KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n"));
342
343         if (flags & LLE_CREATE)
344                 flags |= LLE_EXCLUSIVE;
345         
346         IF_AFDATA_LOCK(ifp);
347         lle = lla_lookup(llt, flags, dst);
348         IF_AFDATA_UNLOCK(ifp);
349         if (LLE_IS_VALID(lle)) {
350                 if (flags & LLE_CREATE) {
351                         /*
352                          * If we delay the delete, then a subsequent
353                          * "arp add" should look up this entry, reset the
354                          * LLE_DELETED flag, and reset the expiration timer
355                          */
356                         bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
357                         lle->la_flags |= (flags & (LLE_PUB | LLE_PROXY));
358                         lle->la_flags |= LLE_VALID;
359                         lle->la_flags &= ~LLE_DELETED;
360 #ifdef INET6
361                         /*
362                          * ND6
363                          */
364                         if (dst->sa_family == AF_INET6)
365                                 lle->ln_state = ND6_LLINFO_REACHABLE;
366 #endif
367                         /*
368                          * NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
369                          */
370
371                         if (rtm->rtm_rmx.rmx_expire == 0) {
372                                 lle->la_flags |= LLE_STATIC;
373                                 lle->la_expire = 0;
374                         } else
375                                 lle->la_expire = rtm->rtm_rmx.rmx_expire;
376                         laflags = lle->la_flags;
377                         LLE_WUNLOCK(lle);
378 #ifdef INET
379                         /*  gratuitous ARP */
380                         if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
381                                 arprequest(ifp, 
382                                     &((struct sockaddr_in *)dst)->sin_addr,
383                                     &((struct sockaddr_in *)dst)->sin_addr,
384                                     ((laflags & LLE_PROXY) ?
385                                         (u_char *)IF_LLADDR(ifp) :
386                                         (u_char *)LLADDR(dl)));
387                         }
388 #endif
389                 } else {
390                         if (flags & LLE_EXCLUSIVE)
391                                 LLE_WUNLOCK(lle);
392                         else
393                                 LLE_RUNLOCK(lle);
394                 }
395         } else if ((lle == NULL) && (flags & LLE_DELETE))
396                 error = EINVAL;
397
398
399         return (error);
400 }
401
402 static void
403 vnet_lltable_init()
404 {
405
406         SLIST_INIT(&V_lltables);
407 }
408 VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST,
409     vnet_lltable_init, NULL);
410
411 #ifdef DDB
412 struct llentry_sa {
413         struct llentry          base;
414         struct sockaddr         l3_addr;
415 };
416
417 static void
418 llatbl_lle_show(struct llentry_sa *la)
419 {
420         struct llentry *lle;
421         uint8_t octet[6];
422
423         lle = &la->base;
424         db_printf("lle=%p\n", lle);
425         db_printf(" lle_next=%p\n", lle->lle_next.le_next);
426         db_printf(" lle_lock=%p\n", &lle->lle_lock);
427         db_printf(" lle_tbl=%p\n", lle->lle_tbl);
428         db_printf(" lle_head=%p\n", lle->lle_head);
429         db_printf(" la_hold=%p\n", lle->la_hold);
430         db_printf(" la_numheld=%d\n", lle->la_numheld);
431         db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire);
432         db_printf(" la_flags=0x%04x\n", lle->la_flags);
433         db_printf(" la_asked=%u\n", lle->la_asked);
434         db_printf(" la_preempt=%u\n", lle->la_preempt);
435         db_printf(" ln_byhint=%u\n", lle->ln_byhint);
436         db_printf(" ln_state=%d\n", lle->ln_state);
437         db_printf(" ln_router=%u\n", lle->ln_router);
438         db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick);
439         db_printf(" lle_refcnt=%d\n", lle->lle_refcnt);
440         bcopy(&lle->ll_addr.mac16, octet, sizeof(octet));
441         db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
442             octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]);
443         db_printf(" la_timer=%p\n", &lle->la_timer);
444
445         switch (la->l3_addr.sa_family) {
446 #ifdef INET
447         case AF_INET:
448         {
449                 struct sockaddr_in *sin;
450                 char l3s[INET_ADDRSTRLEN];
451
452                 sin = (struct sockaddr_in *)&la->l3_addr;
453                 inet_ntoa_r(sin->sin_addr, l3s);
454                 db_printf(" l3_addr=%s\n", l3s);        
455                 break;
456         }
457 #endif
458 #ifdef INET6
459         case AF_INET6:
460         {
461                 struct sockaddr_in6 *sin6;
462                 char l3s[INET6_ADDRSTRLEN];
463
464                 sin6 = (struct sockaddr_in6 *)&la->l3_addr;
465                 ip6_sprintf(l3s, &sin6->sin6_addr);
466                 db_printf(" l3_addr=%s\n", l3s);        
467                 break;
468         }
469 #endif
470         default:
471                 db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family);
472                 break;
473         }
474 }
475
476 DB_SHOW_COMMAND(llentry, db_show_llentry)
477 {
478
479         if (!have_addr) {
480                 db_printf("usage: show llentry <struct llentry *>\n");
481                 return;
482         }
483
484         llatbl_lle_show((struct llentry_sa *)addr);
485 }
486
487 static void
488 llatbl_llt_show(struct lltable *llt)
489 {
490         int i;
491         struct llentry *lle;
492
493         db_printf("llt=%p llt_af=%d llt_ifp=%p\n",
494             llt, llt->llt_af, llt->llt_ifp);
495
496         for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
497                 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
498
499                         llatbl_lle_show((struct llentry_sa *)lle);
500                         if (db_pager_quit)
501                                 return;
502                 }
503         }
504 }
505
506 DB_SHOW_COMMAND(lltable, db_show_lltable)
507 {
508
509         if (!have_addr) {
510                 db_printf("usage: show lltable <struct lltable *>\n");
511                 return;
512         }
513
514         llatbl_llt_show((struct lltable *)addr);
515 }
516
517 DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables)
518 {
519         VNET_ITERATOR_DECL(vnet_iter);
520         struct lltable *llt;
521
522         VNET_FOREACH(vnet_iter) {
523                 CURVNET_SET_QUIET(vnet_iter);
524 #ifdef VIMAGE
525                 db_printf("vnet=%p\n", curvnet);
526 #endif
527                 SLIST_FOREACH(llt, &V_lltables, llt_link) {
528                         db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n",
529                             llt, llt->llt_af, llt->llt_ifp,
530                             (llt->llt_ifp != NULL) ?
531                                 llt->llt_ifp->if_xname : "?");
532                         if (have_addr && addr != 0) /* verbose */
533                                 llatbl_llt_show(llt);
534                         if (db_pager_quit) {
535                                 CURVNET_RESTORE();
536                                 return;
537                         }
538                 }
539                 CURVNET_RESTORE();
540         }
541 }
542 #endif