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: releng/5.3/sys/netgraph/ng_device.c 136588 2004-10-16 08:43:07Z cvs2svn $
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 }
Cache object: b210a2bd6087af8beb3d218b69d41d06
|