Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/ng_bpf.c
34375 views
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 <[email protected]>
39
* $Whistle: ng_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $
40
*/
41
42
/*
43
* BPF NETGRAPH NODE TYPE
44
*
45
* This node type accepts any number of hook connections. With each hook
46
* is associated a bpf(4) filter program, and two hook names (each possibly
47
* the empty string). Incoming packets are compared against the filter;
48
* matching packets are delivered out the first named hook (or dropped if
49
* the empty string), and non-matching packets are delivered out the second
50
* named hook (or dropped if the empty string).
51
*
52
* Each hook also keeps statistics about how many packets have matched, etc.
53
*/
54
55
#include "opt_bpf.h"
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
#ifdef BPF_JITTER
66
#include <net/bpf_jitter.h>
67
#endif
68
69
#include <netgraph/ng_message.h>
70
#include <netgraph/netgraph.h>
71
#include <netgraph/ng_parse.h>
72
#include <netgraph/ng_bpf.h>
73
74
#ifdef NG_SEPARATE_MALLOC
75
static MALLOC_DEFINE(M_NETGRAPH_BPF, "netgraph_bpf", "netgraph bpf node");
76
#else
77
#define M_NETGRAPH_BPF M_NETGRAPH
78
#endif
79
80
#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0))
81
82
#define ERROUT(x) do { error = (x); goto done; } while (0)
83
84
/* Per hook private info */
85
struct ng_bpf_hookinfo {
86
hook_p hook;
87
hook_p match;
88
hook_p nomatch;
89
struct ng_bpf_hookprog *prog;
90
#ifdef BPF_JITTER
91
bpf_jit_filter *jit_prog;
92
#endif
93
struct ng_bpf_hookstat stats;
94
};
95
typedef struct ng_bpf_hookinfo *hinfo_p;
96
97
/* Netgraph methods */
98
static ng_constructor_t ng_bpf_constructor;
99
static ng_rcvmsg_t ng_bpf_rcvmsg;
100
static ng_shutdown_t ng_bpf_shutdown;
101
static ng_newhook_t ng_bpf_newhook;
102
static ng_rcvdata_t ng_bpf_rcvdata;
103
static ng_disconnect_t ng_bpf_disconnect;
104
105
/* Maximum bpf program instructions */
106
extern int bpf_maxinsns;
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
int error;
275
276
/* Create hook private structure */
277
hip = malloc(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);
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
data = malloc(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
/* Update stats */
480
/* XXX atomically? */
481
hip->stats.recvMatchFrames++;
482
hip->stats.recvMatchOctets += totlen;
483
484
/* Truncate packet length if required by the filter */
485
/* Assume this never changes m */
486
if (len < totlen) {
487
m_adj(m, -(totlen - len));
488
totlen = len;
489
}
490
dest = hip->match;
491
} else
492
dest = hip->nomatch;
493
if (dest == NULL) {
494
NG_FREE_ITEM(item);
495
return (0);
496
}
497
498
/* Deliver frame out destination hook */
499
dhip = NG_HOOK_PRIVATE(dest);
500
dhip->stats.xmitOctets += totlen;
501
dhip->stats.xmitFrames++;
502
NG_FWD_ITEM_HOOK(error, item, dest);
503
return (error);
504
}
505
506
/*
507
* Shutdown processing
508
*/
509
static int
510
ng_bpf_shutdown(node_p node)
511
{
512
NG_NODE_UNREF(node);
513
return (0);
514
}
515
516
/*
517
* Hook disconnection
518
*/
519
static int
520
ng_bpf_disconnect(hook_p hook)
521
{
522
const node_p node = NG_HOOK_NODE(hook);
523
const hinfo_p hip = NG_HOOK_PRIVATE(hook);
524
525
KASSERT(hip != NULL, ("%s: null info", __func__));
526
527
/* Remove our reference from other hooks data. */
528
NG_NODE_FOREACH_HOOK(node, ng_bpf_remrefs, hook);
529
530
free(hip->prog, M_NETGRAPH_BPF);
531
#ifdef BPF_JITTER
532
if (hip->jit_prog != NULL)
533
bpf_destroy_jit_filter(hip->jit_prog);
534
#endif
535
free(hip, M_NETGRAPH_BPF);
536
if ((NG_NODE_NUMHOOKS(node) == 0) &&
537
(NG_NODE_IS_VALID(node))) {
538
ng_rmnode_self(node);
539
}
540
return (0);
541
}
542
543
/************************************************************************
544
HELPER STUFF
545
************************************************************************/
546
547
/*
548
* Set the BPF program associated with a hook
549
*/
550
static int
551
ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0)
552
{
553
const hinfo_p hip = NG_HOOK_PRIVATE(hook);
554
struct ng_bpf_hookprog *hp;
555
#ifdef BPF_JITTER
556
bpf_jit_filter *jit_prog;
557
#endif
558
int size;
559
560
/* Check program for validity */
561
if (hp0->bpf_prog_len > bpf_maxinsns ||
562
!bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len))
563
return (EINVAL);
564
565
/* Make a copy of the program */
566
size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len);
567
hp = malloc(size, M_NETGRAPH_BPF, M_NOWAIT);
568
if (hp == NULL)
569
return (ENOMEM);
570
bcopy(hp0, hp, size);
571
#ifdef BPF_JITTER
572
jit_prog = bpf_jitter(hp->bpf_prog, hp->bpf_prog_len);
573
#endif
574
575
/* Free previous program, if any, and assign new one */
576
if (hip->prog != NULL)
577
free(hip->prog, M_NETGRAPH_BPF);
578
hip->prog = hp;
579
#ifdef BPF_JITTER
580
if (hip->jit_prog != NULL)
581
bpf_destroy_jit_filter(hip->jit_prog);
582
hip->jit_prog = jit_prog;
583
#endif
584
585
/* Prepare direct references on target hooks. */
586
hip->match = ng_findhook(NG_HOOK_NODE(hook), hip->prog->ifMatch);
587
hip->nomatch = ng_findhook(NG_HOOK_NODE(hook), hip->prog->ifNotMatch);
588
return (0);
589
}
590
591