pf_if.c revision 135215
1190214Srpaulo/*	$FreeBSD: head/sys/contrib/pf/net/pf_if.c 135215 2004-09-14 15:20:24Z mlaier $ */
2190214Srpaulo/*	$OpenBSD: pf_if.c,v 1.11 2004/03/15 11:38:23 cedric Exp $ */
3190214Srpaulo/* add	$OpenBSD: pf_if.c,v 1.19 2004/08/11 12:06:44 henning Exp $ */
4190214Srpaulo
5190214Srpaulo/*
6190214Srpaulo * Copyright (c) 2001 Daniel Hartmeier
7190214Srpaulo * Copyright (c) 2003 Cedric Berger
8190214Srpaulo * All rights reserved.
9190214Srpaulo *
10190214Srpaulo * Redistribution and use in source and binary forms, with or without
11190214Srpaulo * modification, are permitted provided that the following conditions
12190214Srpaulo * are met:
13190214Srpaulo *
14190214Srpaulo *    - Redistributions of source code must retain the above copyright
15190214Srpaulo *      notice, this list of conditions and the following disclaimer.
16190214Srpaulo *    - Redistributions in binary form must reproduce the above
17190214Srpaulo *      copyright notice, this list of conditions and the following
18190214Srpaulo *      disclaimer in the documentation and/or other materials provided
19190214Srpaulo *      with the distribution.
20190214Srpaulo *
21190214Srpaulo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22190214Srpaulo * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23190214Srpaulo * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24190214Srpaulo * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25190214Srpaulo * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26190214Srpaulo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27190214Srpaulo * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28190214Srpaulo * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29190214Srpaulo * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30190214Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31190214Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32190214Srpaulo * POSSIBILITY OF SUCH DAMAGE.
33190214Srpaulo */
34190214Srpaulo
35190214Srpaulo#if defined(__FreeBSD__)
36190214Srpaulo#include "opt_inet.h"
37190214Srpaulo#include "opt_inet6.h"
38190214Srpaulo#endif
39190214Srpaulo
40190214Srpaulo#include <sys/param.h>
41190214Srpaulo#include <sys/systm.h>
42190214Srpaulo#ifdef __FreeBSD__
43190214Srpaulo#include <sys/malloc.h>
44190214Srpaulo#endif
45190214Srpaulo#include <sys/mbuf.h>
46190214Srpaulo#include <sys/filio.h>
47190214Srpaulo#include <sys/socket.h>
48190214Srpaulo#include <sys/socketvar.h>
49190214Srpaulo#include <sys/kernel.h>
50190214Srpaulo#ifndef __FreeBSD__
51190214Srpaulo#include <sys/device.h>
52190214Srpaulo#endif
53190214Srpaulo#include <sys/time.h>
54190214Srpaulo
55190214Srpaulo#include <net/if.h>
56190214Srpaulo#include <net/if_types.h>
57190214Srpaulo#include <net/route.h>
58190214Srpaulo
59190214Srpaulo#include <netinet/in.h>
60190214Srpaulo#include <netinet/in_var.h>
61190214Srpaulo#include <netinet/in_systm.h>
62190214Srpaulo#include <netinet/ip.h>
63190214Srpaulo#include <netinet/ip_var.h>
64190214Srpaulo
65190214Srpaulo#include <net/pfvar.h>
66190214Srpaulo
67190214Srpaulo#ifdef INET6
68190214Srpaulo#include <netinet/ip6.h>
69190214Srpaulo#endif /* INET6 */
70190214Srpaulo
71190214Srpaulo#define ACCEPT_FLAGS(oklist)			\
72190214Srpaulo	do {					\
73190214Srpaulo		if ((flags & ~(oklist)) &	\
74190214Srpaulo		    PFI_FLAG_ALLMASK)		\
75190214Srpaulo			return (EINVAL);	\
76190214Srpaulo	} while (0)
77190214Srpaulo
78190214Srpaulo#define senderr(e)      do { rv = (e); goto _bad; } while (0)
79190214Srpaulo
80190214Srpaulostruct pfi_kif		**pfi_index2kif;
81190214Srpaulostruct pfi_kif		 *pfi_self, *pfi_dummy;
82190214Srpauloint			  pfi_indexlim;
83190214Srpaulostruct pfi_ifhead	  pfi_ifs;
84190214Srpaulostruct pfi_statehead	  pfi_statehead;
85190214Srpauloint			  pfi_ifcnt;
86190214Srpaulo#ifdef __FreeBSD__
87190214Srpaulouma_zone_t		  pfi_addr_pl;
88190214Srpaulo#else
89190214Srpaulostruct pool		  pfi_addr_pl;
90190214Srpaulo#endif
91190214Srpaulolong			  pfi_update = 1;
92190214Srpaulostruct pfr_addr		 *pfi_buffer;
93190214Srpauloint			  pfi_buffer_cnt;
94190214Srpauloint			  pfi_buffer_max;
95190214Srpaulochar			  pfi_reserved_anchor[PF_ANCHOR_NAME_SIZE] =
96190214Srpaulo				PF_RESERVED_ANCHOR;
97190214Srpaulochar			  pfi_interface_ruleset[PF_RULESET_NAME_SIZE] =
98190214Srpaulo				PF_INTERFACE_RULESET;
99190214Srpaulo#ifdef __FreeBSD__
100190214Srpauloeventhandler_tag	 pfi_clone_cookie = NULL;
101190214Srpauloeventhandler_tag	 pfi_attach_cookie = NULL;
102190214Srpauloeventhandler_tag	 pfi_detach_cookie = NULL;
103190214Srpaulo#endif
104190214Srpaulo
105190214Srpaulovoid		 pfi_dynaddr_update(void *);
106190214Srpaulovoid		 pfi_kifaddr_update(void *);
107190214Srpaulovoid		 pfi_table_update(struct pfr_ktable *, struct pfi_kif *,
108190214Srpaulo		    int, int);
109190214Srpaulovoid		 pfi_instance_add(struct ifnet *, int, int);
110190214Srpaulovoid		 pfi_address_add(struct sockaddr *, int, int);
111190214Srpauloint		 pfi_if_compare(struct pfi_kif *, struct pfi_kif *);
112190214Srpaulostruct pfi_kif	*pfi_if_create(const char *, struct pfi_kif *, int);
113190214Srpaulovoid		 pfi_copy_group(char *, const char *, int);
114190214Srpaulovoid		 pfi_dynamic_drivers(void);
115190214Srpaulovoid		 pfi_newgroup(const char *, int);
116190214Srpauloint		 pfi_skip_if(const char *, struct pfi_kif *, int);
117190214Srpauloint		 pfi_unmask(void *);
118190214Srpaulovoid		 pfi_dohooks(struct pfi_kif *);
119190214Srpaulo#ifdef __FreeBSD__
120190214Srpaulovoid		 pfi_kifaddr_update_event(void *, struct ifnet *);
121190214Srpaulovoid		 pfi_attach_clone_event(void * __unused, struct if_clone *);
122190214Srpaulovoid		 pfi_attach_ifnet_event(void * __unused, struct ifnet *);
123190214Srpaulovoid		 pfi_detach_ifnet_event(void * __unused, struct ifnet *);
124190214Srpaulo#endif
125190214Srpaulo
126190214SrpauloRB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
127190214SrpauloRB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
128190214Srpaulo
129190214Srpaulo#define PFI_DYNAMIC_BUSES	{ "pcmcia", "cardbus", "uhub" }
130190214Srpaulo#define PFI_BUFFER_MAX		0x10000
131190214Srpaulo#ifdef __FreeBSD__
132190214SrpauloMALLOC_DEFINE(PFI_MTYPE, "pf_if", "pf interface table");
133190214Srpaulo#else
134190214Srpaulo#define PFI_MTYPE		M_IFADDR
135190214Srpaulo#endif
136190214Srpaulo
137190214Srpaulovoid
138190214Srpaulopfi_initialize(void)
139190214Srpaulo{
140190214Srpaulo#ifdef __FreeBSD__
141190214Srpaulo	struct ifnet	*ifp;
142190214Srpaulo#endif
143190214Srpaulo
144190214Srpaulo	if (pfi_self != NULL)	/* already initialized */
145190214Srpaulo		return;
146190214Srpaulo
147190214Srpaulo	TAILQ_INIT(&pfi_statehead);
148190214Srpaulo#ifndef __FreeBSD__
149190214Srpaulo	pool_init(&pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, 0, 0,
150190214Srpaulo	    "pfiaddrpl", &pool_allocator_nointr);
151190214Srpaulo#endif
152190214Srpaulo	pfi_buffer_max = 64;
153190214Srpaulo	pfi_buffer = malloc(pfi_buffer_max * sizeof(*pfi_buffer),
154190214Srpaulo	    PFI_MTYPE, M_WAITOK);
155190214Srpaulo	pfi_self = pfi_if_create("self", NULL, PFI_IFLAG_GROUP);
156190214Srpaulo	pfi_dynamic_drivers();
157190214Srpaulo#ifdef __FreeBSD__
158190214Srpaulo	PF_LOCK();
159190214Srpaulo	IFNET_RLOCK();
160190214Srpaulo	TAILQ_FOREACH(ifp, &ifnet, if_link)
161190214Srpaulo		if (ifp->if_dunit != IF_DUNIT_NONE) {
162190214Srpaulo			IFNET_RUNLOCK();
163190214Srpaulo			pfi_attach_ifnet(ifp);
164190214Srpaulo			IFNET_RLOCK();
165190214Srpaulo		}
166190214Srpaulo	IFNET_RUNLOCK();
167190214Srpaulo	PF_UNLOCK();
168190214Srpaulo	pfi_dummy = pfi_if_create("notyet", pfi_self,
169190214Srpaulo	    PFI_IFLAG_GROUP | PFI_IFLAG_DYNAMIC);
170190214Srpaulo	pfi_attach_cookie = EVENTHANDLER_REGISTER(ifnet_arrival_event,
171190214Srpaulo	    pfi_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
172190214Srpaulo	pfi_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
173190214Srpaulo	    pfi_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
174190214Srpaulo	pfi_clone_cookie = EVENTHANDLER_REGISTER(if_clone_event,
175190214Srpaulo	    pfi_attach_clone_event, NULL, EVENTHANDLER_PRI_ANY);
176190214Srpaulo#endif
177190214Srpaulo}
178190214Srpaulo
179190214Srpaulo#ifdef __FreeBSD__
180190214Srpaulovoid
181190214Srpaulopfi_cleanup(void)
182190214Srpaulo{
183190214Srpaulo	struct pfi_kif *p, key;
184190214Srpaulo	struct ifnet *ifp;
185190214Srpaulo
186190214Srpaulo	PF_ASSERT(MA_OWNED);
187190214Srpaulo
188190214Srpaulo	PF_UNLOCK();
189190214Srpaulo	EVENTHANDLER_DEREGISTER(ifnet_arrival_event, pfi_attach_cookie);
190190214Srpaulo	EVENTHANDLER_DEREGISTER(ifnet_departure_event, pfi_detach_cookie);
191190214Srpaulo	EVENTHANDLER_DEREGISTER(if_clone_event, pfi_clone_cookie);
192190214Srpaulo	PF_LOCK();
193190214Srpaulo
194190214Srpaulo	IFNET_RLOCK();
195190214Srpaulo	/* release PFI_IFLAG_INSTANCE */
196190214Srpaulo	TAILQ_FOREACH(ifp, &ifnet, if_link) {
197190214Srpaulo		strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name));
198190214Srpaulo		p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
199190214Srpaulo		if (p != NULL) {
200190214Srpaulo			IFNET_RUNLOCK();
201190214Srpaulo			pfi_detach_ifnet(ifp);
202190214Srpaulo			IFNET_RLOCK();
203190214Srpaulo		}
204190214Srpaulo	}
205190214Srpaulo	IFNET_RUNLOCK();
206190214Srpaulo
207190214Srpaulo	/* XXX clear all other interface group */
208190214Srpaulo	while ((p = RB_MIN(pfi_ifhead, &pfi_ifs))) {
209190214Srpaulo		RB_REMOVE(pfi_ifhead, &pfi_ifs, p);
210190214Srpaulo
211190214Srpaulo		free(p->pfik_ah_head, PFI_MTYPE);
212190214Srpaulo		free(p, PFI_MTYPE);
213190214Srpaulo	}
214190214Srpaulo	free(pfi_index2kif, PFI_MTYPE);
215190214Srpaulo	free(pfi_buffer, PFI_MTYPE);
216190214Srpaulo	pfi_index2kif = NULL;
217190214Srpaulo	pfi_buffer = NULL;
218190214Srpaulo	pfi_self = NULL;
219190214Srpaulo}
220190214Srpaulo
221190214Srpaulo/*
222190214Srpaulo * Wrapper functions for FreeBSD eventhandler
223190214Srpaulo */
224190214Srpaulovoid
225190214Srpaulopfi_kifaddr_update_event(void *arg, struct ifnet *ifp)
226190214Srpaulo{
227190214Srpaulo	struct pfi_kif *p = arg;
228190214Srpaulo
229190214Srpaulo	PF_LOCK();
230190214Srpaulo	/*
231190214Srpaulo	 * Check to see if it is 'our' interface as we do not have per
232190214Srpaulo	 * interface hooks and thus get an update for every interface.
233190214Srpaulo	 */
234190214Srpaulo	if (p && p->pfik_ifp == ifp)
235190214Srpaulo		pfi_kifaddr_update(p);
236190214Srpaulo	PF_UNLOCK();
237190214Srpaulo}
238190214Srpaulo
239190214Srpaulovoid
240190214Srpaulopfi_attach_clone_event(void *arg __unused, struct if_clone *ifc)
241190214Srpaulo{
242190214Srpaulo	PF_LOCK();
243190214Srpaulo	pfi_attach_clone(ifc);
244190214Srpaulo	PF_UNLOCK();
245190214Srpaulo}
246190214Srpaulo
247190214Srpaulovoid
248190214Srpaulopfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp)
249190214Srpaulo{
250190214Srpaulo	PF_LOCK();
251190214Srpaulo	if (ifp->if_dunit != IF_DUNIT_NONE)
252190214Srpaulo		pfi_attach_ifnet(ifp);
253190214Srpaulo	PF_UNLOCK();
254190214Srpaulo}
255190214Srpaulo
256190214Srpaulovoid
257190214Srpaulopfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp)
258190214Srpaulo{
259190214Srpaulo	PF_LOCK();
260190214Srpaulo	pfi_detach_ifnet(ifp);
261190214Srpaulo	PF_UNLOCK();
262190214Srpaulo}
263190214Srpaulo#endif /* __FreeBSD__ */
264190214Srpaulo
265190214Srpaulovoid
266190214Srpaulopfi_attach_clone(struct if_clone *ifc)
267190214Srpaulo{
268190214Srpaulo	pfi_initialize();
269190214Srpaulo	pfi_newgroup(ifc->ifc_name, PFI_IFLAG_CLONABLE);
270190214Srpaulo}
271190214Srpaulo
272190214Srpaulovoid
273190214Srpaulopfi_attach_ifnet(struct ifnet *ifp)
274190214Srpaulo{
275190214Srpaulo	struct pfi_kif	*p, *q, key;
276190214Srpaulo	int		 s;
277190214Srpaulo#ifdef __FreeBSD__
278190214Srpaulo	int		 realname;
279190214Srpaulo#endif
280190214Srpaulo
281190214Srpaulo	pfi_initialize();
282190214Srpaulo	s = splsoftnet();
283190214Srpaulo	pfi_update++;
284190214Srpaulo	if (ifp->if_index >= pfi_indexlim) {
285190214Srpaulo		/*
286190214Srpaulo		 * grow pfi_index2kif,  similar to ifindex2ifnet code in if.c
287190214Srpaulo		 */
288190214Srpaulo		size_t m, n, oldlim;
289190214Srpaulo		struct pfi_kif **mp, **np;
290190214Srpaulo
291190214Srpaulo		oldlim = pfi_indexlim;
292190214Srpaulo		if (pfi_indexlim == 0)
293190214Srpaulo			pfi_indexlim = 64;
294190214Srpaulo		while (ifp->if_index >= pfi_indexlim)
295190214Srpaulo			pfi_indexlim <<= 1;
296190214Srpaulo
297190214Srpaulo		m = oldlim * sizeof(struct pfi_kif *);
298190214Srpaulo		mp = pfi_index2kif;
299190214Srpaulo		n = pfi_indexlim * sizeof(struct pfi_kif *);
300190214Srpaulo#ifdef __FreeBSD__
301190214Srpaulo		np = malloc(n, PFI_MTYPE, M_NOWAIT);
302190214Srpaulo#else
303190214Srpaulo		np = malloc(n, PFI_MTYPE, M_DONTWAIT);
304190214Srpaulo#endif
305190214Srpaulo		if (np == NULL)
306190214Srpaulo			panic("pfi_attach_ifnet: "
307190214Srpaulo			    "cannot allocate translation table");
308190214Srpaulo		bzero(np, n);
309190214Srpaulo		if (mp != NULL)
310190214Srpaulo			bcopy(mp, np, m);
311190214Srpaulo		pfi_index2kif = np;
312190214Srpaulo		if (mp != NULL)
313190214Srpaulo			free(mp, PFI_MTYPE);
314190214Srpaulo	}
315190214Srpaulo
316190214Srpaulo	strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name));
317190214Srpaulo	p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
318190214Srpaulo#ifdef __FreeBSD__
319190214Srpaulo	/* some additional trickery for placeholders */
320190214Srpaulo	if ((p == NULL) || (p->pfik_parent == pfi_dummy)) {
321190214Srpaulo		/* are we looking at a renamed instance or not? */
322190214Srpaulo		pfi_copy_group(key.pfik_name, ifp->if_xname,
323190214Srpaulo		    sizeof(key.pfik_name));
324190214Srpaulo		realname = (strncmp(key.pfik_name, ifp->if_dname,
325190214Srpaulo		    sizeof(key.pfik_name)) == 0);
326190214Srpaulo		/* add group */
327190214Srpaulo		/* we can change if_xname, hence use if_dname as group id */
328190214Srpaulo		pfi_copy_group(key.pfik_name, ifp->if_dname,
329190214Srpaulo		    sizeof(key.pfik_name));
330190214Srpaulo		q = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
331190214Srpaulo		if (q == NULL)
332190214Srpaulo		    q = pfi_if_create(key.pfik_name, pfi_self,
333190214Srpaulo		        PFI_IFLAG_GROUP|PFI_IFLAG_DYNAMIC);
334190214Srpaulo		else if (q->pfik_parent == pfi_dummy) {
335190214Srpaulo			q->pfik_parent = pfi_self;
336190214Srpaulo			q->pfik_flags = (PFI_IFLAG_GROUP | PFI_IFLAG_DYNAMIC);
337190214Srpaulo		}
338190214Srpaulo		if (q == NULL)
339190214Srpaulo			panic("pfi_attach_ifnet: "
340190214Srpaulo			    "cannot allocate '%s' group", key.pfik_name);
341190214Srpaulo
342190214Srpaulo		/* add/modify interface */
343190214Srpaulo		if (p == NULL)
344190214Srpaulo			p = pfi_if_create(ifp->if_xname, q,
345190214Srpaulo			    realname?PFI_IFLAG_INSTANCE:PFI_IFLAG_PLACEHOLDER);
346190214Srpaulo		else {
347190214Srpaulo			/* remove from the dummy group */
348190214Srpaulo			/* XXX: copy stats? We should not have any!!! */
349190214Srpaulo			pfi_dummy->pfik_delcnt++;
350190214Srpaulo			TAILQ_REMOVE(&pfi_dummy->pfik_grouphead, p,
351190214Srpaulo			    pfik_instances);
352190214Srpaulo			/* move to the right group */
353190214Srpaulo			p->pfik_parent = q;
354190214Srpaulo			q->pfik_addcnt++;
355190214Srpaulo			TAILQ_INSERT_TAIL(&q->pfik_grouphead, p,
356190214Srpaulo			    pfik_instances);
357190214Srpaulo			if (realname) {
358190214Srpaulo				p->pfik_flags &= ~PFI_IFLAG_PLACEHOLDER;
359190214Srpaulo				p->pfik_flags |= PFI_IFLAG_INSTANCE;
360190214Srpaulo			}
361190214Srpaulo		}
362190214Srpaulo		if (p == NULL)
363190214Srpaulo			panic("pfi_attach_ifnet: "
364190214Srpaulo			    "cannot allocate '%s' interface", ifp->if_xname);
365190214Srpaulo#else
366190214Srpaulo	if (p == NULL) {
367190214Srpaulo		/* add group */
368190214Srpaulo		pfi_copy_group(key.pfik_name, ifp->if_xname,
369190214Srpaulo		    sizeof(key.pfik_name));
370190214Srpaulo		q = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
371190214Srpaulo		if (q == NULL)
372190214Srpaulo		    q = pfi_if_create(key.pfik_name, pfi_self, PFI_IFLAG_GROUP);
373190214Srpaulo		else if (q->pfik_parent == pfi_dummy) {
374190214Srpaulo			q->pfik_parent = pfi_self;
375190214Srpaulo			q->pfik_flags = (PFI_IFLAG_GROUP | PFI_IFLAG_DYNAMIC);
376190214Srpaulo		}
377190214Srpaulo		if (q == NULL)
378190214Srpaulo			panic("pfi_attach_ifnet: "
379190214Srpaulo			    "cannot allocate '%s' group", key.pfik_name);
380190214Srpaulo
381190214Srpaulo		/* add interface */
382190214Srpaulo		p = pfi_if_create(ifp->if_xname, q, PFI_IFLAG_INSTANCE);
383190214Srpaulo		if (p == NULL)
384190214Srpaulo			panic("pfi_attach_ifnet: "
385190214Srpaulo			    "cannot allocate '%s' interface", ifp->if_xname);
386190214Srpaulo#endif
387190214Srpaulo	} else
388190214Srpaulo		q = p->pfik_parent;
389190214Srpaulo	p->pfik_ifp = ifp;
390190214Srpaulo	p->pfik_flags |= PFI_IFLAG_ATTACHED;
391190214Srpaulo#ifdef __FreeBSD__
392190214Srpaulo	PF_UNLOCK();
393190214Srpaulo	p->pfik_ah_cookie = EVENTHANDLER_REGISTER(ifaddr_event,
394190214Srpaulo	    pfi_kifaddr_update_event, p, EVENTHANDLER_PRI_ANY);
395190214Srpaulo	PF_LOCK();
396190214Srpaulo#else
397190214Srpaulo	p->pfik_ah_cookie =
398190214Srpaulo	    hook_establish(ifp->if_addrhooks, 1, pfi_kifaddr_update, p);
399190214Srpaulo#endif
400190214Srpaulo	pfi_index2kif[ifp->if_index] = p;
401190214Srpaulo	pfi_dohooks(p);
402190214Srpaulo	splx(s);
403190214Srpaulo}
404190214Srpaulo
405190214Srpaulovoid
406190214Srpaulopfi_detach_ifnet(struct ifnet *ifp)
407190214Srpaulo{
408190214Srpaulo	struct pfi_kif	*p, *q, key;
409190214Srpaulo	int		 s;
410190214Srpaulo
411190214Srpaulo	strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name));
412190214Srpaulo
413190214Srpaulo	s = splsoftnet();
414190214Srpaulo	pfi_update++;
415190214Srpaulo	p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
416190214Srpaulo	if (p == NULL) {
417190214Srpaulo		printf("pfi_detach_ifnet: cannot find %s", ifp->if_xname);
418190214Srpaulo		splx(s);
419190214Srpaulo		return;
420190214Srpaulo	}
421190214Srpaulo#ifdef __FreeBSD__
422190214Srpaulo	PF_UNLOCK();
423190214Srpaulo	EVENTHANDLER_DEREGISTER(ifaddr_event, p->pfik_ah_cookie);
424190214Srpaulo	PF_LOCK();
425190214Srpaulo#else
426190214Srpaulo	hook_disestablish(p->pfik_ifp->if_addrhooks, p->pfik_ah_cookie);
427190214Srpaulo#endif
428190214Srpaulo	q = p->pfik_parent;
429190214Srpaulo	p->pfik_ifp = NULL;
430190214Srpaulo	p->pfik_flags &= ~PFI_IFLAG_ATTACHED;
431190214Srpaulo	pfi_index2kif[ifp->if_index] = NULL;
432190214Srpaulo	pfi_dohooks(p);
433190214Srpaulo	pfi_maybe_destroy(p);
434190214Srpaulo	splx(s);
435190214Srpaulo}
436190214Srpaulo
437190214Srpaulostruct pfi_kif *
438190214Srpaulopfi_lookup_create(const char *name)
439190214Srpaulo{
440190214Srpaulo	struct pfi_kif	*p, *q, key;
441190214Srpaulo	int		 s;
442190214Srpaulo
443190214Srpaulo	s = splsoftnet();
444190214Srpaulo	p = pfi_lookup_if(name);
445190214Srpaulo	if (p == NULL) {
446190214Srpaulo		pfi_copy_group(key.pfik_name, name, sizeof(key.pfik_name));
447190214Srpaulo		q = pfi_lookup_if(key.pfik_name);
448190214Srpaulo#ifdef __FreeBSD__
449190214Srpaulo		if ((q != NULL) && (q->pfik_parent != pfi_dummy))
450190214Srpaulo			p = pfi_if_create(name, q, PFI_IFLAG_INSTANCE);
451190214Srpaulo		else {
452190214Srpaulo			if (pfi_dummy == NULL)
453190214Srpaulo				panic("no 'notyet' dummy group");
454190214Srpaulo			p = pfi_if_create(name, pfi_dummy,
455190214Srpaulo			    PFI_IFLAG_PLACEHOLDER);
456190214Srpaulo		}
457190214Srpaulo#else
458190214Srpaulo		if (q != NULL)
459190214Srpaulo			p = pfi_if_create(name, q, PFI_IFLAG_INSTANCE);
460190214Srpaulo#endif
461190214Srpaulo	}
462190214Srpaulo	splx(s);
463190214Srpaulo	return (p);
464190214Srpaulo}
465190214Srpaulo
466190214Srpaulostruct pfi_kif *
467190214Srpaulopfi_attach_rule(const char *name)
468190214Srpaulo{
469190214Srpaulo	struct pfi_kif	*p;
470190214Srpaulo
471190214Srpaulo	p = pfi_lookup_create(name);
472190214Srpaulo	if (p != NULL)
473190214Srpaulo		p->pfik_rules++;
474190214Srpaulo	return (p);
475190214Srpaulo}
476190214Srpaulo
477190214Srpaulovoid
478190214Srpaulopfi_detach_rule(struct pfi_kif *p)
479190214Srpaulo{
480190214Srpaulo	if (p == NULL)
481190214Srpaulo		return;
482190214Srpaulo	if (p->pfik_rules > 0)
483190214Srpaulo		p->pfik_rules--;
484190214Srpaulo	else
485190214Srpaulo		printf("pfi_detach_rule: reference count at 0\n");
486190214Srpaulo	pfi_maybe_destroy(p);
487190214Srpaulo}
488190214Srpaulo
489190214Srpaulovoid
490190214Srpaulopfi_attach_state(struct pfi_kif *p)
491190214Srpaulo{
492190214Srpaulo	if (!p->pfik_states++)
493190214Srpaulo		TAILQ_INSERT_TAIL(&pfi_statehead, p, pfik_w_states);
494190214Srpaulo}
495190214Srpaulo
496190214Srpaulovoid
497190214Srpaulopfi_detach_state(struct pfi_kif *p)
498190214Srpaulo{
499190214Srpaulo	if (p == NULL)
500190214Srpaulo		return;
501190214Srpaulo	if (p->pfik_states <= 0) {
502190214Srpaulo		printf("pfi_detach_state: reference count <= 0\n");
503190214Srpaulo		return;
504190214Srpaulo	}
505190214Srpaulo	if (!--p->pfik_states)
506190214Srpaulo		TAILQ_REMOVE(&pfi_statehead, p, pfik_w_states);
507190214Srpaulo	pfi_maybe_destroy(p);
508190214Srpaulo}
509190214Srpaulo
510190214Srpauloint
511190214Srpaulopfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af)
512190214Srpaulo{
513190214Srpaulo	struct pfi_dynaddr	*dyn;
514190214Srpaulo	char			 tblname[PF_TABLE_NAME_SIZE];
515190214Srpaulo	struct pf_ruleset	*ruleset = NULL;
516190214Srpaulo	int			 s, rv = 0;
517190214Srpaulo
518190214Srpaulo	if (aw->type != PF_ADDR_DYNIFTL)
519190214Srpaulo		return (0);
520190214Srpaulo	dyn = pool_get(&pfi_addr_pl, PR_NOWAIT);
521190214Srpaulo	if (dyn == NULL)
522190214Srpaulo		return (1);
523190214Srpaulo	bzero(dyn, sizeof(*dyn));
524190214Srpaulo
525190214Srpaulo	s = splsoftnet();
526190214Srpaulo	dyn->pfid_kif = pfi_attach_rule(aw->v.ifname);
527190214Srpaulo	if (dyn->pfid_kif == NULL)
528190214Srpaulo		senderr(1);
529190214Srpaulo
530190214Srpaulo	dyn->pfid_net = pfi_unmask(&aw->v.a.mask);
531190214Srpaulo	if (af == AF_INET && dyn->pfid_net == 32)
532190214Srpaulo		dyn->pfid_net = 128;
533190214Srpaulo	strlcpy(tblname, aw->v.ifname, sizeof(tblname));
534190214Srpaulo	if (aw->iflags & PFI_AFLAG_NETWORK)
535190214Srpaulo		strlcat(tblname, ":network", sizeof(tblname));
536190214Srpaulo	if (aw->iflags & PFI_AFLAG_BROADCAST)
537190214Srpaulo		strlcat(tblname, ":broadcast", sizeof(tblname));
538190214Srpaulo	if (aw->iflags & PFI_AFLAG_PEER)
539190214Srpaulo		strlcat(tblname, ":peer", sizeof(tblname));
540190214Srpaulo	if (aw->iflags & PFI_AFLAG_NOALIAS)
541190214Srpaulo		strlcat(tblname, ":0", sizeof(tblname));
542190214Srpaulo	if (dyn->pfid_net != 128)
543190214Srpaulo		snprintf(tblname + strlen(tblname),
544190214Srpaulo		    sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net);
545190214Srpaulo	ruleset = pf_find_or_create_ruleset(pfi_reserved_anchor,
546190214Srpaulo	    pfi_interface_ruleset);
547190214Srpaulo	if (ruleset == NULL)
548190214Srpaulo		senderr(1);
549190214Srpaulo
550190214Srpaulo	dyn->pfid_kt = pfr_attach_table(ruleset, tblname);
551190214Srpaulo	if (dyn->pfid_kt == NULL)
552190214Srpaulo		senderr(1);
553190214Srpaulo
554190214Srpaulo	dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE;
555190214Srpaulo	dyn->pfid_iflags = aw->iflags;
556190214Srpaulo	dyn->pfid_af = af;
557190214Srpaulo	dyn->pfid_hook_cookie = hook_establish(dyn->pfid_kif->pfik_ah_head, 1,
558190214Srpaulo	    pfi_dynaddr_update, dyn);
559190214Srpaulo	if (dyn->pfid_hook_cookie == NULL)
560190214Srpaulo		senderr(1);
561190214Srpaulo
562190214Srpaulo	aw->p.dyn = dyn;
563190214Srpaulo	pfi_dynaddr_update(aw->p.dyn);
564190214Srpaulo	splx(s);
565190214Srpaulo	return (0);
566190214Srpaulo
567190214Srpaulo_bad:
568190214Srpaulo	if (dyn->pfid_kt != NULL)
569190214Srpaulo		pfr_detach_table(dyn->pfid_kt);
570190214Srpaulo	if (ruleset != NULL)
571190214Srpaulo		pf_remove_if_empty_ruleset(ruleset);
572190214Srpaulo	if (dyn->pfid_kif != NULL)
573190214Srpaulo		pfi_detach_rule(dyn->pfid_kif);
574190214Srpaulo	pool_put(&pfi_addr_pl, dyn);
575190214Srpaulo	splx(s);
576190214Srpaulo	return (rv);
577190214Srpaulo}
578190214Srpaulo
579190214Srpaulovoid
580190214Srpaulopfi_dynaddr_update(void *p)
581190214Srpaulo{
582190214Srpaulo	struct pfi_dynaddr	*dyn = (struct pfi_dynaddr *)p;
583190214Srpaulo	struct pfi_kif		*kif = dyn->pfid_kif;
584190214Srpaulo	struct pfr_ktable	*kt = dyn->pfid_kt;
585190214Srpaulo
586190214Srpaulo	if (dyn == NULL || kif == NULL || kt == NULL)
587190214Srpaulo		panic("pfi_dynaddr_update");
588190214Srpaulo	if (kt->pfrkt_larg != pfi_update) {
589190214Srpaulo		/* this table needs to be brought up-to-date */
590190214Srpaulo		pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags);
591190214Srpaulo		kt->pfrkt_larg = pfi_update;
592190214Srpaulo	}
593190214Srpaulo	pfr_dynaddr_update(kt, dyn);
594190214Srpaulo}
595190214Srpaulo
596190214Srpaulovoid
597190214Srpaulopfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags)
598190214Srpaulo{
599190214Srpaulo	int			 e, size2 = 0;
600190214Srpaulo	struct pfi_kif		*p;
601190214Srpaulo	struct pfr_table	 t;
602190214Srpaulo
603190214Srpaulo	if ((kif->pfik_flags & PFI_IFLAG_INSTANCE) && kif->pfik_ifp == NULL) {
604190214Srpaulo		pfr_clr_addrs(&kt->pfrkt_t, NULL, 0);
605190214Srpaulo		return;
606190214Srpaulo	}
607190214Srpaulo	pfi_buffer_cnt = 0;
608190214Srpaulo	if ((kif->pfik_flags & PFI_IFLAG_INSTANCE))
609190214Srpaulo		pfi_instance_add(kif->pfik_ifp, net, flags);
610190214Srpaulo	else if (strcmp(kif->pfik_name, "self")) {
611190214Srpaulo		TAILQ_FOREACH(p, &kif->pfik_grouphead, pfik_instances)
612190214Srpaulo			pfi_instance_add(p->pfik_ifp, net, flags);
613190214Srpaulo	} else {
614190214Srpaulo		RB_FOREACH(p, pfi_ifhead, &pfi_ifs)
615190214Srpaulo			if (p->pfik_flags & PFI_IFLAG_INSTANCE)
616190214Srpaulo				pfi_instance_add(p->pfik_ifp, net, flags);
617190214Srpaulo	}
618190214Srpaulo	t = kt->pfrkt_t;
619190214Srpaulo	t.pfrt_flags = 0;
620190214Srpaulo	if ((e = pfr_set_addrs(&t, pfi_buffer, pfi_buffer_cnt, &size2,
621190214Srpaulo	    NULL, NULL, NULL, 0)))
622190214Srpaulo		printf("pfi_table_update: cannot set %d new addresses "
623190214Srpaulo		    "into table %s: %d\n", pfi_buffer_cnt, kt->pfrkt_name, e);
624190214Srpaulo}
625190214Srpaulo
626190214Srpaulovoid
627190214Srpaulopfi_instance_add(struct ifnet *ifp, int net, int flags)
628190214Srpaulo{
629190214Srpaulo	struct ifaddr	*ia;
630190214Srpaulo	int		 got4 = 0, got6 = 0;
631190214Srpaulo	int		 net2, af;
632190214Srpaulo
633	if (ifp == NULL)
634		return;
635	TAILQ_FOREACH(ia, &ifp->if_addrlist, ifa_list) {
636		if (ia->ifa_addr == NULL)
637			continue;
638		af = ia->ifa_addr->sa_family;
639		if (af != AF_INET && af != AF_INET6)
640			continue;
641#ifdef __FreeBSD__
642		/*
643		 * XXX: For point-to-point interfaces, (ifname:0) and IPv4,
644		 *	jump over addresses without a proper route to work
645		 *	around a problem with ppp not fully removing the
646		 *	address used during IPCP.
647		 */
648		if ((ifp->if_flags & IFF_POINTOPOINT) &&
649		    !(ia->ifa_flags & IFA_ROUTE) &&
650		    (flags & PFI_AFLAG_NOALIAS) && (af == AF_INET))
651			continue;
652#endif
653		if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6)
654			continue;
655		if ((flags & PFI_AFLAG_BROADCAST) &&
656		    !(ifp->if_flags & IFF_BROADCAST))
657			continue;
658		if ((flags & PFI_AFLAG_PEER) &&
659		    !(ifp->if_flags & IFF_POINTOPOINT))
660			continue;
661		if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 &&
662		    IN6_IS_ADDR_LINKLOCAL(
663		    &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr))
664			continue;
665		if (flags & PFI_AFLAG_NOALIAS) {
666			if (af == AF_INET && got4)
667				continue;
668			if (af == AF_INET6 && got6)
669				continue;
670		}
671		if (af == AF_INET)
672			got4 = 1;
673		else
674			got6 = 1;
675		net2 = net;
676		if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) {
677			if (af == AF_INET) {
678				net2 = pfi_unmask(&((struct sockaddr_in *)
679				    ia->ifa_netmask)->sin_addr);
680			} else {
681				net2 = pfi_unmask(&((struct sockaddr_in6 *)
682				    ia->ifa_netmask)->sin6_addr);
683			}
684		}
685		if (af == AF_INET && net2 > 32)
686			net2 = 32;
687		if (flags & PFI_AFLAG_BROADCAST)
688			pfi_address_add(ia->ifa_broadaddr, af, net2);
689		else if (flags & PFI_AFLAG_PEER)
690			pfi_address_add(ia->ifa_dstaddr, af, net2);
691		else
692			pfi_address_add(ia->ifa_addr, af, net2);
693	}
694}
695
696void
697pfi_address_add(struct sockaddr *sa, int af, int net)
698{
699	struct pfr_addr	*p;
700	int		 i;
701
702	if (pfi_buffer_cnt >= pfi_buffer_max) {
703		int		 new_max = pfi_buffer_max * 2;
704
705		if (new_max > PFI_BUFFER_MAX) {
706			printf("pfi_address_add: address buffer full (%d/%d)\n",
707			    pfi_buffer_cnt, PFI_BUFFER_MAX);
708			return;
709		}
710#ifdef __FreeBSD__
711		p = malloc(new_max * sizeof(*pfi_buffer), PFI_MTYPE,
712		    M_NOWAIT);
713#else
714		p = malloc(new_max * sizeof(*pfi_buffer), PFI_MTYPE,
715		    M_DONTWAIT);
716#endif
717		if (p == NULL) {
718			printf("pfi_address_add: no memory to grow buffer "
719			    "(%d/%d)\n", pfi_buffer_cnt, PFI_BUFFER_MAX);
720			return;
721		}
722		memcpy(pfi_buffer, p, pfi_buffer_cnt * sizeof(*pfi_buffer));
723		/* no need to zero buffer */
724		free(pfi_buffer, PFI_MTYPE);
725		pfi_buffer = p;
726		pfi_buffer_max = new_max;
727	}
728	if (af == AF_INET && net > 32)
729		net = 128;
730	p = pfi_buffer + pfi_buffer_cnt++;
731	bzero(p, sizeof(*p));
732	p->pfra_af = af;
733	p->pfra_net = net;
734	if (af == AF_INET)
735		p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr;
736	if (af == AF_INET6) {
737		p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
738		if (IN6_IS_ADDR_LINKLOCAL(&p->pfra_ip6addr))
739			p->pfra_ip6addr.s6_addr16[1] = 0;
740	}
741	/* mask network address bits */
742	if (net < 128)
743		((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8));
744	for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++)
745		((caddr_t)p)[i] = 0;
746}
747
748void
749pfi_dynaddr_remove(struct pf_addr_wrap *aw)
750{
751	int	s;
752
753	if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
754	    aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL)
755		return;
756
757	s = splsoftnet();
758	hook_disestablish(aw->p.dyn->pfid_kif->pfik_ah_head,
759	    aw->p.dyn->pfid_hook_cookie);
760	pfi_detach_rule(aw->p.dyn->pfid_kif);
761	aw->p.dyn->pfid_kif = NULL;
762	pfr_detach_table(aw->p.dyn->pfid_kt);
763	aw->p.dyn->pfid_kt = NULL;
764	pool_put(&pfi_addr_pl, aw->p.dyn);
765	aw->p.dyn = NULL;
766	splx(s);
767}
768
769void
770pfi_dynaddr_copyout(struct pf_addr_wrap *aw)
771{
772	if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
773	    aw->p.dyn->pfid_kif == NULL)
774		return;
775	aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6;
776}
777
778void
779pfi_kifaddr_update(void *v)
780{
781	int		 s;
782
783	s = splsoftnet();
784	pfi_update++;
785	pfi_dohooks(v);
786	splx(s);
787}
788
789int
790pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q)
791{
792	return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ));
793}
794
795struct pfi_kif *
796pfi_if_create(const char *name, struct pfi_kif *q, int flags)
797{
798	struct pfi_kif *p;
799
800#ifdef __FreeBSD__
801	p = malloc(sizeof(*p), PFI_MTYPE, M_NOWAIT);
802#else
803	p = malloc(sizeof(*p), PFI_MTYPE, M_DONTWAIT);
804#endif
805	if (p == NULL)
806		return (NULL);
807	bzero(p, sizeof(*p));
808#ifdef __FreeBSD__
809	p->pfik_ah_head = malloc(sizeof(*p->pfik_ah_head), PFI_MTYPE,
810	    M_NOWAIT);
811#else
812	p->pfik_ah_head = malloc(sizeof(*p->pfik_ah_head), PFI_MTYPE,
813	    M_DONTWAIT);
814#endif
815	if (p->pfik_ah_head == NULL) {
816		free(p, PFI_MTYPE);
817		return (NULL);
818	}
819	bzero(p->pfik_ah_head, sizeof(*p->pfik_ah_head));
820	TAILQ_INIT(p->pfik_ah_head);
821	TAILQ_INIT(&p->pfik_grouphead);
822	strlcpy(p->pfik_name, name, sizeof(p->pfik_name));
823	RB_INIT(&p->pfik_lan_ext);
824	RB_INIT(&p->pfik_ext_gwy);
825	p->pfik_flags = flags;
826	p->pfik_parent = q;
827#ifdef __FreeBSD__
828	p->pfik_tzero = time_second;
829#else
830	p->pfik_tzero = time.tv_sec;
831#endif
832
833	RB_INSERT(pfi_ifhead, &pfi_ifs, p);
834	if (q != NULL) {
835		q->pfik_addcnt++;
836		TAILQ_INSERT_TAIL(&q->pfik_grouphead, p, pfik_instances);
837	}
838	pfi_ifcnt++;
839	return (p);
840}
841
842int
843pfi_maybe_destroy(struct pfi_kif *p)
844{
845	int		 i, j, k, s;
846	struct pfi_kif	*q = p->pfik_parent;
847
848	if ((p->pfik_flags & (PFI_IFLAG_ATTACHED | PFI_IFLAG_GROUP)) ||
849	    p->pfik_rules > 0 || p->pfik_states > 0)
850#ifdef __FreeBSD__
851		if (!(p->pfik_flags & PFI_IFLAG_PLACEHOLDER))
852#endif
853		return (0);
854
855	s = splsoftnet();
856	if (q != NULL) {
857		for (i = 0; i < 2; i++)
858			for (j = 0; j < 2; j++)
859				for (k = 0; k < 2; k++) {
860					q->pfik_bytes[i][j][k] +=
861					    p->pfik_bytes[i][j][k];
862					q->pfik_packets[i][j][k] +=
863					    p->pfik_packets[i][j][k];
864#ifdef __FreeBSD__
865			/* clear stats in case we return to the dummy group */
866					p->pfik_bytes[i][j][k] = 0;
867					p->pfik_packets[i][j][k] = 0;
868#endif
869				}
870		q->pfik_delcnt++;
871		TAILQ_REMOVE(&q->pfik_grouphead, p, pfik_instances);
872	}
873#ifdef __FreeBSD__
874	if (p->pfik_rules > 0 || p->pfik_states > 0) {
875		/* move back to the dummy group */
876		p->pfik_parent = pfi_dummy;
877		pfi_dummy->pfik_addcnt++;
878		TAILQ_INSERT_TAIL(&pfi_dummy->pfik_grouphead, p,
879		    pfik_instances);
880		return (0);
881	}
882#endif
883	pfi_ifcnt--;
884	RB_REMOVE(pfi_ifhead, &pfi_ifs, p);
885	splx(s);
886
887	free(p->pfik_ah_head, PFI_MTYPE);
888	free(p, PFI_MTYPE);
889	return (1);
890}
891
892void
893pfi_copy_group(char *p, const char *q, int m)
894{
895	while (m > 1 && *q && !(*q >= '0' && *q <= '9')) {
896		*p++ = *q++;
897		m--;
898	}
899	if (m > 0)
900		*p++ = '\0';
901}
902
903void
904pfi_dynamic_drivers(void)
905{
906#ifdef __FreeBSD__
907	struct ifnet	*ifp;
908
909/*
910 * For FreeBSD basically every interface is "dynamic" as we can unload
911 * modules e.g.
912 */
913
914	IFNET_RLOCK();
915	TAILQ_FOREACH(ifp, &ifnet, if_link) {
916		if (ifp->if_dunit == IF_DUNIT_NONE)
917			continue;
918		pfi_newgroup(ifp->if_dname, PFI_IFLAG_DYNAMIC);
919	}
920	IFNET_RUNLOCK();
921#else
922	char		*buses[] = PFI_DYNAMIC_BUSES;
923	int		 nbuses = sizeof(buses)/sizeof(buses[0]);
924	int		 enabled[sizeof(buses)/sizeof(buses[0])];
925	struct device	*dev;
926	struct cfdata	*cf;
927	struct cfdriver	*drv;
928	short		*p;
929	int		 i;
930
931	bzero(enabled, sizeof(enabled));
932	TAILQ_FOREACH(dev, &alldevs, dv_list) {
933		if (!(dev->dv_flags & DVF_ACTIVE))
934			continue;
935		for (i = 0; i < nbuses; i++)
936			if (!enabled[i] && !strcmp(buses[i],
937			    dev->dv_cfdata->cf_driver->cd_name))
938				enabled[i] = 1;
939	}
940	for (cf = cfdata; cf->cf_driver; cf++) {
941		if (cf->cf_driver->cd_class != DV_IFNET)
942			continue;
943		for (p = cf->cf_parents; p && *p >= 0; p++) {
944			if ((drv = cfdata[*p].cf_driver) == NULL)
945				continue;
946			for (i = 0; i < nbuses; i++)
947				if (enabled[i] &&
948				    !strcmp(drv->cd_name, buses[i]))
949					break;
950			if (i < nbuses) {
951				pfi_newgroup(cf->cf_driver->cd_name,
952				    PFI_IFLAG_DYNAMIC);
953				break;
954			}
955		}
956	}
957#endif
958}
959
960void
961pfi_newgroup(const char *name, int flags)
962{
963	struct pfi_kif	*p;
964
965	p = pfi_lookup_if(name);
966	if (p == NULL)
967		p = pfi_if_create(name, pfi_self, PFI_IFLAG_GROUP);
968	if (p == NULL) {
969		printf("pfi_newgroup: cannot allocate '%s' group", name);
970		return;
971	}
972	p->pfik_flags |= flags;
973}
974
975void
976pfi_fill_oldstatus(struct pf_status *pfs)
977{
978	struct pfi_kif	*p, key;
979	int		 i, j, k, s;
980
981	strlcpy(key.pfik_name, pfs->ifname, sizeof(key.pfik_name));
982	s = splsoftnet();
983	p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
984	if (p == NULL) {
985		splx(s);
986		return;
987	}
988	bzero(pfs->pcounters, sizeof(pfs->pcounters));
989	bzero(pfs->bcounters, sizeof(pfs->bcounters));
990	for (i = 0; i < 2; i++)
991		for (j = 0; j < 2; j++)
992			for (k = 0; k < 2; k++) {
993				pfs->pcounters[i][j][k] =
994					p->pfik_packets[i][j][k];
995				pfs->bcounters[i][j] +=
996					p->pfik_bytes[i][j][k];
997			}
998	splx(s);
999}
1000
1001int
1002pfi_clr_istats(const char *name, int *nzero, int flags)
1003{
1004	struct pfi_kif	*p;
1005	int		 n = 0, s;
1006#ifdef __FreeBSD__
1007	long		 tzero = time_second;
1008#else
1009	long		 tzero = time.tv_sec;
1010#endif
1011
1012	s = splsoftnet();
1013	ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE);
1014	RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
1015		if (pfi_skip_if(name, p, flags))
1016			continue;
1017		bzero(p->pfik_packets, sizeof(p->pfik_packets));
1018		bzero(p->pfik_bytes, sizeof(p->pfik_bytes));
1019		p->pfik_tzero = tzero;
1020		n++;
1021	}
1022	splx(s);
1023	if (nzero != NULL)
1024		*nzero = n;
1025	return (0);
1026}
1027
1028int
1029pfi_get_ifaces(const char *name, struct pfi_if *buf, int *size, int flags)
1030{
1031	struct pfi_kif	*p;
1032	int		 s, n = 0;
1033#ifdef __FreeBSD__
1034	int		 ec;
1035#endif
1036
1037	ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE);
1038	s = splsoftnet();
1039	RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
1040		if (pfi_skip_if(name, p, flags))
1041			continue;
1042		if (*size > n++) {
1043			if (!p->pfik_tzero)
1044				p->pfik_tzero = boottime.tv_sec;
1045#ifdef __FreeBSD__
1046			PF_COPYOUT(p, buf++, sizeof(*buf), ec);
1047			if (ec) {
1048#else
1049			if (copyout(p, buf++, sizeof(*buf))) {
1050#endif
1051				splx(s);
1052				return (EFAULT);
1053			}
1054		}
1055	}
1056	splx(s);
1057	*size = n;
1058	return (0);
1059}
1060
1061struct pfi_kif *
1062pfi_lookup_if(const char *name)
1063{
1064	struct pfi_kif	*p, key;
1065
1066	strlcpy(key.pfik_name, name, sizeof(key.pfik_name));
1067	p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
1068	return (p);
1069}
1070
1071int
1072pfi_skip_if(const char *filter, struct pfi_kif *p, int f)
1073{
1074	int	n;
1075
1076	if ((p->pfik_flags & PFI_IFLAG_GROUP) && !(f & PFI_FLAG_GROUP))
1077		return (1);
1078	if ((p->pfik_flags & PFI_IFLAG_INSTANCE) && !(f & PFI_FLAG_INSTANCE))
1079		return (1);
1080	if (filter == NULL || !*filter)
1081		return (0);
1082	if (!strcmp(p->pfik_name, filter))
1083		return (0);	/* exact match */
1084	n = strlen(filter);
1085	if (n < 1 || n >= IFNAMSIZ)
1086		return (1);	/* sanity check */
1087	if (filter[n-1] >= '0' && filter[n-1] <= '9')
1088		return (1);	/* only do exact match in that case */
1089	if (strncmp(p->pfik_name, filter, n))
1090		return (1);	/* prefix doesn't match */
1091	return (p->pfik_name[n] < '0' || p->pfik_name[n] > '9');
1092}
1093
1094/* from pf_print_state.c */
1095int
1096pfi_unmask(void *addr)
1097{
1098	struct pf_addr *m = addr;
1099	int i = 31, j = 0, b = 0;
1100	u_int32_t tmp;
1101
1102	while (j < 4 && m->addr32[j] == 0xffffffff) {
1103		b += 32;
1104		j++;
1105	}
1106	if (j < 4) {
1107		tmp = ntohl(m->addr32[j]);
1108		for (i = 31; tmp & (1 << i); --i)
1109			b++;
1110	}
1111	return (b);
1112}
1113
1114void
1115pfi_dohooks(struct pfi_kif *p)
1116{
1117	for (; p != NULL; p = p->pfik_parent)
1118		dohooks(p->pfik_ah_head, 0);
1119}
1120
1121int
1122pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af)
1123{
1124	if (af == AF_INET) {
1125		switch (dyn->pfid_acnt4) {
1126		case 0:
1127			return (0);
1128		case 1:
1129			return (PF_MATCHA(0, &dyn->pfid_addr4,
1130			    &dyn->pfid_mask4, a, AF_INET));
1131		default:
1132			return (pfr_match_addr(dyn->pfid_kt, a, AF_INET));
1133		}
1134	} else {
1135		switch (dyn->pfid_acnt6) {
1136		case 0:
1137			return (0);
1138		case 1:
1139			return (PF_MATCHA(0, &dyn->pfid_addr6,
1140			    &dyn->pfid_mask6, a, AF_INET6));
1141		default:
1142			return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6));
1143		}
1144	}
1145}
1146