xref: /freebsd/sys/netgraph/ng_device.c (revision 6780ab54325a71e7e70112b11657973edde8655e)
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$
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 */
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_ABI_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_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 	dev_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_NODELEN + 1];
100 } ngd_softc;
101 
102 /* the per connection receiving queue maximum */
103 #define NGD_QUEUE_SIZE (1024*10)
104 
105 /* Maximum number of NGD devices */
106 #define MAX_NGD	25 		/* should be more than enough for now */
107 
108 static d_close_t ngdclose;
109 static d_open_t ngdopen;
110 static d_read_t ngdread;
111 static d_write_t ngdwrite;
112 static d_ioctl_t ngdioctl;
113 static d_poll_t ngdpoll;
114 
115 #define NGD_CDEV_MAJOR 20
116 static struct cdevsw ngd_cdevsw = {
117         /* open */      ngdopen,
118         /* close */     ngdclose,
119         /* read */      ngdread,
120         /* write */     ngdwrite,
121         /* ioctl */     ngdioctl,
122         /* poll */      ngdpoll,
123         /* mmap */      nommap,
124         /* strategy */  nostrategy,
125         /* name */      "ngd",
126         /* maj */       NGD_CDEV_MAJOR,
127         /* dump */      nodump,
128         /* psize */     nopsize,
129         /* flags */     0,
130 };
131 
132 /*
133  * this holds all the stuff that should be done at load time
134  */
135 static int
136 ng_device_mod_event(module_t mod, int event, void *data)
137 {
138 	int error = 0;
139 
140 #ifdef NGD_DEBUG
141 	printf("%s()\n",__func__);
142 #endif /* NGD_DEBUG */
143 
144 	switch (event) {
145 		case MOD_LOAD:
146 
147 			ng_device_init();
148 			break;
149 
150 		case MOD_UNLOAD:
151 			/* XXX do we need to do something specific ? */
152 			/* ng_device_breakdown */
153 			break;
154 
155 		default:
156 			error = EOPNOTSUPP;
157 			break;
158 	}
159 
160 	return(error);
161 }
162 
163 
164 static int
165 ng_device_init()
166 {
167         struct ngd_softc *sc = &ngd_softc;
168 
169 #ifdef NGD_DEBUG
170 	printf("%s()\n",__func__);
171 #endif /* NGD_DEBUG */
172 
173 	SLIST_INIT(&sc->head);
174 
175         if (ng_make_node_common(&typestruct, &sc->node) != 0) {
176                 printf("%s(): ng_make_node_common failed\n",__func__);
177                 return(ENXIO);
178         }
179         sprintf(sc->nodename, "%s", NG_DEVICE_NODE_TYPE);
180         if (ng_name_node(sc->node, sc->nodename)) {
181                 NG_NODE_UNREF(sc->node); /* make it go away again */
182                 printf("%s(): ng_name_node failed\n",__func__);
183                 return(ENXIO);
184         }
185         NG_NODE_SET_PRIVATE(sc->node, sc);
186 
187 	return(0);
188 }
189 
190 /*
191  * don't allow to be created, only the device can do that
192  */
193 static int
194 ng_device_cons(node_p node)
195 {
196 
197 #ifdef NGD_DEBUG
198 	printf("%s()\n",__func__);
199 #endif /* NGD_DEBUG */
200 
201 	return(EINVAL);
202 }
203 
204 /*
205  * Receive control message. We just bounce it back as a reply.
206  */
207 static int
208 ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
209 {
210         struct ngd_softc *sc = &ngd_softc;
211 	struct ng_mesg *msg;
212 	int error = 0;
213 	struct ngd_connection * connection = NULL;
214 	struct ngd_connection *tmp = NULL;
215 
216 #ifdef NGD_DEBUG
217 	printf("%s()\n",__func__);
218 #endif /* NGD_DEBUG */
219 
220 	NGI_GET_MSG(item, msg);
221 
222 	SLIST_FOREACH(tmp,&sc->head,links) {
223 		if(tmp->active_hook == lasthook) {
224 			connection = tmp;
225 		}
226 	}
227 	if(connection == NULL) {
228 		printf("%s(): connection is still NULL, no hook found\n",__func__);
229 		return(-1);
230 	}
231 
232 	return(error);
233 }
234 
235 static int
236 get_free_unit()
237 {
238 	struct ngd_connection *tmp = NULL;
239 	struct ngd_softc *sc = &ngd_softc;
240 	int n = 0;
241 	int unit = -1;
242 
243 #ifdef NGD_DEBUG
244 	printf("%s()\n",__func__);
245 #endif /* NGD_DEBUG */
246 
247 	/* When there is no list yet, the first device unit is always 0. */
248 	if SLIST_EMPTY(&sc->head) {
249 		unit = 0;
250 		return(unit);
251 	}
252 
253 	/* Just do a brute force loop to find the first free unit that is
254 	 * smaller than MAX_NGD.
255 	 * Set MAX_NGD to a large value, doesn't impact performance.
256 	 */
257 	for(n = 0;n<MAX_NGD && unit == -1;n++) {
258 		SLIST_FOREACH(tmp,&sc->head,links) {
259 
260 			if(tmp->unit == n) {
261 				unit = -1;
262 				break;
263 			}
264 			unit = n;
265 		}
266 	}
267 
268 	return(unit);
269 }
270 
271 /*
272  * incoming hook
273  */
274 static int
275 ng_device_newhook(node_p node, hook_p hook, const char *name)
276 {
277 	struct ngd_softc *sc = &ngd_softc;
278 	struct ngd_connection * new_connection = NULL;
279 
280 #ifdef NGD_DEBUG
281 	printf("%s()\n",__func__);
282 #endif /* NGD_DEBUG */
283 
284 	new_connection = malloc(sizeof(struct ngd_connection), M_DEVBUF, M_NOWAIT);
285 	if(new_connection == NULL) {
286 		printf("%s(): ERROR: new_connection == NULL\n",__func__);
287 		return(-1);
288 	}
289 
290 	new_connection->unit = get_free_unit();
291 	if(new_connection->unit<0) {
292 		printf("%s: No free unit found by get_free_unit(), "
293 				"increas MAX_NGD\n",__func__);
294 		return(-1);
295 	}
296 	new_connection->ngddev = make_dev(&ngd_cdevsw, new_connection->unit, 0, 0,0600,"ngd%d",new_connection->unit);
297 	if(new_connection->ngddev == NULL) {
298 		printf("%s(): make_dev failed\n",__func__);
299 		return(-1);
300 	}
301 
302 	new_connection->readq = malloc(sizeof(char)*NGD_QUEUE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO);
303 	if(new_connection->readq == NULL) {
304 		printf("%s(): readq malloc failed\n",__func__);
305 		return(-1);
306 	}
307 
308 	/* point to begin of buffer */
309 	new_connection->loc = 0;
310 	new_connection->active_hook = hook;
311 
312 	SLIST_INSERT_HEAD(&sc->head, new_connection, links);
313 
314 	return(0);
315 }
316 
317 /*
318  * we gave ok to a new hook
319  * now connect
320  */
321 static int
322 ng_device_connect(hook_p hook)
323 {
324 
325 #ifdef NGD_DEBUG
326 	printf("%s()\n",__func__);
327 #endif /* NGD_DEBUG */
328 
329 	return(0);
330 }
331 
332 
333 /*
334  * Receive data from hook
335  */
336 static int
337 ng_device_rcvdata(hook_p hook, item_p item)
338 {
339 	struct mbuf *m;
340 	struct ngd_softc *sc = &ngd_softc;
341 	struct ngd_connection * connection = NULL;
342 	struct ngd_connection * tmp;
343 	char *buffer;
344 
345 #ifdef NGD_DEBUG
346 	printf("%s()\n",__func__);
347 #endif /* NGD_DEBUG */
348 
349 	SLIST_FOREACH(tmp,&sc->head,links) {
350 		if(tmp->active_hook == hook) {
351 			connection = tmp;
352 		}
353 	}
354 	if(connection == NULL) {
355 		printf("%s(): connection is still NULL, no hook found\n",__func__);
356 		return(-1);
357 	}
358 
359 	NGI_GET_M(item, m);
360 	NG_FREE_ITEM(item);
361 
362 	m = m_pullup(m,m->m_len);
363 	if(m == NULL) {
364 		printf("%s(): ERROR: m_pullup failed\n",__func__);
365 		return(-1);
366 	}
367 
368 	buffer = malloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);
369 	if(buffer == NULL) {
370 		printf("%s(): ERROR: buffer malloc failed\n",__func__);
371 		return(-1);
372 	}
373 
374 	buffer = mtod(m,char *);
375 
376 	if( (connection->loc+m->m_len) < NGD_QUEUE_SIZE) {
377 	        memcpy(connection->readq+connection->loc, buffer, m->m_len);
378 		connection->loc += m->m_len;
379 	} else
380 		printf("%s(): queue full, first read out a bit\n",__func__);
381 
382 	free(buffer,M_DEVBUF);
383 
384 	return(0);
385 }
386 
387 /*
388  * Removal of the last link destroys the node
389  */
390 static int
391 ng_device_disconnect(hook_p hook)
392 {
393 	struct ngd_softc *sc = &ngd_softc;
394 	struct ngd_connection * connection = NULL;
395 	struct ngd_connection * tmp;
396 
397 #ifdef NGD_DEBUG
398 	printf("%s()\n",__func__);
399 #endif /* NGD_DEBUG */
400 
401 	SLIST_FOREACH(tmp,&sc->head,links) {
402 		if(tmp->active_hook == hook) {
403 			connection = tmp;
404 		}
405 	}
406 	if(connection == NULL) {
407 		printf("%s(): connection is still NULL, no hook found\n",__func__);
408 		return(-1);
409 	}
410 
411         free(connection->readq,M_DEVBUF);
412 
413 	destroy_dev(connection->ngddev);
414 
415 	SLIST_REMOVE(&sc->head,connection,ngd_connection,links);
416 
417 	return(0);
418 }
419 /*
420  * the device is opened
421  */
422 static int
423 ngdopen(dev_t dev, int flag, int mode, struct thread *td)
424 {
425 
426 #ifdef NGD_DEBUG
427 	printf("%s()\n",__func__);
428 #endif /* NGD_DEBUG */
429 
430 	return(0);
431 }
432 
433 /*
434  * the device is closed
435  */
436 static int
437 ngdclose(dev_t dev, int flag, int mode, struct thread *td)
438 {
439 
440 #ifdef NGD_DEBUG
441 	printf("%s()\n",__func__);
442 #endif
443 
444 	return(0);
445 }
446 
447 
448 /*
449  * process ioctl
450  *
451  * they are translated into netgraph messages and passed on
452  *
453  */
454 static int
455 ngdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
456 {
457 	struct ngd_softc *sc = &ngd_softc;
458 	struct ngd_connection * connection = NULL;
459 	struct ngd_connection * tmp;
460 	int error = 0;
461 	struct ng_mesg *msg;
462         struct ngd_param_s * datap;
463 
464 #ifdef NGD_DEBUG
465 	printf("%s()\n",__func__);
466 #endif /* NGD_DEBUG */
467 
468 	SLIST_FOREACH(tmp,&sc->head,links) {
469 		if(tmp->ngddev == dev) {
470 			connection = tmp;
471 		}
472 	}
473 	if(connection == NULL) {
474 		printf("%s(): connection is still NULL, no dev found\n",__func__);
475 		return(-1);
476 	}
477 
478 	/* NG_MKMESSAGE(msg, cookie, cmdid, len, how) */
479 	NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
480 			M_NOWAIT);
481 	if (msg == NULL) {
482 		printf("%s(): msg == NULL\n",__func__);
483 		goto nomsg;
484 	}
485 
486 	/* pass the ioctl data into the ->data area */
487 	datap = (struct ngd_param_s *)msg->data;
488         datap->p = addr;
489 
490 	/* NG_SEND_MSG_HOOK(error, here, msg, hook, retaddr) */
491 	NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, NULL);
492 	if(error)
493 		printf("%s(): NG_SEND_MSG_HOOK 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(dev_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 	printf("%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 		printf("%s(): connection is 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 	printf("%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(dev_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 	printf("%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 		printf("%s(): connection is 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 		printf("%s(): len <= 0 : is this 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 	printf("%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(dev_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 			printf("%s(): ERROR: connection is 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