ng_device.c revision 137525
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: head/sys/netgraph/ng_device.c 137525 2004-11-10 11:18:05Z glebius $
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 */
66static ng_constructor_t	ng_device_constructor;
67static ng_rcvmsg_t	ng_device_rcvmsg;
68static ng_shutdown_t	ng_device_shutdown;
69static ng_newhook_t	ng_device_newhook;
70static ng_rcvdata_t	ng_device_rcvdata;
71static ng_disconnect_t	ng_device_disconnect;
72
73/* Netgraph type */
74static struct ng_type ngd_typestruct = {
75	.version =	NG_ABI_VERSION,
76	.name =		NG_DEVICE_NODE_TYPE,
77	.constructor =	ng_device_constructor,
78	.rcvmsg	=	ng_device_rcvmsg,
79	.shutdown = 	ng_device_shutdown,
80	.newhook =	ng_device_newhook,
81	.rcvdata =	ng_device_rcvdata,
82	.disconnect =	ng_device_disconnect,
83};
84NETGRAPH_INIT(device, &ngd_typestruct);
85
86/* per node data */
87struct ngd_private {
88	struct	ifqueue	readq;
89	SLIST_ENTRY(ngd_private) links;
90	struct	ng_node	*node;
91	struct	ng_hook	*hook;
92	struct	cdev	*ngddev;
93	struct	mtx	ngd_mtx;
94	int 		unit;
95	uint16_t	flags;
96#define	NGDF_OPEN	0x0001
97#define	NGDF_RWAIT	0x0002
98};
99typedef struct ngd_private *priv_p;
100
101/* List of all active nodes and mutex to protect it */
102static SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes);
103static struct mtx	ng_device_mtx;
104MTX_SYSINIT(ng_device, &ng_device_mtx, "ng_device", MTX_DEF);
105
106/* Maximum number of NGD devices */
107#define MAX_NGD	25	/* should be more than enough for now */
108
109static d_close_t ngdclose;
110static d_open_t ngdopen;
111static d_read_t ngdread;
112static d_write_t ngdwrite;
113#if 0
114static d_ioctl_t ngdioctl;
115#endif
116static d_poll_t ngdpoll;
117
118static struct cdevsw ngd_cdevsw = {
119	.d_version =	D_VERSION,
120	.d_open =	ngdopen,
121	.d_close =	ngdclose,
122	.d_read =	ngdread,
123	.d_write =	ngdwrite,
124#if 0
125	.d_ioctl =	ngdioctl,
126#endif
127	.d_poll =	ngdpoll,
128	.d_name =	NG_DEVICE_DEVNAME,
129};
130
131/* Helper functions */
132static int get_free_unit(void);
133
134/******************************************************************************
135 *  Netgraph methods
136 ******************************************************************************/
137
138/*
139 * create new node
140 */
141static int
142ng_device_constructor(node_p node)
143{
144	priv_p	priv;
145
146	DBG;
147
148	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
149	if (priv == NULL)
150		return (ENOMEM);
151
152	mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
153	mtx_lock(&priv->ngd_mtx);
154
155	mtx_lock(&ng_device_mtx);
156
157	priv->unit = get_free_unit();
158	if(priv->unit < 0) {
159		printf("%s: No free unit found by get_free_unit(), "
160				"increase MAX_NGD\n",__func__);
161		mtx_unlock(&ng_device_mtx);
162		mtx_destroy(&priv->ngd_mtx);
163		FREE(priv, M_NETGRAPH);
164		return(EINVAL);
165	}
166
167	priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT,
168	    GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
169	if(priv->ngddev == NULL) {
170		printf("%s(): make_dev() failed\n",__func__);
171		mtx_unlock(&ng_device_mtx);
172		mtx_destroy(&priv->ngd_mtx);
173		FREE(priv, M_NETGRAPH);
174		return(EINVAL);
175	}
176
177	SLIST_INSERT_HEAD(&ngd_nodes, priv, links);
178
179	mtx_unlock(&ng_device_mtx);
180
181	mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
182	IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
183
184	/* Link everything together */
185	NG_NODE_SET_PRIVATE(node, priv);
186	priv->node = node;
187	priv->ngddev->si_drv1 = priv;
188
189	mtx_unlock(&priv->ngd_mtx);
190
191	return(0);
192}
193
194/*
195 * Process control message.
196 */
197
198static int
199ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
200{
201	const priv_p priv = NG_NODE_PRIVATE(node);
202	struct ng_mesg *msg;
203	struct ng_mesg *resp = NULL;
204	int error = 0;
205
206	NGI_GET_MSG(item, msg);
207
208	if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
209		switch (msg->header.cmd) {
210		case NGM_DEVICE_GET_DEVNAME:
211			/* XXX: Fix when NGD_MAX us bigger */
212			NG_MKRESPONSE(resp, msg,
213			    strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT);
214
215			if (resp == NULL)
216				ERROUT(ENOMEM);
217
218			strlcpy((char *)resp->data, priv->ngddev->si_name,
219			    strlen(priv->ngddev->si_name) + 1);
220			break;
221
222		default:
223			error = EINVAL;
224			break;
225		}
226	} else
227		error = EINVAL;
228
229done:
230	NG_RESPOND_MSG(error, node, item, resp);
231	NG_FREE_MSG(msg);
232	return (error);
233}
234
235/*
236 * Accept incoming hook. We support only one hook per node.
237 */
238static int
239ng_device_newhook(node_p node, hook_p hook, const char *name)
240{
241	priv_p priv = NG_NODE_PRIVATE(node);
242
243	DBG;
244
245	/* We have only one hook per node */
246	if (priv->hook != NULL)
247		return (EISCONN);
248
249	priv->hook = hook;
250
251	return(0);
252}
253
254/*
255 * Receive data from hook, write it to device.
256 */
257static int
258ng_device_rcvdata(hook_p hook, item_p item)
259{
260	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
261	struct mbuf *m;
262
263	DBG;
264
265	NGI_GET_M(item, m);
266	NG_FREE_ITEM(item);
267
268	IF_LOCK(&priv->readq);
269	if (_IF_QFULL(&priv->readq)) {
270		_IF_DROP(&priv->readq);
271		IF_UNLOCK(&priv->readq);
272		NG_FREE_M(m);
273		return (ENOBUFS);
274	}
275
276	_IF_ENQUEUE(&priv->readq, m);
277	IF_UNLOCK(&priv->readq);
278	mtx_lock(&priv->ngd_mtx);
279	if (priv->flags & NGDF_RWAIT) {
280		priv->flags &= ~NGDF_RWAIT;
281		wakeup(priv);
282	}
283	mtx_unlock(&priv->ngd_mtx);
284
285	return(0);
286}
287
288/*
289 * Removal of the hook destroys the node.
290 */
291static int
292ng_device_disconnect(hook_p hook)
293{
294	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
295
296	DBG;
297
298	destroy_dev(priv->ngddev);
299	mtx_destroy(&priv->ngd_mtx);
300
301	mtx_lock(&ng_device_mtx);
302	SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links);
303	mtx_unlock(&ng_device_mtx);
304
305	IF_DRAIN(&priv->readq);
306	mtx_destroy(&(priv)->readq.ifq_mtx);
307
308	FREE(priv, M_NETGRAPH);
309
310	ng_rmnode_self(NG_HOOK_NODE(hook));
311
312	return(0);
313}
314
315/*
316 * Node shutdown. Everything is already done in disconnect method.
317 */
318static int
319ng_device_shutdown(node_p node)
320{
321	NG_NODE_UNREF(node);
322	return (0);
323}
324
325/******************************************************************************
326 *  Device methods
327 ******************************************************************************/
328
329/*
330 * the device is opened
331 */
332static int
333ngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
334{
335	priv_p	priv = (priv_p )dev->si_drv1;
336
337	DBG;
338
339	mtx_lock(&priv->ngd_mtx);
340	priv->flags |= NGDF_OPEN;
341	mtx_unlock(&priv->ngd_mtx);
342
343	return(0);
344}
345
346/*
347 * the device is closed
348 */
349static int
350ngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
351{
352	priv_p	priv = (priv_p )dev->si_drv1;
353
354	DBG;
355	mtx_lock(&priv->ngd_mtx);
356	priv->flags &= ~NGDF_OPEN;
357	mtx_unlock(&priv->ngd_mtx);
358
359	return(0);
360}
361
362#if 0	/*
363	 * The ioctl is transformed into netgraph control message.
364	 * We do not process them, yet.
365	 */
366/*
367 * process ioctl
368 *
369 * they are translated into netgraph messages and passed on
370 *
371 */
372static int
373ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
374{
375	struct ngd_softc *sc = &ngd_softc;
376	struct ngd_connection * connection = NULL;
377	struct ngd_connection * tmp;
378	int error = 0;
379	struct ng_mesg *msg;
380	struct ngd_param_s * datap;
381
382	DBG;
383
384	SLIST_FOREACH(tmp,&sc->head,links) {
385		if(tmp->ngddev == dev) {
386			connection = tmp;
387		}
388	}
389	if(connection == NULL) {
390		printf("%s(): connection is still NULL, no dev found\n",__func__);
391		return(-1);
392	}
393
394	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
395			M_NOWAIT);
396	if (msg == NULL) {
397		printf("%s(): msg == NULL\n",__func__);
398		goto nomsg;
399	}
400
401	/* pass the ioctl data into the ->data area */
402	datap = (struct ngd_param_s *)msg->data;
403	datap->p = addr;
404
405	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
406	if(error)
407		printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
408
409nomsg:
410
411	return(0);
412}
413#endif /* if 0 */
414
415/*
416 * This function is called when a read(2) is done to our device.
417 * We process one mbuf from queue.
418 */
419static int
420ngdread(struct cdev *dev, struct uio *uio, int flag)
421{
422	priv_p	priv = (priv_p )dev->si_drv1;
423	struct mbuf *m;
424	int len, error = 0;
425
426	DBG;
427
428	/* get an mbuf */
429	do {
430		IF_DEQUEUE(&priv->readq, m);
431		if (m == NULL) {
432			if (flag & IO_NDELAY)
433				return (EWOULDBLOCK);
434			mtx_lock(&priv->ngd_mtx);
435			priv->flags |= NGDF_RWAIT;
436			mtx_unlock(&priv->ngd_mtx);
437			if ((error = tsleep(priv, PCATCH | (PZERO + 1),
438			    "ngdread", 0)) != 0)
439				return (error);
440		}
441	} while (m == NULL);
442
443	while (m && uio->uio_resid > 0 && error == 0) {
444		len = MIN(uio->uio_resid, m->m_len);
445		if (len != 0)
446			error = uiomove(mtod(m, void *), len, uio);
447		m = m_free(m);
448	}
449
450	if (m)
451		m_freem(m);
452
453	return (error);
454}
455
456
457/*
458 * This function is called when our device is written to.
459 * We read the data from userland into mbuf chain and pass it to the remote hook.
460 *
461 */
462static int
463ngdwrite(struct cdev *dev, struct uio *uio, int flag)
464{
465	priv_p	priv = (priv_p )dev->si_drv1;
466	struct mbuf *m;
467	int error = 0;
468
469	DBG;
470
471	if (uio->uio_resid == 0)
472		return (0);
473
474	if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
475		return (EIO);
476
477	if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL)
478		return (ENOBUFS);
479
480	NG_SEND_DATA_ONLY(error, priv->hook, m);
481
482	return (error);
483}
484
485/*
486 * we are being polled/selected
487 * check if there is data available for read
488 */
489static int
490ngdpoll(struct cdev *dev, int events, struct thread *td)
491{
492	priv_p	priv = (priv_p )dev->si_drv1;
493	int revents = 0;
494
495	if (events & (POLLIN | POLLRDNORM) &&
496	    !IFQ_IS_EMPTY(&priv->readq))
497		revents |= events & (POLLIN | POLLRDNORM);
498
499	return (revents);
500}
501
502/******************************************************************************
503 *  Helper subroutines
504 ******************************************************************************/
505
506static int
507get_free_unit()
508{
509	struct ngd_private *priv = NULL;
510	int n = 0;
511	int unit = -1;
512
513	DBG;
514
515	mtx_assert(&ng_device_mtx, MA_OWNED);
516
517	/* When there is no list yet, the first device unit is always 0. */
518	if SLIST_EMPTY(&ngd_nodes)
519		return(0);
520
521	/* Just do a brute force loop to find the first free unit that is
522	 * smaller than MAX_NGD.
523	 * Set MAX_NGD to a large value, doesn't impact performance.
524	 */
525	for(n = 0; n<MAX_NGD && unit == -1; n++) {
526		SLIST_FOREACH(priv, &ngd_nodes, links) {
527
528			if(priv->unit == n) {
529				unit = -1;
530				break;
531			}
532			unit = n;
533		}
534	}
535
536	return (unit);
537}
538