1139823Simp/*-
298402Sjulian * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
3143593Sglebius * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
498402Sjulian *
598402Sjulian * Redistribution and use in source and binary forms, with or without
698402Sjulian * modification, are permitted provided that the following conditions
798402Sjulian * are met:
898402Sjulian * 1. Redistributions of source code must retain the above copyright
998402Sjulian *    notice, this list of conditions and the following disclaimer.
1098402Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1198402Sjulian *    notice, this list of conditions and the following disclaimer in the
1298402Sjulian *    documentation and/or other materials provided with the distribution.
1398402Sjulian *
1498402Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1598402Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1698402Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1798402Sjulian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1898402Sjulian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1998402Sjulian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2098402Sjulian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2198402Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2298402Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2398402Sjulian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2498402Sjulian *
2598402Sjulian * Netgraph "device" node
2698402Sjulian *
27136673Sglebius * This node presents a /dev/ngd%d device that interfaces to an other
2898402Sjulian * netgraph node.
2998402Sjulian *
3098402Sjulian * $FreeBSD$
3198402Sjulian *
3298402Sjulian */
3398402Sjulian
34136673Sglebius#if 0
35137100Sglebius#define	DBG do { printf("ng_device: %s\n", __func__ ); } while (0)
36136673Sglebius#else
37137100Sglebius#define	DBG do {} while (0)
38136673Sglebius#endif
39136673Sglebius
4098402Sjulian#include <sys/param.h>
41132446Sglebius#include <sys/conf.h>
42132446Sglebius#include <sys/ioccom.h>
4398402Sjulian#include <sys/kernel.h>
44132446Sglebius#include <sys/malloc.h>
4598402Sjulian#include <sys/mbuf.h>
46132446Sglebius#include <sys/poll.h>
47132446Sglebius#include <sys/queue.h>
48136673Sglebius#include <sys/socket.h>
49132446Sglebius#include <sys/systm.h>
5098402Sjulian#include <sys/uio.h>
51136673Sglebius#include <sys/vnode.h>
5298402Sjulian
53136673Sglebius#include <net/if.h>
54136673Sglebius#include <net/if_var.h>
55136673Sglebius#include <netinet/in.h>
56136673Sglebius#include <netinet/in_systm.h>
57136673Sglebius#include <netinet/ip.h>
58136673Sglebius
5998402Sjulian#include <netgraph/ng_message.h>
6098402Sjulian#include <netgraph/netgraph.h>
61132446Sglebius#include <netgraph/ng_device.h>
6298402Sjulian
63132448Sglebius#define	ERROUT(x) do { error = (x); goto done; } while (0)
64132448Sglebius
6598402Sjulian/* Netgraph methods */
66141341Srustatic int		ng_device_mod_event(module_t, int, void *);
67136673Sglebiusstatic ng_constructor_t	ng_device_constructor;
6898402Sjulianstatic ng_rcvmsg_t	ng_device_rcvmsg;
69136673Sglebiusstatic ng_shutdown_t	ng_device_shutdown;
7098402Sjulianstatic ng_newhook_t	ng_device_newhook;
7198402Sjulianstatic ng_rcvdata_t	ng_device_rcvdata;
7298402Sjulianstatic ng_disconnect_t	ng_device_disconnect;
7398402Sjulian
7498402Sjulian/* Netgraph type */
75136673Sglebiusstatic struct ng_type ngd_typestruct = {
76129823Sjulian	.version =	NG_ABI_VERSION,
77129823Sjulian	.name =		NG_DEVICE_NODE_TYPE,
78141341Sru	.mod_event =	ng_device_mod_event,
79136673Sglebius	.constructor =	ng_device_constructor,
80136673Sglebius	.rcvmsg	=	ng_device_rcvmsg,
81136673Sglebius	.shutdown = 	ng_device_shutdown,
82129823Sjulian	.newhook =	ng_device_newhook,
83129823Sjulian	.rcvdata =	ng_device_rcvdata,
84129823Sjulian	.disconnect =	ng_device_disconnect,
8598402Sjulian};
86136673SglebiusNETGRAPH_INIT(device, &ngd_typestruct);
8798402Sjulian
88136673Sglebius/* per node data */
89136673Sglebiusstruct ngd_private {
90136673Sglebius	struct	ifqueue	readq;
91136673Sglebius	struct	ng_node	*node;
92136673Sglebius	struct	ng_hook	*hook;
93136673Sglebius	struct	cdev	*ngddev;
94136673Sglebius	struct	mtx	ngd_mtx;
95136673Sglebius	int 		unit;
96136673Sglebius	uint16_t	flags;
97136673Sglebius#define	NGDF_OPEN	0x0001
98136673Sglebius#define	NGDF_RWAIT	0x0002
9998402Sjulian};
100136673Sglebiustypedef struct ngd_private *priv_p;
10198402Sjulian
102143593Sglebius/* unit number allocator entity */
103143593Sglebiusstatic struct unrhdr *ngd_unit;
10498402Sjulian
10598402Sjulian/* Maximum number of NGD devices */
106143593Sglebius#define MAX_NGD	999
10798402Sjulian
10898402Sjulianstatic d_close_t ngdclose;
10998402Sjulianstatic d_open_t ngdopen;
11098402Sjulianstatic d_read_t ngdread;
11198402Sjulianstatic d_write_t ngdwrite;
112136673Sglebius#if 0
11398402Sjulianstatic d_ioctl_t ngdioctl;
114136673Sglebius#endif
11598402Sjulianstatic d_poll_t ngdpoll;
11698402Sjulian
11798402Sjulianstatic struct cdevsw ngd_cdevsw = {
118126080Sphk	.d_version =	D_VERSION,
119111815Sphk	.d_open =	ngdopen,
120111815Sphk	.d_close =	ngdclose,
121111815Sphk	.d_read =	ngdread,
122111815Sphk	.d_write =	ngdwrite,
123136673Sglebius#if 0
124111815Sphk	.d_ioctl =	ngdioctl,
125136673Sglebius#endif
126111815Sphk	.d_poll =	ngdpoll,
127136673Sglebius	.d_name =	NG_DEVICE_DEVNAME,
12898402Sjulian};
12998402Sjulian
130136673Sglebius/******************************************************************************
131136673Sglebius *  Netgraph methods
132136673Sglebius ******************************************************************************/
133136673Sglebius
134136673Sglebius/*
135143593Sglebius * Handle loading and unloading for this node type.
136143593Sglebius */
137143593Sglebiusstatic int
138143593Sglebiusng_device_mod_event(module_t mod, int event, void *data)
139143593Sglebius{
140143593Sglebius	int error = 0;
141143593Sglebius
142143593Sglebius	switch (event) {
143143593Sglebius	case MOD_LOAD:
144143593Sglebius		ngd_unit = new_unrhdr(0, MAX_NGD, NULL);
145143593Sglebius		break;
146143593Sglebius	case MOD_UNLOAD:
147143593Sglebius		delete_unrhdr(ngd_unit);
148143593Sglebius		break;
149143593Sglebius	default:
150143593Sglebius		error = EOPNOTSUPP;
151143593Sglebius		break;
152143593Sglebius	}
153143593Sglebius	return (error);
154143593Sglebius}
155143593Sglebius
156143593Sglebius/*
157136673Sglebius * create new node
15898402Sjulian */
15998402Sjulianstatic int
160136673Sglebiusng_device_constructor(node_p node)
16198402Sjulian{
162136673Sglebius	priv_p	priv;
16398402Sjulian
164137022Sglebius	DBG;
16598402Sjulian
166220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
16798402Sjulian
168143593Sglebius	/* Allocate unit number */
169143593Sglebius	priv->unit = alloc_unr(ngd_unit);
17098402Sjulian
171141914Sglebius	/* Initialize mutexes and queue */
172141914Sglebius	mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
173141914Sglebius	mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
174141914Sglebius	IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
175141914Sglebius
176141914Sglebius	/* Link everything together */
177141914Sglebius	NG_NODE_SET_PRIVATE(node, priv);
178141914Sglebius	priv->node = node;
179141914Sglebius
180183381Sed	priv->ngddev = make_dev(&ngd_cdevsw, priv->unit, UID_ROOT,
181136673Sglebius	    GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
182136673Sglebius	if(priv->ngddev == NULL) {
183136673Sglebius		printf("%s(): make_dev() failed\n",__func__);
184136673Sglebius		mtx_destroy(&priv->ngd_mtx);
185141914Sglebius		mtx_destroy(&priv->readq.ifq_mtx);
186143593Sglebius		free_unr(ngd_unit, priv->unit);
187184205Sdes		free(priv, M_NETGRAPH);
188136673Sglebius		return(EINVAL);
189136673Sglebius	}
190141914Sglebius	/* XXX: race here? */
191136673Sglebius	priv->ngddev->si_drv1 = priv;
19298402Sjulian
19398402Sjulian	return(0);
19498402Sjulian}
19598402Sjulian
196136673Sglebius/*
197136673Sglebius * Process control message.
19898402Sjulian */
19998402Sjulian
20098402Sjulianstatic int
20198402Sjulianng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
20298402Sjulian{
203136673Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
20498402Sjulian	struct ng_mesg *msg;
205136673Sglebius	struct ng_mesg *resp = NULL;
206231378Sed	const char *dn;
20798402Sjulian	int error = 0;
20898402Sjulian
209136673Sglebius	NGI_GET_MSG(item, msg);
21098402Sjulian
211136673Sglebius	if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
212136673Sglebius		switch (msg->header.cmd) {
213136673Sglebius		case NGM_DEVICE_GET_DEVNAME:
214143593Sglebius			/* XXX: Fix when MAX_NGD us bigger */
215136673Sglebius			NG_MKRESPONSE(resp, msg,
216143593Sglebius			    strlen(NG_DEVICE_DEVNAME) + 4, M_NOWAIT);
21798402Sjulian
218136673Sglebius			if (resp == NULL)
219136673Sglebius				ERROUT(ENOMEM);
22098402Sjulian
221231378Sed			dn = devtoname(priv->ngddev);
222231378Sed			strlcpy((char *)resp->data, dn, strlen(dn) + 1);
223136673Sglebius			break;
22498402Sjulian
225136673Sglebius		default:
226136673Sglebius			error = EINVAL;
227136673Sglebius			break;
22898402Sjulian		}
229136673Sglebius	} else
230136673Sglebius		error = EINVAL;
23198402Sjulian
232136673Sglebiusdone:
233136673Sglebius	NG_RESPOND_MSG(error, node, item, resp);
234136673Sglebius	NG_FREE_MSG(msg);
235136673Sglebius	return (error);
23698402Sjulian}
23798402Sjulian
23898402Sjulian/*
239136673Sglebius * Accept incoming hook. We support only one hook per node.
24098402Sjulian */
24198402Sjulianstatic int
24298402Sjulianng_device_newhook(node_p node, hook_p hook, const char *name)
24398402Sjulian{
244136673Sglebius	priv_p priv = NG_NODE_PRIVATE(node);
24598402Sjulian
246137022Sglebius	DBG;
24798402Sjulian
248136673Sglebius	/* We have only one hook per node */
249136673Sglebius	if (priv->hook != NULL)
250136673Sglebius		return (EISCONN);
25198402Sjulian
252136673Sglebius	priv->hook = hook;
25398402Sjulian
25498402Sjulian	return(0);
25598402Sjulian}
25698402Sjulian
25798402Sjulian/*
258136673Sglebius * Receive data from hook, write it to device.
25998402Sjulian */
26098402Sjulianstatic int
26198402Sjulianng_device_rcvdata(hook_p hook, item_p item)
26298402Sjulian{
263136673Sglebius	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
26498402Sjulian	struct mbuf *m;
26598402Sjulian
266137022Sglebius	DBG;
26798402Sjulian
268132448Sglebius	NGI_GET_M(item, m);
269132448Sglebius	NG_FREE_ITEM(item);
270132448Sglebius
271136673Sglebius	IF_LOCK(&priv->readq);
272136673Sglebius	if (_IF_QFULL(&priv->readq)) {
273136673Sglebius		_IF_DROP(&priv->readq);
274136673Sglebius		IF_UNLOCK(&priv->readq);
275136673Sglebius		NG_FREE_M(m);
276136673Sglebius		return (ENOBUFS);
27798402Sjulian	}
27898402Sjulian
279136673Sglebius	_IF_ENQUEUE(&priv->readq, m);
280136673Sglebius	IF_UNLOCK(&priv->readq);
281136673Sglebius	mtx_lock(&priv->ngd_mtx);
282136673Sglebius	if (priv->flags & NGDF_RWAIT) {
283136673Sglebius		priv->flags &= ~NGDF_RWAIT;
284136673Sglebius		wakeup(priv);
28598402Sjulian	}
286136673Sglebius	mtx_unlock(&priv->ngd_mtx);
28798402Sjulian
288136673Sglebius	return(0);
28998402Sjulian}
29098402Sjulian
29198402Sjulian/*
292136673Sglebius * Removal of the hook destroys the node.
29398402Sjulian */
29498402Sjulianstatic int
29598402Sjulianng_device_disconnect(hook_p hook)
29698402Sjulian{
297136673Sglebius	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
29898402Sjulian
299137022Sglebius	DBG;
30098402Sjulian
301136673Sglebius	destroy_dev(priv->ngddev);
302136673Sglebius	mtx_destroy(&priv->ngd_mtx);
303132448Sglebius
304136673Sglebius	IF_DRAIN(&priv->readq);
305136673Sglebius	mtx_destroy(&(priv)->readq.ifq_mtx);
30698402Sjulian
307143593Sglebius	free_unr(ngd_unit, priv->unit);
308143593Sglebius
309184205Sdes	free(priv, M_NETGRAPH);
31098402Sjulian
311136673Sglebius	ng_rmnode_self(NG_HOOK_NODE(hook));
31298402Sjulian
31398402Sjulian	return(0);
31498402Sjulian}
315136673Sglebius
31698402Sjulian/*
317136673Sglebius * Node shutdown. Everything is already done in disconnect method.
31898402Sjulian */
31998402Sjulianstatic int
320136673Sglebiusng_device_shutdown(node_p node)
321136673Sglebius{
322136673Sglebius	NG_NODE_UNREF(node);
323136673Sglebius	return (0);
324136673Sglebius}
325136673Sglebius
326136673Sglebius/******************************************************************************
327136673Sglebius *  Device methods
328136673Sglebius ******************************************************************************/
329136673Sglebius
330136673Sglebius/*
331136673Sglebius * the device is opened
332136673Sglebius */
333136673Sglebiusstatic int
334130585Sphkngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
33598402Sjulian{
336136673Sglebius	priv_p	priv = (priv_p )dev->si_drv1;
33798402Sjulian
338137022Sglebius	DBG;
339137022Sglebius
340136673Sglebius	mtx_lock(&priv->ngd_mtx);
341136673Sglebius	priv->flags |= NGDF_OPEN;
342136673Sglebius	mtx_unlock(&priv->ngd_mtx);
34398402Sjulian
34498402Sjulian	return(0);
34598402Sjulian}
34698402Sjulian
34798402Sjulian/*
348136673Sglebius * the device is closed
34998402Sjulian */
35098402Sjulianstatic int
351130585Sphkngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
35298402Sjulian{
353136673Sglebius	priv_p	priv = (priv_p )dev->si_drv1;
35498402Sjulian
355137022Sglebius	DBG;
356136673Sglebius	mtx_lock(&priv->ngd_mtx);
357136673Sglebius	priv->flags &= ~NGDF_OPEN;
358136673Sglebius	mtx_unlock(&priv->ngd_mtx);
35998402Sjulian
36098402Sjulian	return(0);
36198402Sjulian}
36298402Sjulian
363136673Sglebius#if 0	/*
364136673Sglebius	 * The ioctl is transformed into netgraph control message.
365136673Sglebius	 * We do not process them, yet.
366136673Sglebius	 */
36798402Sjulian/*
36898402Sjulian * process ioctl
36998402Sjulian *
37098402Sjulian * they are translated into netgraph messages and passed on
371136673Sglebius *
37298402Sjulian */
37398402Sjulianstatic int
374130585Sphkngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
37598402Sjulian{
37698402Sjulian	struct ngd_softc *sc = &ngd_softc;
37798402Sjulian	struct ngd_connection * connection = NULL;
37898402Sjulian	struct ngd_connection * tmp;
37998402Sjulian	int error = 0;
38098402Sjulian	struct ng_mesg *msg;
381136673Sglebius	struct ngd_param_s * datap;
38298402Sjulian
383137022Sglebius	DBG;
38498402Sjulian
385136673Sglebius	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
38698402Sjulian			M_NOWAIT);
38798402Sjulian	if (msg == NULL) {
38898402Sjulian		printf("%s(): msg == NULL\n",__func__);
38998402Sjulian		goto nomsg;
39098402Sjulian	}
39198402Sjulian
39298402Sjulian	/* pass the ioctl data into the ->data area */
39398402Sjulian	datap = (struct ngd_param_s *)msg->data;
394136673Sglebius	datap->p = addr;
39598402Sjulian
396132446Sglebius	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
39798402Sjulian	if(error)
39898402Sjulian		printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
39998402Sjulian
40098402Sjuliannomsg:
40198402Sjulian
40298402Sjulian	return(0);
40398402Sjulian}
404136673Sglebius#endif /* if 0 */
40598402Sjulian
40698402Sjulian/*
40798402Sjulian * This function is called when a read(2) is done to our device.
408136673Sglebius * We process one mbuf from queue.
40998402Sjulian */
41098402Sjulianstatic int
411130585Sphkngdread(struct cdev *dev, struct uio *uio, int flag)
41298402Sjulian{
413136673Sglebius	priv_p	priv = (priv_p )dev->si_drv1;
414136673Sglebius	struct mbuf *m;
415136673Sglebius	int len, error = 0;
41698402Sjulian
417137022Sglebius	DBG;
41898402Sjulian
419136673Sglebius	/* get an mbuf */
420136673Sglebius	do {
421136673Sglebius		IF_DEQUEUE(&priv->readq, m);
422136673Sglebius		if (m == NULL) {
423136673Sglebius			if (flag & IO_NDELAY)
424136673Sglebius				return (EWOULDBLOCK);
425136673Sglebius			mtx_lock(&priv->ngd_mtx);
426136673Sglebius			priv->flags |= NGDF_RWAIT;
427139331Srik			if ((error = msleep(priv, &priv->ngd_mtx,
428139331Srik			    PDROP | PCATCH | (PZERO + 1),
429136673Sglebius			    "ngdread", 0)) != 0)
430136673Sglebius				return (error);
43198402Sjulian		}
432136673Sglebius	} while (m == NULL);
433136673Sglebius
434136673Sglebius	while (m && uio->uio_resid > 0 && error == 0) {
435136673Sglebius		len = MIN(uio->uio_resid, m->m_len);
436136673Sglebius		if (len != 0)
437136673Sglebius			error = uiomove(mtod(m, void *), len, uio);
438136673Sglebius		m = m_free(m);
43998402Sjulian	}
44098402Sjulian
441136673Sglebius	if (m)
442136673Sglebius		m_freem(m);
44398402Sjulian
444136673Sglebius	return (error);
44598402Sjulian}
44698402Sjulian
44798402Sjulian
448136673Sglebius/*
44998402Sjulian * This function is called when our device is written to.
450136673Sglebius * We read the data from userland into mbuf chain and pass it to the remote hook.
45198402Sjulian *
45298402Sjulian */
45398402Sjulianstatic int
454130585Sphkngdwrite(struct cdev *dev, struct uio *uio, int flag)
45598402Sjulian{
456136673Sglebius	priv_p	priv = (priv_p )dev->si_drv1;
457136673Sglebius	struct mbuf *m;
45898402Sjulian	int error = 0;
45998402Sjulian
460137022Sglebius	DBG;
46198402Sjulian
462136673Sglebius	if (uio->uio_resid == 0)
463136673Sglebius		return (0);
46498402Sjulian
465136673Sglebius	if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
466136673Sglebius		return (EIO);
46798402Sjulian
468243882Sglebius	if ((m = m_uiotombuf(uio, M_NOWAIT, 0, 0, M_PKTHDR)) == NULL)
469136673Sglebius		return (ENOBUFS);
47098402Sjulian
471136673Sglebius	NG_SEND_DATA_ONLY(error, priv->hook, m);
47298402Sjulian
473136673Sglebius	return (error);
47498402Sjulian}
47598402Sjulian
47698402Sjulian/*
47798402Sjulian * we are being polled/selected
47898402Sjulian * check if there is data available for read
47998402Sjulian */
48098402Sjulianstatic int
481130585Sphkngdpoll(struct cdev *dev, int events, struct thread *td)
48298402Sjulian{
483136673Sglebius	priv_p	priv = (priv_p )dev->si_drv1;
48498402Sjulian	int revents = 0;
48598402Sjulian
486136673Sglebius	if (events & (POLLIN | POLLRDNORM) &&
487136673Sglebius	    !IFQ_IS_EMPTY(&priv->readq))
488136673Sglebius		revents |= events & (POLLIN | POLLRDNORM);
48998402Sjulian
490136673Sglebius	return (revents);
491136673Sglebius}
492