192a3e552SArchie Cobbs /* 292a3e552SArchie Cobbs * ng_bpf.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 692a3e552SArchie Cobbs * Copyright (c) 1999 Whistle Communications, Inc. 792a3e552SArchie Cobbs * All rights reserved. 892a3e552SArchie Cobbs * 992a3e552SArchie Cobbs * Subject to the following obligations and disclaimer of warranty, use and 1092a3e552SArchie Cobbs * redistribution of this software, in source or object code forms, with or 1192a3e552SArchie Cobbs * without modifications are expressly permitted by Whistle Communications; 1292a3e552SArchie Cobbs * provided, however, that: 1392a3e552SArchie Cobbs * 1. Any and all reproductions of the source or object code must include the 1492a3e552SArchie Cobbs * copyright notice above and the following disclaimer of warranties; and 1592a3e552SArchie Cobbs * 2. No rights are granted, in any manner or form, to use Whistle 1692a3e552SArchie Cobbs * Communications, Inc. trademarks, including the mark "WHISTLE 1792a3e552SArchie Cobbs * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1892a3e552SArchie Cobbs * such appears in the above copyright notice or in the software. 1992a3e552SArchie Cobbs * 2092a3e552SArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2192a3e552SArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2292a3e552SArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2392a3e552SArchie Cobbs * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2492a3e552SArchie Cobbs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25d2a57575SJulian Elischer * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2692a3e552SArchie Cobbs * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2792a3e552SArchie Cobbs * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2892a3e552SArchie Cobbs * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2992a3e552SArchie Cobbs * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 3092a3e552SArchie Cobbs * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3192a3e552SArchie Cobbs * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3292a3e552SArchie Cobbs * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3392a3e552SArchie Cobbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3492a3e552SArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3592a3e552SArchie Cobbs * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3692a3e552SArchie Cobbs * OF SUCH DAMAGE. 3792a3e552SArchie Cobbs * 38cc3bbd68SJulian Elischer * Author: Archie Cobbs <archie@freebsd.org> 3992a3e552SArchie Cobbs * 4092a3e552SArchie Cobbs * $FreeBSD$ 4192a3e552SArchie Cobbs * $Whistle: ng_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $ 4292a3e552SArchie Cobbs */ 4392a3e552SArchie Cobbs 4492a3e552SArchie Cobbs /* 4592a3e552SArchie Cobbs * BPF NETGRAPH NODE TYPE 4692a3e552SArchie Cobbs * 4792a3e552SArchie Cobbs * This node type accepts any number of hook connections. With each hook 4892a3e552SArchie Cobbs * is associated a bpf(4) filter program, and two hook names (each possibly 4992a3e552SArchie Cobbs * the empty string). Incoming packets are compared against the filter; 5092a3e552SArchie Cobbs * matching packets are delivered out the first named hook (or dropped if 5192a3e552SArchie Cobbs * the empty string), and non-matching packets are delivered out the second 5292a3e552SArchie Cobbs * named hook (or dropped if the empty string). 5392a3e552SArchie Cobbs * 5492a3e552SArchie Cobbs * Each hook also keeps statistics about how many packets have matched, etc. 5592a3e552SArchie Cobbs */ 5692a3e552SArchie Cobbs 57848c454cSJung-uk Kim #include "opt_bpf.h" 58848c454cSJung-uk Kim 5992a3e552SArchie Cobbs #include <sys/param.h> 6092a3e552SArchie Cobbs #include <sys/systm.h> 6192a3e552SArchie Cobbs #include <sys/errno.h> 6292a3e552SArchie Cobbs #include <sys/kernel.h> 6392a3e552SArchie Cobbs #include <sys/malloc.h> 6492a3e552SArchie Cobbs #include <sys/mbuf.h> 6592a3e552SArchie Cobbs 6692a3e552SArchie Cobbs #include <net/bpf.h> 67848c454cSJung-uk Kim #ifdef BPF_JITTER 68848c454cSJung-uk Kim #include <net/bpf_jitter.h> 69848c454cSJung-uk Kim #endif 7092a3e552SArchie Cobbs 7192a3e552SArchie Cobbs #include <netgraph/ng_message.h> 7292a3e552SArchie Cobbs #include <netgraph/netgraph.h> 7392a3e552SArchie Cobbs #include <netgraph/ng_parse.h> 7492a3e552SArchie Cobbs #include <netgraph/ng_bpf.h> 7592a3e552SArchie Cobbs 769c8c302fSJulian Elischer #ifdef NG_SEPARATE_MALLOC 779c8c302fSJulian Elischer MALLOC_DEFINE(M_NETGRAPH_BPF, "netgraph_bpf", "netgraph bpf node "); 789c8c302fSJulian Elischer #else 799c8c302fSJulian Elischer #define M_NETGRAPH_BPF M_NETGRAPH 809c8c302fSJulian Elischer #endif 819c8c302fSJulian Elischer 8292a3e552SArchie Cobbs #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 8392a3e552SArchie Cobbs 8492a3e552SArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 8592a3e552SArchie Cobbs 8692a3e552SArchie Cobbs /* Per hook private info */ 8792a3e552SArchie Cobbs struct ng_bpf_hookinfo { 8892a3e552SArchie Cobbs hook_p hook; 89193f57e2SAlexander Motin hook_p match; 90193f57e2SAlexander Motin hook_p nomatch; 9192a3e552SArchie Cobbs struct ng_bpf_hookprog *prog; 92848c454cSJung-uk Kim #ifdef BPF_JITTER 93848c454cSJung-uk Kim bpf_jit_filter *jit_prog; 94848c454cSJung-uk Kim #endif 9592a3e552SArchie Cobbs struct ng_bpf_hookstat stats; 9692a3e552SArchie Cobbs }; 9792a3e552SArchie Cobbs typedef struct ng_bpf_hookinfo *hinfo_p; 9892a3e552SArchie Cobbs 9992a3e552SArchie Cobbs /* Netgraph methods */ 10092a3e552SArchie Cobbs static ng_constructor_t ng_bpf_constructor; 10192a3e552SArchie Cobbs static ng_rcvmsg_t ng_bpf_rcvmsg; 102069154d5SJulian Elischer static ng_shutdown_t ng_bpf_shutdown; 10392a3e552SArchie Cobbs static ng_newhook_t ng_bpf_newhook; 10492a3e552SArchie Cobbs static ng_rcvdata_t ng_bpf_rcvdata; 10592a3e552SArchie Cobbs static ng_disconnect_t ng_bpf_disconnect; 10692a3e552SArchie Cobbs 10737b5fe59SJung-uk Kim /* Maximum bpf program instructions */ 10837b5fe59SJung-uk Kim extern int bpf_maxinsns; 10937b5fe59SJung-uk Kim 11092a3e552SArchie Cobbs /* Internal helper functions */ 11192a3e552SArchie Cobbs static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp); 11292a3e552SArchie Cobbs 11392a3e552SArchie Cobbs /* Parse type for one struct bfp_insn */ 114f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_bpf_insn_type_fields[] = { 11557b57be3SArchie Cobbs { "code", &ng_parse_hint16_type }, 11657b57be3SArchie Cobbs { "jt", &ng_parse_uint8_type }, 11757b57be3SArchie Cobbs { "jf", &ng_parse_uint8_type }, 11857b57be3SArchie Cobbs { "k", &ng_parse_uint32_type }, 11992a3e552SArchie Cobbs { NULL } 12092a3e552SArchie Cobbs }; 12192a3e552SArchie Cobbs static const struct ng_parse_type ng_bpf_insn_type = { 12292a3e552SArchie Cobbs &ng_parse_struct_type, 123f0184ff8SArchie Cobbs &ng_bpf_insn_type_fields 12492a3e552SArchie Cobbs }; 12592a3e552SArchie Cobbs 12692a3e552SArchie Cobbs /* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */ 12792a3e552SArchie Cobbs static int 12892a3e552SArchie Cobbs ng_bpf_hookprogary_getLength(const struct ng_parse_type *type, 12992a3e552SArchie Cobbs const u_char *start, const u_char *buf) 13092a3e552SArchie Cobbs { 13192a3e552SArchie Cobbs const struct ng_bpf_hookprog *hp; 13292a3e552SArchie Cobbs 13392a3e552SArchie Cobbs hp = (const struct ng_bpf_hookprog *) 13492a3e552SArchie Cobbs (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog)); 13592a3e552SArchie Cobbs return hp->bpf_prog_len; 13692a3e552SArchie Cobbs } 13792a3e552SArchie Cobbs 13892a3e552SArchie Cobbs static const struct ng_parse_array_info ng_bpf_hookprogary_info = { 13992a3e552SArchie Cobbs &ng_bpf_insn_type, 14092a3e552SArchie Cobbs &ng_bpf_hookprogary_getLength, 14192a3e552SArchie Cobbs NULL 14292a3e552SArchie Cobbs }; 14392a3e552SArchie Cobbs static const struct ng_parse_type ng_bpf_hookprogary_type = { 14492a3e552SArchie Cobbs &ng_parse_array_type, 14592a3e552SArchie Cobbs &ng_bpf_hookprogary_info 14692a3e552SArchie Cobbs }; 14792a3e552SArchie Cobbs 14892a3e552SArchie Cobbs /* Parse type for struct ng_bpf_hookprog */ 149f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_bpf_hookprog_type_fields[] 15092a3e552SArchie Cobbs = NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type); 15192a3e552SArchie Cobbs static const struct ng_parse_type ng_bpf_hookprog_type = { 15292a3e552SArchie Cobbs &ng_parse_struct_type, 153f0184ff8SArchie Cobbs &ng_bpf_hookprog_type_fields 15492a3e552SArchie Cobbs }; 15592a3e552SArchie Cobbs 15692a3e552SArchie Cobbs /* Parse type for struct ng_bpf_hookstat */ 157f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_bpf_hookstat_type_fields[] 158f0184ff8SArchie Cobbs = NG_BPF_HOOKSTAT_TYPE_INFO; 15992a3e552SArchie Cobbs static const struct ng_parse_type ng_bpf_hookstat_type = { 16092a3e552SArchie Cobbs &ng_parse_struct_type, 161f0184ff8SArchie Cobbs &ng_bpf_hookstat_type_fields 16292a3e552SArchie Cobbs }; 16392a3e552SArchie Cobbs 16492a3e552SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 16592a3e552SArchie Cobbs static const struct ng_cmdlist ng_bpf_cmdlist[] = { 16692a3e552SArchie Cobbs { 16792a3e552SArchie Cobbs NGM_BPF_COOKIE, 16892a3e552SArchie Cobbs NGM_BPF_SET_PROGRAM, 16992a3e552SArchie Cobbs "setprogram", 17092a3e552SArchie Cobbs &ng_bpf_hookprog_type, 17192a3e552SArchie Cobbs NULL 17292a3e552SArchie Cobbs }, 17392a3e552SArchie Cobbs { 17492a3e552SArchie Cobbs NGM_BPF_COOKIE, 17592a3e552SArchie Cobbs NGM_BPF_GET_PROGRAM, 17692a3e552SArchie Cobbs "getprogram", 17792a3e552SArchie Cobbs &ng_parse_hookbuf_type, 17892a3e552SArchie Cobbs &ng_bpf_hookprog_type 17992a3e552SArchie Cobbs }, 18092a3e552SArchie Cobbs { 18192a3e552SArchie Cobbs NGM_BPF_COOKIE, 18292a3e552SArchie Cobbs NGM_BPF_GET_STATS, 18392a3e552SArchie Cobbs "getstats", 18492a3e552SArchie Cobbs &ng_parse_hookbuf_type, 18592a3e552SArchie Cobbs &ng_bpf_hookstat_type 18692a3e552SArchie Cobbs }, 18792a3e552SArchie Cobbs { 18892a3e552SArchie Cobbs NGM_BPF_COOKIE, 18992a3e552SArchie Cobbs NGM_BPF_CLR_STATS, 19092a3e552SArchie Cobbs "clrstats", 19192a3e552SArchie Cobbs &ng_parse_hookbuf_type, 19292a3e552SArchie Cobbs NULL 19392a3e552SArchie Cobbs }, 19492a3e552SArchie Cobbs { 19592a3e552SArchie Cobbs NGM_BPF_COOKIE, 19692a3e552SArchie Cobbs NGM_BPF_GETCLR_STATS, 19792a3e552SArchie Cobbs "getclrstats", 19892a3e552SArchie Cobbs &ng_parse_hookbuf_type, 19992a3e552SArchie Cobbs &ng_bpf_hookstat_type 20092a3e552SArchie Cobbs }, 20192a3e552SArchie Cobbs { 0 } 20292a3e552SArchie Cobbs }; 20392a3e552SArchie Cobbs 20492a3e552SArchie Cobbs /* Netgraph type descriptor */ 20592a3e552SArchie Cobbs static struct ng_type typestruct = { 206f8aae777SJulian Elischer .version = NG_ABI_VERSION, 207f8aae777SJulian Elischer .name = NG_BPF_NODE_TYPE, 208f8aae777SJulian Elischer .constructor = ng_bpf_constructor, 209f8aae777SJulian Elischer .rcvmsg = ng_bpf_rcvmsg, 210f8aae777SJulian Elischer .shutdown = ng_bpf_shutdown, 211f8aae777SJulian Elischer .newhook = ng_bpf_newhook, 212f8aae777SJulian Elischer .rcvdata = ng_bpf_rcvdata, 213f8aae777SJulian Elischer .disconnect = ng_bpf_disconnect, 214f8aae777SJulian Elischer .cmdlist = ng_bpf_cmdlist, 21592a3e552SArchie Cobbs }; 21692a3e552SArchie Cobbs NETGRAPH_INIT(bpf, &typestruct); 21792a3e552SArchie Cobbs 21892a3e552SArchie Cobbs /* Default BPF program for a hook that matches nothing */ 21992a3e552SArchie Cobbs static const struct ng_bpf_hookprog ng_bpf_default_prog = { 22092a3e552SArchie Cobbs { '\0' }, /* to be filled in at hook creation time */ 22192a3e552SArchie Cobbs { '\0' }, 22292a3e552SArchie Cobbs { '\0' }, 22392a3e552SArchie Cobbs 1, 22492a3e552SArchie Cobbs { BPF_STMT(BPF_RET+BPF_K, 0) } 22592a3e552SArchie Cobbs }; 22692a3e552SArchie Cobbs 22792a3e552SArchie Cobbs /* 22892a3e552SArchie Cobbs * Node constructor 22992a3e552SArchie Cobbs * 23092a3e552SArchie Cobbs * We don't keep any per-node private data 231069154d5SJulian Elischer * We go via the hooks. 23292a3e552SArchie Cobbs */ 23392a3e552SArchie Cobbs static int 234069154d5SJulian Elischer ng_bpf_constructor(node_p node) 23592a3e552SArchie Cobbs { 23630400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 23792a3e552SArchie Cobbs return (0); 23892a3e552SArchie Cobbs } 23992a3e552SArchie Cobbs 24092a3e552SArchie Cobbs /* 241193f57e2SAlexander Motin * Callback functions to be used by NG_NODE_FOREACH_HOOK() macro. 242193f57e2SAlexander Motin */ 243193f57e2SAlexander Motin static int 244193f57e2SAlexander Motin ng_bpf_addrefs(hook_p hook, void* arg) 245193f57e2SAlexander Motin { 246193f57e2SAlexander Motin hinfo_p hip = NG_HOOK_PRIVATE(hook); 247193f57e2SAlexander Motin hook_p h = (hook_p)arg; 248193f57e2SAlexander Motin 249193f57e2SAlexander Motin if (strcmp(hip->prog->ifMatch, NG_HOOK_NAME(h)) == 0) 250193f57e2SAlexander Motin hip->match = h; 251193f57e2SAlexander Motin if (strcmp(hip->prog->ifNotMatch, NG_HOOK_NAME(h)) == 0) 252193f57e2SAlexander Motin hip->nomatch = h; 253193f57e2SAlexander Motin return (1); 254193f57e2SAlexander Motin } 255193f57e2SAlexander Motin 256193f57e2SAlexander Motin static int 257193f57e2SAlexander Motin ng_bpf_remrefs(hook_p hook, void* arg) 258193f57e2SAlexander Motin { 259193f57e2SAlexander Motin hinfo_p hip = NG_HOOK_PRIVATE(hook); 260193f57e2SAlexander Motin hook_p h = (hook_p)arg; 261193f57e2SAlexander Motin 262193f57e2SAlexander Motin if (hip->match == h) 263193f57e2SAlexander Motin hip->match = NULL; 264193f57e2SAlexander Motin if (hip->nomatch == h) 265193f57e2SAlexander Motin hip->nomatch = NULL; 266193f57e2SAlexander Motin return (1); 267193f57e2SAlexander Motin } 268193f57e2SAlexander Motin 269193f57e2SAlexander Motin /* 27092a3e552SArchie Cobbs * Add a hook 27192a3e552SArchie Cobbs */ 27292a3e552SArchie Cobbs static int 27392a3e552SArchie Cobbs ng_bpf_newhook(node_p node, hook_p hook, const char *name) 27492a3e552SArchie Cobbs { 27592a3e552SArchie Cobbs hinfo_p hip; 276193f57e2SAlexander Motin hook_p tmp; 27792a3e552SArchie Cobbs int error; 27892a3e552SArchie Cobbs 27992a3e552SArchie Cobbs /* Create hook private structure */ 2809c8c302fSJulian Elischer MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH_BPF, M_NOWAIT | M_ZERO); 28192a3e552SArchie Cobbs if (hip == NULL) 28292a3e552SArchie Cobbs return (ENOMEM); 28392a3e552SArchie Cobbs hip->hook = hook; 28430400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, hip); 285193f57e2SAlexander Motin 286193f57e2SAlexander Motin /* Add our reference into other hooks data. */ 287193f57e2SAlexander Motin NG_NODE_FOREACH_HOOK(node, ng_bpf_addrefs, hook, tmp); 28892a3e552SArchie Cobbs 28992a3e552SArchie Cobbs /* Attach the default BPF program */ 29092a3e552SArchie Cobbs if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) { 2919c8c302fSJulian Elischer FREE(hip, M_NETGRAPH_BPF); 29230400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, NULL); 29392a3e552SArchie Cobbs return (error); 29492a3e552SArchie Cobbs } 29592a3e552SArchie Cobbs 29692a3e552SArchie Cobbs /* Set hook name */ 297193f57e2SAlexander Motin strlcpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook)); 29892a3e552SArchie Cobbs return (0); 29992a3e552SArchie Cobbs } 30092a3e552SArchie Cobbs 30192a3e552SArchie Cobbs /* 30292a3e552SArchie Cobbs * Receive a control message 30392a3e552SArchie Cobbs */ 30492a3e552SArchie Cobbs static int 305069154d5SJulian Elischer ng_bpf_rcvmsg(node_p node, item_p item, hook_p lasthook) 30692a3e552SArchie Cobbs { 307069154d5SJulian Elischer struct ng_mesg *msg; 30892a3e552SArchie Cobbs struct ng_mesg *resp = NULL; 30992a3e552SArchie Cobbs int error = 0; 31092a3e552SArchie Cobbs 311069154d5SJulian Elischer NGI_GET_MSG(item, msg); 31292a3e552SArchie Cobbs switch (msg->header.typecookie) { 31392a3e552SArchie Cobbs case NGM_BPF_COOKIE: 31492a3e552SArchie Cobbs switch (msg->header.cmd) { 31592a3e552SArchie Cobbs case NGM_BPF_SET_PROGRAM: 31692a3e552SArchie Cobbs { 31792a3e552SArchie Cobbs struct ng_bpf_hookprog *const 31892a3e552SArchie Cobbs hp = (struct ng_bpf_hookprog *)msg->data; 31992a3e552SArchie Cobbs hook_p hook; 32092a3e552SArchie Cobbs 32192a3e552SArchie Cobbs /* Sanity check */ 32292a3e552SArchie Cobbs if (msg->header.arglen < sizeof(*hp) 323ab0d3c94SArchie Cobbs || msg->header.arglen 324ab0d3c94SArchie Cobbs != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) 32592a3e552SArchie Cobbs ERROUT(EINVAL); 32692a3e552SArchie Cobbs 32792a3e552SArchie Cobbs /* Find hook */ 32892a3e552SArchie Cobbs if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 32992a3e552SArchie Cobbs ERROUT(ENOENT); 33092a3e552SArchie Cobbs 33192a3e552SArchie Cobbs /* Set new program */ 33292a3e552SArchie Cobbs if ((error = ng_bpf_setprog(hook, hp)) != 0) 33392a3e552SArchie Cobbs ERROUT(error); 33492a3e552SArchie Cobbs break; 33592a3e552SArchie Cobbs } 33692a3e552SArchie Cobbs 33792a3e552SArchie Cobbs case NGM_BPF_GET_PROGRAM: 33892a3e552SArchie Cobbs { 339ab0d3c94SArchie Cobbs struct ng_bpf_hookprog *hp; 34092a3e552SArchie Cobbs hook_p hook; 34192a3e552SArchie Cobbs 34292a3e552SArchie Cobbs /* Sanity check */ 34392a3e552SArchie Cobbs if (msg->header.arglen == 0) 34492a3e552SArchie Cobbs ERROUT(EINVAL); 34592a3e552SArchie Cobbs msg->data[msg->header.arglen - 1] = '\0'; 34692a3e552SArchie Cobbs 34792a3e552SArchie Cobbs /* Find hook */ 34892a3e552SArchie Cobbs if ((hook = ng_findhook(node, msg->data)) == NULL) 34992a3e552SArchie Cobbs ERROUT(ENOENT); 35092a3e552SArchie Cobbs 35192a3e552SArchie Cobbs /* Build response */ 35230400f03SJulian Elischer hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->prog; 35392a3e552SArchie Cobbs NG_MKRESPONSE(resp, msg, 354ab0d3c94SArchie Cobbs NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT); 35592a3e552SArchie Cobbs if (resp == NULL) 35692a3e552SArchie Cobbs ERROUT(ENOMEM); 357ab0d3c94SArchie Cobbs bcopy(hp, resp->data, 358ab0d3c94SArchie Cobbs NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)); 35992a3e552SArchie Cobbs break; 36092a3e552SArchie Cobbs } 36192a3e552SArchie Cobbs 36292a3e552SArchie Cobbs case NGM_BPF_GET_STATS: 36392a3e552SArchie Cobbs case NGM_BPF_CLR_STATS: 36492a3e552SArchie Cobbs case NGM_BPF_GETCLR_STATS: 36592a3e552SArchie Cobbs { 36692a3e552SArchie Cobbs struct ng_bpf_hookstat *stats; 36792a3e552SArchie Cobbs hook_p hook; 36892a3e552SArchie Cobbs 36992a3e552SArchie Cobbs /* Sanity check */ 37092a3e552SArchie Cobbs if (msg->header.arglen == 0) 37192a3e552SArchie Cobbs ERROUT(EINVAL); 37292a3e552SArchie Cobbs msg->data[msg->header.arglen - 1] = '\0'; 37392a3e552SArchie Cobbs 37492a3e552SArchie Cobbs /* Find hook */ 37592a3e552SArchie Cobbs if ((hook = ng_findhook(node, msg->data)) == NULL) 37692a3e552SArchie Cobbs ERROUT(ENOENT); 37730400f03SJulian Elischer stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats; 37892a3e552SArchie Cobbs 37992a3e552SArchie Cobbs /* Build response (if desired) */ 38092a3e552SArchie Cobbs if (msg->header.cmd != NGM_BPF_CLR_STATS) { 38192a3e552SArchie Cobbs NG_MKRESPONSE(resp, 38292a3e552SArchie Cobbs msg, sizeof(*stats), M_NOWAIT); 38392a3e552SArchie Cobbs if (resp == NULL) 38492a3e552SArchie Cobbs ERROUT(ENOMEM); 38592a3e552SArchie Cobbs bcopy(stats, resp->data, sizeof(*stats)); 38692a3e552SArchie Cobbs } 38792a3e552SArchie Cobbs 38892a3e552SArchie Cobbs /* Clear stats (if desired) */ 38992a3e552SArchie Cobbs if (msg->header.cmd != NGM_BPF_GET_STATS) 39092a3e552SArchie Cobbs bzero(stats, sizeof(*stats)); 39192a3e552SArchie Cobbs break; 39292a3e552SArchie Cobbs } 39392a3e552SArchie Cobbs 39492a3e552SArchie Cobbs default: 39592a3e552SArchie Cobbs error = EINVAL; 39692a3e552SArchie Cobbs break; 39792a3e552SArchie Cobbs } 39892a3e552SArchie Cobbs break; 39992a3e552SArchie Cobbs default: 40092a3e552SArchie Cobbs error = EINVAL; 40192a3e552SArchie Cobbs break; 40292a3e552SArchie Cobbs } 40392a3e552SArchie Cobbs done: 404cdbfe124SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 405069154d5SJulian Elischer NG_FREE_MSG(msg); 40692a3e552SArchie Cobbs return (error); 40792a3e552SArchie Cobbs } 40892a3e552SArchie Cobbs 40992a3e552SArchie Cobbs /* 41092a3e552SArchie Cobbs * Receive data on a hook 41192a3e552SArchie Cobbs * 41292a3e552SArchie Cobbs * Apply the filter, and then drop or forward packet as appropriate. 41392a3e552SArchie Cobbs */ 41492a3e552SArchie Cobbs static int 415069154d5SJulian Elischer ng_bpf_rcvdata(hook_p hook, item_p item) 41692a3e552SArchie Cobbs { 41730400f03SJulian Elischer const hinfo_p hip = NG_HOOK_PRIVATE(hook); 418069154d5SJulian Elischer int totlen; 419f38b3703SAlexander Motin int needfree = 0, error = 0, usejit = 0; 420f38b3703SAlexander Motin u_char *data = NULL; 42192a3e552SArchie Cobbs hinfo_p dhip; 42292a3e552SArchie Cobbs hook_p dest; 42392a3e552SArchie Cobbs u_int len; 424069154d5SJulian Elischer struct mbuf *m; 42592a3e552SArchie Cobbs 426069154d5SJulian Elischer m = NGI_M(item); /* 'item' still owns it.. we are peeking */ 427069154d5SJulian Elischer totlen = m->m_pkthdr.len; 428069154d5SJulian Elischer /* Update stats on incoming hook. XXX Can we do 64 bits atomically? */ 429069154d5SJulian Elischer /* atomic_add_int64(&hip->stats.recvFrames, 1); */ 430069154d5SJulian Elischer /* atomic_add_int64(&hip->stats.recvOctets, totlen); */ 43192a3e552SArchie Cobbs hip->stats.recvFrames++; 43292a3e552SArchie Cobbs hip->stats.recvOctets += totlen; 43392a3e552SArchie Cobbs 434193f57e2SAlexander Motin /* Don't call bpf_filter() with totlen == 0! */ 435193f57e2SAlexander Motin if (totlen == 0) { 436193f57e2SAlexander Motin len = 0; 437193f57e2SAlexander Motin goto ready; 438193f57e2SAlexander Motin } 439193f57e2SAlexander Motin 440f38b3703SAlexander Motin #ifdef BPF_JITTER 441f38b3703SAlexander Motin if (bpf_jitter_enable != 0 && hip->jit_prog != NULL) 442f38b3703SAlexander Motin usejit = 1; 443f38b3703SAlexander Motin #endif 444f38b3703SAlexander Motin 44592a3e552SArchie Cobbs /* Need to put packet in contiguous memory for bpf */ 446f38b3703SAlexander Motin if (m->m_next != NULL && totlen > MHLEN) { 447f38b3703SAlexander Motin if (usejit) { 4489c8c302fSJulian Elischer MALLOC(data, u_char *, totlen, M_NETGRAPH_BPF, M_NOWAIT); 44992a3e552SArchie Cobbs if (data == NULL) { 450069154d5SJulian Elischer NG_FREE_ITEM(item); 45192a3e552SArchie Cobbs return (ENOMEM); 45292a3e552SArchie Cobbs } 45392a3e552SArchie Cobbs needfree = 1; 45492a3e552SArchie Cobbs m_copydata(m, 0, totlen, (caddr_t)data); 455f38b3703SAlexander Motin } 456091193feSAlexander Motin } else { 457f38b3703SAlexander Motin if (m->m_next != NULL) { 458091193feSAlexander Motin NGI_M(item) = m = m_pullup(m, totlen); 459091193feSAlexander Motin if (m == NULL) { 460091193feSAlexander Motin NG_FREE_ITEM(item); 461091193feSAlexander Motin return (ENOBUFS); 462091193feSAlexander Motin } 463f38b3703SAlexander Motin } 464091193feSAlexander Motin data = mtod(m, u_char *); 465091193feSAlexander Motin } 46692a3e552SArchie Cobbs 46792a3e552SArchie Cobbs /* Run packet through filter */ 468848c454cSJung-uk Kim #ifdef BPF_JITTER 469f38b3703SAlexander Motin if (usejit) 470848c454cSJung-uk Kim len = (*(hip->jit_prog->func))(data, totlen, totlen); 471669bb973SArchie Cobbs else 472848c454cSJung-uk Kim #endif 473193f57e2SAlexander Motin if (data) 474193f57e2SAlexander Motin len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen); 475193f57e2SAlexander Motin else 476193f57e2SAlexander Motin len = bpf_filter(hip->prog->bpf_prog, (u_char *)m, totlen, 0); 47792a3e552SArchie Cobbs if (needfree) 4789c8c302fSJulian Elischer FREE(data, M_NETGRAPH_BPF); 479193f57e2SAlexander Motin ready: 48092a3e552SArchie Cobbs /* See if we got a match and find destination hook */ 48192a3e552SArchie Cobbs if (len > 0) { 48292a3e552SArchie Cobbs 48392a3e552SArchie Cobbs /* Update stats */ 484069154d5SJulian Elischer /* XXX atomically? */ 48592a3e552SArchie Cobbs hip->stats.recvMatchFrames++; 48692a3e552SArchie Cobbs hip->stats.recvMatchOctets += totlen; 48792a3e552SArchie Cobbs 48892a3e552SArchie Cobbs /* Truncate packet length if required by the filter */ 489069154d5SJulian Elischer /* Assume this never changes m */ 49092a3e552SArchie Cobbs if (len < totlen) { 49192a3e552SArchie Cobbs m_adj(m, -(totlen - len)); 492193f57e2SAlexander Motin totlen = len; 49392a3e552SArchie Cobbs } 494193f57e2SAlexander Motin dest = hip->match; 49592a3e552SArchie Cobbs } else 496193f57e2SAlexander Motin dest = hip->nomatch; 49792a3e552SArchie Cobbs if (dest == NULL) { 498069154d5SJulian Elischer NG_FREE_ITEM(item); 49992a3e552SArchie Cobbs return (0); 50092a3e552SArchie Cobbs } 50192a3e552SArchie Cobbs 50292a3e552SArchie Cobbs /* Deliver frame out destination hook */ 50330400f03SJulian Elischer dhip = NG_HOOK_PRIVATE(dest); 50492a3e552SArchie Cobbs dhip->stats.xmitOctets += totlen; 50592a3e552SArchie Cobbs dhip->stats.xmitFrames++; 50630400f03SJulian Elischer NG_FWD_ITEM_HOOK(error, item, dest); 50792a3e552SArchie Cobbs return (error); 50892a3e552SArchie Cobbs } 50992a3e552SArchie Cobbs 51092a3e552SArchie Cobbs /* 51192a3e552SArchie Cobbs * Shutdown processing 51292a3e552SArchie Cobbs */ 51392a3e552SArchie Cobbs static int 514069154d5SJulian Elischer ng_bpf_shutdown(node_p node) 51592a3e552SArchie Cobbs { 51630400f03SJulian Elischer NG_NODE_UNREF(node); 51792a3e552SArchie Cobbs return (0); 51892a3e552SArchie Cobbs } 51992a3e552SArchie Cobbs 52092a3e552SArchie Cobbs /* 52192a3e552SArchie Cobbs * Hook disconnection 52292a3e552SArchie Cobbs */ 52392a3e552SArchie Cobbs static int 52492a3e552SArchie Cobbs ng_bpf_disconnect(hook_p hook) 52592a3e552SArchie Cobbs { 526193f57e2SAlexander Motin const node_p node = NG_HOOK_NODE(hook); 52730400f03SJulian Elischer const hinfo_p hip = NG_HOOK_PRIVATE(hook); 528193f57e2SAlexander Motin hook_p tmp; 52992a3e552SArchie Cobbs 5306e551fb6SDavid E. O'Brien KASSERT(hip != NULL, ("%s: null info", __func__)); 531193f57e2SAlexander Motin 532193f57e2SAlexander Motin /* Remove our reference from other hooks data. */ 533193f57e2SAlexander Motin NG_NODE_FOREACH_HOOK(node, ng_bpf_remrefs, hook, tmp); 534193f57e2SAlexander Motin 5359c8c302fSJulian Elischer FREE(hip->prog, M_NETGRAPH_BPF); 536848c454cSJung-uk Kim #ifdef BPF_JITTER 537848c454cSJung-uk Kim if (hip->jit_prog != NULL) 538848c454cSJung-uk Kim bpf_destroy_jit_filter(hip->jit_prog); 539848c454cSJung-uk Kim #endif 5409c8c302fSJulian Elischer FREE(hip, M_NETGRAPH_BPF); 541193f57e2SAlexander Motin if ((NG_NODE_NUMHOOKS(node) == 0) && 542193f57e2SAlexander Motin (NG_NODE_IS_VALID(node))) { 543193f57e2SAlexander Motin ng_rmnode_self(node); 544069154d5SJulian Elischer } 54592a3e552SArchie Cobbs return (0); 54692a3e552SArchie Cobbs } 54792a3e552SArchie Cobbs 54892a3e552SArchie Cobbs /************************************************************************ 54992a3e552SArchie Cobbs HELPER STUFF 55092a3e552SArchie Cobbs ************************************************************************/ 55192a3e552SArchie Cobbs 55292a3e552SArchie Cobbs /* 55392a3e552SArchie Cobbs * Set the BPF program associated with a hook 55492a3e552SArchie Cobbs */ 55592a3e552SArchie Cobbs static int 55692a3e552SArchie Cobbs ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0) 55792a3e552SArchie Cobbs { 55830400f03SJulian Elischer const hinfo_p hip = NG_HOOK_PRIVATE(hook); 55992a3e552SArchie Cobbs struct ng_bpf_hookprog *hp; 560ae2cb97eSJung-uk Kim #ifdef BPF_JITTER 561848c454cSJung-uk Kim bpf_jit_filter *jit_prog; 562ae2cb97eSJung-uk Kim #endif 56392a3e552SArchie Cobbs int size; 56492a3e552SArchie Cobbs 56592a3e552SArchie Cobbs /* Check program for validity */ 56637b5fe59SJung-uk Kim if (hp0->bpf_prog_len > bpf_maxinsns || 56737b5fe59SJung-uk Kim !bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len)) 56892a3e552SArchie Cobbs return (EINVAL); 56992a3e552SArchie Cobbs 57092a3e552SArchie Cobbs /* Make a copy of the program */ 571ab0d3c94SArchie Cobbs size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len); 5729c8c302fSJulian Elischer MALLOC(hp, struct ng_bpf_hookprog *, size, M_NETGRAPH_BPF, M_NOWAIT); 57392a3e552SArchie Cobbs if (hp == NULL) 57492a3e552SArchie Cobbs return (ENOMEM); 57592a3e552SArchie Cobbs bcopy(hp0, hp, size); 576848c454cSJung-uk Kim #ifdef BPF_JITTER 577848c454cSJung-uk Kim jit_prog = bpf_jitter(hp->bpf_prog, hp->bpf_prog_len); 578848c454cSJung-uk Kim #endif 57992a3e552SArchie Cobbs 58092a3e552SArchie Cobbs /* Free previous program, if any, and assign new one */ 58192a3e552SArchie Cobbs if (hip->prog != NULL) 5829c8c302fSJulian Elischer FREE(hip->prog, M_NETGRAPH_BPF); 58392a3e552SArchie Cobbs hip->prog = hp; 584848c454cSJung-uk Kim #ifdef BPF_JITTER 585848c454cSJung-uk Kim if (hip->jit_prog != NULL) 586848c454cSJung-uk Kim bpf_destroy_jit_filter(hip->jit_prog); 587848c454cSJung-uk Kim hip->jit_prog = jit_prog; 588ae2cb97eSJung-uk Kim #endif 589193f57e2SAlexander Motin 590193f57e2SAlexander Motin /* Prepare direct references on target hooks. */ 591193f57e2SAlexander Motin hip->match = ng_findhook(NG_HOOK_NODE(hook), hip->prog->ifMatch); 592193f57e2SAlexander Motin hip->nomatch = ng_findhook(NG_HOOK_NODE(hook), hip->prog->ifNotMatch); 59392a3e552SArchie Cobbs return (0); 59492a3e552SArchie Cobbs } 595