diff options
author | julian <julian@FreeBSD.org> | 2002-10-31 23:03:09 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 2002-10-31 23:03:09 +0000 |
commit | 2932d3587e1bb2c35e5123f5dbd2e5842a68ba95 (patch) | |
tree | e7b1b606e497af5c80860da5918f9d71822c4f90 | |
parent | b2bc94cc0d79ef0ae249eac8fcd4547ca9e63c1d (diff) | |
download | FreeBSD-src-2932d3587e1bb2c35e5123f5dbd2e5842a68ba95.zip FreeBSD-src-2932d3587e1bb2c35e5123f5dbd2e5842a68ba95.tar.gz |
Add the netgraph 'source' module.
This is NOT YET CONVERTED TO -current.
This node is a source for preprogrammed packets at a known rate for testing.
I will convert it to -current "in place" but will MFC teh original
pre-conversion variant as that is what is originally submitted.
Man page my me, info from Dave's README.
Submitted by: Dave Chapeskie <dchapeskie@SANDVINE.com>
Obtained from: Sandvine inc.
MFC after: 1 week
-rw-r--r-- | share/man/man4/ng_source.4 | 264 | ||||
-rw-r--r-- | sys/modules/netgraph/source/Makefile | 8 | ||||
-rw-r--r-- | sys/netgraph/ng_source.c | 682 | ||||
-rw-r--r-- | sys/netgraph/ng_source.h | 94 |
4 files changed, 1048 insertions, 0 deletions
diff --git a/share/man/man4/ng_source.4 b/share/man/man4/ng_source.4 new file mode 100644 index 0000000..0af9181 --- /dev/null +++ b/share/man/man4/ng_source.4 @@ -0,0 +1,264 @@ +.\" ng_source.4 +.\" Copyright 2002 Sandvine Inc. +.\" All rights reserved. +.\" +.\" Subject to the following obligations and disclaimer of warranty, use and +.\" redistribution of this software, in source or object code forms, with or +.\" without modifications are expressly permitted by Sandvine Inc.; provided, +.\" however, that: +.\" 1. Any and all reproductions of the source or object code must include the +.\" copyright notice above and the following disclaimer of warranties; and +.\" 2. No rights are granted, in any manner or form, to use Sandvine Inc. +.\" trademarks, including the mark "SANDVINE" on advertising, endorsements, +.\" or otherwise except as such appears in the above copyright notice or in +.\" the software. +.\" +.\" THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM +.\" EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, +.\" EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, +.\" ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +.\" PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR +.\" MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE +.\" USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY +.\" OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES +.\" RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING +.\" WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +.\" PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH +.\" DAMAGE. +.\" +.\" Author: Dave Chapeskie <dchapeskie@sandvine.com> +.\" $FreeBSD$ +.\" +.Dd November 1, 2002 +.Dt NG_SOURCE 4 +.Os +.Sh NAME +.Nm ng_source +.Nd netgraph discard node type +.Sh SYNOPSIS +.In netgraph/ng_source.h +.Sh DESCRIPTION +The +.Nm source +node acts as a source of packets according to the parameters set up +using control messages and input packets. +The 'output' hook must also be connected to a node that responds to the +.Em NGM_ETHER_COOKIE +/ +.Em NGM_ETHER_GET_IFINDEX +message (e.g. an +ng_ether +node). +node type silently discards all data and control messages it receives. +This type is used for testing and debugging. +.Pp +The operation of the node is as follows: +.Pp +.Bl -bullet -compact -offset 2n +.It +Packets received on the 'input' hook are queued internally. +.It +On recpetion of a NGM_SOURCE_START message the node starts sending +the queued packets out the 'output' hook on every clock tick as fast +as the connect interface will take them. +.It +While active, on every clock tick the node checks the available space +in the ifqueue of the interface connected to the output hook and sends +that many packets out it's output hook. +.It +Once the number of packets indicated in the start message have been +sent, or on reception of a stop message, the node stops sending data. +.El +.Sh HOOKS +The +.Nm source +node has two hooks: 'input' and 'output'. The 'output' +hook must remain connected, its disconnection will shutdown the node. +.Sh CONTROL MESSAGES +This node type supports the generic control messages as well as the following, +which must be sent with the +.Em NGM_SOURCE_COOKIE +attached. +.Bl -tag -width NGM_SOURCE_GETCLR_STATS +.It NGM_SOURCE_GET_STATS +"getstats": +Returns a structure containing the following fields: +.\".Bl -bullet -compact -offset 2n +.Bl -tag -width queueFrames: +.It outOctets: +The number of octets/bytes sent out the 'output' hook. +.It outFrames: +The number of frames/packets sent out the 'output' hook. +.It queueOctets: +The number of octets queued from the 'input' hook. +.It queueFrames: +The number of frames queued from the 'input' hook. +.It startTime: +The time the last start message was recieved. +.It endTime: +The time the last end message was recieved or +the output packet count was reached. +.It elapsedTime: +Either endTime-startTime or current time - startTime. +.El +.It NGM_SOURCE_CLR_STATS +"clrstats": +Clears and resets the statistics returned by getstats (except +queueOctects and queueFrames). +.It NGM_SOURCE_GETCLR_STATS +"getclrstats": +As getstats but clears the statistics at the same time. +.It NGM_SOURCE_START +"start": +.Bl -bullet -compact -offset 2n +.It +Takes a single u_int64_t parameter which is the number of packets to +send before stopping. +.It +Starts sending the queued packets out the output hook. +.It +The output hook must be connected or EINVAL is returned. +.It +The node connected to the output hook must respond to +.Em NGM_ETHER_GET_IFINDEX +which is used to get the ifqueue of the attached +interface. +.It +.Em NGM_ETHER_SET_AUTOSRC +is sent to the node connected to the output hook +to turn off automatic ethernet source address overwriting (any errors +from this message are ignored). +.El +.It NGM_SOURCE_STOP +"stop": +Stops the node if it is active. +.It NGM_SOURCE_CLR_DATA +"clrdata": +Clears the packets queued from the 'input' hook. +.El +.Sh SHUTDOWN +This node shuts down upon receipt of a +.Dv NGM_SHUTDOWN +control message, or when the +.Em output +hook has been disconnected. +.Sh EXAMPLE +Build and install the node to /modules (or load it anually). +.Bd -literal -offset 0n +$ make obj +$ make depend +$ make +$ make install +.Ed +.Pp +Attach the node to an ng_ether node for an interface. If ng_ether is +not already loaded you'll need to do so. For example these commands +load the ng_ether module and attach the output hook of a new source node +to orphans hook of the bge0: ng_ether node. +.Bd -literal -offset 0n +$ kldload ng_ether +$ ngctl mkpeer bge0: source orphans output +.Ed +.Pp +At this point the new node can be refered to as "bge0:orphans". The +node can be given it's own name like this: +.Bd -literal -offset 0n +$ ngctl name bge0:orphans src0 +.Ed +.Pp +After which it can be refered to as "src0:". +.Pp +Once created packets need to be sent to the node, the TCL net package +can be used to generate these packets: +.Pp +[Sandvine specific TCL code example omitted] +.Pp +To feed the output of the above TCL script to the ng_source node's input +hook via nghook: +.Bd -literal -offset 0n +$ tcl genPacket | nghook bge0:orphans input +.Ed +.Pp +To check that the node has queued these packets you can get the node +statistics: +.Bd -literal -offset 0n +$ ngctl msg bge0:orphans getstats +Args: { queueOctets=64 queueFrames=1 } +.Ed +.Pp +Send as many packets as required out the output hook: +.Bd -literal -offset 0n +$ ngctl msg bge0:orphans start 16 +.Ed +.Pp +Either wait for them to be sent (periodicly fetching stats if desired) +or send the stop message: +.Bd -literal -offset 0n +$ ngctl msg bge0:orphans stop +.Ed +.Pp +Check the statistics (here we use getclrstats to also clear the +statistics): +.Bd -literal -offset 0n +$ ngctl msg bge0:orphans getclrstats +Args: { outOctets=1024 outFrames=16 queueOctets=64 queueFrames=1 +startTime={ tv_sec=1035305880 tv_usec=758036 } endTime={ tv_sec=1035305880 +tv_usec=759041 } elapsedTime={ tv_usec=1005 } } +.Ed +.Pp +The times are from "struct timeval"s, the tv_sec field is seconds since +the epoch and can be converted into a date string via TCL's [clock +format] or via the UNIX date command: +.Bd -literal -offset 0n +$ date -r 1035305880 +Tue Oct 22 12:58:00 EDT 2002 +.Ed +.Pp +.Sh IMPLEMENTATION NOTES +(FreeBSD 4.4 version) +.Pp +The use of splimp around the NG_SEND_DATA loop is important. Without +it the time taken by a single invocation of ng_source_intr becomes too +large and the packet rate drops. Probably due to the NIC starting to +send the packets right away. +.Pp +Copying all the packets in one loop and sending them in another inside +of ng_source_send is done to limit how long we're at splimp and gave +minor packet rate increases (~5% at 256 byte packets). However note +that if there are errors in the send loop the remaining copied packets +are simply freed and discarded thus we skip those packets and ordering +of the input queue to the output is not maintained. +.Pp +Calling timeout(9) at the end of ng_source_intr instead of near the +begining is done to help avoid CPU starvaion if ng_source_intr takes a +long time to run. +.Pp +The use of splnet may be sub-optimal. It's used for syncronization +within the node (e.g. data recieved on the input hook while +ng_source_send is active) but we don't want to hold it too long and risk +starving the NIC. +.Pp +For clarity and simplicity debugging messages and instrumentation code +has been removed. On i386 one can include machine/cpufunc.h to have +access to the rdtsc() function to read the instruction counter at the +start and end of ng_source_intr. Also useful is the packet count +returned by ng_source_send. Do not try to report such things from +within ng_source_intr, instead include the values in sc->stats. +.Sh SEE ALSO +.Xr netgraph 4 , +.Xr ng_echo 4 , +.Xr ng_hole 4 , +.Xr ng_tee 4 , +.Xr ngctl 8 +.Xr nghook 8 +.Sh HISTORY +The +.Nm +node type was implemented in +.Fx 4.8 . +.Sh AUTHORS +.An Dave Chapeskie Aq dchapeskie@SANDVINE.com diff --git a/sys/modules/netgraph/source/Makefile b/sys/modules/netgraph/source/Makefile new file mode 100644 index 0000000..c70a6bb --- /dev/null +++ b/sys/modules/netgraph/source/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +KMOD= ng_source +SRCS= ng_source.c +# 4.x only +#KMODDEPS= netgraph + +.include <bsd.kmod.mk> diff --git a/sys/netgraph/ng_source.c b/sys/netgraph/ng_source.c new file mode 100644 index 0000000..4dd401b --- /dev/null +++ b/sys/netgraph/ng_source.c @@ -0,0 +1,682 @@ +/* + * ng_source.c + * + * Copyright 2002 Sandvine Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Sandvine Inc.; +provided, + * however, that: + * 1. Any and all reproductions of the source or object code must include +the + * copyright notice above and the following disclaimer of warranties; +and + * 2. No rights are granted, in any manner or form, to use Sandvine Inc. + * trademarks, including the mark "SANDVINE" on advertising, +endorsements, + * or otherwise except as such appears in the above copyright notice or +in + * the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM + * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR +WARRANTIES, + * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT +LIMITATION, + * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR + * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE + * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY + * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Dave Chapeskie <dchapeskie@sandvine.com> + * + * $FreeBSD$ + */ + +/* + * This node is used for high speed packet geneneration. It queues + * all data recieved on it's 'input' hook and when told to start via + * a control message it sends the packets out it's 'output' hook. In + * this way this node can be preloaded with a packet stream which is + * continuously sent. + * + * Currently it just copies the mbufs as required. It could do various + * tricks to try and avoid this. Probably the best performance would + * be achieved by modifying the appropriate drivers to be told to + * self-re-enqueue packets (e.g. the if_bge driver could reuse the same + * transmit descriptors) under control of this node; perhaps via some + * flag in the mbuf or some such. The node would peak at an appropriate + * ifnet flag to see if such support is available for the connected + * interface. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_var.h> +#include <netgraph/ng_message.h> +#include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> +#include <netgraph/ng_ether.h> +#include <netgraph/ng_source.h> + +#define NG_SOURCE_INTR_TICKS 1 +#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) + + +/* Per hook info */ +struct source_hookinfo { + hook_p hook; +}; + +/* Per node info */ +struct privdata { + node_p node; + struct source_hookinfo input; + struct source_hookinfo output; + struct ng_source_stats stats; + struct ifqueue snd_queue; /* packets to send +*/ + struct ifnet *output_ifp; + struct callout_handle intr_ch; + u_int64_t packets; /* packets to send +*/ + u_int32_t queueOctets; +}; +typedef struct privdata *sc_p; + +/* Node flags */ +#define NG_SOURCE_ACTIVE (NGF_TYPE1) + +/* XXX */ +#if 1 +#undef KASSERT +#define KASSERT(expr,msg) do { \ + if (!(expr)) { \ + printf msg ; \ + panic("Assertion"); \ + } \ + } while(0) +#endif + +/* Netgraph methods */ +static ng_constructor_t ng_source_constructor; +static ng_rcvmsg_t ng_source_rcvmsg; +static ng_shutdown_t ng_source_rmnode; +static ng_newhook_t ng_source_newhook; +static ng_rcvdata_t ng_source_rcvdata; +static ng_disconnect_t ng_source_disconnect; + +/* Other functions */ +static timeout_t ng_source_intr; +static int ng_source_get_output_ifp (sc_p); +static void ng_source_clr_data (sc_p); +static void ng_source_start (sc_p); +static void ng_source_stop (sc_p); +static int ng_source_send (sc_p, int, int *); + + +/* Parse type for timeval */ +static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = +{ + { "tv_sec", &ng_parse_int32_type }, + { "tv_usec", &ng_parse_int32_type }, + { NULL } +}; +const struct ng_parse_type ng_source_timeval_type = { + &ng_parse_struct_type, + &ng_source_timeval_type_fields +}; + +/* Parse type for struct ng_source_stats */ +static const struct ng_parse_struct_field ng_source_stats_type_fields[] + = NG_SOURCE_STATS_TYPE_INFO; +static const struct ng_parse_type ng_source_stats_type = { + &ng_parse_struct_type, + &ng_source_stats_type_fields +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_source_cmds[] = { + { + NGM_SOURCE_COOKIE, + NGM_SOURCE_GET_STATS, + "getstats", + NULL, + &ng_source_stats_type + }, + { + NGM_SOURCE_COOKIE, + NGM_SOURCE_CLR_STATS, + "clrstats", + NULL, + NULL + }, + { + NGM_SOURCE_COOKIE, + NGM_SOURCE_GETCLR_STATS, + "getclrstats", + NULL, + &ng_source_stats_type + }, + { + NGM_SOURCE_COOKIE, + NGM_SOURCE_START, + "start", + &ng_parse_uint64_type, + NULL + }, + { + NGM_SOURCE_COOKIE, + NGM_SOURCE_STOP, + "stop", + NULL, + NULL + }, + { + NGM_SOURCE_COOKIE, + NGM_SOURCE_CLR_DATA, + "clrdata", + NULL, + NULL + }, + { 0 } +}; + +/* Netgraph type descriptor */ +static struct ng_type ng_source_typestruct = { + NG_VERSION, + NG_SOURCE_NODE_TYPE, + NULL, /* module event handler */ + ng_source_constructor, + ng_source_rcvmsg, + ng_source_rmnode, + ng_source_newhook, + NULL, /* findhook */ + NULL, + ng_source_rcvdata, /* rcvdata */ + ng_source_rcvdata, /* rcvdataq */ + ng_source_disconnect, + ng_source_cmds +}; +NETGRAPH_INIT(source, &ng_source_typestruct); + +/* + * Node constructor + */ +static int +ng_source_constructor(node_p *nodep) +{ + sc_p sc; + int error = 0; + + MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT); + if (sc == NULL) + return (ENOMEM); + bzero(sc, sizeof(*sc)); + + if ((error = ng_make_node_common(&ng_source_typestruct, nodep))) { + FREE(sc, M_NETGRAPH); + return (error); + } + (*nodep)->private = sc; + sc->node = *nodep; + sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ + callout_handle_init(&sc->intr_ch); + return (0); +} + +/* + * Add a hook + */ +static int +ng_source_newhook(node_p node, hook_p hook, const char *name) +{ + const sc_p sc = node->private; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { + sc->input.hook = hook; + hook->private = &sc->input; + } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { + sc->output.hook = hook; + hook->private = &sc->output; + sc->output_ifp = 0; + bzero(&sc->stats, sizeof(sc->stats)); + } else + return (EINVAL); + return (0); +} + +/* + * Receive a control message + */ +static int +ng_source_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, + struct ng_mesg **rptr) +{ + const sc_p sc = node->private; + struct ng_mesg *resp = NULL; + int error = 0; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + switch (msg->header.typecookie) { + case NGM_SOURCE_COOKIE: + switch (msg->header.cmd) { + case NGM_SOURCE_GET_STATS: + case NGM_SOURCE_CLR_STATS: + case NGM_SOURCE_GETCLR_STATS: + { + struct ng_source_stats *stats; + + if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { + NG_MKRESPONSE(resp, msg, + sizeof(*stats), M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + goto done; + } + sc->stats.queueOctets = sc->queueOctets; + sc->stats.queueFrames = +sc->snd_queue.ifq_len; + if ((sc->node->flags & NG_SOURCE_ACTIVE) + && !timevalisset(&sc->stats.endTime)) { + +getmicrotime(&sc->stats.elapsedTime); + timevalsub(&sc->stats.elapsedTime, + +&sc->stats.startTime); + } + stats = (struct ng_source_stats +*)resp->data; + bcopy(&sc->stats, stats, sizeof(* stats)); + } + if (msg->header.cmd != NGM_SOURCE_GET_STATS) + bzero(&sc->stats, sizeof(sc->stats)); + } + break; + case NGM_SOURCE_START: + { + u_int64_t packets = *(u_int64_t *)msg->data; + if (sc->output.hook == NULL) { + printf("%s: start on node with no output +hook\n", __FUNCTION__); + error = EINVAL; + break; + } + /* TODO validation of packets */ + sc->packets = packets; + ng_source_start(sc); + } + break; + case NGM_SOURCE_STOP: + ng_source_stop(sc); + break; + case NGM_SOURCE_CLR_DATA: + ng_source_clr_data(sc); + break; + default: + error = EINVAL; + break; + } + break; + default: + error = EINVAL; + break; + } + if (rptr) + *rptr = resp; + else if (resp) + FREE(resp, M_NETGRAPH); + +done: + FREE(msg, M_NETGRAPH); + return (error); +} + +/* + * Receive data on a hook + * + * If data comes in the input hook, enqueue it on the send queue. + * If data comes in the output hook, discard it. + */ +static int +ng_source_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) +{ + const sc_p sc = hook->node->private; + struct source_hookinfo *const hinfo = (struct source_hookinfo *) +hook->private; + int error = 0; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__)); + + /* Which hook? */ + if (hinfo == &sc->output) { + /* discard */ + NG_FREE_DATA(m, meta); + return (error); + } + KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__)); + + if ((m->m_flags & M_PKTHDR) == 0) { + printf("%s: mbuf without PKTHDR\n", __FUNCTION__); + NG_FREE_DATA(m, meta); + return (EINVAL); + } + + /* XXX we discard the meta data for now */ + NG_FREE_META(meta); + + /* enque packet */ + /* XXX should we check IF_QFULL() ? */ + IF_ENQUEUE(&sc->snd_queue, m); + sc->queueOctets += m->m_pkthdr.len; + + return (0); +} + +/* + * Shutdown processing + */ +static int +ng_source_rmnode(node_p node) +{ + const sc_p sc = node->private; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + node->flags |= NG_INVALID; + ng_source_stop(sc); + ng_cutlinks(node); + ng_source_clr_data(sc); + ng_unname(node); + node->private = NULL; + ng_unref(sc->node); + FREE(sc, M_NETGRAPH); + return (0); +} + +/* + * Hook disconnection + */ +static int +ng_source_disconnect(hook_p hook) +{ + struct source_hookinfo *const hinfo = (struct source_hookinfo *) +hook->private; + sc_p sc = (sc_p) hinfo->hook->node->private; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + hinfo->hook = NULL; + if (hook->node->numhooks == 0 || hinfo == &sc->output) + ng_rmnode(hook->node); + return (0); +} + +/* + * Set sc->output_ifp to point to the the struct ifnet of the interface + * reached via our output hook. + */ +static int +ng_source_get_output_ifp(sc_p sc) +{ + struct ng_mesg *msg, *rsp; + struct ifnet *ifp; + u_int32_t if_index; + int error = 0; + int s; + + sc->output_ifp = NULL; + + /* Ask the attached node for the connected interface's index */ + NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, +M_NOWAIT); + if (msg == NULL) + return (ENOBUFS); + + error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, &rsp); + if (error != 0) + return (error); + + if (rsp == NULL) + return (EINVAL); + + if (rsp->header.arglen < sizeof(u_int32_t)) + return (EINVAL); + + if_index = *(u_int32_t *)rsp->data; + /* Could use ifindex2ifnet[if_index] except that we have no + * way of verifying if_index is valid since if_indexlim is + * local to if_attach() + */ + TAILQ_FOREACH(ifp, &ifnet, if_link) { + if (ifp->if_index == if_index) + break; + } + + if (ifp == NULL) { + printf("%s: can't find interface %d\n", __FUNCTION__, +if_index); + return (EINVAL); + } + sc->output_ifp = ifp; + +#if 1 + /* XXX mucking with a drivers ifqueue size is ugly but we need it + * to queue a lot of packets to get close to line rate on a gigabit + * interface with small packets. + * XXX we should restore the original value at stop or disconnect + */ + s = splimp(); /* XXX is this required? */ + if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) + { + printf("ng_source: changing ifq_maxlen from %d to %d\n", + ifp->if_snd.ifq_maxlen, +NG_SOURCE_DRIVER_IFQ_MAXLEN); + ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; + } + splx(s); +#endif + return (0); +} + +/* + * Set the attached ethernet node's ethernet source address override flag. + */ +static int +ng_source_set_autosrc(sc_p sc, u_int32_t flag) +{ + struct ng_mesg *msg; + int error = 0; + + NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, + sizeof (u_int32_t), M_NOWAIT); + if (msg == NULL) + return(ENOBUFS); + + *(u_int32_t *)msg->data = flag; + error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, NULL); + return (error); +} + +/* + * Clear out the data we've queued + */ +static void +ng_source_clr_data (sc_p sc) +{ + struct mbuf *m; + + SPLASSERT(net, __FUNCTION__); + for (;;) { + IF_DEQUEUE(&sc->snd_queue, m); + if (m == NULL) + break; + NG_FREE_M(m); + } + sc->queueOctets = 0; +} + +/* + * Start sending queued data out the output hook + */ +static void +ng_source_start (sc_p sc) +{ + SPLASSERT(net, __FUNCTION__); + KASSERT(sc->output.hook != NULL, + ("%s: output hook unconnected", __FUNCTION__)); + if ((sc->node->flags & NG_SOURCE_ACTIVE) == 0) { + if (sc->output_ifp == NULL && ng_source_get_output_ifp(sc) +!= 0) + return; + ng_source_set_autosrc(sc, 0); + sc->node->flags |= NG_SOURCE_ACTIVE; + timevalclear(&sc->stats.elapsedTime); + timevalclear(&sc->stats.endTime); + getmicrotime(&sc->stats.startTime); + sc->intr_ch = timeout(ng_source_intr, sc, 0); + } +} + +/* + * Stop sending queued data out the output hook + */ +static void +ng_source_stop (sc_p sc) +{ + SPLASSERT(net, __FUNCTION__); + if (sc->node->flags & NG_SOURCE_ACTIVE) { + untimeout(ng_source_intr, sc, sc->intr_ch); + sc->node->flags &= ~NG_SOURCE_ACTIVE; + getmicrotime(&sc->stats.endTime); + sc->stats.elapsedTime = sc->stats.endTime; + timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); + /* XXX should set this to the initial value instead */ + ng_source_set_autosrc(sc, 1); + } +} + +/* + * While active called every NG_SOURCE_INTR_TICKS ticks. + * Sends as many packets as the interface connected to our + * output hook is able to enqueue. + */ +static void +ng_source_intr (void *arg) +{ + const sc_p sc = (sc_p) arg; + struct ifqueue *ifq; + int packets; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + + callout_handle_init(&sc->intr_ch); + if (sc->packets == 0 || sc->output.hook == NULL + || (sc->node->flags & NG_SOURCE_ACTIVE) == 0) { + ng_source_stop(sc); + return; + } + + ifq = &sc->output_ifp->if_snd; + packets = ifq->ifq_maxlen - ifq->ifq_len; + ng_source_send(sc, packets, NULL); + if (sc->packets == 0) { + int s = splnet(); + ng_source_stop(sc); + splx(s); + } else + sc->intr_ch = timeout(ng_source_intr, sc, +NG_SOURCE_INTR_TICKS); +} + +/* + * Send packets out our output hook + */ +static int +ng_source_send (sc_p sc, int tosend, int *sent_p) +{ + struct ifqueue tmp_queue; + struct mbuf *m, *m2; + int sent = 0; + int error = 0; + int s, s2; + + KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); + KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__)); + KASSERT(sc->node->flags & NG_SOURCE_ACTIVE, + ("%s: inactive node", __FUNCTION__)); + + if ((u_int64_t)tosend > sc->packets) + tosend = sc->packets; + + /* Copy the required number of packets to a temporary queue */ + bzero (&tmp_queue, sizeof (tmp_queue)); + for (sent = 0; error == 0 && sent < tosend; ++sent) { + s = splnet(); + IF_DEQUEUE(&sc->snd_queue, m); + splx(s); + if (m == NULL) + break; + + /* duplicate the packet */ + m2 = m_copypacket(m, M_NOWAIT); + if (m2 == NULL) { + s = splnet(); + IF_PREPEND(&sc->snd_queue, m); + splx(s); + error = ENOBUFS; + break; + } + + /* re-enqueue the original packet for us */ + s = splnet(); + IF_ENQUEUE(&sc->snd_queue, m); + splx(s); + + /* queue the copy for sending at smplimp */ + IF_ENQUEUE(&tmp_queue, m2); + } + + sent = 0; + s = splimp(); + for (;;) { + IF_DEQUEUE(&tmp_queue, m2); + if (m2 == NULL) + break; + if (error == 0) { + ++sent; + sc->stats.outFrames++; + sc->stats.outOctets += m2->m_pkthdr.len; + s2 = splnet(); + NG_SEND_DATA_ONLY(error, sc->output.hook, m2); + splx(s2); + } else { + NG_FREE_M(m2); + } + } + splx(s); + + sc->packets -= sent; + if (sent_p != NULL) + *sent_p = sent; + return (error); +} diff --git a/sys/netgraph/ng_source.h b/sys/netgraph/ng_source.h new file mode 100644 index 0000000..271c15c --- /dev/null +++ b/sys/netgraph/ng_source.h @@ -0,0 +1,94 @@ +/* + * ng_source.h + * + * Copyright 2002 Sandvine Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Sandvine Inc.; +provided, + * however, that: + * 1. Any and all reproductions of the source or object code must include +the + * copyright notice above and the following disclaimer of warranties; +and + * 2. No rights are granted, in any manner or form, to use Sandvine Inc. + * trademarks, including the mark "SANDVINE" on advertising, +endorsements, + * or otherwise except as such appears in the above copyright notice or +in + * the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM + * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR +WARRANTIES, + * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT +LIMITATION, + * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR + * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE + * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY + * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Dave Chapeskie <dchapeskie@sandvine.com> + * + * $FreeBSD$ + */ + +#ifndef _NETGRAPH_SOURCE_H_ +#define _NETGRAPH_SOURCE_H_ + +/* Node type name and magic cookie */ +#define NG_SOURCE_NODE_TYPE "source" +#define NGM_SOURCE_COOKIE 1034346805 + +/* Hook names */ +#define NG_SOURCE_HOOK_INPUT "input" +#define NG_SOURCE_HOOK_OUTPUT "output" + +/* Statistics structure returned by NGM_SOURCE_GET_STATS */ +struct ng_source_stats { + u_int64_t outOctets; + u_int64_t outFrames; + u_int32_t queueOctets; + u_int32_t queueFrames; + struct timeval startTime; + struct timeval endTime; + struct timeval elapsedTime; +}; + +extern const struct ng_parse_type ng_source_timeval_type; +/* Keep this in sync with the above structure definition */ +#define NG_SOURCE_STATS_TYPE_INFO { \ + { "outOctets", &ng_parse_uint64_type }, \ + { "outFrames", &ng_parse_uint64_type }, \ + { "queueOctets", &ng_parse_uint32_type }, \ + { "queueFrames", &ng_parse_uint32_type }, \ + { "startTime", &ng_source_timeval_type }, \ + { "endTime", &ng_source_timeval_type }, \ + { "elapsedTime", &ng_source_timeval_type }, \ + { NULL } \ +} + +/* Netgraph commands */ +enum { + NGM_SOURCE_GET_STATS = 1, /* get stats */ + NGM_SOURCE_CLR_STATS, /* clear stats */ + NGM_SOURCE_GETCLR_STATS, /* atomically get and clear stats */ + NGM_SOURCE_START, /* start sending queued data */ + NGM_SOURCE_STOP, /* stop sending queued data */ + NGM_SOURCE_CLR_DATA, /* clear the queued data */ +}; + +#endif /* _NETGRAPH_SOURCE_H_ */ |