xref: /freebsd/sys/netgraph/ng_device.c (revision 30aabc9afd5b14ce95bbf898be4cc0704d67a1b7)
1c398230bSWarner Losh /*-
2a8353960SJulian Elischer  * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
381755e6eSGleb Smirnoff  * Copyright (c) 2004 Gleb Smirnoff <glebius@FreeBSD.org>
4a8353960SJulian Elischer  *
5a8353960SJulian Elischer  * Redistribution and use in source and binary forms, with or without
6a8353960SJulian Elischer  * modification, are permitted provided that the following conditions
7a8353960SJulian Elischer  * are met:
8a8353960SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
9a8353960SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
10a8353960SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
11a8353960SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
12a8353960SJulian Elischer  *    documentation and/or other materials provided with the distribution.
13a8353960SJulian Elischer  *
14a8353960SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15a8353960SJulian Elischer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16a8353960SJulian Elischer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17a8353960SJulian Elischer  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18a8353960SJulian Elischer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19a8353960SJulian Elischer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20a8353960SJulian Elischer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21a8353960SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22a8353960SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23a8353960SJulian Elischer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24a8353960SJulian Elischer  *
25a8353960SJulian Elischer  * Netgraph "device" node
26a8353960SJulian Elischer  *
27a8353960SJulian Elischer  * This node presents a /dev/ngd%d device that interfaces to an other
28a8353960SJulian Elischer  * netgraph node.
29a8353960SJulian Elischer  *
30a8353960SJulian Elischer  * $FreeBSD$
31a8353960SJulian Elischer  *
32a8353960SJulian Elischer  */
33a8353960SJulian Elischer 
34547d3473SGleb Smirnoff #if 0
35ec774932SGleb Smirnoff #define	DBG do { printf("ng_device: %s\n", __func__ ); } while (0)
36547d3473SGleb Smirnoff #else
37ec774932SGleb Smirnoff #define	DBG do {} while (0)
38547d3473SGleb Smirnoff #endif
39547d3473SGleb Smirnoff 
40a8353960SJulian Elischer #include <sys/param.h>
41a8353960SJulian Elischer #include <sys/conf.h>
42a8353960SJulian Elischer #include <sys/ioccom.h>
43b3e3ef98SGleb Smirnoff #include <sys/kernel.h>
44b3e3ef98SGleb Smirnoff #include <sys/malloc.h>
45b3e3ef98SGleb Smirnoff #include <sys/mbuf.h>
46b3e3ef98SGleb Smirnoff #include <sys/poll.h>
47b3e3ef98SGleb Smirnoff #include <sys/queue.h>
48547d3473SGleb Smirnoff #include <sys/socket.h>
49b3e3ef98SGleb Smirnoff #include <sys/systm.h>
50b3e3ef98SGleb Smirnoff #include <sys/uio.h>
51547d3473SGleb Smirnoff #include <sys/vnode.h>
52547d3473SGleb Smirnoff 
53547d3473SGleb Smirnoff #include <net/if.h>
54547d3473SGleb Smirnoff #include <net/if_var.h>
55547d3473SGleb Smirnoff #include <netinet/in.h>
56547d3473SGleb Smirnoff #include <netinet/in_systm.h>
57547d3473SGleb Smirnoff #include <netinet/ip.h>
58a8353960SJulian Elischer 
59a8353960SJulian Elischer #include <netgraph/ng_message.h>
60a8353960SJulian Elischer #include <netgraph/netgraph.h>
61b3e3ef98SGleb Smirnoff #include <netgraph/ng_device.h>
62a8353960SJulian Elischer 
63c1eec6c5SGleb Smirnoff #define	ERROUT(x) do { error = (x); goto done; } while (0)
64c1eec6c5SGleb Smirnoff 
65a8353960SJulian Elischer /* Netgraph methods */
6630aabc9aSRuslan Ermilov static int		ng_device_mod_event(module_t, int, void *);
67547d3473SGleb Smirnoff static ng_constructor_t	ng_device_constructor;
68a8353960SJulian Elischer static ng_rcvmsg_t	ng_device_rcvmsg;
69547d3473SGleb Smirnoff static ng_shutdown_t	ng_device_shutdown;
70a8353960SJulian Elischer static ng_newhook_t	ng_device_newhook;
71a8353960SJulian Elischer static ng_rcvdata_t	ng_device_rcvdata;
72a8353960SJulian Elischer static ng_disconnect_t	ng_device_disconnect;
73a8353960SJulian Elischer 
74a8353960SJulian Elischer /* Netgraph type */
75547d3473SGleb Smirnoff static struct ng_type ngd_typestruct = {
76f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
77f8aae777SJulian Elischer 	.name =		NG_DEVICE_NODE_TYPE,
7830aabc9aSRuslan Ermilov 	.mod_event =	ng_device_mod_event,
79547d3473SGleb Smirnoff 	.constructor =	ng_device_constructor,
80f8aae777SJulian Elischer 	.rcvmsg	=	ng_device_rcvmsg,
81547d3473SGleb Smirnoff 	.shutdown = 	ng_device_shutdown,
82f8aae777SJulian Elischer 	.newhook =	ng_device_newhook,
83f8aae777SJulian Elischer 	.rcvdata =	ng_device_rcvdata,
84f8aae777SJulian Elischer 	.disconnect =	ng_device_disconnect,
85a8353960SJulian Elischer };
86547d3473SGleb Smirnoff NETGRAPH_INIT(device, &ngd_typestruct);
87a8353960SJulian Elischer 
88547d3473SGleb Smirnoff /* per node data */
89547d3473SGleb Smirnoff struct ngd_private {
90547d3473SGleb Smirnoff 	struct	ifqueue	readq;
91547d3473SGleb Smirnoff 	SLIST_ENTRY(ngd_private) links;
92547d3473SGleb Smirnoff 	struct	ng_node	*node;
93547d3473SGleb Smirnoff 	struct	ng_hook	*hook;
9489c9c53dSPoul-Henning Kamp 	struct	cdev	*ngddev;
95547d3473SGleb Smirnoff 	struct	mtx	ngd_mtx;
96a8353960SJulian Elischer 	int 		unit;
97547d3473SGleb Smirnoff 	uint16_t	flags;
98547d3473SGleb Smirnoff #define	NGDF_OPEN	0x0001
99547d3473SGleb Smirnoff #define	NGDF_RWAIT	0x0002
100a8353960SJulian Elischer };
101547d3473SGleb Smirnoff typedef struct ngd_private *priv_p;
102a8353960SJulian Elischer 
103547d3473SGleb Smirnoff /* List of all active nodes and mutex to protect it */
104547d3473SGleb Smirnoff static SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes);
105547d3473SGleb Smirnoff static struct mtx	ng_device_mtx;
106a8353960SJulian Elischer 
107a8353960SJulian Elischer /* Maximum number of NGD devices */
108a8353960SJulian Elischer #define MAX_NGD	25	/* should be more than enough for now */
109a8353960SJulian Elischer 
110a8353960SJulian Elischer static d_close_t ngdclose;
111a8353960SJulian Elischer static d_open_t ngdopen;
112a8353960SJulian Elischer static d_read_t ngdread;
113a8353960SJulian Elischer static d_write_t ngdwrite;
114547d3473SGleb Smirnoff #if 0
115a8353960SJulian Elischer static d_ioctl_t ngdioctl;
116547d3473SGleb Smirnoff #endif
117a8353960SJulian Elischer static d_poll_t ngdpoll;
118a8353960SJulian Elischer 
119a8353960SJulian Elischer static struct cdevsw ngd_cdevsw = {
120dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
1217ac40f5fSPoul-Henning Kamp 	.d_open =	ngdopen,
1227ac40f5fSPoul-Henning Kamp 	.d_close =	ngdclose,
1237ac40f5fSPoul-Henning Kamp 	.d_read =	ngdread,
1247ac40f5fSPoul-Henning Kamp 	.d_write =	ngdwrite,
125547d3473SGleb Smirnoff #if 0
1267ac40f5fSPoul-Henning Kamp 	.d_ioctl =	ngdioctl,
127547d3473SGleb Smirnoff #endif
1287ac40f5fSPoul-Henning Kamp 	.d_poll =	ngdpoll,
129547d3473SGleb Smirnoff 	.d_name =	NG_DEVICE_DEVNAME,
130a8353960SJulian Elischer };
131a8353960SJulian Elischer 
132547d3473SGleb Smirnoff /* Helper functions */
133547d3473SGleb Smirnoff static int get_free_unit(void);
134547d3473SGleb Smirnoff 
135547d3473SGleb Smirnoff /******************************************************************************
136547d3473SGleb Smirnoff  *  Netgraph methods
137547d3473SGleb Smirnoff  ******************************************************************************/
138547d3473SGleb Smirnoff 
139a8353960SJulian Elischer /*
140547d3473SGleb Smirnoff  * create new node
141a8353960SJulian Elischer  */
142a8353960SJulian Elischer static int
143547d3473SGleb Smirnoff ng_device_constructor(node_p node)
144a8353960SJulian Elischer {
145547d3473SGleb Smirnoff 	priv_p	priv;
146a8353960SJulian Elischer 
147f2b9562cSGleb Smirnoff 	DBG;
148a8353960SJulian Elischer 
149547d3473SGleb Smirnoff 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
150547d3473SGleb Smirnoff 	if (priv == NULL)
151547d3473SGleb Smirnoff 		return (ENOMEM);
152a8353960SJulian Elischer 
153547d3473SGleb Smirnoff 	mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
154547d3473SGleb Smirnoff 	mtx_lock(&priv->ngd_mtx);
155a8353960SJulian Elischer 
156547d3473SGleb Smirnoff 	mtx_lock(&ng_device_mtx);
157a8353960SJulian Elischer 
158547d3473SGleb Smirnoff 	priv->unit = get_free_unit();
159547d3473SGleb Smirnoff 	if(priv->unit < 0) {
160547d3473SGleb Smirnoff 		printf("%s: No free unit found by get_free_unit(), "
161547d3473SGleb Smirnoff 				"increase MAX_NGD\n",__func__);
162547d3473SGleb Smirnoff 		mtx_unlock(&ng_device_mtx);
163547d3473SGleb Smirnoff 		mtx_destroy(&priv->ngd_mtx);
164547d3473SGleb Smirnoff 		FREE(priv, M_NETGRAPH);
165547d3473SGleb Smirnoff 		return(EINVAL);
166a8353960SJulian Elischer 	}
167a8353960SJulian Elischer 
168547d3473SGleb Smirnoff 	priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT,
169547d3473SGleb Smirnoff 	    GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
170547d3473SGleb Smirnoff 	if(priv->ngddev == NULL) {
171547d3473SGleb Smirnoff 		printf("%s(): make_dev() failed\n",__func__);
172547d3473SGleb Smirnoff 		mtx_unlock(&ng_device_mtx);
173547d3473SGleb Smirnoff 		mtx_destroy(&priv->ngd_mtx);
174547d3473SGleb Smirnoff 		FREE(priv, M_NETGRAPH);
175547d3473SGleb Smirnoff 		return(EINVAL);
176a8353960SJulian Elischer 	}
177a8353960SJulian Elischer 
178547d3473SGleb Smirnoff 	SLIST_INSERT_HEAD(&ngd_nodes, priv, links);
179a8353960SJulian Elischer 
180547d3473SGleb Smirnoff 	mtx_unlock(&ng_device_mtx);
181a8353960SJulian Elischer 
182547d3473SGleb Smirnoff 	mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
183547d3473SGleb Smirnoff 	IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
184a8353960SJulian Elischer 
185547d3473SGleb Smirnoff 	/* Link everything together */
186547d3473SGleb Smirnoff 	NG_NODE_SET_PRIVATE(node, priv);
187547d3473SGleb Smirnoff 	priv->node = node;
188547d3473SGleb Smirnoff 	priv->ngddev->si_drv1 = priv;
189a8353960SJulian Elischer 
190547d3473SGleb Smirnoff 	mtx_unlock(&priv->ngd_mtx);
191a8353960SJulian Elischer 
192a8353960SJulian Elischer 	return(0);
193a8353960SJulian Elischer }
194a8353960SJulian Elischer 
195a8353960SJulian Elischer /*
196547d3473SGleb Smirnoff  * Process control message.
197a8353960SJulian Elischer  */
198a8353960SJulian Elischer 
199a8353960SJulian Elischer static int
200a8353960SJulian Elischer ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
201a8353960SJulian Elischer {
202547d3473SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
203a8353960SJulian Elischer 	struct ng_mesg *msg;
204547d3473SGleb Smirnoff 	struct ng_mesg *resp = NULL;
205a8353960SJulian Elischer 	int error = 0;
206a8353960SJulian Elischer 
207a8353960SJulian Elischer 	NGI_GET_MSG(item, msg);
208a8353960SJulian Elischer 
209547d3473SGleb Smirnoff 	if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
210547d3473SGleb Smirnoff 		switch (msg->header.cmd) {
211547d3473SGleb Smirnoff 		case NGM_DEVICE_GET_DEVNAME:
212547d3473SGleb Smirnoff 			/* XXX: Fix when NGD_MAX us bigger */
213547d3473SGleb Smirnoff 			NG_MKRESPONSE(resp, msg,
214547d3473SGleb Smirnoff 			    strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT);
215a8353960SJulian Elischer 
216547d3473SGleb Smirnoff 			if (resp == NULL)
217547d3473SGleb Smirnoff 				ERROUT(ENOMEM);
218547d3473SGleb Smirnoff 
219547d3473SGleb Smirnoff 			strlcpy((char *)resp->data, priv->ngddev->si_name,
220547d3473SGleb Smirnoff 			    strlen(priv->ngddev->si_name) + 1);
221547d3473SGleb Smirnoff 			break;
222547d3473SGleb Smirnoff 
223547d3473SGleb Smirnoff 		default:
224547d3473SGleb Smirnoff 			error = EINVAL;
225547d3473SGleb Smirnoff 			break;
226547d3473SGleb Smirnoff 		}
227547d3473SGleb Smirnoff 	} else
228547d3473SGleb Smirnoff 		error = EINVAL;
229547d3473SGleb Smirnoff 
230547d3473SGleb Smirnoff done:
231547d3473SGleb Smirnoff 	NG_RESPOND_MSG(error, node, item, resp);
232547d3473SGleb Smirnoff 	NG_FREE_MSG(msg);
233a8353960SJulian Elischer 	return (error);
234a8353960SJulian Elischer }
235a8353960SJulian Elischer 
236a8353960SJulian Elischer /*
237547d3473SGleb Smirnoff  * Accept incoming hook. We support only one hook per node.
238a8353960SJulian Elischer  */
239a8353960SJulian Elischer static int
240a8353960SJulian Elischer ng_device_newhook(node_p node, hook_p hook, const char *name)
241a8353960SJulian Elischer {
242547d3473SGleb Smirnoff 	priv_p priv = NG_NODE_PRIVATE(node);
243a8353960SJulian Elischer 
244f2b9562cSGleb Smirnoff 	DBG;
245a8353960SJulian Elischer 
246547d3473SGleb Smirnoff 	/* We have only one hook per node */
247547d3473SGleb Smirnoff 	if (priv->hook != NULL)
248547d3473SGleb Smirnoff 		return (EISCONN);
249a8353960SJulian Elischer 
250547d3473SGleb Smirnoff 	priv->hook = hook;
251a8353960SJulian Elischer 
252a8353960SJulian Elischer 	return(0);
253a8353960SJulian Elischer }
254a8353960SJulian Elischer 
255a8353960SJulian Elischer /*
256547d3473SGleb Smirnoff  * Receive data from hook, write it to device.
257a8353960SJulian Elischer  */
258a8353960SJulian Elischer static int
259a8353960SJulian Elischer ng_device_rcvdata(hook_p hook, item_p item)
260a8353960SJulian Elischer {
261547d3473SGleb Smirnoff 	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
262a8353960SJulian Elischer 	struct mbuf *m;
263a8353960SJulian Elischer 
264f2b9562cSGleb Smirnoff 	DBG;
265a8353960SJulian Elischer 
266a8353960SJulian Elischer 	NGI_GET_M(item, m);
267a8353960SJulian Elischer 	NG_FREE_ITEM(item);
268a8353960SJulian Elischer 
269547d3473SGleb Smirnoff 	IF_LOCK(&priv->readq);
270547d3473SGleb Smirnoff 	if (_IF_QFULL(&priv->readq)) {
271547d3473SGleb Smirnoff 		_IF_DROP(&priv->readq);
272547d3473SGleb Smirnoff 		IF_UNLOCK(&priv->readq);
273c1eec6c5SGleb Smirnoff 		NG_FREE_M(m);
274547d3473SGleb Smirnoff 		return (ENOBUFS);
275547d3473SGleb Smirnoff 	}
276547d3473SGleb Smirnoff 
277547d3473SGleb Smirnoff 	_IF_ENQUEUE(&priv->readq, m);
278547d3473SGleb Smirnoff 	IF_UNLOCK(&priv->readq);
279547d3473SGleb Smirnoff 	mtx_lock(&priv->ngd_mtx);
280547d3473SGleb Smirnoff 	if (priv->flags & NGDF_RWAIT) {
281547d3473SGleb Smirnoff 		priv->flags &= ~NGDF_RWAIT;
282547d3473SGleb Smirnoff 		wakeup(priv);
283547d3473SGleb Smirnoff 	}
284547d3473SGleb Smirnoff 	mtx_unlock(&priv->ngd_mtx);
285547d3473SGleb Smirnoff 
286547d3473SGleb Smirnoff 	return(0);
287a8353960SJulian Elischer }
288a8353960SJulian Elischer 
289a8353960SJulian Elischer /*
290547d3473SGleb Smirnoff  * Removal of the hook destroys the node.
291a8353960SJulian Elischer  */
292a8353960SJulian Elischer static int
293a8353960SJulian Elischer ng_device_disconnect(hook_p hook)
294a8353960SJulian Elischer {
295547d3473SGleb Smirnoff 	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
296a8353960SJulian Elischer 
297f2b9562cSGleb Smirnoff 	DBG;
298a8353960SJulian Elischer 
299547d3473SGleb Smirnoff 	destroy_dev(priv->ngddev);
300547d3473SGleb Smirnoff 	mtx_destroy(&priv->ngd_mtx);
301c1eec6c5SGleb Smirnoff 
302547d3473SGleb Smirnoff 	mtx_lock(&ng_device_mtx);
303547d3473SGleb Smirnoff 	SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links);
304547d3473SGleb Smirnoff 	mtx_unlock(&ng_device_mtx);
305a8353960SJulian Elischer 
306547d3473SGleb Smirnoff 	IF_DRAIN(&priv->readq);
307547d3473SGleb Smirnoff 	mtx_destroy(&(priv)->readq.ifq_mtx);
308a8353960SJulian Elischer 
309547d3473SGleb Smirnoff 	FREE(priv, M_NETGRAPH);
310a8353960SJulian Elischer 
311547d3473SGleb Smirnoff 	ng_rmnode_self(NG_HOOK_NODE(hook));
312a8353960SJulian Elischer 
313a8353960SJulian Elischer 	return(0);
314a8353960SJulian Elischer }
315547d3473SGleb Smirnoff 
316547d3473SGleb Smirnoff /*
317547d3473SGleb Smirnoff  * Node shutdown. Everything is already done in disconnect method.
318547d3473SGleb Smirnoff  */
319547d3473SGleb Smirnoff static int
320547d3473SGleb Smirnoff ng_device_shutdown(node_p node)
321547d3473SGleb Smirnoff {
322547d3473SGleb Smirnoff 	NG_NODE_UNREF(node);
323547d3473SGleb Smirnoff 	return (0);
324547d3473SGleb Smirnoff }
325547d3473SGleb Smirnoff 
326547d3473SGleb Smirnoff /******************************************************************************
327547d3473SGleb Smirnoff  *  Device methods
328547d3473SGleb Smirnoff  ******************************************************************************/
329547d3473SGleb Smirnoff 
330a8353960SJulian Elischer /*
331a8353960SJulian Elischer  * the device is opened
332a8353960SJulian Elischer  */
333a8353960SJulian Elischer static int
33489c9c53dSPoul-Henning Kamp ngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
335a8353960SJulian Elischer {
336547d3473SGleb Smirnoff 	priv_p	priv = (priv_p )dev->si_drv1;
337a8353960SJulian Elischer 
338f2b9562cSGleb Smirnoff 	DBG;
339f2b9562cSGleb Smirnoff 
340547d3473SGleb Smirnoff 	mtx_lock(&priv->ngd_mtx);
341547d3473SGleb Smirnoff 	priv->flags |= NGDF_OPEN;
342547d3473SGleb Smirnoff 	mtx_unlock(&priv->ngd_mtx);
343a8353960SJulian Elischer 
344a8353960SJulian Elischer 	return(0);
345a8353960SJulian Elischer }
346a8353960SJulian Elischer 
347a8353960SJulian Elischer /*
348a8353960SJulian Elischer  * the device is closed
349a8353960SJulian Elischer  */
350a8353960SJulian Elischer static int
35189c9c53dSPoul-Henning Kamp ngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
352a8353960SJulian Elischer {
353547d3473SGleb Smirnoff 	priv_p	priv = (priv_p )dev->si_drv1;
354a8353960SJulian Elischer 
355f2b9562cSGleb Smirnoff 	DBG;
356547d3473SGleb Smirnoff 	mtx_lock(&priv->ngd_mtx);
357547d3473SGleb Smirnoff 	priv->flags &= ~NGDF_OPEN;
358547d3473SGleb Smirnoff 	mtx_unlock(&priv->ngd_mtx);
359a8353960SJulian Elischer 
360a8353960SJulian Elischer 	return(0);
361a8353960SJulian Elischer }
362a8353960SJulian Elischer 
363547d3473SGleb Smirnoff #if 0	/*
364547d3473SGleb Smirnoff 	 * The ioctl is transformed into netgraph control message.
365547d3473SGleb Smirnoff 	 * We do not process them, yet.
366547d3473SGleb Smirnoff 	 */
367a8353960SJulian Elischer /*
368a8353960SJulian Elischer  * process ioctl
369a8353960SJulian Elischer  *
370a8353960SJulian Elischer  * they are translated into netgraph messages and passed on
371a8353960SJulian Elischer  *
372a8353960SJulian Elischer  */
373a8353960SJulian Elischer static int
37489c9c53dSPoul-Henning Kamp ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
375a8353960SJulian Elischer {
376a8353960SJulian Elischer 	struct ngd_softc *sc = &ngd_softc;
377a8353960SJulian Elischer 	struct ngd_connection * connection = NULL;
378a8353960SJulian Elischer 	struct ngd_connection * tmp;
379a8353960SJulian Elischer 	int error = 0;
380a8353960SJulian Elischer 	struct ng_mesg *msg;
381a8353960SJulian Elischer 	struct ngd_param_s * datap;
382a8353960SJulian Elischer 
383f2b9562cSGleb Smirnoff 	DBG;
384a8353960SJulian Elischer 
385a8353960SJulian Elischer 	SLIST_FOREACH(tmp,&sc->head,links) {
386a8353960SJulian Elischer 		if(tmp->ngddev == dev) {
387a8353960SJulian Elischer 			connection = tmp;
388a8353960SJulian Elischer 		}
389a8353960SJulian Elischer 	}
390a8353960SJulian Elischer 	if(connection == NULL) {
391a8353960SJulian Elischer 		printf("%s(): connection is still NULL, no dev found\n",__func__);
392a8353960SJulian Elischer 		return(-1);
393a8353960SJulian Elischer 	}
394a8353960SJulian Elischer 
395a8353960SJulian Elischer 	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
396a8353960SJulian Elischer 			M_NOWAIT);
397a8353960SJulian Elischer 	if (msg == NULL) {
398a8353960SJulian Elischer 		printf("%s(): msg == NULL\n",__func__);
399a8353960SJulian Elischer 		goto nomsg;
400a8353960SJulian Elischer 	}
401a8353960SJulian Elischer 
402a8353960SJulian Elischer 	/* pass the ioctl data into the ->data area */
403a8353960SJulian Elischer 	datap = (struct ngd_param_s *)msg->data;
404a8353960SJulian Elischer 	datap->p = addr;
405a8353960SJulian Elischer 
406b3e3ef98SGleb Smirnoff 	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
407a8353960SJulian Elischer 	if(error)
408a8353960SJulian Elischer 		printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
409a8353960SJulian Elischer 
410a8353960SJulian Elischer nomsg:
411a8353960SJulian Elischer 
412a8353960SJulian Elischer 	return(0);
413a8353960SJulian Elischer }
414547d3473SGleb Smirnoff #endif /* if 0 */
415a8353960SJulian Elischer 
416a8353960SJulian Elischer /*
417a8353960SJulian Elischer  * This function is called when a read(2) is done to our device.
418547d3473SGleb Smirnoff  * We process one mbuf from queue.
419a8353960SJulian Elischer  */
420a8353960SJulian Elischer static int
42189c9c53dSPoul-Henning Kamp ngdread(struct cdev *dev, struct uio *uio, int flag)
422a8353960SJulian Elischer {
423547d3473SGleb Smirnoff 	priv_p	priv = (priv_p )dev->si_drv1;
424547d3473SGleb Smirnoff 	struct mbuf *m;
425547d3473SGleb Smirnoff 	int len, error = 0;
426a8353960SJulian Elischer 
427f2b9562cSGleb Smirnoff 	DBG;
428a8353960SJulian Elischer 
429547d3473SGleb Smirnoff 	/* get an mbuf */
430547d3473SGleb Smirnoff 	do {
431547d3473SGleb Smirnoff 		IF_DEQUEUE(&priv->readq, m);
432547d3473SGleb Smirnoff 		if (m == NULL) {
433547d3473SGleb Smirnoff 			if (flag & IO_NDELAY)
434547d3473SGleb Smirnoff 				return (EWOULDBLOCK);
435547d3473SGleb Smirnoff 			mtx_lock(&priv->ngd_mtx);
436547d3473SGleb Smirnoff 			priv->flags |= NGDF_RWAIT;
43747095f77SRoman Kurakin 			if ((error = msleep(priv, &priv->ngd_mtx,
43847095f77SRoman Kurakin 			    PDROP | PCATCH | (PZERO + 1),
439547d3473SGleb Smirnoff 			    "ngdread", 0)) != 0)
440547d3473SGleb Smirnoff 				return (error);
441a8353960SJulian Elischer 		}
442547d3473SGleb Smirnoff 	} while (m == NULL);
443547d3473SGleb Smirnoff 
444547d3473SGleb Smirnoff 	while (m && uio->uio_resid > 0 && error == 0) {
445547d3473SGleb Smirnoff 		len = MIN(uio->uio_resid, m->m_len);
446547d3473SGleb Smirnoff 		if (len != 0)
447547d3473SGleb Smirnoff 			error = uiomove(mtod(m, void *), len, uio);
448547d3473SGleb Smirnoff 		m = m_free(m);
449a8353960SJulian Elischer 	}
450a8353960SJulian Elischer 
451547d3473SGleb Smirnoff 	if (m)
452547d3473SGleb Smirnoff 		m_freem(m);
453a8353960SJulian Elischer 
454547d3473SGleb Smirnoff 	return (error);
455a8353960SJulian Elischer }
456a8353960SJulian Elischer 
457a8353960SJulian Elischer 
458a8353960SJulian Elischer /*
459a8353960SJulian Elischer  * This function is called when our device is written to.
460547d3473SGleb Smirnoff  * We read the data from userland into mbuf chain and pass it to the remote hook.
461a8353960SJulian Elischer  *
462a8353960SJulian Elischer  */
463a8353960SJulian Elischer static int
46489c9c53dSPoul-Henning Kamp ngdwrite(struct cdev *dev, struct uio *uio, int flag)
465a8353960SJulian Elischer {
466547d3473SGleb Smirnoff 	priv_p	priv = (priv_p )dev->si_drv1;
467a8353960SJulian Elischer 	struct mbuf *m;
468547d3473SGleb Smirnoff 	int error = 0;
469a8353960SJulian Elischer 
470f2b9562cSGleb Smirnoff 	DBG;
471a8353960SJulian Elischer 
472547d3473SGleb Smirnoff 	if (uio->uio_resid == 0)
473a8353960SJulian Elischer 		return (0);
474a8353960SJulian Elischer 
475547d3473SGleb Smirnoff 	if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
476547d3473SGleb Smirnoff 		return (EIO);
477a8353960SJulian Elischer 
478547d3473SGleb Smirnoff 	if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL)
479547d3473SGleb Smirnoff 		return (ENOBUFS);
480547d3473SGleb Smirnoff 
481547d3473SGleb Smirnoff 	NG_SEND_DATA_ONLY(error, priv->hook, m);
482547d3473SGleb Smirnoff 
483547d3473SGleb Smirnoff 	return (error);
484a8353960SJulian Elischer }
485a8353960SJulian Elischer 
486a8353960SJulian Elischer /*
487a8353960SJulian Elischer  * we are being polled/selected
488a8353960SJulian Elischer  * check if there is data available for read
489a8353960SJulian Elischer  */
490a8353960SJulian Elischer static int
49189c9c53dSPoul-Henning Kamp ngdpoll(struct cdev *dev, int events, struct thread *td)
492a8353960SJulian Elischer {
493547d3473SGleb Smirnoff 	priv_p	priv = (priv_p )dev->si_drv1;
494a8353960SJulian Elischer 	int revents = 0;
495a8353960SJulian Elischer 
496547d3473SGleb Smirnoff 	if (events & (POLLIN | POLLRDNORM) &&
497547d3473SGleb Smirnoff 	    !IFQ_IS_EMPTY(&priv->readq))
498a8353960SJulian Elischer 		revents |= events & (POLLIN | POLLRDNORM);
499a8353960SJulian Elischer 
500a8353960SJulian Elischer 	return (revents);
501a8353960SJulian Elischer }
502547d3473SGleb Smirnoff 
503547d3473SGleb Smirnoff /******************************************************************************
504547d3473SGleb Smirnoff  *  Helper subroutines
505547d3473SGleb Smirnoff  ******************************************************************************/
506547d3473SGleb Smirnoff 
507547d3473SGleb Smirnoff static int
508547d3473SGleb Smirnoff get_free_unit()
509547d3473SGleb Smirnoff {
510547d3473SGleb Smirnoff 	struct ngd_private *priv = NULL;
511547d3473SGleb Smirnoff 	int n = 0;
512547d3473SGleb Smirnoff 	int unit = -1;
513547d3473SGleb Smirnoff 
514f2b9562cSGleb Smirnoff 	DBG;
515547d3473SGleb Smirnoff 
516547d3473SGleb Smirnoff 	mtx_assert(&ng_device_mtx, MA_OWNED);
517547d3473SGleb Smirnoff 
518547d3473SGleb Smirnoff 	/* When there is no list yet, the first device unit is always 0. */
519547d3473SGleb Smirnoff 	if SLIST_EMPTY(&ngd_nodes)
520547d3473SGleb Smirnoff 		return(0);
521547d3473SGleb Smirnoff 
522547d3473SGleb Smirnoff 	/* Just do a brute force loop to find the first free unit that is
523547d3473SGleb Smirnoff 	 * smaller than MAX_NGD.
524547d3473SGleb Smirnoff 	 * Set MAX_NGD to a large value, doesn't impact performance.
525547d3473SGleb Smirnoff 	 */
526547d3473SGleb Smirnoff 	for(n = 0; n<MAX_NGD && unit == -1; n++) {
527547d3473SGleb Smirnoff 		SLIST_FOREACH(priv, &ngd_nodes, links) {
528547d3473SGleb Smirnoff 
529547d3473SGleb Smirnoff 			if(priv->unit == n) {
530547d3473SGleb Smirnoff 				unit = -1;
531547d3473SGleb Smirnoff 				break;
532547d3473SGleb Smirnoff 			}
533547d3473SGleb Smirnoff 			unit = n;
534547d3473SGleb Smirnoff 		}
535547d3473SGleb Smirnoff 	}
536547d3473SGleb Smirnoff 
537547d3473SGleb Smirnoff 	return (unit);
538547d3473SGleb Smirnoff }
53930aabc9aSRuslan Ermilov 
54030aabc9aSRuslan Ermilov /*
54130aabc9aSRuslan Ermilov  * Handle loading and unloading for this node type.
54230aabc9aSRuslan Ermilov  */
54330aabc9aSRuslan Ermilov static int
54430aabc9aSRuslan Ermilov ng_device_mod_event(module_t mod, int event, void *data)
54530aabc9aSRuslan Ermilov {
54630aabc9aSRuslan Ermilov 	int error = 0;
54730aabc9aSRuslan Ermilov 
54830aabc9aSRuslan Ermilov 	switch (event) {
54930aabc9aSRuslan Ermilov 	case MOD_LOAD:
55030aabc9aSRuslan Ermilov 		mtx_init(&ng_device_mtx, "ng_device", NULL, MTX_DEF);
55130aabc9aSRuslan Ermilov 		break;
55230aabc9aSRuslan Ermilov 	case MOD_UNLOAD:
55330aabc9aSRuslan Ermilov 		mtx_destroy(&ng_device_mtx);
55430aabc9aSRuslan Ermilov 		break;
55530aabc9aSRuslan Ermilov 	default:
55630aabc9aSRuslan Ermilov 		error = EOPNOTSUPP;
55730aabc9aSRuslan Ermilov 		break;
55830aabc9aSRuslan Ermilov 	}
55930aabc9aSRuslan Ermilov 	return (error);
56030aabc9aSRuslan Ermilov }
561