ng_device.c revision 129823
1/*
2 * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * Netgraph "device" node
27 *
28 * This node presents a /dev/ngd%d device that interfaces to an other
29 * netgraph node.
30 *
31 * $FreeBSD: head/sys/netgraph/ng_device.c 129823 2004-05-29 00:51:19Z julian $
32 *
33 */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/mbuf.h>
39#include <sys/uio.h>
40#include <sys/queue.h>
41#include <sys/malloc.h>
42#include <sys/conf.h>
43#include <sys/poll.h>
44#include <sys/ioccom.h>
45
46#include <netgraph/ng_message.h>
47#include <netgraph/netgraph.h>
48
49#include "ng_device.h"
50
51/* turn this on for verbose messages */
52#define NGD_DEBUG
53
54/* Netgraph methods */
55static ng_constructor_t	ng_device_cons;
56static ng_rcvmsg_t	ng_device_rcvmsg;
57static ng_newhook_t	ng_device_newhook;
58static ng_connect_t 	ng_device_connect;
59static ng_rcvdata_t	ng_device_rcvdata;
60static ng_disconnect_t	ng_device_disconnect;
61static int              ng_device_mod_event(module_t mod, int event, void *data);
62
63static int ng_device_init(void);
64static int get_free_unit(void);
65
66/* Netgraph type */
67static struct ng_type typestruct = {
68	.version =	NG_ABI_VERSION,
69	.name =		NG_DEVICE_NODE_TYPE,
70	.mod_event =	ng_device_mod_event,
71	.constructor =	ng_device_cons,
72	.rcvmsg =	ng_device_rcvmsg,
73	.newhook =	ng_device_newhook,
74	.connect = 	ng_device_connect,
75	.rcvdata =	ng_device_rcvdata,
76	.disconnect =	ng_device_disconnect,
77};
78NETGRAPH_INIT(device, &typestruct);
79
80/* per hook data */
81struct ngd_connection {
82	SLIST_ENTRY(ngd_connection) links;
83
84	dev_t 	ngddev;
85	struct 	ng_hook *active_hook;
86	char	*readq;
87	int 	loc;
88	int 	unit;
89};
90
91/* global data */
92struct ngd_softc {
93	SLIST_HEAD(, ngd_connection) head;
94
95	node_p node;
96	char nodename[NG_NODESIZ];
97} ngd_softc;
98
99/* the per connection receiving queue maximum */
100#define NGD_QUEUE_SIZE (1024*10)
101
102/* Maximum number of NGD devices */
103#define MAX_NGD	25 		/* should be more than enough for now */
104
105static d_close_t ngdclose;
106static d_open_t ngdopen;
107static d_read_t ngdread;
108static d_write_t ngdwrite;
109static d_ioctl_t ngdioctl;
110static d_poll_t ngdpoll;
111
112static struct cdevsw ngd_cdevsw = {
113	.d_version =	D_VERSION,
114	.d_flags =	D_NEEDGIANT,
115	.d_open =	ngdopen,
116	.d_close =	ngdclose,
117	.d_read =	ngdread,
118	.d_write =	ngdwrite,
119	.d_ioctl =	ngdioctl,
120	.d_poll =	ngdpoll,
121	.d_name =	"ngd",
122};
123
124/*
125 * this holds all the stuff that should be done at load time
126 */
127static int
128ng_device_mod_event(module_t mod, int event, void *data)
129{
130	int error = 0;
131
132#ifdef NGD_DEBUG
133	printf("%s()\n",__func__);
134#endif /* NGD_DEBUG */
135
136	switch (event) {
137		case MOD_LOAD:
138
139			ng_device_init();
140			break;
141
142		case MOD_UNLOAD:
143			/* XXX do we need to do something specific ? */
144			/* ng_device_breakdown */
145			break;
146
147		default:
148			error = EOPNOTSUPP;
149			break;
150	}
151
152	return(error);
153}
154
155
156static int
157ng_device_init()
158{
159        struct ngd_softc *sc = &ngd_softc;
160
161#ifdef NGD_DEBUG
162	printf("%s()\n",__func__);
163#endif /* NGD_DEBUG */
164
165	SLIST_INIT(&sc->head);
166
167        if (ng_make_node_common(&typestruct, &sc->node) != 0) {
168                printf("%s(): ng_make_node_common failed\n",__func__);
169                return(ENXIO);
170        }
171        sprintf(sc->nodename, "%s", NG_DEVICE_NODE_TYPE);
172        if (ng_name_node(sc->node, sc->nodename)) {
173                NG_NODE_UNREF(sc->node); /* make it go away again */
174                printf("%s(): ng_name_node failed\n",__func__);
175                return(ENXIO);
176        }
177        NG_NODE_SET_PRIVATE(sc->node, sc);
178
179	return(0);
180}
181
182/*
183 * don't allow to be created, only the device can do that
184 */
185static int
186ng_device_cons(node_p node)
187{
188
189#ifdef NGD_DEBUG
190	printf("%s()\n",__func__);
191#endif /* NGD_DEBUG */
192
193	return(EINVAL);
194}
195
196/*
197 * Receive control message. We just bounce it back as a reply.
198 */
199static int
200ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
201{
202        struct ngd_softc *sc = &ngd_softc;
203	struct ng_mesg *msg;
204	int error = 0;
205	struct ngd_connection * connection = NULL;
206	struct ngd_connection *tmp = NULL;
207
208#ifdef NGD_DEBUG
209	printf("%s()\n",__func__);
210#endif /* NGD_DEBUG */
211
212	NGI_GET_MSG(item, msg);
213
214	SLIST_FOREACH(tmp,&sc->head,links) {
215		if(tmp->active_hook == lasthook) {
216			connection = tmp;
217		}
218	}
219	if(connection == NULL) {
220		printf("%s(): connection is still NULL, no hook found\n",__func__);
221		return(-1);
222	}
223
224	return(error);
225}
226
227static int
228get_free_unit()
229{
230	struct ngd_connection *tmp = NULL;
231	struct ngd_softc *sc = &ngd_softc;
232	int n = 0;
233	int unit = -1;
234
235#ifdef NGD_DEBUG
236	printf("%s()\n",__func__);
237#endif /* NGD_DEBUG */
238
239	/* When there is no list yet, the first device unit is always 0. */
240	if SLIST_EMPTY(&sc->head) {
241		unit = 0;
242		return(unit);
243	}
244
245	/* Just do a brute force loop to find the first free unit that is
246	 * smaller than MAX_NGD.
247	 * Set MAX_NGD to a large value, doesn't impact performance.
248	 */
249	for(n = 0;n<MAX_NGD && unit == -1;n++) {
250		SLIST_FOREACH(tmp,&sc->head,links) {
251
252			if(tmp->unit == n) {
253				unit = -1;
254				break;
255			}
256			unit = n;
257		}
258	}
259
260	return(unit);
261}
262
263/*
264 * incoming hook
265 */
266static int
267ng_device_newhook(node_p node, hook_p hook, const char *name)
268{
269	struct ngd_softc *sc = &ngd_softc;
270	struct ngd_connection * new_connection = NULL;
271
272#ifdef NGD_DEBUG
273	printf("%s()\n",__func__);
274#endif /* NGD_DEBUG */
275
276	new_connection = malloc(sizeof(struct ngd_connection), M_DEVBUF, M_NOWAIT);
277	if(new_connection == NULL) {
278		printf("%s(): ERROR: new_connection == NULL\n",__func__);
279		return(-1);
280	}
281
282	new_connection->unit = get_free_unit();
283	if(new_connection->unit<0) {
284		printf("%s: No free unit found by get_free_unit(), "
285				"increas MAX_NGD\n",__func__);
286		return(-1);
287	}
288	new_connection->ngddev = make_dev(&ngd_cdevsw, new_connection->unit, 0, 0,0600,"ngd%d",new_connection->unit);
289	if(new_connection->ngddev == NULL) {
290		printf("%s(): make_dev failed\n",__func__);
291		return(-1);
292	}
293
294	new_connection->readq = malloc(sizeof(char)*NGD_QUEUE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO);
295	if(new_connection->readq == NULL) {
296		printf("%s(): readq malloc failed\n",__func__);
297		return(-1);
298	}
299
300	/* point to begin of buffer */
301	new_connection->loc = 0;
302	new_connection->active_hook = hook;
303
304	SLIST_INSERT_HEAD(&sc->head, new_connection, links);
305
306	return(0);
307}
308
309/*
310 * we gave ok to a new hook
311 * now connect
312 */
313static int
314ng_device_connect(hook_p hook)
315{
316
317#ifdef NGD_DEBUG
318	printf("%s()\n",__func__);
319#endif /* NGD_DEBUG */
320
321	return(0);
322}
323
324
325/*
326 * Receive data from hook
327 */
328static int
329ng_device_rcvdata(hook_p hook, item_p item)
330{
331	struct mbuf *m;
332	struct ngd_softc *sc = &ngd_softc;
333	struct ngd_connection * connection = NULL;
334	struct ngd_connection * tmp;
335	char *buffer;
336
337#ifdef NGD_DEBUG
338	printf("%s()\n",__func__);
339#endif /* NGD_DEBUG */
340
341	SLIST_FOREACH(tmp,&sc->head,links) {
342		if(tmp->active_hook == hook) {
343			connection = tmp;
344		}
345	}
346	if(connection == NULL) {
347		printf("%s(): connection is still NULL, no hook found\n",__func__);
348		return(-1);
349	}
350
351	NGI_GET_M(item, m);
352	NG_FREE_ITEM(item);
353
354	m = m_pullup(m,m->m_len);
355	if(m == NULL) {
356		printf("%s(): ERROR: m_pullup failed\n",__func__);
357		return(-1);
358	}
359
360	buffer = malloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);
361	if(buffer == NULL) {
362		printf("%s(): ERROR: buffer malloc failed\n",__func__);
363		return(-1);
364	}
365
366	buffer = mtod(m,char *);
367
368	if( (connection->loc+m->m_len) < NGD_QUEUE_SIZE) {
369	        memcpy(connection->readq+connection->loc, buffer, m->m_len);
370		connection->loc += m->m_len;
371	} else
372		printf("%s(): queue full, first read out a bit\n",__func__);
373
374	free(buffer,M_DEVBUF);
375
376	return(0);
377}
378
379/*
380 * Removal of the last link destroys the node
381 */
382static int
383ng_device_disconnect(hook_p hook)
384{
385	struct ngd_softc *sc = &ngd_softc;
386	struct ngd_connection * connection = NULL;
387	struct ngd_connection * tmp;
388
389#ifdef NGD_DEBUG
390	printf("%s()\n",__func__);
391#endif /* NGD_DEBUG */
392
393	SLIST_FOREACH(tmp,&sc->head,links) {
394		if(tmp->active_hook == hook) {
395			connection = tmp;
396		}
397	}
398	if(connection == NULL) {
399		printf("%s(): connection is still NULL, no hook found\n",__func__);
400		return(-1);
401	}
402
403        free(connection->readq,M_DEVBUF);
404
405	destroy_dev(connection->ngddev);
406
407	SLIST_REMOVE(&sc->head,connection,ngd_connection,links);
408
409	return(0);
410}
411/*
412 * the device is opened
413 */
414static int
415ngdopen(dev_t dev, int flag, int mode, struct thread *td)
416{
417
418#ifdef NGD_DEBUG
419	printf("%s()\n",__func__);
420#endif /* NGD_DEBUG */
421
422	return(0);
423}
424
425/*
426 * the device is closed
427 */
428static int
429ngdclose(dev_t dev, int flag, int mode, struct thread *td)
430{
431
432#ifdef NGD_DEBUG
433	printf("%s()\n",__func__);
434#endif
435
436	return(0);
437}
438
439
440/*
441 * process ioctl
442 *
443 * they are translated into netgraph messages and passed on
444 *
445 */
446static int
447ngdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
448{
449	struct ngd_softc *sc = &ngd_softc;
450	struct ngd_connection * connection = NULL;
451	struct ngd_connection * tmp;
452	int error = 0;
453	struct ng_mesg *msg;
454        struct ngd_param_s * datap;
455
456#ifdef NGD_DEBUG
457	printf("%s()\n",__func__);
458#endif /* NGD_DEBUG */
459
460	SLIST_FOREACH(tmp,&sc->head,links) {
461		if(tmp->ngddev == dev) {
462			connection = tmp;
463		}
464	}
465	if(connection == NULL) {
466		printf("%s(): connection is still NULL, no dev found\n",__func__);
467		return(-1);
468	}
469
470	/* NG_MKMESSAGE(msg, cookie, cmdid, len, how) */
471	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
472			M_NOWAIT);
473	if (msg == NULL) {
474		printf("%s(): msg == NULL\n",__func__);
475		goto nomsg;
476	}
477
478	/* pass the ioctl data into the ->data area */
479	datap = (struct ngd_param_s *)msg->data;
480        datap->p = addr;
481
482	/* NG_SEND_MSG_HOOK(error, here, msg, hook, retaddr) */
483	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, NULL);
484	if(error)
485		printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
486
487nomsg:
488
489	return(0);
490}
491
492
493/*
494 * This function is called when a read(2) is done to our device.
495 * We pass the data available in kernelspace on into userland using
496 * uiomove.
497 */
498static int
499ngdread(dev_t dev, struct uio *uio, int flag)
500{
501	int ret = 0, amnt;
502	char buffer[uio->uio_resid+1];
503	struct ngd_softc *sc = &ngd_softc;
504	struct ngd_connection * connection = NULL;
505	struct ngd_connection * tmp;
506
507#ifdef NGD_DEBUG
508	printf("%s()\n",__func__);
509#endif /* NGD_DEBUG */
510
511	SLIST_FOREACH(tmp,&sc->head,links) {
512		if(tmp->ngddev == dev) {
513			connection = tmp;
514		}
515	}
516	if(connection == NULL) {
517		printf("%s(): connection is still NULL, no dev found\n",__func__);
518		return(-1);
519	}
520
521	while ( ( uio->uio_resid > 0 ) && ( connection->loc > 0 ) ) {
522		amnt = MIN(uio->uio_resid,connection->loc);
523
524		memcpy(buffer,connection->readq, amnt);
525		memcpy(connection->readq, connection->readq+amnt,
526				connection->loc-amnt);
527		connection->loc -= amnt;
528
529		ret = uiomove((caddr_t)buffer, amnt, uio);
530		if(ret != 0)
531			goto error;
532
533	}
534	return(0);
535
536error:
537	printf("%s(): uiomove returns error %d\n",__func__,ret);
538	/* do error cleanup here */
539	return(ret);
540}
541
542
543/*
544 * This function is called when our device is written to.
545 * We read the data from userland into our local buffer and pass it on
546 * into the remote hook.
547 *
548 */
549static int
550ngdwrite(dev_t dev, struct uio *uio, int flag)
551{
552	int ret;
553	int error = 0;
554	struct mbuf *m;
555	char buffer[uio->uio_resid];
556	int len = uio->uio_resid;
557	struct ngd_softc *sc =& ngd_softc;
558	struct ngd_connection * connection = NULL;
559	struct ngd_connection * tmp;
560
561#ifdef NGD_DEBUG
562	printf("%s()\n",__func__);
563#endif /* NGD_DEBUG */
564
565	SLIST_FOREACH(tmp,&sc->head,links) {
566		if(tmp->ngddev == dev) {
567			connection = tmp;
568		}
569	}
570
571	if(connection == NULL) {
572		printf("%s(): connection is still NULL, no dev found\n",__func__);
573		return(-1);
574	}
575
576	if (len > 0) {
577		if ((ret = uiomove((caddr_t)buffer, len, uio)) != 0)
578			goto error;
579	} else
580		printf("%s(): len <= 0 : is this supposed to happen?!\n",__func__);
581
582	m = m_devget(buffer,len,0,NULL,NULL);
583
584	NG_SEND_DATA_ONLY(error,connection->active_hook,m);
585
586	return(0);
587
588error:
589	/* do error cleanup here */
590	printf("%s(): uiomove returned err: %d\n",__func__,ret);
591
592	return(ret);
593}
594
595/*
596 * we are being polled/selected
597 * check if there is data available for read
598 */
599static int
600ngdpoll(dev_t dev, int events, struct thread *td)
601{
602	int revents = 0;
603	struct ngd_softc *sc = &ngd_softc;
604	struct ngd_connection * connection = NULL;
605	struct ngd_connection * tmp;
606
607
608	if (events & (POLLIN | POLLRDNORM)) {
609		/* get the connection we have to know the loc from */
610		SLIST_FOREACH(tmp,&sc->head,links) {
611			if(tmp->ngddev == dev) {
612				connection = tmp;
613			}
614		}
615		if(connection == NULL) {
616			printf("%s(): ERROR: connection is still NULL,"
617				"no dev found\n",__func__);
618			return(-1);
619		}
620
621		if (connection->loc > 0)
622			revents |= events & (POLLIN | POLLRDNORM);
623	}
624
625	return(revents);
626}
627