From 2932d3587e1bb2c35e5123f5dbd2e5842a68ba95 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 31 Oct 2002 23:03:09 +0000 Subject: 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 Obtained from: Sandvine inc. MFC after: 1 week --- sys/netgraph/ng_source.c | 682 +++++++++++++++++++++++++++++++++++++++++++++++ sys/netgraph/ng_source.h | 94 +++++++ 2 files changed, 776 insertions(+) create mode 100644 sys/netgraph/ng_source.c create mode 100644 sys/netgraph/ng_source.h (limited to 'sys/netgraph') 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 + * + * $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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * $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_ */ -- cgit v1.1