xref: /freebsd/sys/netgraph/ng_source.c (revision 72235857719ffee76b1a620edf1c2a68570af007)
1585ff168SJulian Elischer /*
2585ff168SJulian Elischer  * ng_source.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6d8f5d037SGleb Smirnoff  * Copyright (c) 2005 Gleb Smirnoff <glebius@FreeBSD.org>
7585ff168SJulian Elischer  * Copyright 2002 Sandvine Inc.
8585ff168SJulian Elischer  * All rights reserved.
9585ff168SJulian Elischer  *
10585ff168SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
11585ff168SJulian Elischer  * redistribution of this software, in source or object code forms, with or
124b52f283SJulian Elischer  * without modifications are expressly permitted by Sandvine Inc.; provided,
13585ff168SJulian Elischer  * however, that:
144b52f283SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
154b52f283SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
16585ff168SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
174b52f283SJulian Elischer  *    trademarks, including the mark "SANDVINE" on advertising, endorsements,
184b52f283SJulian Elischer  *    or otherwise except as such appears in the above copyright notice or in
19585ff168SJulian Elischer  *    the software.
20585ff168SJulian Elischer  *
21585ff168SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
224b52f283SJulian Elischer  * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES,
234b52f283SJulian Elischer  * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION,
244b52f283SJulian Elischer  * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25585ff168SJulian Elischer  * PURPOSE, OR NON-INFRINGEMENT.  SANDVINE DOES NOT WARRANT, GUARANTEE, OR
26585ff168SJulian Elischer  * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
27585ff168SJulian Elischer  * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
28585ff168SJulian Elischer  * OR OTHERWISE.  IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
29585ff168SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
304b52f283SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31585ff168SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32585ff168SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33585ff168SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34585ff168SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35585ff168SJulian Elischer  * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
36585ff168SJulian Elischer  * DAMAGE.
37585ff168SJulian Elischer  *
38585ff168SJulian Elischer  * Author: Dave Chapeskie <dchapeskie@sandvine.com>
39585ff168SJulian Elischer  */
40585ff168SJulian Elischer 
4176bd5857SHartmut Brandt #include <sys/cdefs.h>
4276bd5857SHartmut Brandt __FBSDID("$FreeBSD$");
4376bd5857SHartmut Brandt 
44585ff168SJulian Elischer /*
45585ff168SJulian Elischer  * This node is used for high speed packet geneneration.  It queues
462b0ffc02SBosko Milekic  * all data recieved on its 'input' hook and when told to start via
472b0ffc02SBosko Milekic  * a control message it sends the packets out its 'output' hook.  In
482b0ffc02SBosko Milekic  * this way this node can be preloaded with a packet stream which it
492b0ffc02SBosko Milekic  * can then send continuously as fast as possible.
50585ff168SJulian Elischer  *
51585ff168SJulian Elischer  * Currently it just copies the mbufs as required.  It could do various
52585ff168SJulian Elischer  * tricks to try and avoid this.  Probably the best performance would
53585ff168SJulian Elischer  * be achieved by modifying the appropriate drivers to be told to
54585ff168SJulian Elischer  * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
55585ff168SJulian Elischer  * transmit descriptors) under control of this node; perhaps via some
562b0ffc02SBosko Milekic  * flag in the mbuf or some such.  The node could peek at an appropriate
57585ff168SJulian Elischer  * ifnet flag to see if such support is available for the connected
58585ff168SJulian Elischer  * interface.
59585ff168SJulian Elischer  */
60585ff168SJulian Elischer 
61585ff168SJulian Elischer #include <sys/param.h>
62585ff168SJulian Elischer #include <sys/systm.h>
63585ff168SJulian Elischer #include <sys/errno.h>
64585ff168SJulian Elischer #include <sys/kernel.h>
65585ff168SJulian Elischer #include <sys/malloc.h>
66585ff168SJulian Elischer #include <sys/mbuf.h>
67585ff168SJulian Elischer #include <sys/socket.h>
68d8f5d037SGleb Smirnoff #include <sys/syslog.h>
69585ff168SJulian Elischer #include <net/if.h>
70585ff168SJulian Elischer #include <net/if_var.h>
71585ff168SJulian Elischer #include <netgraph/ng_message.h>
72585ff168SJulian Elischer #include <netgraph/netgraph.h>
73585ff168SJulian Elischer #include <netgraph/ng_parse.h>
74585ff168SJulian Elischer #include <netgraph/ng_ether.h>
75585ff168SJulian Elischer #include <netgraph/ng_source.h>
76585ff168SJulian Elischer 
77585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS		1
78585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN	(4*1024)
79585ff168SJulian Elischer 
80585ff168SJulian Elischer /* Per node info */
81585ff168SJulian Elischer struct privdata {
82585ff168SJulian Elischer 	node_p				node;
83d8f5d037SGleb Smirnoff 	hook_p				input;
84d8f5d037SGleb Smirnoff 	hook_p				output;
85585ff168SJulian Elischer 	struct ng_source_stats		stats;
864b52f283SJulian Elischer 	struct ifqueue			snd_queue;	/* packets to send */
87585ff168SJulian Elischer 	struct ifnet			*output_ifp;
8830bef41bSGleb Smirnoff 	struct callout			intr_ch;
89d8f5d037SGleb Smirnoff 	uint64_t			packets;	/* packets to send */
90d8f5d037SGleb Smirnoff 	uint32_t			queueOctets;
91585ff168SJulian Elischer };
92585ff168SJulian Elischer typedef struct privdata *sc_p;
93585ff168SJulian Elischer 
94585ff168SJulian Elischer /* Node flags */
95585ff168SJulian Elischer #define NG_SOURCE_ACTIVE	(NGF_TYPE1)
96585ff168SJulian Elischer 
97585ff168SJulian Elischer /* Netgraph methods */
98585ff168SJulian Elischer static ng_constructor_t	ng_source_constructor;
99585ff168SJulian Elischer static ng_rcvmsg_t	ng_source_rcvmsg;
100585ff168SJulian Elischer static ng_shutdown_t	ng_source_rmnode;
101585ff168SJulian Elischer static ng_newhook_t	ng_source_newhook;
102d8f5d037SGleb Smirnoff static ng_connect_t	ng_source_connect;
103585ff168SJulian Elischer static ng_rcvdata_t	ng_source_rcvdata;
104585ff168SJulian Elischer static ng_disconnect_t	ng_source_disconnect;
105585ff168SJulian Elischer 
106585ff168SJulian Elischer /* Other functions */
107a1adb510SHartmut Brandt static void		ng_source_intr(node_p, hook_p, void *, int);
108585ff168SJulian Elischer static void		ng_source_clr_data (sc_p);
109d8f5d037SGleb Smirnoff static int		ng_source_start (sc_p, uint64_t);
110585ff168SJulian Elischer static void		ng_source_stop (sc_p);
111585ff168SJulian Elischer static int		ng_source_send (sc_p, int, int *);
112d8f5d037SGleb Smirnoff static int		ng_source_store_output_ifp(sc_p, char *);
113585ff168SJulian Elischer 
114585ff168SJulian Elischer /* Parse type for timeval */
11576bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = {
116585ff168SJulian Elischer 	{ "tv_sec",		&ng_parse_int32_type	},
117585ff168SJulian Elischer 	{ "tv_usec",		&ng_parse_int32_type	},
118585ff168SJulian Elischer 	{ NULL }
119585ff168SJulian Elischer };
120585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = {
121585ff168SJulian Elischer 	&ng_parse_struct_type,
122585ff168SJulian Elischer 	&ng_source_timeval_type_fields
123585ff168SJulian Elischer };
124585ff168SJulian Elischer 
125585ff168SJulian Elischer /* Parse type for struct ng_source_stats */
126585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[]
127585ff168SJulian Elischer 	= NG_SOURCE_STATS_TYPE_INFO;
128585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = {
129585ff168SJulian Elischer 	&ng_parse_struct_type,
130585ff168SJulian Elischer 	&ng_source_stats_type_fields
131585ff168SJulian Elischer };
132585ff168SJulian Elischer 
133585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */
134585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = {
135585ff168SJulian Elischer 	{
136585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
137585ff168SJulian Elischer 	  NGM_SOURCE_GET_STATS,
138585ff168SJulian Elischer 	  "getstats",
139585ff168SJulian Elischer 	  NULL,
140585ff168SJulian Elischer 	  &ng_source_stats_type
141585ff168SJulian Elischer 	},
142585ff168SJulian Elischer 	{
143585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
144585ff168SJulian Elischer 	  NGM_SOURCE_CLR_STATS,
145585ff168SJulian Elischer 	  "clrstats",
146585ff168SJulian Elischer 	  NULL,
147585ff168SJulian Elischer 	  NULL
148585ff168SJulian Elischer 	},
149585ff168SJulian Elischer 	{
150585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
151585ff168SJulian Elischer 	  NGM_SOURCE_GETCLR_STATS,
152585ff168SJulian Elischer 	  "getclrstats",
153585ff168SJulian Elischer 	  NULL,
154585ff168SJulian Elischer 	  &ng_source_stats_type
155585ff168SJulian Elischer 	},
156585ff168SJulian Elischer 	{
157585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
158585ff168SJulian Elischer 	  NGM_SOURCE_START,
159585ff168SJulian Elischer 	  "start",
160585ff168SJulian Elischer 	  &ng_parse_uint64_type,
161585ff168SJulian Elischer 	  NULL
162585ff168SJulian Elischer 	},
163585ff168SJulian Elischer 	{
164585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
165585ff168SJulian Elischer 	  NGM_SOURCE_STOP,
166585ff168SJulian Elischer 	  "stop",
167585ff168SJulian Elischer 	  NULL,
168585ff168SJulian Elischer 	  NULL
169585ff168SJulian Elischer 	},
170585ff168SJulian Elischer 	{
171585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
172585ff168SJulian Elischer 	  NGM_SOURCE_CLR_DATA,
173585ff168SJulian Elischer 	  "clrdata",
174585ff168SJulian Elischer 	  NULL,
175585ff168SJulian Elischer 	  NULL
176585ff168SJulian Elischer 	},
177f5d15522SHartmut Brandt 	{
178f5d15522SHartmut Brandt 	  NGM_SOURCE_COOKIE,
179d8f5d037SGleb Smirnoff 	  NGM_SOURCE_SETIFACE,
180d8f5d037SGleb Smirnoff 	  "setiface",
181d8f5d037SGleb Smirnoff 	  &ng_parse_string_type,
182f5d15522SHartmut Brandt 	  NULL
183f5d15522SHartmut Brandt 	},
18472235857SGleb Smirnoff 	{
18572235857SGleb Smirnoff 	  NGM_SOURCE_COOKIE,
18672235857SGleb Smirnoff 	  NGM_SOURCE_SETPPS,
18772235857SGleb Smirnoff 	  "setpps",
18872235857SGleb Smirnoff 	  &ng_parse_uint32_type,
18972235857SGleb Smirnoff 	  NULL
19072235857SGleb Smirnoff 	},
191585ff168SJulian Elischer 	{ 0 }
192585ff168SJulian Elischer };
193585ff168SJulian Elischer 
194585ff168SJulian Elischer /* Netgraph type descriptor */
195585ff168SJulian Elischer static struct ng_type ng_source_typestruct = {
196f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
197f8aae777SJulian Elischer 	.name =		NG_SOURCE_NODE_TYPE,
198f8aae777SJulian Elischer 	.constructor =	ng_source_constructor,
199f8aae777SJulian Elischer 	.rcvmsg =	ng_source_rcvmsg,
200f8aae777SJulian Elischer 	.shutdown =	ng_source_rmnode,
201f8aae777SJulian Elischer 	.newhook =	ng_source_newhook,
202d8f5d037SGleb Smirnoff 	.connect =	ng_source_connect,
203f8aae777SJulian Elischer 	.rcvdata =	ng_source_rcvdata,
204f8aae777SJulian Elischer 	.disconnect =	ng_source_disconnect,
205f8aae777SJulian Elischer 	.cmdlist =	ng_source_cmds,
206585ff168SJulian Elischer };
207585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct);
208585ff168SJulian Elischer 
209d8f5d037SGleb Smirnoff static int ng_source_set_autosrc(sc_p, uint32_t);
2102cafef3eSHartmut Brandt 
211585ff168SJulian Elischer /*
212585ff168SJulian Elischer  * Node constructor
213585ff168SJulian Elischer  */
214585ff168SJulian Elischer static int
2155968e29eSJulian Elischer ng_source_constructor(node_p node)
216585ff168SJulian Elischer {
217585ff168SJulian Elischer 	sc_p sc;
218585ff168SJulian Elischer 
219b1b70498SHartmut Brandt 	sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
220585ff168SJulian Elischer 	if (sc == NULL)
221585ff168SJulian Elischer 		return (ENOMEM);
222585ff168SJulian Elischer 
2235968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, sc);
2245968e29eSJulian Elischer 	sc->node = node;
225585ff168SJulian Elischer 	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
22630bef41bSGleb Smirnoff 	ng_callout_init(&sc->intr_ch);
22730bef41bSGleb Smirnoff 
228585ff168SJulian Elischer 	return (0);
229585ff168SJulian Elischer }
230585ff168SJulian Elischer 
231585ff168SJulian Elischer /*
232585ff168SJulian Elischer  * Add a hook
233585ff168SJulian Elischer  */
234585ff168SJulian Elischer static int
235585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name)
236585ff168SJulian Elischer {
237d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
238585ff168SJulian Elischer 
239585ff168SJulian Elischer 	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
240d8f5d037SGleb Smirnoff 		sc->input = hook;
241585ff168SJulian Elischer 	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
242d8f5d037SGleb Smirnoff 		sc->output = hook;
243585ff168SJulian Elischer 		sc->output_ifp = 0;
244585ff168SJulian Elischer 		bzero(&sc->stats, sizeof(sc->stats));
245585ff168SJulian Elischer 	} else
246585ff168SJulian Elischer 		return (EINVAL);
247d8f5d037SGleb Smirnoff 
248d8f5d037SGleb Smirnoff 	return (0);
249d8f5d037SGleb Smirnoff }
250d8f5d037SGleb Smirnoff 
251d8f5d037SGleb Smirnoff /*
252d8f5d037SGleb Smirnoff  * Hook has been added
253d8f5d037SGleb Smirnoff  */
254d8f5d037SGleb Smirnoff static int
255d8f5d037SGleb Smirnoff ng_source_connect(hook_p hook)
256d8f5d037SGleb Smirnoff {
257d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
258d8f5d037SGleb Smirnoff 	struct ng_mesg *msg;
259d8f5d037SGleb Smirnoff 	int dummy_error = 0;
260d8f5d037SGleb Smirnoff 
261d8f5d037SGleb Smirnoff 	/*
262d8f5d037SGleb Smirnoff 	 * If this is "output" hook, then request information
263d8f5d037SGleb Smirnoff 	 * from our downstream.
264d8f5d037SGleb Smirnoff 	 */
265d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
266d8f5d037SGleb Smirnoff 		NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME,
267d8f5d037SGleb Smirnoff 		    0, M_NOWAIT);
268d8f5d037SGleb Smirnoff 		if (msg == NULL)
269d8f5d037SGleb Smirnoff 			return (ENOBUFS);
270d8f5d037SGleb Smirnoff 
271d8f5d037SGleb Smirnoff 		/*
272d8f5d037SGleb Smirnoff 		 * Our hook and peer hook have HK_INVALID flag set,
273d8f5d037SGleb Smirnoff 		 * so we can't use NG_SEND_MSG_HOOK() macro here.
274d8f5d037SGleb Smirnoff 		 */
275d8f5d037SGleb Smirnoff 		NG_SEND_MSG_ID(dummy_error, sc->node, msg,
276d8f5d037SGleb Smirnoff 		    NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node));
277d8f5d037SGleb Smirnoff 	}
278d8f5d037SGleb Smirnoff 
279585ff168SJulian Elischer 	return (0);
280585ff168SJulian Elischer }
281585ff168SJulian Elischer 
282585ff168SJulian Elischer /*
283585ff168SJulian Elischer  * Receive a control message
284585ff168SJulian Elischer  */
285585ff168SJulian Elischer static int
2865968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
287585ff168SJulian Elischer {
288d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
289d8f5d037SGleb Smirnoff 	struct ng_mesg *msg, *resp = NULL;
290585ff168SJulian Elischer 	int error = 0;
291585ff168SJulian Elischer 
2925968e29eSJulian Elischer 	NGI_GET_MSG(item, msg);
293d8f5d037SGleb Smirnoff 
294585ff168SJulian Elischer 	switch (msg->header.typecookie) {
295585ff168SJulian Elischer 	case NGM_SOURCE_COOKIE:
296b655e33dSJulian Elischer 		if (msg->header.flags & NGF_RESP) {
297b655e33dSJulian Elischer 			error = EINVAL;
298b655e33dSJulian Elischer 			break;
299b655e33dSJulian Elischer 		}
300585ff168SJulian Elischer 		switch (msg->header.cmd) {
301585ff168SJulian Elischer 		case NGM_SOURCE_GET_STATS:
302585ff168SJulian Elischer 		case NGM_SOURCE_CLR_STATS:
303585ff168SJulian Elischer 		case NGM_SOURCE_GETCLR_STATS:
304585ff168SJulian Elischer                     {
305585ff168SJulian Elischer 			struct ng_source_stats *stats;
306585ff168SJulian Elischer 
307585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
308585ff168SJulian Elischer                                 NG_MKRESPONSE(resp, msg,
309585ff168SJulian Elischer                                     sizeof(*stats), M_NOWAIT);
310585ff168SJulian Elischer 				if (resp == NULL) {
311585ff168SJulian Elischer 					error = ENOMEM;
312585ff168SJulian Elischer 					goto done;
313585ff168SJulian Elischer 				}
314585ff168SJulian Elischer 				sc->stats.queueOctets = sc->queueOctets;
3154b52f283SJulian Elischer 				sc->stats.queueFrames = sc->snd_queue.ifq_len;
3165968e29eSJulian Elischer 				if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
317585ff168SJulian Elischer 				    && !timevalisset(&sc->stats.endTime)) {
318585ff168SJulian Elischer 					getmicrotime(&sc->stats.elapsedTime);
319585ff168SJulian Elischer 					timevalsub(&sc->stats.elapsedTime,
320585ff168SJulian Elischer 					    &sc->stats.startTime);
321585ff168SJulian Elischer 				}
3224b52f283SJulian Elischer 				stats = (struct ng_source_stats *)resp->data;
323585ff168SJulian Elischer 				bcopy(&sc->stats, stats, sizeof(* stats));
324585ff168SJulian Elischer                         }
325585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_GET_STATS)
326585ff168SJulian Elischer 				bzero(&sc->stats, sizeof(sc->stats));
327585ff168SJulian Elischer 		    }
328585ff168SJulian Elischer 		    break;
329585ff168SJulian Elischer 		case NGM_SOURCE_START:
330585ff168SJulian Elischer 		    {
331d8f5d037SGleb Smirnoff 			uint64_t packets;
332f5d15522SHartmut Brandt 
333d8f5d037SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint64_t)) {
334d8f5d037SGleb Smirnoff 				error = EINVAL;
335f5d15522SHartmut Brandt 				break;
336d8f5d037SGleb Smirnoff 			}
337d8f5d037SGleb Smirnoff 
338d8f5d037SGleb Smirnoff 			packets = *(uint64_t *)msg->data;
339d8f5d037SGleb Smirnoff 
340d8f5d037SGleb Smirnoff 			error = ng_source_start(sc, packets);
341d8f5d037SGleb Smirnoff 
342d8f5d037SGleb Smirnoff 		    	break;
343d8f5d037SGleb Smirnoff 		    }
344585ff168SJulian Elischer 		case NGM_SOURCE_STOP:
345585ff168SJulian Elischer 			ng_source_stop(sc);
346585ff168SJulian Elischer 			break;
347585ff168SJulian Elischer 		case NGM_SOURCE_CLR_DATA:
348585ff168SJulian Elischer 			ng_source_clr_data(sc);
349585ff168SJulian Elischer 			break;
350d8f5d037SGleb Smirnoff 		case NGM_SOURCE_SETIFACE:
351d8f5d037SGleb Smirnoff 		    {
352d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
353d8f5d037SGleb Smirnoff 
354d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
355d8f5d037SGleb Smirnoff 				error = EINVAL;
356d8f5d037SGleb Smirnoff 				break;
357d8f5d037SGleb Smirnoff 			}
358d8f5d037SGleb Smirnoff 
359d8f5d037SGleb Smirnoff 			ng_source_store_output_ifp(sc, ifname);
360d8f5d037SGleb Smirnoff 			break;
361d8f5d037SGleb Smirnoff 		    }
36272235857SGleb Smirnoff 		case NGM_SOURCE_SETPPS:
36372235857SGleb Smirnoff 		    {
36472235857SGleb Smirnoff 			uint32_t pps;
36572235857SGleb Smirnoff 
36672235857SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint32_t)) {
36772235857SGleb Smirnoff 				error = EINVAL;
36872235857SGleb Smirnoff 				break;
36972235857SGleb Smirnoff 			}
37072235857SGleb Smirnoff 
37172235857SGleb Smirnoff 			pps = *(uint32_t *)msg->data;
37272235857SGleb Smirnoff 
37372235857SGleb Smirnoff 			sc->stats.maxPps = pps;
37472235857SGleb Smirnoff 
37572235857SGleb Smirnoff 			break;
37672235857SGleb Smirnoff 		    }
377585ff168SJulian Elischer 		default:
378585ff168SJulian Elischer 			error = EINVAL;
379585ff168SJulian Elischer 			break;
380585ff168SJulian Elischer 		}
381585ff168SJulian Elischer 		break;
382b655e33dSJulian Elischer 	case NGM_ETHER_COOKIE:
383b655e33dSJulian Elischer 		if (!(msg->header.flags & NGF_RESP)) {
384b655e33dSJulian Elischer 			error = EINVAL;
385b655e33dSJulian Elischer 			break;
386b655e33dSJulian Elischer 		}
387b655e33dSJulian Elischer 		switch (msg->header.cmd) {
388d8f5d037SGleb Smirnoff 		case NGM_ETHER_GET_IFNAME:
389d8f5d037SGleb Smirnoff 		    {
390d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
391d8f5d037SGleb Smirnoff 
392d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
393d8f5d037SGleb Smirnoff 				error = EINVAL;
394b655e33dSJulian Elischer 				break;
395d8f5d037SGleb Smirnoff 			}
396d8f5d037SGleb Smirnoff 
397d8f5d037SGleb Smirnoff 			if (ng_source_store_output_ifp(sc, ifname) == 0)
398d8f5d037SGleb Smirnoff 				ng_source_set_autosrc(sc, 0);
399d8f5d037SGleb Smirnoff 			break;
400d8f5d037SGleb Smirnoff 		    }
401b655e33dSJulian Elischer 		default:
402b655e33dSJulian Elischer 			error = EINVAL;
403b655e33dSJulian Elischer 		}
404b655e33dSJulian Elischer 		break;
405585ff168SJulian Elischer 	default:
406585ff168SJulian Elischer 		error = EINVAL;
407585ff168SJulian Elischer 		break;
408585ff168SJulian Elischer 	}
409585ff168SJulian Elischer 
410585ff168SJulian Elischer done:
411d8f5d037SGleb Smirnoff 	/* Take care of synchronous response, if any. */
4125968e29eSJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
413d8f5d037SGleb Smirnoff 	/* Free the message and return. */
4145968e29eSJulian Elischer 	NG_FREE_MSG(msg);
415585ff168SJulian Elischer 	return (error);
416585ff168SJulian Elischer }
417585ff168SJulian Elischer 
418585ff168SJulian Elischer /*
419585ff168SJulian Elischer  * Receive data on a hook
420585ff168SJulian Elischer  *
421585ff168SJulian Elischer  * If data comes in the input hook, enqueue it on the send queue.
422585ff168SJulian Elischer  * If data comes in the output hook, discard it.
423585ff168SJulian Elischer  */
424585ff168SJulian Elischer static int
4255968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item)
426585ff168SJulian Elischer {
427d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
4285968e29eSJulian Elischer 	struct mbuf *m;
429d8f5d037SGleb Smirnoff 	int error = 0;
430585ff168SJulian Elischer 
4315968e29eSJulian Elischer 	NGI_GET_M(item, m);
4325968e29eSJulian Elischer 	NG_FREE_ITEM(item);
433585ff168SJulian Elischer 
434585ff168SJulian Elischer 	/* Which hook? */
435d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
436585ff168SJulian Elischer 		/* discard */
4375968e29eSJulian Elischer 		NG_FREE_M(m);
438585ff168SJulian Elischer 		return (error);
439585ff168SJulian Elischer 	}
440d8f5d037SGleb Smirnoff 	KASSERT(hook == sc->input, ("%s: no hook!", __func__));
441585ff168SJulian Elischer 
442d8f5d037SGleb Smirnoff 	/* Enqueue packet. */
443585ff168SJulian Elischer 	/* XXX should we check IF_QFULL() ? */
44481a4ef81SHartmut Brandt 	_IF_ENQUEUE(&sc->snd_queue, m);
445585ff168SJulian Elischer 	sc->queueOctets += m->m_pkthdr.len;
446585ff168SJulian Elischer 
447585ff168SJulian Elischer 	return (0);
448585ff168SJulian Elischer }
449585ff168SJulian Elischer 
450585ff168SJulian Elischer /*
451585ff168SJulian Elischer  * Shutdown processing
452585ff168SJulian Elischer  */
453585ff168SJulian Elischer static int
454585ff168SJulian Elischer ng_source_rmnode(node_p node)
455585ff168SJulian Elischer {
456d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
457585ff168SJulian Elischer 
458585ff168SJulian Elischer 	ng_source_stop(sc);
459585ff168SJulian Elischer 	ng_source_clr_data(sc);
4605968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
4615968e29eSJulian Elischer 	NG_NODE_UNREF(node);
46276bd5857SHartmut Brandt 	free(sc, M_NETGRAPH);
463d8f5d037SGleb Smirnoff 
464585ff168SJulian Elischer 	return (0);
465585ff168SJulian Elischer }
466585ff168SJulian Elischer 
467585ff168SJulian Elischer /*
468585ff168SJulian Elischer  * Hook disconnection
469585ff168SJulian Elischer  */
470585ff168SJulian Elischer static int
471585ff168SJulian Elischer ng_source_disconnect(hook_p hook)
472585ff168SJulian Elischer {
4734b52f283SJulian Elischer 	sc_p sc;
474585ff168SJulian Elischer 
4755968e29eSJulian Elischer 	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
47676bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
477d8f5d037SGleb Smirnoff 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output)
4785968e29eSJulian Elischer 		ng_rmnode_self(NG_HOOK_NODE(hook));
479585ff168SJulian Elischer 	return (0);
480585ff168SJulian Elischer }
481585ff168SJulian Elischer 
482585ff168SJulian Elischer /*
483b655e33dSJulian Elischer  * Set sc->output_ifp to point to the the struct ifnet of the interface
484b655e33dSJulian Elischer  * reached via our output hook.
485b655e33dSJulian Elischer  */
486b655e33dSJulian Elischer static int
487d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc_p sc, char *ifname)
488b655e33dSJulian Elischer {
489b655e33dSJulian Elischer 	struct ifnet *ifp;
490b655e33dSJulian Elischer 	int s;
491b655e33dSJulian Elischer 
492d8f5d037SGleb Smirnoff 	ifp = ifunit(ifname);
493585ff168SJulian Elischer 
494585ff168SJulian Elischer 	if (ifp == NULL) {
49576bd5857SHartmut Brandt 		printf("%s: can't find interface %d\n", __func__, if_index);
496585ff168SJulian Elischer 		return (EINVAL);
497585ff168SJulian Elischer 	}
498585ff168SJulian Elischer 	sc->output_ifp = ifp;
499585ff168SJulian Elischer 
500585ff168SJulian Elischer #if 1
501585ff168SJulian Elischer 	/* XXX mucking with a drivers ifqueue size is ugly but we need it
502585ff168SJulian Elischer 	 * to queue a lot of packets to get close to line rate on a gigabit
503585ff168SJulian Elischer 	 * interface with small packets.
504585ff168SJulian Elischer 	 * XXX we should restore the original value at stop or disconnect
505585ff168SJulian Elischer 	 */
506585ff168SJulian Elischer 	s = splimp();		/* XXX is this required? */
50776bd5857SHartmut Brandt 	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) {
508585ff168SJulian Elischer 		printf("ng_source: changing ifq_maxlen from %d to %d\n",
5094b52f283SJulian Elischer 		    ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
510585ff168SJulian Elischer 		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
511585ff168SJulian Elischer 	}
512585ff168SJulian Elischer 	splx(s);
513585ff168SJulian Elischer #endif
514585ff168SJulian Elischer 	return (0);
515585ff168SJulian Elischer }
516585ff168SJulian Elischer 
517585ff168SJulian Elischer /*
518585ff168SJulian Elischer  * Set the attached ethernet node's ethernet source address override flag.
519585ff168SJulian Elischer  */
520585ff168SJulian Elischer static int
521d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc_p sc, uint32_t flag)
522585ff168SJulian Elischer {
523585ff168SJulian Elischer 	struct ng_mesg *msg;
524585ff168SJulian Elischer 	int error = 0;
525585ff168SJulian Elischer 
526585ff168SJulian Elischer 	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
527d8f5d037SGleb Smirnoff 	    sizeof (uint32_t), M_NOWAIT);
528585ff168SJulian Elischer 	if (msg == NULL)
529585ff168SJulian Elischer 		return(ENOBUFS);
530585ff168SJulian Elischer 
531d8f5d037SGleb Smirnoff 	*(uint32_t *)msg->data = flag;
532d8f5d037SGleb Smirnoff 	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0);
533585ff168SJulian Elischer 	return (error);
534585ff168SJulian Elischer }
535585ff168SJulian Elischer 
536585ff168SJulian Elischer /*
537585ff168SJulian Elischer  * Clear out the data we've queued
538585ff168SJulian Elischer  */
539585ff168SJulian Elischer static void
540585ff168SJulian Elischer ng_source_clr_data (sc_p sc)
541585ff168SJulian Elischer {
542585ff168SJulian Elischer 	struct mbuf *m;
543585ff168SJulian Elischer 
544585ff168SJulian Elischer 	for (;;) {
54581a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
546585ff168SJulian Elischer 		if (m == NULL)
547585ff168SJulian Elischer 			break;
548585ff168SJulian Elischer 		NG_FREE_M(m);
549585ff168SJulian Elischer 	}
550585ff168SJulian Elischer 	sc->queueOctets = 0;
551585ff168SJulian Elischer }
552585ff168SJulian Elischer 
553585ff168SJulian Elischer /*
554585ff168SJulian Elischer  * Start sending queued data out the output hook
555585ff168SJulian Elischer  */
556d8f5d037SGleb Smirnoff static int
557d8f5d037SGleb Smirnoff ng_source_start(sc_p sc, uint64_t packets)
558585ff168SJulian Elischer {
559d8f5d037SGleb Smirnoff 	if (sc->output_ifp == NULL) {
560d8f5d037SGleb Smirnoff 		printf("ng_source: start without iface configured\n");
561d8f5d037SGleb Smirnoff 		return (ENXIO);
562d8f5d037SGleb Smirnoff 	}
563d8f5d037SGleb Smirnoff 
564d8f5d037SGleb Smirnoff 	if (sc->node->nd_flags & NG_SOURCE_ACTIVE)
565d8f5d037SGleb Smirnoff 		return (EBUSY);
566d8f5d037SGleb Smirnoff 
567d8f5d037SGleb Smirnoff 	sc->node->nd_flags |= NG_SOURCE_ACTIVE;
568d8f5d037SGleb Smirnoff 
569d8f5d037SGleb Smirnoff 	sc->packets = packets;
570d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.elapsedTime);
571d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.endTime);
572d8f5d037SGleb Smirnoff 	getmicrotime(&sc->stats.startTime);
57372235857SGleb Smirnoff 	getmicrotime(&sc->stats.lastTime);
574d8f5d037SGleb Smirnoff 	ng_callout(&sc->intr_ch, sc->node, NULL, 0,
575d8f5d037SGleb Smirnoff 	    ng_source_intr, sc, 0);
576d8f5d037SGleb Smirnoff 
577d8f5d037SGleb Smirnoff 	return (0);
578585ff168SJulian Elischer }
579585ff168SJulian Elischer 
580585ff168SJulian Elischer /*
581585ff168SJulian Elischer  * Stop sending queued data out the output hook
582585ff168SJulian Elischer  */
583585ff168SJulian Elischer static void
584585ff168SJulian Elischer ng_source_stop(sc_p sc)
585585ff168SJulian Elischer {
586f9d9e1b4SGleb Smirnoff 	ng_uncallout(&sc->intr_ch, sc->node);
5875968e29eSJulian Elischer 	sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
588585ff168SJulian Elischer 	getmicrotime(&sc->stats.endTime);
589585ff168SJulian Elischer 	sc->stats.elapsedTime = sc->stats.endTime;
590585ff168SJulian Elischer 	timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
591585ff168SJulian Elischer }
592585ff168SJulian Elischer 
593585ff168SJulian Elischer /*
594585ff168SJulian Elischer  * While active called every NG_SOURCE_INTR_TICKS ticks.
595585ff168SJulian Elischer  * Sends as many packets as the interface connected to our
596585ff168SJulian Elischer  * output hook is able to enqueue.
597585ff168SJulian Elischer  */
598585ff168SJulian Elischer static void
599a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2)
600585ff168SJulian Elischer {
601a1adb510SHartmut Brandt 	sc_p sc = (sc_p)arg1;
602585ff168SJulian Elischer 	struct ifqueue *ifq;
603585ff168SJulian Elischer 	int packets;
604585ff168SJulian Elischer 
60576bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
606585ff168SJulian Elischer 
607d8f5d037SGleb Smirnoff 	if (sc->packets == 0 || sc->output == NULL
6085968e29eSJulian Elischer 	    || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
609585ff168SJulian Elischer 		ng_source_stop(sc);
610585ff168SJulian Elischer 		return;
611585ff168SJulian Elischer 	}
612585ff168SJulian Elischer 
613f5d15522SHartmut Brandt 	if (sc->output_ifp != NULL) {
6140572dfacSRuslan Ermilov 		ifq = (struct ifqueue *)&sc->output_ifp->if_snd;
615585ff168SJulian Elischer 		packets = ifq->ifq_maxlen - ifq->ifq_len;
616f5d15522SHartmut Brandt 	} else
617f5d15522SHartmut Brandt 		packets = sc->snd_queue.ifq_len;
618f5d15522SHartmut Brandt 
61972235857SGleb Smirnoff 	if (sc->stats.maxPps != 0) {
62072235857SGleb Smirnoff 		struct timeval	now, elapsed;
62172235857SGleb Smirnoff 		uint64_t	usec;
62272235857SGleb Smirnoff 		int		maxpkt;
62372235857SGleb Smirnoff 
62472235857SGleb Smirnoff 		getmicrotime(&now);
62572235857SGleb Smirnoff 		elapsed = now;
62672235857SGleb Smirnoff 		timevalsub(&elapsed, &sc->stats.lastTime);
62772235857SGleb Smirnoff 		usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec;
62872235857SGleb Smirnoff 		maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000;
62972235857SGleb Smirnoff 		sc->stats.lastTime = now;
63072235857SGleb Smirnoff 		if (packets > maxpkt)
63172235857SGleb Smirnoff 			packets = maxpkt;
63272235857SGleb Smirnoff 	}
63372235857SGleb Smirnoff 
634585ff168SJulian Elischer 	ng_source_send(sc, packets, NULL);
635a1adb510SHartmut Brandt 	if (sc->packets == 0)
636585ff168SJulian Elischer 		ng_source_stop(sc);
637a1adb510SHartmut Brandt 	else
638f9d9e1b4SGleb Smirnoff 		ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS,
639d312eaf5SGleb Smirnoff 		    ng_source_intr, sc, 0);
640585ff168SJulian Elischer }
641585ff168SJulian Elischer 
642585ff168SJulian Elischer /*
643585ff168SJulian Elischer  * Send packets out our output hook
644585ff168SJulian Elischer  */
645585ff168SJulian Elischer static int
646585ff168SJulian Elischer ng_source_send (sc_p sc, int tosend, int *sent_p)
647585ff168SJulian Elischer {
648585ff168SJulian Elischer 	struct ifqueue tmp_queue;
649585ff168SJulian Elischer 	struct mbuf *m, *m2;
650585ff168SJulian Elischer 	int sent = 0;
651585ff168SJulian Elischer 	int error = 0;
652585ff168SJulian Elischer 
65376bd5857SHartmut Brandt 	KASSERT(tosend >= 0, ("%s: negative tosend param", __func__));
6545968e29eSJulian Elischer 	KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
65576bd5857SHartmut Brandt 	    ("%s: inactive node", __func__));
656585ff168SJulian Elischer 
657d8f5d037SGleb Smirnoff 	if ((uint64_t)tosend > sc->packets)
658585ff168SJulian Elischer 		tosend = sc->packets;
659585ff168SJulian Elischer 
660585ff168SJulian Elischer 	/* Copy the required number of packets to a temporary queue */
661585ff168SJulian Elischer 	bzero (&tmp_queue, sizeof (tmp_queue));
662585ff168SJulian Elischer 	for (sent = 0; error == 0 && sent < tosend; ++sent) {
66381a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
664585ff168SJulian Elischer 		if (m == NULL)
665585ff168SJulian Elischer 			break;
666585ff168SJulian Elischer 
667585ff168SJulian Elischer 		/* duplicate the packet */
668a163d034SWarner Losh 		m2 = m_copypacket(m, M_DONTWAIT);
669585ff168SJulian Elischer 		if (m2 == NULL) {
67081a4ef81SHartmut Brandt 			_IF_PREPEND(&sc->snd_queue, m);
671585ff168SJulian Elischer 			error = ENOBUFS;
672585ff168SJulian Elischer 			break;
673585ff168SJulian Elischer 		}
674585ff168SJulian Elischer 
675d8f5d037SGleb Smirnoff 		/* Re-enqueue the original packet for us. */
67681a4ef81SHartmut Brandt 		_IF_ENQUEUE(&sc->snd_queue, m);
677585ff168SJulian Elischer 
678d8f5d037SGleb Smirnoff 		/* Queue the copy for sending at splimp. */
67981a4ef81SHartmut Brandt 		_IF_ENQUEUE(&tmp_queue, m2);
680585ff168SJulian Elischer 	}
681585ff168SJulian Elischer 
682585ff168SJulian Elischer 	sent = 0;
683585ff168SJulian Elischer 	for (;;) {
68481a4ef81SHartmut Brandt 		_IF_DEQUEUE(&tmp_queue, m2);
685585ff168SJulian Elischer 		if (m2 == NULL)
686585ff168SJulian Elischer 			break;
687585ff168SJulian Elischer 		if (error == 0) {
688585ff168SJulian Elischer 			++sent;
689585ff168SJulian Elischer 			sc->stats.outFrames++;
690585ff168SJulian Elischer 			sc->stats.outOctets += m2->m_pkthdr.len;
691d8f5d037SGleb Smirnoff 			NG_SEND_DATA_ONLY(error, sc->output, m2);
692a1adb510SHartmut Brandt 			if (error)
693d8f5d037SGleb Smirnoff 				log(LOG_DEBUG, "%s: error=%d", __func__, error);
694585ff168SJulian Elischer 		} else {
695585ff168SJulian Elischer 			NG_FREE_M(m2);
696585ff168SJulian Elischer 		}
697585ff168SJulian Elischer 	}
698585ff168SJulian Elischer 
699585ff168SJulian Elischer 	sc->packets -= sent;
700585ff168SJulian Elischer 	if (sent_p != NULL)
701585ff168SJulian Elischer 		*sent_p = sent;
702585ff168SJulian Elischer 	return (error);
703585ff168SJulian Elischer }
704