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_source.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * ng_source.c
    3  */
    4 
    5 /*-
    6  * Copyright (c) 2005 Gleb Smirnoff <glebius@FreeBSD.org>
    7  * Copyright 2002 Sandvine Inc.
    8  * All rights reserved.
    9  *
   10  * Subject to the following obligations and disclaimer of warranty, use and
   11  * redistribution of this software, in source or object code forms, with or
   12  * without modifications are expressly permitted by Sandvine Inc.; provided,
   13  * however, that:
   14  * 1. Any and all reproductions of the source or object code must include the
   15  *    copyright notice above and the following disclaimer of warranties; and
   16  * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
   17  *    trademarks, including the mark "SANDVINE" on advertising, endorsements,
   18  *    or otherwise except as such appears in the above copyright notice or in
   19  *    the software.
   20  *
   21  * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
   22  * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES,
   23  * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION,
   24  * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
   25  * PURPOSE, OR NON-INFRINGEMENT.  SANDVINE DOES NOT WARRANT, GUARANTEE, OR
   26  * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
   27  * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
   28  * OR OTHERWISE.  IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
   29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
   30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
   32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
   33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   35  * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
   36  * DAMAGE.
   37  *
   38  * Author: Dave Chapeskie
   39  */
   40 
   41 #include <sys/cdefs.h>
   42 __FBSDID("$FreeBSD: releng/11.1/sys/netgraph/ng_source.c 298813 2016-04-29 21:25:05Z pfg $");
   43 
   44 /*
   45  * This node is used for high speed packet geneneration.  It queues
   46  * all data received on its 'input' hook and when told to start via
   47  * a control message it sends the packets out its 'output' hook.  In
   48  * this way this node can be preloaded with a packet stream which it
   49  * can then send continuously as fast as possible.
   50  *
   51  * Currently it just copies the mbufs as required.  It could do various
   52  * tricks to try and avoid this.  Probably the best performance would
   53  * be achieved by modifying the appropriate drivers to be told to
   54  * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
   55  * transmit descriptors) under control of this node; perhaps via some
   56  * flag in the mbuf or some such.  The node could peek at an appropriate
   57  * ifnet flag to see if such support is available for the connected
   58  * interface.
   59  */
   60 
   61 #include <sys/param.h>
   62 #include <sys/systm.h>
   63 #include <sys/errno.h>
   64 #include <sys/kernel.h>
   65 #include <sys/malloc.h>
   66 #include <sys/mbuf.h>
   67 #include <sys/socket.h>
   68 #include <sys/syslog.h>
   69 #include <net/if.h>
   70 #include <net/if_var.h>
   71 #include <netgraph/ng_message.h>
   72 #include <netgraph/netgraph.h>
   73 #include <netgraph/ng_parse.h>
   74 #include <netgraph/ng_ether.h>
   75 #include <netgraph/ng_source.h>
   76 
   77 #define NG_SOURCE_INTR_TICKS            1
   78 #define NG_SOURCE_DRIVER_IFQ_MAXLEN     (4*1024)
   79 
   80 #define mtod_off(m,off,t)       ((t)(mtod((m),caddr_t)+(off)))
   81 
   82 /* Per node info */
   83 struct privdata {
   84         node_p                          node;
   85         hook_p                          input;
   86         hook_p                          output;
   87         struct ng_source_stats          stats;
   88         struct ifqueue                  snd_queue;      /* packets to send */
   89         struct mbuf                     *last_packet;   /* last pkt in queue */
   90         struct ifnet                    *output_ifp;
   91         struct callout                  intr_ch;
   92         uint64_t                        packets;        /* packets to send */
   93         uint32_t                        queueOctets;
   94         struct ng_source_embed_info     embed_timestamp;
   95         struct ng_source_embed_cnt_info embed_counter[NG_SOURCE_COUNTERS];
   96 };
   97 typedef struct privdata *sc_p;
   98 
   99 /* Node flags */
  100 #define NG_SOURCE_ACTIVE        (NGF_TYPE1)
  101 
  102 /* Netgraph methods */
  103 static ng_constructor_t ng_source_constructor;
  104 static ng_rcvmsg_t      ng_source_rcvmsg;
  105 static ng_shutdown_t    ng_source_rmnode;
  106 static ng_newhook_t     ng_source_newhook;
  107 static ng_connect_t     ng_source_connect;
  108 static ng_rcvdata_t     ng_source_rcvdata;
  109 static ng_disconnect_t  ng_source_disconnect;
  110 
  111 /* Other functions */
  112 static void             ng_source_intr(node_p, hook_p, void *, int);
  113 static void             ng_source_clr_data (sc_p);
  114 static int              ng_source_start (sc_p, uint64_t);
  115 static void             ng_source_stop (sc_p);
  116 static int              ng_source_send (sc_p, int, int *);
  117 static int              ng_source_store_output_ifp(sc_p, char *);
  118 static void             ng_source_packet_mod(sc_p, struct mbuf *,
  119                             int, int, caddr_t, int);
  120 static void             ng_source_mod_counter(sc_p sc,
  121                             struct ng_source_embed_cnt_info *cnt,
  122                             struct mbuf *m, int increment);
  123 static int              ng_source_dup_mod(sc_p, struct mbuf *,
  124                             struct mbuf **);
  125 
  126 /* Parse type for timeval */
  127 static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = {
  128         { "tv_sec",             &ng_parse_int32_type    },
  129         { "tv_usec",            &ng_parse_int32_type    },
  130         { NULL }
  131 };
  132 const struct ng_parse_type ng_source_timeval_type = {
  133         &ng_parse_struct_type,
  134         &ng_source_timeval_type_fields
  135 };
  136 
  137 /* Parse type for struct ng_source_stats */
  138 static const struct ng_parse_struct_field ng_source_stats_type_fields[]
  139         = NG_SOURCE_STATS_TYPE_INFO;
  140 static const struct ng_parse_type ng_source_stats_type = {
  141         &ng_parse_struct_type,
  142         &ng_source_stats_type_fields
  143 };
  144 
  145 /* Parse type for struct ng_source_embed_info */
  146 static const struct ng_parse_struct_field ng_source_embed_type_fields[] =
  147         NG_SOURCE_EMBED_TYPE_INFO;
  148 static const struct ng_parse_type ng_source_embed_type = {
  149         &ng_parse_struct_type,
  150         &ng_source_embed_type_fields
  151 };
  152 
  153 /* Parse type for struct ng_source_embed_cnt_info */
  154 static const struct ng_parse_struct_field ng_source_embed_cnt_type_fields[] =
  155         NG_SOURCE_EMBED_CNT_TYPE_INFO;
  156 static const struct ng_parse_type ng_source_embed_cnt_type = {
  157         &ng_parse_struct_type,
  158         &ng_source_embed_cnt_type_fields
  159 };
  160 
  161 /* List of commands and how to convert arguments to/from ASCII */
  162 static const struct ng_cmdlist ng_source_cmds[] = {
  163         {
  164           NGM_SOURCE_COOKIE,
  165           NGM_SOURCE_GET_STATS,
  166           "getstats",
  167           NULL,
  168           &ng_source_stats_type
  169         },
  170         {
  171           NGM_SOURCE_COOKIE,
  172           NGM_SOURCE_CLR_STATS,
  173           "clrstats",
  174           NULL,
  175           NULL
  176         },
  177         {
  178           NGM_SOURCE_COOKIE,
  179           NGM_SOURCE_GETCLR_STATS,
  180           "getclrstats",
  181           NULL,
  182           &ng_source_stats_type
  183         },
  184         {
  185           NGM_SOURCE_COOKIE,
  186           NGM_SOURCE_START,
  187           "start",
  188           &ng_parse_uint64_type,
  189           NULL
  190         },
  191         {
  192           NGM_SOURCE_COOKIE,
  193           NGM_SOURCE_STOP,
  194           "stop",
  195           NULL,
  196           NULL
  197         },
  198         {
  199           NGM_SOURCE_COOKIE,
  200           NGM_SOURCE_CLR_DATA,
  201           "clrdata",
  202           NULL,
  203           NULL
  204         },
  205         {
  206           NGM_SOURCE_COOKIE,
  207           NGM_SOURCE_SETIFACE,
  208           "setiface",
  209           &ng_parse_string_type,
  210           NULL
  211         },
  212         {
  213           NGM_SOURCE_COOKIE,
  214           NGM_SOURCE_SETPPS,
  215           "setpps",
  216           &ng_parse_uint32_type,
  217           NULL
  218         },
  219         {
  220           NGM_SOURCE_COOKIE,
  221           NGM_SOURCE_SET_TIMESTAMP,
  222           "settimestamp",
  223           &ng_source_embed_type,
  224           NULL
  225         },
  226         {
  227           NGM_SOURCE_COOKIE,
  228           NGM_SOURCE_GET_TIMESTAMP,
  229           "gettimestamp",
  230           NULL,
  231           &ng_source_embed_type
  232         },
  233         {
  234           NGM_SOURCE_COOKIE,
  235           NGM_SOURCE_SET_COUNTER,
  236           "setcounter",
  237           &ng_source_embed_cnt_type,
  238           NULL
  239         },
  240         {
  241           NGM_SOURCE_COOKIE,
  242           NGM_SOURCE_GET_COUNTER,
  243           "getcounter",
  244           &ng_parse_uint8_type,
  245           &ng_source_embed_cnt_type
  246         },
  247         { 0 }
  248 };
  249 
  250 /* Netgraph type descriptor */
  251 static struct ng_type ng_source_typestruct = {
  252         .version =      NG_ABI_VERSION,
  253         .name =         NG_SOURCE_NODE_TYPE,
  254         .constructor =  ng_source_constructor,
  255         .rcvmsg =       ng_source_rcvmsg,
  256         .shutdown =     ng_source_rmnode,
  257         .newhook =      ng_source_newhook,
  258         .connect =      ng_source_connect,
  259         .rcvdata =      ng_source_rcvdata,
  260         .disconnect =   ng_source_disconnect,
  261         .cmdlist =      ng_source_cmds,
  262 };
  263 NETGRAPH_INIT(source, &ng_source_typestruct);
  264 
  265 static int ng_source_set_autosrc(sc_p, uint32_t);
  266 
  267 /*
  268  * Node constructor
  269  */
  270 static int
  271 ng_source_constructor(node_p node)
  272 {
  273         sc_p sc;
  274 
  275         sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
  276 
  277         NG_NODE_SET_PRIVATE(node, sc);
  278         sc->node = node;
  279         sc->snd_queue.ifq_maxlen = 2048;        /* XXX not checked */
  280         ng_callout_init(&sc->intr_ch);
  281 
  282         return (0);
  283 }
  284 
  285 /*
  286  * Add a hook
  287  */
  288 static int
  289 ng_source_newhook(node_p node, hook_p hook, const char *name)
  290 {
  291         sc_p sc = NG_NODE_PRIVATE(node);
  292 
  293         if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
  294                 sc->input = hook;
  295         } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
  296                 sc->output = hook;
  297                 sc->output_ifp = NULL;
  298                 bzero(&sc->stats, sizeof(sc->stats));
  299         } else
  300                 return (EINVAL);
  301 
  302         return (0);
  303 }
  304 
  305 /*
  306  * Hook has been added
  307  */
  308 static int
  309 ng_source_connect(hook_p hook)
  310 {
  311         sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  312         struct ng_mesg *msg;
  313         int dummy_error = 0;
  314 
  315         /*
  316          * If this is "output" hook, then request information
  317          * from our downstream.
  318          */
  319         if (hook == sc->output) {
  320                 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME,
  321                     0, M_NOWAIT);
  322                 if (msg == NULL)
  323                         return (ENOBUFS);
  324 
  325                 /*
  326                  * Our hook and peer hook have HK_INVALID flag set,
  327                  * so we can't use NG_SEND_MSG_HOOK() macro here.
  328                  */
  329                 NG_SEND_MSG_ID(dummy_error, sc->node, msg,
  330                     NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node));
  331         }
  332 
  333         return (0);
  334 }
  335 
  336 /*
  337  * Receive a control message
  338  */
  339 static int
  340 ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
  341 {
  342         sc_p sc = NG_NODE_PRIVATE(node);
  343         struct ng_mesg *msg, *resp = NULL;
  344         int error = 0;
  345 
  346         NGI_GET_MSG(item, msg);
  347 
  348         switch (msg->header.typecookie) {
  349         case NGM_SOURCE_COOKIE:
  350                 if (msg->header.flags & NGF_RESP) {
  351                         error = EINVAL;
  352                         break;
  353                 }
  354                 switch (msg->header.cmd) {
  355                 case NGM_SOURCE_GET_STATS:
  356                 case NGM_SOURCE_CLR_STATS:
  357                 case NGM_SOURCE_GETCLR_STATS:
  358                     {
  359                         struct ng_source_stats *stats;
  360 
  361                         if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
  362                                 NG_MKRESPONSE(resp, msg,
  363                                     sizeof(*stats), M_NOWAIT);
  364                                 if (resp == NULL) {
  365                                         error = ENOMEM;
  366                                         goto done;
  367                                 }
  368                                 sc->stats.queueOctets = sc->queueOctets;
  369                                 sc->stats.queueFrames = sc->snd_queue.ifq_len;
  370                                 if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
  371                                     && !timevalisset(&sc->stats.endTime)) {
  372                                         getmicrotime(&sc->stats.elapsedTime);
  373                                         timevalsub(&sc->stats.elapsedTime,
  374                                             &sc->stats.startTime);
  375                                 }
  376                                 stats = (struct ng_source_stats *)resp->data;
  377                                 bcopy(&sc->stats, stats, sizeof(* stats));
  378                         }
  379                         if (msg->header.cmd != NGM_SOURCE_GET_STATS)
  380                                 bzero(&sc->stats, sizeof(sc->stats));
  381                     }
  382                     break;
  383                 case NGM_SOURCE_START:
  384                     {
  385                         uint64_t packets;
  386 
  387                         if (msg->header.arglen != sizeof(uint64_t)) {
  388                                 error = EINVAL;
  389                                 break;
  390                         }
  391 
  392                         packets = *(uint64_t *)msg->data;
  393 
  394                         error = ng_source_start(sc, packets);
  395 
  396                         break;
  397                     }
  398                 case NGM_SOURCE_STOP:
  399                         ng_source_stop(sc);
  400                         break;
  401                 case NGM_SOURCE_CLR_DATA:
  402                         ng_source_clr_data(sc);
  403                         break;
  404                 case NGM_SOURCE_SETIFACE:
  405                     {
  406                         char *ifname = (char *)msg->data;
  407 
  408                         if (msg->header.arglen < 2) {
  409                                 error = EINVAL;
  410                                 break;
  411                         }
  412 
  413                         ng_source_store_output_ifp(sc, ifname);
  414                         break;
  415                     }
  416                 case NGM_SOURCE_SETPPS:
  417                     {
  418                         uint32_t pps;
  419 
  420                         if (msg->header.arglen != sizeof(uint32_t)) {
  421                                 error = EINVAL;
  422                                 break;
  423                         }
  424 
  425                         pps = *(uint32_t *)msg->data;
  426 
  427                         sc->stats.maxPps = pps;
  428 
  429                         break;
  430                     }
  431                 case NGM_SOURCE_SET_TIMESTAMP:
  432                     {
  433                         struct ng_source_embed_info *embed;
  434 
  435                         if (msg->header.arglen != sizeof(*embed)) {
  436                                 error = EINVAL;
  437                                 goto done;
  438                         }
  439                         embed = (struct ng_source_embed_info *)msg->data;
  440                         bcopy(embed, &sc->embed_timestamp, sizeof(*embed));
  441 
  442                         break;
  443                     }
  444                 case NGM_SOURCE_GET_TIMESTAMP:
  445                     {
  446                         struct ng_source_embed_info *embed;
  447 
  448                         NG_MKRESPONSE(resp, msg, sizeof(*embed), M_NOWAIT);
  449                         if (resp == NULL) {
  450                                 error = ENOMEM;
  451                                 goto done;
  452                         }
  453                         embed = (struct ng_source_embed_info *)resp->data;
  454                         bcopy(&sc->embed_timestamp, embed, sizeof(*embed));
  455 
  456                         break;
  457                     }
  458                 case NGM_SOURCE_SET_COUNTER:
  459                     {
  460                         struct ng_source_embed_cnt_info *embed;
  461 
  462                         if (msg->header.arglen != sizeof(*embed)) {
  463                                 error = EINVAL;
  464                                 goto done;
  465                         }
  466                         embed = (struct ng_source_embed_cnt_info *)msg->data;
  467                         if (embed->index >= NG_SOURCE_COUNTERS ||
  468                             !(embed->width == 1 || embed->width == 2 ||
  469                             embed->width == 4)) {
  470                                 error = EINVAL;
  471                                 goto done;
  472                         }
  473                         bcopy(embed, &sc->embed_counter[embed->index],
  474                             sizeof(*embed));
  475 
  476                         break;
  477                     }
  478                 case NGM_SOURCE_GET_COUNTER:
  479                     {
  480                         uint8_t index = *(uint8_t *)msg->data;
  481                         struct ng_source_embed_cnt_info *embed;
  482 
  483                         if (index >= NG_SOURCE_COUNTERS) {
  484                                 error = EINVAL;
  485                                 goto done;
  486                         }
  487                         NG_MKRESPONSE(resp, msg, sizeof(*embed), M_NOWAIT);
  488                         if (resp == NULL) {
  489                                 error = ENOMEM;
  490                                 goto done;
  491                         }
  492                         embed = (struct ng_source_embed_cnt_info *)resp->data;
  493                         bcopy(&sc->embed_counter[index], embed, sizeof(*embed));
  494 
  495                         break;
  496                     }
  497                 default:
  498                         error = EINVAL;
  499                         break;
  500                 }
  501                 break;
  502         case NGM_ETHER_COOKIE:
  503                 if (!(msg->header.flags & NGF_RESP)) {
  504                         error = EINVAL;
  505                         break;
  506                 }
  507                 switch (msg->header.cmd) {
  508                 case NGM_ETHER_GET_IFNAME:
  509                     {
  510                         char *ifname = (char *)msg->data;
  511 
  512                         if (msg->header.arglen < 2) {
  513                                 error = EINVAL;
  514                                 break;
  515                         }
  516 
  517                         if (ng_source_store_output_ifp(sc, ifname) == 0)
  518                                 ng_source_set_autosrc(sc, 0);
  519                         break;
  520                     }
  521                 default:
  522                         error = EINVAL;
  523                 }
  524                 break;
  525         default:
  526                 error = EINVAL;
  527                 break;
  528         }
  529 
  530 done:
  531         /* Take care of synchronous response, if any. */
  532         NG_RESPOND_MSG(error, node, item, resp);
  533         /* Free the message and return. */
  534         NG_FREE_MSG(msg);
  535         return (error);
  536 }
  537 
  538 /*
  539  * Receive data on a hook
  540  *
  541  * If data comes in the input hook, enqueue it on the send queue.
  542  * If data comes in the output hook, discard it.
  543  */
  544 static int
  545 ng_source_rcvdata(hook_p hook, item_p item)
  546 {
  547         sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  548         struct mbuf *m;
  549         int error = 0;
  550 
  551         NGI_GET_M(item, m);
  552         NG_FREE_ITEM(item);
  553 
  554         /* Which hook? */
  555         if (hook == sc->output) {
  556                 /* discard */
  557                 NG_FREE_M(m);
  558                 return (error);
  559         }
  560         KASSERT(hook == sc->input, ("%s: no hook!", __func__));
  561 
  562         /* Enqueue packet. */
  563         /* XXX should we check IF_QFULL() ? */
  564         _IF_ENQUEUE(&sc->snd_queue, m);
  565         sc->queueOctets += m->m_pkthdr.len;
  566         sc->last_packet = m;
  567 
  568         return (0);
  569 }
  570 
  571 /*
  572  * Shutdown processing
  573  */
  574 static int
  575 ng_source_rmnode(node_p node)
  576 {
  577         sc_p sc = NG_NODE_PRIVATE(node);
  578 
  579         ng_source_stop(sc);
  580         ng_source_clr_data(sc);
  581         NG_NODE_SET_PRIVATE(node, NULL);
  582         NG_NODE_UNREF(node);
  583         free(sc, M_NETGRAPH);
  584 
  585         return (0);
  586 }
  587 
  588 /*
  589  * Hook disconnection
  590  */
  591 static int
  592 ng_source_disconnect(hook_p hook)
  593 {
  594         sc_p sc;
  595 
  596         sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  597         KASSERT(sc != NULL, ("%s: null node private", __func__));
  598         if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output)
  599                 ng_rmnode_self(NG_HOOK_NODE(hook));
  600         return (0);
  601 }
  602 
  603 /*
  604  * Set sc->output_ifp to point to the struct ifnet of the interface
  605  * reached via our output hook.
  606  */
  607 static int
  608 ng_source_store_output_ifp(sc_p sc, char *ifname)
  609 {
  610         struct ifnet *ifp;
  611 
  612         ifp = ifunit(ifname);
  613 
  614         if (ifp == NULL) {
  615                 printf("%s: can't find interface %s\n", __func__, ifname);
  616                 return (EINVAL);
  617         }
  618         sc->output_ifp = ifp;
  619 
  620 #if 1
  621         /* XXX mucking with a drivers ifqueue size is ugly but we need it
  622          * to queue a lot of packets to get close to line rate on a gigabit
  623          * interface with small packets.
  624          * XXX we should restore the original value at stop or disconnect
  625          */
  626         if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) {
  627                 printf("ng_source: changing ifq_maxlen from %d to %d\n",
  628                     ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
  629                 ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
  630         }
  631 #endif
  632         return (0);
  633 }
  634 
  635 /*
  636  * Set the attached ethernet node's ethernet source address override flag.
  637  */
  638 static int
  639 ng_source_set_autosrc(sc_p sc, uint32_t flag)
  640 {
  641         struct ng_mesg *msg;
  642         int error = 0;
  643 
  644         NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
  645             sizeof (uint32_t), M_NOWAIT);
  646         if (msg == NULL)
  647                 return(ENOBUFS);
  648 
  649         *(uint32_t *)msg->data = flag;
  650         NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0);
  651         return (error);
  652 }
  653 
  654 /*
  655  * Clear out the data we've queued
  656  */
  657 static void
  658 ng_source_clr_data (sc_p sc)
  659 {
  660         struct mbuf *m;
  661 
  662         for (;;) {
  663                 _IF_DEQUEUE(&sc->snd_queue, m);
  664                 if (m == NULL)
  665                         break;
  666                 NG_FREE_M(m);
  667         }
  668         sc->queueOctets = 0;
  669         sc->last_packet = NULL;
  670 }
  671 
  672 /*
  673  * Start sending queued data out the output hook
  674  */
  675 static int
  676 ng_source_start(sc_p sc, uint64_t packets)
  677 {
  678         if (sc->output_ifp == NULL) {
  679                 printf("ng_source: start without iface configured\n");
  680                 return (ENXIO);
  681         }
  682 
  683         if (sc->node->nd_flags & NG_SOURCE_ACTIVE)
  684                 return (EBUSY);
  685 
  686         sc->node->nd_flags |= NG_SOURCE_ACTIVE;
  687 
  688         sc->packets = packets;
  689         timevalclear(&sc->stats.elapsedTime);
  690         timevalclear(&sc->stats.endTime);
  691         getmicrotime(&sc->stats.startTime);
  692         getmicrotime(&sc->stats.lastTime);
  693         ng_callout(&sc->intr_ch, sc->node, NULL, 0,
  694             ng_source_intr, sc, 0);
  695 
  696         return (0);
  697 }
  698 
  699 /*
  700  * Stop sending queued data out the output hook
  701  */
  702 static void
  703 ng_source_stop(sc_p sc)
  704 {
  705         ng_uncallout(&sc->intr_ch, sc->node);
  706         sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
  707         getmicrotime(&sc->stats.endTime);
  708         sc->stats.elapsedTime = sc->stats.endTime;
  709         timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
  710 }
  711 
  712 /*
  713  * While active called every NG_SOURCE_INTR_TICKS ticks.
  714  * Sends as many packets as the interface connected to our
  715  * output hook is able to enqueue.
  716  */
  717 static void
  718 ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2)
  719 {
  720         sc_p sc = (sc_p)arg1;
  721         struct ifqueue *ifq;
  722         int packets;
  723 
  724         KASSERT(sc != NULL, ("%s: null node private", __func__));
  725 
  726         if (sc->packets == 0 || sc->output == NULL
  727             || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
  728                 ng_source_stop(sc);
  729                 return;
  730         }
  731 
  732         if (sc->output_ifp != NULL) {
  733                 ifq = (struct ifqueue *)&sc->output_ifp->if_snd;
  734                 packets = ifq->ifq_maxlen - ifq->ifq_len;
  735         } else
  736                 packets = sc->snd_queue.ifq_len;
  737 
  738         if (sc->stats.maxPps != 0) {
  739                 struct timeval  now, elapsed;
  740                 uint64_t        usec;
  741                 int             maxpkt;
  742 
  743                 getmicrotime(&now);
  744                 elapsed = now;
  745                 timevalsub(&elapsed, &sc->stats.lastTime);
  746                 usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec;
  747                 maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000;
  748                 sc->stats.lastTime = now;
  749                 if (packets > maxpkt)
  750                         packets = maxpkt;
  751         }
  752 
  753         ng_source_send(sc, packets, NULL);
  754         if (sc->packets == 0)
  755                 ng_source_stop(sc);
  756         else
  757                 ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS,
  758                     ng_source_intr, sc, 0);
  759 }
  760 
  761 /*
  762  * Send packets out our output hook.
  763  */
  764 static int
  765 ng_source_send(sc_p sc, int tosend, int *sent_p)
  766 {
  767         struct mbuf *m, *m2;
  768         int sent;
  769         int error = 0;
  770 
  771         KASSERT(tosend >= 0, ("%s: negative tosend param", __func__));
  772         KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
  773             ("%s: inactive node", __func__));
  774 
  775         if ((uint64_t)tosend > sc->packets)
  776                 tosend = sc->packets;
  777 
  778         /* Go through the queue sending packets one by one. */
  779         for (sent = 0; error == 0 && sent < tosend; ++sent) {
  780                 _IF_DEQUEUE(&sc->snd_queue, m);
  781                 if (m == NULL)
  782                         break;
  783 
  784                 /* Duplicate and modify the packet. */
  785                 error = ng_source_dup_mod(sc, m, &m2);
  786                 if (error) {
  787                         if (error == ENOBUFS)
  788                                 _IF_PREPEND(&sc->snd_queue, m);
  789                         else
  790                                 _IF_ENQUEUE(&sc->snd_queue, m);
  791                         break;
  792                 }
  793 
  794                 /* Re-enqueue the original packet for us. */
  795                 _IF_ENQUEUE(&sc->snd_queue, m);
  796 
  797                 sc->stats.outFrames++;
  798                 sc->stats.outOctets += m2->m_pkthdr.len;
  799                 NG_SEND_DATA_ONLY(error, sc->output, m2);
  800                 if (error)
  801                         break;
  802         }
  803 
  804         sc->packets -= sent;
  805         if (sent_p != NULL)
  806                 *sent_p = sent;
  807         return (error);
  808 }
  809 
  810 /*
  811  * Modify packet in 'm' by changing 'len' bytes starting at 'offset'
  812  * to data in 'cp'.
  813  *
  814  * The packet data in 'm' must be in a contiguous buffer in a single mbuf.
  815  */
  816 static void
  817 ng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp,
  818     int flags)
  819 {
  820         if (len == 0)
  821                 return;
  822 
  823         /* Can't modify beyond end of packet. */
  824         /* TODO: Pad packet for this case. */
  825         if (offset + len > m->m_len)
  826                 return;
  827 
  828         bcopy(cp, mtod_off(m, offset, caddr_t), len);
  829 }
  830 
  831 static void
  832 ng_source_mod_counter(sc_p sc, struct ng_source_embed_cnt_info *cnt,
  833     struct mbuf *m, int increment)
  834 {
  835         caddr_t cp;
  836         uint32_t val;
  837 
  838         val = htonl(cnt->next_val);
  839         cp = (caddr_t)&val + sizeof(val) - cnt->width;
  840         ng_source_packet_mod(sc, m, cnt->offset, cnt->width, cp, cnt->flags);
  841 
  842         if (increment) {
  843                 cnt->next_val += increment;
  844 
  845                 if (increment > 0 && cnt->next_val > cnt->max_val) {
  846                         cnt->next_val = cnt->min_val - 1 +
  847                             (cnt->next_val - cnt->max_val);
  848                         if (cnt->next_val > cnt->max_val)
  849                                 cnt->next_val = cnt->max_val;
  850                 } else if (increment < 0 && cnt->next_val < cnt->min_val) {
  851                         cnt->next_val = cnt->max_val + 1 +
  852                             (cnt->next_val - cnt->min_val);
  853                         if (cnt->next_val < cnt->min_val)
  854                                 cnt->next_val = cnt->max_val;
  855                 }
  856         }
  857 }
  858 
  859 static int
  860 ng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr)
  861 {
  862         struct mbuf *m;
  863         struct ng_source_embed_cnt_info *cnt;
  864         struct ng_source_embed_info *ts;
  865         int modify;
  866         int error = 0;
  867         int i, increment;
  868 
  869         /* Are we going to modify packets? */
  870         modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE;
  871         for (i = 0; !modify && i < NG_SOURCE_COUNTERS; ++i)
  872                 modify = sc->embed_counter[i].flags & NGM_SOURCE_EMBED_ENABLE;
  873 
  874         /* Duplicate the packet. */
  875         if (modify)
  876                 m = m_dup(m0, M_NOWAIT);
  877         else
  878                 m = m_copypacket(m0, M_NOWAIT);
  879         if (m == NULL) {
  880                 error = ENOBUFS;
  881                 goto done;
  882         }
  883         *m_ptr = m;
  884 
  885         if (!modify)
  886                 goto done;
  887 
  888         /* Modify the copied packet for sending. */
  889         KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__));
  890 
  891         for (i = 0; i < NG_SOURCE_COUNTERS; ++i) {
  892                 cnt = &sc->embed_counter[i];
  893                 if (cnt->flags & NGM_SOURCE_EMBED_ENABLE) {
  894                         if ((cnt->flags & NGM_SOURCE_INC_CNT_PER_LIST) == 0 ||
  895                             sc->last_packet == m0)
  896                                 increment = cnt->increment;
  897                         else
  898                                 increment = 0;
  899                         ng_source_mod_counter(sc, cnt, m, increment);
  900                 }
  901         }
  902 
  903         ts = &sc->embed_timestamp;
  904         if (ts->flags & NGM_SOURCE_EMBED_ENABLE) {
  905                 struct timeval now;
  906                 getmicrotime(&now);
  907                 now.tv_sec = htonl(now.tv_sec);
  908                 now.tv_usec = htonl(now.tv_usec);
  909                 ng_source_packet_mod(sc, m, ts->offset, sizeof (now),
  910                     (caddr_t)&now, ts->flags);
  911         }
  912 
  913 done:
  914         return(error);
  915 }

Cache object: cf8d798671f05382897e9bb818bd6af8


[ 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.