Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/net/dummymbuf.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2024 Igor Ostapenko <[email protected]>
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include "opt_inet.h"
29
#include "opt_inet6.h"
30
31
#include <sys/param.h>
32
#include <sys/kernel.h>
33
#include <sys/mbuf.h>
34
#include <sys/module.h>
35
#include <sys/socket.h>
36
#include <sys/sysctl.h>
37
38
#include <net/if.h>
39
#include <net/if_var.h>
40
#include <net/if_private.h>
41
#include <net/vnet.h>
42
#include <net/pfil.h>
43
44
static int validate_rules(const char *);
45
46
/*
47
* Separate sysctl sub-tree
48
*/
49
50
SYSCTL_NODE(_net, OID_AUTO, dummymbuf, 0, NULL,
51
"Dummy mbuf sysctl");
52
53
/*
54
* Rules
55
*/
56
57
static MALLOC_DEFINE(M_DUMMYMBUF_RULES, "dummymbuf_rules",
58
"dummymbuf rules string buffer");
59
60
#define RULES_MAXLEN 1024
61
VNET_DEFINE_STATIC(char *, dmb_rules) = NULL;
62
#define V_dmb_rules VNET(dmb_rules)
63
64
VNET_DEFINE_STATIC(struct sx, dmb_rules_lock);
65
#define V_dmb_rules_lock VNET(dmb_rules_lock)
66
67
#define DMB_RULES_SLOCK() sx_slock(&V_dmb_rules_lock)
68
#define DMB_RULES_SUNLOCK() sx_sunlock(&V_dmb_rules_lock)
69
#define DMB_RULES_XLOCK() sx_xlock(&V_dmb_rules_lock)
70
#define DMB_RULES_XUNLOCK() sx_xunlock(&V_dmb_rules_lock)
71
#define DMB_RULES_LOCK_ASSERT() sx_assert(&V_dmb_rules_lock, SA_LOCKED)
72
73
static int
74
dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS)
75
{
76
int error = 0;
77
char empty = '\0';
78
char **rulesp = (char **)arg1;
79
80
if (req->newptr == NULL) {
81
/* read only */
82
DMB_RULES_SLOCK();
83
arg1 = *rulesp;
84
if (arg1 == NULL) {
85
arg1 = &empty;
86
arg2 = 0;
87
}
88
error = sysctl_handle_string(oidp, arg1, arg2, req);
89
DMB_RULES_SUNLOCK();
90
} else {
91
/* read and write */
92
DMB_RULES_XLOCK();
93
arg1 = malloc(arg2, M_DUMMYMBUF_RULES, M_WAITOK | M_ZERO);
94
error = sysctl_handle_string(oidp, arg1, arg2, req);
95
if (error == 0 && (error = validate_rules(arg1)) == 0) {
96
free(*rulesp, M_DUMMYMBUF_RULES);
97
*rulesp = arg1;
98
arg1 = NULL;
99
}
100
free(arg1, M_DUMMYMBUF_RULES);
101
DMB_RULES_XUNLOCK();
102
}
103
104
return (error);
105
}
106
107
SYSCTL_PROC(_net_dummymbuf, OID_AUTO, rules,
108
CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_VNET,
109
&VNET_NAME(dmb_rules), RULES_MAXLEN, dmb_sysctl_handle_rules, "A",
110
"{inet|inet6|ethernet} {in|out} <ifname> <opname>[ <opargs>]; ...;");
111
112
/*
113
* Statistics
114
*/
115
116
VNET_DEFINE_STATIC(counter_u64_t, dmb_hits);
117
#define V_dmb_hits VNET(dmb_hits)
118
SYSCTL_PROC(_net_dummymbuf, OID_AUTO, hits,
119
CTLTYPE_U64 | CTLFLAG_MPSAFE | CTLFLAG_STATS | CTLFLAG_RW | CTLFLAG_VNET,
120
&VNET_NAME(dmb_hits), 0, sysctl_handle_counter_u64,
121
"QU", "Number of times a rule has been applied");
122
123
/*
124
* pfil(9) context
125
*/
126
127
#ifdef INET
128
VNET_DEFINE_STATIC(pfil_hook_t, dmb_pfil_inet_hook);
129
#define V_dmb_pfil_inet_hook VNET(dmb_pfil_inet_hook)
130
#endif
131
132
#ifdef INET6
133
VNET_DEFINE_STATIC(pfil_hook_t, dmb_pfil_inet6_hook);
134
#define V_dmb_pfil_inet6_hook VNET(dmb_pfil_inet6_hook)
135
#endif
136
137
VNET_DEFINE_STATIC(pfil_hook_t, dmb_pfil_ethernet_hook);
138
#define V_dmb_pfil_ethernet_hook VNET(dmb_pfil_ethernet_hook)
139
140
/*
141
* Logging
142
*/
143
144
#define FEEDBACK_RULE(rule, msg) \
145
printf("dummymbuf: %s: %.*s\n", \
146
(msg), \
147
(rule).syntax_len, (rule).syntax_begin \
148
)
149
150
#define FEEDBACK_PFIL(pfil_type, pfil_flags, ifp, rule, msg) \
151
printf("dummymbuf: %s %b %s: %s: %.*s\n", \
152
((pfil_type) == PFIL_TYPE_IP4 ? "PFIL_TYPE_IP4" : \
153
(pfil_type) == PFIL_TYPE_IP6 ? "PFIL_TYPE_IP6" : \
154
(pfil_type) == PFIL_TYPE_ETHERNET ? "PFIL_TYPE_ETHERNET" : \
155
"PFIL_TYPE_UNKNOWN"), \
156
(pfil_flags), "\20\21PFIL_IN\22PFIL_OUT", \
157
(ifp)->if_xname, \
158
(msg), \
159
(rule).syntax_len, (rule).syntax_begin \
160
)
161
162
/*
163
* Internals
164
*/
165
166
struct rule;
167
typedef struct mbuf * (*op_t)(struct mbuf *, struct rule *);
168
struct rule {
169
const char *syntax_begin;
170
int syntax_len;
171
int pfil_type;
172
int pfil_dir;
173
char ifname[IFNAMSIZ];
174
op_t op;
175
const char *opargs;
176
};
177
178
static struct mbuf *
179
dmb_m_pull_head(struct mbuf *m, struct rule *rule)
180
{
181
struct mbuf *n;
182
int count;
183
184
count = (int)strtol(rule->opargs, NULL, 10);
185
if (count < 0 || count > MCLBYTES)
186
goto bad;
187
188
if (!(m->m_flags & M_PKTHDR))
189
goto bad;
190
if (m->m_pkthdr.len <= 0)
191
return (m);
192
if (count > m->m_pkthdr.len)
193
count = m->m_pkthdr.len;
194
195
if ((n = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
196
goto bad;
197
198
m_move_pkthdr(n, m);
199
m_copydata(m, 0, count, n->m_ext.ext_buf);
200
n->m_len = count;
201
202
m_adj(m, count);
203
n->m_next = m;
204
205
return (n);
206
207
bad:
208
m_freem(m);
209
return (NULL);
210
}
211
212
static struct mbuf *
213
dmb_m_enlarge(struct mbuf *m, struct rule *rule)
214
{
215
struct mbuf *n;
216
int size;
217
218
size = (int)strtol(rule->opargs, NULL, 10);
219
if (size < 0 || size > MJUM16BYTES)
220
goto bad;
221
222
if (!(m->m_flags & M_PKTHDR))
223
goto bad;
224
if (m->m_pkthdr.len <= 0)
225
return (m);
226
227
if ((n = m_get3(size, M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
228
goto bad;
229
230
m_move_pkthdr(n, m);
231
m_copydata(m, 0, m->m_pkthdr.len, n->m_ext.ext_buf);
232
n->m_len = m->m_pkthdr.len;
233
234
n->m_next = m;
235
236
return (n);
237
238
bad:
239
m_freem(m);
240
return (NULL);
241
}
242
243
static bool
244
read_rule(const char **cur, struct rule *rule, bool *eof)
245
{
246
/* {inet|inet6|ethernet} {in|out} <ifname> <opname>[ <opargs>]; */
247
248
rule->syntax_begin = NULL;
249
rule->syntax_len = 0;
250
251
if (*cur == NULL)
252
return (false);
253
254
/* syntax_begin */
255
while (**cur == ' ')
256
(*cur)++;
257
rule->syntax_begin = *cur;
258
rule->syntax_len = strlen(rule->syntax_begin);
259
260
/* syntax_len */
261
char *delim = strchr(*cur, ';');
262
if (delim == NULL)
263
return (false);
264
rule->syntax_len = (int)(delim - *cur + 1);
265
266
/* pfil_type */
267
if (strstr(*cur, "inet6") == *cur) {
268
rule->pfil_type = PFIL_TYPE_IP6;
269
*cur += strlen("inet6");
270
} else if (strstr(*cur, "inet") == *cur) {
271
rule->pfil_type = PFIL_TYPE_IP4;
272
*cur += strlen("inet");
273
} else if (strstr(*cur, "ethernet")) {
274
rule->pfil_type = PFIL_TYPE_ETHERNET;
275
*cur += strlen("ethernet");
276
} else {
277
return (false);
278
}
279
while (**cur == ' ')
280
(*cur)++;
281
282
/* pfil_dir */
283
if (strstr(*cur, "in") == *cur) {
284
rule->pfil_dir = PFIL_IN;
285
*cur += strlen("in");
286
} else if (strstr(*cur, "out") == *cur) {
287
rule->pfil_dir = PFIL_OUT;
288
*cur += strlen("out");
289
} else {
290
return (false);
291
}
292
while (**cur == ' ')
293
(*cur)++;
294
295
/* ifname */
296
char *sp = strchr(*cur, ' ');
297
if (sp == NULL || sp > delim)
298
return (false);
299
size_t len = sp - *cur;
300
if (len >= sizeof(rule->ifname))
301
return (false);
302
strncpy(rule->ifname, *cur, len);
303
rule->ifname[len] = 0;
304
*cur = sp;
305
while (**cur == ' ')
306
(*cur)++;
307
308
/* opname */
309
if (strstr(*cur, "pull-head") == *cur) {
310
rule->op = dmb_m_pull_head;
311
*cur += strlen("pull-head");
312
} else if (strstr(*cur, "enlarge") == *cur) {
313
rule->op = dmb_m_enlarge;
314
*cur += strlen("enlarge");
315
} else {
316
return (false);
317
}
318
while (**cur == ' ')
319
(*cur)++;
320
321
/* opargs */
322
if (*cur > delim)
323
return (false);
324
rule->opargs = *cur;
325
326
/* the next rule & eof */
327
*cur = delim + 1;
328
while (**cur == ' ')
329
(*cur)++;
330
*eof = strlen(*cur) == 0;
331
332
return (true);
333
}
334
335
static int
336
validate_rules(const char *rules)
337
{
338
const char *cursor = rules;
339
bool parsed;
340
struct rule rule;
341
bool eof = false;
342
343
DMB_RULES_LOCK_ASSERT();
344
345
while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
346
/* noop */
347
}
348
349
if (!parsed) {
350
FEEDBACK_RULE(rule, "rule parsing failed");
351
return (EINVAL);
352
}
353
354
return (0);
355
}
356
357
static pfil_return_t
358
dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp,
359
int flags, void *ruleset, void *unused)
360
{
361
struct mbuf *m = *mp;
362
const char *cursor;
363
bool parsed;
364
struct rule rule;
365
bool eof = false;
366
367
DMB_RULES_SLOCK();
368
cursor = V_dmb_rules;
369
while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
370
if (rule.pfil_type == pfil_type &&
371
rule.pfil_dir == (flags & rule.pfil_dir) &&
372
strcmp(rule.ifname, ifp->if_xname) == 0) {
373
m = rule.op(m, &rule);
374
if (m == NULL) {
375
FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
376
"mbuf operation failed");
377
break;
378
}
379
counter_u64_add(V_dmb_hits, 1);
380
}
381
}
382
if (!parsed) {
383
FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
384
"rule parsing failed");
385
m_freem(m);
386
m = NULL;
387
}
388
DMB_RULES_SUNLOCK();
389
390
if (m == NULL) {
391
*mp = NULL;
392
return (PFIL_DROPPED);
393
}
394
if (m != *mp) {
395
*mp = m;
396
return (PFIL_REALLOCED);
397
}
398
399
return (PFIL_PASS);
400
}
401
402
#ifdef INET
403
static pfil_return_t
404
dmb_pfil_inet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
405
void *ruleset, struct inpcb *inp)
406
{
407
return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP4, mp, ifp, flags,
408
ruleset, inp));
409
}
410
#endif
411
412
#ifdef INET6
413
static pfil_return_t
414
dmb_pfil_inet6_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
415
void *ruleset, struct inpcb *inp)
416
{
417
return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP6, mp, ifp, flags,
418
ruleset, inp));
419
}
420
#endif
421
422
static pfil_return_t
423
dmb_pfil_ethernet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
424
void *ruleset, struct inpcb *inp)
425
{
426
return (dmb_pfil_mbuf_chk(PFIL_TYPE_ETHERNET, mp, ifp, flags,
427
ruleset, inp));
428
}
429
430
static void
431
dmb_pfil_init(void)
432
{
433
struct pfil_hook_args pha = {
434
.pa_version = PFIL_VERSION,
435
.pa_modname = "dummymbuf",
436
.pa_flags = PFIL_IN | PFIL_OUT,
437
};
438
439
#ifdef INET
440
pha.pa_type = PFIL_TYPE_IP4;
441
pha.pa_mbuf_chk = dmb_pfil_inet_mbuf_chk;
442
pha.pa_rulname = "inet";
443
V_dmb_pfil_inet_hook = pfil_add_hook(&pha);
444
#endif
445
446
#ifdef INET6
447
pha.pa_type = PFIL_TYPE_IP6;
448
pha.pa_mbuf_chk = dmb_pfil_inet6_mbuf_chk;
449
pha.pa_rulname = "inet6";
450
V_dmb_pfil_inet6_hook = pfil_add_hook(&pha);
451
#endif
452
453
pha.pa_type = PFIL_TYPE_ETHERNET;
454
pha.pa_mbuf_chk = dmb_pfil_ethernet_mbuf_chk;
455
pha.pa_rulname = "ethernet";
456
V_dmb_pfil_ethernet_hook = pfil_add_hook(&pha);
457
}
458
459
static void
460
dmb_pfil_uninit(void)
461
{
462
#ifdef INET
463
pfil_remove_hook(V_dmb_pfil_inet_hook);
464
#endif
465
466
#ifdef INET6
467
pfil_remove_hook(V_dmb_pfil_inet6_hook);
468
#endif
469
470
pfil_remove_hook(V_dmb_pfil_ethernet_hook);
471
}
472
473
static void
474
vnet_dmb_init(const void *unused __unused)
475
{
476
sx_init(&V_dmb_rules_lock, "dummymbuf rules");
477
V_dmb_hits = counter_u64_alloc(M_WAITOK);
478
dmb_pfil_init();
479
}
480
VNET_SYSINIT(vnet_dmb_init, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
481
vnet_dmb_init, NULL);
482
483
static void
484
vnet_dmb_uninit(const void *unused __unused)
485
{
486
dmb_pfil_uninit();
487
counter_u64_free(V_dmb_hits);
488
sx_destroy(&V_dmb_rules_lock);
489
free(V_dmb_rules, M_DUMMYMBUF_RULES);
490
}
491
VNET_SYSUNINIT(vnet_dmb_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
492
vnet_dmb_uninit, NULL);
493
494
static int
495
dmb_modevent(module_t mod __unused, int event, void *arg __unused)
496
{
497
int error = 0;
498
499
switch (event) {
500
case MOD_LOAD:
501
case MOD_UNLOAD:
502
break;
503
default:
504
error = EOPNOTSUPP;
505
break;
506
}
507
508
return (error);
509
}
510
511
static moduledata_t dmb_mod = {
512
"dummymbuf",
513
dmb_modevent,
514
NULL
515
};
516
517
DECLARE_MODULE(dummymbuf, dmb_mod, SI_SUB_PROTO_PFIL, SI_ORDER_ANY);
518
MODULE_VERSION(dummymbuf, 1);
519
520