summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/ng_source.4264
-rw-r--r--sys/modules/netgraph/source/Makefile8
-rw-r--r--sys/netgraph/ng_source.c682
-rw-r--r--sys/netgraph/ng_source.h94
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_ */
OpenPOWER on IntegriCloud