The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netgraph/ng_device.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 770ccbf5b0c5bd3572e2d32ecb36950c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.