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