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