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