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