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