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