xref: /freebsd/sys/netgraph/ng_source.c (revision 5f87dd698c09408e51e4cc31946117003debf270)
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 
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 */
89585ff168SJulian Elischer 	struct ifnet			*output_ifp;
9030bef41bSGleb Smirnoff 	struct callout			intr_ch;
91d8f5d037SGleb Smirnoff 	uint64_t			packets;	/* packets to send */
92d8f5d037SGleb Smirnoff 	uint32_t			queueOctets;
935f87dd69SEd Maste 	struct ng_source_embed_info	embed_timestamp;
94585ff168SJulian Elischer };
95585ff168SJulian Elischer typedef struct privdata *sc_p;
96585ff168SJulian Elischer 
97585ff168SJulian Elischer /* Node flags */
98585ff168SJulian Elischer #define NG_SOURCE_ACTIVE	(NGF_TYPE1)
99585ff168SJulian Elischer 
100585ff168SJulian Elischer /* Netgraph methods */
101585ff168SJulian Elischer static ng_constructor_t	ng_source_constructor;
102585ff168SJulian Elischer static ng_rcvmsg_t	ng_source_rcvmsg;
103585ff168SJulian Elischer static ng_shutdown_t	ng_source_rmnode;
104585ff168SJulian Elischer static ng_newhook_t	ng_source_newhook;
105d8f5d037SGleb Smirnoff static ng_connect_t	ng_source_connect;
106585ff168SJulian Elischer static ng_rcvdata_t	ng_source_rcvdata;
107585ff168SJulian Elischer static ng_disconnect_t	ng_source_disconnect;
108585ff168SJulian Elischer 
109585ff168SJulian Elischer /* Other functions */
110a1adb510SHartmut Brandt static void		ng_source_intr(node_p, hook_p, void *, int);
111585ff168SJulian Elischer static void		ng_source_clr_data (sc_p);
112d8f5d037SGleb Smirnoff static int		ng_source_start (sc_p, uint64_t);
113585ff168SJulian Elischer static void		ng_source_stop (sc_p);
114585ff168SJulian Elischer static int		ng_source_send (sc_p, int, int *);
115d8f5d037SGleb Smirnoff static int		ng_source_store_output_ifp(sc_p, char *);
1165f87dd69SEd Maste static void		ng_source_packet_mod(sc_p, struct mbuf *,
1175f87dd69SEd Maste 			    int, int, caddr_t, int);
1185f87dd69SEd Maste static int		ng_source_dup_mod(sc_p, struct mbuf *,
1195f87dd69SEd Maste 			    struct mbuf **);
120585ff168SJulian Elischer 
121585ff168SJulian Elischer /* Parse type for timeval */
12276bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = {
123585ff168SJulian Elischer 	{ "tv_sec",		&ng_parse_int32_type	},
124585ff168SJulian Elischer 	{ "tv_usec",		&ng_parse_int32_type	},
125585ff168SJulian Elischer 	{ NULL }
126585ff168SJulian Elischer };
127585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = {
128585ff168SJulian Elischer 	&ng_parse_struct_type,
129585ff168SJulian Elischer 	&ng_source_timeval_type_fields
130585ff168SJulian Elischer };
131585ff168SJulian Elischer 
132585ff168SJulian Elischer /* Parse type for struct ng_source_stats */
133585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[]
134585ff168SJulian Elischer 	= NG_SOURCE_STATS_TYPE_INFO;
135585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = {
136585ff168SJulian Elischer 	&ng_parse_struct_type,
137585ff168SJulian Elischer 	&ng_source_stats_type_fields
138585ff168SJulian Elischer };
139585ff168SJulian Elischer 
1405f87dd69SEd Maste /* Parse type for struct ng_source_embed_info */
1415f87dd69SEd Maste static const struct ng_parse_struct_field ng_source_embed_type_fields[] =
1425f87dd69SEd Maste 	NG_SOURCE_EMBED_TYPE_INFO;
1435f87dd69SEd Maste static const struct ng_parse_type ng_source_embed_type = {
1445f87dd69SEd Maste 	&ng_parse_struct_type,
1455f87dd69SEd Maste 	&ng_source_embed_type_fields
1465f87dd69SEd Maste };
1475f87dd69SEd Maste 
148585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */
149585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = {
150585ff168SJulian Elischer 	{
151585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
152585ff168SJulian Elischer 	  NGM_SOURCE_GET_STATS,
153585ff168SJulian Elischer 	  "getstats",
154585ff168SJulian Elischer 	  NULL,
155585ff168SJulian Elischer 	  &ng_source_stats_type
156585ff168SJulian Elischer 	},
157585ff168SJulian Elischer 	{
158585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
159585ff168SJulian Elischer 	  NGM_SOURCE_CLR_STATS,
160585ff168SJulian Elischer 	  "clrstats",
161585ff168SJulian Elischer 	  NULL,
162585ff168SJulian Elischer 	  NULL
163585ff168SJulian Elischer 	},
164585ff168SJulian Elischer 	{
165585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
166585ff168SJulian Elischer 	  NGM_SOURCE_GETCLR_STATS,
167585ff168SJulian Elischer 	  "getclrstats",
168585ff168SJulian Elischer 	  NULL,
169585ff168SJulian Elischer 	  &ng_source_stats_type
170585ff168SJulian Elischer 	},
171585ff168SJulian Elischer 	{
172585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
173585ff168SJulian Elischer 	  NGM_SOURCE_START,
174585ff168SJulian Elischer 	  "start",
175585ff168SJulian Elischer 	  &ng_parse_uint64_type,
176585ff168SJulian Elischer 	  NULL
177585ff168SJulian Elischer 	},
178585ff168SJulian Elischer 	{
179585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
180585ff168SJulian Elischer 	  NGM_SOURCE_STOP,
181585ff168SJulian Elischer 	  "stop",
182585ff168SJulian Elischer 	  NULL,
183585ff168SJulian Elischer 	  NULL
184585ff168SJulian Elischer 	},
185585ff168SJulian Elischer 	{
186585ff168SJulian Elischer 	  NGM_SOURCE_COOKIE,
187585ff168SJulian Elischer 	  NGM_SOURCE_CLR_DATA,
188585ff168SJulian Elischer 	  "clrdata",
189585ff168SJulian Elischer 	  NULL,
190585ff168SJulian Elischer 	  NULL
191585ff168SJulian Elischer 	},
192f5d15522SHartmut Brandt 	{
193f5d15522SHartmut Brandt 	  NGM_SOURCE_COOKIE,
194d8f5d037SGleb Smirnoff 	  NGM_SOURCE_SETIFACE,
195d8f5d037SGleb Smirnoff 	  "setiface",
196d8f5d037SGleb Smirnoff 	  &ng_parse_string_type,
197f5d15522SHartmut Brandt 	  NULL
198f5d15522SHartmut Brandt 	},
19972235857SGleb Smirnoff 	{
20072235857SGleb Smirnoff 	  NGM_SOURCE_COOKIE,
20172235857SGleb Smirnoff 	  NGM_SOURCE_SETPPS,
20272235857SGleb Smirnoff 	  "setpps",
20372235857SGleb Smirnoff 	  &ng_parse_uint32_type,
20472235857SGleb Smirnoff 	  NULL
20572235857SGleb Smirnoff 	},
2065f87dd69SEd Maste 	{
2075f87dd69SEd Maste 	  NGM_SOURCE_COOKIE,
2085f87dd69SEd Maste 	  NGM_SOURCE_SET_TIMESTAMP,
2095f87dd69SEd Maste 	  "settimestamp",
2105f87dd69SEd Maste 	  &ng_source_embed_type,
2115f87dd69SEd Maste 	  NULL
2125f87dd69SEd Maste 	},
2135f87dd69SEd Maste 	{
2145f87dd69SEd Maste 	  NGM_SOURCE_COOKIE,
2155f87dd69SEd Maste 	  NGM_SOURCE_GET_TIMESTAMP,
2165f87dd69SEd Maste 	  "gettimestamp",
2175f87dd69SEd Maste 	  NULL,
2185f87dd69SEd Maste 	  &ng_source_embed_type
2195f87dd69SEd Maste 	},
220585ff168SJulian Elischer 	{ 0 }
221585ff168SJulian Elischer };
222585ff168SJulian Elischer 
223585ff168SJulian Elischer /* Netgraph type descriptor */
224585ff168SJulian Elischer static struct ng_type ng_source_typestruct = {
225f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
226f8aae777SJulian Elischer 	.name =		NG_SOURCE_NODE_TYPE,
227f8aae777SJulian Elischer 	.constructor =	ng_source_constructor,
228f8aae777SJulian Elischer 	.rcvmsg =	ng_source_rcvmsg,
229f8aae777SJulian Elischer 	.shutdown =	ng_source_rmnode,
230f8aae777SJulian Elischer 	.newhook =	ng_source_newhook,
231d8f5d037SGleb Smirnoff 	.connect =	ng_source_connect,
232f8aae777SJulian Elischer 	.rcvdata =	ng_source_rcvdata,
233f8aae777SJulian Elischer 	.disconnect =	ng_source_disconnect,
234f8aae777SJulian Elischer 	.cmdlist =	ng_source_cmds,
235585ff168SJulian Elischer };
236585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct);
237585ff168SJulian Elischer 
238d8f5d037SGleb Smirnoff static int ng_source_set_autosrc(sc_p, uint32_t);
2392cafef3eSHartmut Brandt 
240585ff168SJulian Elischer /*
241585ff168SJulian Elischer  * Node constructor
242585ff168SJulian Elischer  */
243585ff168SJulian Elischer static int
2445968e29eSJulian Elischer ng_source_constructor(node_p node)
245585ff168SJulian Elischer {
246585ff168SJulian Elischer 	sc_p sc;
247585ff168SJulian Elischer 
248b1b70498SHartmut Brandt 	sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
249585ff168SJulian Elischer 	if (sc == NULL)
250585ff168SJulian Elischer 		return (ENOMEM);
251585ff168SJulian Elischer 
2525968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, sc);
2535968e29eSJulian Elischer 	sc->node = node;
254585ff168SJulian Elischer 	sc->snd_queue.ifq_maxlen = 2048;	/* XXX not checked */
25530bef41bSGleb Smirnoff 	ng_callout_init(&sc->intr_ch);
25630bef41bSGleb Smirnoff 
257585ff168SJulian Elischer 	return (0);
258585ff168SJulian Elischer }
259585ff168SJulian Elischer 
260585ff168SJulian Elischer /*
261585ff168SJulian Elischer  * Add a hook
262585ff168SJulian Elischer  */
263585ff168SJulian Elischer static int
264585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name)
265585ff168SJulian Elischer {
266d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
267585ff168SJulian Elischer 
268585ff168SJulian Elischer 	if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
269d8f5d037SGleb Smirnoff 		sc->input = hook;
270585ff168SJulian Elischer 	} else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
271d8f5d037SGleb Smirnoff 		sc->output = hook;
272585ff168SJulian Elischer 		sc->output_ifp = 0;
273585ff168SJulian Elischer 		bzero(&sc->stats, sizeof(sc->stats));
274585ff168SJulian Elischer 	} else
275585ff168SJulian Elischer 		return (EINVAL);
276d8f5d037SGleb Smirnoff 
277d8f5d037SGleb Smirnoff 	return (0);
278d8f5d037SGleb Smirnoff }
279d8f5d037SGleb Smirnoff 
280d8f5d037SGleb Smirnoff /*
281d8f5d037SGleb Smirnoff  * Hook has been added
282d8f5d037SGleb Smirnoff  */
283d8f5d037SGleb Smirnoff static int
284d8f5d037SGleb Smirnoff ng_source_connect(hook_p hook)
285d8f5d037SGleb Smirnoff {
286d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
287d8f5d037SGleb Smirnoff 	struct ng_mesg *msg;
288d8f5d037SGleb Smirnoff 	int dummy_error = 0;
289d8f5d037SGleb Smirnoff 
290d8f5d037SGleb Smirnoff 	/*
291d8f5d037SGleb Smirnoff 	 * If this is "output" hook, then request information
292d8f5d037SGleb Smirnoff 	 * from our downstream.
293d8f5d037SGleb Smirnoff 	 */
294d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
295d8f5d037SGleb Smirnoff 		NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME,
296d8f5d037SGleb Smirnoff 		    0, M_NOWAIT);
297d8f5d037SGleb Smirnoff 		if (msg == NULL)
298d8f5d037SGleb Smirnoff 			return (ENOBUFS);
299d8f5d037SGleb Smirnoff 
300d8f5d037SGleb Smirnoff 		/*
301d8f5d037SGleb Smirnoff 		 * Our hook and peer hook have HK_INVALID flag set,
302d8f5d037SGleb Smirnoff 		 * so we can't use NG_SEND_MSG_HOOK() macro here.
303d8f5d037SGleb Smirnoff 		 */
304d8f5d037SGleb Smirnoff 		NG_SEND_MSG_ID(dummy_error, sc->node, msg,
305d8f5d037SGleb Smirnoff 		    NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node));
306d8f5d037SGleb Smirnoff 	}
307d8f5d037SGleb Smirnoff 
308585ff168SJulian Elischer 	return (0);
309585ff168SJulian Elischer }
310585ff168SJulian Elischer 
311585ff168SJulian Elischer /*
312585ff168SJulian Elischer  * Receive a control message
313585ff168SJulian Elischer  */
314585ff168SJulian Elischer static int
3155968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
316585ff168SJulian Elischer {
317d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
318d8f5d037SGleb Smirnoff 	struct ng_mesg *msg, *resp = NULL;
319585ff168SJulian Elischer 	int error = 0;
320585ff168SJulian Elischer 
3215968e29eSJulian Elischer 	NGI_GET_MSG(item, msg);
322d8f5d037SGleb Smirnoff 
323585ff168SJulian Elischer 	switch (msg->header.typecookie) {
324585ff168SJulian Elischer 	case NGM_SOURCE_COOKIE:
325b655e33dSJulian Elischer 		if (msg->header.flags & NGF_RESP) {
326b655e33dSJulian Elischer 			error = EINVAL;
327b655e33dSJulian Elischer 			break;
328b655e33dSJulian Elischer 		}
329585ff168SJulian Elischer 		switch (msg->header.cmd) {
330585ff168SJulian Elischer 		case NGM_SOURCE_GET_STATS:
331585ff168SJulian Elischer 		case NGM_SOURCE_CLR_STATS:
332585ff168SJulian Elischer 		case NGM_SOURCE_GETCLR_STATS:
333585ff168SJulian Elischer                     {
334585ff168SJulian Elischer 			struct ng_source_stats *stats;
335585ff168SJulian Elischer 
336585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
337585ff168SJulian Elischer                                 NG_MKRESPONSE(resp, msg,
338585ff168SJulian Elischer                                     sizeof(*stats), M_NOWAIT);
339585ff168SJulian Elischer 				if (resp == NULL) {
340585ff168SJulian Elischer 					error = ENOMEM;
341585ff168SJulian Elischer 					goto done;
342585ff168SJulian Elischer 				}
343585ff168SJulian Elischer 				sc->stats.queueOctets = sc->queueOctets;
3444b52f283SJulian Elischer 				sc->stats.queueFrames = sc->snd_queue.ifq_len;
3455968e29eSJulian Elischer 				if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
346585ff168SJulian Elischer 				    && !timevalisset(&sc->stats.endTime)) {
347585ff168SJulian Elischer 					getmicrotime(&sc->stats.elapsedTime);
348585ff168SJulian Elischer 					timevalsub(&sc->stats.elapsedTime,
349585ff168SJulian Elischer 					    &sc->stats.startTime);
350585ff168SJulian Elischer 				}
3514b52f283SJulian Elischer 				stats = (struct ng_source_stats *)resp->data;
352585ff168SJulian Elischer 				bcopy(&sc->stats, stats, sizeof(* stats));
353585ff168SJulian Elischer                         }
354585ff168SJulian Elischer                         if (msg->header.cmd != NGM_SOURCE_GET_STATS)
355585ff168SJulian Elischer 				bzero(&sc->stats, sizeof(sc->stats));
356585ff168SJulian Elischer 		    }
357585ff168SJulian Elischer 		    break;
358585ff168SJulian Elischer 		case NGM_SOURCE_START:
359585ff168SJulian Elischer 		    {
360d8f5d037SGleb Smirnoff 			uint64_t packets;
361f5d15522SHartmut Brandt 
362d8f5d037SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint64_t)) {
363d8f5d037SGleb Smirnoff 				error = EINVAL;
364f5d15522SHartmut Brandt 				break;
365d8f5d037SGleb Smirnoff 			}
366d8f5d037SGleb Smirnoff 
367d8f5d037SGleb Smirnoff 			packets = *(uint64_t *)msg->data;
368d8f5d037SGleb Smirnoff 
369d8f5d037SGleb Smirnoff 			error = ng_source_start(sc, packets);
370d8f5d037SGleb Smirnoff 
371d8f5d037SGleb Smirnoff 		    	break;
372d8f5d037SGleb Smirnoff 		    }
373585ff168SJulian Elischer 		case NGM_SOURCE_STOP:
374585ff168SJulian Elischer 			ng_source_stop(sc);
375585ff168SJulian Elischer 			break;
376585ff168SJulian Elischer 		case NGM_SOURCE_CLR_DATA:
377585ff168SJulian Elischer 			ng_source_clr_data(sc);
378585ff168SJulian Elischer 			break;
379d8f5d037SGleb Smirnoff 		case NGM_SOURCE_SETIFACE:
380d8f5d037SGleb Smirnoff 		    {
381d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
382d8f5d037SGleb Smirnoff 
383d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
384d8f5d037SGleb Smirnoff 				error = EINVAL;
385d8f5d037SGleb Smirnoff 				break;
386d8f5d037SGleb Smirnoff 			}
387d8f5d037SGleb Smirnoff 
388d8f5d037SGleb Smirnoff 			ng_source_store_output_ifp(sc, ifname);
389d8f5d037SGleb Smirnoff 			break;
390d8f5d037SGleb Smirnoff 		    }
39172235857SGleb Smirnoff 		case NGM_SOURCE_SETPPS:
39272235857SGleb Smirnoff 		    {
39372235857SGleb Smirnoff 			uint32_t pps;
39472235857SGleb Smirnoff 
39572235857SGleb Smirnoff 			if (msg->header.arglen != sizeof(uint32_t)) {
39672235857SGleb Smirnoff 				error = EINVAL;
39772235857SGleb Smirnoff 				break;
39872235857SGleb Smirnoff 			}
39972235857SGleb Smirnoff 
40072235857SGleb Smirnoff 			pps = *(uint32_t *)msg->data;
40172235857SGleb Smirnoff 
40272235857SGleb Smirnoff 			sc->stats.maxPps = pps;
40372235857SGleb Smirnoff 
40472235857SGleb Smirnoff 			break;
40572235857SGleb Smirnoff 		    }
4065f87dd69SEd Maste 		case NGM_SOURCE_SET_TIMESTAMP:
4075f87dd69SEd Maste 		    {
4085f87dd69SEd Maste 			struct ng_source_embed_info *embed;
4095f87dd69SEd Maste 
4105f87dd69SEd Maste 			embed = (struct ng_source_embed_info *)msg->data;
4115f87dd69SEd Maste 			bcopy(embed, &sc->embed_timestamp, sizeof(*embed));
4125f87dd69SEd Maste 
4135f87dd69SEd Maste 			break;
4145f87dd69SEd Maste 		    }
4155f87dd69SEd Maste 		case NGM_SOURCE_GET_TIMESTAMP:
4165f87dd69SEd Maste 		    {
4175f87dd69SEd Maste 			struct ng_source_embed_info *embed;
4185f87dd69SEd Maste 
4195f87dd69SEd Maste 			NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT);
4205f87dd69SEd Maste 			if (resp == NULL) {
4215f87dd69SEd Maste 				error = ENOMEM;
4225f87dd69SEd Maste 				goto done;
4235f87dd69SEd Maste 			}
4245f87dd69SEd Maste 			embed = (struct ng_source_embed_info *)resp->data;
4255f87dd69SEd Maste 			bcopy(&sc->embed_timestamp, embed, sizeof(*embed));
4265f87dd69SEd Maste 
4275f87dd69SEd Maste 			break;
4285f87dd69SEd Maste 		    }
429585ff168SJulian Elischer 		default:
430585ff168SJulian Elischer 			error = EINVAL;
431585ff168SJulian Elischer 			break;
432585ff168SJulian Elischer 		}
433585ff168SJulian Elischer 		break;
434b655e33dSJulian Elischer 	case NGM_ETHER_COOKIE:
435b655e33dSJulian Elischer 		if (!(msg->header.flags & NGF_RESP)) {
436b655e33dSJulian Elischer 			error = EINVAL;
437b655e33dSJulian Elischer 			break;
438b655e33dSJulian Elischer 		}
439b655e33dSJulian Elischer 		switch (msg->header.cmd) {
440d8f5d037SGleb Smirnoff 		case NGM_ETHER_GET_IFNAME:
441d8f5d037SGleb Smirnoff 		    {
442d8f5d037SGleb Smirnoff 			char *ifname = (char *)msg->data;
443d8f5d037SGleb Smirnoff 
444d8f5d037SGleb Smirnoff 			if (msg->header.arglen < 2) {
445d8f5d037SGleb Smirnoff 				error = EINVAL;
446b655e33dSJulian Elischer 				break;
447d8f5d037SGleb Smirnoff 			}
448d8f5d037SGleb Smirnoff 
449d8f5d037SGleb Smirnoff 			if (ng_source_store_output_ifp(sc, ifname) == 0)
450d8f5d037SGleb Smirnoff 				ng_source_set_autosrc(sc, 0);
451d8f5d037SGleb Smirnoff 			break;
452d8f5d037SGleb Smirnoff 		    }
453b655e33dSJulian Elischer 		default:
454b655e33dSJulian Elischer 			error = EINVAL;
455b655e33dSJulian Elischer 		}
456b655e33dSJulian Elischer 		break;
457585ff168SJulian Elischer 	default:
458585ff168SJulian Elischer 		error = EINVAL;
459585ff168SJulian Elischer 		break;
460585ff168SJulian Elischer 	}
461585ff168SJulian Elischer 
462585ff168SJulian Elischer done:
463d8f5d037SGleb Smirnoff 	/* Take care of synchronous response, if any. */
4645968e29eSJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
465d8f5d037SGleb Smirnoff 	/* Free the message and return. */
4665968e29eSJulian Elischer 	NG_FREE_MSG(msg);
467585ff168SJulian Elischer 	return (error);
468585ff168SJulian Elischer }
469585ff168SJulian Elischer 
470585ff168SJulian Elischer /*
471585ff168SJulian Elischer  * Receive data on a hook
472585ff168SJulian Elischer  *
473585ff168SJulian Elischer  * If data comes in the input hook, enqueue it on the send queue.
474585ff168SJulian Elischer  * If data comes in the output hook, discard it.
475585ff168SJulian Elischer  */
476585ff168SJulian Elischer static int
4775968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item)
478585ff168SJulian Elischer {
479d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
4805968e29eSJulian Elischer 	struct mbuf *m;
481d8f5d037SGleb Smirnoff 	int error = 0;
482585ff168SJulian Elischer 
4835968e29eSJulian Elischer 	NGI_GET_M(item, m);
4845968e29eSJulian Elischer 	NG_FREE_ITEM(item);
485585ff168SJulian Elischer 
486585ff168SJulian Elischer 	/* Which hook? */
487d8f5d037SGleb Smirnoff 	if (hook == sc->output) {
488585ff168SJulian Elischer 		/* discard */
4895968e29eSJulian Elischer 		NG_FREE_M(m);
490585ff168SJulian Elischer 		return (error);
491585ff168SJulian Elischer 	}
492d8f5d037SGleb Smirnoff 	KASSERT(hook == sc->input, ("%s: no hook!", __func__));
493585ff168SJulian Elischer 
494d8f5d037SGleb Smirnoff 	/* Enqueue packet. */
495585ff168SJulian Elischer 	/* XXX should we check IF_QFULL() ? */
49681a4ef81SHartmut Brandt 	_IF_ENQUEUE(&sc->snd_queue, m);
497585ff168SJulian Elischer 	sc->queueOctets += m->m_pkthdr.len;
498585ff168SJulian Elischer 
499585ff168SJulian Elischer 	return (0);
500585ff168SJulian Elischer }
501585ff168SJulian Elischer 
502585ff168SJulian Elischer /*
503585ff168SJulian Elischer  * Shutdown processing
504585ff168SJulian Elischer  */
505585ff168SJulian Elischer static int
506585ff168SJulian Elischer ng_source_rmnode(node_p node)
507585ff168SJulian Elischer {
508d8f5d037SGleb Smirnoff 	sc_p sc = NG_NODE_PRIVATE(node);
509585ff168SJulian Elischer 
510585ff168SJulian Elischer 	ng_source_stop(sc);
511585ff168SJulian Elischer 	ng_source_clr_data(sc);
5125968e29eSJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
5135968e29eSJulian Elischer 	NG_NODE_UNREF(node);
51476bd5857SHartmut Brandt 	free(sc, M_NETGRAPH);
515d8f5d037SGleb Smirnoff 
516585ff168SJulian Elischer 	return (0);
517585ff168SJulian Elischer }
518585ff168SJulian Elischer 
519585ff168SJulian Elischer /*
520585ff168SJulian Elischer  * Hook disconnection
521585ff168SJulian Elischer  */
522585ff168SJulian Elischer static int
523585ff168SJulian Elischer ng_source_disconnect(hook_p hook)
524585ff168SJulian Elischer {
5254b52f283SJulian Elischer 	sc_p sc;
526585ff168SJulian Elischer 
5275968e29eSJulian Elischer 	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
52876bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
529d8f5d037SGleb Smirnoff 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output)
5305968e29eSJulian Elischer 		ng_rmnode_self(NG_HOOK_NODE(hook));
531585ff168SJulian Elischer 	return (0);
532585ff168SJulian Elischer }
533585ff168SJulian Elischer 
534585ff168SJulian Elischer /*
535b655e33dSJulian Elischer  * Set sc->output_ifp to point to the the struct ifnet of the interface
536b655e33dSJulian Elischer  * reached via our output hook.
537b655e33dSJulian Elischer  */
538b655e33dSJulian Elischer static int
539d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc_p sc, char *ifname)
540b655e33dSJulian Elischer {
541b655e33dSJulian Elischer 	struct ifnet *ifp;
542b655e33dSJulian Elischer 	int s;
543b655e33dSJulian Elischer 
544d8f5d037SGleb Smirnoff 	ifp = ifunit(ifname);
545585ff168SJulian Elischer 
546585ff168SJulian Elischer 	if (ifp == NULL) {
54776bd5857SHartmut Brandt 		printf("%s: can't find interface %d\n", __func__, if_index);
548585ff168SJulian Elischer 		return (EINVAL);
549585ff168SJulian Elischer 	}
550585ff168SJulian Elischer 	sc->output_ifp = ifp;
551585ff168SJulian Elischer 
552585ff168SJulian Elischer #if 1
553585ff168SJulian Elischer 	/* XXX mucking with a drivers ifqueue size is ugly but we need it
554585ff168SJulian Elischer 	 * to queue a lot of packets to get close to line rate on a gigabit
555585ff168SJulian Elischer 	 * interface with small packets.
556585ff168SJulian Elischer 	 * XXX we should restore the original value at stop or disconnect
557585ff168SJulian Elischer 	 */
558585ff168SJulian Elischer 	s = splimp();		/* XXX is this required? */
55976bd5857SHartmut Brandt 	if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) {
560585ff168SJulian Elischer 		printf("ng_source: changing ifq_maxlen from %d to %d\n",
5614b52f283SJulian Elischer 		    ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
562585ff168SJulian Elischer 		ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
563585ff168SJulian Elischer 	}
564585ff168SJulian Elischer 	splx(s);
565585ff168SJulian Elischer #endif
566585ff168SJulian Elischer 	return (0);
567585ff168SJulian Elischer }
568585ff168SJulian Elischer 
569585ff168SJulian Elischer /*
570585ff168SJulian Elischer  * Set the attached ethernet node's ethernet source address override flag.
571585ff168SJulian Elischer  */
572585ff168SJulian Elischer static int
573d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc_p sc, uint32_t flag)
574585ff168SJulian Elischer {
575585ff168SJulian Elischer 	struct ng_mesg *msg;
576585ff168SJulian Elischer 	int error = 0;
577585ff168SJulian Elischer 
578585ff168SJulian Elischer 	NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
579d8f5d037SGleb Smirnoff 	    sizeof (uint32_t), M_NOWAIT);
580585ff168SJulian Elischer 	if (msg == NULL)
581585ff168SJulian Elischer 		return(ENOBUFS);
582585ff168SJulian Elischer 
583d8f5d037SGleb Smirnoff 	*(uint32_t *)msg->data = flag;
584d8f5d037SGleb Smirnoff 	NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0);
585585ff168SJulian Elischer 	return (error);
586585ff168SJulian Elischer }
587585ff168SJulian Elischer 
588585ff168SJulian Elischer /*
589585ff168SJulian Elischer  * Clear out the data we've queued
590585ff168SJulian Elischer  */
591585ff168SJulian Elischer static void
592585ff168SJulian Elischer ng_source_clr_data (sc_p sc)
593585ff168SJulian Elischer {
594585ff168SJulian Elischer 	struct mbuf *m;
595585ff168SJulian Elischer 
596585ff168SJulian Elischer 	for (;;) {
59781a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
598585ff168SJulian Elischer 		if (m == NULL)
599585ff168SJulian Elischer 			break;
600585ff168SJulian Elischer 		NG_FREE_M(m);
601585ff168SJulian Elischer 	}
602585ff168SJulian Elischer 	sc->queueOctets = 0;
603585ff168SJulian Elischer }
604585ff168SJulian Elischer 
605585ff168SJulian Elischer /*
606585ff168SJulian Elischer  * Start sending queued data out the output hook
607585ff168SJulian Elischer  */
608d8f5d037SGleb Smirnoff static int
609d8f5d037SGleb Smirnoff ng_source_start(sc_p sc, uint64_t packets)
610585ff168SJulian Elischer {
611d8f5d037SGleb Smirnoff 	if (sc->output_ifp == NULL) {
612d8f5d037SGleb Smirnoff 		printf("ng_source: start without iface configured\n");
613d8f5d037SGleb Smirnoff 		return (ENXIO);
614d8f5d037SGleb Smirnoff 	}
615d8f5d037SGleb Smirnoff 
616d8f5d037SGleb Smirnoff 	if (sc->node->nd_flags & NG_SOURCE_ACTIVE)
617d8f5d037SGleb Smirnoff 		return (EBUSY);
618d8f5d037SGleb Smirnoff 
619d8f5d037SGleb Smirnoff 	sc->node->nd_flags |= NG_SOURCE_ACTIVE;
620d8f5d037SGleb Smirnoff 
621d8f5d037SGleb Smirnoff 	sc->packets = packets;
622d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.elapsedTime);
623d8f5d037SGleb Smirnoff 	timevalclear(&sc->stats.endTime);
624d8f5d037SGleb Smirnoff 	getmicrotime(&sc->stats.startTime);
62572235857SGleb Smirnoff 	getmicrotime(&sc->stats.lastTime);
626d8f5d037SGleb Smirnoff 	ng_callout(&sc->intr_ch, sc->node, NULL, 0,
627d8f5d037SGleb Smirnoff 	    ng_source_intr, sc, 0);
628d8f5d037SGleb Smirnoff 
629d8f5d037SGleb Smirnoff 	return (0);
630585ff168SJulian Elischer }
631585ff168SJulian Elischer 
632585ff168SJulian Elischer /*
633585ff168SJulian Elischer  * Stop sending queued data out the output hook
634585ff168SJulian Elischer  */
635585ff168SJulian Elischer static void
636585ff168SJulian Elischer ng_source_stop(sc_p sc)
637585ff168SJulian Elischer {
638f9d9e1b4SGleb Smirnoff 	ng_uncallout(&sc->intr_ch, sc->node);
6395968e29eSJulian Elischer 	sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
640585ff168SJulian Elischer 	getmicrotime(&sc->stats.endTime);
641585ff168SJulian Elischer 	sc->stats.elapsedTime = sc->stats.endTime;
642585ff168SJulian Elischer 	timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
643585ff168SJulian Elischer }
644585ff168SJulian Elischer 
645585ff168SJulian Elischer /*
646585ff168SJulian Elischer  * While active called every NG_SOURCE_INTR_TICKS ticks.
647585ff168SJulian Elischer  * Sends as many packets as the interface connected to our
648585ff168SJulian Elischer  * output hook is able to enqueue.
649585ff168SJulian Elischer  */
650585ff168SJulian Elischer static void
651a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2)
652585ff168SJulian Elischer {
653a1adb510SHartmut Brandt 	sc_p sc = (sc_p)arg1;
654585ff168SJulian Elischer 	struct ifqueue *ifq;
655585ff168SJulian Elischer 	int packets;
656585ff168SJulian Elischer 
65776bd5857SHartmut Brandt 	KASSERT(sc != NULL, ("%s: null node private", __func__));
658585ff168SJulian Elischer 
659d8f5d037SGleb Smirnoff 	if (sc->packets == 0 || sc->output == NULL
6605968e29eSJulian Elischer 	    || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
661585ff168SJulian Elischer 		ng_source_stop(sc);
662585ff168SJulian Elischer 		return;
663585ff168SJulian Elischer 	}
664585ff168SJulian Elischer 
665f5d15522SHartmut Brandt 	if (sc->output_ifp != NULL) {
6660572dfacSRuslan Ermilov 		ifq = (struct ifqueue *)&sc->output_ifp->if_snd;
667585ff168SJulian Elischer 		packets = ifq->ifq_maxlen - ifq->ifq_len;
668f5d15522SHartmut Brandt 	} else
669f5d15522SHartmut Brandt 		packets = sc->snd_queue.ifq_len;
670f5d15522SHartmut Brandt 
67172235857SGleb Smirnoff 	if (sc->stats.maxPps != 0) {
67272235857SGleb Smirnoff 		struct timeval	now, elapsed;
67372235857SGleb Smirnoff 		uint64_t	usec;
67472235857SGleb Smirnoff 		int		maxpkt;
67572235857SGleb Smirnoff 
67672235857SGleb Smirnoff 		getmicrotime(&now);
67772235857SGleb Smirnoff 		elapsed = now;
67872235857SGleb Smirnoff 		timevalsub(&elapsed, &sc->stats.lastTime);
67972235857SGleb Smirnoff 		usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec;
68072235857SGleb Smirnoff 		maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000;
68172235857SGleb Smirnoff 		sc->stats.lastTime = now;
68272235857SGleb Smirnoff 		if (packets > maxpkt)
68372235857SGleb Smirnoff 			packets = maxpkt;
68472235857SGleb Smirnoff 	}
68572235857SGleb Smirnoff 
686585ff168SJulian Elischer 	ng_source_send(sc, packets, NULL);
687a1adb510SHartmut Brandt 	if (sc->packets == 0)
688585ff168SJulian Elischer 		ng_source_stop(sc);
689a1adb510SHartmut Brandt 	else
690f9d9e1b4SGleb Smirnoff 		ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS,
691d312eaf5SGleb Smirnoff 		    ng_source_intr, sc, 0);
692585ff168SJulian Elischer }
693585ff168SJulian Elischer 
694585ff168SJulian Elischer /*
695205aefa3SGleb Smirnoff  * Send packets out our output hook.
696585ff168SJulian Elischer  */
697585ff168SJulian Elischer static int
698585ff168SJulian Elischer ng_source_send(sc_p sc, int tosend, int *sent_p)
699585ff168SJulian Elischer {
700585ff168SJulian Elischer 	struct mbuf *m, *m2;
701205aefa3SGleb Smirnoff 	int sent;
702585ff168SJulian Elischer 	int error = 0;
703585ff168SJulian Elischer 
70476bd5857SHartmut Brandt 	KASSERT(tosend >= 0, ("%s: negative tosend param", __func__));
7055968e29eSJulian Elischer 	KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
70676bd5857SHartmut Brandt 	    ("%s: inactive node", __func__));
707585ff168SJulian Elischer 
708d8f5d037SGleb Smirnoff 	if ((uint64_t)tosend > sc->packets)
709585ff168SJulian Elischer 		tosend = sc->packets;
710585ff168SJulian Elischer 
711205aefa3SGleb Smirnoff 	/* Go through the queue sending packets one by one. */
712585ff168SJulian Elischer 	for (sent = 0; error == 0 && sent < tosend; ++sent) {
71381a4ef81SHartmut Brandt 		_IF_DEQUEUE(&sc->snd_queue, m);
714585ff168SJulian Elischer 		if (m == NULL)
715585ff168SJulian Elischer 			break;
716585ff168SJulian Elischer 
7175f87dd69SEd Maste 		/* Duplicate and modify the packet. */
7185f87dd69SEd Maste 		error = ng_source_dup_mod(sc, m, &m2);
7195f87dd69SEd Maste 		if (error) {
7205f87dd69SEd Maste 			if (error == ENOBUFS)
72181a4ef81SHartmut Brandt 				_IF_PREPEND(&sc->snd_queue, m);
7225f87dd69SEd Maste 			else
7235f87dd69SEd Maste 				_IF_ENQUEUE(&sc->snd_queue, m);
724585ff168SJulian Elischer 			break;
725585ff168SJulian Elischer 		}
726585ff168SJulian Elischer 
727d8f5d037SGleb Smirnoff 		/* Re-enqueue the original packet for us. */
72881a4ef81SHartmut Brandt 		_IF_ENQUEUE(&sc->snd_queue, m);
729585ff168SJulian Elischer 
730585ff168SJulian Elischer 		sc->stats.outFrames++;
731585ff168SJulian Elischer 		sc->stats.outOctets += m2->m_pkthdr.len;
732d8f5d037SGleb Smirnoff 		NG_SEND_DATA_ONLY(error, sc->output, m2);
733a1adb510SHartmut Brandt 		if (error)
734205aefa3SGleb Smirnoff 			break;
735585ff168SJulian Elischer 	}
736585ff168SJulian Elischer 
737585ff168SJulian Elischer 	sc->packets -= sent;
738585ff168SJulian Elischer 	if (sent_p != NULL)
739585ff168SJulian Elischer 		*sent_p = sent;
740585ff168SJulian Elischer 	return (error);
741585ff168SJulian Elischer }
7425f87dd69SEd Maste 
7435f87dd69SEd Maste /*
7445f87dd69SEd Maste  * Modify packet in 'm' by changing 'len' bytes starting at 'offset'
7455f87dd69SEd Maste  * to data in 'cp'.
7465f87dd69SEd Maste  *
7475f87dd69SEd Maste  * The packet data in 'm' must be in a contiguous buffer in a single mbuf.
7485f87dd69SEd Maste  */
7495f87dd69SEd Maste static void
7505f87dd69SEd Maste ng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp,
7515f87dd69SEd Maste     int flags)
7525f87dd69SEd Maste {
7535f87dd69SEd Maste 	if (len == 0)
7545f87dd69SEd Maste 		return;
7555f87dd69SEd Maste 
7565f87dd69SEd Maste 	/* Can't modify beyond end of packet. */
7575f87dd69SEd Maste 	/* TODO: Pad packet for this case. */
7585f87dd69SEd Maste 	if (offset + len > m->m_len)
7595f87dd69SEd Maste 		return;
7605f87dd69SEd Maste 
7615f87dd69SEd Maste 	bcopy(cp, mtod_off(m, offset, caddr_t), len);
7625f87dd69SEd Maste }
7635f87dd69SEd Maste 
7645f87dd69SEd Maste static int
7655f87dd69SEd Maste ng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr)
7665f87dd69SEd Maste {
7675f87dd69SEd Maste 	struct mbuf *m;
7685f87dd69SEd Maste 	struct ng_source_embed_info *ts;
7695f87dd69SEd Maste 	int modify;
7705f87dd69SEd Maste 	int error = 0;
7715f87dd69SEd Maste 
7725f87dd69SEd Maste 	/* Are we going to modify packets? */
7735f87dd69SEd Maste 	modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE;
7745f87dd69SEd Maste 
7755f87dd69SEd Maste 	/* Duplicate the packet. */
7765f87dd69SEd Maste 	if (modify)
7775f87dd69SEd Maste 		m = m_dup(m0, M_DONTWAIT);
7785f87dd69SEd Maste 	else
7795f87dd69SEd Maste 		m = m_copypacket(m0, M_DONTWAIT);
7805f87dd69SEd Maste 	if (m == NULL) {
7815f87dd69SEd Maste 		error = ENOBUFS;
7825f87dd69SEd Maste 		goto done;
7835f87dd69SEd Maste 	}
7845f87dd69SEd Maste 	*m_ptr = m;
7855f87dd69SEd Maste 
7865f87dd69SEd Maste 	if (!modify)
7875f87dd69SEd Maste 		goto done;
7885f87dd69SEd Maste 
7895f87dd69SEd Maste 	/* Modify the copied packet for sending. */
7905f87dd69SEd Maste 	KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__));
7915f87dd69SEd Maste 
7925f87dd69SEd Maste 	ts = &sc->embed_timestamp;
7935f87dd69SEd Maste 	if (ts->flags & NGM_SOURCE_EMBED_ENABLE) {
7945f87dd69SEd Maste 		struct timeval now;
7955f87dd69SEd Maste 		getmicrotime(&now);
7965f87dd69SEd Maste 		now.tv_sec = htonl(now.tv_sec);
7975f87dd69SEd Maste 		now.tv_usec = htonl(now.tv_usec);
7985f87dd69SEd Maste 		ng_source_packet_mod(sc, m, ts->offset, sizeof (now),
7995f87dd69SEd Maste 		    (caddr_t)&now, ts->flags);
8005f87dd69SEd Maste 	}
8015f87dd69SEd Maste 
8025f87dd69SEd Maste done:
8035f87dd69SEd Maste 	return(error);
8045f87dd69SEd Maste }
805