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