xref: /freebsd/sys/netgraph/ng_device.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*-
2  * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
3  * Copyright (c) 2004 Gleb Smirnoff <glebius@FreeBSD.org>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * Netgraph "device" node
26  *
27  * This node presents a /dev/ngd%d device that interfaces to an other
28  * netgraph node.
29  *
30  * $FreeBSD$
31  *
32  */
33 
34 #if 0
35 #define	DBG do { printf("ng_device: %s\n", __func__ ); } while (0)
36 #else
37 #define	DBG do {} while (0)
38 #endif
39 
40 #include <sys/param.h>
41 #include <sys/conf.h>
42 #include <sys/ioccom.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/mbuf.h>
46 #include <sys/poll.h>
47 #include <sys/queue.h>
48 #include <sys/socket.h>
49 #include <sys/systm.h>
50 #include <sys/uio.h>
51 #include <sys/vnode.h>
52 
53 #include <net/if.h>
54 #include <net/if_var.h>
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/ip.h>
58 
59 #include <netgraph/ng_message.h>
60 #include <netgraph/netgraph.h>
61 #include <netgraph/ng_device.h>
62 
63 #define	ERROUT(x) do { error = (x); goto done; } while (0)
64 
65 /* Netgraph methods */
66 static int		ng_device_mod_event(module_t, int, void *);
67 static ng_constructor_t	ng_device_constructor;
68 static ng_rcvmsg_t	ng_device_rcvmsg;
69 static ng_shutdown_t	ng_device_shutdown;
70 static ng_newhook_t	ng_device_newhook;
71 static ng_rcvdata_t	ng_device_rcvdata;
72 static ng_disconnect_t	ng_device_disconnect;
73 
74 /* Netgraph type */
75 static struct ng_type ngd_typestruct = {
76 	.version =	NG_ABI_VERSION,
77 	.name =		NG_DEVICE_NODE_TYPE,
78 	.mod_event =	ng_device_mod_event,
79 	.constructor =	ng_device_constructor,
80 	.rcvmsg	=	ng_device_rcvmsg,
81 	.shutdown = 	ng_device_shutdown,
82 	.newhook =	ng_device_newhook,
83 	.rcvdata =	ng_device_rcvdata,
84 	.disconnect =	ng_device_disconnect,
85 };
86 NETGRAPH_INIT(device, &ngd_typestruct);
87 
88 /* per node data */
89 struct ngd_private {
90 	struct	ifqueue	readq;
91 	SLIST_ENTRY(ngd_private) links;
92 	struct	ng_node	*node;
93 	struct	ng_hook	*hook;
94 	struct	cdev	*ngddev;
95 	struct	mtx	ngd_mtx;
96 	int 		unit;
97 	uint16_t	flags;
98 #define	NGDF_OPEN	0x0001
99 #define	NGDF_RWAIT	0x0002
100 };
101 typedef struct ngd_private *priv_p;
102 
103 /* List of all active nodes and mutex to protect it */
104 static SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes);
105 static struct mtx	ng_device_mtx;
106 
107 /* Maximum number of NGD devices */
108 #define MAX_NGD	25	/* should be more than enough for now */
109 
110 static d_close_t ngdclose;
111 static d_open_t ngdopen;
112 static d_read_t ngdread;
113 static d_write_t ngdwrite;
114 #if 0
115 static d_ioctl_t ngdioctl;
116 #endif
117 static d_poll_t ngdpoll;
118 
119 static struct cdevsw ngd_cdevsw = {
120 	.d_version =	D_VERSION,
121 	.d_open =	ngdopen,
122 	.d_close =	ngdclose,
123 	.d_read =	ngdread,
124 	.d_write =	ngdwrite,
125 #if 0
126 	.d_ioctl =	ngdioctl,
127 #endif
128 	.d_poll =	ngdpoll,
129 	.d_name =	NG_DEVICE_DEVNAME,
130 };
131 
132 /* Helper functions */
133 static int get_free_unit(void);
134 
135 /******************************************************************************
136  *  Netgraph methods
137  ******************************************************************************/
138 
139 /*
140  * create new node
141  */
142 static int
143 ng_device_constructor(node_p node)
144 {
145 	priv_p	priv;
146 
147 	DBG;
148 
149 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
150 	if (priv == NULL)
151 		return (ENOMEM);
152 
153 	mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
154 	mtx_lock(&priv->ngd_mtx);
155 
156 	mtx_lock(&ng_device_mtx);
157 
158 	priv->unit = get_free_unit();
159 	if(priv->unit < 0) {
160 		printf("%s: No free unit found by get_free_unit(), "
161 				"increase MAX_NGD\n",__func__);
162 		mtx_unlock(&ng_device_mtx);
163 		mtx_destroy(&priv->ngd_mtx);
164 		FREE(priv, M_NETGRAPH);
165 		return(EINVAL);
166 	}
167 
168 	priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT,
169 	    GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
170 	if(priv->ngddev == NULL) {
171 		printf("%s(): make_dev() failed\n",__func__);
172 		mtx_unlock(&ng_device_mtx);
173 		mtx_destroy(&priv->ngd_mtx);
174 		FREE(priv, M_NETGRAPH);
175 		return(EINVAL);
176 	}
177 
178 	SLIST_INSERT_HEAD(&ngd_nodes, priv, links);
179 
180 	mtx_unlock(&ng_device_mtx);
181 
182 	mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
183 	IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
184 
185 	/* Link everything together */
186 	NG_NODE_SET_PRIVATE(node, priv);
187 	priv->node = node;
188 	priv->ngddev->si_drv1 = priv;
189 
190 	mtx_unlock(&priv->ngd_mtx);
191 
192 	return(0);
193 }
194 
195 /*
196  * Process control message.
197  */
198 
199 static int
200 ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
201 {
202 	const priv_p priv = NG_NODE_PRIVATE(node);
203 	struct ng_mesg *msg;
204 	struct ng_mesg *resp = NULL;
205 	int error = 0;
206 
207 	NGI_GET_MSG(item, msg);
208 
209 	if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
210 		switch (msg->header.cmd) {
211 		case NGM_DEVICE_GET_DEVNAME:
212 			/* XXX: Fix when NGD_MAX us bigger */
213 			NG_MKRESPONSE(resp, msg,
214 			    strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT);
215 
216 			if (resp == NULL)
217 				ERROUT(ENOMEM);
218 
219 			strlcpy((char *)resp->data, priv->ngddev->si_name,
220 			    strlen(priv->ngddev->si_name) + 1);
221 			break;
222 
223 		default:
224 			error = EINVAL;
225 			break;
226 		}
227 	} else
228 		error = EINVAL;
229 
230 done:
231 	NG_RESPOND_MSG(error, node, item, resp);
232 	NG_FREE_MSG(msg);
233 	return (error);
234 }
235 
236 /*
237  * Accept incoming hook. We support only one hook per node.
238  */
239 static int
240 ng_device_newhook(node_p node, hook_p hook, const char *name)
241 {
242 	priv_p priv = NG_NODE_PRIVATE(node);
243 
244 	DBG;
245 
246 	/* We have only one hook per node */
247 	if (priv->hook != NULL)
248 		return (EISCONN);
249 
250 	priv->hook = hook;
251 
252 	return(0);
253 }
254 
255 /*
256  * Receive data from hook, write it to device.
257  */
258 static int
259 ng_device_rcvdata(hook_p hook, item_p item)
260 {
261 	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
262 	struct mbuf *m;
263 
264 	DBG;
265 
266 	NGI_GET_M(item, m);
267 	NG_FREE_ITEM(item);
268 
269 	IF_LOCK(&priv->readq);
270 	if (_IF_QFULL(&priv->readq)) {
271 		_IF_DROP(&priv->readq);
272 		IF_UNLOCK(&priv->readq);
273 		NG_FREE_M(m);
274 		return (ENOBUFS);
275 	}
276 
277 	_IF_ENQUEUE(&priv->readq, m);
278 	IF_UNLOCK(&priv->readq);
279 	mtx_lock(&priv->ngd_mtx);
280 	if (priv->flags & NGDF_RWAIT) {
281 		priv->flags &= ~NGDF_RWAIT;
282 		wakeup(priv);
283 	}
284 	mtx_unlock(&priv->ngd_mtx);
285 
286 	return(0);
287 }
288 
289 /*
290  * Removal of the hook destroys the node.
291  */
292 static int
293 ng_device_disconnect(hook_p hook)
294 {
295 	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
296 
297 	DBG;
298 
299 	destroy_dev(priv->ngddev);
300 	mtx_destroy(&priv->ngd_mtx);
301 
302 	mtx_lock(&ng_device_mtx);
303 	SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links);
304 	mtx_unlock(&ng_device_mtx);
305 
306 	IF_DRAIN(&priv->readq);
307 	mtx_destroy(&(priv)->readq.ifq_mtx);
308 
309 	FREE(priv, M_NETGRAPH);
310 
311 	ng_rmnode_self(NG_HOOK_NODE(hook));
312 
313 	return(0);
314 }
315 
316 /*
317  * Node shutdown. Everything is already done in disconnect method.
318  */
319 static int
320 ng_device_shutdown(node_p node)
321 {
322 	NG_NODE_UNREF(node);
323 	return (0);
324 }
325 
326 /******************************************************************************
327  *  Device methods
328  ******************************************************************************/
329 
330 /*
331  * the device is opened
332  */
333 static int
334 ngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
335 {
336 	priv_p	priv = (priv_p )dev->si_drv1;
337 
338 	DBG;
339 
340 	mtx_lock(&priv->ngd_mtx);
341 	priv->flags |= NGDF_OPEN;
342 	mtx_unlock(&priv->ngd_mtx);
343 
344 	return(0);
345 }
346 
347 /*
348  * the device is closed
349  */
350 static int
351 ngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
352 {
353 	priv_p	priv = (priv_p )dev->si_drv1;
354 
355 	DBG;
356 	mtx_lock(&priv->ngd_mtx);
357 	priv->flags &= ~NGDF_OPEN;
358 	mtx_unlock(&priv->ngd_mtx);
359 
360 	return(0);
361 }
362 
363 #if 0	/*
364 	 * The ioctl is transformed into netgraph control message.
365 	 * We do not process them, yet.
366 	 */
367 /*
368  * process ioctl
369  *
370  * they are translated into netgraph messages and passed on
371  *
372  */
373 static int
374 ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
375 {
376 	struct ngd_softc *sc = &ngd_softc;
377 	struct ngd_connection * connection = NULL;
378 	struct ngd_connection * tmp;
379 	int error = 0;
380 	struct ng_mesg *msg;
381 	struct ngd_param_s * datap;
382 
383 	DBG;
384 
385 	SLIST_FOREACH(tmp,&sc->head,links) {
386 		if(tmp->ngddev == dev) {
387 			connection = tmp;
388 		}
389 	}
390 	if(connection == NULL) {
391 		printf("%s(): connection is still NULL, no dev found\n",__func__);
392 		return(-1);
393 	}
394 
395 	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
396 			M_NOWAIT);
397 	if (msg == NULL) {
398 		printf("%s(): msg == NULL\n",__func__);
399 		goto nomsg;
400 	}
401 
402 	/* pass the ioctl data into the ->data area */
403 	datap = (struct ngd_param_s *)msg->data;
404 	datap->p = addr;
405 
406 	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
407 	if(error)
408 		printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
409 
410 nomsg:
411 
412 	return(0);
413 }
414 #endif /* if 0 */
415 
416 /*
417  * This function is called when a read(2) is done to our device.
418  * We process one mbuf from queue.
419  */
420 static int
421 ngdread(struct cdev *dev, struct uio *uio, int flag)
422 {
423 	priv_p	priv = (priv_p )dev->si_drv1;
424 	struct mbuf *m;
425 	int len, error = 0;
426 
427 	DBG;
428 
429 	/* get an mbuf */
430 	do {
431 		IF_DEQUEUE(&priv->readq, m);
432 		if (m == NULL) {
433 			if (flag & IO_NDELAY)
434 				return (EWOULDBLOCK);
435 			mtx_lock(&priv->ngd_mtx);
436 			priv->flags |= NGDF_RWAIT;
437 			if ((error = msleep(priv, &priv->ngd_mtx,
438 			    PDROP | PCATCH | (PZERO + 1),
439 			    "ngdread", 0)) != 0)
440 				return (error);
441 		}
442 	} while (m == NULL);
443 
444 	while (m && uio->uio_resid > 0 && error == 0) {
445 		len = MIN(uio->uio_resid, m->m_len);
446 		if (len != 0)
447 			error = uiomove(mtod(m, void *), len, uio);
448 		m = m_free(m);
449 	}
450 
451 	if (m)
452 		m_freem(m);
453 
454 	return (error);
455 }
456 
457 
458 /*
459  * This function is called when our device is written to.
460  * We read the data from userland into mbuf chain and pass it to the remote hook.
461  *
462  */
463 static int
464 ngdwrite(struct cdev *dev, struct uio *uio, int flag)
465 {
466 	priv_p	priv = (priv_p )dev->si_drv1;
467 	struct mbuf *m;
468 	int error = 0;
469 
470 	DBG;
471 
472 	if (uio->uio_resid == 0)
473 		return (0);
474 
475 	if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
476 		return (EIO);
477 
478 	if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL)
479 		return (ENOBUFS);
480 
481 	NG_SEND_DATA_ONLY(error, priv->hook, m);
482 
483 	return (error);
484 }
485 
486 /*
487  * we are being polled/selected
488  * check if there is data available for read
489  */
490 static int
491 ngdpoll(struct cdev *dev, int events, struct thread *td)
492 {
493 	priv_p	priv = (priv_p )dev->si_drv1;
494 	int revents = 0;
495 
496 	if (events & (POLLIN | POLLRDNORM) &&
497 	    !IFQ_IS_EMPTY(&priv->readq))
498 		revents |= events & (POLLIN | POLLRDNORM);
499 
500 	return (revents);
501 }
502 
503 /******************************************************************************
504  *  Helper subroutines
505  ******************************************************************************/
506 
507 static int
508 get_free_unit()
509 {
510 	struct ngd_private *priv = NULL;
511 	int n = 0;
512 	int unit = -1;
513 
514 	DBG;
515 
516 	mtx_assert(&ng_device_mtx, MA_OWNED);
517 
518 	/* When there is no list yet, the first device unit is always 0. */
519 	if SLIST_EMPTY(&ngd_nodes)
520 		return(0);
521 
522 	/* Just do a brute force loop to find the first free unit that is
523 	 * smaller than MAX_NGD.
524 	 * Set MAX_NGD to a large value, doesn't impact performance.
525 	 */
526 	for(n = 0; n<MAX_NGD && unit == -1; n++) {
527 		SLIST_FOREACH(priv, &ngd_nodes, links) {
528 
529 			if(priv->unit == n) {
530 				unit = -1;
531 				break;
532 			}
533 			unit = n;
534 		}
535 	}
536 
537 	return (unit);
538 }
539 
540 /*
541  * Handle loading and unloading for this node type.
542  */
543 static int
544 ng_device_mod_event(module_t mod, int event, void *data)
545 {
546 	int error = 0;
547 
548 	switch (event) {
549 	case MOD_LOAD:
550 		mtx_init(&ng_device_mtx, "ng_device", NULL, MTX_DEF);
551 		break;
552 	case MOD_UNLOAD:
553 		mtx_destroy(&ng_device_mtx);
554 		break;
555 	default:
556 		error = EOPNOTSUPP;
557 		break;
558 	}
559 	return (error);
560 }
561