if_llatbl.c revision 238967
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: head/sys/net/if_llatbl.c 238967 2012-08-01 09:00:26Z glebius $");
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
63MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables");
64
65static VNET_DEFINE(SLIST_HEAD(, lltable), lltables);
66#define	V_lltables	VNET(lltables)
67
68static void vnet_lltable_init(void);
69
70struct rwlock lltable_rwlock;
71RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock");
72
73/*
74 * Dump arp state for a specific address family.
75 */
76int
77lltable_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	}
90done:
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 */
103size_t
104llentry_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 */
137int
138llentry_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 */
173void
174lltable_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
201void
202lltable_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
228void
229lltable_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 */
249struct lltable *
250lltable_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 */
272int
273lla_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
402static void
403vnet_lltable_init()
404{
405
406	SLIST_INIT(&V_lltables);
407}
408VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST,
409    vnet_lltable_init, NULL);
410
411#ifdef DDB
412struct llentry_sa {
413	struct llentry		base;
414	struct sockaddr		l3_addr;
415};
416
417static void
418llatbl_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
476DB_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
487static void
488llatbl_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
506DB_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
517DB_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
543