ng_device.c revision 132446
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: head/sys/netgraph/ng_device.c 132446 2004-07-20 11:19:46Z glebius $ 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/* Netgraph methods */ 52static ng_constructor_t ng_device_cons; 53static ng_rcvmsg_t ng_device_rcvmsg; 54static ng_newhook_t ng_device_newhook; 55static ng_connect_t ng_device_connect; 56static ng_rcvdata_t ng_device_rcvdata; 57static ng_disconnect_t ng_device_disconnect; 58static int ng_device_mod_event(module_t mod, int event, void *data); 59 60static int ng_device_init(void); 61static int get_free_unit(void); 62 63/* Netgraph type */ 64static struct ng_type typestruct = { 65 .version = NG_ABI_VERSION, 66 .name = NG_DEVICE_NODE_TYPE, 67 .mod_event = ng_device_mod_event, 68 .constructor = ng_device_cons, 69 .rcvmsg = ng_device_rcvmsg, 70 .newhook = ng_device_newhook, 71 .connect = ng_device_connect, 72 .rcvdata = ng_device_rcvdata, 73 .disconnect = ng_device_disconnect, 74}; 75NETGRAPH_INIT(device, &typestruct); 76 77/* per hook data */ 78struct ngd_connection { 79 SLIST_ENTRY(ngd_connection) links; 80 81 struct cdev *ngddev; 82 struct ng_hook *active_hook; 83 char *readq; 84 int loc; 85 int unit; 86}; 87 88/* global data */ 89struct ngd_softc { 90 SLIST_HEAD(, ngd_connection) head; 91 92 node_p node; 93 char nodename[NG_NODESIZ]; 94} ngd_softc; 95 96/* the per connection receiving queue maximum */ 97#define NGD_QUEUE_SIZE (1024*10) 98 99/* Maximum number of NGD devices */ 100#define MAX_NGD 25 /* should be more than enough for now */ 101 102static d_close_t ngdclose; 103static d_open_t ngdopen; 104static d_read_t ngdread; 105static d_write_t ngdwrite; 106static d_ioctl_t ngdioctl; 107static d_poll_t ngdpoll; 108 109static struct cdevsw ngd_cdevsw = { 110 .d_version = D_VERSION, 111 .d_flags = D_NEEDGIANT, 112 .d_open = ngdopen, 113 .d_close = ngdclose, 114 .d_read = ngdread, 115 .d_write = ngdwrite, 116 .d_ioctl = ngdioctl, 117 .d_poll = ngdpoll, 118 .d_name = "ngd", 119}; 120 121/* 122 * this holds all the stuff that should be done at load time 123 */ 124static int 125ng_device_mod_event(module_t mod, int event, void *data) 126{ 127 int error = 0; 128 129#ifdef NGD_DEBUG 130 printf("%s()\n",__func__); 131#endif /* NGD_DEBUG */ 132 133 switch (event) { 134 case MOD_LOAD: 135 136 ng_device_init(); 137 break; 138 139 case MOD_UNLOAD: 140 /* XXX do we need to do something specific ? */ 141 /* ng_device_breakdown */ 142 break; 143 144 default: 145 error = EOPNOTSUPP; 146 break; 147 } 148 149 return(error); 150} 151 152 153static int 154ng_device_init() 155{ 156 struct ngd_softc *sc = &ngd_softc; 157 158#ifdef NGD_DEBUG 159 printf("%s()\n",__func__); 160#endif /* NGD_DEBUG */ 161 162 SLIST_INIT(&sc->head); 163 164 if (ng_make_node_common(&typestruct, &sc->node) != 0) { 165 printf("%s(): ng_make_node_common failed\n",__func__); 166 return(ENXIO); 167 } 168 sprintf(sc->nodename, "%s", NG_DEVICE_NODE_TYPE); 169 if (ng_name_node(sc->node, sc->nodename)) { 170 NG_NODE_UNREF(sc->node); /* make it go away again */ 171 printf("%s(): ng_name_node failed\n",__func__); 172 return(ENXIO); 173 } 174 NG_NODE_SET_PRIVATE(sc->node, sc); 175 176 return(0); 177} 178 179/* 180 * don't allow to be created, only the device can do that 181 */ 182static int 183ng_device_cons(node_p node) 184{ 185 186#ifdef NGD_DEBUG 187 printf("%s()\n",__func__); 188#endif /* NGD_DEBUG */ 189 190 return(EINVAL); 191} 192 193/* 194 * Receive control message. We just bounce it back as a reply. 195 */ 196static int 197ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook) 198{ 199 struct ngd_softc *sc = &ngd_softc; 200 struct ng_mesg *msg; 201 int error = 0; 202 struct ngd_connection * connection = NULL; 203 struct ngd_connection *tmp = NULL; 204 205#ifdef NGD_DEBUG 206 printf("%s()\n",__func__); 207#endif /* NGD_DEBUG */ 208 209 NGI_GET_MSG(item, msg); 210 211 SLIST_FOREACH(tmp,&sc->head,links) { 212 if(tmp->active_hook == lasthook) { 213 connection = tmp; 214 } 215 } 216 if(connection == NULL) { 217 printf("%s(): connection is still NULL, no hook found\n",__func__); 218 return(-1); 219 } 220 221 return(error); 222} 223 224static int 225get_free_unit() 226{ 227 struct ngd_connection *tmp = NULL; 228 struct ngd_softc *sc = &ngd_softc; 229 int n = 0; 230 int unit = -1; 231 232#ifdef NGD_DEBUG 233 printf("%s()\n",__func__); 234#endif /* NGD_DEBUG */ 235 236 /* When there is no list yet, the first device unit is always 0. */ 237 if SLIST_EMPTY(&sc->head) { 238 unit = 0; 239 return(unit); 240 } 241 242 /* Just do a brute force loop to find the first free unit that is 243 * smaller than MAX_NGD. 244 * Set MAX_NGD to a large value, doesn't impact performance. 245 */ 246 for(n = 0;n<MAX_NGD && unit == -1;n++) { 247 SLIST_FOREACH(tmp,&sc->head,links) { 248 249 if(tmp->unit == n) { 250 unit = -1; 251 break; 252 } 253 unit = n; 254 } 255 } 256 257 return(unit); 258} 259 260/* 261 * incoming hook 262 */ 263static int 264ng_device_newhook(node_p node, hook_p hook, const char *name) 265{ 266 struct ngd_softc *sc = &ngd_softc; 267 struct ngd_connection * new_connection = NULL; 268 269#ifdef NGD_DEBUG 270 printf("%s()\n",__func__); 271#endif /* NGD_DEBUG */ 272 273 new_connection = malloc(sizeof(struct ngd_connection), M_DEVBUF, M_NOWAIT); 274 if(new_connection == NULL) { 275 printf("%s(): ERROR: new_connection == NULL\n",__func__); 276 return(-1); 277 } 278 279 new_connection->unit = get_free_unit(); 280 if(new_connection->unit<0) { 281 printf("%s: No free unit found by get_free_unit(), " 282 "increas MAX_NGD\n",__func__); 283 return(-1); 284 } 285 new_connection->ngddev = make_dev(&ngd_cdevsw, new_connection->unit, 0, 0,0600,"ngd%d",new_connection->unit); 286 if(new_connection->ngddev == NULL) { 287 printf("%s(): make_dev failed\n",__func__); 288 return(-1); 289 } 290 291 new_connection->readq = malloc(sizeof(char)*NGD_QUEUE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); 292 if(new_connection->readq == NULL) { 293 printf("%s(): readq malloc failed\n",__func__); 294 return(-1); 295 } 296 297 /* point to begin of buffer */ 298 new_connection->loc = 0; 299 new_connection->active_hook = hook; 300 301 SLIST_INSERT_HEAD(&sc->head, new_connection, links); 302 303 return(0); 304} 305 306/* 307 * we gave ok to a new hook 308 * now connect 309 */ 310static int 311ng_device_connect(hook_p hook) 312{ 313 314#ifdef NGD_DEBUG 315 printf("%s()\n",__func__); 316#endif /* NGD_DEBUG */ 317 318 return(0); 319} 320 321 322/* 323 * Receive data from hook 324 */ 325static int 326ng_device_rcvdata(hook_p hook, item_p item) 327{ 328 struct mbuf *m; 329 struct ngd_softc *sc = &ngd_softc; 330 struct ngd_connection * connection = NULL; 331 struct ngd_connection * tmp; 332 char *buffer; 333 334#ifdef NGD_DEBUG 335 printf("%s()\n",__func__); 336#endif /* NGD_DEBUG */ 337 338 SLIST_FOREACH(tmp,&sc->head,links) { 339 if(tmp->active_hook == hook) { 340 connection = tmp; 341 } 342 } 343 if(connection == NULL) { 344 printf("%s(): connection is still NULL, no hook found\n",__func__); 345 return(-1); 346 } 347 348 NGI_GET_M(item, m); 349 NG_FREE_ITEM(item); 350 351 m = m_pullup(m,m->m_len); 352 if(m == NULL) { 353 printf("%s(): ERROR: m_pullup failed\n",__func__); 354 return(-1); 355 } 356 357 buffer = malloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO); 358 if(buffer == NULL) { 359 printf("%s(): ERROR: buffer malloc failed\n",__func__); 360 return(-1); 361 } 362 363 buffer = mtod(m,char *); 364 365 if( (connection->loc+m->m_len) < NGD_QUEUE_SIZE) { 366 memcpy(connection->readq+connection->loc, buffer, m->m_len); 367 connection->loc += m->m_len; 368 } else 369 printf("%s(): queue full, first read out a bit\n",__func__); 370 371 free(buffer,M_DEVBUF); 372 373 return(0); 374} 375 376/* 377 * Removal of the last link destroys the node 378 */ 379static int 380ng_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 /* NGD_DEBUG */ 389 390 SLIST_FOREACH(tmp,&sc->head,links) { 391 if(tmp->active_hook == hook) { 392 connection = tmp; 393 } 394 } 395 if(connection == NULL) { 396 printf("%s(): connection is still NULL, no hook found\n",__func__); 397 return(-1); 398 } 399 400 free(connection->readq,M_DEVBUF); 401 402 destroy_dev(connection->ngddev); 403 404 SLIST_REMOVE(&sc->head,connection,ngd_connection,links); 405 406 return(0); 407} 408/* 409 * the device is opened 410 */ 411static int 412ngdopen(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 */ 425static int 426ngdclose(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 */ 443static int 444ngdioctl(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 483nomsg: 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 */ 494static int 495ngdread(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 532error: 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 */ 545static int 546ngdwrite(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 584error: 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 */ 595static int 596ngdpoll(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