xref: /freebsd/sys/netgraph/ng_source.c (revision 585ff168dc1ff9f73177416ed496dbb51cd2bbed)
1585ff168SJulian Elischer /*
2585ff168SJulian Elischer  * ng_source.c
3585ff168SJulian Elischer  *
4585ff168SJulian Elischer  * Copyright 2002 Sandvine Inc.
5585ff168SJulian Elischer  * All rights reserved.
6585ff168SJulian Elischer  *
7585ff168SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
8585ff168SJulian Elischer  * redistribution of this software, in source or object code forms, with or
9585ff168SJulian Elischer  * without modifications are expressly permitted by Sandvine Inc.;
10585ff168SJulian Elischer provided,
11585ff168SJulian Elischer  * however, that:
12585ff168SJulian Elischer  * 1. Any and all reproductions of the source or object code must include
13585ff168SJulian Elischer the
14585ff168SJulian Elischer  *    copyright notice above and the following disclaimer of warranties;
15585ff168SJulian Elischer and
16585ff168SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
17585ff168SJulian Elischer  *    trademarks, including the mark "SANDVINE" on advertising,
18585ff168SJulian Elischer endorsements,
19585ff168SJulian Elischer  *    or otherwise except as such appears in the above copyright notice or
20585ff168SJulian Elischer in
21585ff168SJulian Elischer  *    the software.
22585ff168SJulian Elischer  *
23585ff168SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
24585ff168SJulian Elischer  * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR
25585ff168SJulian Elischer WARRANTIES,
26585ff168SJulian Elischer  * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT
27585ff168SJulian Elischer LIMITATION,
28585ff168SJulian Elischer  * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
29585ff168SJulian Elischer PARTICULAR
30585ff168SJulian Elischer  * PURPOSE, OR NON-INFRINGEMENT.  SANDVINE DOES NOT WARRANT, GUARANTEE, OR
31585ff168SJulian Elischer  * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
32585ff168SJulian Elischer  * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
33585ff168SJulian Elischer  * OR OTHERWISE.  IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
34585ff168SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
35585ff168SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36585ff168SJulian Elischer EXEMPLARY,
37585ff168SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
38585ff168SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
39585ff168SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40585ff168SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
41585ff168SJulian Elischer  * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
42585ff168SJulian Elischer  * DAMAGE.
43585ff168SJulian Elischer  *
44585ff168SJulian Elischer  * Author: Dave Chapeskie <dchapeskie@sandvine.com>
45585ff168SJulian Elischer  *
46585ff168SJulian Elischer  * $FreeBSD$
47585ff168SJulian Elischer  */
48585ff168SJulian Elischer 
49585ff168SJulian Elischer /*
50585ff168SJulian Elischer  * This node is used for high speed packet geneneration.  It queues
51585ff168SJulian Elischer  * all data recieved on it's 'input' hook and when told to start via
52585ff168SJulian Elischer  * a control message it sends the packets out it's 'output' hook.  In
53585ff168SJulian Elischer  * this way this node can be preloaded with a packet stream which is
54585ff168SJulian Elischer  * continuously sent.
55585ff168SJulian Elischer  *
56585ff168SJulian Elischer  * Currently it just copies the mbufs as required.  It could do various
57585ff168SJulian Elischer  * tricks to try and avoid this.  Probably the best performance would
58585ff168SJulian Elischer  * be achieved by modifying the appropriate drivers to be told to
59585ff168SJulian Elischer  * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
60585ff168SJulian Elischer  * transmit descriptors) under control of this node; perhaps via some
61585ff168SJulian Elischer  * flag in the mbuf or some such.  The node would peak at an appropriate
62585ff168SJulian Elischer  * ifnet flag to see if such support is available for the connected
63585ff168SJulian Elischer  * interface.
64585ff168SJulian Elischer  */
65585ff168SJulian Elischer 
66585ff168SJulian Elischer #include <sys/param.h>
67585ff168SJulian Elischer #include <sys/systm.h>
68585ff168SJulian Elischer #include <sys/errno.h>
69585ff168SJulian Elischer #include <sys/kernel.h>
70585ff168SJulian Elischer #include <sys/malloc.h>
71585ff168SJulian Elischer #include <sys/mbuf.h>
72585ff168SJulian Elischer #include <sys/socket.h>
73585ff168SJulian Elischer #include <net/if.h>
74585ff168SJulian Elischer #include <net/if_var.h>
75585ff168SJulian Elischer #include <netgraph/ng_message.h>
76585ff168SJulian Elischer #include <netgraph/netgraph.h>
77585ff168SJulian Elischer #include <netgraph/ng_parse.h>
78585ff168SJulian Elischer #include <netgraph/ng_ether.h>
79585ff168SJulian Elischer #include <netgraph/ng_source.h>
80585ff168SJulian Elischer 
81585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS		1
82585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN	(4*1024)
83585ff168SJulian Elischer 
84585ff168SJulian Elischer 
85585ff168SJulian Elischer /* Per hook info */
86585ff168SJulian Elischer struct source_hookinfo {
87585ff168SJulian Elischer 	hook_p				hook;
88585ff168SJulian Elischer };
89585ff168SJulian Elischer 
90585ff168SJulian Elischer /* Per node info */
91585ff168SJulian Elischer struct privdata {
92585ff168SJulian Elischer 	node_p				node;
93585ff168SJulian Elischer 	struct source_hookinfo		input;
94585ff168SJulian Elischer 	struct source_hookinfo		output;
95585ff168SJulian Elischer 	struct ng_source_stats		stats;
96585ff168SJulian Elischer 	struct ifqueue			snd_queue;	/* packets to send
97585ff168SJulian Elischer */
98585ff168SJulian Elischer 	struct ifnet			*output_ifp;
99585ff168SJulian Elischer 	struct callout_handle		intr_ch;
100585ff168SJulian Elischer 	u_int64_t			packets;	/* packets to send
101585ff168SJulian Elischer */
102585ff168SJulian Elischer 	u_int32_t			queueOctets;
103585ff168SJulian Elischer };
104585ff168SJulian Elischer typedef struct privdata *sc_p;
105585ff168SJulian Elischer 
106585ff168SJulian Elischer /* Node flags */
107585ff168SJulian Elischer #define NG_SOURCE_ACTIVE	(NGF_TYPE1)
108585ff168SJulian Elischer 
109585ff168SJulian Elischer /* XXX */
110585ff168SJulian Elischer #if 1
111585ff168SJulian Elischer #undef KASSERT
112585ff168SJulian Elischer #define KASSERT(expr,msg) do {			\
113585ff168SJulian Elischer 		if (!(expr)) {			\
114585ff168SJulian Elischer 			printf msg ;		\
115585ff168SJulian Elischer 			panic("Assertion");	\
116585ff168SJulian Elischer 		}				\
117585ff168SJulian Elischer 	} while(0)
118585ff168SJulian Elischer #endif
119585ff168SJulian Elischer 
120585ff168SJulian Elischer /* Netgraph methods */
121585ff168SJulian Elischer static ng_constructor_t	ng_source_constructor;
122585ff168SJulian Elischer static ng_rcvmsg_t	ng_source_rcvmsg;
123585ff168SJulian Elischer static ng_shutdown_t	ng_source_rmnode;
124585ff168SJulian Elischer static ng_newhook_t	ng_source_newhook;
125585ff168SJulian Elischer static ng_rcvdata_t	ng_source_rcvdata;
126585ff168SJulian Elischer static ng_disconnect_t	ng_source_disconnect;
127585ff168SJulian Elischer 
128585ff168SJulian Elischer /* Other functions */
129585ff168SJulian Elischer static timeout_t	ng_source_intr;
130585ff168SJulian Elischer static int		ng_source_get_output_ifp (sc_p);
131585ff168SJulian Elischer static void		ng_source_clr_data (sc_p);
132585ff168SJulian Elischer static void		ng_source_start (sc_p);
133585ff168SJulian Elischer static void		ng_source_stop (sc_p);
134585ff168SJulian Elischer static int		ng_source_send (sc_p, int, int *);
135585ff168SJulian Elischer 
136585ff168SJulian Elischer 
137585ff168SJulian Elischer /* Parse type for timeval */
138585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_timeval_type_fields[] =
139585ff168SJulian Elischer {
140585ff168SJulian Elischer 	{ "tv_sec",		&ng_parse_int32_type	},
141585ff168SJulian Elischer 	{ "tv_usec",		&ng_parse_int32_type	},
142585ff168SJulian Elischer 	{ NULL }
143585ff168SJulian Elischer };
144585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = {
145585ff168SJulian Elischer 	&ng_parse_struct_type,
146585ff168SJulian Elischer 	&ng_source_timeval_type_fields
147585ff168SJulian Elischer };
148585ff168SJulian Elischer 
149585ff168SJulian Elischer /* Parse type for struct ng_source_stats */
150585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[]
151585ff168SJulian Elischer 	= NG_SOURCE_STATS_TYPE_INFO;
152585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = {
153585ff168SJulian Elischer 	&ng_parse_struct_type,
154585ff168SJulian Elischer 	&ng_source_stats_type_fields
155585ff168SJulian Elischer };
156585ff168SJulian Elischer 
157585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */
158585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = {
159585ff168SJulian Elischer 	{
160585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
161585ff168SJulian Elischer 	  NGM_SOURCE_GET_STATS,
162585ff168SJulian Elischer 	  "getstats",
163585ff168SJulian Elischer 	  NULL,
164585ff168SJulian Elischer 	  &ng_source_stats_type
165585ff168SJulian Elischer 	},
166585ff168SJulian Elischer 	{
167585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
168585ff168SJulian Elischer 	  NGM_SOURCE_CLR_STATS,
169585ff168SJulian Elischer 	  "clrstats",
170585ff168SJulian Elischer 	  NULL,
171585ff168SJulian Elischer 	  NULL
172585ff168SJulian Elischer 	},
173585ff168SJulian Elischer 	{
174585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
175585ff168SJulian Elischer 	  NGM_SOURCE_GETCLR_STATS,
176585ff168SJulian Elischer 	  "getclrstats",
177585ff168SJulian Elischer 	  NULL,
178585ff168SJulian Elischer 	  &ng_source_stats_type
179585ff168SJulian Elischer 	},
180585ff168SJulian Elischer 	{
181585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
182585ff168SJulian Elischer 	  NGM_SOURCE_START,
183585ff168SJulian Elischer 	  "start",
184585ff168SJulian Elischer 	  &ng_parse_uint64_type,
185585ff168SJulian Elischer 	  NULL
186585ff168SJulian Elischer 	},
187585ff168SJulian Elischer 	{
188585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
189585ff168SJulian Elischer 	  NGM_SOURCE_STOP,
190585ff168SJulian Elischer 	  "stop",
191585ff168SJulian Elischer 	  NULL,
192585ff168SJulian Elischer 	  NULL
193585ff168SJulian Elischer 	},
194585ff168SJulian Elischer 	{
195585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
196585ff168SJulian Elischer 	  NGM_SOURCE_CLR_DATA,
197585ff168SJulian Elischer 	  "clrdata",
198585ff168SJulian Elischer 	  NULL,
199585ff168SJulian Elischer 	  NULL
200585ff168SJulian Elischer 	},
201585ff168SJulian Elischer 	{ 0 }
202585ff168SJulian Elischer };
203585ff168SJulian Elischer 
204585ff168SJulian Elischer /* Netgraph type descriptor */
205585ff168SJulian Elischer static struct ng_type ng_source_typestruct = {
206585ff168SJulian Elischer 	NG_VERSION,
207585ff168SJulian Elischer 	NG_SOURCE_NODE_TYPE,
208585ff168SJulian Elischer 	NULL,					/* module event handler */
209585ff168SJulian Elischer 	ng_source_constructor,
210585ff168SJulian Elischer 	ng_source_rcvmsg,
211585ff168SJulian Elischer 	ng_source_rmnode,
212585ff168SJulian Elischer 	ng_source_newhook,
213585ff168SJulian Elischer 	NULL,					/* findhook */
214585ff168SJulian Elischer 	NULL,
215585ff168SJulian Elischer 	ng_source_rcvdata,			/* rcvdata */
216585ff168SJulian Elischer 	ng_source_rcvdata,			/* rcvdataq */
217585ff168SJulian Elischer 	ng_source_disconnect,
218585ff168SJulian Elischer 	ng_source_cmds
219585ff168SJulian Elischer };
220585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct);
221585ff168SJulian Elischer 
222585ff168SJulian Elischer /*
223585ff168SJulian Elischer  * Node constructor
224585ff168SJulian Elischer  */
225585ff168SJulian Elischer static int
226585ff168SJulian Elischer ng_source_constructor(node_p *nodep)
227585ff168SJulian Elischer {
228585ff168SJulian Elischer 	sc_p sc;
229585ff168SJulian Elischer 	int error = 0;
230585ff168SJulian Elischer 
231585ff168SJulian Elischer 	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT);
232585ff168SJulian Elischer 	if (sc == NULL)
233585ff168SJulian Elischer 		return (ENOMEM);
234585ff168SJulian Elischer 	bzero(sc, sizeof(*sc));
235585ff168SJulian Elischer 
236585ff168SJulian Elischer 	if ((error = ng_make_node_common(&ng_source_typestruct, nodep))) {
237585ff168SJulian Elischer 		FREE(sc, M_NETGRAPH);
238585ff168SJulian Elischer 		return (error);
239585ff168SJulian Elischer 	}
240585ff168SJulian Elischer 	(*nodep)->private = sc;
241585ff168SJulian Elischer 	sc->node = *nodep;
242585ff168SJulian Elischer 	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
243585ff168SJulian Elischer 	callout_handle_init(&sc->intr_ch);
244585ff168SJulian Elischer 	return (0);
245585ff168SJulian Elischer }
246585ff168SJulian Elischer 
247585ff168SJulian Elischer /*
248585ff168SJulian Elischer  * Add a hook
249585ff168SJulian Elischer  */
250585ff168SJulian Elischer static int
251585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name)
252585ff168SJulian Elischer {
253585ff168SJulian Elischer 	const sc_p sc = node->private;
254585ff168SJulian Elischer 
255585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
256585ff168SJulian Elischer 	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
257585ff168SJulian Elischer 		sc->input.hook = hook;
258585ff168SJulian Elischer 		hook->private = &sc->input;
259585ff168SJulian Elischer 	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
260585ff168SJulian Elischer 		sc->output.hook = hook;
261585ff168SJulian Elischer 		hook->private = &sc->output;
262585ff168SJulian Elischer 		sc->output_ifp = 0;
263585ff168SJulian Elischer 		bzero(&sc->stats, sizeof(sc->stats));
264585ff168SJulian Elischer 	} else
265585ff168SJulian Elischer 		return (EINVAL);
266585ff168SJulian Elischer 	return (0);
267585ff168SJulian Elischer }
268585ff168SJulian Elischer 
269585ff168SJulian Elischer /*
270585ff168SJulian Elischer  * Receive a control message
271585ff168SJulian Elischer  */
272585ff168SJulian Elischer static int
273585ff168SJulian Elischer ng_source_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
274585ff168SJulian Elischer 	   struct ng_mesg **rptr)
275585ff168SJulian Elischer {
276585ff168SJulian Elischer 	const sc_p sc = node->private;
277585ff168SJulian Elischer 	struct ng_mesg *resp = NULL;
278585ff168SJulian Elischer 	int error = 0;
279585ff168SJulian Elischer 
280585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
281585ff168SJulian Elischer 	switch (msg->header.typecookie) {
282585ff168SJulian Elischer 	case NGM_SOURCE_COOKIE:
283585ff168SJulian Elischer 		switch (msg->header.cmd) {
284585ff168SJulian Elischer 		case NGM_SOURCE_GET_STATS:
285585ff168SJulian Elischer 		case NGM_SOURCE_CLR_STATS:
286585ff168SJulian Elischer 		case NGM_SOURCE_GETCLR_STATS:
287585ff168SJulian Elischer                     {
288585ff168SJulian Elischer 			struct ng_source_stats *stats;
289585ff168SJulian Elischer 
290585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
291585ff168SJulian Elischer                                 NG_MKRESPONSE(resp, msg,
292585ff168SJulian Elischer                                     sizeof(*stats), M_NOWAIT);
293585ff168SJulian Elischer 				if (resp == NULL) {
294585ff168SJulian Elischer 					error = ENOMEM;
295585ff168SJulian Elischer 					goto done;
296585ff168SJulian Elischer 				}
297585ff168SJulian Elischer 				sc->stats.queueOctets = sc->queueOctets;
298585ff168SJulian Elischer 				sc->stats.queueFrames =
299585ff168SJulian Elischer sc->snd_queue.ifq_len;
300585ff168SJulian Elischer 				if ((sc->node->flags & NG_SOURCE_ACTIVE)
301585ff168SJulian Elischer 				    && !timevalisset(&sc->stats.endTime)) {
302585ff168SJulian Elischer 
303585ff168SJulian Elischer getmicrotime(&sc->stats.elapsedTime);
304585ff168SJulian Elischer 					timevalsub(&sc->stats.elapsedTime,
305585ff168SJulian Elischer 
306585ff168SJulian Elischer &sc->stats.startTime);
307585ff168SJulian Elischer 				}
308585ff168SJulian Elischer 				stats = (struct ng_source_stats
309585ff168SJulian Elischer *)resp->data;
310585ff168SJulian Elischer 				bcopy(&sc->stats, stats, sizeof(* stats));
311585ff168SJulian Elischer                         }
312585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_GET_STATS)
313585ff168SJulian Elischer 				bzero(&sc->stats, sizeof(sc->stats));
314585ff168SJulian Elischer 		    }
315585ff168SJulian Elischer 		    break;
316585ff168SJulian Elischer 		case NGM_SOURCE_START:
317585ff168SJulian Elischer 		    {
318585ff168SJulian Elischer 			u_int64_t packets = *(u_int64_t *)msg->data;
319585ff168SJulian Elischer 			if (sc->output.hook == NULL) {
320585ff168SJulian Elischer 				printf("%s: start on node with no output
321585ff168SJulian Elischer hook\n", __FUNCTION__);
322585ff168SJulian Elischer 				error = EINVAL;
323585ff168SJulian Elischer 				break;
324585ff168SJulian Elischer 			}
325585ff168SJulian Elischer 			/* TODO validation of packets */
326585ff168SJulian Elischer 			sc->packets = packets;
327585ff168SJulian Elischer 			ng_source_start(sc);
328585ff168SJulian Elischer 		    }
329585ff168SJulian Elischer 		    break;
330585ff168SJulian Elischer 		case NGM_SOURCE_STOP:
331585ff168SJulian Elischer 			ng_source_stop(sc);
332585ff168SJulian Elischer 			break;
333585ff168SJulian Elischer 		case NGM_SOURCE_CLR_DATA:
334585ff168SJulian Elischer 			ng_source_clr_data(sc);
335585ff168SJulian Elischer 			break;
336585ff168SJulian Elischer 		default:
337585ff168SJulian Elischer 			error = EINVAL;
338585ff168SJulian Elischer 			break;
339585ff168SJulian Elischer 		}
340585ff168SJulian Elischer 		break;
341585ff168SJulian Elischer 	default:
342585ff168SJulian Elischer 		error = EINVAL;
343585ff168SJulian Elischer 		break;
344585ff168SJulian Elischer 	}
345585ff168SJulian Elischer 	if (rptr)
346585ff168SJulian Elischer 		*rptr = resp;
347585ff168SJulian Elischer 	else if (resp)
348585ff168SJulian Elischer 		FREE(resp, M_NETGRAPH);
349585ff168SJulian Elischer 
350585ff168SJulian Elischer done:
351585ff168SJulian Elischer 	FREE(msg, M_NETGRAPH);
352585ff168SJulian Elischer 	return (error);
353585ff168SJulian Elischer }
354585ff168SJulian Elischer 
355585ff168SJulian Elischer /*
356585ff168SJulian Elischer  * Receive data on a hook
357585ff168SJulian Elischer  *
358585ff168SJulian Elischer  * If data comes in the input hook, enqueue it on the send queue.
359585ff168SJulian Elischer  * If data comes in the output hook, discard it.
360585ff168SJulian Elischer  */
361585ff168SJulian Elischer static int
362585ff168SJulian Elischer ng_source_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
363585ff168SJulian Elischer {
364585ff168SJulian Elischer 	const sc_p sc = hook->node->private;
365585ff168SJulian Elischer 	struct source_hookinfo *const hinfo = (struct source_hookinfo *)
366585ff168SJulian Elischer hook->private;
367585ff168SJulian Elischer 	int error = 0;
368585ff168SJulian Elischer 
369585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
370585ff168SJulian Elischer 	KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__));
371585ff168SJulian Elischer 
372585ff168SJulian Elischer 	/* Which hook? */
373585ff168SJulian Elischer 	if (hinfo == &sc->output) {
374585ff168SJulian Elischer 		/* discard */
375585ff168SJulian Elischer 		NG_FREE_DATA(m, meta);
376585ff168SJulian Elischer 		return (error);
377585ff168SJulian Elischer 	}
378585ff168SJulian Elischer 	KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__));
379585ff168SJulian Elischer 
380585ff168SJulian Elischer 	if ((m->m_flags & M_PKTHDR) == 0) {
381585ff168SJulian Elischer 		printf("%s: mbuf without PKTHDR\n", __FUNCTION__);
382585ff168SJulian Elischer 		NG_FREE_DATA(m, meta);
383585ff168SJulian Elischer 		return (EINVAL);
384585ff168SJulian Elischer 	}
385585ff168SJulian Elischer 
386585ff168SJulian Elischer 	/* XXX we discard the meta data for now */
387585ff168SJulian Elischer 	NG_FREE_META(meta);
388585ff168SJulian Elischer 
389585ff168SJulian Elischer 	/* enque packet */
390585ff168SJulian Elischer 	/* XXX should we check IF_QFULL() ? */
391585ff168SJulian Elischer 	IF_ENQUEUE(&sc->snd_queue, m);
392585ff168SJulian Elischer 	sc->queueOctets += m->m_pkthdr.len;
393585ff168SJulian Elischer 
394585ff168SJulian Elischer 	return (0);
395585ff168SJulian Elischer }
396585ff168SJulian Elischer 
397585ff168SJulian Elischer /*
398585ff168SJulian Elischer  * Shutdown processing
399585ff168SJulian Elischer  */
400585ff168SJulian Elischer static int
401585ff168SJulian Elischer ng_source_rmnode(node_p node)
402585ff168SJulian Elischer {
403585ff168SJulian Elischer 	const sc_p sc = node->private;
404585ff168SJulian Elischer 
405585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
406585ff168SJulian Elischer 	node->flags |= NG_INVALID;
407585ff168SJulian Elischer 	ng_source_stop(sc);
408585ff168SJulian Elischer 	ng_cutlinks(node);
409585ff168SJulian Elischer 	ng_source_clr_data(sc);
410585ff168SJulian Elischer 	ng_unname(node);
411585ff168SJulian Elischer 	node->private = NULL;
412585ff168SJulian Elischer 	ng_unref(sc->node);
413585ff168SJulian Elischer 	FREE(sc, M_NETGRAPH);
414585ff168SJulian Elischer 	return (0);
415585ff168SJulian Elischer }
416585ff168SJulian Elischer 
417585ff168SJulian Elischer /*
418585ff168SJulian Elischer  * Hook disconnection
419585ff168SJulian Elischer  */
420585ff168SJulian Elischer static int
421585ff168SJulian Elischer ng_source_disconnect(hook_p hook)
422585ff168SJulian Elischer {
423585ff168SJulian Elischer 	struct source_hookinfo *const hinfo = (struct source_hookinfo *)
424585ff168SJulian Elischer hook->private;
425585ff168SJulian Elischer 	sc_p sc = (sc_p) hinfo->hook->node->private;
426585ff168SJulian Elischer 
427585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
428585ff168SJulian Elischer 	hinfo->hook = NULL;
429585ff168SJulian Elischer 	if (hook->node->numhooks == 0 || hinfo == &sc->output)
430585ff168SJulian Elischer 		ng_rmnode(hook->node);
431585ff168SJulian Elischer 	return (0);
432585ff168SJulian Elischer }
433585ff168SJulian Elischer 
434585ff168SJulian Elischer /*
435585ff168SJulian Elischer  * Set sc->output_ifp to point to the the struct ifnet of the interface
436585ff168SJulian Elischer  * reached via our output hook.
437585ff168SJulian Elischer  */
438585ff168SJulian Elischer static int
439585ff168SJulian Elischer ng_source_get_output_ifp(sc_p sc)
440585ff168SJulian Elischer {
441585ff168SJulian Elischer 	struct ng_mesg *msg, *rsp;
442585ff168SJulian Elischer 	struct ifnet *ifp;
443585ff168SJulian Elischer 	u_int32_t if_index;
444585ff168SJulian Elischer 	int error = 0;
445585ff168SJulian Elischer 	int s;
446585ff168SJulian Elischer 
447585ff168SJulian Elischer 	sc->output_ifp = NULL;
448585ff168SJulian Elischer 
449585ff168SJulian Elischer 	/* Ask the attached node for the connected interface's index */
450585ff168SJulian Elischer 	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0,
451585ff168SJulian Elischer M_NOWAIT);
452585ff168SJulian Elischer 	if (msg == NULL)
453585ff168SJulian Elischer 		return (ENOBUFS);
454585ff168SJulian Elischer 
455585ff168SJulian Elischer 	error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, &rsp);
456585ff168SJulian Elischer 	if (error != 0)
457585ff168SJulian Elischer 		return (error);
458585ff168SJulian Elischer 
459585ff168SJulian Elischer 	if (rsp == NULL)
460585ff168SJulian Elischer 		return (EINVAL);
461585ff168SJulian Elischer 
462585ff168SJulian Elischer 	if (rsp->header.arglen < sizeof(u_int32_t))
463585ff168SJulian Elischer 		return (EINVAL);
464585ff168SJulian Elischer 
465585ff168SJulian Elischer 	if_index = *(u_int32_t *)rsp->data;
466585ff168SJulian Elischer 	/* Could use ifindex2ifnet[if_index] except that we have no
467585ff168SJulian Elischer 	 * way of verifying if_index is valid since if_indexlim is
468585ff168SJulian Elischer 	 * local to if_attach()
469585ff168SJulian Elischer 	 */
470585ff168SJulian Elischer 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
471585ff168SJulian Elischer 		if (ifp->if_index == if_index)
472585ff168SJulian Elischer 			break;
473585ff168SJulian Elischer 	}
474585ff168SJulian Elischer 
475585ff168SJulian Elischer 	if (ifp == NULL) {
476585ff168SJulian Elischer 		printf("%s: can't find interface %d\n", __FUNCTION__,
477585ff168SJulian Elischer if_index);
478585ff168SJulian Elischer 		return (EINVAL);
479585ff168SJulian Elischer 	}
480585ff168SJulian Elischer 	sc->output_ifp = ifp;
481585ff168SJulian Elischer 
482585ff168SJulian Elischer #if 1
483585ff168SJulian Elischer 	/* XXX mucking with a drivers ifqueue size is ugly but we need it
484585ff168SJulian Elischer 	 * to queue a lot of packets to get close to line rate on a gigabit
485585ff168SJulian Elischer 	 * interface with small packets.
486585ff168SJulian Elischer 	 * XXX we should restore the original value at stop or disconnect
487585ff168SJulian Elischer 	 */
488585ff168SJulian Elischer 	s = splimp();		/* XXX is this required? */
489585ff168SJulian Elischer 	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN)
490585ff168SJulian Elischer 	{
491585ff168SJulian Elischer 		printf("ng_source: changing ifq_maxlen from %d to %d\n",
492585ff168SJulian Elischer 			ifp->if_snd.ifq_maxlen,
493585ff168SJulian Elischer NG_SOURCE_DRIVER_IFQ_MAXLEN);
494585ff168SJulian Elischer 		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
495585ff168SJulian Elischer 	}
496585ff168SJulian Elischer 	splx(s);
497585ff168SJulian Elischer #endif
498585ff168SJulian Elischer 	return (0);
499585ff168SJulian Elischer }
500585ff168SJulian Elischer 
501585ff168SJulian Elischer /*
502585ff168SJulian Elischer  * Set the attached ethernet node's ethernet source address override flag.
503585ff168SJulian Elischer  */
504585ff168SJulian Elischer static int
505585ff168SJulian Elischer ng_source_set_autosrc(sc_p sc, u_int32_t flag)
506585ff168SJulian Elischer {
507585ff168SJulian Elischer 	struct ng_mesg *msg;
508585ff168SJulian Elischer 	int error = 0;
509585ff168SJulian Elischer 
510585ff168SJulian Elischer 	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
511585ff168SJulian Elischer 			sizeof (u_int32_t), M_NOWAIT);
512585ff168SJulian Elischer 	if (msg == NULL)
513585ff168SJulian Elischer 		return(ENOBUFS);
514585ff168SJulian Elischer 
515585ff168SJulian Elischer 	*(u_int32_t *)msg->data = flag;
516585ff168SJulian Elischer 	error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, NULL);
517585ff168SJulian Elischer 	return (error);
518585ff168SJulian Elischer }
519585ff168SJulian Elischer 
520585ff168SJulian Elischer /*
521585ff168SJulian Elischer  * Clear out the data we've queued
522585ff168SJulian Elischer  */
523585ff168SJulian Elischer static void
524585ff168SJulian Elischer ng_source_clr_data (sc_p sc)
525585ff168SJulian Elischer {
526585ff168SJulian Elischer 	struct mbuf *m;
527585ff168SJulian Elischer 
528585ff168SJulian Elischer 	SPLASSERT(net, __FUNCTION__);
529585ff168SJulian Elischer 	for (;;) {
530585ff168SJulian Elischer 		IF_DEQUEUE(&sc->snd_queue, m);
531585ff168SJulian Elischer 		if (m == NULL)
532585ff168SJulian Elischer 			break;
533585ff168SJulian Elischer 		NG_FREE_M(m);
534585ff168SJulian Elischer 	}
535585ff168SJulian Elischer 	sc->queueOctets = 0;
536585ff168SJulian Elischer }
537585ff168SJulian Elischer 
538585ff168SJulian Elischer /*
539585ff168SJulian Elischer  * Start sending queued data out the output hook
540585ff168SJulian Elischer  */
541585ff168SJulian Elischer static void
542585ff168SJulian Elischer ng_source_start (sc_p sc)
543585ff168SJulian Elischer {
544585ff168SJulian Elischer 	SPLASSERT(net, __FUNCTION__);
545585ff168SJulian Elischer 	KASSERT(sc->output.hook != NULL,
546585ff168SJulian Elischer 			("%s: output hook unconnected", __FUNCTION__));
547585ff168SJulian Elischer 	if ((sc->node->flags & NG_SOURCE_ACTIVE) == 0) {
548585ff168SJulian Elischer 		if (sc->output_ifp == NULL && ng_source_get_output_ifp(sc)
549585ff168SJulian Elischer != 0)
550585ff168SJulian Elischer 			return;
551585ff168SJulian Elischer 		ng_source_set_autosrc(sc, 0);
552585ff168SJulian Elischer 		sc->node->flags |= NG_SOURCE_ACTIVE;
553585ff168SJulian Elischer 		timevalclear(&sc->stats.elapsedTime);
554585ff168SJulian Elischer 		timevalclear(&sc->stats.endTime);
555585ff168SJulian Elischer 		getmicrotime(&sc->stats.startTime);
556585ff168SJulian Elischer 		sc->intr_ch = timeout(ng_source_intr, sc, 0);
557585ff168SJulian Elischer 	}
558585ff168SJulian Elischer }
559585ff168SJulian Elischer 
560585ff168SJulian Elischer /*
561585ff168SJulian Elischer  * Stop sending queued data out the output hook
562585ff168SJulian Elischer  */
563585ff168SJulian Elischer static void
564585ff168SJulian Elischer ng_source_stop (sc_p sc)
565585ff168SJulian Elischer {
566585ff168SJulian Elischer 	SPLASSERT(net, __FUNCTION__);
567585ff168SJulian Elischer 	if (sc->node->flags & NG_SOURCE_ACTIVE) {
568585ff168SJulian Elischer 		untimeout(ng_source_intr, sc, sc->intr_ch);
569585ff168SJulian Elischer 		sc->node->flags &= ~NG_SOURCE_ACTIVE;
570585ff168SJulian Elischer 		getmicrotime(&sc->stats.endTime);
571585ff168SJulian Elischer 		sc->stats.elapsedTime = sc->stats.endTime;
572585ff168SJulian Elischer 		timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
573585ff168SJulian Elischer 		/* XXX should set this to the initial value instead */
574585ff168SJulian Elischer 		ng_source_set_autosrc(sc, 1);
575585ff168SJulian Elischer 	}
576585ff168SJulian Elischer }
577585ff168SJulian Elischer 
578585ff168SJulian Elischer /*
579585ff168SJulian Elischer  * While active called every NG_SOURCE_INTR_TICKS ticks.
580585ff168SJulian Elischer  * Sends as many packets as the interface connected to our
581585ff168SJulian Elischer  * output hook is able to enqueue.
582585ff168SJulian Elischer  */
583585ff168SJulian Elischer static void
584585ff168SJulian Elischer ng_source_intr (void *arg)
585585ff168SJulian Elischer {
586585ff168SJulian Elischer 	const sc_p sc = (sc_p) arg;
587585ff168SJulian Elischer 	struct ifqueue *ifq;
588585ff168SJulian Elischer 	int packets;
589585ff168SJulian Elischer 
590585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
591585ff168SJulian Elischer 
592585ff168SJulian Elischer 	callout_handle_init(&sc->intr_ch);
593585ff168SJulian Elischer 	if (sc->packets == 0 || sc->output.hook == NULL
594585ff168SJulian Elischer 	    || (sc->node->flags & NG_SOURCE_ACTIVE) == 0) {
595585ff168SJulian Elischer 		ng_source_stop(sc);
596585ff168SJulian Elischer 		return;
597585ff168SJulian Elischer 	}
598585ff168SJulian Elischer 
599585ff168SJulian Elischer 	ifq = &sc->output_ifp->if_snd;
600585ff168SJulian Elischer 	packets = ifq->ifq_maxlen - ifq->ifq_len;
601585ff168SJulian Elischer 	ng_source_send(sc, packets, NULL);
602585ff168SJulian Elischer 	if (sc->packets == 0) {
603585ff168SJulian Elischer 		int s = splnet();
604585ff168SJulian Elischer 		ng_source_stop(sc);
605585ff168SJulian Elischer 		splx(s);
606585ff168SJulian Elischer 	} else
607585ff168SJulian Elischer 		sc->intr_ch = timeout(ng_source_intr, sc,
608585ff168SJulian Elischer NG_SOURCE_INTR_TICKS);
609585ff168SJulian Elischer }
610585ff168SJulian Elischer 
611585ff168SJulian Elischer /*
612585ff168SJulian Elischer  * Send packets out our output hook
613585ff168SJulian Elischer  */
614585ff168SJulian Elischer static int
615585ff168SJulian Elischer ng_source_send (sc_p sc, int tosend, int *sent_p)
616585ff168SJulian Elischer {
617585ff168SJulian Elischer 	struct ifqueue tmp_queue;
618585ff168SJulian Elischer 	struct mbuf *m, *m2;
619585ff168SJulian Elischer 	int sent = 0;
620585ff168SJulian Elischer 	int error = 0;
621585ff168SJulian Elischer 	int s, s2;
622585ff168SJulian Elischer 
623585ff168SJulian Elischer 	KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
624585ff168SJulian Elischer 	KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__));
625585ff168SJulian Elischer 	KASSERT(sc->node->flags & NG_SOURCE_ACTIVE,
626585ff168SJulian Elischer 			("%s: inactive node", __FUNCTION__));
627585ff168SJulian Elischer 
628585ff168SJulian Elischer 	if ((u_int64_t)tosend > sc->packets)
629585ff168SJulian Elischer 		tosend = sc->packets;
630585ff168SJulian Elischer 
631585ff168SJulian Elischer 	/* Copy the required number of packets to a temporary queue */
632585ff168SJulian Elischer 	bzero (&tmp_queue, sizeof (tmp_queue));
633585ff168SJulian Elischer 	for (sent = 0; error == 0 && sent < tosend; ++sent) {
634585ff168SJulian Elischer 		s = splnet();
635585ff168SJulian Elischer 		IF_DEQUEUE(&sc->snd_queue, m);
636585ff168SJulian Elischer 		splx(s);
637585ff168SJulian Elischer 		if (m == NULL)
638585ff168SJulian Elischer 			break;
639585ff168SJulian Elischer 
640585ff168SJulian Elischer 		/* duplicate the packet */
641585ff168SJulian Elischer 		m2 = m_copypacket(m, M_NOWAIT);
642585ff168SJulian Elischer 		if (m2 == NULL) {
643585ff168SJulian Elischer 			s = splnet();
644585ff168SJulian Elischer 			IF_PREPEND(&sc->snd_queue, m);
645585ff168SJulian Elischer 			splx(s);
646585ff168SJulian Elischer 			error = ENOBUFS;
647585ff168SJulian Elischer 			break;
648585ff168SJulian Elischer 		}
649585ff168SJulian Elischer 
650585ff168SJulian Elischer 		/* re-enqueue the original packet for us */
651585ff168SJulian Elischer 		s = splnet();
652585ff168SJulian Elischer 		IF_ENQUEUE(&sc->snd_queue, m);
653585ff168SJulian Elischer 		splx(s);
654585ff168SJulian Elischer 
655585ff168SJulian Elischer 		/* queue the copy for sending at smplimp */
656585ff168SJulian Elischer 		IF_ENQUEUE(&tmp_queue, m2);
657585ff168SJulian Elischer 	}
658585ff168SJulian Elischer 
659585ff168SJulian Elischer 	sent = 0;
660585ff168SJulian Elischer 	s = splimp();
661585ff168SJulian Elischer 	for (;;) {
662585ff168SJulian Elischer 		IF_DEQUEUE(&tmp_queue, m2);
663585ff168SJulian Elischer 		if (m2 == NULL)
664585ff168SJulian Elischer 			break;
665585ff168SJulian Elischer 		if (error == 0) {
666585ff168SJulian Elischer 			++sent;
667585ff168SJulian Elischer 			sc->stats.outFrames++;
668585ff168SJulian Elischer 			sc->stats.outOctets += m2->m_pkthdr.len;
669585ff168SJulian Elischer 			s2 = splnet();
670585ff168SJulian Elischer 			NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
671585ff168SJulian Elischer 			splx(s2);
672585ff168SJulian Elischer 		} else {
673585ff168SJulian Elischer 			NG_FREE_M(m2);
674585ff168SJulian Elischer 		}
675585ff168SJulian Elischer 	}
676585ff168SJulian Elischer 	splx(s);
677585ff168SJulian Elischer 
678585ff168SJulian Elischer 	sc->packets -= sent;
679585ff168SJulian Elischer 	if (sent_p != NULL)
680585ff168SJulian Elischer 		*sent_p = sent;
681585ff168SJulian Elischer 	return (error);
682585ff168SJulian Elischer }
683