1/*	$OpenBSD: if.c,v 1.165 2007/07/06 14:00:59 naddy Exp $	*/
2/*	$NetBSD$	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * 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 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 1980, 1986, 1993
35 *	The Regents of the University of California.  All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. Neither the name of the University nor the names of its contributors
46 *    may be used to endorse or promote products derived from this software
47 *    without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 *
61 *	@(#)if.c	8.3 (Berkeley) 1/4/94
62 */
63
64#include <sys/cdefs.h>
65__KERNEL_RCSID(0, "$NetBSD$");
66
67#include "pf.h"
68
69#include <sys/param.h>
70#include <sys/systm.h>
71#include <sys/malloc.h>
72#include <sys/socket.h>
73#include <sys/socketvar.h>
74
75#include <net/if.h>
76
77#include <netinet/in.h>
78#include <netinet/in_var.h>
79
80#include <net/if_compat.h>
81
82#if NPF > 0
83#include <net/pfvar.h>
84#endif
85
86#if 0 /* XXX unused - remove later */
87static int if_getgroup(void *, struct ifnet *);
88static int if_getgroupmembers(void *);
89
90static int if_group_egress_build(void);
91#endif
92
93TAILQ_HEAD(, ifg_group) ifg_head = TAILQ_HEAD_INITIALIZER(ifg_head);
94
95void
96if_init_groups(struct ifnet *ifp)
97{
98	struct ifg_list_head *ifgh;
99
100	ifgh = malloc(sizeof(struct ifg_list_head), M_TEMP, M_WAITOK);
101	TAILQ_INIT(ifgh);
102
103	ifp->if_pf_groups = ifgh;
104}
105
106void
107if_destroy_groups(struct ifnet *ifp)
108{
109	struct ifg_list_head *ifgh = if_get_groups(ifp);
110
111	free(ifgh, M_TEMP);
112}
113
114struct ifg_list_head *
115if_get_groups(struct ifnet *ifp)
116{
117	return (ifp->if_pf_groups);
118}
119
120/*
121 * Create interface group without members.
122 */
123struct ifg_group *
124if_creategroup(const char *groupname)
125{
126	struct ifg_group	*ifg = NULL;
127
128	if ((ifg = (struct ifg_group *)malloc(sizeof(struct ifg_group),
129	    M_TEMP, M_NOWAIT)) == NULL)
130		return (NULL);
131
132	strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group));
133	ifg->ifg_refcnt = 0;
134	ifg->ifg_carp_demoted = 0;
135	TAILQ_INIT(&ifg->ifg_members);
136#if NPF > 0
137	pfi_attach_ifgroup(ifg);
138#endif
139	TAILQ_INSERT_TAIL(&ifg_head, ifg, ifg_next);
140
141	return (ifg);
142}
143
144/*
145 * Add a group to an interface.
146 */
147int
148if_addgroup(struct ifnet *ifp, const char *groupname)
149{
150	struct ifg_list_head	*ifgh = if_get_groups(ifp);
151	struct ifg_list		*ifgl;
152	struct ifg_group	*ifg = NULL;
153	struct ifg_member	*ifgm;
154
155	if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' &&
156	    groupname[strlen(groupname) - 1] <= '9')
157		return (EINVAL);
158
159	TAILQ_FOREACH(ifgl, ifgh, ifgl_next)
160		if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
161			return (EEXIST);
162
163	if ((ifgl = (struct ifg_list *)malloc(sizeof(struct ifg_list), M_TEMP,
164	    M_NOWAIT)) == NULL)
165		return (ENOMEM);
166
167	if ((ifgm = (struct ifg_member *)malloc(sizeof(struct ifg_member),
168	    M_TEMP, M_NOWAIT)) == NULL) {
169		free(ifgl, M_TEMP);
170		return (ENOMEM);
171	}
172
173	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
174		if (!strcmp(ifg->ifg_group, groupname))
175			break;
176
177	if (ifg == NULL && (ifg = if_creategroup(groupname)) == NULL) {
178		free(ifgl, M_TEMP);
179		free(ifgm, M_TEMP);
180		return (ENOMEM);
181	}
182
183	ifg->ifg_refcnt++;
184	ifgl->ifgl_group = ifg;
185	ifgm->ifgm_ifp = ifp;
186
187	TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next);
188	TAILQ_INSERT_TAIL(ifgh, ifgl, ifgl_next);
189
190#if NPF > 0
191	pfi_group_change(groupname);
192#endif
193
194	return (0);
195}
196
197/*
198 * Remove a group from an interface.
199 */
200int
201if_delgroup(struct ifnet *ifp, const char *groupname)
202{
203	struct ifg_list_head	*ifgh = if_get_groups(ifp);
204	struct ifg_list		*ifgl;
205	struct ifg_member	*ifgm;
206
207	TAILQ_FOREACH(ifgl, ifgh, ifgl_next)
208		if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
209			break;
210	if (ifgl == NULL)
211		return (ENOENT);
212
213	TAILQ_REMOVE(ifgh, ifgl, ifgl_next);
214
215	TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next)
216		if (ifgm->ifgm_ifp == ifp)
217			break;
218
219	if (ifgm != NULL) {
220		TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next);
221		free(ifgm, M_TEMP);
222	}
223
224	if (--ifgl->ifgl_group->ifg_refcnt == 0) {
225		TAILQ_REMOVE(&ifg_head, ifgl->ifgl_group, ifg_next);
226#if NPF > 0
227		pfi_detach_ifgroup(ifgl->ifgl_group);
228#endif
229		free(ifgl->ifgl_group, M_TEMP);
230	}
231
232	free(ifgl, M_TEMP);
233
234#if NPF > 0
235	pfi_group_change(groupname);
236#endif
237
238	return (0);
239}
240
241#if 0
242/*
243 * Stores all groups from an interface in memory pointed
244 * to by data.
245 */
246static int
247if_getgroup(void *data, struct ifnet *ifp)
248{
249	int			 len, error;
250	struct ifg_list_head	*ifgh = if_get_groups(ifp);
251	struct ifg_list		*ifgl;
252	struct ifg_req		 ifgrq, *ifgp;
253	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
254
255	if (ifgr->ifgr_len == 0) {
256		TAILQ_FOREACH(ifgl, ifgh, ifgl_next)
257			ifgr->ifgr_len += sizeof(struct ifg_req);
258		return (0);
259	}
260
261	len = ifgr->ifgr_len;
262	ifgp = ifgr->ifgr_groups;
263	TAILQ_FOREACH(ifgl, ifgh, ifgl_next) {
264		if (len < sizeof(ifgrq))
265			return (EINVAL);
266		bzero(&ifgrq, sizeof ifgrq);
267		strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
268		    sizeof(ifgrq.ifgrq_group));
269		if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req))))
270			return (error);
271		len -= sizeof(ifgrq);
272		ifgp++;
273	}
274
275	return (0);
276}
277
278/*
279 * Stores all members of a group in memory pointed to by data.
280 */
281static int
282if_getgroupmembers(void *data)
283{
284	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
285	struct ifg_group	*ifg;
286	struct ifg_member	*ifgm;
287	struct ifg_req		 ifgrq, *ifgp;
288	int			 len, error;
289
290	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
291		if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
292			break;
293	if (ifg == NULL)
294		return (ENOENT);
295
296	if (ifgr->ifgr_len == 0) {
297		TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
298			ifgr->ifgr_len += sizeof(ifgrq);
299		return (0);
300	}
301
302	len = ifgr->ifgr_len;
303	ifgp = ifgr->ifgr_groups;
304	TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) {
305		if (len < sizeof(ifgrq))
306			return (EINVAL);
307		bzero(&ifgrq, sizeof ifgrq);
308		strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname,
309		    sizeof(ifgrq.ifgrq_member));
310		if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req))))
311			return (error);
312		len -= sizeof(ifgrq);
313		ifgp++;
314	}
315
316	return (0);
317}
318
319void
320if_group_routechange(struct sockaddr *dst, struct sockaddr *mask)
321{
322	switch (dst->sa_family) {
323	case AF_INET:
324		if (satosin(dst)->sin_addr.s_addr == INADDR_ANY)
325			if_group_egress_build();
326		break;
327#ifdef INET6
328	case AF_INET6:
329		if (IN6_ARE_ADDR_EQUAL(&(satosin6(dst))->sin6_addr,
330		    &in6addr_any) &&
331		    mask && IN6_ARE_ADDR_EQUAL(&(satosin6(mask))->sin6_addr,
332		    &in6addr_any))
333			if_group_egress_build();
334		break;
335#endif
336	}
337}
338
339static int
340if_group_egress_build(void)
341{
342	struct ifg_group	*ifg;
343	struct ifg_member	*ifgm, *next;
344	struct sockaddr_in	 sa_in;
345#ifdef INET6
346	struct sockaddr_in6	 sa_in6;
347#endif
348	struct radix_node	*rn;
349	struct rtentry		*rt;
350
351	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
352		if (!strcmp(ifg->ifg_group, IFG_EGRESS))
353			break;
354
355	if (ifg != NULL)
356		for (ifgm = TAILQ_FIRST(&ifg->ifg_members); ifgm; ifgm = next) {
357			next = TAILQ_NEXT(ifgm, ifgm_next);
358			if_delgroup(ifgm->ifgm_ifp, IFG_EGRESS);
359		}
360
361	bzero(&sa_in, sizeof(sa_in));
362	sa_in.sin_len = sizeof(sa_in);
363	sa_in.sin_family = AF_INET;
364	if ((rn = rt_lookup(sintosa(&sa_in), sintosa(&sa_in), 0)) != NULL) {
365		do {
366			rt = (struct rtentry *)rn;
367			if (rt->rt_ifp)
368				if_addgroup(rt->rt_ifp, IFG_EGRESS);
369#ifndef SMALL_KERNEL
370			rn = rn_mpath_next(rn);
371#else
372			rn = NULL;
373#endif
374		} while (rn != NULL);
375	}
376
377#ifdef INET6
378	bcopy(&sa6_any, &sa_in6, sizeof(sa_in6));
379	if ((rn = rt_lookup(sin6tosa(&sa_in6), sin6tosa(&sa_in6), 0)) != NULL) {
380		do {
381			rt = (struct rtentry *)rn;
382			if (rt->rt_ifp)
383				if_addgroup(rt->rt_ifp, IFG_EGRESS);
384#ifndef SMALL_KERNEL
385			rn = rn_mpath_next(rn);
386#else
387			rn = NULL;
388#endif
389		} while (rn != NULL);
390	}
391#endif
392
393	return (0);
394}
395#endif
396