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