Deleted Added
full compact
ng_source.c (106266) ng_source.c (106319)
1/*
2 * ng_source.c
3 *
4 * Copyright 2002 Sandvine Inc.
5 * All rights reserved.
6 *
7 * Subject to the following obligations and disclaimer of warranty, use and
8 * redistribution of this software, in source or object code forms, with or
1/*
2 * ng_source.c
3 *
4 * Copyright 2002 Sandvine Inc.
5 * All rights reserved.
6 *
7 * Subject to the following obligations and disclaimer of warranty, use and
8 * redistribution of this software, in source or object code forms, with or
9 * without modifications are expressly permitted by Sandvine Inc.;
10provided,
9 * without modifications are expressly permitted by Sandvine Inc.; provided,
11 * however, that:
10 * however, that:
12 * 1. Any and all reproductions of the source or object code must include
13the
14 * copyright notice above and the following disclaimer of warranties;
15and
11 * 1. Any and all reproductions of the source or object code must include the
12 * copyright notice above and the following disclaimer of warranties; and
16 * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
13 * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
17 * trademarks, including the mark "SANDVINE" on advertising,
18endorsements,
19 * or otherwise except as such appears in the above copyright notice or
20in
14 * trademarks, including the mark "SANDVINE" on advertising, endorsements,
15 * or otherwise except as such appears in the above copyright notice or in
21 * the software.
22 *
23 * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
16 * the software.
17 *
18 * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
24 * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR
25WARRANTIES,
26 * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT
27LIMITATION,
28 * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
29PARTICULAR
19 * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES,
20 * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION,
21 * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
30 * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR
31 * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
32 * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
33 * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
34 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
22 * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR
23 * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE
24 * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY
25 * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES
26 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
35 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36EXEMPLARY,
27 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
37 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
38 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
39 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
41 * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
42 * DAMAGE.
43 *
44 * Author: Dave Chapeskie <dchapeskie@sandvine.com>
45 *
28 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH
33 * DAMAGE.
34 *
35 * Author: Dave Chapeskie <dchapeskie@sandvine.com>
36 *
46 * $FreeBSD: head/sys/netgraph/ng_source.c 106266 2002-10-31 23:03:09Z julian $
37 * $FreeBSD: head/sys/netgraph/ng_source.c 106319 2002-11-02 01:26:28Z julian $
47 */
48
49/*
50 * This node is used for high speed packet geneneration. It queues
51 * all data recieved on it's 'input' hook and when told to start via
52 * a control message it sends the packets out it's 'output' hook. In
53 * this way this node can be preloaded with a packet stream which is
54 * continuously sent.
55 *
56 * Currently it just copies the mbufs as required. It could do various
57 * tricks to try and avoid this. Probably the best performance would
58 * be achieved by modifying the appropriate drivers to be told to
59 * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
60 * transmit descriptors) under control of this node; perhaps via some
61 * flag in the mbuf or some such. The node would peak at an appropriate
62 * ifnet flag to see if such support is available for the connected
63 * interface.
64 */
65
66#include <sys/param.h>
67#include <sys/systm.h>
68#include <sys/errno.h>
69#include <sys/kernel.h>
70#include <sys/malloc.h>
71#include <sys/mbuf.h>
72#include <sys/socket.h>
73#include <net/if.h>
74#include <net/if_var.h>
75#include <netgraph/ng_message.h>
76#include <netgraph/netgraph.h>
77#include <netgraph/ng_parse.h>
78#include <netgraph/ng_ether.h>
79#include <netgraph/ng_source.h>
80
81#define NG_SOURCE_INTR_TICKS 1
82#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024)
83
84
85/* Per hook info */
86struct source_hookinfo {
87 hook_p hook;
88};
89
90/* Per node info */
91struct privdata {
92 node_p node;
93 struct source_hookinfo input;
94 struct source_hookinfo output;
95 struct ng_source_stats stats;
38 */
39
40/*
41 * This node is used for high speed packet geneneration. It queues
42 * all data recieved on it's 'input' hook and when told to start via
43 * a control message it sends the packets out it's 'output' hook. In
44 * this way this node can be preloaded with a packet stream which is
45 * continuously sent.
46 *
47 * Currently it just copies the mbufs as required. It could do various
48 * tricks to try and avoid this. Probably the best performance would
49 * be achieved by modifying the appropriate drivers to be told to
50 * self-re-enqueue packets (e.g. the if_bge driver could reuse the same
51 * transmit descriptors) under control of this node; perhaps via some
52 * flag in the mbuf or some such. The node would peak at an appropriate
53 * ifnet flag to see if such support is available for the connected
54 * interface.
55 */
56
57#include <sys/param.h>
58#include <sys/systm.h>
59#include <sys/errno.h>
60#include <sys/kernel.h>
61#include <sys/malloc.h>
62#include <sys/mbuf.h>
63#include <sys/socket.h>
64#include <net/if.h>
65#include <net/if_var.h>
66#include <netgraph/ng_message.h>
67#include <netgraph/netgraph.h>
68#include <netgraph/ng_parse.h>
69#include <netgraph/ng_ether.h>
70#include <netgraph/ng_source.h>
71
72#define NG_SOURCE_INTR_TICKS 1
73#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024)
74
75
76/* Per hook info */
77struct source_hookinfo {
78 hook_p hook;
79};
80
81/* Per node info */
82struct privdata {
83 node_p node;
84 struct source_hookinfo input;
85 struct source_hookinfo output;
86 struct ng_source_stats stats;
96 struct ifqueue snd_queue; /* packets to send
97*/
87 struct ifqueue snd_queue; /* packets to send */
98 struct ifnet *output_ifp;
99 struct callout_handle intr_ch;
88 struct ifnet *output_ifp;
89 struct callout_handle intr_ch;
100 u_int64_t packets; /* packets to send
101*/
90 u_int64_t packets; /* packets to send */
102 u_int32_t queueOctets;
103};
104typedef struct privdata *sc_p;
105
106/* Node flags */
107#define NG_SOURCE_ACTIVE (NGF_TYPE1)
108
109/* XXX */
110#if 1
111#undef KASSERT
112#define KASSERT(expr,msg) do { \
113 if (!(expr)) { \
114 printf msg ; \
115 panic("Assertion"); \
116 } \
117 } while(0)
118#endif
119
120/* Netgraph methods */
121static ng_constructor_t ng_source_constructor;
122static ng_rcvmsg_t ng_source_rcvmsg;
123static ng_shutdown_t ng_source_rmnode;
124static ng_newhook_t ng_source_newhook;
125static ng_rcvdata_t ng_source_rcvdata;
126static ng_disconnect_t ng_source_disconnect;
127
128/* Other functions */
129static timeout_t ng_source_intr;
130static int ng_source_get_output_ifp (sc_p);
131static void ng_source_clr_data (sc_p);
132static void ng_source_start (sc_p);
133static void ng_source_stop (sc_p);
134static int ng_source_send (sc_p, int, int *);
135
136
137/* Parse type for timeval */
138static const struct ng_parse_struct_field ng_source_timeval_type_fields[] =
139{
140 { "tv_sec", &ng_parse_int32_type },
141 { "tv_usec", &ng_parse_int32_type },
142 { NULL }
143};
144const struct ng_parse_type ng_source_timeval_type = {
145 &ng_parse_struct_type,
146 &ng_source_timeval_type_fields
147};
148
149/* Parse type for struct ng_source_stats */
150static const struct ng_parse_struct_field ng_source_stats_type_fields[]
151 = NG_SOURCE_STATS_TYPE_INFO;
152static const struct ng_parse_type ng_source_stats_type = {
153 &ng_parse_struct_type,
154 &ng_source_stats_type_fields
155};
156
157/* List of commands and how to convert arguments to/from ASCII */
158static const struct ng_cmdlist ng_source_cmds[] = {
159 {
160 NGM_SOURCE_COOKIE,
161 NGM_SOURCE_GET_STATS,
162 "getstats",
163 NULL,
164 &ng_source_stats_type
165 },
166 {
167 NGM_SOURCE_COOKIE,
168 NGM_SOURCE_CLR_STATS,
169 "clrstats",
170 NULL,
171 NULL
172 },
173 {
174 NGM_SOURCE_COOKIE,
175 NGM_SOURCE_GETCLR_STATS,
176 "getclrstats",
177 NULL,
178 &ng_source_stats_type
179 },
180 {
181 NGM_SOURCE_COOKIE,
182 NGM_SOURCE_START,
183 "start",
184 &ng_parse_uint64_type,
185 NULL
186 },
187 {
188 NGM_SOURCE_COOKIE,
189 NGM_SOURCE_STOP,
190 "stop",
191 NULL,
192 NULL
193 },
194 {
195 NGM_SOURCE_COOKIE,
196 NGM_SOURCE_CLR_DATA,
197 "clrdata",
198 NULL,
199 NULL
200 },
201 { 0 }
202};
203
204/* Netgraph type descriptor */
205static struct ng_type ng_source_typestruct = {
206 NG_VERSION,
207 NG_SOURCE_NODE_TYPE,
208 NULL, /* module event handler */
209 ng_source_constructor,
210 ng_source_rcvmsg,
211 ng_source_rmnode,
212 ng_source_newhook,
213 NULL, /* findhook */
214 NULL,
215 ng_source_rcvdata, /* rcvdata */
216 ng_source_rcvdata, /* rcvdataq */
217 ng_source_disconnect,
218 ng_source_cmds
219};
220NETGRAPH_INIT(source, &ng_source_typestruct);
221
222/*
223 * Node constructor
224 */
225static int
226ng_source_constructor(node_p *nodep)
227{
228 sc_p sc;
229 int error = 0;
230
231 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT);
232 if (sc == NULL)
233 return (ENOMEM);
234 bzero(sc, sizeof(*sc));
235
236 if ((error = ng_make_node_common(&ng_source_typestruct, nodep))) {
237 FREE(sc, M_NETGRAPH);
238 return (error);
239 }
240 (*nodep)->private = sc;
241 sc->node = *nodep;
242 sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */
243 callout_handle_init(&sc->intr_ch);
244 return (0);
245}
246
247/*
248 * Add a hook
249 */
250static int
251ng_source_newhook(node_p node, hook_p hook, const char *name)
252{
253 const sc_p sc = node->private;
254
255 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
256 if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
257 sc->input.hook = hook;
258 hook->private = &sc->input;
259 } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
260 sc->output.hook = hook;
261 hook->private = &sc->output;
262 sc->output_ifp = 0;
263 bzero(&sc->stats, sizeof(sc->stats));
264 } else
265 return (EINVAL);
266 return (0);
267}
268
269/*
270 * Receive a control message
271 */
272static int
273ng_source_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
274 struct ng_mesg **rptr)
275{
276 const sc_p sc = node->private;
277 struct ng_mesg *resp = NULL;
278 int error = 0;
279
280 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
281 switch (msg->header.typecookie) {
282 case NGM_SOURCE_COOKIE:
283 switch (msg->header.cmd) {
284 case NGM_SOURCE_GET_STATS:
285 case NGM_SOURCE_CLR_STATS:
286 case NGM_SOURCE_GETCLR_STATS:
287 {
288 struct ng_source_stats *stats;
289
290 if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
291 NG_MKRESPONSE(resp, msg,
292 sizeof(*stats), M_NOWAIT);
293 if (resp == NULL) {
294 error = ENOMEM;
295 goto done;
296 }
297 sc->stats.queueOctets = sc->queueOctets;
91 u_int32_t queueOctets;
92};
93typedef struct privdata *sc_p;
94
95/* Node flags */
96#define NG_SOURCE_ACTIVE (NGF_TYPE1)
97
98/* XXX */
99#if 1
100#undef KASSERT
101#define KASSERT(expr,msg) do { \
102 if (!(expr)) { \
103 printf msg ; \
104 panic("Assertion"); \
105 } \
106 } while(0)
107#endif
108
109/* Netgraph methods */
110static ng_constructor_t ng_source_constructor;
111static ng_rcvmsg_t ng_source_rcvmsg;
112static ng_shutdown_t ng_source_rmnode;
113static ng_newhook_t ng_source_newhook;
114static ng_rcvdata_t ng_source_rcvdata;
115static ng_disconnect_t ng_source_disconnect;
116
117/* Other functions */
118static timeout_t ng_source_intr;
119static int ng_source_get_output_ifp (sc_p);
120static void ng_source_clr_data (sc_p);
121static void ng_source_start (sc_p);
122static void ng_source_stop (sc_p);
123static int ng_source_send (sc_p, int, int *);
124
125
126/* Parse type for timeval */
127static const struct ng_parse_struct_field ng_source_timeval_type_fields[] =
128{
129 { "tv_sec", &ng_parse_int32_type },
130 { "tv_usec", &ng_parse_int32_type },
131 { NULL }
132};
133const struct ng_parse_type ng_source_timeval_type = {
134 &ng_parse_struct_type,
135 &ng_source_timeval_type_fields
136};
137
138/* Parse type for struct ng_source_stats */
139static const struct ng_parse_struct_field ng_source_stats_type_fields[]
140 = NG_SOURCE_STATS_TYPE_INFO;
141static const struct ng_parse_type ng_source_stats_type = {
142 &ng_parse_struct_type,
143 &ng_source_stats_type_fields
144};
145
146/* List of commands and how to convert arguments to/from ASCII */
147static const struct ng_cmdlist ng_source_cmds[] = {
148 {
149 NGM_SOURCE_COOKIE,
150 NGM_SOURCE_GET_STATS,
151 "getstats",
152 NULL,
153 &ng_source_stats_type
154 },
155 {
156 NGM_SOURCE_COOKIE,
157 NGM_SOURCE_CLR_STATS,
158 "clrstats",
159 NULL,
160 NULL
161 },
162 {
163 NGM_SOURCE_COOKIE,
164 NGM_SOURCE_GETCLR_STATS,
165 "getclrstats",
166 NULL,
167 &ng_source_stats_type
168 },
169 {
170 NGM_SOURCE_COOKIE,
171 NGM_SOURCE_START,
172 "start",
173 &ng_parse_uint64_type,
174 NULL
175 },
176 {
177 NGM_SOURCE_COOKIE,
178 NGM_SOURCE_STOP,
179 "stop",
180 NULL,
181 NULL
182 },
183 {
184 NGM_SOURCE_COOKIE,
185 NGM_SOURCE_CLR_DATA,
186 "clrdata",
187 NULL,
188 NULL
189 },
190 { 0 }
191};
192
193/* Netgraph type descriptor */
194static struct ng_type ng_source_typestruct = {
195 NG_VERSION,
196 NG_SOURCE_NODE_TYPE,
197 NULL, /* module event handler */
198 ng_source_constructor,
199 ng_source_rcvmsg,
200 ng_source_rmnode,
201 ng_source_newhook,
202 NULL, /* findhook */
203 NULL,
204 ng_source_rcvdata, /* rcvdata */
205 ng_source_rcvdata, /* rcvdataq */
206 ng_source_disconnect,
207 ng_source_cmds
208};
209NETGRAPH_INIT(source, &ng_source_typestruct);
210
211/*
212 * Node constructor
213 */
214static int
215ng_source_constructor(node_p *nodep)
216{
217 sc_p sc;
218 int error = 0;
219
220 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT);
221 if (sc == NULL)
222 return (ENOMEM);
223 bzero(sc, sizeof(*sc));
224
225 if ((error = ng_make_node_common(&ng_source_typestruct, nodep))) {
226 FREE(sc, M_NETGRAPH);
227 return (error);
228 }
229 (*nodep)->private = sc;
230 sc->node = *nodep;
231 sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */
232 callout_handle_init(&sc->intr_ch);
233 return (0);
234}
235
236/*
237 * Add a hook
238 */
239static int
240ng_source_newhook(node_p node, hook_p hook, const char *name)
241{
242 const sc_p sc = node->private;
243
244 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
245 if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
246 sc->input.hook = hook;
247 hook->private = &sc->input;
248 } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
249 sc->output.hook = hook;
250 hook->private = &sc->output;
251 sc->output_ifp = 0;
252 bzero(&sc->stats, sizeof(sc->stats));
253 } else
254 return (EINVAL);
255 return (0);
256}
257
258/*
259 * Receive a control message
260 */
261static int
262ng_source_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
263 struct ng_mesg **rptr)
264{
265 const sc_p sc = node->private;
266 struct ng_mesg *resp = NULL;
267 int error = 0;
268
269 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
270 switch (msg->header.typecookie) {
271 case NGM_SOURCE_COOKIE:
272 switch (msg->header.cmd) {
273 case NGM_SOURCE_GET_STATS:
274 case NGM_SOURCE_CLR_STATS:
275 case NGM_SOURCE_GETCLR_STATS:
276 {
277 struct ng_source_stats *stats;
278
279 if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
280 NG_MKRESPONSE(resp, msg,
281 sizeof(*stats), M_NOWAIT);
282 if (resp == NULL) {
283 error = ENOMEM;
284 goto done;
285 }
286 sc->stats.queueOctets = sc->queueOctets;
298 sc->stats.queueFrames =
299sc->snd_queue.ifq_len;
287 sc->stats.queueFrames = sc->snd_queue.ifq_len;
300 if ((sc->node->flags & NG_SOURCE_ACTIVE)
301 && !timevalisset(&sc->stats.endTime)) {
288 if ((sc->node->flags & NG_SOURCE_ACTIVE)
289 && !timevalisset(&sc->stats.endTime)) {
302
303getmicrotime(&sc->stats.elapsedTime);
290 getmicrotime(&sc->stats.elapsedTime);
304 timevalsub(&sc->stats.elapsedTime,
291 timevalsub(&sc->stats.elapsedTime,
305
306&sc->stats.startTime);
292 &sc->stats.startTime);
307 }
293 }
308 stats = (struct ng_source_stats
309*)resp->data;
294 stats = (struct ng_source_stats *)resp->data;
310 bcopy(&sc->stats, stats, sizeof(* stats));
311 }
312 if (msg->header.cmd != NGM_SOURCE_GET_STATS)
313 bzero(&sc->stats, sizeof(sc->stats));
314 }
315 break;
316 case NGM_SOURCE_START:
317 {
318 u_int64_t packets = *(u_int64_t *)msg->data;
319 if (sc->output.hook == NULL) {
295 bcopy(&sc->stats, stats, sizeof(* stats));
296 }
297 if (msg->header.cmd != NGM_SOURCE_GET_STATS)
298 bzero(&sc->stats, sizeof(sc->stats));
299 }
300 break;
301 case NGM_SOURCE_START:
302 {
303 u_int64_t packets = *(u_int64_t *)msg->data;
304 if (sc->output.hook == NULL) {
320 printf("%s: start on node with no output
321hook\n", __FUNCTION__);
305 printf("%s: start on node with no output hook\n"
306 , __FUNCTION__);
322 error = EINVAL;
323 break;
324 }
325 /* TODO validation of packets */
326 sc->packets = packets;
327 ng_source_start(sc);
328 }
329 break;
330 case NGM_SOURCE_STOP:
331 ng_source_stop(sc);
332 break;
333 case NGM_SOURCE_CLR_DATA:
334 ng_source_clr_data(sc);
335 break;
336 default:
337 error = EINVAL;
338 break;
339 }
340 break;
341 default:
342 error = EINVAL;
343 break;
344 }
345 if (rptr)
346 *rptr = resp;
347 else if (resp)
348 FREE(resp, M_NETGRAPH);
349
350done:
351 FREE(msg, M_NETGRAPH);
352 return (error);
353}
354
355/*
356 * Receive data on a hook
357 *
358 * If data comes in the input hook, enqueue it on the send queue.
359 * If data comes in the output hook, discard it.
360 */
361static int
362ng_source_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
363{
364 const sc_p sc = hook->node->private;
307 error = EINVAL;
308 break;
309 }
310 /* TODO validation of packets */
311 sc->packets = packets;
312 ng_source_start(sc);
313 }
314 break;
315 case NGM_SOURCE_STOP:
316 ng_source_stop(sc);
317 break;
318 case NGM_SOURCE_CLR_DATA:
319 ng_source_clr_data(sc);
320 break;
321 default:
322 error = EINVAL;
323 break;
324 }
325 break;
326 default:
327 error = EINVAL;
328 break;
329 }
330 if (rptr)
331 *rptr = resp;
332 else if (resp)
333 FREE(resp, M_NETGRAPH);
334
335done:
336 FREE(msg, M_NETGRAPH);
337 return (error);
338}
339
340/*
341 * Receive data on a hook
342 *
343 * If data comes in the input hook, enqueue it on the send queue.
344 * If data comes in the output hook, discard it.
345 */
346static int
347ng_source_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
348{
349 const sc_p sc = hook->node->private;
365 struct source_hookinfo *const hinfo = (struct source_hookinfo *)
366hook->private;
350 struct source_hookinfo *const hinfo;
367 int error = 0;
368
351 int error = 0;
352
353 hinfo = (struct source_hookinfo *) hook->private;
369 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
370 KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__));
371
372 /* Which hook? */
373 if (hinfo == &sc->output) {
374 /* discard */
375 NG_FREE_DATA(m, meta);
376 return (error);
377 }
378 KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__));
379
380 if ((m->m_flags & M_PKTHDR) == 0) {
381 printf("%s: mbuf without PKTHDR\n", __FUNCTION__);
382 NG_FREE_DATA(m, meta);
383 return (EINVAL);
384 }
385
386 /* XXX we discard the meta data for now */
387 NG_FREE_META(meta);
388
389 /* enque packet */
390 /* XXX should we check IF_QFULL() ? */
391 IF_ENQUEUE(&sc->snd_queue, m);
392 sc->queueOctets += m->m_pkthdr.len;
393
394 return (0);
395}
396
397/*
398 * Shutdown processing
399 */
400static int
401ng_source_rmnode(node_p node)
402{
403 const sc_p sc = node->private;
404
405 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
406 node->flags |= NG_INVALID;
407 ng_source_stop(sc);
408 ng_cutlinks(node);
409 ng_source_clr_data(sc);
410 ng_unname(node);
411 node->private = NULL;
412 ng_unref(sc->node);
413 FREE(sc, M_NETGRAPH);
414 return (0);
415}
416
417/*
418 * Hook disconnection
419 */
420static int
421ng_source_disconnect(hook_p hook)
422{
354 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
355 KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__));
356
357 /* Which hook? */
358 if (hinfo == &sc->output) {
359 /* discard */
360 NG_FREE_DATA(m, meta);
361 return (error);
362 }
363 KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__));
364
365 if ((m->m_flags & M_PKTHDR) == 0) {
366 printf("%s: mbuf without PKTHDR\n", __FUNCTION__);
367 NG_FREE_DATA(m, meta);
368 return (EINVAL);
369 }
370
371 /* XXX we discard the meta data for now */
372 NG_FREE_META(meta);
373
374 /* enque packet */
375 /* XXX should we check IF_QFULL() ? */
376 IF_ENQUEUE(&sc->snd_queue, m);
377 sc->queueOctets += m->m_pkthdr.len;
378
379 return (0);
380}
381
382/*
383 * Shutdown processing
384 */
385static int
386ng_source_rmnode(node_p node)
387{
388 const sc_p sc = node->private;
389
390 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
391 node->flags |= NG_INVALID;
392 ng_source_stop(sc);
393 ng_cutlinks(node);
394 ng_source_clr_data(sc);
395 ng_unname(node);
396 node->private = NULL;
397 ng_unref(sc->node);
398 FREE(sc, M_NETGRAPH);
399 return (0);
400}
401
402/*
403 * Hook disconnection
404 */
405static int
406ng_source_disconnect(hook_p hook)
407{
423 struct source_hookinfo *const hinfo = (struct source_hookinfo *)
424hook->private;
425 sc_p sc = (sc_p) hinfo->hook->node->private;
408 struct source_hookinfo *const hinfo;
409 sc_p sc;
426
410
411 hinfo = (struct source_hookinfo *) hook->private;
412 sc = (sc_p) hinfo->hook->node->private;
427 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
428 hinfo->hook = NULL;
429 if (hook->node->numhooks == 0 || hinfo == &sc->output)
430 ng_rmnode(hook->node);
431 return (0);
432}
433
434/*
435 * Set sc->output_ifp to point to the the struct ifnet of the interface
436 * reached via our output hook.
437 */
438static int
439ng_source_get_output_ifp(sc_p sc)
440{
441 struct ng_mesg *msg, *rsp;
442 struct ifnet *ifp;
443 u_int32_t if_index;
444 int error = 0;
445 int s;
446
447 sc->output_ifp = NULL;
448
449 /* Ask the attached node for the connected interface's index */
413 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
414 hinfo->hook = NULL;
415 if (hook->node->numhooks == 0 || hinfo == &sc->output)
416 ng_rmnode(hook->node);
417 return (0);
418}
419
420/*
421 * Set sc->output_ifp to point to the the struct ifnet of the interface
422 * reached via our output hook.
423 */
424static int
425ng_source_get_output_ifp(sc_p sc)
426{
427 struct ng_mesg *msg, *rsp;
428 struct ifnet *ifp;
429 u_int32_t if_index;
430 int error = 0;
431 int s;
432
433 sc->output_ifp = NULL;
434
435 /* Ask the attached node for the connected interface's index */
450 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0,
451M_NOWAIT);
436 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT);
452 if (msg == NULL)
453 return (ENOBUFS);
454
455 error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, &rsp);
456 if (error != 0)
457 return (error);
458
459 if (rsp == NULL)
460 return (EINVAL);
461
462 if (rsp->header.arglen < sizeof(u_int32_t))
463 return (EINVAL);
464
465 if_index = *(u_int32_t *)rsp->data;
466 /* Could use ifindex2ifnet[if_index] except that we have no
467 * way of verifying if_index is valid since if_indexlim is
468 * local to if_attach()
469 */
470 TAILQ_FOREACH(ifp, &ifnet, if_link) {
471 if (ifp->if_index == if_index)
472 break;
473 }
474
475 if (ifp == NULL) {
437 if (msg == NULL)
438 return (ENOBUFS);
439
440 error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, &rsp);
441 if (error != 0)
442 return (error);
443
444 if (rsp == NULL)
445 return (EINVAL);
446
447 if (rsp->header.arglen < sizeof(u_int32_t))
448 return (EINVAL);
449
450 if_index = *(u_int32_t *)rsp->data;
451 /* Could use ifindex2ifnet[if_index] except that we have no
452 * way of verifying if_index is valid since if_indexlim is
453 * local to if_attach()
454 */
455 TAILQ_FOREACH(ifp, &ifnet, if_link) {
456 if (ifp->if_index == if_index)
457 break;
458 }
459
460 if (ifp == NULL) {
476 printf("%s: can't find interface %d\n", __FUNCTION__,
477if_index);
461 printf("%s: can't find interface %d\n", __FUNCTION__, if_index);
478 return (EINVAL);
479 }
480 sc->output_ifp = ifp;
481
482#if 1
483 /* XXX mucking with a drivers ifqueue size is ugly but we need it
484 * to queue a lot of packets to get close to line rate on a gigabit
485 * interface with small packets.
486 * XXX we should restore the original value at stop or disconnect
487 */
488 s = splimp(); /* XXX is this required? */
489 if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN)
490 {
491 printf("ng_source: changing ifq_maxlen from %d to %d\n",
462 return (EINVAL);
463 }
464 sc->output_ifp = ifp;
465
466#if 1
467 /* XXX mucking with a drivers ifqueue size is ugly but we need it
468 * to queue a lot of packets to get close to line rate on a gigabit
469 * interface with small packets.
470 * XXX we should restore the original value at stop or disconnect
471 */
472 s = splimp(); /* XXX is this required? */
473 if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN)
474 {
475 printf("ng_source: changing ifq_maxlen from %d to %d\n",
492 ifp->if_snd.ifq_maxlen,
493NG_SOURCE_DRIVER_IFQ_MAXLEN);
476 ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
494 ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
495 }
496 splx(s);
497#endif
498 return (0);
499}
500
501/*
502 * Set the attached ethernet node's ethernet source address override flag.
503 */
504static int
505ng_source_set_autosrc(sc_p sc, u_int32_t flag)
506{
507 struct ng_mesg *msg;
508 int error = 0;
509
510 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
511 sizeof (u_int32_t), M_NOWAIT);
512 if (msg == NULL)
513 return(ENOBUFS);
514
515 *(u_int32_t *)msg->data = flag;
516 error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, NULL);
517 return (error);
518}
519
520/*
521 * Clear out the data we've queued
522 */
523static void
524ng_source_clr_data (sc_p sc)
525{
526 struct mbuf *m;
527
528 SPLASSERT(net, __FUNCTION__);
529 for (;;) {
530 IF_DEQUEUE(&sc->snd_queue, m);
531 if (m == NULL)
532 break;
533 NG_FREE_M(m);
534 }
535 sc->queueOctets = 0;
536}
537
538/*
539 * Start sending queued data out the output hook
540 */
541static void
542ng_source_start (sc_p sc)
543{
544 SPLASSERT(net, __FUNCTION__);
545 KASSERT(sc->output.hook != NULL,
546 ("%s: output hook unconnected", __FUNCTION__));
547 if ((sc->node->flags & NG_SOURCE_ACTIVE) == 0) {
477 ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
478 }
479 splx(s);
480#endif
481 return (0);
482}
483
484/*
485 * Set the attached ethernet node's ethernet source address override flag.
486 */
487static int
488ng_source_set_autosrc(sc_p sc, u_int32_t flag)
489{
490 struct ng_mesg *msg;
491 int error = 0;
492
493 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
494 sizeof (u_int32_t), M_NOWAIT);
495 if (msg == NULL)
496 return(ENOBUFS);
497
498 *(u_int32_t *)msg->data = flag;
499 error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, NULL);
500 return (error);
501}
502
503/*
504 * Clear out the data we've queued
505 */
506static void
507ng_source_clr_data (sc_p sc)
508{
509 struct mbuf *m;
510
511 SPLASSERT(net, __FUNCTION__);
512 for (;;) {
513 IF_DEQUEUE(&sc->snd_queue, m);
514 if (m == NULL)
515 break;
516 NG_FREE_M(m);
517 }
518 sc->queueOctets = 0;
519}
520
521/*
522 * Start sending queued data out the output hook
523 */
524static void
525ng_source_start (sc_p sc)
526{
527 SPLASSERT(net, __FUNCTION__);
528 KASSERT(sc->output.hook != NULL,
529 ("%s: output hook unconnected", __FUNCTION__));
530 if ((sc->node->flags & NG_SOURCE_ACTIVE) == 0) {
548 if (sc->output_ifp == NULL && ng_source_get_output_ifp(sc)
549!= 0)
531 if (sc->output_ifp == NULL && ng_source_get_output_ifp(sc) != 0)
550 return;
551 ng_source_set_autosrc(sc, 0);
552 sc->node->flags |= NG_SOURCE_ACTIVE;
553 timevalclear(&sc->stats.elapsedTime);
554 timevalclear(&sc->stats.endTime);
555 getmicrotime(&sc->stats.startTime);
556 sc->intr_ch = timeout(ng_source_intr, sc, 0);
557 }
558}
559
560/*
561 * Stop sending queued data out the output hook
562 */
563static void
564ng_source_stop (sc_p sc)
565{
566 SPLASSERT(net, __FUNCTION__);
567 if (sc->node->flags & NG_SOURCE_ACTIVE) {
568 untimeout(ng_source_intr, sc, sc->intr_ch);
569 sc->node->flags &= ~NG_SOURCE_ACTIVE;
570 getmicrotime(&sc->stats.endTime);
571 sc->stats.elapsedTime = sc->stats.endTime;
572 timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
573 /* XXX should set this to the initial value instead */
574 ng_source_set_autosrc(sc, 1);
575 }
576}
577
578/*
579 * While active called every NG_SOURCE_INTR_TICKS ticks.
580 * Sends as many packets as the interface connected to our
581 * output hook is able to enqueue.
582 */
583static void
584ng_source_intr (void *arg)
585{
586 const sc_p sc = (sc_p) arg;
587 struct ifqueue *ifq;
588 int packets;
589
590 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
591
592 callout_handle_init(&sc->intr_ch);
593 if (sc->packets == 0 || sc->output.hook == NULL
594 || (sc->node->flags & NG_SOURCE_ACTIVE) == 0) {
595 ng_source_stop(sc);
596 return;
597 }
598
599 ifq = &sc->output_ifp->if_snd;
600 packets = ifq->ifq_maxlen - ifq->ifq_len;
601 ng_source_send(sc, packets, NULL);
602 if (sc->packets == 0) {
603 int s = splnet();
604 ng_source_stop(sc);
605 splx(s);
606 } else
532 return;
533 ng_source_set_autosrc(sc, 0);
534 sc->node->flags |= NG_SOURCE_ACTIVE;
535 timevalclear(&sc->stats.elapsedTime);
536 timevalclear(&sc->stats.endTime);
537 getmicrotime(&sc->stats.startTime);
538 sc->intr_ch = timeout(ng_source_intr, sc, 0);
539 }
540}
541
542/*
543 * Stop sending queued data out the output hook
544 */
545static void
546ng_source_stop (sc_p sc)
547{
548 SPLASSERT(net, __FUNCTION__);
549 if (sc->node->flags & NG_SOURCE_ACTIVE) {
550 untimeout(ng_source_intr, sc, sc->intr_ch);
551 sc->node->flags &= ~NG_SOURCE_ACTIVE;
552 getmicrotime(&sc->stats.endTime);
553 sc->stats.elapsedTime = sc->stats.endTime;
554 timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
555 /* XXX should set this to the initial value instead */
556 ng_source_set_autosrc(sc, 1);
557 }
558}
559
560/*
561 * While active called every NG_SOURCE_INTR_TICKS ticks.
562 * Sends as many packets as the interface connected to our
563 * output hook is able to enqueue.
564 */
565static void
566ng_source_intr (void *arg)
567{
568 const sc_p sc = (sc_p) arg;
569 struct ifqueue *ifq;
570 int packets;
571
572 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
573
574 callout_handle_init(&sc->intr_ch);
575 if (sc->packets == 0 || sc->output.hook == NULL
576 || (sc->node->flags & NG_SOURCE_ACTIVE) == 0) {
577 ng_source_stop(sc);
578 return;
579 }
580
581 ifq = &sc->output_ifp->if_snd;
582 packets = ifq->ifq_maxlen - ifq->ifq_len;
583 ng_source_send(sc, packets, NULL);
584 if (sc->packets == 0) {
585 int s = splnet();
586 ng_source_stop(sc);
587 splx(s);
588 } else
607 sc->intr_ch = timeout(ng_source_intr, sc,
608NG_SOURCE_INTR_TICKS);
589 sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS);
609}
610
611/*
612 * Send packets out our output hook
613 */
614static int
615ng_source_send (sc_p sc, int tosend, int *sent_p)
616{
617 struct ifqueue tmp_queue;
618 struct mbuf *m, *m2;
619 int sent = 0;
620 int error = 0;
621 int s, s2;
622
623 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
624 KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__));
625 KASSERT(sc->node->flags & NG_SOURCE_ACTIVE,
626 ("%s: inactive node", __FUNCTION__));
627
628 if ((u_int64_t)tosend > sc->packets)
629 tosend = sc->packets;
630
631 /* Copy the required number of packets to a temporary queue */
632 bzero (&tmp_queue, sizeof (tmp_queue));
633 for (sent = 0; error == 0 && sent < tosend; ++sent) {
634 s = splnet();
635 IF_DEQUEUE(&sc->snd_queue, m);
636 splx(s);
637 if (m == NULL)
638 break;
639
640 /* duplicate the packet */
641 m2 = m_copypacket(m, M_NOWAIT);
642 if (m2 == NULL) {
643 s = splnet();
644 IF_PREPEND(&sc->snd_queue, m);
645 splx(s);
646 error = ENOBUFS;
647 break;
648 }
649
650 /* re-enqueue the original packet for us */
651 s = splnet();
652 IF_ENQUEUE(&sc->snd_queue, m);
653 splx(s);
654
655 /* queue the copy for sending at smplimp */
656 IF_ENQUEUE(&tmp_queue, m2);
657 }
658
659 sent = 0;
660 s = splimp();
661 for (;;) {
662 IF_DEQUEUE(&tmp_queue, m2);
663 if (m2 == NULL)
664 break;
665 if (error == 0) {
666 ++sent;
667 sc->stats.outFrames++;
668 sc->stats.outOctets += m2->m_pkthdr.len;
669 s2 = splnet();
670 NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
671 splx(s2);
672 } else {
673 NG_FREE_M(m2);
674 }
675 }
676 splx(s);
677
678 sc->packets -= sent;
679 if (sent_p != NULL)
680 *sent_p = sent;
681 return (error);
682}
590}
591
592/*
593 * Send packets out our output hook
594 */
595static int
596ng_source_send (sc_p sc, int tosend, int *sent_p)
597{
598 struct ifqueue tmp_queue;
599 struct mbuf *m, *m2;
600 int sent = 0;
601 int error = 0;
602 int s, s2;
603
604 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
605 KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__));
606 KASSERT(sc->node->flags & NG_SOURCE_ACTIVE,
607 ("%s: inactive node", __FUNCTION__));
608
609 if ((u_int64_t)tosend > sc->packets)
610 tosend = sc->packets;
611
612 /* Copy the required number of packets to a temporary queue */
613 bzero (&tmp_queue, sizeof (tmp_queue));
614 for (sent = 0; error == 0 && sent < tosend; ++sent) {
615 s = splnet();
616 IF_DEQUEUE(&sc->snd_queue, m);
617 splx(s);
618 if (m == NULL)
619 break;
620
621 /* duplicate the packet */
622 m2 = m_copypacket(m, M_NOWAIT);
623 if (m2 == NULL) {
624 s = splnet();
625 IF_PREPEND(&sc->snd_queue, m);
626 splx(s);
627 error = ENOBUFS;
628 break;
629 }
630
631 /* re-enqueue the original packet for us */
632 s = splnet();
633 IF_ENQUEUE(&sc->snd_queue, m);
634 splx(s);
635
636 /* queue the copy for sending at smplimp */
637 IF_ENQUEUE(&tmp_queue, m2);
638 }
639
640 sent = 0;
641 s = splimp();
642 for (;;) {
643 IF_DEQUEUE(&tmp_queue, m2);
644 if (m2 == NULL)
645 break;
646 if (error == 0) {
647 ++sent;
648 sc->stats.outFrames++;
649 sc->stats.outOctets += m2->m_pkthdr.len;
650 s2 = splnet();
651 NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
652 splx(s2);
653 } else {
654 NG_FREE_M(m2);
655 }
656 }
657 splx(s);
658
659 sc->packets -= sent;
660 if (sent_p != NULL)
661 *sent_p = sent;
662 return (error);
663}