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