xref: /freebsd/sys/netgraph/ng_source.c (revision 674d86bf9177ff80b5f38f7191951f303a816cac)
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  *
38d11f4f5dSEd Maste  * Author: Dave Chapeskie
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>
71530c0060SRobert Watson #include <net/vnet.h>
72585ff168SJulian Elischer #include <netgraph/ng_message.h>
73585ff168SJulian Elischer #include <netgraph/netgraph.h>
74585ff168SJulian Elischer #include <netgraph/ng_parse.h>
75585ff168SJulian Elischer #include <netgraph/ng_ether.h>
76585ff168SJulian Elischer #include <netgraph/ng_source.h>
77585ff168SJulian Elischer 
78585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS		1
79585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN	(4*1024)
80585ff168SJulian Elischer 
815f87dd69SEd Maste #define	mtod_off(m,off,t)	((t)(mtod((m),caddr_t)+(off)))
825f87dd69SEd Maste 
83585ff168SJulian Elischer /* Per node info */
84585ff168SJulian Elischer struct privdata {
85585ff168SJulian Elischer 	node_p				node;
86d8f5d037SGleb Smirnoff 	hook_p				input;
87d8f5d037SGleb Smirnoff 	hook_p				output;
88585ff168SJulian Elischer 	struct ng_source_stats		stats;
894b52f283SJulian Elischer 	struct ifqueue			snd_queue;	/* packets to send */
90577421ebSEd Maste 	struct mbuf			*last_packet;	/* last pkt in queue */
91585ff168SJulian Elischer 	struct ifnet			*output_ifp;
9230bef41bSGleb Smirnoff 	struct callout			intr_ch;
93d8f5d037SGleb Smirnoff 	uint64_t			packets;	/* packets to send */
94d8f5d037SGleb Smirnoff 	uint32_t			queueOctets;
955f87dd69SEd Maste 	struct ng_source_embed_info	embed_timestamp;
96577421ebSEd Maste 	struct ng_source_embed_cnt_info	embed_counter[NG_SOURCE_COUNTERS];
97585ff168SJulian Elischer };
98585ff168SJulian Elischer typedef struct privdata *sc_p;
99585ff168SJulian Elischer 
100585ff168SJulian Elischer /* Node flags */
101585ff168SJulian Elischer #define NG_SOURCE_ACTIVE	(NGF_TYPE1)
102585ff168SJulian Elischer 
103585ff168SJulian Elischer /* Netgraph methods */
104585ff168SJulian Elischer static ng_constructor_t	ng_source_constructor;
105585ff168SJulian Elischer static ng_rcvmsg_t	ng_source_rcvmsg;
106585ff168SJulian Elischer static ng_shutdown_t	ng_source_rmnode;
107585ff168SJulian Elischer static ng_newhook_t	ng_source_newhook;
108d8f5d037SGleb Smirnoff static ng_connect_t	ng_source_connect;
109585ff168SJulian Elischer static ng_rcvdata_t	ng_source_rcvdata;
110585ff168SJulian Elischer static ng_disconnect_t	ng_source_disconnect;
111585ff168SJulian Elischer 
112585ff168SJulian Elischer /* Other functions */
113a1adb510SHartmut Brandt static void		ng_source_intr(node_p, hook_p, void *, int);
114585ff168SJulian Elischer static void		ng_source_clr_data (sc_p);
115d8f5d037SGleb Smirnoff static int		ng_source_start (sc_p, uint64_t);
116585ff168SJulian Elischer static void		ng_source_stop (sc_p);
117585ff168SJulian Elischer static int		ng_source_send (sc_p, int, int *);
118d8f5d037SGleb Smirnoff static int		ng_source_store_output_ifp(sc_p, char *);
1195f87dd69SEd Maste static void		ng_source_packet_mod(sc_p, struct mbuf *,
1205f87dd69SEd Maste 			    int, int, caddr_t, int);
121577421ebSEd Maste static void		ng_source_mod_counter(sc_p sc,
122577421ebSEd Maste 			    struct ng_source_embed_cnt_info *cnt,
123577421ebSEd Maste 			    struct mbuf *m, int increment);
1245f87dd69SEd Maste static int		ng_source_dup_mod(sc_p, struct mbuf *,
1255f87dd69SEd Maste 			    struct mbuf **);
126585ff168SJulian Elischer 
127585ff168SJulian Elischer /* Parse type for timeval */
12876bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = {
129585ff168SJulian Elischer 	{ "tv_sec",		&ng_parse_int32_type	},
130585ff168SJulian Elischer 	{ "tv_usec",		&ng_parse_int32_type	},
131585ff168SJulian Elischer 	{ NULL }
132585ff168SJulian Elischer };
133585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = {
134585ff168SJulian Elischer 	&ng_parse_struct_type,
135585ff168SJulian Elischer 	&ng_source_timeval_type_fields
136585ff168SJulian Elischer };
137585ff168SJulian Elischer 
138585ff168SJulian Elischer /* Parse type for struct ng_source_stats */
139585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[]
140585ff168SJulian Elischer 	= NG_SOURCE_STATS_TYPE_INFO;
141585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = {
142585ff168SJulian Elischer 	&ng_parse_struct_type,
143585ff168SJulian Elischer 	&ng_source_stats_type_fields
144585ff168SJulian Elischer };
145585ff168SJulian Elischer 
1465f87dd69SEd Maste /* Parse type for struct ng_source_embed_info */
1475f87dd69SEd Maste static const struct ng_parse_struct_field ng_source_embed_type_fields[] =
1485f87dd69SEd Maste 	NG_SOURCE_EMBED_TYPE_INFO;
1495f87dd69SEd Maste static const struct ng_parse_type ng_source_embed_type = {
1505f87dd69SEd Maste 	&ng_parse_struct_type,
1515f87dd69SEd Maste 	&ng_source_embed_type_fields
1525f87dd69SEd Maste };
1535f87dd69SEd Maste 
154577421ebSEd Maste /* Parse type for struct ng_source_embed_cnt_info */
155577421ebSEd Maste static const struct ng_parse_struct_field ng_source_embed_cnt_type_fields[] =
156577421ebSEd Maste 	NG_SOURCE_EMBED_CNT_TYPE_INFO;
157577421ebSEd Maste static const struct ng_parse_type ng_source_embed_cnt_type = {
158577421ebSEd Maste 	&ng_parse_struct_type,
159577421ebSEd Maste 	&ng_source_embed_cnt_type_fields
160577421ebSEd Maste };
161577421ebSEd Maste 
162585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */
163585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = {
164585ff168SJulian Elischer 	{
165585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
166585ff168SJulian Elischer 	  NGM_SOURCE_GET_STATS,
167585ff168SJulian Elischer 	  "getstats",
168585ff168SJulian Elischer 	  NULL,
169585ff168SJulian Elischer 	  &ng_source_stats_type
170585ff168SJulian Elischer 	},
171585ff168SJulian Elischer 	{
172585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
173585ff168SJulian Elischer 	  NGM_SOURCE_CLR_STATS,
174585ff168SJulian Elischer 	  "clrstats",
175585ff168SJulian Elischer 	  NULL,
176585ff168SJulian Elischer 	  NULL
177585ff168SJulian Elischer 	},
178585ff168SJulian Elischer 	{
179585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
180585ff168SJulian Elischer 	  NGM_SOURCE_GETCLR_STATS,
181585ff168SJulian Elischer 	  "getclrstats",
182585ff168SJulian Elischer 	  NULL,
183585ff168SJulian Elischer 	  &ng_source_stats_type
184585ff168SJulian Elischer 	},
185585ff168SJulian Elischer 	{
186585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
187585ff168SJulian Elischer 	  NGM_SOURCE_START,
188585ff168SJulian Elischer 	  "start",
189585ff168SJulian Elischer 	  &ng_parse_uint64_type,
190585ff168SJulian Elischer 	  NULL
191585ff168SJulian Elischer 	},
192585ff168SJulian Elischer 	{
193585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
194585ff168SJulian Elischer 	  NGM_SOURCE_STOP,
195585ff168SJulian Elischer 	  "stop",
196585ff168SJulian Elischer 	  NULL,
197585ff168SJulian Elischer 	  NULL
198585ff168SJulian Elischer 	},
199585ff168SJulian Elischer 	{
200585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
201585ff168SJulian Elischer 	  NGM_SOURCE_CLR_DATA,
202585ff168SJulian Elischer 	  "clrdata",
203585ff168SJulian Elischer 	  NULL,
204585ff168SJulian Elischer 	  NULL
205585ff168SJulian Elischer 	},
206f5d15522SHartmut Brandt 	{
207f5d15522SHartmut Brandt 	  NGM_SOURCE_COOKIE,
208d8f5d037SGleb Smirnoff 	  NGM_SOURCE_SETIFACE,
209d8f5d037SGleb Smirnoff 	  "setiface",
210d8f5d037SGleb Smirnoff 	  &ng_parse_string_type,
211f5d15522SHartmut Brandt 	  NULL
212f5d15522SHartmut Brandt 	},
21372235857SGleb Smirnoff 	{
21472235857SGleb Smirnoff 	  NGM_SOURCE_COOKIE,
21572235857SGleb Smirnoff 	  NGM_SOURCE_SETPPS,
21672235857SGleb Smirnoff 	  "setpps",
21772235857SGleb Smirnoff 	  &ng_parse_uint32_type,
21872235857SGleb Smirnoff 	  NULL
21972235857SGleb Smirnoff 	},
2205f87dd69SEd Maste 	{
2215f87dd69SEd Maste 	  NGM_SOURCE_COOKIE,
2225f87dd69SEd Maste 	  NGM_SOURCE_SET_TIMESTAMP,
2235f87dd69SEd Maste 	  "settimestamp",
2245f87dd69SEd Maste 	  &ng_source_embed_type,
2255f87dd69SEd Maste 	  NULL
2265f87dd69SEd Maste 	},
2275f87dd69SEd Maste 	{
2285f87dd69SEd Maste 	  NGM_SOURCE_COOKIE,
2295f87dd69SEd Maste 	  NGM_SOURCE_GET_TIMESTAMP,
2305f87dd69SEd Maste 	  "gettimestamp",
2315f87dd69SEd Maste 	  NULL,
2325f87dd69SEd Maste 	  &ng_source_embed_type
2335f87dd69SEd Maste 	},
234577421ebSEd Maste 	{
235577421ebSEd Maste 	  NGM_SOURCE_COOKIE,
236577421ebSEd Maste 	  NGM_SOURCE_SET_COUNTER,
237577421ebSEd Maste 	  "setcounter",
238577421ebSEd Maste 	  &ng_source_embed_cnt_type,
239577421ebSEd Maste 	  NULL
240577421ebSEd Maste 	},
241577421ebSEd Maste 	{
242577421ebSEd Maste 	  NGM_SOURCE_COOKIE,
243577421ebSEd Maste 	  NGM_SOURCE_GET_COUNTER,
244577421ebSEd Maste 	  "getcounter",
245577421ebSEd Maste 	  &ng_parse_uint8_type,
246577421ebSEd Maste 	  &ng_source_embed_cnt_type
247577421ebSEd Maste 	},
248585ff168SJulian Elischer 	{ 0 }
249585ff168SJulian Elischer };
250585ff168SJulian Elischer 
251585ff168SJulian Elischer /* Netgraph type descriptor */
252585ff168SJulian Elischer static struct ng_type ng_source_typestruct = {
253f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
254f8aae777SJulian Elischer 	.name =		NG_SOURCE_NODE_TYPE,
255f8aae777SJulian Elischer 	.constructor =	ng_source_constructor,
256f8aae777SJulian Elischer 	.rcvmsg =	ng_source_rcvmsg,
257f8aae777SJulian Elischer 	.shutdown =	ng_source_rmnode,
258f8aae777SJulian Elischer 	.newhook =	ng_source_newhook,
259d8f5d037SGleb Smirnoff 	.connect =	ng_source_connect,
260f8aae777SJulian Elischer 	.rcvdata =	ng_source_rcvdata,
261f8aae777SJulian Elischer 	.disconnect =	ng_source_disconnect,
262f8aae777SJulian Elischer 	.cmdlist =	ng_source_cmds,
263585ff168SJulian Elischer };
264585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct);
265585ff168SJulian Elischer 
266d8f5d037SGleb Smirnoff static int ng_source_set_autosrc(sc_p, uint32_t);
2672cafef3eSHartmut Brandt 
268585ff168SJulian Elischer /*
269585ff168SJulian Elischer  * Node constructor
270585ff168SJulian Elischer  */
271585ff168SJulian Elischer static int
2725968e29eSJulian Elischer ng_source_constructor(node_p node)
273585ff168SJulian Elischer {
274585ff168SJulian Elischer 	sc_p sc;
275585ff168SJulian Elischer 
276*674d86bfSGleb Smirnoff 	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
277585ff168SJulian Elischer 
2785968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, sc);
2795968e29eSJulian Elischer 	sc->node = node;
280585ff168SJulian Elischer 	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
28130bef41bSGleb Smirnoff 	ng_callout_init(&sc->intr_ch);
28230bef41bSGleb Smirnoff 
283585ff168SJulian Elischer 	return (0);
284585ff168SJulian Elischer }
285585ff168SJulian Elischer 
286585ff168SJulian Elischer /*
287585ff168SJulian Elischer  * Add a hook
288585ff168SJulian Elischer  */
289585ff168SJulian Elischer static int
290585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name)
291585ff168SJulian Elischer {
292d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
293585ff168SJulian Elischer 
294585ff168SJulian Elischer 	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
295d8f5d037SGleb Smirnoff 		sc->input = hook;
296585ff168SJulian Elischer 	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
297d8f5d037SGleb Smirnoff 		sc->output = hook;
298585ff168SJulian Elischer 		sc->output_ifp = 0;
299585ff168SJulian Elischer 		bzero(&sc->stats, sizeof(sc->stats));
300585ff168SJulian Elischer 	} else
301585ff168SJulian Elischer 		return (EINVAL);
302d8f5d037SGleb Smirnoff 
303d8f5d037SGleb Smirnoff 	return (0);
304d8f5d037SGleb Smirnoff }
305d8f5d037SGleb Smirnoff 
306d8f5d037SGleb Smirnoff /*
307d8f5d037SGleb Smirnoff  * Hook has been added
308d8f5d037SGleb Smirnoff  */
309d8f5d037SGleb Smirnoff static int
310d8f5d037SGleb Smirnoff ng_source_connect(hook_p hook)
311d8f5d037SGleb Smirnoff {
312d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
313d8f5d037SGleb Smirnoff 	struct ng_mesg *msg;
314d8f5d037SGleb Smirnoff 	int dummy_error = 0;
315d8f5d037SGleb Smirnoff 
316d8f5d037SGleb Smirnoff 	/*
317d8f5d037SGleb Smirnoff 	 * If this is "output" hook, then request information
318d8f5d037SGleb Smirnoff 	 * from our downstream.
319d8f5d037SGleb Smirnoff 	 */
320d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
321d8f5d037SGleb Smirnoff 		NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME,
322d8f5d037SGleb Smirnoff 		    0, M_NOWAIT);
323d8f5d037SGleb Smirnoff 		if (msg == NULL)
324d8f5d037SGleb Smirnoff 			return (ENOBUFS);
325d8f5d037SGleb Smirnoff 
326d8f5d037SGleb Smirnoff 		/*
327d8f5d037SGleb Smirnoff 		 * Our hook and peer hook have HK_INVALID flag set,
328d8f5d037SGleb Smirnoff 		 * so we can't use NG_SEND_MSG_HOOK() macro here.
329d8f5d037SGleb Smirnoff 		 */
330d8f5d037SGleb Smirnoff 		NG_SEND_MSG_ID(dummy_error, sc->node, msg,
331d8f5d037SGleb Smirnoff 		    NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node));
332d8f5d037SGleb Smirnoff 	}
333d8f5d037SGleb Smirnoff 
334585ff168SJulian Elischer 	return (0);
335585ff168SJulian Elischer }
336585ff168SJulian Elischer 
337585ff168SJulian Elischer /*
338585ff168SJulian Elischer  * Receive a control message
339585ff168SJulian Elischer  */
340585ff168SJulian Elischer static int
3415968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
342585ff168SJulian Elischer {
343d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
344d8f5d037SGleb Smirnoff 	struct ng_mesg *msg, *resp = NULL;
345585ff168SJulian Elischer 	int error = 0;
346585ff168SJulian Elischer 
3475968e29eSJulian Elischer 	NGI_GET_MSG(item, msg);
348d8f5d037SGleb Smirnoff 
349585ff168SJulian Elischer 	switch (msg->header.typecookie) {
350585ff168SJulian Elischer 	case NGM_SOURCE_COOKIE:
351b655e33dSJulian Elischer 		if (msg->header.flags & NGF_RESP) {
352b655e33dSJulian Elischer 			error = EINVAL;
353b655e33dSJulian Elischer 			break;
354b655e33dSJulian Elischer 		}
355585ff168SJulian Elischer 		switch (msg->header.cmd) {
356585ff168SJulian Elischer 		case NGM_SOURCE_GET_STATS:
357585ff168SJulian Elischer 		case NGM_SOURCE_CLR_STATS:
358585ff168SJulian Elischer 		case NGM_SOURCE_GETCLR_STATS:
359585ff168SJulian Elischer                     {
360585ff168SJulian Elischer 			struct ng_source_stats *stats;
361585ff168SJulian Elischer 
362585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
363585ff168SJulian Elischer                                 NG_MKRESPONSE(resp, msg,
364585ff168SJulian Elischer                                     sizeof(*stats), M_NOWAIT);
365585ff168SJulian Elischer 				if (resp == NULL) {
366585ff168SJulian Elischer 					error = ENOMEM;
367585ff168SJulian Elischer 					goto done;
368585ff168SJulian Elischer 				}
369585ff168SJulian Elischer 				sc->stats.queueOctets = sc->queueOctets;
3704b52f283SJulian Elischer 				sc->stats.queueFrames = sc->snd_queue.ifq_len;
3715968e29eSJulian Elischer 				if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
372585ff168SJulian Elischer 				    && !timevalisset(&sc->stats.endTime)) {
373585ff168SJulian Elischer 					getmicrotime(&sc->stats.elapsedTime);
374585ff168SJulian Elischer 					timevalsub(&sc->stats.elapsedTime,
375585ff168SJulian Elischer 					    &sc->stats.startTime);
376585ff168SJulian Elischer 				}
3774b52f283SJulian Elischer 				stats = (struct ng_source_stats *)resp->data;
378585ff168SJulian Elischer 				bcopy(&sc->stats, stats, sizeof(* stats));
379585ff168SJulian Elischer                         }
380585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_GET_STATS)
381585ff168SJulian Elischer 				bzero(&sc->stats, sizeof(sc->stats));
382585ff168SJulian Elischer 		    }
383585ff168SJulian Elischer 		    break;
384585ff168SJulian Elischer 		case NGM_SOURCE_START:
385585ff168SJulian Elischer 		    {
386d8f5d037SGleb Smirnoff 			uint64_t packets;
387f5d15522SHartmut Brandt 
388d8f5d037SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint64_t)) {
389d8f5d037SGleb Smirnoff 				error = EINVAL;
390f5d15522SHartmut Brandt 				break;
391d8f5d037SGleb Smirnoff 			}
392d8f5d037SGleb Smirnoff 
393d8f5d037SGleb Smirnoff 			packets = *(uint64_t *)msg->data;
394d8f5d037SGleb Smirnoff 
395d8f5d037SGleb Smirnoff 			error = ng_source_start(sc, packets);
396d8f5d037SGleb Smirnoff 
397d8f5d037SGleb Smirnoff 		    	break;
398d8f5d037SGleb Smirnoff 		    }
399585ff168SJulian Elischer 		case NGM_SOURCE_STOP:
400585ff168SJulian Elischer 			ng_source_stop(sc);
401585ff168SJulian Elischer 			break;
402585ff168SJulian Elischer 		case NGM_SOURCE_CLR_DATA:
403585ff168SJulian Elischer 			ng_source_clr_data(sc);
404585ff168SJulian Elischer 			break;
405d8f5d037SGleb Smirnoff 		case NGM_SOURCE_SETIFACE:
406d8f5d037SGleb Smirnoff 		    {
407d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
408d8f5d037SGleb Smirnoff 
409d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
410d8f5d037SGleb Smirnoff 				error = EINVAL;
411d8f5d037SGleb Smirnoff 				break;
412d8f5d037SGleb Smirnoff 			}
413d8f5d037SGleb Smirnoff 
414d8f5d037SGleb Smirnoff 			ng_source_store_output_ifp(sc, ifname);
415d8f5d037SGleb Smirnoff 			break;
416d8f5d037SGleb Smirnoff 		    }
41772235857SGleb Smirnoff 		case NGM_SOURCE_SETPPS:
41872235857SGleb Smirnoff 		    {
41972235857SGleb Smirnoff 			uint32_t pps;
42072235857SGleb Smirnoff 
42172235857SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint32_t)) {
42272235857SGleb Smirnoff 				error = EINVAL;
42372235857SGleb Smirnoff 				break;
42472235857SGleb Smirnoff 			}
42572235857SGleb Smirnoff 
42672235857SGleb Smirnoff 			pps = *(uint32_t *)msg->data;
42772235857SGleb Smirnoff 
42872235857SGleb Smirnoff 			sc->stats.maxPps = pps;
42972235857SGleb Smirnoff 
43072235857SGleb Smirnoff 			break;
43172235857SGleb Smirnoff 		    }
4325f87dd69SEd Maste 		case NGM_SOURCE_SET_TIMESTAMP:
4335f87dd69SEd Maste 		    {
4345f87dd69SEd Maste 			struct ng_source_embed_info *embed;
4355f87dd69SEd Maste 
436ab2e868cSEd Maste 			if (msg->header.arglen != sizeof(*embed)) {
437ab2e868cSEd Maste 				error = EINVAL;
438ab2e868cSEd Maste 				goto done;
439ab2e868cSEd Maste 			}
4405f87dd69SEd Maste 			embed = (struct ng_source_embed_info *)msg->data;
4415f87dd69SEd Maste 			bcopy(embed, &sc->embed_timestamp, sizeof(*embed));
4425f87dd69SEd Maste 
4435f87dd69SEd Maste 			break;
4445f87dd69SEd Maste 		    }
4455f87dd69SEd Maste 		case NGM_SOURCE_GET_TIMESTAMP:
4465f87dd69SEd Maste 		    {
4475f87dd69SEd Maste 			struct ng_source_embed_info *embed;
4485f87dd69SEd Maste 
4495f87dd69SEd Maste 			NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT);
4505f87dd69SEd Maste 			if (resp == NULL) {
4515f87dd69SEd Maste 				error = ENOMEM;
4525f87dd69SEd Maste 				goto done;
4535f87dd69SEd Maste 			}
4545f87dd69SEd Maste 			embed = (struct ng_source_embed_info *)resp->data;
4555f87dd69SEd Maste 			bcopy(&sc->embed_timestamp, embed, sizeof(*embed));
4565f87dd69SEd Maste 
4575f87dd69SEd Maste 			break;
4585f87dd69SEd Maste 		    }
459577421ebSEd Maste 		case NGM_SOURCE_SET_COUNTER:
460577421ebSEd Maste 		    {
461577421ebSEd Maste 			struct ng_source_embed_cnt_info *embed;
462577421ebSEd Maste 
463ab2e868cSEd Maste 			if (msg->header.arglen != sizeof(*embed)) {
464ab2e868cSEd Maste 				error = EINVAL;
465ab2e868cSEd Maste 				goto done;
466ab2e868cSEd Maste 			}
467577421ebSEd Maste 			embed = (struct ng_source_embed_cnt_info *)msg->data;
468577421ebSEd Maste 			if (embed->index >= NG_SOURCE_COUNTERS ||
469577421ebSEd Maste 			    !(embed->width == 1 || embed->width == 2 ||
470577421ebSEd Maste 			    embed->width == 4)) {
471577421ebSEd Maste 				error = EINVAL;
472577421ebSEd Maste 				goto done;
473577421ebSEd Maste 			}
474577421ebSEd Maste 			bcopy(embed, &sc->embed_counter[embed->index],
475577421ebSEd Maste 			    sizeof(*embed));
476577421ebSEd Maste 
477577421ebSEd Maste 			break;
478577421ebSEd Maste 		    }
479577421ebSEd Maste 		case NGM_SOURCE_GET_COUNTER:
480577421ebSEd Maste 		    {
481577421ebSEd Maste 			uint8_t index = *(uint8_t *)msg->data;
482577421ebSEd Maste 			struct ng_source_embed_cnt_info *embed;
483577421ebSEd Maste 
484577421ebSEd Maste 			if (index >= NG_SOURCE_COUNTERS) {
485577421ebSEd Maste 				error = EINVAL;
486577421ebSEd Maste 				goto done;
487577421ebSEd Maste 			}
488577421ebSEd Maste 			NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT);
489577421ebSEd Maste 			if (resp == NULL) {
490577421ebSEd Maste 				error = ENOMEM;
491577421ebSEd Maste 				goto done;
492577421ebSEd Maste 			}
493577421ebSEd Maste 			embed = (struct ng_source_embed_cnt_info *)resp->data;
494577421ebSEd Maste 			bcopy(&sc->embed_counter[index], embed, sizeof(*embed));
495577421ebSEd Maste 
496577421ebSEd Maste 			break;
497577421ebSEd Maste 		    }
498585ff168SJulian Elischer 		default:
499585ff168SJulian Elischer 			error = EINVAL;
500585ff168SJulian Elischer 			break;
501585ff168SJulian Elischer 		}
502585ff168SJulian Elischer 		break;
503b655e33dSJulian Elischer 	case NGM_ETHER_COOKIE:
504b655e33dSJulian Elischer 		if (!(msg->header.flags & NGF_RESP)) {
505b655e33dSJulian Elischer 			error = EINVAL;
506b655e33dSJulian Elischer 			break;
507b655e33dSJulian Elischer 		}
508b655e33dSJulian Elischer 		switch (msg->header.cmd) {
509d8f5d037SGleb Smirnoff 		case NGM_ETHER_GET_IFNAME:
510d8f5d037SGleb Smirnoff 		    {
511d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
512d8f5d037SGleb Smirnoff 
513d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
514d8f5d037SGleb Smirnoff 				error = EINVAL;
515b655e33dSJulian Elischer 				break;
516d8f5d037SGleb Smirnoff 			}
517d8f5d037SGleb Smirnoff 
518d8f5d037SGleb Smirnoff 			if (ng_source_store_output_ifp(sc, ifname) == 0)
519d8f5d037SGleb Smirnoff 				ng_source_set_autosrc(sc, 0);
520d8f5d037SGleb Smirnoff 			break;
521d8f5d037SGleb Smirnoff 		    }
522b655e33dSJulian Elischer 		default:
523b655e33dSJulian Elischer 			error = EINVAL;
524b655e33dSJulian Elischer 		}
525b655e33dSJulian Elischer 		break;
526585ff168SJulian Elischer 	default:
527585ff168SJulian Elischer 		error = EINVAL;
528585ff168SJulian Elischer 		break;
529585ff168SJulian Elischer 	}
530585ff168SJulian Elischer 
531585ff168SJulian Elischer done:
532d8f5d037SGleb Smirnoff 	/* Take care of synchronous response, if any. */
5335968e29eSJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
534d8f5d037SGleb Smirnoff 	/* Free the message and return. */
5355968e29eSJulian Elischer 	NG_FREE_MSG(msg);
536585ff168SJulian Elischer 	return (error);
537585ff168SJulian Elischer }
538585ff168SJulian Elischer 
539585ff168SJulian Elischer /*
540585ff168SJulian Elischer  * Receive data on a hook
541585ff168SJulian Elischer  *
542585ff168SJulian Elischer  * If data comes in the input hook, enqueue it on the send queue.
543585ff168SJulian Elischer  * If data comes in the output hook, discard it.
544585ff168SJulian Elischer  */
545585ff168SJulian Elischer static int
5465968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item)
547585ff168SJulian Elischer {
548d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
5495968e29eSJulian Elischer 	struct mbuf *m;
550d8f5d037SGleb Smirnoff 	int error = 0;
551585ff168SJulian Elischer 
5525968e29eSJulian Elischer 	NGI_GET_M(item, m);
5535968e29eSJulian Elischer 	NG_FREE_ITEM(item);
554585ff168SJulian Elischer 
555585ff168SJulian Elischer 	/* Which hook? */
556d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
557585ff168SJulian Elischer 		/* discard */
5585968e29eSJulian Elischer 		NG_FREE_M(m);
559585ff168SJulian Elischer 		return (error);
560585ff168SJulian Elischer 	}
561d8f5d037SGleb Smirnoff 	KASSERT(hook == sc->input, ("%s: no hook!", __func__));
562585ff168SJulian Elischer 
563d8f5d037SGleb Smirnoff 	/* Enqueue packet. */
564585ff168SJulian Elischer 	/* XXX should we check IF_QFULL() ? */
56581a4ef81SHartmut Brandt 	_IF_ENQUEUE(&sc->snd_queue, m);
566585ff168SJulian Elischer 	sc->queueOctets += m->m_pkthdr.len;
567577421ebSEd Maste 	sc->last_packet = m;
568585ff168SJulian Elischer 
569585ff168SJulian Elischer 	return (0);
570585ff168SJulian Elischer }
571585ff168SJulian Elischer 
572585ff168SJulian Elischer /*
573585ff168SJulian Elischer  * Shutdown processing
574585ff168SJulian Elischer  */
575585ff168SJulian Elischer static int
576585ff168SJulian Elischer ng_source_rmnode(node_p node)
577585ff168SJulian Elischer {
578d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
579585ff168SJulian Elischer 
580585ff168SJulian Elischer 	ng_source_stop(sc);
581585ff168SJulian Elischer 	ng_source_clr_data(sc);
5825968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
5835968e29eSJulian Elischer 	NG_NODE_UNREF(node);
58476bd5857SHartmut Brandt 	free(sc, M_NETGRAPH);
585d8f5d037SGleb Smirnoff 
586585ff168SJulian Elischer 	return (0);
587585ff168SJulian Elischer }
588585ff168SJulian Elischer 
589585ff168SJulian Elischer /*
590585ff168SJulian Elischer  * Hook disconnection
591585ff168SJulian Elischer  */
592585ff168SJulian Elischer static int
593585ff168SJulian Elischer ng_source_disconnect(hook_p hook)
594585ff168SJulian Elischer {
5954b52f283SJulian Elischer 	sc_p sc;
596585ff168SJulian Elischer 
5975968e29eSJulian Elischer 	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
59876bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
599d8f5d037SGleb Smirnoff 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output)
6005968e29eSJulian Elischer 		ng_rmnode_self(NG_HOOK_NODE(hook));
601585ff168SJulian Elischer 	return (0);
602585ff168SJulian Elischer }
603585ff168SJulian Elischer 
604585ff168SJulian Elischer /*
6056bccea7cSRebecca Cran  * Set sc->output_ifp to point to the struct ifnet of the interface
606b655e33dSJulian Elischer  * reached via our output hook.
607b655e33dSJulian Elischer  */
608b655e33dSJulian Elischer static int
609d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc_p sc, char *ifname)
610b655e33dSJulian Elischer {
611b655e33dSJulian Elischer 	struct ifnet *ifp;
612b655e33dSJulian Elischer 	int s;
613b655e33dSJulian Elischer 
614d8f5d037SGleb Smirnoff 	ifp = ifunit(ifname);
615585ff168SJulian Elischer 
616585ff168SJulian Elischer 	if (ifp == NULL) {
6173ac12c59SMarko Zec 		printf("%s: can't find interface %s\n", __func__, ifname);
618585ff168SJulian Elischer 		return (EINVAL);
619585ff168SJulian Elischer 	}
620585ff168SJulian Elischer 	sc->output_ifp = ifp;
621585ff168SJulian Elischer 
622585ff168SJulian Elischer #if 1
623585ff168SJulian Elischer 	/* XXX mucking with a drivers ifqueue size is ugly but we need it
624585ff168SJulian Elischer 	 * to queue a lot of packets to get close to line rate on a gigabit
625585ff168SJulian Elischer 	 * interface with small packets.
626585ff168SJulian Elischer 	 * XXX we should restore the original value at stop or disconnect
627585ff168SJulian Elischer 	 */
628585ff168SJulian Elischer 	s = splimp();		/* XXX is this required? */
62976bd5857SHartmut Brandt 	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) {
630585ff168SJulian Elischer 		printf("ng_source: changing ifq_maxlen from %d to %d\n",
6314b52f283SJulian Elischer 		    ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
632585ff168SJulian Elischer 		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
633585ff168SJulian Elischer 	}
634585ff168SJulian Elischer 	splx(s);
635585ff168SJulian Elischer #endif
636585ff168SJulian Elischer 	return (0);
637585ff168SJulian Elischer }
638585ff168SJulian Elischer 
639585ff168SJulian Elischer /*
640585ff168SJulian Elischer  * Set the attached ethernet node's ethernet source address override flag.
641585ff168SJulian Elischer  */
642585ff168SJulian Elischer static int
643d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc_p sc, uint32_t flag)
644585ff168SJulian Elischer {
645585ff168SJulian Elischer 	struct ng_mesg *msg;
646585ff168SJulian Elischer 	int error = 0;
647585ff168SJulian Elischer 
648585ff168SJulian Elischer 	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
649d8f5d037SGleb Smirnoff 	    sizeof (uint32_t), M_NOWAIT);
650585ff168SJulian Elischer 	if (msg == NULL)
651585ff168SJulian Elischer 		return(ENOBUFS);
652585ff168SJulian Elischer 
653d8f5d037SGleb Smirnoff 	*(uint32_t *)msg->data = flag;
654d8f5d037SGleb Smirnoff 	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0);
655585ff168SJulian Elischer 	return (error);
656585ff168SJulian Elischer }
657585ff168SJulian Elischer 
658585ff168SJulian Elischer /*
659585ff168SJulian Elischer  * Clear out the data we've queued
660585ff168SJulian Elischer  */
661585ff168SJulian Elischer static void
662585ff168SJulian Elischer ng_source_clr_data (sc_p sc)
663585ff168SJulian Elischer {
664585ff168SJulian Elischer 	struct mbuf *m;
665585ff168SJulian Elischer 
666585ff168SJulian Elischer 	for (;;) {
66781a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
668585ff168SJulian Elischer 		if (m == NULL)
669585ff168SJulian Elischer 			break;
670585ff168SJulian Elischer 		NG_FREE_M(m);
671585ff168SJulian Elischer 	}
672585ff168SJulian Elischer 	sc->queueOctets = 0;
673ab2e868cSEd Maste 	sc->last_packet = NULL;
674585ff168SJulian Elischer }
675585ff168SJulian Elischer 
676585ff168SJulian Elischer /*
677585ff168SJulian Elischer  * Start sending queued data out the output hook
678585ff168SJulian Elischer  */
679d8f5d037SGleb Smirnoff static int
680d8f5d037SGleb Smirnoff ng_source_start(sc_p sc, uint64_t packets)
681585ff168SJulian Elischer {
682d8f5d037SGleb Smirnoff 	if (sc->output_ifp == NULL) {
683d8f5d037SGleb Smirnoff 		printf("ng_source: start without iface configured\n");
684d8f5d037SGleb Smirnoff 		return (ENXIO);
685d8f5d037SGleb Smirnoff 	}
686d8f5d037SGleb Smirnoff 
687d8f5d037SGleb Smirnoff 	if (sc->node->nd_flags & NG_SOURCE_ACTIVE)
688d8f5d037SGleb Smirnoff 		return (EBUSY);
689d8f5d037SGleb Smirnoff 
690d8f5d037SGleb Smirnoff 	sc->node->nd_flags |= NG_SOURCE_ACTIVE;
691d8f5d037SGleb Smirnoff 
692d8f5d037SGleb Smirnoff 	sc->packets = packets;
693d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.elapsedTime);
694d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.endTime);
695d8f5d037SGleb Smirnoff 	getmicrotime(&sc->stats.startTime);
69672235857SGleb Smirnoff 	getmicrotime(&sc->stats.lastTime);
697d8f5d037SGleb Smirnoff 	ng_callout(&sc->intr_ch, sc->node, NULL, 0,
698d8f5d037SGleb Smirnoff 	    ng_source_intr, sc, 0);
699d8f5d037SGleb Smirnoff 
700d8f5d037SGleb Smirnoff 	return (0);
701585ff168SJulian Elischer }
702585ff168SJulian Elischer 
703585ff168SJulian Elischer /*
704585ff168SJulian Elischer  * Stop sending queued data out the output hook
705585ff168SJulian Elischer  */
706585ff168SJulian Elischer static void
707585ff168SJulian Elischer ng_source_stop(sc_p sc)
708585ff168SJulian Elischer {
709f9d9e1b4SGleb Smirnoff 	ng_uncallout(&sc->intr_ch, sc->node);
7105968e29eSJulian Elischer 	sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
711585ff168SJulian Elischer 	getmicrotime(&sc->stats.endTime);
712585ff168SJulian Elischer 	sc->stats.elapsedTime = sc->stats.endTime;
713585ff168SJulian Elischer 	timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
714585ff168SJulian Elischer }
715585ff168SJulian Elischer 
716585ff168SJulian Elischer /*
717585ff168SJulian Elischer  * While active called every NG_SOURCE_INTR_TICKS ticks.
718585ff168SJulian Elischer  * Sends as many packets as the interface connected to our
719585ff168SJulian Elischer  * output hook is able to enqueue.
720585ff168SJulian Elischer  */
721585ff168SJulian Elischer static void
722a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2)
723585ff168SJulian Elischer {
724a1adb510SHartmut Brandt 	sc_p sc = (sc_p)arg1;
725585ff168SJulian Elischer 	struct ifqueue *ifq;
726585ff168SJulian Elischer 	int packets;
727585ff168SJulian Elischer 
72876bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
729585ff168SJulian Elischer 
730d8f5d037SGleb Smirnoff 	if (sc->packets == 0 || sc->output == NULL
7315968e29eSJulian Elischer 	    || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
732585ff168SJulian Elischer 		ng_source_stop(sc);
733585ff168SJulian Elischer 		return;
734585ff168SJulian Elischer 	}
735585ff168SJulian Elischer 
736f5d15522SHartmut Brandt 	if (sc->output_ifp != NULL) {
7370572dfacSRuslan Ermilov 		ifq = (struct ifqueue *)&sc->output_ifp->if_snd;
738585ff168SJulian Elischer 		packets = ifq->ifq_maxlen - ifq->ifq_len;
739f5d15522SHartmut Brandt 	} else
740f5d15522SHartmut Brandt 		packets = sc->snd_queue.ifq_len;
741f5d15522SHartmut Brandt 
74272235857SGleb Smirnoff 	if (sc->stats.maxPps != 0) {
74372235857SGleb Smirnoff 		struct timeval	now, elapsed;
74472235857SGleb Smirnoff 		uint64_t	usec;
74572235857SGleb Smirnoff 		int		maxpkt;
74672235857SGleb Smirnoff 
74772235857SGleb Smirnoff 		getmicrotime(&now);
74872235857SGleb Smirnoff 		elapsed = now;
74972235857SGleb Smirnoff 		timevalsub(&elapsed, &sc->stats.lastTime);
75072235857SGleb Smirnoff 		usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec;
75172235857SGleb Smirnoff 		maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000;
75272235857SGleb Smirnoff 		sc->stats.lastTime = now;
75372235857SGleb Smirnoff 		if (packets > maxpkt)
75472235857SGleb Smirnoff 			packets = maxpkt;
75572235857SGleb Smirnoff 	}
75672235857SGleb Smirnoff 
757585ff168SJulian Elischer 	ng_source_send(sc, packets, NULL);
758a1adb510SHartmut Brandt 	if (sc->packets == 0)
759585ff168SJulian Elischer 		ng_source_stop(sc);
760a1adb510SHartmut Brandt 	else
761f9d9e1b4SGleb Smirnoff 		ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS,
762d312eaf5SGleb Smirnoff 		    ng_source_intr, sc, 0);
763585ff168SJulian Elischer }
764585ff168SJulian Elischer 
765585ff168SJulian Elischer /*
766205aefa3SGleb Smirnoff  * Send packets out our output hook.
767585ff168SJulian Elischer  */
768585ff168SJulian Elischer static int
769585ff168SJulian Elischer ng_source_send(sc_p sc, int tosend, int *sent_p)
770585ff168SJulian Elischer {
771585ff168SJulian Elischer 	struct mbuf *m, *m2;
772205aefa3SGleb Smirnoff 	int sent;
773585ff168SJulian Elischer 	int error = 0;
774585ff168SJulian Elischer 
77576bd5857SHartmut Brandt 	KASSERT(tosend >= 0, ("%s: negative tosend param", __func__));
7765968e29eSJulian Elischer 	KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
77776bd5857SHartmut Brandt 	    ("%s: inactive node", __func__));
778585ff168SJulian Elischer 
779d8f5d037SGleb Smirnoff 	if ((uint64_t)tosend > sc->packets)
780585ff168SJulian Elischer 		tosend = sc->packets;
781585ff168SJulian Elischer 
782205aefa3SGleb Smirnoff 	/* Go through the queue sending packets one by one. */
783585ff168SJulian Elischer 	for (sent = 0; error == 0 && sent < tosend; ++sent) {
78481a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
785585ff168SJulian Elischer 		if (m == NULL)
786585ff168SJulian Elischer 			break;
787585ff168SJulian Elischer 
7885f87dd69SEd Maste 		/* Duplicate and modify the packet. */
7895f87dd69SEd Maste 		error = ng_source_dup_mod(sc, m, &m2);
7905f87dd69SEd Maste 		if (error) {
7915f87dd69SEd Maste 			if (error == ENOBUFS)
79281a4ef81SHartmut Brandt 				_IF_PREPEND(&sc->snd_queue, m);
7935f87dd69SEd Maste 			else
7945f87dd69SEd Maste 				_IF_ENQUEUE(&sc->snd_queue, m);
795585ff168SJulian Elischer 			break;
796585ff168SJulian Elischer 		}
797585ff168SJulian Elischer 
798d8f5d037SGleb Smirnoff 		/* Re-enqueue the original packet for us. */
79981a4ef81SHartmut Brandt 		_IF_ENQUEUE(&sc->snd_queue, m);
800585ff168SJulian Elischer 
801585ff168SJulian Elischer 		sc->stats.outFrames++;
802585ff168SJulian Elischer 		sc->stats.outOctets += m2->m_pkthdr.len;
803d8f5d037SGleb Smirnoff 		NG_SEND_DATA_ONLY(error, sc->output, m2);
804a1adb510SHartmut Brandt 		if (error)
805205aefa3SGleb Smirnoff 			break;
806585ff168SJulian Elischer 	}
807585ff168SJulian Elischer 
808585ff168SJulian Elischer 	sc->packets -= sent;
809585ff168SJulian Elischer 	if (sent_p != NULL)
810585ff168SJulian Elischer 		*sent_p = sent;
811585ff168SJulian Elischer 	return (error);
812585ff168SJulian Elischer }
8135f87dd69SEd Maste 
8145f87dd69SEd Maste /*
8155f87dd69SEd Maste  * Modify packet in 'm' by changing 'len' bytes starting at 'offset'
8165f87dd69SEd Maste  * to data in 'cp'.
8175f87dd69SEd Maste  *
8185f87dd69SEd Maste  * The packet data in 'm' must be in a contiguous buffer in a single mbuf.
8195f87dd69SEd Maste  */
8205f87dd69SEd Maste static void
8215f87dd69SEd Maste ng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp,
8225f87dd69SEd Maste     int flags)
8235f87dd69SEd Maste {
8245f87dd69SEd Maste 	if (len == 0)
8255f87dd69SEd Maste 		return;
8265f87dd69SEd Maste 
8275f87dd69SEd Maste 	/* Can't modify beyond end of packet. */
8285f87dd69SEd Maste 	/* TODO: Pad packet for this case. */
8295f87dd69SEd Maste 	if (offset + len > m->m_len)
8305f87dd69SEd Maste 		return;
8315f87dd69SEd Maste 
8325f87dd69SEd Maste 	bcopy(cp, mtod_off(m, offset, caddr_t), len);
8335f87dd69SEd Maste }
8345f87dd69SEd Maste 
835577421ebSEd Maste static void
836577421ebSEd Maste ng_source_mod_counter(sc_p sc, struct ng_source_embed_cnt_info *cnt,
837577421ebSEd Maste     struct mbuf *m, int increment)
838577421ebSEd Maste {
839577421ebSEd Maste 	caddr_t cp;
840577421ebSEd Maste 	uint32_t val;
841577421ebSEd Maste 
842577421ebSEd Maste 	val = htonl(cnt->next_val);
843577421ebSEd Maste 	cp = (caddr_t)&val + sizeof(val) - cnt->width;
844577421ebSEd Maste 	ng_source_packet_mod(sc, m, cnt->offset, cnt->width, cp, cnt->flags);
845577421ebSEd Maste 
846577421ebSEd Maste 	if (increment) {
847577421ebSEd Maste 		cnt->next_val += increment;
848577421ebSEd Maste 
849577421ebSEd Maste 		if (increment > 0 && cnt->next_val > cnt->max_val) {
850577421ebSEd Maste 			cnt->next_val = cnt->min_val - 1 +
851577421ebSEd Maste 			    (cnt->next_val - cnt->max_val);
852577421ebSEd Maste 			if (cnt->next_val > cnt->max_val)
853577421ebSEd Maste 				cnt->next_val = cnt->max_val;
854577421ebSEd Maste 		} else if (increment < 0 && cnt->next_val < cnt->min_val) {
855577421ebSEd Maste 			cnt->next_val = cnt->max_val + 1 +
856577421ebSEd Maste 			    (cnt->next_val - cnt->min_val);
857577421ebSEd Maste 			if (cnt->next_val < cnt->min_val)
858577421ebSEd Maste 				cnt->next_val = cnt->max_val;
859577421ebSEd Maste 		}
860577421ebSEd Maste 	}
861577421ebSEd Maste }
862577421ebSEd Maste 
8635f87dd69SEd Maste static int
8645f87dd69SEd Maste ng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr)
8655f87dd69SEd Maste {
8665f87dd69SEd Maste 	struct mbuf *m;
867577421ebSEd Maste 	struct ng_source_embed_cnt_info *cnt;
8685f87dd69SEd Maste 	struct ng_source_embed_info *ts;
8695f87dd69SEd Maste 	int modify;
8705f87dd69SEd Maste 	int error = 0;
871577421ebSEd Maste 	int i, increment;
8725f87dd69SEd Maste 
8735f87dd69SEd Maste 	/* Are we going to modify packets? */
8745f87dd69SEd Maste 	modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE;
875577421ebSEd Maste 	for (i = 0; !modify && i < NG_SOURCE_COUNTERS; ++i)
876577421ebSEd Maste 		modify = sc->embed_counter[i].flags & NGM_SOURCE_EMBED_ENABLE;
8775f87dd69SEd Maste 
8785f87dd69SEd Maste 	/* Duplicate the packet. */
8795f87dd69SEd Maste 	if (modify)
8805f87dd69SEd Maste 		m = m_dup(m0, M_DONTWAIT);
8815f87dd69SEd Maste 	else
8825f87dd69SEd Maste 		m = m_copypacket(m0, M_DONTWAIT);
8835f87dd69SEd Maste 	if (m == NULL) {
8845f87dd69SEd Maste 		error = ENOBUFS;
8855f87dd69SEd Maste 		goto done;
8865f87dd69SEd Maste 	}
8875f87dd69SEd Maste 	*m_ptr = m;
8885f87dd69SEd Maste 
8895f87dd69SEd Maste 	if (!modify)
8905f87dd69SEd Maste 		goto done;
8915f87dd69SEd Maste 
8925f87dd69SEd Maste 	/* Modify the copied packet for sending. */
8935f87dd69SEd Maste 	KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__));
8945f87dd69SEd Maste 
895577421ebSEd Maste 	for (i = 0; i < NG_SOURCE_COUNTERS; ++i) {
896577421ebSEd Maste 		cnt = &sc->embed_counter[i];
897577421ebSEd Maste 		if (cnt->flags & NGM_SOURCE_EMBED_ENABLE) {
898577421ebSEd Maste 			if ((cnt->flags & NGM_SOURCE_INC_CNT_PER_LIST) == 0 ||
899577421ebSEd Maste 			    sc->last_packet == m0)
900577421ebSEd Maste 				increment = cnt->increment;
901577421ebSEd Maste 			else
902577421ebSEd Maste 				increment = 0;
903577421ebSEd Maste 			ng_source_mod_counter(sc, cnt, m, increment);
904577421ebSEd Maste 		}
905577421ebSEd Maste 	}
906577421ebSEd Maste 
9075f87dd69SEd Maste 	ts = &sc->embed_timestamp;
9085f87dd69SEd Maste 	if (ts->flags & NGM_SOURCE_EMBED_ENABLE) {
9095f87dd69SEd Maste 		struct timeval now;
9105f87dd69SEd Maste 		getmicrotime(&now);
9115f87dd69SEd Maste 		now.tv_sec = htonl(now.tv_sec);
9125f87dd69SEd Maste 		now.tv_usec = htonl(now.tv_usec);
9135f87dd69SEd Maste 		ng_source_packet_mod(sc, m, ts->offset, sizeof (now),
9145f87dd69SEd Maste 		    (caddr_t)&now, ts->flags);
9155f87dd69SEd Maste 	}
9165f87dd69SEd Maste 
9175f87dd69SEd Maste done:
9185f87dd69SEd Maste 	return(error);
9195f87dd69SEd Maste }
920