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