ng_bpf.c revision 70870
1 2/* 3 * ng_bpf.c 4 * 5 * Copyright (c) 1999 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANBPF, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Author: Archie Cobbs <archie@freebsd.org> 38 * 39 * $FreeBSD: head/sys/netgraph/ng_bpf.c 70870 2001-01-10 07:13:58Z julian $ 40 * $Whistle: ng_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $ 41 */ 42 43/* 44 * BPF NETGRAPH NODE TYPE 45 * 46 * This node type accepts any number of hook connections. With each hook 47 * is associated a bpf(4) filter program, and two hook names (each possibly 48 * the empty string). Incoming packets are compared against the filter; 49 * matching packets are delivered out the first named hook (or dropped if 50 * the empty string), and non-matching packets are delivered out the second 51 * named hook (or dropped if the empty string). 52 * 53 * Each hook also keeps statistics about how many packets have matched, etc. 54 */ 55 56#include <sys/param.h> 57#include <sys/systm.h> 58#include <sys/errno.h> 59#include <sys/kernel.h> 60#include <sys/malloc.h> 61#include <sys/mbuf.h> 62 63#include <net/bpf.h> 64 65#include <netgraph/ng_message.h> 66#include <netgraph/netgraph.h> 67#include <netgraph/ng_parse.h> 68#include <netgraph/ng_bpf.h> 69 70#ifdef NG_SEPARATE_MALLOC 71MALLOC_DEFINE(M_NETGRAPH_BPF, "netgraph_bpf", "netgraph bpf node "); 72#else 73#define M_NETGRAPH_BPF M_NETGRAPH 74#endif 75 76#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 77 78#define ERROUT(x) do { error = (x); goto done; } while (0) 79 80/* Per hook private info */ 81struct ng_bpf_hookinfo { 82 node_p node; 83 hook_p hook; 84 struct ng_bpf_hookprog *prog; 85 struct ng_bpf_hookstat stats; 86}; 87typedef struct ng_bpf_hookinfo *hinfo_p; 88 89/* Netgraph methods */ 90static ng_constructor_t ng_bpf_constructor; 91static ng_rcvmsg_t ng_bpf_rcvmsg; 92static ng_shutdown_t ng_bpf_shutdown; 93static ng_newhook_t ng_bpf_newhook; 94static ng_rcvdata_t ng_bpf_rcvdata; 95static ng_disconnect_t ng_bpf_disconnect; 96 97/* Internal helper functions */ 98static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp); 99 100/* Parse type for one struct bfp_insn */ 101static const struct ng_parse_struct_info ng_bpf_insn_type_info = { 102 { 103 { "code", &ng_parse_hint16_type }, 104 { "jt", &ng_parse_uint8_type }, 105 { "jf", &ng_parse_uint8_type }, 106 { "k", &ng_parse_uint32_type }, 107 { NULL } 108 } 109}; 110static const struct ng_parse_type ng_bpf_insn_type = { 111 &ng_parse_struct_type, 112 &ng_bpf_insn_type_info 113}; 114 115/* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */ 116static int 117ng_bpf_hookprogary_getLength(const struct ng_parse_type *type, 118 const u_char *start, const u_char *buf) 119{ 120 const struct ng_bpf_hookprog *hp; 121 122 hp = (const struct ng_bpf_hookprog *) 123 (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog)); 124 return hp->bpf_prog_len; 125} 126 127static const struct ng_parse_array_info ng_bpf_hookprogary_info = { 128 &ng_bpf_insn_type, 129 &ng_bpf_hookprogary_getLength, 130 NULL 131}; 132static const struct ng_parse_type ng_bpf_hookprogary_type = { 133 &ng_parse_array_type, 134 &ng_bpf_hookprogary_info 135}; 136 137/* Parse type for struct ng_bpf_hookprog */ 138static const struct ng_parse_struct_info ng_bpf_hookprog_type_info 139 = NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type); 140static const struct ng_parse_type ng_bpf_hookprog_type = { 141 &ng_parse_struct_type, 142 &ng_bpf_hookprog_type_info 143}; 144 145/* Parse type for struct ng_bpf_hookstat */ 146static const struct ng_parse_struct_info 147 ng_bpf_hookstat_type_info = NG_BPF_HOOKSTAT_TYPE_INFO; 148static const struct ng_parse_type ng_bpf_hookstat_type = { 149 &ng_parse_struct_type, 150 &ng_bpf_hookstat_type_info 151}; 152 153/* List of commands and how to convert arguments to/from ASCII */ 154static const struct ng_cmdlist ng_bpf_cmdlist[] = { 155 { 156 NGM_BPF_COOKIE, 157 NGM_BPF_SET_PROGRAM, 158 "setprogram", 159 &ng_bpf_hookprog_type, 160 NULL 161 }, 162 { 163 NGM_BPF_COOKIE, 164 NGM_BPF_GET_PROGRAM, 165 "getprogram", 166 &ng_parse_hookbuf_type, 167 &ng_bpf_hookprog_type 168 }, 169 { 170 NGM_BPF_COOKIE, 171 NGM_BPF_GET_STATS, 172 "getstats", 173 &ng_parse_hookbuf_type, 174 &ng_bpf_hookstat_type 175 }, 176 { 177 NGM_BPF_COOKIE, 178 NGM_BPF_CLR_STATS, 179 "clrstats", 180 &ng_parse_hookbuf_type, 181 NULL 182 }, 183 { 184 NGM_BPF_COOKIE, 185 NGM_BPF_GETCLR_STATS, 186 "getclrstats", 187 &ng_parse_hookbuf_type, 188 &ng_bpf_hookstat_type 189 }, 190 { 0 } 191}; 192 193/* Netgraph type descriptor */ 194static struct ng_type typestruct = { 195 NG_ABI_VERSION, 196 NG_BPF_NODE_TYPE, 197 NULL, 198 ng_bpf_constructor, 199 ng_bpf_rcvmsg, 200 ng_bpf_shutdown, 201 ng_bpf_newhook, 202 NULL, 203 NULL, 204 ng_bpf_rcvdata, 205 ng_bpf_disconnect, 206 ng_bpf_cmdlist 207}; 208NETGRAPH_INIT(bpf, &typestruct); 209 210/* Default BPF program for a hook that matches nothing */ 211static const struct ng_bpf_hookprog ng_bpf_default_prog = { 212 { '\0' }, /* to be filled in at hook creation time */ 213 { '\0' }, 214 { '\0' }, 215 1, 216 { BPF_STMT(BPF_RET+BPF_K, 0) } 217}; 218 219/* 220 * Node constructor 221 * 222 * We don't keep any per-node private data 223 * We go via the hooks. 224 */ 225static int 226ng_bpf_constructor(node_p node) 227{ 228 NG_NODE_SET_PRIVATE(node, NULL); 229 return (0); 230} 231 232/* 233 * Add a hook 234 */ 235static int 236ng_bpf_newhook(node_p node, hook_p hook, const char *name) 237{ 238 hinfo_p hip; 239 int error; 240 241 /* Create hook private structure */ 242 MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH_BPF, M_NOWAIT | M_ZERO); 243 if (hip == NULL) 244 return (ENOMEM); 245 hip->hook = hook; 246 NG_HOOK_SET_PRIVATE(hook, hip); 247 hip->node = node; 248 249 /* Attach the default BPF program */ 250 if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) { 251 FREE(hip, M_NETGRAPH_BPF); 252 NG_HOOK_SET_PRIVATE(hook, NULL); 253 return (error); 254 } 255 256 /* Set hook name */ 257 strncpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook) - 1); 258 hip->prog->thisHook[sizeof(hip->prog->thisHook) - 1] = '\0'; 259 return (0); 260} 261 262/* 263 * Receive a control message 264 */ 265static int 266ng_bpf_rcvmsg(node_p node, item_p item, hook_p lasthook) 267{ 268 struct ng_mesg *msg; 269 struct ng_mesg *resp = NULL; 270 int error = 0; 271 272 NGI_GET_MSG(item, msg); 273 switch (msg->header.typecookie) { 274 case NGM_BPF_COOKIE: 275 switch (msg->header.cmd) { 276 case NGM_BPF_SET_PROGRAM: 277 { 278 struct ng_bpf_hookprog *const 279 hp = (struct ng_bpf_hookprog *)msg->data; 280 hook_p hook; 281 282 /* Sanity check */ 283 if (msg->header.arglen < sizeof(*hp) 284 || msg->header.arglen 285 != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) 286 ERROUT(EINVAL); 287 288 /* Find hook */ 289 if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 290 ERROUT(ENOENT); 291 292 /* Set new program */ 293 if ((error = ng_bpf_setprog(hook, hp)) != 0) 294 ERROUT(error); 295 break; 296 } 297 298 case NGM_BPF_GET_PROGRAM: 299 { 300 struct ng_bpf_hookprog *hp; 301 hook_p hook; 302 303 /* Sanity check */ 304 if (msg->header.arglen == 0) 305 ERROUT(EINVAL); 306 msg->data[msg->header.arglen - 1] = '\0'; 307 308 /* Find hook */ 309 if ((hook = ng_findhook(node, msg->data)) == NULL) 310 ERROUT(ENOENT); 311 312 /* Build response */ 313 hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->prog; 314 NG_MKRESPONSE(resp, msg, 315 NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT); 316 if (resp == NULL) 317 ERROUT(ENOMEM); 318 bcopy(hp, resp->data, 319 NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)); 320 break; 321 } 322 323 case NGM_BPF_GET_STATS: 324 case NGM_BPF_CLR_STATS: 325 case NGM_BPF_GETCLR_STATS: 326 { 327 struct ng_bpf_hookstat *stats; 328 hook_p hook; 329 330 /* Sanity check */ 331 if (msg->header.arglen == 0) 332 ERROUT(EINVAL); 333 msg->data[msg->header.arglen - 1] = '\0'; 334 335 /* Find hook */ 336 if ((hook = ng_findhook(node, msg->data)) == NULL) 337 ERROUT(ENOENT); 338 stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats; 339 340 /* Build response (if desired) */ 341 if (msg->header.cmd != NGM_BPF_CLR_STATS) { 342 NG_MKRESPONSE(resp, 343 msg, sizeof(*stats), M_NOWAIT); 344 if (resp == NULL) 345 ERROUT(ENOMEM); 346 bcopy(stats, resp->data, sizeof(*stats)); 347 } 348 349 /* Clear stats (if desired) */ 350 if (msg->header.cmd != NGM_BPF_GET_STATS) 351 bzero(stats, sizeof(*stats)); 352 break; 353 } 354 355 default: 356 error = EINVAL; 357 break; 358 } 359 break; 360 default: 361 error = EINVAL; 362 break; 363 } 364 NG_RESPOND_MSG(error, node, item, resp); 365done: 366 if (item) 367 NG_FREE_ITEM(item); 368 NG_FREE_MSG(msg); 369 return (error); 370} 371 372/* 373 * Receive data on a hook 374 * 375 * Apply the filter, and then drop or forward packet as appropriate. 376 */ 377static int 378ng_bpf_rcvdata(hook_p hook, item_p item) 379{ 380 const hinfo_p hip = NG_HOOK_PRIVATE(hook); 381 int totlen; 382 int needfree = 0, error = 0; 383 u_char *data, buf[256]; 384 hinfo_p dhip; 385 hook_p dest; 386 u_int len; 387 struct mbuf *m; 388 389 m = NGI_M(item); /* 'item' still owns it.. we are peeking */ 390 totlen = m->m_pkthdr.len; 391 /* Update stats on incoming hook. XXX Can we do 64 bits atomically? */ 392 /* atomic_add_int64(&hip->stats.recvFrames, 1); */ 393 /* atomic_add_int64(&hip->stats.recvOctets, totlen); */ 394 hip->stats.recvFrames++; 395 hip->stats.recvOctets += totlen; 396 397 /* Need to put packet in contiguous memory for bpf */ 398 if (m->m_next != NULL) { 399 if (totlen > sizeof(buf)) { 400 MALLOC(data, u_char *, totlen, M_NETGRAPH_BPF, M_NOWAIT); 401 if (data == NULL) { 402 NG_FREE_ITEM(item); 403 return (ENOMEM); 404 } 405 needfree = 1; 406 } else 407 data = buf; 408 m_copydata(m, 0, totlen, (caddr_t)data); 409 } else 410 data = mtod(m, u_char *); 411 412 /* Run packet through filter */ 413 len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen); 414 if (needfree) 415 FREE(data, M_NETGRAPH_BPF); 416 417 /* See if we got a match and find destination hook */ 418 if (len > 0) { 419 420 /* Update stats */ 421 /* XXX atomically? */ 422 hip->stats.recvMatchFrames++; 423 hip->stats.recvMatchOctets += totlen; 424 425 /* Truncate packet length if required by the filter */ 426 /* Assume this never changes m */ 427 if (len < totlen) { 428 m_adj(m, -(totlen - len)); 429 totlen -= len; 430 } 431 dest = ng_findhook(hip->node, hip->prog->ifMatch); 432 } else 433 dest = ng_findhook(hip->node, hip->prog->ifNotMatch); 434 if (dest == NULL) { 435 NG_FREE_ITEM(item); 436 return (0); 437 } 438 439 /* Deliver frame out destination hook */ 440 dhip = NG_HOOK_PRIVATE(dest); 441 dhip->stats.xmitOctets += totlen; 442 dhip->stats.xmitFrames++; 443 NG_FWD_ITEM_HOOK(error, item, dest); 444 return (error); 445} 446 447/* 448 * Shutdown processing 449 */ 450static int 451ng_bpf_shutdown(node_p node) 452{ 453 NG_NODE_UNREF(node); 454 return (0); 455} 456 457/* 458 * Hook disconnection 459 */ 460static int 461ng_bpf_disconnect(hook_p hook) 462{ 463 const hinfo_p hip = NG_HOOK_PRIVATE(hook); 464 465 KASSERT(hip != NULL, ("%s: null info", __FUNCTION__)); 466 FREE(hip->prog, M_NETGRAPH_BPF); 467 bzero(hip, sizeof(*hip)); 468 FREE(hip, M_NETGRAPH_BPF); 469 NG_HOOK_SET_PRIVATE(hook, NULL); /* for good measure */ 470 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 471 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { 472 ng_rmnode_self(NG_HOOK_NODE(hook)); 473 } 474 return (0); 475} 476 477/************************************************************************ 478 HELPER STUFF 479 ************************************************************************/ 480 481/* 482 * Set the BPF program associated with a hook 483 */ 484static int 485ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0) 486{ 487 const hinfo_p hip = NG_HOOK_PRIVATE(hook); 488 struct ng_bpf_hookprog *hp; 489 int size; 490 491 /* Check program for validity */ 492 if (!bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len)) 493 return (EINVAL); 494 495 /* Make a copy of the program */ 496 size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len); 497 MALLOC(hp, struct ng_bpf_hookprog *, size, M_NETGRAPH_BPF, M_NOWAIT); 498 if (hp == NULL) 499 return (ENOMEM); 500 bcopy(hp0, hp, size); 501 502 /* Free previous program, if any, and assign new one */ 503 if (hip->prog != NULL) 504 FREE(hip->prog, M_NETGRAPH_BPF); 505 hip->prog = hp; 506 return (0); 507} 508 509