xref: /freebsd/sys/netgraph/ng_source.c (revision d642b94209ac7e676d988e08f8e2efad0827758b)
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
46053359b7SPedro F. Giffuni  * all data received 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 
805f87dd69SEd Maste #define	mtod_off(m,off,t)	((t)(mtod((m),caddr_t)+(off)))
815f87dd69SEd Maste 
82585ff168SJulian Elischer /* Per node info */
83585ff168SJulian Elischer struct privdata {
84585ff168SJulian Elischer 	node_p				node;
85d8f5d037SGleb Smirnoff 	hook_p				input;
86d8f5d037SGleb Smirnoff 	hook_p				output;
87585ff168SJulian Elischer 	struct ng_source_stats		stats;
884b52f283SJulian Elischer 	struct ifqueue			snd_queue;	/* packets to send */
89577421ebSEd Maste 	struct mbuf			*last_packet;	/* last pkt in queue */
90585ff168SJulian Elischer 	struct ifnet			*output_ifp;
9130bef41bSGleb Smirnoff 	struct callout			intr_ch;
92d8f5d037SGleb Smirnoff 	uint64_t			packets;	/* packets to send */
93d8f5d037SGleb Smirnoff 	uint32_t			queueOctets;
945f87dd69SEd Maste 	struct ng_source_embed_info	embed_timestamp;
95577421ebSEd Maste 	struct ng_source_embed_cnt_info	embed_counter[NG_SOURCE_COUNTERS];
96585ff168SJulian Elischer };
97585ff168SJulian Elischer typedef struct privdata *sc_p;
98585ff168SJulian Elischer 
99585ff168SJulian Elischer /* Node flags */
100585ff168SJulian Elischer #define NG_SOURCE_ACTIVE	(NGF_TYPE1)
101585ff168SJulian Elischer 
102585ff168SJulian Elischer /* Netgraph methods */
103585ff168SJulian Elischer static ng_constructor_t	ng_source_constructor;
104585ff168SJulian Elischer static ng_rcvmsg_t	ng_source_rcvmsg;
105585ff168SJulian Elischer static ng_shutdown_t	ng_source_rmnode;
106585ff168SJulian Elischer static ng_newhook_t	ng_source_newhook;
107d8f5d037SGleb Smirnoff static ng_connect_t	ng_source_connect;
108585ff168SJulian Elischer static ng_rcvdata_t	ng_source_rcvdata;
109585ff168SJulian Elischer static ng_disconnect_t	ng_source_disconnect;
110585ff168SJulian Elischer 
111585ff168SJulian Elischer /* Other functions */
112a1adb510SHartmut Brandt static void		ng_source_intr(node_p, hook_p, void *, int);
113585ff168SJulian Elischer static void		ng_source_clr_data (sc_p);
114d8f5d037SGleb Smirnoff static int		ng_source_start (sc_p, uint64_t);
115585ff168SJulian Elischer static void		ng_source_stop (sc_p);
116585ff168SJulian Elischer static int		ng_source_send (sc_p, int, int *);
117d8f5d037SGleb Smirnoff static int		ng_source_store_output_ifp(sc_p, char *);
1185f87dd69SEd Maste static void		ng_source_packet_mod(sc_p, struct mbuf *,
1195f87dd69SEd Maste 			    int, int, caddr_t, int);
120577421ebSEd Maste static void		ng_source_mod_counter(sc_p sc,
121577421ebSEd Maste 			    struct ng_source_embed_cnt_info *cnt,
122577421ebSEd Maste 			    struct mbuf *m, int increment);
1235f87dd69SEd Maste static int		ng_source_dup_mod(sc_p, struct mbuf *,
1245f87dd69SEd Maste 			    struct mbuf **);
125585ff168SJulian Elischer 
126585ff168SJulian Elischer /* Parse type for timeval */
12776bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = {
128*d642b942SEugene Grosbein #ifdef __LP64__
129*d642b942SEugene Grosbein 	{ "tv_sec",		&ng_parse_int64_type	},
130*d642b942SEugene Grosbein 	{ "tv_usec",		&ng_parse_int64_type	},
131*d642b942SEugene Grosbein #else
132585ff168SJulian Elischer 	{ "tv_sec",		&ng_parse_int32_type	},
133585ff168SJulian Elischer 	{ "tv_usec",		&ng_parse_int32_type	},
134*d642b942SEugene Grosbein #endif
135585ff168SJulian Elischer 	{ NULL }
136585ff168SJulian Elischer };
137585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = {
138585ff168SJulian Elischer 	&ng_parse_struct_type,
139585ff168SJulian Elischer 	&ng_source_timeval_type_fields
140585ff168SJulian Elischer };
141585ff168SJulian Elischer 
142585ff168SJulian Elischer /* Parse type for struct ng_source_stats */
143585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[]
144585ff168SJulian Elischer 	= NG_SOURCE_STATS_TYPE_INFO;
145585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = {
146585ff168SJulian Elischer 	&ng_parse_struct_type,
147585ff168SJulian Elischer 	&ng_source_stats_type_fields
148585ff168SJulian Elischer };
149585ff168SJulian Elischer 
1505f87dd69SEd Maste /* Parse type for struct ng_source_embed_info */
1515f87dd69SEd Maste static const struct ng_parse_struct_field ng_source_embed_type_fields[] =
1525f87dd69SEd Maste 	NG_SOURCE_EMBED_TYPE_INFO;
1535f87dd69SEd Maste static const struct ng_parse_type ng_source_embed_type = {
1545f87dd69SEd Maste 	&ng_parse_struct_type,
1555f87dd69SEd Maste 	&ng_source_embed_type_fields
1565f87dd69SEd Maste };
1575f87dd69SEd Maste 
158577421ebSEd Maste /* Parse type for struct ng_source_embed_cnt_info */
159577421ebSEd Maste static const struct ng_parse_struct_field ng_source_embed_cnt_type_fields[] =
160577421ebSEd Maste 	NG_SOURCE_EMBED_CNT_TYPE_INFO;
161577421ebSEd Maste static const struct ng_parse_type ng_source_embed_cnt_type = {
162577421ebSEd Maste 	&ng_parse_struct_type,
163577421ebSEd Maste 	&ng_source_embed_cnt_type_fields
164577421ebSEd Maste };
165577421ebSEd Maste 
166585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */
167585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = {
168585ff168SJulian Elischer 	{
169585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
170585ff168SJulian Elischer 	  NGM_SOURCE_GET_STATS,
171585ff168SJulian Elischer 	  "getstats",
172585ff168SJulian Elischer 	  NULL,
173585ff168SJulian Elischer 	  &ng_source_stats_type
174585ff168SJulian Elischer 	},
175585ff168SJulian Elischer 	{
176585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
177585ff168SJulian Elischer 	  NGM_SOURCE_CLR_STATS,
178585ff168SJulian Elischer 	  "clrstats",
179585ff168SJulian Elischer 	  NULL,
180585ff168SJulian Elischer 	  NULL
181585ff168SJulian Elischer 	},
182585ff168SJulian Elischer 	{
183585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
184585ff168SJulian Elischer 	  NGM_SOURCE_GETCLR_STATS,
185585ff168SJulian Elischer 	  "getclrstats",
186585ff168SJulian Elischer 	  NULL,
187585ff168SJulian Elischer 	  &ng_source_stats_type
188585ff168SJulian Elischer 	},
189585ff168SJulian Elischer 	{
190585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
191585ff168SJulian Elischer 	  NGM_SOURCE_START,
192585ff168SJulian Elischer 	  "start",
193585ff168SJulian Elischer 	  &ng_parse_uint64_type,
194585ff168SJulian Elischer 	  NULL
195585ff168SJulian Elischer 	},
196585ff168SJulian Elischer 	{
197585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
198585ff168SJulian Elischer 	  NGM_SOURCE_STOP,
199585ff168SJulian Elischer 	  "stop",
200585ff168SJulian Elischer 	  NULL,
201585ff168SJulian Elischer 	  NULL
202585ff168SJulian Elischer 	},
203585ff168SJulian Elischer 	{
204585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
205585ff168SJulian Elischer 	  NGM_SOURCE_CLR_DATA,
206585ff168SJulian Elischer 	  "clrdata",
207585ff168SJulian Elischer 	  NULL,
208585ff168SJulian Elischer 	  NULL
209585ff168SJulian Elischer 	},
210f5d15522SHartmut Brandt 	{
211f5d15522SHartmut Brandt 	  NGM_SOURCE_COOKIE,
212d8f5d037SGleb Smirnoff 	  NGM_SOURCE_SETIFACE,
213d8f5d037SGleb Smirnoff 	  "setiface",
214d8f5d037SGleb Smirnoff 	  &ng_parse_string_type,
215f5d15522SHartmut Brandt 	  NULL
216f5d15522SHartmut Brandt 	},
21772235857SGleb Smirnoff 	{
21872235857SGleb Smirnoff 	  NGM_SOURCE_COOKIE,
21972235857SGleb Smirnoff 	  NGM_SOURCE_SETPPS,
22072235857SGleb Smirnoff 	  "setpps",
22172235857SGleb Smirnoff 	  &ng_parse_uint32_type,
22272235857SGleb Smirnoff 	  NULL
22372235857SGleb Smirnoff 	},
2245f87dd69SEd Maste 	{
2255f87dd69SEd Maste 	  NGM_SOURCE_COOKIE,
2265f87dd69SEd Maste 	  NGM_SOURCE_SET_TIMESTAMP,
2275f87dd69SEd Maste 	  "settimestamp",
2285f87dd69SEd Maste 	  &ng_source_embed_type,
2295f87dd69SEd Maste 	  NULL
2305f87dd69SEd Maste 	},
2315f87dd69SEd Maste 	{
2325f87dd69SEd Maste 	  NGM_SOURCE_COOKIE,
2335f87dd69SEd Maste 	  NGM_SOURCE_GET_TIMESTAMP,
2345f87dd69SEd Maste 	  "gettimestamp",
2355f87dd69SEd Maste 	  NULL,
2365f87dd69SEd Maste 	  &ng_source_embed_type
2375f87dd69SEd Maste 	},
238577421ebSEd Maste 	{
239577421ebSEd Maste 	  NGM_SOURCE_COOKIE,
240577421ebSEd Maste 	  NGM_SOURCE_SET_COUNTER,
241577421ebSEd Maste 	  "setcounter",
242577421ebSEd Maste 	  &ng_source_embed_cnt_type,
243577421ebSEd Maste 	  NULL
244577421ebSEd Maste 	},
245577421ebSEd Maste 	{
246577421ebSEd Maste 	  NGM_SOURCE_COOKIE,
247577421ebSEd Maste 	  NGM_SOURCE_GET_COUNTER,
248577421ebSEd Maste 	  "getcounter",
249577421ebSEd Maste 	  &ng_parse_uint8_type,
250577421ebSEd Maste 	  &ng_source_embed_cnt_type
251577421ebSEd Maste 	},
252585ff168SJulian Elischer 	{ 0 }
253585ff168SJulian Elischer };
254585ff168SJulian Elischer 
255585ff168SJulian Elischer /* Netgraph type descriptor */
256585ff168SJulian Elischer static struct ng_type ng_source_typestruct = {
257f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
258f8aae777SJulian Elischer 	.name =		NG_SOURCE_NODE_TYPE,
259f8aae777SJulian Elischer 	.constructor =	ng_source_constructor,
260f8aae777SJulian Elischer 	.rcvmsg =	ng_source_rcvmsg,
261f8aae777SJulian Elischer 	.shutdown =	ng_source_rmnode,
262f8aae777SJulian Elischer 	.newhook =	ng_source_newhook,
263d8f5d037SGleb Smirnoff 	.connect =	ng_source_connect,
264f8aae777SJulian Elischer 	.rcvdata =	ng_source_rcvdata,
265f8aae777SJulian Elischer 	.disconnect =	ng_source_disconnect,
266f8aae777SJulian Elischer 	.cmdlist =	ng_source_cmds,
267585ff168SJulian Elischer };
268585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct);
269585ff168SJulian Elischer 
270d8f5d037SGleb Smirnoff static int ng_source_set_autosrc(sc_p, uint32_t);
2712cafef3eSHartmut Brandt 
272585ff168SJulian Elischer /*
273585ff168SJulian Elischer  * Node constructor
274585ff168SJulian Elischer  */
275585ff168SJulian Elischer static int
2765968e29eSJulian Elischer ng_source_constructor(node_p node)
277585ff168SJulian Elischer {
278585ff168SJulian Elischer 	sc_p sc;
279585ff168SJulian Elischer 
280674d86bfSGleb Smirnoff 	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
281585ff168SJulian Elischer 
2825968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, sc);
2835968e29eSJulian Elischer 	sc->node = node;
284585ff168SJulian Elischer 	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
28530bef41bSGleb Smirnoff 	ng_callout_init(&sc->intr_ch);
28630bef41bSGleb Smirnoff 
287585ff168SJulian Elischer 	return (0);
288585ff168SJulian Elischer }
289585ff168SJulian Elischer 
290585ff168SJulian Elischer /*
291585ff168SJulian Elischer  * Add a hook
292585ff168SJulian Elischer  */
293585ff168SJulian Elischer static int
294585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name)
295585ff168SJulian Elischer {
296d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
297585ff168SJulian Elischer 
298585ff168SJulian Elischer 	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
299d8f5d037SGleb Smirnoff 		sc->input = hook;
300585ff168SJulian Elischer 	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
301d8f5d037SGleb Smirnoff 		sc->output = hook;
302155d72c4SPedro F. Giffuni 		sc->output_ifp = NULL;
303585ff168SJulian Elischer 		bzero(&sc->stats, sizeof(sc->stats));
304585ff168SJulian Elischer 	} else
305585ff168SJulian Elischer 		return (EINVAL);
306d8f5d037SGleb Smirnoff 
307d8f5d037SGleb Smirnoff 	return (0);
308d8f5d037SGleb Smirnoff }
309d8f5d037SGleb Smirnoff 
310d8f5d037SGleb Smirnoff /*
311d8f5d037SGleb Smirnoff  * Hook has been added
312d8f5d037SGleb Smirnoff  */
313d8f5d037SGleb Smirnoff static int
314d8f5d037SGleb Smirnoff ng_source_connect(hook_p hook)
315d8f5d037SGleb Smirnoff {
316d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
317d8f5d037SGleb Smirnoff 	struct ng_mesg *msg;
318d8f5d037SGleb Smirnoff 	int dummy_error = 0;
319d8f5d037SGleb Smirnoff 
320d8f5d037SGleb Smirnoff 	/*
321d8f5d037SGleb Smirnoff 	 * If this is "output" hook, then request information
322d8f5d037SGleb Smirnoff 	 * from our downstream.
323d8f5d037SGleb Smirnoff 	 */
324d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
325d8f5d037SGleb Smirnoff 		NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME,
326d8f5d037SGleb Smirnoff 		    0, M_NOWAIT);
327d8f5d037SGleb Smirnoff 		if (msg == NULL)
328d8f5d037SGleb Smirnoff 			return (ENOBUFS);
329d8f5d037SGleb Smirnoff 
330d8f5d037SGleb Smirnoff 		/*
331d8f5d037SGleb Smirnoff 		 * Our hook and peer hook have HK_INVALID flag set,
332d8f5d037SGleb Smirnoff 		 * so we can't use NG_SEND_MSG_HOOK() macro here.
333d8f5d037SGleb Smirnoff 		 */
334d8f5d037SGleb Smirnoff 		NG_SEND_MSG_ID(dummy_error, sc->node, msg,
335d8f5d037SGleb Smirnoff 		    NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node));
336d8f5d037SGleb Smirnoff 	}
337d8f5d037SGleb Smirnoff 
338585ff168SJulian Elischer 	return (0);
339585ff168SJulian Elischer }
340585ff168SJulian Elischer 
341585ff168SJulian Elischer /*
342585ff168SJulian Elischer  * Receive a control message
343585ff168SJulian Elischer  */
344585ff168SJulian Elischer static int
3455968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
346585ff168SJulian Elischer {
347d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
348d8f5d037SGleb Smirnoff 	struct ng_mesg *msg, *resp = NULL;
349585ff168SJulian Elischer 	int error = 0;
350585ff168SJulian Elischer 
3515968e29eSJulian Elischer 	NGI_GET_MSG(item, msg);
352d8f5d037SGleb Smirnoff 
353585ff168SJulian Elischer 	switch (msg->header.typecookie) {
354585ff168SJulian Elischer 	case NGM_SOURCE_COOKIE:
355b655e33dSJulian Elischer 		if (msg->header.flags & NGF_RESP) {
356b655e33dSJulian Elischer 			error = EINVAL;
357b655e33dSJulian Elischer 			break;
358b655e33dSJulian Elischer 		}
359585ff168SJulian Elischer 		switch (msg->header.cmd) {
360585ff168SJulian Elischer 		case NGM_SOURCE_GET_STATS:
361585ff168SJulian Elischer 		case NGM_SOURCE_CLR_STATS:
362585ff168SJulian Elischer 		case NGM_SOURCE_GETCLR_STATS:
363585ff168SJulian Elischer                     {
364585ff168SJulian Elischer 			struct ng_source_stats *stats;
365585ff168SJulian Elischer 
366585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
367585ff168SJulian Elischer                                 NG_MKRESPONSE(resp, msg,
368585ff168SJulian Elischer                                     sizeof(*stats), M_NOWAIT);
369585ff168SJulian Elischer 				if (resp == NULL) {
370585ff168SJulian Elischer 					error = ENOMEM;
371585ff168SJulian Elischer 					goto done;
372585ff168SJulian Elischer 				}
373585ff168SJulian Elischer 				sc->stats.queueOctets = sc->queueOctets;
3744b52f283SJulian Elischer 				sc->stats.queueFrames = sc->snd_queue.ifq_len;
3755968e29eSJulian Elischer 				if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
376585ff168SJulian Elischer 				    && !timevalisset(&sc->stats.endTime)) {
377585ff168SJulian Elischer 					getmicrotime(&sc->stats.elapsedTime);
378585ff168SJulian Elischer 					timevalsub(&sc->stats.elapsedTime,
379585ff168SJulian Elischer 					    &sc->stats.startTime);
380585ff168SJulian Elischer 				}
3814b52f283SJulian Elischer 				stats = (struct ng_source_stats *)resp->data;
382585ff168SJulian Elischer 				bcopy(&sc->stats, stats, sizeof(* stats));
383585ff168SJulian Elischer                         }
384585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_GET_STATS)
385585ff168SJulian Elischer 				bzero(&sc->stats, sizeof(sc->stats));
386585ff168SJulian Elischer 		    }
387585ff168SJulian Elischer 		    break;
388585ff168SJulian Elischer 		case NGM_SOURCE_START:
389585ff168SJulian Elischer 		    {
390d8f5d037SGleb Smirnoff 			uint64_t packets;
391f5d15522SHartmut Brandt 
392d8f5d037SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint64_t)) {
393d8f5d037SGleb Smirnoff 				error = EINVAL;
394f5d15522SHartmut Brandt 				break;
395d8f5d037SGleb Smirnoff 			}
396d8f5d037SGleb Smirnoff 
397d8f5d037SGleb Smirnoff 			packets = *(uint64_t *)msg->data;
398d8f5d037SGleb Smirnoff 
399d8f5d037SGleb Smirnoff 			error = ng_source_start(sc, packets);
400d8f5d037SGleb Smirnoff 
401d8f5d037SGleb Smirnoff 		    	break;
402d8f5d037SGleb Smirnoff 		    }
403585ff168SJulian Elischer 		case NGM_SOURCE_STOP:
404585ff168SJulian Elischer 			ng_source_stop(sc);
405585ff168SJulian Elischer 			break;
406585ff168SJulian Elischer 		case NGM_SOURCE_CLR_DATA:
407585ff168SJulian Elischer 			ng_source_clr_data(sc);
408585ff168SJulian Elischer 			break;
409d8f5d037SGleb Smirnoff 		case NGM_SOURCE_SETIFACE:
410d8f5d037SGleb Smirnoff 		    {
411d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
412d8f5d037SGleb Smirnoff 
413d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
414d8f5d037SGleb Smirnoff 				error = EINVAL;
415d8f5d037SGleb Smirnoff 				break;
416d8f5d037SGleb Smirnoff 			}
417d8f5d037SGleb Smirnoff 
418d8f5d037SGleb Smirnoff 			ng_source_store_output_ifp(sc, ifname);
419d8f5d037SGleb Smirnoff 			break;
420d8f5d037SGleb Smirnoff 		    }
42172235857SGleb Smirnoff 		case NGM_SOURCE_SETPPS:
42272235857SGleb Smirnoff 		    {
42372235857SGleb Smirnoff 			uint32_t pps;
42472235857SGleb Smirnoff 
42572235857SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint32_t)) {
42672235857SGleb Smirnoff 				error = EINVAL;
42772235857SGleb Smirnoff 				break;
42872235857SGleb Smirnoff 			}
42972235857SGleb Smirnoff 
43072235857SGleb Smirnoff 			pps = *(uint32_t *)msg->data;
43172235857SGleb Smirnoff 
43272235857SGleb Smirnoff 			sc->stats.maxPps = pps;
43372235857SGleb Smirnoff 
43472235857SGleb Smirnoff 			break;
43572235857SGleb Smirnoff 		    }
4365f87dd69SEd Maste 		case NGM_SOURCE_SET_TIMESTAMP:
4375f87dd69SEd Maste 		    {
4385f87dd69SEd Maste 			struct ng_source_embed_info *embed;
4395f87dd69SEd Maste 
440ab2e868cSEd Maste 			if (msg->header.arglen != sizeof(*embed)) {
441ab2e868cSEd Maste 				error = EINVAL;
442ab2e868cSEd Maste 				goto done;
443ab2e868cSEd Maste 			}
4445f87dd69SEd Maste 			embed = (struct ng_source_embed_info *)msg->data;
4455f87dd69SEd Maste 			bcopy(embed, &sc->embed_timestamp, sizeof(*embed));
4465f87dd69SEd Maste 
4475f87dd69SEd Maste 			break;
4485f87dd69SEd Maste 		    }
4495f87dd69SEd Maste 		case NGM_SOURCE_GET_TIMESTAMP:
4505f87dd69SEd Maste 		    {
4515f87dd69SEd Maste 			struct ng_source_embed_info *embed;
4525f87dd69SEd Maste 
453eb1b1807SGleb Smirnoff 			NG_MKRESPONSE(resp, msg, sizeof(*embed), M_NOWAIT);
4545f87dd69SEd Maste 			if (resp == NULL) {
4555f87dd69SEd Maste 				error = ENOMEM;
4565f87dd69SEd Maste 				goto done;
4575f87dd69SEd Maste 			}
4585f87dd69SEd Maste 			embed = (struct ng_source_embed_info *)resp->data;
4595f87dd69SEd Maste 			bcopy(&sc->embed_timestamp, embed, sizeof(*embed));
4605f87dd69SEd Maste 
4615f87dd69SEd Maste 			break;
4625f87dd69SEd Maste 		    }
463577421ebSEd Maste 		case NGM_SOURCE_SET_COUNTER:
464577421ebSEd Maste 		    {
465577421ebSEd Maste 			struct ng_source_embed_cnt_info *embed;
466577421ebSEd Maste 
467ab2e868cSEd Maste 			if (msg->header.arglen != sizeof(*embed)) {
468ab2e868cSEd Maste 				error = EINVAL;
469ab2e868cSEd Maste 				goto done;
470ab2e868cSEd Maste 			}
471577421ebSEd Maste 			embed = (struct ng_source_embed_cnt_info *)msg->data;
472577421ebSEd Maste 			if (embed->index >= NG_SOURCE_COUNTERS ||
473577421ebSEd Maste 			    !(embed->width == 1 || embed->width == 2 ||
474577421ebSEd Maste 			    embed->width == 4)) {
475577421ebSEd Maste 				error = EINVAL;
476577421ebSEd Maste 				goto done;
477577421ebSEd Maste 			}
478577421ebSEd Maste 			bcopy(embed, &sc->embed_counter[embed->index],
479577421ebSEd Maste 			    sizeof(*embed));
480577421ebSEd Maste 
481577421ebSEd Maste 			break;
482577421ebSEd Maste 		    }
483577421ebSEd Maste 		case NGM_SOURCE_GET_COUNTER:
484577421ebSEd Maste 		    {
485577421ebSEd Maste 			uint8_t index = *(uint8_t *)msg->data;
486577421ebSEd Maste 			struct ng_source_embed_cnt_info *embed;
487577421ebSEd Maste 
488577421ebSEd Maste 			if (index >= NG_SOURCE_COUNTERS) {
489577421ebSEd Maste 				error = EINVAL;
490577421ebSEd Maste 				goto done;
491577421ebSEd Maste 			}
492eb1b1807SGleb Smirnoff 			NG_MKRESPONSE(resp, msg, sizeof(*embed), M_NOWAIT);
493577421ebSEd Maste 			if (resp == NULL) {
494577421ebSEd Maste 				error = ENOMEM;
495577421ebSEd Maste 				goto done;
496577421ebSEd Maste 			}
497577421ebSEd Maste 			embed = (struct ng_source_embed_cnt_info *)resp->data;
498577421ebSEd Maste 			bcopy(&sc->embed_counter[index], embed, sizeof(*embed));
499577421ebSEd Maste 
500577421ebSEd Maste 			break;
501577421ebSEd Maste 		    }
502585ff168SJulian Elischer 		default:
503585ff168SJulian Elischer 			error = EINVAL;
504585ff168SJulian Elischer 			break;
505585ff168SJulian Elischer 		}
506585ff168SJulian Elischer 		break;
507b655e33dSJulian Elischer 	case NGM_ETHER_COOKIE:
508b655e33dSJulian Elischer 		if (!(msg->header.flags & NGF_RESP)) {
509b655e33dSJulian Elischer 			error = EINVAL;
510b655e33dSJulian Elischer 			break;
511b655e33dSJulian Elischer 		}
512b655e33dSJulian Elischer 		switch (msg->header.cmd) {
513d8f5d037SGleb Smirnoff 		case NGM_ETHER_GET_IFNAME:
514d8f5d037SGleb Smirnoff 		    {
515d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
516d8f5d037SGleb Smirnoff 
517d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
518d8f5d037SGleb Smirnoff 				error = EINVAL;
519b655e33dSJulian Elischer 				break;
520d8f5d037SGleb Smirnoff 			}
521d8f5d037SGleb Smirnoff 
522d8f5d037SGleb Smirnoff 			if (ng_source_store_output_ifp(sc, ifname) == 0)
523d8f5d037SGleb Smirnoff 				ng_source_set_autosrc(sc, 0);
524d8f5d037SGleb Smirnoff 			break;
525d8f5d037SGleb Smirnoff 		    }
526b655e33dSJulian Elischer 		default:
527b655e33dSJulian Elischer 			error = EINVAL;
528b655e33dSJulian Elischer 		}
529b655e33dSJulian Elischer 		break;
530585ff168SJulian Elischer 	default:
531585ff168SJulian Elischer 		error = EINVAL;
532585ff168SJulian Elischer 		break;
533585ff168SJulian Elischer 	}
534585ff168SJulian Elischer 
535585ff168SJulian Elischer done:
536d8f5d037SGleb Smirnoff 	/* Take care of synchronous response, if any. */
5375968e29eSJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
538d8f5d037SGleb Smirnoff 	/* Free the message and return. */
5395968e29eSJulian Elischer 	NG_FREE_MSG(msg);
540585ff168SJulian Elischer 	return (error);
541585ff168SJulian Elischer }
542585ff168SJulian Elischer 
543585ff168SJulian Elischer /*
544585ff168SJulian Elischer  * Receive data on a hook
545585ff168SJulian Elischer  *
546585ff168SJulian Elischer  * If data comes in the input hook, enqueue it on the send queue.
547585ff168SJulian Elischer  * If data comes in the output hook, discard it.
548585ff168SJulian Elischer  */
549585ff168SJulian Elischer static int
5505968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item)
551585ff168SJulian Elischer {
552d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
5535968e29eSJulian Elischer 	struct mbuf *m;
554d8f5d037SGleb Smirnoff 	int error = 0;
555585ff168SJulian Elischer 
5565968e29eSJulian Elischer 	NGI_GET_M(item, m);
5575968e29eSJulian Elischer 	NG_FREE_ITEM(item);
558585ff168SJulian Elischer 
559585ff168SJulian Elischer 	/* Which hook? */
560d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
561585ff168SJulian Elischer 		/* discard */
5625968e29eSJulian Elischer 		NG_FREE_M(m);
563585ff168SJulian Elischer 		return (error);
564585ff168SJulian Elischer 	}
565d8f5d037SGleb Smirnoff 	KASSERT(hook == sc->input, ("%s: no hook!", __func__));
566585ff168SJulian Elischer 
567d8f5d037SGleb Smirnoff 	/* Enqueue packet. */
568585ff168SJulian Elischer 	/* XXX should we check IF_QFULL() ? */
56981a4ef81SHartmut Brandt 	_IF_ENQUEUE(&sc->snd_queue, m);
570585ff168SJulian Elischer 	sc->queueOctets += m->m_pkthdr.len;
571577421ebSEd Maste 	sc->last_packet = m;
572585ff168SJulian Elischer 
573585ff168SJulian Elischer 	return (0);
574585ff168SJulian Elischer }
575585ff168SJulian Elischer 
576585ff168SJulian Elischer /*
577585ff168SJulian Elischer  * Shutdown processing
578585ff168SJulian Elischer  */
579585ff168SJulian Elischer static int
580585ff168SJulian Elischer ng_source_rmnode(node_p node)
581585ff168SJulian Elischer {
582d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
583585ff168SJulian Elischer 
584585ff168SJulian Elischer 	ng_source_stop(sc);
585585ff168SJulian Elischer 	ng_source_clr_data(sc);
5865968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
5875968e29eSJulian Elischer 	NG_NODE_UNREF(node);
58876bd5857SHartmut Brandt 	free(sc, M_NETGRAPH);
589d8f5d037SGleb Smirnoff 
590585ff168SJulian Elischer 	return (0);
591585ff168SJulian Elischer }
592585ff168SJulian Elischer 
593585ff168SJulian Elischer /*
594585ff168SJulian Elischer  * Hook disconnection
595585ff168SJulian Elischer  */
596585ff168SJulian Elischer static int
597585ff168SJulian Elischer ng_source_disconnect(hook_p hook)
598585ff168SJulian Elischer {
5994b52f283SJulian Elischer 	sc_p sc;
600585ff168SJulian Elischer 
6015968e29eSJulian Elischer 	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
60276bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
603d8f5d037SGleb Smirnoff 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output)
6045968e29eSJulian Elischer 		ng_rmnode_self(NG_HOOK_NODE(hook));
605585ff168SJulian Elischer 	return (0);
606585ff168SJulian Elischer }
607585ff168SJulian Elischer 
608585ff168SJulian Elischer /*
6096bccea7cSRebecca Cran  * Set sc->output_ifp to point to the struct ifnet of the interface
610b655e33dSJulian Elischer  * reached via our output hook.
611b655e33dSJulian Elischer  */
612b655e33dSJulian Elischer static int
613d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc_p sc, char *ifname)
614b655e33dSJulian Elischer {
615b655e33dSJulian Elischer 	struct ifnet *ifp;
616b655e33dSJulian Elischer 
617d8f5d037SGleb Smirnoff 	ifp = ifunit(ifname);
618585ff168SJulian Elischer 
619585ff168SJulian Elischer 	if (ifp == NULL) {
6203ac12c59SMarko Zec 		printf("%s: can't find interface %s\n", __func__, ifname);
621585ff168SJulian Elischer 		return (EINVAL);
622585ff168SJulian Elischer 	}
623585ff168SJulian Elischer 	sc->output_ifp = ifp;
624585ff168SJulian Elischer 
625585ff168SJulian Elischer #if 1
626585ff168SJulian Elischer 	/* XXX mucking with a drivers ifqueue size is ugly but we need it
627585ff168SJulian Elischer 	 * to queue a lot of packets to get close to line rate on a gigabit
628585ff168SJulian Elischer 	 * interface with small packets.
629585ff168SJulian Elischer 	 * XXX we should restore the original value at stop or disconnect
630585ff168SJulian Elischer 	 */
63176bd5857SHartmut Brandt 	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) {
632585ff168SJulian Elischer 		printf("ng_source: changing ifq_maxlen from %d to %d\n",
6334b52f283SJulian Elischer 		    ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
634585ff168SJulian Elischer 		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
635585ff168SJulian Elischer 	}
636585ff168SJulian Elischer #endif
637585ff168SJulian Elischer 	return (0);
638585ff168SJulian Elischer }
639585ff168SJulian Elischer 
640585ff168SJulian Elischer /*
641585ff168SJulian Elischer  * Set the attached ethernet node's ethernet source address override flag.
642585ff168SJulian Elischer  */
643585ff168SJulian Elischer static int
644d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc_p sc, uint32_t flag)
645585ff168SJulian Elischer {
646585ff168SJulian Elischer 	struct ng_mesg *msg;
647585ff168SJulian Elischer 	int error = 0;
648585ff168SJulian Elischer 
649585ff168SJulian Elischer 	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
650d8f5d037SGleb Smirnoff 	    sizeof (uint32_t), M_NOWAIT);
651585ff168SJulian Elischer 	if (msg == NULL)
652585ff168SJulian Elischer 		return(ENOBUFS);
653585ff168SJulian Elischer 
654d8f5d037SGleb Smirnoff 	*(uint32_t *)msg->data = flag;
655d8f5d037SGleb Smirnoff 	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0);
656585ff168SJulian Elischer 	return (error);
657585ff168SJulian Elischer }
658585ff168SJulian Elischer 
659585ff168SJulian Elischer /*
660585ff168SJulian Elischer  * Clear out the data we've queued
661585ff168SJulian Elischer  */
662585ff168SJulian Elischer static void
663585ff168SJulian Elischer ng_source_clr_data (sc_p sc)
664585ff168SJulian Elischer {
665585ff168SJulian Elischer 	struct mbuf *m;
666585ff168SJulian Elischer 
667585ff168SJulian Elischer 	for (;;) {
66881a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
669585ff168SJulian Elischer 		if (m == NULL)
670585ff168SJulian Elischer 			break;
671585ff168SJulian Elischer 		NG_FREE_M(m);
672585ff168SJulian Elischer 	}
673585ff168SJulian Elischer 	sc->queueOctets = 0;
674ab2e868cSEd Maste 	sc->last_packet = NULL;
675585ff168SJulian Elischer }
676585ff168SJulian Elischer 
677585ff168SJulian Elischer /*
678585ff168SJulian Elischer  * Start sending queued data out the output hook
679585ff168SJulian Elischer  */
680d8f5d037SGleb Smirnoff static int
681d8f5d037SGleb Smirnoff ng_source_start(sc_p sc, uint64_t packets)
682585ff168SJulian Elischer {
683d8f5d037SGleb Smirnoff 	if (sc->output_ifp == NULL) {
684d8f5d037SGleb Smirnoff 		printf("ng_source: start without iface configured\n");
685d8f5d037SGleb Smirnoff 		return (ENXIO);
686d8f5d037SGleb Smirnoff 	}
687d8f5d037SGleb Smirnoff 
688d8f5d037SGleb Smirnoff 	if (sc->node->nd_flags & NG_SOURCE_ACTIVE)
689d8f5d037SGleb Smirnoff 		return (EBUSY);
690d8f5d037SGleb Smirnoff 
691d8f5d037SGleb Smirnoff 	sc->node->nd_flags |= NG_SOURCE_ACTIVE;
692d8f5d037SGleb Smirnoff 
693d8f5d037SGleb Smirnoff 	sc->packets = packets;
694d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.elapsedTime);
695d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.endTime);
696d8f5d037SGleb Smirnoff 	getmicrotime(&sc->stats.startTime);
69772235857SGleb Smirnoff 	getmicrotime(&sc->stats.lastTime);
698d8f5d037SGleb Smirnoff 	ng_callout(&sc->intr_ch, sc->node, NULL, 0,
699d8f5d037SGleb Smirnoff 	    ng_source_intr, sc, 0);
700d8f5d037SGleb Smirnoff 
701d8f5d037SGleb Smirnoff 	return (0);
702585ff168SJulian Elischer }
703585ff168SJulian Elischer 
704585ff168SJulian Elischer /*
705585ff168SJulian Elischer  * Stop sending queued data out the output hook
706585ff168SJulian Elischer  */
707585ff168SJulian Elischer static void
708585ff168SJulian Elischer ng_source_stop(sc_p sc)
709585ff168SJulian Elischer {
710f9d9e1b4SGleb Smirnoff 	ng_uncallout(&sc->intr_ch, sc->node);
7115968e29eSJulian Elischer 	sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
712585ff168SJulian Elischer 	getmicrotime(&sc->stats.endTime);
713585ff168SJulian Elischer 	sc->stats.elapsedTime = sc->stats.endTime;
714585ff168SJulian Elischer 	timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
715585ff168SJulian Elischer }
716585ff168SJulian Elischer 
717585ff168SJulian Elischer /*
718585ff168SJulian Elischer  * While active called every NG_SOURCE_INTR_TICKS ticks.
719585ff168SJulian Elischer  * Sends as many packets as the interface connected to our
720585ff168SJulian Elischer  * output hook is able to enqueue.
721585ff168SJulian Elischer  */
722585ff168SJulian Elischer static void
723a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2)
724585ff168SJulian Elischer {
725a1adb510SHartmut Brandt 	sc_p sc = (sc_p)arg1;
726585ff168SJulian Elischer 	struct ifqueue *ifq;
727585ff168SJulian Elischer 	int packets;
728585ff168SJulian Elischer 
72976bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
730585ff168SJulian Elischer 
731d8f5d037SGleb Smirnoff 	if (sc->packets == 0 || sc->output == NULL
7325968e29eSJulian Elischer 	    || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
733585ff168SJulian Elischer 		ng_source_stop(sc);
734585ff168SJulian Elischer 		return;
735585ff168SJulian Elischer 	}
736585ff168SJulian Elischer 
737f5d15522SHartmut Brandt 	if (sc->output_ifp != NULL) {
7380572dfacSRuslan Ermilov 		ifq = (struct ifqueue *)&sc->output_ifp->if_snd;
739585ff168SJulian Elischer 		packets = ifq->ifq_maxlen - ifq->ifq_len;
740f5d15522SHartmut Brandt 	} else
741f5d15522SHartmut Brandt 		packets = sc->snd_queue.ifq_len;
742f5d15522SHartmut Brandt 
74372235857SGleb Smirnoff 	if (sc->stats.maxPps != 0) {
74472235857SGleb Smirnoff 		struct timeval	now, elapsed;
74572235857SGleb Smirnoff 		uint64_t	usec;
74672235857SGleb Smirnoff 		int		maxpkt;
74772235857SGleb Smirnoff 
74872235857SGleb Smirnoff 		getmicrotime(&now);
74972235857SGleb Smirnoff 		elapsed = now;
75072235857SGleb Smirnoff 		timevalsub(&elapsed, &sc->stats.lastTime);
75172235857SGleb Smirnoff 		usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec;
75272235857SGleb Smirnoff 		maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000;
75372235857SGleb Smirnoff 		sc->stats.lastTime = now;
75472235857SGleb Smirnoff 		if (packets > maxpkt)
75572235857SGleb Smirnoff 			packets = maxpkt;
75672235857SGleb Smirnoff 	}
75772235857SGleb Smirnoff 
758585ff168SJulian Elischer 	ng_source_send(sc, packets, NULL);
759a1adb510SHartmut Brandt 	if (sc->packets == 0)
760585ff168SJulian Elischer 		ng_source_stop(sc);
761a1adb510SHartmut Brandt 	else
762f9d9e1b4SGleb Smirnoff 		ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS,
763d312eaf5SGleb Smirnoff 		    ng_source_intr, sc, 0);
764585ff168SJulian Elischer }
765585ff168SJulian Elischer 
766585ff168SJulian Elischer /*
767205aefa3SGleb Smirnoff  * Send packets out our output hook.
768585ff168SJulian Elischer  */
769585ff168SJulian Elischer static int
770585ff168SJulian Elischer ng_source_send(sc_p sc, int tosend, int *sent_p)
771585ff168SJulian Elischer {
772585ff168SJulian Elischer 	struct mbuf *m, *m2;
773205aefa3SGleb Smirnoff 	int sent;
774585ff168SJulian Elischer 	int error = 0;
775585ff168SJulian Elischer 
77676bd5857SHartmut Brandt 	KASSERT(tosend >= 0, ("%s: negative tosend param", __func__));
7775968e29eSJulian Elischer 	KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
77876bd5857SHartmut Brandt 	    ("%s: inactive node", __func__));
779585ff168SJulian Elischer 
780d8f5d037SGleb Smirnoff 	if ((uint64_t)tosend > sc->packets)
781585ff168SJulian Elischer 		tosend = sc->packets;
782585ff168SJulian Elischer 
783205aefa3SGleb Smirnoff 	/* Go through the queue sending packets one by one. */
784585ff168SJulian Elischer 	for (sent = 0; error == 0 && sent < tosend; ++sent) {
78581a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
786585ff168SJulian Elischer 		if (m == NULL)
787585ff168SJulian Elischer 			break;
788585ff168SJulian Elischer 
7895f87dd69SEd Maste 		/* Duplicate and modify the packet. */
7905f87dd69SEd Maste 		error = ng_source_dup_mod(sc, m, &m2);
7915f87dd69SEd Maste 		if (error) {
7925f87dd69SEd Maste 			if (error == ENOBUFS)
79381a4ef81SHartmut Brandt 				_IF_PREPEND(&sc->snd_queue, m);
7945f87dd69SEd Maste 			else
7955f87dd69SEd Maste 				_IF_ENQUEUE(&sc->snd_queue, m);
796585ff168SJulian Elischer 			break;
797585ff168SJulian Elischer 		}
798585ff168SJulian Elischer 
799d8f5d037SGleb Smirnoff 		/* Re-enqueue the original packet for us. */
80081a4ef81SHartmut Brandt 		_IF_ENQUEUE(&sc->snd_queue, m);
801585ff168SJulian Elischer 
802585ff168SJulian Elischer 		sc->stats.outFrames++;
803585ff168SJulian Elischer 		sc->stats.outOctets += m2->m_pkthdr.len;
804d8f5d037SGleb Smirnoff 		NG_SEND_DATA_ONLY(error, sc->output, m2);
805a1adb510SHartmut Brandt 		if (error)
806205aefa3SGleb Smirnoff 			break;
807585ff168SJulian Elischer 	}
808585ff168SJulian Elischer 
809585ff168SJulian Elischer 	sc->packets -= sent;
810585ff168SJulian Elischer 	if (sent_p != NULL)
811585ff168SJulian Elischer 		*sent_p = sent;
812585ff168SJulian Elischer 	return (error);
813585ff168SJulian Elischer }
8145f87dd69SEd Maste 
8155f87dd69SEd Maste /*
8165f87dd69SEd Maste  * Modify packet in 'm' by changing 'len' bytes starting at 'offset'
8175f87dd69SEd Maste  * to data in 'cp'.
8185f87dd69SEd Maste  *
8195f87dd69SEd Maste  * The packet data in 'm' must be in a contiguous buffer in a single mbuf.
8205f87dd69SEd Maste  */
8215f87dd69SEd Maste static void
8225f87dd69SEd Maste ng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp,
8235f87dd69SEd Maste     int flags)
8245f87dd69SEd Maste {
8255f87dd69SEd Maste 	if (len == 0)
8265f87dd69SEd Maste 		return;
8275f87dd69SEd Maste 
8285f87dd69SEd Maste 	/* Can't modify beyond end of packet. */
8295f87dd69SEd Maste 	/* TODO: Pad packet for this case. */
8305f87dd69SEd Maste 	if (offset + len > m->m_len)
8315f87dd69SEd Maste 		return;
8325f87dd69SEd Maste 
8335f87dd69SEd Maste 	bcopy(cp, mtod_off(m, offset, caddr_t), len);
8345f87dd69SEd Maste }
8355f87dd69SEd Maste 
836577421ebSEd Maste static void
837577421ebSEd Maste ng_source_mod_counter(sc_p sc, struct ng_source_embed_cnt_info *cnt,
838577421ebSEd Maste     struct mbuf *m, int increment)
839577421ebSEd Maste {
840577421ebSEd Maste 	caddr_t cp;
841577421ebSEd Maste 	uint32_t val;
842577421ebSEd Maste 
843577421ebSEd Maste 	val = htonl(cnt->next_val);
844577421ebSEd Maste 	cp = (caddr_t)&val + sizeof(val) - cnt->width;
845577421ebSEd Maste 	ng_source_packet_mod(sc, m, cnt->offset, cnt->width, cp, cnt->flags);
846577421ebSEd Maste 
847577421ebSEd Maste 	if (increment) {
848577421ebSEd Maste 		cnt->next_val += increment;
849577421ebSEd Maste 
850577421ebSEd Maste 		if (increment > 0 && cnt->next_val > cnt->max_val) {
851577421ebSEd Maste 			cnt->next_val = cnt->min_val - 1 +
852577421ebSEd Maste 			    (cnt->next_val - cnt->max_val);
853577421ebSEd Maste 			if (cnt->next_val > cnt->max_val)
854577421ebSEd Maste 				cnt->next_val = cnt->max_val;
855577421ebSEd Maste 		} else if (increment < 0 && cnt->next_val < cnt->min_val) {
856577421ebSEd Maste 			cnt->next_val = cnt->max_val + 1 +
857577421ebSEd Maste 			    (cnt->next_val - cnt->min_val);
858577421ebSEd Maste 			if (cnt->next_val < cnt->min_val)
859577421ebSEd Maste 				cnt->next_val = cnt->max_val;
860577421ebSEd Maste 		}
861577421ebSEd Maste 	}
862577421ebSEd Maste }
863577421ebSEd Maste 
8645f87dd69SEd Maste static int
8655f87dd69SEd Maste ng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr)
8665f87dd69SEd Maste {
8675f87dd69SEd Maste 	struct mbuf *m;
868577421ebSEd Maste 	struct ng_source_embed_cnt_info *cnt;
8695f87dd69SEd Maste 	struct ng_source_embed_info *ts;
8705f87dd69SEd Maste 	int modify;
8715f87dd69SEd Maste 	int error = 0;
872577421ebSEd Maste 	int i, increment;
8735f87dd69SEd Maste 
8745f87dd69SEd Maste 	/* Are we going to modify packets? */
8755f87dd69SEd Maste 	modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE;
876577421ebSEd Maste 	for (i = 0; !modify && i < NG_SOURCE_COUNTERS; ++i)
877577421ebSEd Maste 		modify = sc->embed_counter[i].flags & NGM_SOURCE_EMBED_ENABLE;
8785f87dd69SEd Maste 
8795f87dd69SEd Maste 	/* Duplicate the packet. */
8805f87dd69SEd Maste 	if (modify)
881eb1b1807SGleb Smirnoff 		m = m_dup(m0, M_NOWAIT);
8825f87dd69SEd Maste 	else
883eb1b1807SGleb Smirnoff 		m = m_copypacket(m0, M_NOWAIT);
8845f87dd69SEd Maste 	if (m == NULL) {
8855f87dd69SEd Maste 		error = ENOBUFS;
8865f87dd69SEd Maste 		goto done;
8875f87dd69SEd Maste 	}
8885f87dd69SEd Maste 	*m_ptr = m;
8895f87dd69SEd Maste 
8905f87dd69SEd Maste 	if (!modify)
8915f87dd69SEd Maste 		goto done;
8925f87dd69SEd Maste 
8935f87dd69SEd Maste 	/* Modify the copied packet for sending. */
8945f87dd69SEd Maste 	KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__));
8955f87dd69SEd Maste 
896577421ebSEd Maste 	for (i = 0; i < NG_SOURCE_COUNTERS; ++i) {
897577421ebSEd Maste 		cnt = &sc->embed_counter[i];
898577421ebSEd Maste 		if (cnt->flags & NGM_SOURCE_EMBED_ENABLE) {
899577421ebSEd Maste 			if ((cnt->flags & NGM_SOURCE_INC_CNT_PER_LIST) == 0 ||
900577421ebSEd Maste 			    sc->last_packet == m0)
901577421ebSEd Maste 				increment = cnt->increment;
902577421ebSEd Maste 			else
903577421ebSEd Maste 				increment = 0;
904577421ebSEd Maste 			ng_source_mod_counter(sc, cnt, m, increment);
905577421ebSEd Maste 		}
906577421ebSEd Maste 	}
907577421ebSEd Maste 
9085f87dd69SEd Maste 	ts = &sc->embed_timestamp;
9095f87dd69SEd Maste 	if (ts->flags & NGM_SOURCE_EMBED_ENABLE) {
9105f87dd69SEd Maste 		struct timeval now;
9115f87dd69SEd Maste 		getmicrotime(&now);
9125f87dd69SEd Maste 		now.tv_sec = htonl(now.tv_sec);
9135f87dd69SEd Maste 		now.tv_usec = htonl(now.tv_usec);
9145f87dd69SEd Maste 		ng_source_packet_mod(sc, m, ts->offset, sizeof (now),
9155f87dd69SEd Maste 		    (caddr_t)&now, ts->flags);
9165f87dd69SEd Maste 	}
9175f87dd69SEd Maste 
9185f87dd69SEd Maste done:
9195f87dd69SEd Maste 	return(error);
9205f87dd69SEd Maste }
921