Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/ng_device.c
101189 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2002 Mark Santcroos <[email protected]>
5
* Copyright (c) 2004-2005 Gleb Smirnoff <[email protected]>
6
* Copyright (c) 2025 Quentin Thébault <[email protected]>
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
*
28
* Netgraph "device" node
29
*
30
* This node presents a /dev/ngd%d device that interfaces to an other
31
* netgraph node.
32
*
33
*/
34
35
#if 0
36
#define DBG do { printf("ng_device: %s\n", __func__); } while (0)
37
#else
38
#define DBG do {} while (0)
39
#endif
40
41
#include <sys/param.h>
42
#include <sys/systm.h>
43
#include <sys/conf.h>
44
#include <sys/epoch.h>
45
#include <sys/fcntl.h>
46
#include <sys/filio.h>
47
#include <sys/ioccom.h>
48
#include <sys/kernel.h>
49
#include <sys/malloc.h>
50
#include <sys/mbuf.h>
51
#include <sys/poll.h>
52
#include <sys/proc.h>
53
#include <sys/queue.h>
54
#include <sys/selinfo.h>
55
#include <sys/socket.h>
56
#include <sys/syslog.h>
57
#include <sys/uio.h>
58
59
#include <net/ethernet.h>
60
#include <net/if.h>
61
#include <net/if_var.h>
62
#include <netinet/in.h>
63
#include <netinet/in_systm.h>
64
#include <netinet/ip.h>
65
66
#include <netgraph/ng_message.h>
67
#include <netgraph/netgraph.h>
68
#include <netgraph/ng_device.h>
69
#include <netgraph/ng_parse.h>
70
71
#define ERROUT(x) do { error = (x); goto done; } while (0)
72
73
/* Netgraph methods */
74
static int ng_device_mod_event(module_t, int, void *);
75
static ng_constructor_t ng_device_constructor;
76
static ng_rcvmsg_t ng_device_rcvmsg;
77
static ng_shutdown_t ng_device_shutdown;
78
static ng_newhook_t ng_device_newhook;
79
static ng_rcvdata_t ng_device_rcvdata;
80
static ng_disconnect_t ng_device_disconnect;
81
82
/* List of commands and how to convert arguments to/from ASCII. */
83
static const struct ng_cmdlist ng_device_cmds[] = {
84
{
85
NGM_DEVICE_COOKIE,
86
NGM_DEVICE_GET_DEVNAME,
87
"getdevname",
88
NULL,
89
&ng_parse_string_type
90
},
91
{
92
NGM_DEVICE_COOKIE,
93
NGM_DEVICE_ETHERALIGN,
94
"etheralign",
95
NULL,
96
NULL
97
},
98
{ 0 }
99
};
100
101
/* Netgraph type */
102
static struct ng_type ngd_typestruct = {
103
.version = NG_ABI_VERSION,
104
.name = NG_DEVICE_NODE_TYPE,
105
.mod_event = ng_device_mod_event,
106
.constructor = ng_device_constructor,
107
.rcvmsg = ng_device_rcvmsg,
108
.shutdown = ng_device_shutdown,
109
.newhook = ng_device_newhook,
110
.rcvdata = ng_device_rcvdata,
111
.disconnect = ng_device_disconnect,
112
.cmdlist = ng_device_cmds,
113
};
114
NETGRAPH_INIT(device, &ngd_typestruct);
115
116
/* per node data */
117
struct ngd_private {
118
struct ifqueue readq;
119
struct ng_node *node;
120
struct ng_hook *hook;
121
struct cdev *ngddev;
122
struct selinfo rsel;
123
struct selinfo wsel;
124
struct mtx ngd_mtx;
125
int unit;
126
int ether_align;
127
uint16_t flags;
128
#define NGDF_OPEN 0x0001
129
#define NGDF_RWAIT 0x0002
130
#define NGDF_DYING 0x0004
131
};
132
typedef struct ngd_private *priv_p;
133
134
/* unit number allocator entity */
135
static struct unrhdr *ngd_unit;
136
137
/* Maximum number of NGD devices */
138
#define MAX_NGD 999
139
140
static d_close_t ngdclose;
141
static d_open_t ngdopen;
142
static d_read_t ngdread;
143
static d_write_t ngdwrite;
144
static d_ioctl_t ngdioctl;
145
static d_poll_t ngdpoll;
146
static d_kqfilter_t ngdkqfilter;
147
148
static int ngd_kqread_event(struct knote *, long);
149
static int ngd_kqwrite_event(struct knote *, long);
150
static void ngd_kqread_detach(struct knote *);
151
static void ngd_kqwrite_detach(struct knote *);
152
153
static const struct filterops ngd_read_filterops = {
154
.f_isfd = 1,
155
.f_detach = ngd_kqread_detach,
156
.f_event = ngd_kqread_event
157
};
158
159
static const struct filterops ngd_write_filterops = {
160
.f_isfd = 1,
161
.f_detach = ngd_kqwrite_detach,
162
.f_event = ngd_kqwrite_event
163
};
164
165
static struct cdevsw ngd_cdevsw = {
166
.d_version = D_VERSION,
167
.d_open = ngdopen,
168
.d_close = ngdclose,
169
.d_read = ngdread,
170
.d_write = ngdwrite,
171
.d_ioctl = ngdioctl,
172
.d_kqfilter = ngdkqfilter,
173
.d_poll = ngdpoll,
174
.d_name = NG_DEVICE_DEVNAME,
175
};
176
177
/*
178
*****************************************************************************
179
* Netgraph methods
180
*****************************************************************************
181
*/
182
183
/*
184
* Handle loading and unloading for this node type.
185
*/
186
static int
187
ng_device_mod_event(module_t mod, int event, void *data)
188
{
189
int error = 0;
190
191
switch (event) {
192
case MOD_LOAD:
193
ngd_unit = new_unrhdr(0, MAX_NGD, NULL);
194
break;
195
case MOD_UNLOAD:
196
delete_unrhdr(ngd_unit);
197
break;
198
default:
199
error = EOPNOTSUPP;
200
break;
201
}
202
return (error);
203
}
204
205
/*
206
* create new node
207
*/
208
static int
209
ng_device_constructor(node_p node)
210
{
211
priv_p priv;
212
213
DBG;
214
215
priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
216
217
/* Allocate unit number */
218
priv->unit = alloc_unr(ngd_unit);
219
220
/* Initialize mutexes and queue */
221
mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
222
mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
223
IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
224
225
knlist_init_mtx(&priv->rsel.si_note, &priv->ngd_mtx);
226
knlist_init_mtx(&priv->wsel.si_note, &priv->ngd_mtx);
227
228
/* Link everything together */
229
NG_NODE_SET_PRIVATE(node, priv);
230
priv->node = node;
231
232
priv->ngddev = make_dev(&ngd_cdevsw, priv->unit, UID_ROOT,
233
GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
234
if (priv->ngddev == NULL) {
235
printf("%s(): make_dev() failed\n", __func__);
236
knlist_destroy(&priv->rsel.si_note);
237
knlist_destroy(&priv->wsel.si_note);
238
mtx_destroy(&priv->ngd_mtx);
239
mtx_destroy(&priv->readq.ifq_mtx);
240
free_unr(ngd_unit, priv->unit);
241
free(priv, M_NETGRAPH);
242
return (EINVAL);
243
}
244
/* XXX: race here? */
245
priv->ngddev->si_drv1 = priv;
246
247
/* Give this node the same name as the device (if possible). */
248
if (ng_name_node(node, devtoname(priv->ngddev)) != 0)
249
log(LOG_WARNING, "%s: can't acquire netgraph name\n",
250
devtoname(priv->ngddev));
251
252
return (0);
253
}
254
255
/*
256
* Process control message.
257
*/
258
259
static int
260
ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
261
{
262
const priv_p priv = NG_NODE_PRIVATE(node);
263
struct ng_mesg *msg;
264
struct ng_mesg *resp = NULL;
265
const char *dn;
266
int error = 0;
267
268
NGI_GET_MSG(item, msg);
269
270
if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
271
switch (msg->header.cmd) {
272
case NGM_DEVICE_GET_DEVNAME:
273
/* XXX: Fix when MAX_NGD us bigger */
274
NG_MKRESPONSE(resp, msg,
275
strlen(NG_DEVICE_DEVNAME) + 4, M_NOWAIT);
276
277
if (resp == NULL)
278
ERROUT(ENOMEM);
279
280
dn = devtoname(priv->ngddev);
281
strlcpy((char *)resp->data, dn, strlen(dn) + 1);
282
break;
283
284
case NGM_DEVICE_ETHERALIGN:
285
/* Use ETHER_ALIGN on arches that require it. */
286
#ifndef __NO_STRICT_ALIGNMENT
287
priv->ether_align = ETHER_ALIGN;
288
#endif
289
break;
290
291
default:
292
error = EINVAL;
293
break;
294
}
295
} else
296
error = EINVAL;
297
298
done:
299
NG_RESPOND_MSG(error, node, item, resp);
300
NG_FREE_MSG(msg);
301
return (error);
302
}
303
304
/*
305
* Accept incoming hook. We support only one hook per node.
306
*/
307
static int
308
ng_device_newhook(node_p node, hook_p hook, const char *name)
309
{
310
priv_p priv = NG_NODE_PRIVATE(node);
311
312
DBG;
313
314
/* We have only one hook per node */
315
if (priv->hook != NULL)
316
return (EISCONN);
317
318
priv->hook = hook;
319
320
return (0);
321
}
322
323
/*
324
* Receive data from hook, write it to device.
325
*/
326
static int
327
ng_device_rcvdata(hook_p hook, item_p item)
328
{
329
priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
330
struct mbuf *m;
331
332
DBG;
333
334
NGI_GET_M(item, m);
335
NG_FREE_ITEM(item);
336
337
IF_LOCK(&priv->readq);
338
if (_IF_QFULL(&priv->readq)) {
339
IF_UNLOCK(&priv->readq);
340
NG_FREE_M(m);
341
return (ENOBUFS);
342
}
343
344
_IF_ENQUEUE(&priv->readq, m);
345
IF_UNLOCK(&priv->readq);
346
mtx_lock(&priv->ngd_mtx);
347
if (priv->flags & NGDF_RWAIT) {
348
priv->flags &= ~NGDF_RWAIT;
349
wakeup(priv);
350
}
351
selwakeup(&priv->rsel);
352
KNOTE_LOCKED(&priv->rsel.si_note, 0);
353
mtx_unlock(&priv->ngd_mtx);
354
355
return (0);
356
}
357
358
/*
359
* Removal of the hook destroys the node.
360
*/
361
static int
362
ng_device_disconnect(hook_p hook)
363
{
364
priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
365
366
DBG;
367
368
mtx_lock(&priv->ngd_mtx);
369
priv->flags |= NGDF_DYING;
370
wakeup(priv);
371
mtx_unlock(&priv->ngd_mtx);
372
373
destroy_dev(priv->ngddev);
374
375
knlist_clear(&priv->rsel.si_note, 0);
376
knlist_clear(&priv->wsel.si_note, 0);
377
knlist_destroy(&priv->rsel.si_note);
378
knlist_destroy(&priv->wsel.si_note);
379
mtx_destroy(&priv->ngd_mtx);
380
381
seldrain(&priv->rsel);
382
seldrain(&priv->wsel);
383
384
IF_DRAIN(&priv->readq);
385
mtx_destroy(&(priv)->readq.ifq_mtx);
386
387
free_unr(ngd_unit, priv->unit);
388
389
free(priv, M_NETGRAPH);
390
391
ng_rmnode_self(NG_HOOK_NODE(hook));
392
393
return (0);
394
}
395
396
/*
397
* Node shutdown. Everything is already done in disconnect method.
398
*/
399
static int
400
ng_device_shutdown(node_p node)
401
{
402
NG_NODE_UNREF(node);
403
return (0);
404
}
405
406
/*
407
*****************************************************************************
408
* Device methods
409
*****************************************************************************
410
*/
411
412
/*
413
* the device is opened
414
*/
415
static int
416
ngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
417
{
418
priv_p priv = (priv_p)dev->si_drv1;
419
420
DBG;
421
422
mtx_lock(&priv->ngd_mtx);
423
priv->flags |= NGDF_OPEN;
424
mtx_unlock(&priv->ngd_mtx);
425
426
return (0);
427
}
428
429
/*
430
* the device is closed
431
*/
432
static int
433
ngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
434
{
435
priv_p priv = (priv_p)dev->si_drv1;
436
437
DBG;
438
mtx_lock(&priv->ngd_mtx);
439
priv->flags &= ~NGDF_OPEN;
440
mtx_unlock(&priv->ngd_mtx);
441
442
return (0);
443
}
444
445
/*
446
* Process IOCTLs
447
*
448
* At this stage we only return success on FIONBIO to allow setting the device
449
* as non-blocking.
450
*
451
*/
452
static int
453
ngdioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
454
struct thread *td)
455
{
456
int error;
457
458
switch (cmd) {
459
case FIONBIO:
460
error = 0;
461
break;
462
case FIOASYNC:
463
if (*(int *)data != 0)
464
error = EINVAL;
465
else
466
error = 0;
467
break;
468
default:
469
error = ENOTTY;
470
}
471
472
return (error);
473
}
474
475
#if 0 /*
476
* The ioctl is transformed into netgraph control message.
477
* We do not process them, yet.
478
*/
479
/*
480
* process ioctl
481
*
482
* they are translated into netgraph messages and passed on
483
*
484
*/
485
static int
486
ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
487
struct thread *td)
488
{
489
struct ngd_softc *sc = &ngd_softc;
490
struct ngd_connection *connection = NULL;
491
struct ngd_connection *tmp;
492
int error = 0;
493
struct ng_mesg *msg;
494
struct ngd_param_s *datap;
495
496
DBG;
497
498
NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
499
M_NOWAIT);
500
if (msg == NULL) {
501
printf("%s(): msg == NULL\n", __func__);
502
goto nomsg;
503
}
504
505
/* pass the ioctl data into the ->data area */
506
datap = (struct ngd_param_s *)msg->data;
507
datap->p = addr;
508
509
NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
510
if (error)
511
printf("%s(): NG_SEND_MSG_HOOK error: %d\n", __func__, error);
512
513
nomsg:
514
515
return (0);
516
}
517
#endif /* if 0 */
518
519
/*
520
* This function is called when a read(2) is done to our device.
521
* We process one mbuf from queue.
522
*/
523
static int
524
ngdread(struct cdev *dev, struct uio *uio, int flag)
525
{
526
priv_p priv = (priv_p)dev->si_drv1;
527
struct mbuf *m;
528
int len, error = 0;
529
530
DBG;
531
532
/* get an mbuf */
533
do {
534
IF_DEQUEUE(&priv->readq, m);
535
if (m == NULL) {
536
if (flag & O_NONBLOCK)
537
return (EWOULDBLOCK);
538
mtx_lock(&priv->ngd_mtx);
539
priv->flags |= NGDF_RWAIT;
540
if (priv->flags & NGDF_DYING) {
541
mtx_unlock(&priv->ngd_mtx);
542
error = ENXIO;
543
} else
544
error = mtx_sleep(priv, &priv->ngd_mtx,
545
PDROP | PCATCH, "ngdread", 0);
546
if (error != 0)
547
return (error);
548
}
549
} while (m == NULL);
550
551
while (m && uio->uio_resid > 0 && error == 0) {
552
len = MIN(uio->uio_resid, m->m_len);
553
if (len != 0)
554
error = uiomove(mtod(m, void *), len, uio);
555
m = m_free(m);
556
}
557
558
if (m)
559
m_freem(m);
560
561
return (error);
562
}
563
564
/*
565
* This function is called when our device is written to.
566
* We read the data from userland into mbuf chain and pass it to the remote
567
* hook.
568
*/
569
static int
570
ngdwrite(struct cdev *dev, struct uio *uio, int flag)
571
{
572
struct epoch_tracker et;
573
priv_p priv = (priv_p)dev->si_drv1;
574
struct mbuf *m;
575
int error = 0;
576
577
DBG;
578
579
if (uio->uio_resid == 0)
580
return (0);
581
582
if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
583
return (EIO);
584
585
m = m_uiotombuf(uio, M_NOWAIT, 0, priv->ether_align, M_PKTHDR);
586
if (m == NULL)
587
return (ENOBUFS);
588
589
/* Setting VNET is required if connecting to a ng_bridge. */
590
CURVNET_SET(priv->node->nd_vnet);
591
NET_EPOCH_ENTER(et);
592
NG_SEND_DATA_ONLY(error, priv->hook, m);
593
NET_EPOCH_EXIT(et);
594
CURVNET_RESTORE();
595
596
return (error);
597
}
598
599
/*
600
* we are being polled/selected
601
* check if there is data available for read
602
*/
603
static int
604
ngdpoll(struct cdev *dev, int events, struct thread *td)
605
{
606
priv_p priv = (priv_p)dev->si_drv1;
607
int revents = 0;
608
609
if (events & (POLLIN | POLLRDNORM) &&
610
!IFQ_IS_EMPTY(&priv->readq))
611
revents |= events & (POLLIN | POLLRDNORM);
612
613
return (revents);
614
}
615
616
static void
617
ngd_kqread_detach(struct knote *kn)
618
{
619
priv_p priv = (priv_p)kn->kn_hook;
620
621
knlist_remove(&priv->rsel.si_note, kn, 0);
622
}
623
624
static int
625
ngd_kqread_event(struct knote *kn, long hint)
626
{
627
priv_p priv = (priv_p)kn->kn_hook;
628
struct mbuf *m;
629
630
IFQ_LOCK(&priv->readq);
631
if (IFQ_IS_EMPTY(&priv->readq)) {
632
kn->kn_data = 0;
633
} else {
634
/*
635
* Since the queue does not store the total number of bytes that
636
* could be read across all packets and we do not want to
637
* traverse the whole queue, we only report the number of bytes
638
* for the first packet in the queue.
639
*/
640
IF_POLL(&priv->readq, m);
641
kn->kn_data = m->m_len;
642
}
643
IFQ_UNLOCK(&priv->readq);
644
645
return (kn->kn_data > 0);
646
}
647
648
static void
649
ngd_kqwrite_detach(struct knote *kn)
650
{
651
priv_p priv = (priv_p)kn->kn_hook;
652
653
knlist_remove(&priv->wsel.si_note, kn, 0);
654
}
655
656
static int
657
ngd_kqwrite_event(struct knote *kn, long hint)
658
{
659
kn->kn_data = IP_MAXPACKET;
660
661
return (1);
662
}
663
664
static int
665
ngdkqfilter(struct cdev *dev, struct knote *kn)
666
{
667
priv_p priv = (priv_p)dev->si_drv1;
668
669
switch (kn->kn_filter) {
670
case EVFILT_READ:
671
kn->kn_fop = &ngd_read_filterops;
672
kn->kn_hook = priv;
673
knlist_add(&priv->rsel.si_note, kn, 0);
674
return (0);
675
case EVFILT_WRITE:
676
kn->kn_fop = &ngd_write_filterops;
677
kn->kn_hook = priv;
678
knlist_add(&priv->wsel.si_note, kn, 0);
679
return (0);
680
default:
681
return (EINVAL);
682
}
683
}
684
685