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