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