if_llatbl.c revision 191154
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 191154 2009-04-16 22:04:07Z kmacy $");
29
30#include "opt_inet.h"
31#include "opt_inet6.h"
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/malloc.h>
36#include <sys/mbuf.h>
37#include <sys/syslog.h>
38#include <sys/sysctl.h>
39#include <sys/socket.h>
40#include <sys/kernel.h>
41#include <sys/lock.h>
42#include <sys/mutex.h>
43#include <sys/rwlock.h>
44#include <sys/vimage.h>
45
46#include <vm/uma.h>
47
48#include <netinet/in.h>
49#include <net/if_llatbl.h>
50#include <net/if.h>
51#include <net/if_dl.h>
52#include <net/if_var.h>
53#include <net/route.h>
54#include <netinet/if_ether.h>
55#include <netinet6/in6_var.h>
56#include <netinet6/nd6.h>
57
58MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables");
59
60static	SLIST_HEAD(, lltable) lltables = SLIST_HEAD_INITIALIZER(lltables);
61
62extern void arprequest(struct ifnet *, struct in_addr *, struct in_addr *,
63	u_char *);
64
65/*
66 * Dump arp state for a specific address family.
67 */
68int
69lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
70{
71	struct lltable *llt;
72	int error = 0;
73
74	IFNET_RLOCK();
75	SLIST_FOREACH(llt, &lltables, llt_link) {
76		if (llt->llt_af == af) {
77			error = llt->llt_dump(llt, wr);
78			if (error != 0)
79				goto done;
80		}
81	}
82done:
83	IFNET_RUNLOCK();
84	return (error);
85}
86
87/*
88 * Deletes an address from the address table.
89 * This function is called by the timer functions
90 * such as arptimer() and nd6_llinfo_timer(), and
91 * the caller does the locking.
92 */
93void
94llentry_free(struct llentry *lle)
95{
96
97	LLE_WLOCK_ASSERT(lle);
98	LIST_REMOVE(lle, lle_next);
99
100	if (lle->la_hold != NULL)
101		m_freem(lle->la_hold);
102
103	LLE_FREE_LOCKED(lle);
104}
105
106int
107llentry_update(struct llentry **llep, struct lltable *lt,
108    struct sockaddr *dst, struct ifnet *ifp)
109{
110	struct llentry *la;
111
112	IF_AFDATA_RLOCK(ifp);
113	la = lla_lookup(lt, LLE_EXCLUSIVE,
114	    (struct sockaddr *)dst);
115	IF_AFDATA_RUNLOCK(ifp);
116	if ((la == NULL) &&
117	    (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
118		IF_AFDATA_WLOCK(ifp);
119		la = lla_lookup(lt,
120		    (LLE_CREATE | LLE_EXCLUSIVE),
121		    (struct sockaddr *)dst);
122		IF_AFDATA_WUNLOCK(ifp);
123	}
124	if (la != NULL && (*llep != la)) {
125		if (*llep != NULL)
126			LLE_FREE(*llep);
127		LLE_ADDREF(la);
128		LLE_WUNLOCK(la);
129		*llep = la;
130	} else if (la != NULL)
131		LLE_WUNLOCK(la);
132
133	if (la == NULL)
134		return (ENOENT);
135
136	return (0);
137}
138
139/*
140 * Free all entries from given table and free itself.
141 * Since lltables collects from all of the intefaces,
142 * the caller of this function must acquire IFNET_WLOCK().
143 */
144void
145lltable_free(struct lltable *llt)
146{
147	struct llentry *lle, *next;
148	int i;
149
150	KASSERT(llt != NULL, ("%s: llt is NULL", __func__));
151
152	IFNET_WLOCK();
153	SLIST_REMOVE(&lltables, llt, lltable, llt_link);
154	IFNET_WUNLOCK();
155
156	for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
157		LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
158
159			callout_drain(&lle->la_timer);
160			LLE_WLOCK(lle);
161			llentry_free(lle);
162		}
163	}
164
165	free(llt, M_LLTABLE);
166}
167
168void
169lltable_drain(int af)
170{
171	struct lltable	*llt;
172	struct llentry	*lle;
173	register int i;
174
175	IFNET_RLOCK();
176	SLIST_FOREACH(llt, &lltables, llt_link) {
177		if (llt->llt_af != af)
178			continue;
179
180		for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
181			LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
182				if (lle->la_hold) {
183					m_freem(lle->la_hold);
184					lle->la_hold = NULL;
185				}
186			}
187		}
188	}
189	IFNET_RUNLOCK();
190}
191
192/*
193 * Create a new lltable.
194 */
195struct lltable *
196lltable_init(struct ifnet *ifp, int af)
197{
198	struct lltable *llt;
199	register int i;
200
201	llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK);
202	if (llt == NULL)
203		return (NULL);
204
205	llt->llt_af = af;
206	llt->llt_ifp = ifp;
207	for (i = 0; i < LLTBL_HASHTBL_SIZE; i++)
208		LIST_INIT(&llt->lle_head[i]);
209
210	IFNET_WLOCK();
211	SLIST_INSERT_HEAD(&lltables, llt, llt_link);
212	IFNET_WUNLOCK();
213
214	return (llt);
215}
216
217/*
218 * Called in route_output when adding/deleting a route to an interface.
219 */
220int
221lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
222{
223	struct sockaddr_dl *dl =
224	    (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY];
225	struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST];
226	struct ifnet *ifp;
227	struct lltable *llt;
228	struct llentry *lle;
229	u_int laflags = 0, flags = 0;
230	int error = 0;
231
232	if (dl == NULL || dl->sdl_family != AF_LINK) {
233		log(LOG_INFO, "%s: invalid dl\n", __func__);
234		return EINVAL;
235	}
236	ifp = ifnet_byindex(dl->sdl_index);
237	if (ifp == NULL) {
238		log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n",
239		    __func__, dl->sdl_index);
240		return EINVAL;
241	}
242
243	switch (rtm->rtm_type) {
244	case RTM_ADD:
245		if (rtm->rtm_flags & RTF_ANNOUNCE) {
246			flags |= LLE_PUB;
247#ifdef INET
248			if (dst->sa_family == AF_INET &&
249			    ((struct sockaddr_inarp *)dst)->sin_other != 0) {
250				struct rtentry *rt = rtalloc1(dst, 0, 0);
251				if (rt == NULL || !(rt->rt_flags & RTF_HOST)) {
252					log(LOG_INFO, "%s: RTM_ADD publish "
253					    "(proxy only) is invalid\n",
254					    __func__);
255					if (rt)
256						RTFREE_LOCKED(rt);
257					return EINVAL;
258				}
259				RTFREE_LOCKED(rt);
260
261				flags |= LLE_PROXY;
262			}
263#endif
264		}
265		flags |= LLE_CREATE;
266		break;
267
268	case RTM_DELETE:
269		flags |= LLE_DELETE;
270		break;
271
272	case RTM_CHANGE:
273		break;
274
275	default:
276		return EINVAL; /* XXX not implemented yet */
277	}
278
279	/* XXX linked list may be too expensive */
280	IFNET_RLOCK();
281	SLIST_FOREACH(llt, &lltables, llt_link) {
282		if (llt->llt_af == dst->sa_family &&
283		    llt->llt_ifp == ifp)
284			break;
285	}
286	IFNET_RUNLOCK();
287	KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n"));
288
289	if (flags && LLE_CREATE)
290		flags |= LLE_EXCLUSIVE;
291
292	IF_AFDATA_LOCK(ifp);
293	lle = lla_lookup(llt, flags, dst);
294	IF_AFDATA_UNLOCK(ifp);
295	if (LLE_IS_VALID(lle)) {
296		if (flags & LLE_CREATE) {
297			/*
298			 * If we delay the delete, then a subsequent
299			 * "arp add" should look up this entry, reset the
300			 * LLE_DELETED flag, and reset the expiration timer
301			 */
302			bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
303			lle->la_flags |= LLE_VALID;
304			lle->la_flags &= ~LLE_DELETED;
305#ifdef INET6
306			/*
307			 * ND6
308			 */
309			if (dst->sa_family == AF_INET6)
310				lle->ln_state = ND6_LLINFO_REACHABLE;
311#endif
312			/*
313			 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST)
314			 */
315
316			if (rtm->rtm_rmx.rmx_expire == 0) {
317				lle->la_flags |= LLE_STATIC;
318				lle->la_expire = 0;
319			} else
320				lle->la_expire = rtm->rtm_rmx.rmx_expire;
321			laflags = lle->la_flags;
322			LLE_WUNLOCK(lle);
323#ifdef INET
324			/*  gratuitous ARP */
325			if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
326				arprequest(ifp,
327				    &((struct sockaddr_in *)dst)->sin_addr,
328				    &((struct sockaddr_in *)dst)->sin_addr,
329				    ((laflags & LLE_PROXY) ?
330					(u_char *)IF_LLADDR(ifp) :
331					(u_char *)LLADDR(dl)));
332			}
333#endif
334		} else {
335			if (flags & LLE_EXCLUSIVE)
336				LLE_WUNLOCK(lle);
337			else
338				LLE_RUNLOCK(lle);
339		}
340	} else if ((lle == NULL) && (flags & LLE_DELETE))
341		error = EINVAL;
342
343
344	return (error);
345}
346