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