ng_device.c revision 137525
1/* 2 * Copyright (c) 2002 Mark Santcroos <marks@ripe.net> 3 * Copyright (c) 2004 Gleb Smirnoff <glebius@FreeBSD.org> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 * Netgraph "device" node 26 * 27 * This node presents a /dev/ngd%d device that interfaces to an other 28 * netgraph node. 29 * 30 * $FreeBSD: head/sys/netgraph/ng_device.c 137525 2004-11-10 11:18:05Z glebius $ 31 * 32 */ 33 34#if 0 35#define DBG do { printf("ng_device: %s\n", __func__ ); } while (0) 36#else 37#define DBG do {} while (0) 38#endif 39 40#include <sys/param.h> 41#include <sys/conf.h> 42#include <sys/ioccom.h> 43#include <sys/kernel.h> 44#include <sys/malloc.h> 45#include <sys/mbuf.h> 46#include <sys/poll.h> 47#include <sys/queue.h> 48#include <sys/socket.h> 49#include <sys/systm.h> 50#include <sys/uio.h> 51#include <sys/vnode.h> 52 53#include <net/if.h> 54#include <net/if_var.h> 55#include <netinet/in.h> 56#include <netinet/in_systm.h> 57#include <netinet/ip.h> 58 59#include <netgraph/ng_message.h> 60#include <netgraph/netgraph.h> 61#include <netgraph/ng_device.h> 62 63#define ERROUT(x) do { error = (x); goto done; } while (0) 64 65/* Netgraph methods */ 66static ng_constructor_t ng_device_constructor; 67static ng_rcvmsg_t ng_device_rcvmsg; 68static ng_shutdown_t ng_device_shutdown; 69static ng_newhook_t ng_device_newhook; 70static ng_rcvdata_t ng_device_rcvdata; 71static ng_disconnect_t ng_device_disconnect; 72 73/* Netgraph type */ 74static struct ng_type ngd_typestruct = { 75 .version = NG_ABI_VERSION, 76 .name = NG_DEVICE_NODE_TYPE, 77 .constructor = ng_device_constructor, 78 .rcvmsg = ng_device_rcvmsg, 79 .shutdown = ng_device_shutdown, 80 .newhook = ng_device_newhook, 81 .rcvdata = ng_device_rcvdata, 82 .disconnect = ng_device_disconnect, 83}; 84NETGRAPH_INIT(device, &ngd_typestruct); 85 86/* per node data */ 87struct ngd_private { 88 struct ifqueue readq; 89 SLIST_ENTRY(ngd_private) links; 90 struct ng_node *node; 91 struct ng_hook *hook; 92 struct cdev *ngddev; 93 struct mtx ngd_mtx; 94 int unit; 95 uint16_t flags; 96#define NGDF_OPEN 0x0001 97#define NGDF_RWAIT 0x0002 98}; 99typedef struct ngd_private *priv_p; 100 101/* List of all active nodes and mutex to protect it */ 102static SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes); 103static struct mtx ng_device_mtx; 104MTX_SYSINIT(ng_device, &ng_device_mtx, "ng_device", MTX_DEF); 105 106/* Maximum number of NGD devices */ 107#define MAX_NGD 25 /* should be more than enough for now */ 108 109static d_close_t ngdclose; 110static d_open_t ngdopen; 111static d_read_t ngdread; 112static d_write_t ngdwrite; 113#if 0 114static d_ioctl_t ngdioctl; 115#endif 116static d_poll_t ngdpoll; 117 118static struct cdevsw ngd_cdevsw = { 119 .d_version = D_VERSION, 120 .d_open = ngdopen, 121 .d_close = ngdclose, 122 .d_read = ngdread, 123 .d_write = ngdwrite, 124#if 0 125 .d_ioctl = ngdioctl, 126#endif 127 .d_poll = ngdpoll, 128 .d_name = NG_DEVICE_DEVNAME, 129}; 130 131/* Helper functions */ 132static int get_free_unit(void); 133 134/****************************************************************************** 135 * Netgraph methods 136 ******************************************************************************/ 137 138/* 139 * create new node 140 */ 141static int 142ng_device_constructor(node_p node) 143{ 144 priv_p priv; 145 146 DBG; 147 148 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 149 if (priv == NULL) 150 return (ENOMEM); 151 152 mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF); 153 mtx_lock(&priv->ngd_mtx); 154 155 mtx_lock(&ng_device_mtx); 156 157 priv->unit = get_free_unit(); 158 if(priv->unit < 0) { 159 printf("%s: No free unit found by get_free_unit(), " 160 "increase MAX_NGD\n",__func__); 161 mtx_unlock(&ng_device_mtx); 162 mtx_destroy(&priv->ngd_mtx); 163 FREE(priv, M_NETGRAPH); 164 return(EINVAL); 165 } 166 167 priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT, 168 GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit); 169 if(priv->ngddev == NULL) { 170 printf("%s(): make_dev() failed\n",__func__); 171 mtx_unlock(&ng_device_mtx); 172 mtx_destroy(&priv->ngd_mtx); 173 FREE(priv, M_NETGRAPH); 174 return(EINVAL); 175 } 176 177 SLIST_INSERT_HEAD(&ngd_nodes, priv, links); 178 179 mtx_unlock(&ng_device_mtx); 180 181 mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); 182 IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); 183 184 /* Link everything together */ 185 NG_NODE_SET_PRIVATE(node, priv); 186 priv->node = node; 187 priv->ngddev->si_drv1 = priv; 188 189 mtx_unlock(&priv->ngd_mtx); 190 191 return(0); 192} 193 194/* 195 * Process control message. 196 */ 197 198static int 199ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook) 200{ 201 const priv_p priv = NG_NODE_PRIVATE(node); 202 struct ng_mesg *msg; 203 struct ng_mesg *resp = NULL; 204 int error = 0; 205 206 NGI_GET_MSG(item, msg); 207 208 if (msg->header.typecookie == NGM_DEVICE_COOKIE) { 209 switch (msg->header.cmd) { 210 case NGM_DEVICE_GET_DEVNAME: 211 /* XXX: Fix when NGD_MAX us bigger */ 212 NG_MKRESPONSE(resp, msg, 213 strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT); 214 215 if (resp == NULL) 216 ERROUT(ENOMEM); 217 218 strlcpy((char *)resp->data, priv->ngddev->si_name, 219 strlen(priv->ngddev->si_name) + 1); 220 break; 221 222 default: 223 error = EINVAL; 224 break; 225 } 226 } else 227 error = EINVAL; 228 229done: 230 NG_RESPOND_MSG(error, node, item, resp); 231 NG_FREE_MSG(msg); 232 return (error); 233} 234 235/* 236 * Accept incoming hook. We support only one hook per node. 237 */ 238static int 239ng_device_newhook(node_p node, hook_p hook, const char *name) 240{ 241 priv_p priv = NG_NODE_PRIVATE(node); 242 243 DBG; 244 245 /* We have only one hook per node */ 246 if (priv->hook != NULL) 247 return (EISCONN); 248 249 priv->hook = hook; 250 251 return(0); 252} 253 254/* 255 * Receive data from hook, write it to device. 256 */ 257static int 258ng_device_rcvdata(hook_p hook, item_p item) 259{ 260 priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 261 struct mbuf *m; 262 263 DBG; 264 265 NGI_GET_M(item, m); 266 NG_FREE_ITEM(item); 267 268 IF_LOCK(&priv->readq); 269 if (_IF_QFULL(&priv->readq)) { 270 _IF_DROP(&priv->readq); 271 IF_UNLOCK(&priv->readq); 272 NG_FREE_M(m); 273 return (ENOBUFS); 274 } 275 276 _IF_ENQUEUE(&priv->readq, m); 277 IF_UNLOCK(&priv->readq); 278 mtx_lock(&priv->ngd_mtx); 279 if (priv->flags & NGDF_RWAIT) { 280 priv->flags &= ~NGDF_RWAIT; 281 wakeup(priv); 282 } 283 mtx_unlock(&priv->ngd_mtx); 284 285 return(0); 286} 287 288/* 289 * Removal of the hook destroys the node. 290 */ 291static int 292ng_device_disconnect(hook_p hook) 293{ 294 priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 295 296 DBG; 297 298 destroy_dev(priv->ngddev); 299 mtx_destroy(&priv->ngd_mtx); 300 301 mtx_lock(&ng_device_mtx); 302 SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links); 303 mtx_unlock(&ng_device_mtx); 304 305 IF_DRAIN(&priv->readq); 306 mtx_destroy(&(priv)->readq.ifq_mtx); 307 308 FREE(priv, M_NETGRAPH); 309 310 ng_rmnode_self(NG_HOOK_NODE(hook)); 311 312 return(0); 313} 314 315/* 316 * Node shutdown. Everything is already done in disconnect method. 317 */ 318static int 319ng_device_shutdown(node_p node) 320{ 321 NG_NODE_UNREF(node); 322 return (0); 323} 324 325/****************************************************************************** 326 * Device methods 327 ******************************************************************************/ 328 329/* 330 * the device is opened 331 */ 332static int 333ngdopen(struct cdev *dev, int flag, int mode, struct thread *td) 334{ 335 priv_p priv = (priv_p )dev->si_drv1; 336 337 DBG; 338 339 mtx_lock(&priv->ngd_mtx); 340 priv->flags |= NGDF_OPEN; 341 mtx_unlock(&priv->ngd_mtx); 342 343 return(0); 344} 345 346/* 347 * the device is closed 348 */ 349static int 350ngdclose(struct cdev *dev, int flag, int mode, struct thread *td) 351{ 352 priv_p priv = (priv_p )dev->si_drv1; 353 354 DBG; 355 mtx_lock(&priv->ngd_mtx); 356 priv->flags &= ~NGDF_OPEN; 357 mtx_unlock(&priv->ngd_mtx); 358 359 return(0); 360} 361 362#if 0 /* 363 * The ioctl is transformed into netgraph control message. 364 * We do not process them, yet. 365 */ 366/* 367 * process ioctl 368 * 369 * they are translated into netgraph messages and passed on 370 * 371 */ 372static int 373ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 374{ 375 struct ngd_softc *sc = &ngd_softc; 376 struct ngd_connection * connection = NULL; 377 struct ngd_connection * tmp; 378 int error = 0; 379 struct ng_mesg *msg; 380 struct ngd_param_s * datap; 381 382 DBG; 383 384 SLIST_FOREACH(tmp,&sc->head,links) { 385 if(tmp->ngddev == dev) { 386 connection = tmp; 387 } 388 } 389 if(connection == NULL) { 390 printf("%s(): connection is still NULL, no dev found\n",__func__); 391 return(-1); 392 } 393 394 NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 395 M_NOWAIT); 396 if (msg == NULL) { 397 printf("%s(): msg == NULL\n",__func__); 398 goto nomsg; 399 } 400 401 /* pass the ioctl data into the ->data area */ 402 datap = (struct ngd_param_s *)msg->data; 403 datap->p = addr; 404 405 NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0); 406 if(error) 407 printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error); 408 409nomsg: 410 411 return(0); 412} 413#endif /* if 0 */ 414 415/* 416 * This function is called when a read(2) is done to our device. 417 * We process one mbuf from queue. 418 */ 419static int 420ngdread(struct cdev *dev, struct uio *uio, int flag) 421{ 422 priv_p priv = (priv_p )dev->si_drv1; 423 struct mbuf *m; 424 int len, error = 0; 425 426 DBG; 427 428 /* get an mbuf */ 429 do { 430 IF_DEQUEUE(&priv->readq, m); 431 if (m == NULL) { 432 if (flag & IO_NDELAY) 433 return (EWOULDBLOCK); 434 mtx_lock(&priv->ngd_mtx); 435 priv->flags |= NGDF_RWAIT; 436 mtx_unlock(&priv->ngd_mtx); 437 if ((error = tsleep(priv, PCATCH | (PZERO + 1), 438 "ngdread", 0)) != 0) 439 return (error); 440 } 441 } while (m == NULL); 442 443 while (m && uio->uio_resid > 0 && error == 0) { 444 len = MIN(uio->uio_resid, m->m_len); 445 if (len != 0) 446 error = uiomove(mtod(m, void *), len, uio); 447 m = m_free(m); 448 } 449 450 if (m) 451 m_freem(m); 452 453 return (error); 454} 455 456 457/* 458 * This function is called when our device is written to. 459 * We read the data from userland into mbuf chain and pass it to the remote hook. 460 * 461 */ 462static int 463ngdwrite(struct cdev *dev, struct uio *uio, int flag) 464{ 465 priv_p priv = (priv_p )dev->si_drv1; 466 struct mbuf *m; 467 int error = 0; 468 469 DBG; 470 471 if (uio->uio_resid == 0) 472 return (0); 473 474 if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET) 475 return (EIO); 476 477 if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL) 478 return (ENOBUFS); 479 480 NG_SEND_DATA_ONLY(error, priv->hook, m); 481 482 return (error); 483} 484 485/* 486 * we are being polled/selected 487 * check if there is data available for read 488 */ 489static int 490ngdpoll(struct cdev *dev, int events, struct thread *td) 491{ 492 priv_p priv = (priv_p )dev->si_drv1; 493 int revents = 0; 494 495 if (events & (POLLIN | POLLRDNORM) && 496 !IFQ_IS_EMPTY(&priv->readq)) 497 revents |= events & (POLLIN | POLLRDNORM); 498 499 return (revents); 500} 501 502/****************************************************************************** 503 * Helper subroutines 504 ******************************************************************************/ 505 506static int 507get_free_unit() 508{ 509 struct ngd_private *priv = NULL; 510 int n = 0; 511 int unit = -1; 512 513 DBG; 514 515 mtx_assert(&ng_device_mtx, MA_OWNED); 516 517 /* When there is no list yet, the first device unit is always 0. */ 518 if SLIST_EMPTY(&ngd_nodes) 519 return(0); 520 521 /* Just do a brute force loop to find the first free unit that is 522 * smaller than MAX_NGD. 523 * Set MAX_NGD to a large value, doesn't impact performance. 524 */ 525 for(n = 0; n<MAX_NGD && unit == -1; n++) { 526 SLIST_FOREACH(priv, &ngd_nodes, links) { 527 528 if(priv->unit == n) { 529 unit = -1; 530 break; 531 } 532 unit = n; 533 } 534 } 535 536 return (unit); 537} 538