ng_source.c revision 138268
191041Sobrien/*
291041Sobrien * ng_source.c
3130561Sobrien *
491041Sobrien * Copyright 2002 Sandvine Inc.
591041Sobrien * All rights reserved.
691041Sobrien *
791041Sobrien * Subject to the following obligations and disclaimer of warranty, use and
891041Sobrien * redistribution of this software, in source or object code forms, with or
991041Sobrien * without modifications are expressly permitted by Sandvine Inc.; provided,
1091041Sobrien * however, that:
1191041Sobrien * 1. Any and all reproductions of the source or object code must include the
12130561Sobrien *    copyright notice above and the following disclaimer of warranties; and
13130561Sobrien * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
1491041Sobrien *    trademarks, including the mark "SANDVINE" on advertising, endorsements,
1591041Sobrien *    or otherwise except as such appears in the above copyright notice or in
1691041Sobrien *    the software.
1791041Sobrien *
1891041Sobrien * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
1991041Sobrien * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES,
2091041Sobrien * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION,
2191041Sobrien * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
2291041Sobrien * PURPOSE, OR NON-INFRINGEMENT.  SANDVINE DOES NOT WARRANT, GUARANTEE, OR
2391041Sobrien * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
2491041Sobrien * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
2591041Sobrien * OR OTHERWISE.  IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
2691041Sobrien * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2791041Sobrien * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2891041Sobrien * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
2991041Sobrien * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3091041Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3191041Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3291041Sobrien * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
3391041Sobrien * DAMAGE.
3491041Sobrien *
3591041Sobrien * Author: Dave Chapeskie <dchapeskie@sandvine.com>
3691041Sobrien */
3791041Sobrien
3891041Sobrien#include <sys/cdefs.h>
3991041Sobrien__FBSDID("$FreeBSD: head/sys/netgraph/ng_source.c 138268 2004-12-01 11:56:32Z glebius $");
4091041Sobrien
4191041Sobrien/*
4291041Sobrien * This node is used for high speed packet geneneration.  It queues
4391041Sobrien * all data recieved on it's 'input' hook and when told to start via
4491041Sobrien * a control message it sends the packets out it's 'output' hook.  In
4591041Sobrien * this way this node can be preloaded with a packet stream which is
4691041Sobrien * continuously sent.
4791041Sobrien *
4891041Sobrien * Currently it just copies the mbufs as required.  It could do various
4991041Sobrien * tricks to try and avoid this.  Probably the best performance would
5091041Sobrien * be achieved by modifying the appropriate drivers to be told to
5191041Sobrien * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
5291041Sobrien * transmit descriptors) under control of this node; perhaps via some
5391041Sobrien * flag in the mbuf or some such.  The node would peak at an appropriate
5491041Sobrien * ifnet flag to see if such support is available for the connected
5591041Sobrien * interface.
5691041Sobrien */
5791041Sobrien
5891041Sobrien#include <sys/param.h>
5991041Sobrien#include <sys/systm.h>
6091041Sobrien#include <sys/errno.h>
6191041Sobrien#include <sys/kernel.h>
6291041Sobrien#include <sys/malloc.h>
6391041Sobrien#include <sys/mbuf.h>
6491041Sobrien#include <sys/socket.h>
65130561Sobrien#include <net/if.h>
6691041Sobrien#include <net/if_var.h>
67130561Sobrien#include <netgraph/ng_message.h>
6891041Sobrien#include <netgraph/netgraph.h>
69130561Sobrien#include <netgraph/ng_parse.h>
70130561Sobrien#include <netgraph/ng_ether.h>
71130561Sobrien#include <netgraph/ng_source.h>
72130561Sobrien
73130561Sobrien#define NG_SOURCE_INTR_TICKS		1
7491041Sobrien#define NG_SOURCE_DRIVER_IFQ_MAXLEN	(4*1024)
75130561Sobrien
76130561Sobrien
77130561Sobrien/* Per hook info */
7891041Sobrienstruct source_hookinfo {
79130561Sobrien	hook_p				hook;
8091041Sobrien};
81130561Sobrien
8291041Sobrien/* Per node info */
8391041Sobrienstruct privdata {
84130561Sobrien	node_p				node;
8591041Sobrien	struct source_hookinfo		input;
86130561Sobrien	struct source_hookinfo		output;
87130561Sobrien	struct ng_source_stats		stats;
88130561Sobrien	struct ifqueue			snd_queue;	/* packets to send */
8991041Sobrien	struct ifnet			*output_ifp;
9091041Sobrien	struct callout			intr_ch;
9191041Sobrien	u_int64_t			packets;	/* packets to send */
9291041Sobrien	u_int32_t			queueOctets;
9391041Sobrien};
9491041Sobrientypedef struct privdata *sc_p;
9591041Sobrien
9691041Sobrien/* Node flags */
97130561Sobrien#define NG_SOURCE_ACTIVE	(NGF_TYPE1)
9891041Sobrien
9991041Sobrien/* Netgraph methods */
10091041Sobrienstatic ng_constructor_t	ng_source_constructor;
10191041Sobrienstatic ng_rcvmsg_t	ng_source_rcvmsg;
10291041Sobrienstatic ng_shutdown_t	ng_source_rmnode;
10391041Sobrienstatic ng_newhook_t	ng_source_newhook;
10491041Sobrienstatic ng_rcvdata_t	ng_source_rcvdata;
10591041Sobrienstatic ng_disconnect_t	ng_source_disconnect;
10691041Sobrien
10791041Sobrien/* Other functions */
10891041Sobrienstatic void		ng_source_intr(node_p, hook_p, void *, int);
10991041Sobrienstatic int		ng_source_request_output_ifp (sc_p);
11091041Sobrienstatic void		ng_source_clr_data (sc_p);
11191041Sobrienstatic void		ng_source_start (sc_p);
11291041Sobrienstatic void		ng_source_stop (sc_p);
11391041Sobrienstatic int		ng_source_send (sc_p, int, int *);
11491041Sobrienstatic int		ng_source_store_output_ifp(sc_p sc,
11591041Sobrien			    struct ng_mesg *msg);
11691041Sobrien
11791041Sobrien
11891041Sobrien/* Parse type for timeval */
11991041Sobrienstatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = {
120130561Sobrien	{ "tv_sec",		&ng_parse_int32_type	},
12191041Sobrien	{ "tv_usec",		&ng_parse_int32_type	},
12291041Sobrien	{ NULL }
12391041Sobrien};
12491041Sobrienconst struct ng_parse_type ng_source_timeval_type = {
12591041Sobrien	&ng_parse_struct_type,
12691041Sobrien	&ng_source_timeval_type_fields
12791041Sobrien};
12891041Sobrien
12991041Sobrien/* Parse type for struct ng_source_stats */
13091041Sobrienstatic const struct ng_parse_struct_field ng_source_stats_type_fields[]
13191041Sobrien	= NG_SOURCE_STATS_TYPE_INFO;
13291041Sobrienstatic const struct ng_parse_type ng_source_stats_type = {
13391041Sobrien	&ng_parse_struct_type,
13491041Sobrien	&ng_source_stats_type_fields
13591041Sobrien};
13691041Sobrien
13791041Sobrien/* List of commands and how to convert arguments to/from ASCII */
13891041Sobrienstatic const struct ng_cmdlist ng_source_cmds[] = {
13991041Sobrien	{
14091041Sobrien	  NGM_SOURCE_COOKIE,
14191041Sobrien	  NGM_SOURCE_GET_STATS,
14291041Sobrien	  "getstats",
143130561Sobrien	  NULL,
14491041Sobrien	  &ng_source_stats_type
14591041Sobrien	},
14691041Sobrien	{
14791041Sobrien	  NGM_SOURCE_COOKIE,
14891041Sobrien	  NGM_SOURCE_CLR_STATS,
14991041Sobrien	  "clrstats",
15091041Sobrien	  NULL,
151130561Sobrien	  NULL
15291041Sobrien	},
15391041Sobrien	{
15491041Sobrien	  NGM_SOURCE_COOKIE,
15591041Sobrien	  NGM_SOURCE_GETCLR_STATS,
15691041Sobrien	  "getclrstats",
15791041Sobrien	  NULL,
15891041Sobrien	  &ng_source_stats_type
15991041Sobrien	},
16091041Sobrien	{
16191041Sobrien	  NGM_SOURCE_COOKIE,
16291041Sobrien	  NGM_SOURCE_START,
16391041Sobrien	  "start",
16491041Sobrien	  &ng_parse_uint64_type,
16591041Sobrien	  NULL
16691041Sobrien	},
167130561Sobrien	{
16891041Sobrien	  NGM_SOURCE_COOKIE,
16991041Sobrien	  NGM_SOURCE_STOP,
17091041Sobrien	  "stop",
17191041Sobrien	  NULL,
17291041Sobrien	  NULL
17391041Sobrien	},
17491041Sobrien	{
17591041Sobrien	  NGM_SOURCE_COOKIE,
17691041Sobrien	  NGM_SOURCE_CLR_DATA,
17791041Sobrien	  "clrdata",
17891041Sobrien	  NULL,
17991041Sobrien	  NULL
18091041Sobrien	},
18191041Sobrien	{
18291041Sobrien	  NGM_SOURCE_COOKIE,
18391041Sobrien	  NGM_SOURCE_START_NOW,
18491041Sobrien	  "start_now",
18591041Sobrien	  &ng_parse_uint64_type,
18691041Sobrien	  NULL
18791041Sobrien	},
18891041Sobrien	{ 0 }
18991041Sobrien};
190130561Sobrien
19191041Sobrien/* Netgraph type descriptor */
19291041Sobrienstatic struct ng_type ng_source_typestruct = {
19391041Sobrien	.version =	NG_ABI_VERSION,
19491041Sobrien	.name =		NG_SOURCE_NODE_TYPE,
19591041Sobrien	.constructor =	ng_source_constructor,
19691041Sobrien	.rcvmsg =	ng_source_rcvmsg,
19791041Sobrien	.shutdown =	ng_source_rmnode,
19891041Sobrien	.newhook =	ng_source_newhook,
19991041Sobrien	.rcvdata =	ng_source_rcvdata,
20091041Sobrien	.disconnect =	ng_source_disconnect,
20191041Sobrien	.cmdlist =	ng_source_cmds,
20291041Sobrien};
20391041SobrienNETGRAPH_INIT(source, &ng_source_typestruct);
20491041Sobrien
20591041Sobrienstatic int ng_source_set_autosrc(sc_p, u_int32_t);
20691041Sobrien
20791041Sobrien/*
20891041Sobrien * Node constructor
20991041Sobrien */
21091041Sobrienstatic int
21191041Sobrienng_source_constructor(node_p node)
21291041Sobrien{
21391041Sobrien	sc_p sc;
21491041Sobrien
21591041Sobrien	sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
21691041Sobrien	if (sc == NULL)
21791041Sobrien		return (ENOMEM);
21891041Sobrien
21991041Sobrien	NG_NODE_SET_PRIVATE(node, sc);
22091041Sobrien	sc->node = node;
22191041Sobrien	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
222130561Sobrien	ng_callout_init(&sc->intr_ch);
223130561Sobrien
22491041Sobrien	return (0);
22591041Sobrien}
22691041Sobrien
22791041Sobrien/*
22891041Sobrien * Add a hook
22991041Sobrien */
23091041Sobrienstatic int
23191041Sobrienng_source_newhook(node_p node, hook_p hook, const char *name)
23291041Sobrien{
23391041Sobrien	sc_p sc;
23491041Sobrien
23591041Sobrien	sc = NG_NODE_PRIVATE(node);
23691041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
23791041Sobrien	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
238130561Sobrien		sc->input.hook = hook;
23991041Sobrien		NG_HOOK_SET_PRIVATE(hook, &sc->input);
24091041Sobrien	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
24191041Sobrien		sc->output.hook = hook;
24291041Sobrien		NG_HOOK_SET_PRIVATE(hook, &sc->output);
24391041Sobrien		sc->output_ifp = 0;
24491041Sobrien		bzero(&sc->stats, sizeof(sc->stats));
24591041Sobrien	} else
24691041Sobrien		return (EINVAL);
24791041Sobrien	return (0);
24891041Sobrien}
24991041Sobrien
25091041Sobrien/*
25191041Sobrien * Receive a control message
25291041Sobrien */
25391041Sobrienstatic int
25491041Sobrienng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
25591041Sobrien{
25691041Sobrien	sc_p sc;
25791041Sobrien	struct ng_mesg *resp = NULL;
25891041Sobrien	int error = 0;
25991041Sobrien	struct ng_mesg *msg;
26091041Sobrien
26191041Sobrien	sc = NG_NODE_PRIVATE(node);
26291041Sobrien	NGI_GET_MSG(item, msg);
26391041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
26491041Sobrien	switch (msg->header.typecookie) {
26591041Sobrien	case NGM_SOURCE_COOKIE:
26691041Sobrien		if (msg->header.flags & NGF_RESP) {
26791041Sobrien			error = EINVAL;
26891041Sobrien			break;
26991041Sobrien		}
270130561Sobrien		switch (msg->header.cmd) {
27191041Sobrien		case NGM_SOURCE_GET_STATS:
27291041Sobrien		case NGM_SOURCE_CLR_STATS:
27391041Sobrien		case NGM_SOURCE_GETCLR_STATS:
27491041Sobrien                    {
27591041Sobrien			struct ng_source_stats *stats;
27691041Sobrien
27791041Sobrien                        if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
27891041Sobrien                                NG_MKRESPONSE(resp, msg,
27991041Sobrien                                    sizeof(*stats), M_NOWAIT);
28091041Sobrien				if (resp == NULL) {
28191041Sobrien					error = ENOMEM;
28291041Sobrien					goto done;
283130561Sobrien				}
28491041Sobrien				sc->stats.queueOctets = sc->queueOctets;
28591041Sobrien				sc->stats.queueFrames = sc->snd_queue.ifq_len;
28691041Sobrien				if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
28791041Sobrien				    && !timevalisset(&sc->stats.endTime)) {
288130561Sobrien					getmicrotime(&sc->stats.elapsedTime);
289130561Sobrien					timevalsub(&sc->stats.elapsedTime,
290130561Sobrien					    &sc->stats.startTime);
29191041Sobrien				}
29291041Sobrien				stats = (struct ng_source_stats *)resp->data;
29391041Sobrien				bcopy(&sc->stats, stats, sizeof(* stats));
29491041Sobrien                        }
29591041Sobrien                        if (msg->header.cmd != NGM_SOURCE_GET_STATS)
29691041Sobrien				bzero(&sc->stats, sizeof(sc->stats));
29791041Sobrien		    }
29891041Sobrien		    break;
29991041Sobrien		case NGM_SOURCE_START:
30091041Sobrien		    {
30191041Sobrien			u_int64_t packets = *(u_int64_t *)msg->data;
30291041Sobrien			if (sc->output.hook == NULL) {
30391041Sobrien				printf("%s: start on node with no output hook\n"
30491041Sobrien				    , __func__);
30591041Sobrien				error = EINVAL;
30691041Sobrien				break;
30791041Sobrien			}
30891041Sobrien			/* TODO validation of packets */
30991041Sobrien			sc->packets = packets;
31091041Sobrien			ng_source_start(sc);
31191041Sobrien		    }
31291041Sobrien		    break;
31391041Sobrien		case NGM_SOURCE_START_NOW:
31491041Sobrien		    {
31591041Sobrien			u_int64_t packets = *(u_int64_t *)msg->data;
31691041Sobrien			if (sc->output.hook == NULL) {
31791041Sobrien				printf("%s: start on node with no output hook\n"
31891041Sobrien				    , __func__);
31991041Sobrien				error = EINVAL;
32091041Sobrien				break;
32191041Sobrien			}
32291041Sobrien			if (sc->node->nd_flags & NG_SOURCE_ACTIVE) {
32391041Sobrien				error = EBUSY;
32491041Sobrien				break;
32591041Sobrien			}
32691041Sobrien			/* TODO validation of packets */
32791041Sobrien			sc->packets = packets;
32891041Sobrien		        sc->output_ifp = NULL;
32991041Sobrien
33091041Sobrien			sc->node->nd_flags |= NG_SOURCE_ACTIVE;
33191041Sobrien			timevalclear(&sc->stats.elapsedTime);
33291041Sobrien			timevalclear(&sc->stats.endTime);
33391041Sobrien			getmicrotime(&sc->stats.startTime);
33491041Sobrien			ng_callout(&sc->intr_ch, node, NULL, 0,
33591041Sobrien			    ng_source_intr, sc, 0);
33691041Sobrien		    }
33791041Sobrien		    break;
33891041Sobrien		case NGM_SOURCE_STOP:
33991041Sobrien			ng_source_stop(sc);
34091041Sobrien			break;
34191041Sobrien		case NGM_SOURCE_CLR_DATA:
34291041Sobrien			ng_source_clr_data(sc);
34391041Sobrien			break;
34491041Sobrien		default:
34591041Sobrien			error = EINVAL;
34691041Sobrien			break;
34791041Sobrien		}
34891041Sobrien		break;
34991041Sobrien	case NGM_ETHER_COOKIE:
35091041Sobrien		if (!(msg->header.flags & NGF_RESP)) {
35191041Sobrien			error = EINVAL;
35291041Sobrien			break;
35391041Sobrien		}
35491041Sobrien		switch (msg->header.cmd) {
35591041Sobrien		case NGM_ETHER_GET_IFINDEX:
35691041Sobrien			if (ng_source_store_output_ifp(sc, msg) == 0) {
35791041Sobrien				ng_source_set_autosrc(sc, 0);
35891041Sobrien				sc->node->nd_flags |= NG_SOURCE_ACTIVE;
359130561Sobrien				timevalclear(&sc->stats.elapsedTime);
36091041Sobrien				timevalclear(&sc->stats.endTime);
36191041Sobrien				getmicrotime(&sc->stats.startTime);
36291041Sobrien				ng_callout(&sc->intr_ch, node, NULL, 0,
36391041Sobrien				    ng_source_intr, sc, 0);
364130561Sobrien			}
36591041Sobrien			break;
36691041Sobrien		default:
36791041Sobrien			error = EINVAL;
36891041Sobrien		}
369130561Sobrien		break;
37091041Sobrien	default:
37191041Sobrien		error = EINVAL;
37291041Sobrien		break;
37391041Sobrien	}
37491041Sobrien
37591041Sobriendone:
37691041Sobrien	/* Take care of synchronous response, if any */
37791041Sobrien	NG_RESPOND_MSG(error, node, item, resp);
37891041Sobrien	/* Free the message and return */
37991041Sobrien	NG_FREE_MSG(msg);
38091041Sobrien	return (error);
38191041Sobrien}
38291041Sobrien
38391041Sobrien/*
38491041Sobrien * Receive data on a hook
38591041Sobrien *
38691041Sobrien * If data comes in the input hook, enqueue it on the send queue.
38791041Sobrien * If data comes in the output hook, discard it.
38891041Sobrien */
38991041Sobrienstatic int
39091041Sobrienng_source_rcvdata(hook_p hook, item_p item)
39191041Sobrien{
39291041Sobrien	sc_p sc;
39391041Sobrien	struct source_hookinfo *hinfo;
39491041Sobrien	int error = 0;
39591041Sobrien	struct mbuf *m;
39691041Sobrien
39791041Sobrien	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
398130561Sobrien	NGI_GET_M(item, m);
39991041Sobrien	NG_FREE_ITEM(item);
40091041Sobrien	hinfo = NG_HOOK_PRIVATE(hook);
40191041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
40291041Sobrien	KASSERT(hinfo != NULL, ("%s: null hook info", __func__));
40391041Sobrien
40491041Sobrien	/* Which hook? */
40591041Sobrien	if (hinfo == &sc->output) {
40691041Sobrien		/* discard */
40791041Sobrien		NG_FREE_M(m);
40891041Sobrien		return (error);
40991041Sobrien	}
41091041Sobrien	KASSERT(hinfo == &sc->input, ("%s: no hook!", __func__));
41191041Sobrien
41291041Sobrien	if ((m->m_flags & M_PKTHDR) == 0) {
41391041Sobrien		printf("%s: mbuf without PKTHDR\n", __func__);
41491041Sobrien		NG_FREE_M(m);
41591041Sobrien		return (EINVAL);
41691041Sobrien	}
41791041Sobrien
41891041Sobrien	/* enque packet */
41991041Sobrien	/* XXX should we check IF_QFULL() ? */
42091041Sobrien	_IF_ENQUEUE(&sc->snd_queue, m);
42191041Sobrien	sc->queueOctets += m->m_pkthdr.len;
42291041Sobrien
42391041Sobrien	return (0);
42491041Sobrien}
42591041Sobrien
42691041Sobrien/*
42791041Sobrien * Shutdown processing
42891041Sobrien */
42991041Sobrienstatic int
43091041Sobrienng_source_rmnode(node_p node)
43191041Sobrien{
43291041Sobrien	sc_p sc;
43391041Sobrien
43491041Sobrien	sc = NG_NODE_PRIVATE(node);
43591041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
43691041Sobrien	ng_source_stop(sc);
43791041Sobrien	ng_source_clr_data(sc);
43891041Sobrien	NG_NODE_SET_PRIVATE(node, NULL);
43991041Sobrien	NG_NODE_UNREF(node);
44091041Sobrien	free(sc, M_NETGRAPH);
44191041Sobrien	return (0);
44291041Sobrien}
44391041Sobrien
44491041Sobrien/*
44591041Sobrien * Hook disconnection
44691041Sobrien */
44791041Sobrienstatic int
44891041Sobrienng_source_disconnect(hook_p hook)
44991041Sobrien{
45091041Sobrien	struct source_hookinfo *hinfo;
45191041Sobrien	sc_p sc;
45291041Sobrien
45391041Sobrien	hinfo = NG_HOOK_PRIVATE(hook);
45491041Sobrien	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
45591041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
45691041Sobrien	hinfo->hook = NULL;
45791041Sobrien	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output)
45891041Sobrien		ng_rmnode_self(NG_HOOK_NODE(hook));
45991041Sobrien	return (0);
46091041Sobrien}
46191041Sobrien
46291041Sobrien/*
46391041Sobrien *
46491041Sobrien * Ask out neighbour on the output hook side to send us it's interface
46591041Sobrien * information.
46691041Sobrien */
46791041Sobrienstatic int
46891041Sobrienng_source_request_output_ifp(sc_p sc)
46991041Sobrien{
47091041Sobrien	struct ng_mesg *msg;
47191041Sobrien	int error = 0;
47291041Sobrien
47391041Sobrien	sc->output_ifp = NULL;
47491041Sobrien
47591041Sobrien	/* Ask the attached node for the connected interface's index */
47691041Sobrien	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT);
47791041Sobrien	if (msg == NULL)
47891041Sobrien		return (ENOBUFS);
47991041Sobrien
48091041Sobrien	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, 0);
48191041Sobrien	return (error);
48291041Sobrien}
48391041Sobrien
48491041Sobrien/*
48591041Sobrien * Set sc->output_ifp to point to the the struct ifnet of the interface
48691041Sobrien * reached via our output hook.
48791041Sobrien */
48891041Sobrienstatic int
48991041Sobrienng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg)
49091041Sobrien{
49191041Sobrien	struct ifnet *ifp;
49291041Sobrien	u_int32_t if_index;
49391041Sobrien	int s;
49491041Sobrien
49591041Sobrien	if (msg->header.arglen < sizeof(u_int32_t))
49691041Sobrien		return (EINVAL);
49791041Sobrien
49891041Sobrien	if_index = *(u_int32_t *)msg->data;
499130561Sobrien	/* Could use ifindex2ifnet[if_index] except that we have no
500130561Sobrien	 * way of verifying if_index is valid since if_indexlim is
50191041Sobrien	 * local to if_attach()
50291041Sobrien	 */
50391041Sobrien	IFNET_RLOCK();
50491041Sobrien	TAILQ_FOREACH(ifp, &ifnet, if_link) {
505130561Sobrien		if (ifp->if_index == if_index)
50691041Sobrien			break;
50791041Sobrien	}
50891041Sobrien	IFNET_RUNLOCK();
50991041Sobrien
51091041Sobrien	if (ifp == NULL) {
51191041Sobrien		printf("%s: can't find interface %d\n", __func__, if_index);
51291041Sobrien		return (EINVAL);
51391041Sobrien	}
51491041Sobrien	sc->output_ifp = ifp;
51591041Sobrien
51691041Sobrien#if 1
51791041Sobrien	/* XXX mucking with a drivers ifqueue size is ugly but we need it
51891041Sobrien	 * to queue a lot of packets to get close to line rate on a gigabit
51991041Sobrien	 * interface with small packets.
52091041Sobrien	 * XXX we should restore the original value at stop or disconnect
52191041Sobrien	 */
52291041Sobrien	s = splimp();		/* XXX is this required? */
52391041Sobrien	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) {
52491041Sobrien		printf("ng_source: changing ifq_maxlen from %d to %d\n",
52591041Sobrien		    ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
526130561Sobrien		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
52791041Sobrien	}
52891041Sobrien	splx(s);
52991041Sobrien#endif
53091041Sobrien	return (0);
53191041Sobrien}
53291041Sobrien
53391041Sobrien/*
53491041Sobrien * Set the attached ethernet node's ethernet source address override flag.
53591041Sobrien */
536130561Sobrienstatic int
53791041Sobrienng_source_set_autosrc(sc_p sc, u_int32_t flag)
53891041Sobrien{
53991041Sobrien	struct ng_mesg *msg;
54091041Sobrien	int error = 0;
54191041Sobrien
542130561Sobrien	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
543130561Sobrien	    sizeof (u_int32_t), M_NOWAIT);
54491041Sobrien	if (msg == NULL)
54591041Sobrien		return(ENOBUFS);
54691041Sobrien
547130561Sobrien	*(u_int32_t *)msg->data = flag;
54891041Sobrien	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, 0);
54991041Sobrien	return (error);
55091041Sobrien}
55191041Sobrien
552130561Sobrien/*
55391041Sobrien * Clear out the data we've queued
55491041Sobrien */
55591041Sobrienstatic void
55691041Sobrienng_source_clr_data (sc_p sc)
55791041Sobrien{
55891041Sobrien	struct mbuf *m;
559130561Sobrien
56091041Sobrien	for (;;) {
56191041Sobrien		_IF_DEQUEUE(&sc->snd_queue, m);
56291041Sobrien		if (m == NULL)
563130561Sobrien			break;
56491041Sobrien		NG_FREE_M(m);
56591041Sobrien	}
56691041Sobrien	sc->queueOctets = 0;
56791041Sobrien}
568130561Sobrien
56991041Sobrien/*
57091041Sobrien * Start sending queued data out the output hook
57191041Sobrien */
57291041Sobrienstatic void
57391041Sobrienng_source_start (sc_p sc)
57491041Sobrien{
57591041Sobrien	KASSERT(sc->output.hook != NULL,
57691041Sobrien			("%s: output hook unconnected", __func__));
577130561Sobrien	if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) &&
57891041Sobrien	    (sc->output_ifp == NULL))
57991041Sobrien		ng_source_request_output_ifp(sc);
58091041Sobrien}
58191041Sobrien
58291041Sobrien/*
58391041Sobrien * Stop sending queued data out the output hook
58491041Sobrien */
585130561Sobrienstatic void
58691041Sobrienng_source_stop (sc_p sc)
58791041Sobrien{
58891041Sobrien	if (sc->node->nd_flags & NG_SOURCE_ACTIVE) {
58991041Sobrien		ng_uncallout(&sc->intr_ch, sc->node);
59091041Sobrien		sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
59191041Sobrien		getmicrotime(&sc->stats.endTime);
592130561Sobrien		sc->stats.elapsedTime = sc->stats.endTime;
59391041Sobrien		timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
59491041Sobrien		/* XXX should set this to the initial value instead */
59591041Sobrien		ng_source_set_autosrc(sc, 1);
59691041Sobrien	}
59791041Sobrien}
59891041Sobrien
59991041Sobrien/*
60091041Sobrien * While active called every NG_SOURCE_INTR_TICKS ticks.
60191041Sobrien * Sends as many packets as the interface connected to our
60291041Sobrien * output hook is able to enqueue.
60391041Sobrien */
60491041Sobrienstatic void
60591041Sobrienng_source_intr(node_p node, hook_p hook, void *arg1, int arg2)
60691041Sobrien{
60791041Sobrien	sc_p sc = (sc_p)arg1;
60891041Sobrien	struct ifqueue *ifq;
60991041Sobrien	int packets;
61091041Sobrien
61191041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
61291041Sobrien
61391041Sobrien	if (sc->packets == 0 || sc->output.hook == NULL
61491041Sobrien	    || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
61591041Sobrien		ng_source_stop(sc);
61691041Sobrien		return;
61791041Sobrien	}
61891041Sobrien
61991041Sobrien	if (sc->output_ifp != NULL) {
62091041Sobrien		ifq = &sc->output_ifp->if_snd;
62191041Sobrien		packets = ifq->ifq_maxlen - ifq->ifq_len;
62291041Sobrien	} else
62391041Sobrien		packets = sc->snd_queue.ifq_len;
62491041Sobrien
62591041Sobrien	ng_source_send(sc, packets, NULL);
62691041Sobrien	if (sc->packets == 0)
62791041Sobrien		ng_source_stop(sc);
62891041Sobrien	else
62991041Sobrien		ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS,
63091041Sobrien		    ng_source_intr, sc, 0);
63191041Sobrien}
632130561Sobrien
63391041Sobrien/*
63491041Sobrien * Send packets out our output hook
63591041Sobrien */
63691041Sobrienstatic int
63791041Sobrienng_source_send (sc_p sc, int tosend, int *sent_p)
63891041Sobrien{
63991041Sobrien	struct ifqueue tmp_queue;
640130561Sobrien	struct mbuf *m, *m2;
64191041Sobrien	int sent = 0;
64291041Sobrien	int error = 0;
64391041Sobrien
64491041Sobrien	KASSERT(sc != NULL, ("%s: null node private", __func__));
64591041Sobrien	KASSERT(tosend >= 0, ("%s: negative tosend param", __func__));
64691041Sobrien	KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
64791041Sobrien	    ("%s: inactive node", __func__));
64891041Sobrien
64991041Sobrien	if ((u_int64_t)tosend > sc->packets)
65091041Sobrien		tosend = sc->packets;
65191041Sobrien
65291041Sobrien	/* Copy the required number of packets to a temporary queue */
65391041Sobrien	bzero (&tmp_queue, sizeof (tmp_queue));
65491041Sobrien	for (sent = 0; error == 0 && sent < tosend; ++sent) {
65591041Sobrien		_IF_DEQUEUE(&sc->snd_queue, m);
65691041Sobrien		if (m == NULL)
65791041Sobrien			break;
65891041Sobrien
65991041Sobrien		/* duplicate the packet */
660130561Sobrien		m2 = m_copypacket(m, M_DONTWAIT);
66191041Sobrien		if (m2 == NULL) {
66291041Sobrien			_IF_PREPEND(&sc->snd_queue, m);
66391041Sobrien			error = ENOBUFS;
66491041Sobrien			break;
66591041Sobrien		}
66691041Sobrien
66791041Sobrien		/* re-enqueue the original packet for us */
66891041Sobrien		_IF_ENQUEUE(&sc->snd_queue, m);
66991041Sobrien
670130561Sobrien		/* queue the copy for sending at smplimp */
67191041Sobrien		_IF_ENQUEUE(&tmp_queue, m2);
67291041Sobrien	}
673130561Sobrien
674130561Sobrien	sent = 0;
67591041Sobrien	for (;;) {
67691041Sobrien		_IF_DEQUEUE(&tmp_queue, m2);
67791041Sobrien		if (m2 == NULL)
67891041Sobrien			break;
67991041Sobrien		if (error == 0) {
68091041Sobrien			++sent;
68191041Sobrien			sc->stats.outFrames++;
68291041Sobrien			sc->stats.outOctets += m2->m_pkthdr.len;
683130561Sobrien			NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
68491041Sobrien			if (error)
68591041Sobrien				printf("%s: error=%d\n", __func__, error);
68691041Sobrien		} else {
687130561Sobrien			NG_FREE_M(m2);
68891041Sobrien		}
68991041Sobrien	}
69091041Sobrien
69191041Sobrien	sc->packets -= sent;
69291041Sobrien	if (sent_p != NULL)
69391041Sobrien		*sent_p = sent;
69491041Sobrien	return (error);
69591041Sobrien}
69691041Sobrien