154097Sarchie/*
254097Sarchie * ng_bpf.c
3139823Simp */
4139823Simp
5139823Simp/*-
654097Sarchie * Copyright (c) 1999 Whistle Communications, Inc.
754097Sarchie * All rights reserved.
854097Sarchie *
954097Sarchie * Subject to the following obligations and disclaimer of warranty, use and
1054097Sarchie * redistribution of this software, in source or object code forms, with or
1154097Sarchie * without modifications are expressly permitted by Whistle Communications;
1254097Sarchie * provided, however, that:
1354097Sarchie * 1. Any and all reproductions of the source or object code must include the
1454097Sarchie *    copyright notice above and the following disclaimer of warranties; and
1554097Sarchie * 2. No rights are granted, in any manner or form, to use Whistle
1654097Sarchie *    Communications, Inc. trademarks, including the mark "WHISTLE
1754097Sarchie *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1854097Sarchie *    such appears in the above copyright notice or in the software.
1954097Sarchie *
2054097Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2154097Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2254097Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2354097Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2454097Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2571821Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2654097Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2754097Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2854097Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2954097Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3054097Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3154097Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3254097Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3354097Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3454097Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3554097Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3654097Sarchie * OF SUCH DAMAGE.
3754097Sarchie *
3867506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
3954097Sarchie *
4054097Sarchie * $FreeBSD$
4154097Sarchie * $Whistle: ng_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $
4254097Sarchie */
4354097Sarchie
4454097Sarchie/*
4554097Sarchie * BPF NETGRAPH NODE TYPE
4654097Sarchie *
4754097Sarchie * This node type accepts any number of hook connections.  With each hook
4854097Sarchie * is associated a bpf(4) filter program, and two hook names (each possibly
4954097Sarchie * the empty string).  Incoming packets are compared against the filter;
5054097Sarchie * matching packets are delivered out the first named hook (or dropped if
5154097Sarchie * the empty string), and non-matching packets are delivered out the second
5254097Sarchie * named hook (or dropped if the empty string).
5354097Sarchie *
5454097Sarchie * Each hook also keeps statistics about how many packets have matched, etc.
5554097Sarchie */
5654097Sarchie
57153213Sjkim#include "opt_bpf.h"
58153213Sjkim
5954097Sarchie#include <sys/param.h>
6054097Sarchie#include <sys/systm.h>
6154097Sarchie#include <sys/errno.h>
6254097Sarchie#include <sys/kernel.h>
6354097Sarchie#include <sys/malloc.h>
6454097Sarchie#include <sys/mbuf.h>
6554097Sarchie
6654097Sarchie#include <net/bpf.h>
67153213Sjkim#ifdef BPF_JITTER
68153213Sjkim#include <net/bpf_jitter.h>
69153213Sjkim#endif
7054097Sarchie
7154097Sarchie#include <netgraph/ng_message.h>
7254097Sarchie#include <netgraph/netgraph.h>
7354097Sarchie#include <netgraph/ng_parse.h>
7454097Sarchie#include <netgraph/ng_bpf.h>
7554097Sarchie
7670870Sjulian#ifdef NG_SEPARATE_MALLOC
77227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_BPF, "netgraph_bpf", "netgraph bpf node");
7870870Sjulian#else
7970870Sjulian#define M_NETGRAPH_BPF M_NETGRAPH
8070870Sjulian#endif
8170870Sjulian
8254097Sarchie#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0))
8354097Sarchie
8454097Sarchie#define ERROUT(x)	do { error = (x); goto done; } while (0)
8554097Sarchie
8654097Sarchie/* Per hook private info */
8754097Sarchiestruct ng_bpf_hookinfo {
8854097Sarchie	hook_p			hook;
89175974Smav	hook_p			match;
90175974Smav	hook_p			nomatch;
9154097Sarchie	struct ng_bpf_hookprog	*prog;
92153213Sjkim#ifdef BPF_JITTER
93153213Sjkim	bpf_jit_filter		*jit_prog;
94153213Sjkim#endif
9554097Sarchie	struct ng_bpf_hookstat	stats;
9654097Sarchie};
9754097Sarchietypedef struct ng_bpf_hookinfo *hinfo_p;
9854097Sarchie
9954097Sarchie/* Netgraph methods */
10054097Sarchiestatic ng_constructor_t	ng_bpf_constructor;
10154097Sarchiestatic ng_rcvmsg_t	ng_bpf_rcvmsg;
10270700Sjulianstatic ng_shutdown_t	ng_bpf_shutdown;
10354097Sarchiestatic ng_newhook_t	ng_bpf_newhook;
10454097Sarchiestatic ng_rcvdata_t	ng_bpf_rcvdata;
10554097Sarchiestatic ng_disconnect_t	ng_bpf_disconnect;
10654097Sarchie
107182447Sjkim/* Maximum bpf program instructions */
108182447Sjkimextern int	bpf_maxinsns;
109182447Sjkim
11054097Sarchie/* Internal helper functions */
11154097Sarchiestatic int	ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp);
11254097Sarchie
11354097Sarchie/* Parse type for one struct bfp_insn */
11497685Sarchiestatic const struct ng_parse_struct_field ng_bpf_insn_type_fields[] = {
11564508Sarchie	{ "code",	&ng_parse_hint16_type	},
11664508Sarchie	{ "jt",		&ng_parse_uint8_type	},
11764508Sarchie	{ "jf",		&ng_parse_uint8_type	},
11864508Sarchie	{ "k",		&ng_parse_uint32_type	},
11954097Sarchie	{ NULL }
12054097Sarchie};
12154097Sarchiestatic const struct ng_parse_type ng_bpf_insn_type = {
12254097Sarchie	&ng_parse_struct_type,
12397685Sarchie	&ng_bpf_insn_type_fields
12454097Sarchie};
12554097Sarchie
12654097Sarchie/* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */
12754097Sarchiestatic int
12854097Sarchieng_bpf_hookprogary_getLength(const struct ng_parse_type *type,
12954097Sarchie	const u_char *start, const u_char *buf)
13054097Sarchie{
13154097Sarchie	const struct ng_bpf_hookprog *hp;
13254097Sarchie
13354097Sarchie	hp = (const struct ng_bpf_hookprog *)
13454097Sarchie	    (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog));
13554097Sarchie	return hp->bpf_prog_len;
13654097Sarchie}
13754097Sarchie
13854097Sarchiestatic const struct ng_parse_array_info ng_bpf_hookprogary_info = {
13954097Sarchie	&ng_bpf_insn_type,
14054097Sarchie	&ng_bpf_hookprogary_getLength,
14154097Sarchie	NULL
14254097Sarchie};
14354097Sarchiestatic const struct ng_parse_type ng_bpf_hookprogary_type = {
14454097Sarchie	&ng_parse_array_type,
14554097Sarchie	&ng_bpf_hookprogary_info
14654097Sarchie};
14754097Sarchie
14854097Sarchie/* Parse type for struct ng_bpf_hookprog */
14997685Sarchiestatic const struct ng_parse_struct_field ng_bpf_hookprog_type_fields[]
15054097Sarchie	= NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type);
15154097Sarchiestatic const struct ng_parse_type ng_bpf_hookprog_type = {
15254097Sarchie	&ng_parse_struct_type,
15397685Sarchie	&ng_bpf_hookprog_type_fields
15454097Sarchie};
15554097Sarchie
15654097Sarchie/* Parse type for struct ng_bpf_hookstat */
15797685Sarchiestatic const struct ng_parse_struct_field ng_bpf_hookstat_type_fields[]
15897685Sarchie	= NG_BPF_HOOKSTAT_TYPE_INFO;
15954097Sarchiestatic const struct ng_parse_type ng_bpf_hookstat_type = {
16054097Sarchie	&ng_parse_struct_type,
16197685Sarchie	&ng_bpf_hookstat_type_fields
16254097Sarchie};
16354097Sarchie
16454097Sarchie/* List of commands and how to convert arguments to/from ASCII */
16554097Sarchiestatic const struct ng_cmdlist ng_bpf_cmdlist[] = {
16654097Sarchie	{
16754097Sarchie	  NGM_BPF_COOKIE,
16854097Sarchie	  NGM_BPF_SET_PROGRAM,
16954097Sarchie	  "setprogram",
17054097Sarchie	  &ng_bpf_hookprog_type,
17154097Sarchie	  NULL
17254097Sarchie	},
17354097Sarchie	{
17454097Sarchie	  NGM_BPF_COOKIE,
17554097Sarchie	  NGM_BPF_GET_PROGRAM,
17654097Sarchie	  "getprogram",
17754097Sarchie	  &ng_parse_hookbuf_type,
17854097Sarchie	  &ng_bpf_hookprog_type
17954097Sarchie	},
18054097Sarchie	{
18154097Sarchie	  NGM_BPF_COOKIE,
18254097Sarchie	  NGM_BPF_GET_STATS,
18354097Sarchie	  "getstats",
18454097Sarchie	  &ng_parse_hookbuf_type,
18554097Sarchie	  &ng_bpf_hookstat_type
18654097Sarchie	},
18754097Sarchie	{
18854097Sarchie	  NGM_BPF_COOKIE,
18954097Sarchie	  NGM_BPF_CLR_STATS,
19054097Sarchie	  "clrstats",
19154097Sarchie	  &ng_parse_hookbuf_type,
19254097Sarchie	  NULL
19354097Sarchie	},
19454097Sarchie	{
19554097Sarchie	  NGM_BPF_COOKIE,
19654097Sarchie	  NGM_BPF_GETCLR_STATS,
19754097Sarchie	  "getclrstats",
19854097Sarchie	  &ng_parse_hookbuf_type,
19954097Sarchie	  &ng_bpf_hookstat_type
20054097Sarchie	},
20154097Sarchie	{ 0 }
20254097Sarchie};
20354097Sarchie
20454097Sarchie/* Netgraph type descriptor */
20554097Sarchiestatic struct ng_type typestruct = {
206129823Sjulian	.version =	NG_ABI_VERSION,
207129823Sjulian	.name =		NG_BPF_NODE_TYPE,
208129823Sjulian	.constructor =	ng_bpf_constructor,
209129823Sjulian	.rcvmsg =	ng_bpf_rcvmsg,
210129823Sjulian	.shutdown =	ng_bpf_shutdown,
211129823Sjulian	.newhook =	ng_bpf_newhook,
212129823Sjulian	.rcvdata =	ng_bpf_rcvdata,
213129823Sjulian	.disconnect =	ng_bpf_disconnect,
214129823Sjulian	.cmdlist =	ng_bpf_cmdlist,
21554097Sarchie};
21654097SarchieNETGRAPH_INIT(bpf, &typestruct);
21754097Sarchie
21854097Sarchie/* Default BPF program for a hook that matches nothing */
21954097Sarchiestatic const struct ng_bpf_hookprog ng_bpf_default_prog = {
22054097Sarchie	{ '\0' },		/* to be filled in at hook creation time */
22154097Sarchie	{ '\0' },
22254097Sarchie	{ '\0' },
22354097Sarchie	1,
22454097Sarchie	{ BPF_STMT(BPF_RET+BPF_K, 0) }
22554097Sarchie};
22654097Sarchie
22754097Sarchie/*
22854097Sarchie * Node constructor
22954097Sarchie *
23054097Sarchie * We don't keep any per-node private data
23170700Sjulian * We go via the hooks.
23254097Sarchie */
23354097Sarchiestatic int
23470700Sjulianng_bpf_constructor(node_p node)
23554097Sarchie{
23670784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
23754097Sarchie	return (0);
23854097Sarchie}
23954097Sarchie
24054097Sarchie/*
241175974Smav * Callback functions to be used by NG_NODE_FOREACH_HOOK() macro.
242175974Smav */
243175974Smavstatic int
244175974Smavng_bpf_addrefs(hook_p hook, void* arg)
245175974Smav{
246175974Smav	hinfo_p hip = NG_HOOK_PRIVATE(hook);
247175974Smav	hook_p h = (hook_p)arg;
248175974Smav
249175974Smav	if (strcmp(hip->prog->ifMatch, NG_HOOK_NAME(h)) == 0)
250175974Smav	    hip->match = h;
251175974Smav	if (strcmp(hip->prog->ifNotMatch, NG_HOOK_NAME(h)) == 0)
252175974Smav	    hip->nomatch = h;
253175974Smav	return (1);
254175974Smav}
255175974Smav
256175974Smavstatic int
257175974Smavng_bpf_remrefs(hook_p hook, void* arg)
258175974Smav{
259175974Smav	hinfo_p hip = NG_HOOK_PRIVATE(hook);
260175974Smav	hook_p h = (hook_p)arg;
261175974Smav
262175974Smav	if (hip->match == h)
263175974Smav	    hip->match = NULL;
264175974Smav	if (hip->nomatch == h)
265175974Smav	    hip->nomatch = NULL;
266175974Smav	return (1);
267175974Smav}
268175974Smav
269175974Smav/*
27054097Sarchie * Add a hook
27154097Sarchie */
27254097Sarchiestatic int
27354097Sarchieng_bpf_newhook(node_p node, hook_p hook, const char *name)
27454097Sarchie{
27554097Sarchie	hinfo_p hip;
276175974Smav	hook_p tmp;
27754097Sarchie	int error;
27854097Sarchie
27954097Sarchie	/* Create hook private structure */
280184205Sdes	hip = malloc(sizeof(*hip), M_NETGRAPH_BPF, M_NOWAIT | M_ZERO);
28154097Sarchie	if (hip == NULL)
28254097Sarchie		return (ENOMEM);
28354097Sarchie	hip->hook = hook;
28470784Sjulian	NG_HOOK_SET_PRIVATE(hook, hip);
28554097Sarchie
286175974Smav	/* Add our reference into other hooks data. */
287175974Smav	NG_NODE_FOREACH_HOOK(node, ng_bpf_addrefs, hook, tmp);
288175974Smav
28954097Sarchie	/* Attach the default BPF program */
29054097Sarchie	if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) {
291184205Sdes		free(hip, M_NETGRAPH_BPF);
29270784Sjulian		NG_HOOK_SET_PRIVATE(hook, NULL);
29354097Sarchie		return (error);
29454097Sarchie	}
29554097Sarchie
29654097Sarchie	/* Set hook name */
297175974Smav	strlcpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook));
29854097Sarchie	return (0);
29954097Sarchie}
30054097Sarchie
30154097Sarchie/*
30254097Sarchie * Receive a control message
30354097Sarchie */
30454097Sarchiestatic int
30570700Sjulianng_bpf_rcvmsg(node_p node, item_p item, hook_p lasthook)
30654097Sarchie{
30770700Sjulian	struct ng_mesg *msg;
30854097Sarchie	struct ng_mesg *resp = NULL;
30954097Sarchie	int error = 0;
31054097Sarchie
31170700Sjulian	NGI_GET_MSG(item, msg);
31254097Sarchie	switch (msg->header.typecookie) {
31354097Sarchie	case NGM_BPF_COOKIE:
31454097Sarchie		switch (msg->header.cmd) {
31554097Sarchie		case NGM_BPF_SET_PROGRAM:
31654097Sarchie		    {
31754097Sarchie			struct ng_bpf_hookprog *const
31854097Sarchie			    hp = (struct ng_bpf_hookprog *)msg->data;
31954097Sarchie			hook_p hook;
32054097Sarchie
32154097Sarchie			/* Sanity check */
32254097Sarchie			if (msg->header.arglen < sizeof(*hp)
32354214Sarchie			    || msg->header.arglen
32454214Sarchie			      != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len))
32554097Sarchie				ERROUT(EINVAL);
32654097Sarchie
32754097Sarchie			/* Find hook */
32854097Sarchie			if ((hook = ng_findhook(node, hp->thisHook)) == NULL)
32954097Sarchie				ERROUT(ENOENT);
33054097Sarchie
33154097Sarchie			/* Set new program */
33254097Sarchie			if ((error = ng_bpf_setprog(hook, hp)) != 0)
33354097Sarchie				ERROUT(error);
33454097Sarchie			break;
33554097Sarchie		    }
33654097Sarchie
33754097Sarchie		case NGM_BPF_GET_PROGRAM:
33854097Sarchie		    {
33954214Sarchie			struct ng_bpf_hookprog *hp;
34054097Sarchie			hook_p hook;
34154097Sarchie
34254097Sarchie			/* Sanity check */
34354097Sarchie			if (msg->header.arglen == 0)
34454097Sarchie				ERROUT(EINVAL);
34554097Sarchie			msg->data[msg->header.arglen - 1] = '\0';
34654097Sarchie
34754097Sarchie			/* Find hook */
34854097Sarchie			if ((hook = ng_findhook(node, msg->data)) == NULL)
34954097Sarchie				ERROUT(ENOENT);
35054097Sarchie
35154097Sarchie			/* Build response */
35270784Sjulian			hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->prog;
35354097Sarchie			NG_MKRESPONSE(resp, msg,
35454214Sarchie			    NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT);
35554097Sarchie			if (resp == NULL)
35654097Sarchie				ERROUT(ENOMEM);
35754214Sarchie			bcopy(hp, resp->data,
35854214Sarchie			   NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len));
35954097Sarchie			break;
36054097Sarchie		    }
36154097Sarchie
36254097Sarchie		case NGM_BPF_GET_STATS:
36354097Sarchie		case NGM_BPF_CLR_STATS:
36454097Sarchie		case NGM_BPF_GETCLR_STATS:
36554097Sarchie		    {
36654097Sarchie			struct ng_bpf_hookstat *stats;
36754097Sarchie			hook_p hook;
36854097Sarchie
36954097Sarchie			/* Sanity check */
37054097Sarchie			if (msg->header.arglen == 0)
37154097Sarchie				ERROUT(EINVAL);
37254097Sarchie			msg->data[msg->header.arglen - 1] = '\0';
37354097Sarchie
37454097Sarchie			/* Find hook */
37554097Sarchie			if ((hook = ng_findhook(node, msg->data)) == NULL)
37654097Sarchie				ERROUT(ENOENT);
37770784Sjulian			stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats;
37854097Sarchie
37954097Sarchie			/* Build response (if desired) */
38054097Sarchie			if (msg->header.cmd != NGM_BPF_CLR_STATS) {
38154097Sarchie				NG_MKRESPONSE(resp,
38254097Sarchie				    msg, sizeof(*stats), M_NOWAIT);
38354097Sarchie				if (resp == NULL)
38454097Sarchie					ERROUT(ENOMEM);
38554097Sarchie				bcopy(stats, resp->data, sizeof(*stats));
38654097Sarchie			}
38754097Sarchie
38854097Sarchie			/* Clear stats (if desired) */
38954097Sarchie			if (msg->header.cmd != NGM_BPF_GET_STATS)
39054097Sarchie				bzero(stats, sizeof(*stats));
39154097Sarchie			break;
39254097Sarchie		    }
39354097Sarchie
39454097Sarchie		default:
39554097Sarchie			error = EINVAL;
39654097Sarchie			break;
39754097Sarchie		}
39854097Sarchie		break;
39954097Sarchie	default:
40054097Sarchie		error = EINVAL;
40154097Sarchie		break;
40254097Sarchie	}
40370933Sjuliandone:
40470700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
40570700Sjulian	NG_FREE_MSG(msg);
40654097Sarchie	return (error);
40754097Sarchie}
40854097Sarchie
40954097Sarchie/*
41054097Sarchie * Receive data on a hook
41154097Sarchie *
41254097Sarchie * Apply the filter, and then drop or forward packet as appropriate.
41354097Sarchie */
41454097Sarchiestatic int
41570700Sjulianng_bpf_rcvdata(hook_p hook, item_p item)
41654097Sarchie{
41770784Sjulian	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
41870700Sjulian	int totlen;
419175776Smav	int needfree = 0, error = 0, usejit = 0;
420175776Smav	u_char *data = NULL;
42154097Sarchie	hinfo_p dhip;
42254097Sarchie	hook_p dest;
42354097Sarchie	u_int len;
42470700Sjulian	struct mbuf *m;
42554097Sarchie
42670700Sjulian	m = NGI_M(item);	/* 'item' still owns it.. we are peeking */
42770700Sjulian	totlen = m->m_pkthdr.len;
42870700Sjulian	/* Update stats on incoming hook. XXX Can we do 64 bits atomically? */
42970700Sjulian	/* atomic_add_int64(&hip->stats.recvFrames, 1); */
43070700Sjulian	/* atomic_add_int64(&hip->stats.recvOctets, totlen); */
43170700Sjulian	hip->stats.recvFrames++;
43254097Sarchie	hip->stats.recvOctets += totlen;
43354097Sarchie
434175974Smav	/* Don't call bpf_filter() with totlen == 0! */
435175974Smav	if (totlen == 0) {
436175974Smav		len = 0;
437175974Smav		goto ready;
438175974Smav	}
439175974Smav
440175776Smav#ifdef BPF_JITTER
441175776Smav	if (bpf_jitter_enable != 0 && hip->jit_prog != NULL)
442175776Smav		usejit = 1;
443175776Smav#endif
444175776Smav
44554097Sarchie	/* Need to put packet in contiguous memory for bpf */
446175776Smav	if (m->m_next != NULL && totlen > MHLEN) {
447175776Smav		if (usejit) {
448184205Sdes			data = malloc(totlen, M_NETGRAPH_BPF, M_NOWAIT);
44954097Sarchie			if (data == NULL) {
45070700Sjulian				NG_FREE_ITEM(item);
45154097Sarchie				return (ENOMEM);
45254097Sarchie			}
45354097Sarchie			needfree = 1;
454171600Smav			m_copydata(m, 0, totlen, (caddr_t)data);
455175776Smav		}
456175776Smav	} else {
457175776Smav		if (m->m_next != NULL) {
458171600Smav			NGI_M(item) = m = m_pullup(m, totlen);
459171600Smav			if (m == NULL) {
460171600Smav				NG_FREE_ITEM(item);
461171600Smav				return (ENOBUFS);
462171600Smav			}
463171600Smav		}
46454097Sarchie		data = mtod(m, u_char *);
465175776Smav	}
46654097Sarchie
46754097Sarchie	/* Run packet through filter */
468153213Sjkim#ifdef BPF_JITTER
469175974Smav	if (usejit)
470175974Smav		len = (*(hip->jit_prog->func))(data, totlen, totlen);
471175974Smav	else
472153213Sjkim#endif
473175974Smav	if (data)
474175974Smav		len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen);
475175974Smav	else
476175974Smav		len = bpf_filter(hip->prog->bpf_prog, (u_char *)m, totlen, 0);
47754097Sarchie	if (needfree)
478184205Sdes		free(data, M_NETGRAPH_BPF);
479175974Smavready:
48054097Sarchie	/* See if we got a match and find destination hook */
48154097Sarchie	if (len > 0) {
48254097Sarchie
48354097Sarchie		/* Update stats */
48470700Sjulian		/* XXX atomically? */
48554097Sarchie		hip->stats.recvMatchFrames++;
48654097Sarchie		hip->stats.recvMatchOctets += totlen;
48754097Sarchie
48854097Sarchie		/* Truncate packet length if required by the filter */
48970700Sjulian		/* Assume this never changes m */
49054097Sarchie		if (len < totlen) {
49154097Sarchie			m_adj(m, -(totlen - len));
492175974Smav			totlen = len;
49354097Sarchie		}
494175974Smav		dest = hip->match;
49554097Sarchie	} else
496175974Smav		dest = hip->nomatch;
49754097Sarchie	if (dest == NULL) {
49870700Sjulian		NG_FREE_ITEM(item);
49954097Sarchie		return (0);
50054097Sarchie	}
50154097Sarchie
50254097Sarchie	/* Deliver frame out destination hook */
50370784Sjulian	dhip = NG_HOOK_PRIVATE(dest);
50454097Sarchie	dhip->stats.xmitOctets += totlen;
50554097Sarchie	dhip->stats.xmitFrames++;
50670784Sjulian	NG_FWD_ITEM_HOOK(error, item, dest);
50754097Sarchie	return (error);
50854097Sarchie}
50954097Sarchie
51054097Sarchie/*
51154097Sarchie * Shutdown processing
51254097Sarchie */
51354097Sarchiestatic int
51470700Sjulianng_bpf_shutdown(node_p node)
51554097Sarchie{
51670784Sjulian	NG_NODE_UNREF(node);
51754097Sarchie	return (0);
51854097Sarchie}
51954097Sarchie
52054097Sarchie/*
52154097Sarchie * Hook disconnection
52254097Sarchie */
52354097Sarchiestatic int
52454097Sarchieng_bpf_disconnect(hook_p hook)
52554097Sarchie{
526175974Smav	const node_p node = NG_HOOK_NODE(hook);
52770784Sjulian	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
528175974Smav	hook_p tmp;
52954097Sarchie
53087599Sobrien	KASSERT(hip != NULL, ("%s: null info", __func__));
531175974Smav
532175974Smav	/* Remove our reference from other hooks data. */
533175974Smav	NG_NODE_FOREACH_HOOK(node, ng_bpf_remrefs, hook, tmp);
534175974Smav
535184205Sdes	free(hip->prog, M_NETGRAPH_BPF);
536153213Sjkim#ifdef BPF_JITTER
537153213Sjkim	if (hip->jit_prog != NULL)
538153213Sjkim		bpf_destroy_jit_filter(hip->jit_prog);
539153213Sjkim#endif
540184205Sdes	free(hip, M_NETGRAPH_BPF);
541175974Smav	if ((NG_NODE_NUMHOOKS(node) == 0) &&
542175974Smav	    (NG_NODE_IS_VALID(node))) {
543175974Smav		ng_rmnode_self(node);
54470700Sjulian	}
54554097Sarchie	return (0);
54654097Sarchie}
54754097Sarchie
54854097Sarchie/************************************************************************
54954097Sarchie			HELPER STUFF
55054097Sarchie ************************************************************************/
55154097Sarchie
55254097Sarchie/*
55354097Sarchie * Set the BPF program associated with a hook
55454097Sarchie */
55554097Sarchiestatic int
55654097Sarchieng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0)
55754097Sarchie{
55870784Sjulian	const hinfo_p hip = NG_HOOK_PRIVATE(hook);
55954097Sarchie	struct ng_bpf_hookprog *hp;
560153214Sjkim#ifdef BPF_JITTER
561153213Sjkim	bpf_jit_filter *jit_prog;
562153214Sjkim#endif
56354097Sarchie	int size;
56454097Sarchie
56554097Sarchie	/* Check program for validity */
566182447Sjkim	if (hp0->bpf_prog_len > bpf_maxinsns ||
567182447Sjkim	    !bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len))
56854097Sarchie		return (EINVAL);
56954097Sarchie
57054097Sarchie	/* Make a copy of the program */
57154214Sarchie	size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len);
572184205Sdes	hp = malloc(size, M_NETGRAPH_BPF, M_NOWAIT);
57354097Sarchie	if (hp == NULL)
57454097Sarchie		return (ENOMEM);
57554097Sarchie	bcopy(hp0, hp, size);
576153213Sjkim#ifdef BPF_JITTER
577153213Sjkim	jit_prog = bpf_jitter(hp->bpf_prog, hp->bpf_prog_len);
578153213Sjkim#endif
57954097Sarchie
58054097Sarchie	/* Free previous program, if any, and assign new one */
58154097Sarchie	if (hip->prog != NULL)
582184205Sdes		free(hip->prog, M_NETGRAPH_BPF);
58354097Sarchie	hip->prog = hp;
584153213Sjkim#ifdef BPF_JITTER
585153213Sjkim	if (hip->jit_prog != NULL)
586153213Sjkim		bpf_destroy_jit_filter(hip->jit_prog);
587153214Sjkim	hip->jit_prog = jit_prog;
588153213Sjkim#endif
589175974Smav
590175974Smav	/* Prepare direct references on target hooks. */
591175974Smav	hip->match = ng_findhook(NG_HOOK_NODE(hook), hip->prog->ifMatch);
592175974Smav	hip->nomatch = ng_findhook(NG_HOOK_NODE(hook), hip->prog->ifNotMatch);
59354097Sarchie	return (0);
59454097Sarchie}
595