xref: /freebsd/sys/netgraph/ng_device.c (revision df21a004be237a1dccd03c7b47254625eea62fa9)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
5  * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
6  * Copyright (c) 2025 Quentin Thébault <quentin.thebault@defenso.fr>
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