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