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$ 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 "opt_bpf.h" 58 59 #include <sys/param.h> 60 #include <sys/systm.h> 61 #include <sys/errno.h> 62 #include <sys/kernel.h> 63 #include <sys/malloc.h> 64 #include <sys/mbuf.h> 65 66 #include <net/bpf.h> 67 #ifdef BPF_JITTER 68 #include <net/bpf_jitter.h> 69 #endif 70 71 #include <netgraph/ng_message.h> 72 #include <netgraph/netgraph.h> 73 #include <netgraph/ng_parse.h> 74 #include <netgraph/ng_bpf.h> 75 76 #ifdef NG_SEPARATE_MALLOC 77 MALLOC_DEFINE(M_NETGRAPH_BPF, "netgraph_bpf", "netgraph bpf node "); 78 #else 79 #define M_NETGRAPH_BPF M_NETGRAPH 80 #endif 81 82 #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 83 84 #define ERROUT(x) do { error = (x); goto done; } while (0) 85 86 /* Per hook private info */ 87 struct ng_bpf_hookinfo { 88 node_p node; 89 hook_p hook; 90 struct ng_bpf_hookprog *prog; 91 #ifdef BPF_JITTER 92 bpf_jit_filter *jit_prog; 93 #endif 94 struct ng_bpf_hookstat stats; 95 }; 96 typedef struct ng_bpf_hookinfo *hinfo_p; 97 98 /* Netgraph methods */ 99 static ng_constructor_t ng_bpf_constructor; 100 static ng_rcvmsg_t ng_bpf_rcvmsg; 101 static ng_shutdown_t ng_bpf_shutdown; 102 static ng_newhook_t ng_bpf_newhook; 103 static ng_rcvdata_t ng_bpf_rcvdata; 104 static ng_disconnect_t ng_bpf_disconnect; 105 106 /* Internal helper functions */ 107 static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp); 108 109 /* Parse type for one struct bfp_insn */ 110 static const struct ng_parse_struct_field ng_bpf_insn_type_fields[] = { 111 { "code", &ng_parse_hint16_type }, 112 { "jt", &ng_parse_uint8_type }, 113 { "jf", &ng_parse_uint8_type }, 114 { "k", &ng_parse_uint32_type }, 115 { NULL } 116 }; 117 static const struct ng_parse_type ng_bpf_insn_type = { 118 &ng_parse_struct_type, 119 &ng_bpf_insn_type_fields 120 }; 121 122 /* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */ 123 static int 124 ng_bpf_hookprogary_getLength(const struct ng_parse_type *type, 125 const u_char *start, const u_char *buf) 126 { 127 const struct ng_bpf_hookprog *hp; 128 129 hp = (const struct ng_bpf_hookprog *) 130 (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog)); 131 return hp->bpf_prog_len; 132 } 133 134 static const struct ng_parse_array_info ng_bpf_hookprogary_info = { 135 &ng_bpf_insn_type, 136 &ng_bpf_hookprogary_getLength, 137 NULL 138 }; 139 static const struct ng_parse_type ng_bpf_hookprogary_type = { 140 &ng_parse_array_type, 141 &ng_bpf_hookprogary_info 142 }; 143 144 /* Parse type for struct ng_bpf_hookprog */ 145 static const struct ng_parse_struct_field ng_bpf_hookprog_type_fields[] 146 = NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type); 147 static const struct ng_parse_type ng_bpf_hookprog_type = { 148 &ng_parse_struct_type, 149 &ng_bpf_hookprog_type_fields 150 }; 151 152 /* Parse type for struct ng_bpf_hookstat */ 153 static const struct ng_parse_struct_field ng_bpf_hookstat_type_fields[] 154 = NG_BPF_HOOKSTAT_TYPE_INFO; 155 static const struct ng_parse_type ng_bpf_hookstat_type = { 156 &ng_parse_struct_type, 157 &ng_bpf_hookstat_type_fields 158 }; 159 160 /* List of commands and how to convert arguments to/from ASCII */ 161 static const struct ng_cmdlist ng_bpf_cmdlist[] = { 162 { 163 NGM_BPF_COOKIE, 164 NGM_BPF_SET_PROGRAM, 165 "setprogram", 166 &ng_bpf_hookprog_type, 167 NULL 168 }, 169 { 170 NGM_BPF_COOKIE, 171 NGM_BPF_GET_PROGRAM, 172 "getprogram", 173 &ng_parse_hookbuf_type, 174 &ng_bpf_hookprog_type 175 }, 176 { 177 NGM_BPF_COOKIE, 178 NGM_BPF_GET_STATS, 179 "getstats", 180 &ng_parse_hookbuf_type, 181 &ng_bpf_hookstat_type 182 }, 183 { 184 NGM_BPF_COOKIE, 185 NGM_BPF_CLR_STATS, 186 "clrstats", 187 &ng_parse_hookbuf_type, 188 NULL 189 }, 190 { 191 NGM_BPF_COOKIE, 192 NGM_BPF_GETCLR_STATS, 193 "getclrstats", 194 &ng_parse_hookbuf_type, 195 &ng_bpf_hookstat_type 196 }, 197 { 0 } 198 }; 199 200 /* Netgraph type descriptor */ 201 static struct ng_type typestruct = { 202 .version = NG_ABI_VERSION, 203 .name = NG_BPF_NODE_TYPE, 204 .constructor = ng_bpf_constructor, 205 .rcvmsg = ng_bpf_rcvmsg, 206 .shutdown = ng_bpf_shutdown, 207 .newhook = ng_bpf_newhook, 208 .rcvdata = ng_bpf_rcvdata, 209 .disconnect = ng_bpf_disconnect, 210 .cmdlist = ng_bpf_cmdlist, 211 }; 212 NETGRAPH_INIT(bpf, &typestruct); 213 214 /* Default BPF program for a hook that matches nothing */ 215 static const struct ng_bpf_hookprog ng_bpf_default_prog = { 216 { '\0' }, /* to be filled in at hook creation time */ 217 { '\0' }, 218 { '\0' }, 219 1, 220 { BPF_STMT(BPF_RET+BPF_K, 0) } 221 }; 222 223 /* 224 * Node constructor 225 * 226 * We don't keep any per-node private data 227 * We go via the hooks. 228 */ 229 static int 230 ng_bpf_constructor(node_p node) 231 { 232 NG_NODE_SET_PRIVATE(node, NULL); 233 return (0); 234 } 235 236 /* 237 * Add a hook 238 */ 239 static int 240 ng_bpf_newhook(node_p node, hook_p hook, const char *name) 241 { 242 hinfo_p hip; 243 int error; 244 245 /* Create hook private structure */ 246 MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH_BPF, M_NOWAIT | M_ZERO); 247 if (hip == NULL) 248 return (ENOMEM); 249 hip->hook = hook; 250 NG_HOOK_SET_PRIVATE(hook, hip); 251 hip->node = node; 252 253 /* Attach the default BPF program */ 254 if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) { 255 FREE(hip, M_NETGRAPH_BPF); 256 NG_HOOK_SET_PRIVATE(hook, NULL); 257 return (error); 258 } 259 260 /* Set hook name */ 261 strncpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook) - 1); 262 hip->prog->thisHook[sizeof(hip->prog->thisHook) - 1] = '\0'; 263 return (0); 264 } 265 266 /* 267 * Receive a control message 268 */ 269 static int 270 ng_bpf_rcvmsg(node_p node, item_p item, hook_p lasthook) 271 { 272 struct ng_mesg *msg; 273 struct ng_mesg *resp = NULL; 274 int error = 0; 275 276 NGI_GET_MSG(item, msg); 277 switch (msg->header.typecookie) { 278 case NGM_BPF_COOKIE: 279 switch (msg->header.cmd) { 280 case NGM_BPF_SET_PROGRAM: 281 { 282 struct ng_bpf_hookprog *const 283 hp = (struct ng_bpf_hookprog *)msg->data; 284 hook_p hook; 285 286 /* Sanity check */ 287 if (msg->header.arglen < sizeof(*hp) 288 || msg->header.arglen 289 != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) 290 ERROUT(EINVAL); 291 292 /* Find hook */ 293 if ((hook = ng_findhook(node, hp->thisHook)) == NULL) 294 ERROUT(ENOENT); 295 296 /* Set new program */ 297 if ((error = ng_bpf_setprog(hook, hp)) != 0) 298 ERROUT(error); 299 break; 300 } 301 302 case NGM_BPF_GET_PROGRAM: 303 { 304 struct ng_bpf_hookprog *hp; 305 hook_p hook; 306 307 /* Sanity check */ 308 if (msg->header.arglen == 0) 309 ERROUT(EINVAL); 310 msg->data[msg->header.arglen - 1] = '\0'; 311 312 /* Find hook */ 313 if ((hook = ng_findhook(node, msg->data)) == NULL) 314 ERROUT(ENOENT); 315 316 /* Build response */ 317 hp = ((hinfo_p)NG_HOOK_PRIVATE(hook))->prog; 318 NG_MKRESPONSE(resp, msg, 319 NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT); 320 if (resp == NULL) 321 ERROUT(ENOMEM); 322 bcopy(hp, resp->data, 323 NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)); 324 break; 325 } 326 327 case NGM_BPF_GET_STATS: 328 case NGM_BPF_CLR_STATS: 329 case NGM_BPF_GETCLR_STATS: 330 { 331 struct ng_bpf_hookstat *stats; 332 hook_p hook; 333 334 /* Sanity check */ 335 if (msg->header.arglen == 0) 336 ERROUT(EINVAL); 337 msg->data[msg->header.arglen - 1] = '\0'; 338 339 /* Find hook */ 340 if ((hook = ng_findhook(node, msg->data)) == NULL) 341 ERROUT(ENOENT); 342 stats = &((hinfo_p)NG_HOOK_PRIVATE(hook))->stats; 343 344 /* Build response (if desired) */ 345 if (msg->header.cmd != NGM_BPF_CLR_STATS) { 346 NG_MKRESPONSE(resp, 347 msg, sizeof(*stats), M_NOWAIT); 348 if (resp == NULL) 349 ERROUT(ENOMEM); 350 bcopy(stats, resp->data, sizeof(*stats)); 351 } 352 353 /* Clear stats (if desired) */ 354 if (msg->header.cmd != NGM_BPF_GET_STATS) 355 bzero(stats, sizeof(*stats)); 356 break; 357 } 358 359 default: 360 error = EINVAL; 361 break; 362 } 363 break; 364 default: 365 error = EINVAL; 366 break; 367 } 368 done: 369 NG_RESPOND_MSG(error, node, item, resp); 370 NG_FREE_MSG(msg); 371 return (error); 372 } 373 374 /* 375 * Receive data on a hook 376 * 377 * Apply the filter, and then drop or forward packet as appropriate. 378 */ 379 static int 380 ng_bpf_rcvdata(hook_p hook, item_p item) 381 { 382 const hinfo_p hip = NG_HOOK_PRIVATE(hook); 383 int totlen; 384 int needfree = 0, error = 0; 385 u_char *data, buf[256]; 386 hinfo_p dhip; 387 hook_p dest; 388 u_int len; 389 struct mbuf *m; 390 391 m = NGI_M(item); /* 'item' still owns it.. we are peeking */ 392 totlen = m->m_pkthdr.len; 393 /* Update stats on incoming hook. XXX Can we do 64 bits atomically? */ 394 /* atomic_add_int64(&hip->stats.recvFrames, 1); */ 395 /* atomic_add_int64(&hip->stats.recvOctets, totlen); */ 396 hip->stats.recvFrames++; 397 hip->stats.recvOctets += totlen; 398 399 /* Need to put packet in contiguous memory for bpf */ 400 if (m->m_next != NULL) { 401 if (totlen > sizeof(buf)) { 402 MALLOC(data, u_char *, totlen, M_NETGRAPH_BPF, M_NOWAIT); 403 if (data == NULL) { 404 NG_FREE_ITEM(item); 405 return (ENOMEM); 406 } 407 needfree = 1; 408 } else 409 data = buf; 410 m_copydata(m, 0, totlen, (caddr_t)data); 411 } else 412 data = mtod(m, u_char *); 413 414 /* Run packet through filter */ 415 if (totlen == 0) 416 len = 0; /* don't call bpf_filter() with totlen == 0! */ 417 else { 418 #ifdef BPF_JITTER 419 if (bpf_jitter_enable != 0 && hip->jit_prog != NULL) 420 len = (*(hip->jit_prog->func))(data, totlen, totlen); 421 else 422 #endif 423 len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen); 424 } 425 if (needfree) 426 FREE(data, M_NETGRAPH_BPF); 427 428 /* See if we got a match and find destination hook */ 429 if (len > 0) { 430 431 /* Update stats */ 432 /* XXX atomically? */ 433 hip->stats.recvMatchFrames++; 434 hip->stats.recvMatchOctets += totlen; 435 436 /* Truncate packet length if required by the filter */ 437 /* Assume this never changes m */ 438 if (len < totlen) { 439 m_adj(m, -(totlen - len)); 440 totlen -= len; 441 } 442 dest = ng_findhook(hip->node, hip->prog->ifMatch); 443 } else 444 dest = ng_findhook(hip->node, hip->prog->ifNotMatch); 445 if (dest == NULL) { 446 NG_FREE_ITEM(item); 447 return (0); 448 } 449 450 /* Deliver frame out destination hook */ 451 dhip = NG_HOOK_PRIVATE(dest); 452 dhip->stats.xmitOctets += totlen; 453 dhip->stats.xmitFrames++; 454 NG_FWD_ITEM_HOOK(error, item, dest); 455 return (error); 456 } 457 458 /* 459 * Shutdown processing 460 */ 461 static int 462 ng_bpf_shutdown(node_p node) 463 { 464 NG_NODE_UNREF(node); 465 return (0); 466 } 467 468 /* 469 * Hook disconnection 470 */ 471 static int 472 ng_bpf_disconnect(hook_p hook) 473 { 474 const hinfo_p hip = NG_HOOK_PRIVATE(hook); 475 476 KASSERT(hip != NULL, ("%s: null info", __func__)); 477 FREE(hip->prog, M_NETGRAPH_BPF); 478 #ifdef BPF_JITTER 479 if (hip->jit_prog != NULL) 480 bpf_destroy_jit_filter(hip->jit_prog); 481 #endif 482 bzero(hip, sizeof(*hip)); 483 FREE(hip, M_NETGRAPH_BPF); 484 NG_HOOK_SET_PRIVATE(hook, NULL); /* for good measure */ 485 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 486 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { 487 ng_rmnode_self(NG_HOOK_NODE(hook)); 488 } 489 return (0); 490 } 491 492 /************************************************************************ 493 HELPER STUFF 494 ************************************************************************/ 495 496 /* 497 * Set the BPF program associated with a hook 498 */ 499 static int 500 ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0) 501 { 502 const hinfo_p hip = NG_HOOK_PRIVATE(hook); 503 struct ng_bpf_hookprog *hp; 504 #ifdef BPF_JITTER 505 bpf_jit_filter *jit_prog; 506 #endif 507 int size; 508 509 /* Check program for validity */ 510 if (!bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len)) 511 return (EINVAL); 512 513 /* Make a copy of the program */ 514 size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len); 515 MALLOC(hp, struct ng_bpf_hookprog *, size, M_NETGRAPH_BPF, M_NOWAIT); 516 if (hp == NULL) 517 return (ENOMEM); 518 bcopy(hp0, hp, size); 519 #ifdef BPF_JITTER 520 jit_prog = bpf_jitter(hp->bpf_prog, hp->bpf_prog_len); 521 #endif 522 523 /* Free previous program, if any, and assign new one */ 524 if (hip->prog != NULL) 525 FREE(hip->prog, M_NETGRAPH_BPF); 526 hip->prog = hp; 527 #ifdef BPF_JITTER 528 if (hip->jit_prog != NULL) 529 bpf_destroy_jit_filter(hip->jit_prog); 530 hip->jit_prog = jit_prog; 531 #endif 532 return (0); 533 } 534