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