ng_source.c revision 125031
1210284Sjmallett/*
2232812Sjmallett * ng_source.c
3215990Sjmallett *
4210284Sjmallett * Copyright 2002 Sandvine Inc.
5210284Sjmallett * All rights reserved.
6215990Sjmallett *
7215990Sjmallett * Subject to the following obligations and disclaimer of warranty, use and
8215990Sjmallett * redistribution of this software, in source or object code forms, with or
9210284Sjmallett * without modifications are expressly permitted by Sandvine Inc.; provided,
10215990Sjmallett * however, that:
11215990Sjmallett * 1. Any and all reproductions of the source or object code must include the
12210284Sjmallett *    copyright notice above and the following disclaimer of warranties; and
13215990Sjmallett * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
14215990Sjmallett *    trademarks, including the mark "SANDVINE" on advertising, endorsements,
15215990Sjmallett *    or otherwise except as such appears in the above copyright notice or in
16215990Sjmallett *    the software.
17215990Sjmallett *
18232812Sjmallett * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
19215990Sjmallett * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES,
20215990Sjmallett * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION,
21215990Sjmallett * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22215990Sjmallett * PURPOSE, OR NON-INFRINGEMENT.  SANDVINE DOES NOT WARRANT, GUARANTEE, OR
23215990Sjmallett * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
24215990Sjmallett * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
25215990Sjmallett * OR OTHERWISE.  IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
26215990Sjmallett * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
27215990Sjmallett * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
28215990Sjmallett * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
29232812Sjmallett * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
30215990Sjmallett * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31215990Sjmallett * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32215990Sjmallett * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
33215990Sjmallett * DAMAGE.
34215990Sjmallett *
35215990Sjmallett * Author: Dave Chapeskie <dchapeskie@sandvine.com>
36215990Sjmallett *
37215990Sjmallett * $FreeBSD: head/sys/netgraph/ng_source.c 125031 2004-01-26 14:46:35Z harti $
38210284Sjmallett */
39210284Sjmallett
40210284Sjmallett/*
41210284Sjmallett * This node is used for high speed packet geneneration.  It queues
42210284Sjmallett * all data recieved on it's 'input' hook and when told to start via
43210284Sjmallett * a control message it sends the packets out it's 'output' hook.  In
44210284Sjmallett * this way this node can be preloaded with a packet stream which is
45215990Sjmallett * continuously sent.
46210284Sjmallett *
47210284Sjmallett * Currently it just copies the mbufs as required.  It could do various
48210284Sjmallett * tricks to try and avoid this.  Probably the best performance would
49210284Sjmallett * be achieved by modifying the appropriate drivers to be told to
50210284Sjmallett * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
51210284Sjmallett * transmit descriptors) under control of this node; perhaps via some
52210284Sjmallett * flag in the mbuf or some such.  The node would peak at an appropriate
53210284Sjmallett * ifnet flag to see if such support is available for the connected
54210284Sjmallett * interface.
55210284Sjmallett */
56210284Sjmallett
57210284Sjmallett#include <sys/param.h>
58210284Sjmallett#include <sys/systm.h>
59210284Sjmallett#include <sys/errno.h>
60210284Sjmallett#include <sys/kernel.h>
61210284Sjmallett#include <sys/malloc.h>
62210284Sjmallett#include <sys/mbuf.h>
63210284Sjmallett#include <sys/socket.h>
64210284Sjmallett#include <net/if.h>
65215990Sjmallett#include <net/if_var.h>
66210284Sjmallett#include <netgraph/ng_message.h>
67210284Sjmallett#include <netgraph/netgraph.h>
68210284Sjmallett#include <netgraph/ng_parse.h>
69210284Sjmallett#include <netgraph/ng_ether.h>
70210284Sjmallett#include <netgraph/ng_source.h>
71210284Sjmallett
72210284Sjmallett#define NG_SOURCE_INTR_TICKS		1
73210284Sjmallett#define NG_SOURCE_DRIVER_IFQ_MAXLEN	(4*1024)
74210284Sjmallett
75210284Sjmallett
76210284Sjmallett/* Per hook info */
77210284Sjmallettstruct source_hookinfo {
78210284Sjmallett	hook_p				hook;
79210284Sjmallett};
80210284Sjmallett
81210284Sjmallett/* Per node info */
82210284Sjmallettstruct privdata {
83210284Sjmallett	node_p				node;
84210284Sjmallett	struct source_hookinfo		input;
85210284Sjmallett	struct source_hookinfo		output;
86210284Sjmallett	struct ng_source_stats		stats;
87210284Sjmallett	struct ifqueue			snd_queue;	/* packets to send */
88210284Sjmallett	struct ifnet			*output_ifp;
89210284Sjmallett	struct callout_handle		intr_ch;
90210284Sjmallett	u_int64_t			packets;	/* packets to send */
91232812Sjmallett	u_int32_t			queueOctets;
92210284Sjmallett};
93210284Sjmalletttypedef struct privdata *sc_p;
94210284Sjmallett
95210284Sjmallett/* Node flags */
96210284Sjmallett#define NG_SOURCE_ACTIVE	(NGF_TYPE1)
97215990Sjmallett
98210284Sjmallett/* XXX */
99210284Sjmallett#if 1
100210311Sjmallett#undef KASSERT
101215990Sjmallett#define KASSERT(expr,msg) do {			\
102210284Sjmallett		if (!(expr)) {			\
103210284Sjmallett			printf msg ;		\
104210284Sjmallett			panic("Assertion");	\
105210284Sjmallett		}				\
106210284Sjmallett	} while(0)
107210284Sjmallett#endif
108210284Sjmallett
109210284Sjmallett/* Netgraph methods */
110210284Sjmallettstatic ng_constructor_t	ng_source_constructor;
111210284Sjmallettstatic ng_rcvmsg_t	ng_source_rcvmsg;
112210284Sjmallettstatic ng_shutdown_t	ng_source_rmnode;
113210284Sjmallettstatic ng_newhook_t	ng_source_newhook;
114210284Sjmallettstatic ng_rcvdata_t	ng_source_rcvdata;
115210284Sjmallettstatic ng_disconnect_t	ng_source_disconnect;
116210284Sjmallett
117210284Sjmallett/* Other functions */
118210284Sjmallettstatic timeout_t	ng_source_intr;
119210284Sjmallettstatic int		ng_source_request_output_ifp (sc_p);
120210284Sjmallettstatic void		ng_source_clr_data (sc_p);
121210284Sjmallettstatic void		ng_source_start (sc_p);
122210284Sjmallettstatic void		ng_source_stop (sc_p);
123210284Sjmallettstatic int		ng_source_send (sc_p, int, int *);
124210284Sjmallettstatic int		ng_source_store_output_ifp(sc_p sc,
125210284Sjmallett			    struct ng_mesg *msg);
126210284Sjmallett
127210284Sjmallett
128232812Sjmallett/* Parse type for timeval */
129210284Sjmallettstatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] =
130210284Sjmallett{
131210284Sjmallett	{ "tv_sec",		&ng_parse_int32_type	},
132210284Sjmallett	{ "tv_usec",		&ng_parse_int32_type	},
133210284Sjmallett	{ NULL }
134210284Sjmallett};
135210284Sjmallettconst struct ng_parse_type ng_source_timeval_type = {
136210284Sjmallett	&ng_parse_struct_type,
137215990Sjmallett	&ng_source_timeval_type_fields
138210284Sjmallett};
139210284Sjmallett
140210284Sjmallett/* Parse type for struct ng_source_stats */
141210284Sjmallettstatic const struct ng_parse_struct_field ng_source_stats_type_fields[]
142210284Sjmallett	= NG_SOURCE_STATS_TYPE_INFO;
143210284Sjmallettstatic const struct ng_parse_type ng_source_stats_type = {
144210284Sjmallett	&ng_parse_struct_type,
145210284Sjmallett	&ng_source_stats_type_fields
146210284Sjmallett};
147210284Sjmallett
148210284Sjmallett/* List of commands and how to convert arguments to/from ASCII */
149210284Sjmallettstatic const struct ng_cmdlist ng_source_cmds[] = {
150210284Sjmallett	{
151210284Sjmallett	  NGM_SOURCE_COOKIE,
152210284Sjmallett	  NGM_SOURCE_GET_STATS,
153210284Sjmallett	  "getstats",
154210284Sjmallett	  NULL,
155210284Sjmallett	  &ng_source_stats_type
156210284Sjmallett	},
157210284Sjmallett	{
158210284Sjmallett	  NGM_SOURCE_COOKIE,
159210284Sjmallett	  NGM_SOURCE_CLR_STATS,
160215990Sjmallett	  "clrstats",
161210284Sjmallett	  NULL,
162210284Sjmallett	  NULL
163210284Sjmallett	},
164215990Sjmallett	{
165210284Sjmallett	  NGM_SOURCE_COOKIE,
166215990Sjmallett	  NGM_SOURCE_GETCLR_STATS,
167215990Sjmallett	  "getclrstats",
168210284Sjmallett	  NULL,
169210284Sjmallett	  &ng_source_stats_type
170210284Sjmallett	},
171210284Sjmallett	{
172210284Sjmallett	  NGM_SOURCE_COOKIE,
173210284Sjmallett	  NGM_SOURCE_START,
174210284Sjmallett	  "start",
175210284Sjmallett	  &ng_parse_uint64_type,
176210284Sjmallett	  NULL
177210311Sjmallett	},
178210311Sjmallett	{
179210284Sjmallett	  NGM_SOURCE_COOKIE,
180210284Sjmallett	  NGM_SOURCE_STOP,
181210284Sjmallett	  "stop",
182210284Sjmallett	  NULL,
183210284Sjmallett	  NULL
184210284Sjmallett	},
185210284Sjmallett	{
186210284Sjmallett	  NGM_SOURCE_COOKIE,
187210284Sjmallett	  NGM_SOURCE_CLR_DATA,
188210284Sjmallett	  "clrdata",
189210284Sjmallett	  NULL,
190210284Sjmallett	  NULL
191210284Sjmallett	},
192210284Sjmallett	{ 0 }
193210284Sjmallett};
194210284Sjmallett
195210284Sjmallett/* Netgraph type descriptor */
196210284Sjmallettstatic struct ng_type ng_source_typestruct = {
197210284Sjmallett	NG_ABI_VERSION,
198210284Sjmallett	NG_SOURCE_NODE_TYPE,
199210284Sjmallett	NULL,					/* module event handler */
200210284Sjmallett	ng_source_constructor,
201210284Sjmallett	ng_source_rcvmsg,
202210284Sjmallett	ng_source_rmnode,
203210284Sjmallett	ng_source_newhook,
204210284Sjmallett	NULL,					/* findhook */
205210284Sjmallett	NULL,
206210284Sjmallett	ng_source_rcvdata,			/* rcvdata */
207210284Sjmallett	ng_source_disconnect,
208210284Sjmallett	ng_source_cmds
209210284Sjmallett};
210210284SjmallettNETGRAPH_INIT(source, &ng_source_typestruct);
211210284Sjmallett
212210284Sjmallett/*
213210284Sjmallett * Node constructor
214210284Sjmallett */
215210284Sjmallettstatic int
216215990Sjmallettng_source_constructor(node_p node)
217210284Sjmallett{
218210284Sjmallett	sc_p sc;
219210284Sjmallett
220210284Sjmallett	sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
221210284Sjmallett	if (sc == NULL)
222210284Sjmallett		return (ENOMEM);
223210284Sjmallett
224210284Sjmallett	NG_NODE_SET_PRIVATE(node, sc);
225210284Sjmallett	sc->node = node;
226210284Sjmallett	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
227210284Sjmallett	callout_handle_init(&sc->intr_ch);   /* XXX fix.. will
228210284Sjmallett						cause problems. */
229210284Sjmallett	return (0);
230210284Sjmallett}
231210284Sjmallett
232210284Sjmallett/*
233210284Sjmallett * Add a hook
234210284Sjmallett */
235210284Sjmallettstatic int
236210284Sjmallettng_source_newhook(node_p node, hook_p hook, const char *name)
237232812Sjmallett{
238210284Sjmallett	sc_p sc;
239210284Sjmallett
240210284Sjmallett	sc = NG_NODE_PRIVATE(node);
241210284Sjmallett	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
242210284Sjmallett	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
243210284Sjmallett		sc->input.hook = hook;
244210284Sjmallett		NG_HOOK_SET_PRIVATE(hook, &sc->input);
245210284Sjmallett	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
246210284Sjmallett		sc->output.hook = hook;
247210284Sjmallett		NG_HOOK_SET_PRIVATE(hook, &sc->output);
248210284Sjmallett		sc->output_ifp = 0;
249210284Sjmallett		bzero(&sc->stats, sizeof(sc->stats));
250210284Sjmallett	} else
251210284Sjmallett		return (EINVAL);
252210284Sjmallett	return (0);
253210284Sjmallett}
254210284Sjmallett
255210284Sjmallett/*
256210284Sjmallett * Receive a control message
257210284Sjmallett */
258210284Sjmallettstatic int
259210284Sjmallettng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
260210284Sjmallett{
261210284Sjmallett	sc_p sc;
262210284Sjmallett	struct ng_mesg *resp = NULL;
263210284Sjmallett	int error = 0;
264210284Sjmallett	struct ng_mesg *msg;
265210284Sjmallett
266210284Sjmallett	sc = NG_NODE_PRIVATE(node);
267210284Sjmallett	NGI_GET_MSG(item, msg);
268210284Sjmallett	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
269210284Sjmallett	switch (msg->header.typecookie) {
270210284Sjmallett	case NGM_SOURCE_COOKIE:
271210284Sjmallett		if (msg->header.flags & NGF_RESP) {
272210284Sjmallett			error = EINVAL;
273210284Sjmallett			break;
274210284Sjmallett		}
275210284Sjmallett		switch (msg->header.cmd) {
276210284Sjmallett		case NGM_SOURCE_GET_STATS:
277210284Sjmallett		case NGM_SOURCE_CLR_STATS:
278210284Sjmallett		case NGM_SOURCE_GETCLR_STATS:
279210284Sjmallett                    {
280210284Sjmallett			struct ng_source_stats *stats;
281210284Sjmallett
282210284Sjmallett                        if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
283210284Sjmallett                                NG_MKRESPONSE(resp, msg,
284210284Sjmallett                                    sizeof(*stats), M_NOWAIT);
285210284Sjmallett				if (resp == NULL) {
286210284Sjmallett					error = ENOMEM;
287210284Sjmallett					goto done;
288210284Sjmallett				}
289210284Sjmallett				sc->stats.queueOctets = sc->queueOctets;
290210284Sjmallett				sc->stats.queueFrames = sc->snd_queue.ifq_len;
291210284Sjmallett				if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
292210284Sjmallett				    && !timevalisset(&sc->stats.endTime)) {
293210284Sjmallett					getmicrotime(&sc->stats.elapsedTime);
294210284Sjmallett					timevalsub(&sc->stats.elapsedTime,
295210284Sjmallett					    &sc->stats.startTime);
296210284Sjmallett				}
297210284Sjmallett				stats = (struct ng_source_stats *)resp->data;
298210284Sjmallett				bcopy(&sc->stats, stats, sizeof(* stats));
299215990Sjmallett                        }
300215990Sjmallett                        if (msg->header.cmd != NGM_SOURCE_GET_STATS)
301215990Sjmallett				bzero(&sc->stats, sizeof(sc->stats));
302215990Sjmallett		    }
303215990Sjmallett		    break;
304215990Sjmallett		case NGM_SOURCE_START:
305210284Sjmallett		    {
306210284Sjmallett			u_int64_t packets = *(u_int64_t *)msg->data;
307210284Sjmallett			if (sc->output.hook == NULL) {
308210284Sjmallett				printf("%s: start on node with no output hook\n"
309210284Sjmallett				    , __FUNCTION__);
310210284Sjmallett				error = EINVAL;
311210284Sjmallett				break;
312210284Sjmallett			}
313210284Sjmallett			/* TODO validation of packets */
314210284Sjmallett			sc->packets = packets;
315210284Sjmallett			ng_source_start(sc);
316210284Sjmallett		    }
317210284Sjmallett		    break;
318210284Sjmallett		case NGM_SOURCE_STOP:
319210284Sjmallett			ng_source_stop(sc);
320210284Sjmallett			break;
321210284Sjmallett		case NGM_SOURCE_CLR_DATA:
322210284Sjmallett			ng_source_clr_data(sc);
323210284Sjmallett			break;
324210284Sjmallett		default:
325210284Sjmallett			error = EINVAL;
326210284Sjmallett			break;
327210284Sjmallett		}
328210284Sjmallett		break;
329210284Sjmallett	case NGM_ETHER_COOKIE:
330210284Sjmallett		if (!(msg->header.flags & NGF_RESP)) {
331215990Sjmallett			error = EINVAL;
332210284Sjmallett			break;
333210284Sjmallett		}
334210284Sjmallett		switch (msg->header.cmd) {
335210284Sjmallett		case NGM_ETHER_GET_IFINDEX:
336210284Sjmallett			if (ng_source_store_output_ifp(sc, msg) == 0) {
337210284Sjmallett				ng_source_set_autosrc(sc, 0);
338210284Sjmallett				sc->node->nd_flags |= NG_SOURCE_ACTIVE;
339210284Sjmallett				timevalclear(&sc->stats.elapsedTime);
340215990Sjmallett				timevalclear(&sc->stats.endTime);
341210284Sjmallett				getmicrotime(&sc->stats.startTime);
342210284Sjmallett				sc->intr_ch = timeout(ng_source_intr, sc, 0);
343210284Sjmallett			}
344210284Sjmallett			break;
345210284Sjmallett		default:
346210284Sjmallett			error = EINVAL;
347210284Sjmallett		}
348210284Sjmallett		break;
349210284Sjmallett	default:
350210284Sjmallett		error = EINVAL;
351210284Sjmallett		break;
352210284Sjmallett	}
353210284Sjmallett
354210284Sjmallettdone:
355210284Sjmallett	/* Take care of synchronous response, if any */
356210284Sjmallett	NG_RESPOND_MSG(error, node, item, resp);
357210284Sjmallett	/* Free the message and return */
358210284Sjmallett	NG_FREE_MSG(msg);
359210284Sjmallett	return (error);
360210284Sjmallett}
361210284Sjmallett
362210284Sjmallett/*
363210284Sjmallett * Receive data on a hook
364210284Sjmallett *
365210284Sjmallett * If data comes in the input hook, enqueue it on the send queue.
366210284Sjmallett * If data comes in the output hook, discard it.
367210284Sjmallett */
368210284Sjmallettstatic int
369210284Sjmallettng_source_rcvdata(hook_p hook, item_p item)
370210284Sjmallett{
371210284Sjmallett	sc_p sc;
372210284Sjmallett	struct source_hookinfo *hinfo;
373210284Sjmallett	int error = 0;
374210284Sjmallett	struct mbuf *m;
375210284Sjmallett
376210284Sjmallett	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
377210284Sjmallett	NGI_GET_M(item, m);
378210284Sjmallett	NG_FREE_ITEM(item);
379210284Sjmallett	hinfo = NG_HOOK_PRIVATE(hook);
380210284Sjmallett	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
381210284Sjmallett	KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__));
382210284Sjmallett
383210284Sjmallett	/* Which hook? */
384210284Sjmallett	if (hinfo == &sc->output) {
385210284Sjmallett		/* discard */
386210284Sjmallett		NG_FREE_M(m);
387210284Sjmallett		return (error);
388215990Sjmallett	}
389210284Sjmallett	KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__));
390210284Sjmallett
391210284Sjmallett	if ((m->m_flags & M_PKTHDR) == 0) {
392210284Sjmallett		printf("%s: mbuf without PKTHDR\n", __FUNCTION__);
393210284Sjmallett		NG_FREE_M(m);
394210284Sjmallett		return (EINVAL);
395210284Sjmallett	}
396210284Sjmallett
397210284Sjmallett	/* enque packet */
398210284Sjmallett	/* XXX should we check IF_QFULL() ? */
399210284Sjmallett	_IF_ENQUEUE(&sc->snd_queue, m);
400210284Sjmallett	sc->queueOctets += m->m_pkthdr.len;
401210284Sjmallett
402210284Sjmallett	return (0);
403210284Sjmallett}
404210284Sjmallett
405210284Sjmallett/*
406210284Sjmallett * Shutdown processing
407210284Sjmallett */
408210284Sjmallettstatic int
409210284Sjmallettng_source_rmnode(node_p node)
410210284Sjmallett{
411210284Sjmallett	sc_p sc;
412210284Sjmallett
413210284Sjmallett	sc = NG_NODE_PRIVATE(node);
414210284Sjmallett	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
415210284Sjmallett	node->nd_flags |= NG_INVALID;
416210284Sjmallett	ng_source_stop(sc);
417210284Sjmallett	ng_source_clr_data(sc);
418210284Sjmallett	NG_NODE_SET_PRIVATE(node, NULL);
419210284Sjmallett	NG_NODE_UNREF(node);
420210284Sjmallett	FREE(sc, M_NETGRAPH);
421210284Sjmallett	return (0);
422210284Sjmallett}
423210284Sjmallett
424210284Sjmallett/*
425210284Sjmallett * Hook disconnection
426210284Sjmallett */
427210284Sjmallettstatic int
428210284Sjmallettng_source_disconnect(hook_p hook)
429210284Sjmallett{
430210284Sjmallett	struct source_hookinfo *hinfo;
431210284Sjmallett	sc_p sc;
432210284Sjmallett
433210284Sjmallett	hinfo = NG_HOOK_PRIVATE(hook);
434210284Sjmallett	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
435210284Sjmallett	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
436210284Sjmallett	hinfo->hook = NULL;
437210284Sjmallett	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output)
438210284Sjmallett		ng_rmnode_self(NG_HOOK_NODE(hook));
439210284Sjmallett	return (0);
440210284Sjmallett}
441210284Sjmallett
442210284Sjmallett/*
443210284Sjmallett *
444210284Sjmallett * Ask out neighbour on the output hook side to send us it's interface
445210284Sjmallett * information.
446210284Sjmallett */
447210284Sjmallettstatic int
448210284Sjmallettng_source_request_output_ifp(sc_p sc)
449210284Sjmallett{
450210284Sjmallett	struct ng_mesg *msg;
451210284Sjmallett	int error = 0;
452210284Sjmallett
453210284Sjmallett	sc->output_ifp = NULL;
454210284Sjmallett
455210284Sjmallett	/* Ask the attached node for the connected interface's index */
456210284Sjmallett	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT);
457210284Sjmallett	if (msg == NULL)
458210284Sjmallett		return (ENOBUFS);
459210284Sjmallett
460210284Sjmallett	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL);
461210284Sjmallett	return (error);
462210284Sjmallett}
463210284Sjmallett
464210284Sjmallett/*
465210284Sjmallett * Set sc->output_ifp to point to the the struct ifnet of the interface
466210284Sjmallett * reached via our output hook.
467210284Sjmallett */
468210284Sjmallettstatic int
469210284Sjmallettng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg)
470210284Sjmallett{
471210284Sjmallett	struct ifnet *ifp;
472210284Sjmallett	u_int32_t if_index;
473210284Sjmallett	int s;
474210284Sjmallett
475210284Sjmallett	if (msg->header.arglen < sizeof(u_int32_t))
476210284Sjmallett		return (EINVAL);
477215990Sjmallett
478210284Sjmallett	if_index = *(u_int32_t *)msg->data;
479210284Sjmallett	/* Could use ifindex2ifnet[if_index] except that we have no
480210284Sjmallett	 * way of verifying if_index is valid since if_indexlim is
481210284Sjmallett	 * local to if_attach()
482210284Sjmallett	 */
483210284Sjmallett	IFNET_RLOCK();
484210284Sjmallett	TAILQ_FOREACH(ifp, &ifnet, if_link) {
485210284Sjmallett		if (ifp->if_index == if_index)
486210284Sjmallett			break;
487210284Sjmallett	}
488210284Sjmallett	IFNET_RUNLOCK();
489210284Sjmallett
490210284Sjmallett	if (ifp == NULL) {
491210284Sjmallett		printf("%s: can't find interface %d\n", __FUNCTION__, if_index);
492210284Sjmallett		return (EINVAL);
493210284Sjmallett	}
494210284Sjmallett	sc->output_ifp = ifp;
495210284Sjmallett
496210284Sjmallett#if 1
497210284Sjmallett	/* XXX mucking with a drivers ifqueue size is ugly but we need it
498210284Sjmallett	 * to queue a lot of packets to get close to line rate on a gigabit
499210284Sjmallett	 * interface with small packets.
500210284Sjmallett	 * XXX we should restore the original value at stop or disconnect
501210284Sjmallett	 */
502210284Sjmallett	s = splimp();		/* XXX is this required? */
503210284Sjmallett	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN)
504210284Sjmallett	{
505210284Sjmallett		printf("ng_source: changing ifq_maxlen from %d to %d\n",
506210284Sjmallett		    ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
507210284Sjmallett		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
508210284Sjmallett	}
509210284Sjmallett	splx(s);
510210284Sjmallett#endif
511210284Sjmallett	return (0);
512210284Sjmallett}
513210284Sjmallett
514210284Sjmallett/*
515210284Sjmallett * Set the attached ethernet node's ethernet source address override flag.
516210284Sjmallett */
517210284Sjmallettstatic int
518210284Sjmallettng_source_set_autosrc(sc_p sc, u_int32_t flag)
519210284Sjmallett{
520210284Sjmallett	struct ng_mesg *msg;
521210284Sjmallett	int error = 0;
522210284Sjmallett
523210284Sjmallett	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
524210284Sjmallett			sizeof (u_int32_t), M_NOWAIT);
525210284Sjmallett	if (msg == NULL)
526210284Sjmallett		return(ENOBUFS);
527210284Sjmallett
528210284Sjmallett	*(u_int32_t *)msg->data = flag;
529210284Sjmallett	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL);
530210284Sjmallett	return (error);
531210284Sjmallett}
532210284Sjmallett
533210284Sjmallett/*
534210284Sjmallett * Clear out the data we've queued
535210284Sjmallett */
536210284Sjmallettstatic void
537210284Sjmallettng_source_clr_data (sc_p sc)
538210284Sjmallett{
539210284Sjmallett	struct mbuf *m;
540210284Sjmallett
541210284Sjmallett	for (;;) {
542210284Sjmallett		_IF_DEQUEUE(&sc->snd_queue, m);
543210284Sjmallett		if (m == NULL)
544210284Sjmallett			break;
545210284Sjmallett		NG_FREE_M(m);
546210284Sjmallett	}
547210284Sjmallett	sc->queueOctets = 0;
548210284Sjmallett}
549210284Sjmallett
550210284Sjmallett/*
551210284Sjmallett * Start sending queued data out the output hook
552210284Sjmallett */
553210284Sjmallettstatic void
554210284Sjmallettng_source_start (sc_p sc)
555210284Sjmallett{
556210284Sjmallett	KASSERT(sc->output.hook != NULL,
557210284Sjmallett			("%s: output hook unconnected", __FUNCTION__));
558210284Sjmallett	if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) &&
559210284Sjmallett	    (sc->output_ifp == NULL))
560210284Sjmallett		ng_source_request_output_ifp(sc);
561210284Sjmallett}
562210284Sjmallett
563210284Sjmallett/*
564210284Sjmallett * Stop sending queued data out the output hook
565210284Sjmallett */
566210284Sjmallettstatic void
567210284Sjmallettng_source_stop (sc_p sc)
568215990Sjmallett{
569210284Sjmallett	if (sc->node->nd_flags & NG_SOURCE_ACTIVE) {
570210284Sjmallett		untimeout(ng_source_intr, sc, sc->intr_ch);
571210284Sjmallett		sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
572210284Sjmallett		getmicrotime(&sc->stats.endTime);
573210284Sjmallett		sc->stats.elapsedTime = sc->stats.endTime;
574210284Sjmallett		timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
575210284Sjmallett		/* XXX should set this to the initial value instead */
576210284Sjmallett		ng_source_set_autosrc(sc, 1);
577210284Sjmallett	}
578210284Sjmallett}
579210284Sjmallett
580210284Sjmallett/*
581210284Sjmallett * While active called every NG_SOURCE_INTR_TICKS ticks.
582210284Sjmallett * Sends as many packets as the interface connected to our
583210284Sjmallett * output hook is able to enqueue.
584210284Sjmallett */
585210284Sjmallettstatic void
586210284Sjmallettng_source_intr (void *arg)
587210284Sjmallett{
588210284Sjmallett	sc_p sc = (sc_p) arg;
589210284Sjmallett	struct ifqueue *ifq;
590210284Sjmallett	int packets;
591210284Sjmallett
592210284Sjmallett	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
593210284Sjmallett
594210284Sjmallett	callout_handle_init(&sc->intr_ch);
595210284Sjmallett	if (sc->packets == 0 || sc->output.hook == NULL
596210284Sjmallett	    || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
597210284Sjmallett		ng_source_stop(sc);
598210284Sjmallett		return;
599210284Sjmallett	}
600210284Sjmallett
601210284Sjmallett	ifq = &sc->output_ifp->if_snd;
602210284Sjmallett	packets = ifq->ifq_maxlen - ifq->ifq_len;
603210284Sjmallett	ng_source_send(sc, packets, NULL);
604210284Sjmallett	if (sc->packets == 0) {
605210284Sjmallett		int s = splnet();
606210284Sjmallett		ng_source_stop(sc);
607210284Sjmallett		splx(s);
608210284Sjmallett	} else
609210284Sjmallett		sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS);
610210284Sjmallett}
611210284Sjmallett
612210284Sjmallett/*
613210284Sjmallett * Send packets out our output hook
614210284Sjmallett */
615static int
616ng_source_send (sc_p sc, int tosend, int *sent_p)
617{
618	struct ifqueue tmp_queue;
619	struct mbuf *m, *m2;
620	int sent = 0;
621	int error = 0;
622	int s, s2;
623
624	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
625	KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__));
626	KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
627			("%s: inactive node", __FUNCTION__));
628
629	if ((u_int64_t)tosend > sc->packets)
630		tosend = sc->packets;
631
632	/* Copy the required number of packets to a temporary queue */
633	bzero (&tmp_queue, sizeof (tmp_queue));
634	for (sent = 0; error == 0 && sent < tosend; ++sent) {
635		s = splnet();
636		_IF_DEQUEUE(&sc->snd_queue, m);
637		splx(s);
638		if (m == NULL)
639			break;
640
641		/* duplicate the packet */
642		m2 = m_copypacket(m, M_DONTWAIT);
643		if (m2 == NULL) {
644			s = splnet();
645			_IF_PREPEND(&sc->snd_queue, m);
646			splx(s);
647			error = ENOBUFS;
648			break;
649		}
650
651		/* re-enqueue the original packet for us */
652		s = splnet();
653		_IF_ENQUEUE(&sc->snd_queue, m);
654		splx(s);
655
656		/* queue the copy for sending at smplimp */
657		_IF_ENQUEUE(&tmp_queue, m2);
658	}
659
660	sent = 0;
661	s = splimp();
662	for (;;) {
663		_IF_DEQUEUE(&tmp_queue, m2);
664		if (m2 == NULL)
665			break;
666		if (error == 0) {
667			++sent;
668			sc->stats.outFrames++;
669			sc->stats.outOctets += m2->m_pkthdr.len;
670			s2 = splnet();
671			NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
672			splx(s2);
673		} else {
674			NG_FREE_M(m2);
675		}
676	}
677	splx(s);
678
679	sc->packets -= sent;
680	if (sent_p != NULL)
681		*sent_p = sent;
682	return (error);
683}
684