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