Deleted Added
full compact
ng_source.c (109623) ng_source.c (111119)
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.; provided,
10 * however, that:
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
13 * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
14 * trademarks, including the mark "SANDVINE" on advertising, endorsements,
15 * or otherwise except as such appears in the above copyright notice or in
16 * the software.
17 *
18 * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
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
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
27 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
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 *
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.; provided,
10 * however, that:
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
13 * 2. No rights are granted, in any manner or form, to use Sandvine Inc.
14 * trademarks, including the mark "SANDVINE" on advertising, endorsements,
15 * or otherwise except as such appears in the above copyright notice or in
16 * the software.
17 *
18 * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM
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
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
27 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
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 *
37 * $FreeBSD: head/sys/netgraph/ng_source.c 109623 2003-01-21 08:56:16Z alfred $
37 * $FreeBSD: head/sys/netgraph/ng_source.c 111119 2003-02-19 05:47:46Z imp $
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;
87 struct ifqueue snd_queue; /* packets to send */
88 struct ifnet *output_ifp;
89 struct callout_handle intr_ch;
90 u_int64_t packets; /* packets to send */
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 void ng_source_request_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 *);
124static int ng_source_store_output_ifp(sc_p sc,
125 struct ng_mesg *msg);
126
127
128/* Parse type for timeval */
129static const struct ng_parse_struct_field ng_source_timeval_type_fields[] =
130{
131 { "tv_sec", &ng_parse_int32_type },
132 { "tv_usec", &ng_parse_int32_type },
133 { NULL }
134};
135const struct ng_parse_type ng_source_timeval_type = {
136 &ng_parse_struct_type,
137 &ng_source_timeval_type_fields
138};
139
140/* Parse type for struct ng_source_stats */
141static const struct ng_parse_struct_field ng_source_stats_type_fields[]
142 = NG_SOURCE_STATS_TYPE_INFO;
143static const struct ng_parse_type ng_source_stats_type = {
144 &ng_parse_struct_type,
145 &ng_source_stats_type_fields
146};
147
148/* List of commands and how to convert arguments to/from ASCII */
149static const struct ng_cmdlist ng_source_cmds[] = {
150 {
151 NGM_SOURCE_COOKIE,
152 NGM_SOURCE_GET_STATS,
153 "getstats",
154 NULL,
155 &ng_source_stats_type
156 },
157 {
158 NGM_SOURCE_COOKIE,
159 NGM_SOURCE_CLR_STATS,
160 "clrstats",
161 NULL,
162 NULL
163 },
164 {
165 NGM_SOURCE_COOKIE,
166 NGM_SOURCE_GETCLR_STATS,
167 "getclrstats",
168 NULL,
169 &ng_source_stats_type
170 },
171 {
172 NGM_SOURCE_COOKIE,
173 NGM_SOURCE_START,
174 "start",
175 &ng_parse_uint64_type,
176 NULL
177 },
178 {
179 NGM_SOURCE_COOKIE,
180 NGM_SOURCE_STOP,
181 "stop",
182 NULL,
183 NULL
184 },
185 {
186 NGM_SOURCE_COOKIE,
187 NGM_SOURCE_CLR_DATA,
188 "clrdata",
189 NULL,
190 NULL
191 },
192 { 0 }
193};
194
195/* Netgraph type descriptor */
196static struct ng_type ng_source_typestruct = {
197 NG_VERSION,
198 NG_SOURCE_NODE_TYPE,
199 NULL, /* module event handler */
200 ng_source_constructor,
201 ng_source_rcvmsg,
202 ng_source_rmnode,
203 ng_source_newhook,
204 NULL, /* findhook */
205 NULL,
206 ng_source_rcvdata, /* rcvdata */
207 ng_source_disconnect,
208 ng_source_cmds
209};
210NETGRAPH_INIT(source, &ng_source_typestruct);
211
212/*
213 * Node constructor
214 */
215static int
216ng_source_constructor(node_p node)
217{
218 sc_p sc;
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 NG_NODE_SET_PRIVATE(node, sc);
226 sc->node = node;
227 sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */
228 callout_handle_init(&sc->intr_ch); /* XXX fix.. will
229 cause problems. */
230 return (0);
231}
232
233/*
234 * Add a hook
235 */
236static int
237ng_source_newhook(node_p node, hook_p hook, const char *name)
238{
239 sc_p sc;
240
241 sc = NG_NODE_PRIVATE(node);
242 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
243 if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
244 sc->input.hook = hook;
245 NG_HOOK_SET_PRIVATE(hook, &sc->input);
246 } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
247 sc->output.hook = hook;
248 NG_HOOK_SET_PRIVATE(hook, &sc->output);
249 sc->output_ifp = 0;
250 bzero(&sc->stats, sizeof(sc->stats));
251 } else
252 return (EINVAL);
253 return (0);
254}
255
256/*
257 * Receive a control message
258 */
259static int
260ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
261{
262 sc_p sc;
263 struct ng_mesg *resp = NULL;
264 int error = 0;
265 struct ng_mesg *msg;
266
267 sc = NG_NODE_PRIVATE(node);
268 NGI_GET_MSG(item, msg);
269 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
270 switch (msg->header.typecookie) {
271 case NGM_SOURCE_COOKIE:
272 if (msg->header.flags & NGF_RESP) {
273 error = EINVAL;
274 break;
275 }
276 switch (msg->header.cmd) {
277 case NGM_SOURCE_GET_STATS:
278 case NGM_SOURCE_CLR_STATS:
279 case NGM_SOURCE_GETCLR_STATS:
280 {
281 struct ng_source_stats *stats;
282
283 if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
284 NG_MKRESPONSE(resp, msg,
285 sizeof(*stats), M_NOWAIT);
286 if (resp == NULL) {
287 error = ENOMEM;
288 goto done;
289 }
290 sc->stats.queueOctets = sc->queueOctets;
291 sc->stats.queueFrames = sc->snd_queue.ifq_len;
292 if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
293 && !timevalisset(&sc->stats.endTime)) {
294 getmicrotime(&sc->stats.elapsedTime);
295 timevalsub(&sc->stats.elapsedTime,
296 &sc->stats.startTime);
297 }
298 stats = (struct ng_source_stats *)resp->data;
299 bcopy(&sc->stats, stats, sizeof(* stats));
300 }
301 if (msg->header.cmd != NGM_SOURCE_GET_STATS)
302 bzero(&sc->stats, sizeof(sc->stats));
303 }
304 break;
305 case NGM_SOURCE_START:
306 {
307 u_int64_t packets = *(u_int64_t *)msg->data;
308 if (sc->output.hook == NULL) {
309 printf("%s: start on node with no output hook\n"
310 , __FUNCTION__);
311 error = EINVAL;
312 break;
313 }
314 /* TODO validation of packets */
315 sc->packets = packets;
316 ng_source_start(sc);
317 }
318 break;
319 case NGM_SOURCE_STOP:
320 ng_source_stop(sc);
321 break;
322 case NGM_SOURCE_CLR_DATA:
323 ng_source_clr_data(sc);
324 break;
325 default:
326 error = EINVAL;
327 break;
328 }
329 break;
330 case NGM_ETHER_COOKIE:
331 if (!(msg->header.flags & NGF_RESP)) {
332 error = EINVAL;
333 break;
334 }
335 switch (msg->header.cmd) {
336 case NGM_ETHER_GET_IFINDEX:
337 if (ng_source_store_output_ifp(sc, msg) == 0) {
338 ng_source_set_autosrc(sc, 0);
339 sc->node->nd_flags |= NG_SOURCE_ACTIVE;
340 timevalclear(&sc->stats.elapsedTime);
341 timevalclear(&sc->stats.endTime);
342 getmicrotime(&sc->stats.startTime);
343 sc->intr_ch = timeout(ng_source_intr, sc, 0);
344 }
345 break;
346 default:
347 error = EINVAL;
348 }
349 break;
350 default:
351 error = EINVAL;
352 break;
353 }
354
355done:
356 /* Take care of synchronous response, if any */
357 NG_RESPOND_MSG(error, node, item, resp);
358 /* Free the message and return */
359 NG_FREE_MSG(msg);
360 return (error);
361}
362
363/*
364 * Receive data on a hook
365 *
366 * If data comes in the input hook, enqueue it on the send queue.
367 * If data comes in the output hook, discard it.
368 */
369static int
370ng_source_rcvdata(hook_p hook, item_p item)
371{
372 sc_p sc;
373 struct source_hookinfo *hinfo;
374 int error = 0;
375 struct mbuf *m;
376
377 sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
378 NGI_GET_M(item, m);
379 NG_FREE_ITEM(item);
380 hinfo = NG_HOOK_PRIVATE(hook);
381 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
382 KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__));
383
384 /* Which hook? */
385 if (hinfo == &sc->output) {
386 /* discard */
387 NG_FREE_M(m);
388 return (error);
389 }
390 KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__));
391
392 if ((m->m_flags & M_PKTHDR) == 0) {
393 printf("%s: mbuf without PKTHDR\n", __FUNCTION__);
394 NG_FREE_M(m);
395 return (EINVAL);
396 }
397
398 /* enque packet */
399 /* XXX should we check IF_QFULL() ? */
400 IF_ENQUEUE(&sc->snd_queue, m);
401 sc->queueOctets += m->m_pkthdr.len;
402
403 return (0);
404}
405
406/*
407 * Shutdown processing
408 */
409static int
410ng_source_rmnode(node_p node)
411{
412 sc_p sc;
413
414 sc = NG_NODE_PRIVATE(node);
415 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
416 node->nd_flags |= NG_INVALID;
417 ng_source_stop(sc);
418 ng_source_clr_data(sc);
419 NG_NODE_SET_PRIVATE(node, NULL);
420 NG_NODE_UNREF(node);
421 FREE(sc, M_NETGRAPH);
422 return (0);
423}
424
425/*
426 * Hook disconnection
427 */
428static int
429ng_source_disconnect(hook_p hook)
430{
431 struct source_hookinfo *hinfo;
432 sc_p sc;
433
434 hinfo = NG_HOOK_PRIVATE(hook);
435 sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
436 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
437 hinfo->hook = NULL;
438 if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output)
439 ng_rmnode_self(NG_HOOK_NODE(hook));
440 return (0);
441}
442
443/*
444 *
445 * Ask out neighbour on the output hook side to send us it's interface
446 * information.
447 */
448static void
449ng_source_request_output_ifp(sc_p sc)
450{
451 struct ng_mesg *msg;
452 int error = 0;
453
454 sc->output_ifp = NULL;
455
456 /* Ask the attached node for the connected interface's index */
457 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT);
458 if (msg == NULL)
459 return (ENOBUFS);
460
461 NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL);
462 return (error);
463}
464
465/*
466 * Set sc->output_ifp to point to the the struct ifnet of the interface
467 * reached via our output hook.
468 */
469static int
470ng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg)
471{
472 struct ifnet *ifp;
473 u_int32_t if_index;
474 int s;
475
476 if (msg->header.arglen < sizeof(u_int32_t))
477 return (EINVAL);
478
479 if_index = *(u_int32_t *)msg->data;
480 /* Could use ifindex2ifnet[if_index] except that we have no
481 * way of verifying if_index is valid since if_indexlim is
482 * local to if_attach()
483 */
484 IFNET_RLOCK();
485 TAILQ_FOREACH(ifp, &ifnet, if_link) {
486 if (ifp->if_index == if_index)
487 break;
488 }
489 IFNET_RUNLOCK();
490
491 if (ifp == NULL) {
492 printf("%s: can't find interface %d\n", __FUNCTION__, if_index);
493 return (EINVAL);
494 }
495 sc->output_ifp = ifp;
496
497#if 1
498 /* XXX mucking with a drivers ifqueue size is ugly but we need it
499 * to queue a lot of packets to get close to line rate on a gigabit
500 * interface with small packets.
501 * XXX we should restore the original value at stop or disconnect
502 */
503 s = splimp(); /* XXX is this required? */
504 if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN)
505 {
506 printf("ng_source: changing ifq_maxlen from %d to %d\n",
507 ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
508 ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
509 }
510 splx(s);
511#endif
512 return (0);
513}
514
515/*
516 * Set the attached ethernet node's ethernet source address override flag.
517 */
518static int
519ng_source_set_autosrc(sc_p sc, u_int32_t flag)
520{
521 struct ng_mesg *msg;
522 int error = 0;
523
524 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
525 sizeof (u_int32_t), M_NOWAIT);
526 if (msg == NULL)
527 return(ENOBUFS);
528
529 *(u_int32_t *)msg->data = flag;
530 NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL);
531 return (error);
532}
533
534/*
535 * Clear out the data we've queued
536 */
537static void
538ng_source_clr_data (sc_p sc)
539{
540 struct mbuf *m;
541
542 for (;;) {
543 IF_DEQUEUE(&sc->snd_queue, m);
544 if (m == NULL)
545 break;
546 NG_FREE_M(m);
547 }
548 sc->queueOctets = 0;
549}
550
551/*
552 * Start sending queued data out the output hook
553 */
554static void
555ng_source_start (sc_p sc)
556{
557 KASSERT(sc->output.hook != NULL,
558 ("%s: output hook unconnected", __FUNCTION__));
559 if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) &&
560 (sc->output_ifp == NULL))
561 ng_source_request_output_ifp(sc);
562}
563
564/*
565 * Stop sending queued data out the output hook
566 */
567static void
568ng_source_stop (sc_p sc)
569{
570 if (sc->node->nd_flags & NG_SOURCE_ACTIVE) {
571 untimeout(ng_source_intr, sc, sc->intr_ch);
572 sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
573 getmicrotime(&sc->stats.endTime);
574 sc->stats.elapsedTime = sc->stats.endTime;
575 timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
576 /* XXX should set this to the initial value instead */
577 ng_source_set_autosrc(sc, 1);
578 }
579}
580
581/*
582 * While active called every NG_SOURCE_INTR_TICKS ticks.
583 * Sends as many packets as the interface connected to our
584 * output hook is able to enqueue.
585 */
586static void
587ng_source_intr (void *arg)
588{
589 sc_p sc = (sc_p) arg;
590 struct ifqueue *ifq;
591 int packets;
592
593 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
594
595 callout_handle_init(&sc->intr_ch);
596 if (sc->packets == 0 || sc->output.hook == NULL
597 || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
598 ng_source_stop(sc);
599 return;
600 }
601
602 ifq = &sc->output_ifp->if_snd;
603 packets = ifq->ifq_maxlen - ifq->ifq_len;
604 ng_source_send(sc, packets, NULL);
605 if (sc->packets == 0) {
606 int s = splnet();
607 ng_source_stop(sc);
608 splx(s);
609 } else
610 sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS);
611}
612
613/*
614 * Send packets out our output hook
615 */
616static int
617ng_source_send (sc_p sc, int tosend, int *sent_p)
618{
619 struct ifqueue tmp_queue;
620 struct mbuf *m, *m2;
621 int sent = 0;
622 int error = 0;
623 int s, s2;
624
625 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
626 KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__));
627 KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
628 ("%s: inactive node", __FUNCTION__));
629
630 if ((u_int64_t)tosend > sc->packets)
631 tosend = sc->packets;
632
633 /* Copy the required number of packets to a temporary queue */
634 bzero (&tmp_queue, sizeof (tmp_queue));
635 for (sent = 0; error == 0 && sent < tosend; ++sent) {
636 s = splnet();
637 IF_DEQUEUE(&sc->snd_queue, m);
638 splx(s);
639 if (m == NULL)
640 break;
641
642 /* duplicate the packet */
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;
87 struct ifqueue snd_queue; /* packets to send */
88 struct ifnet *output_ifp;
89 struct callout_handle intr_ch;
90 u_int64_t packets; /* packets to send */
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 void ng_source_request_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 *);
124static int ng_source_store_output_ifp(sc_p sc,
125 struct ng_mesg *msg);
126
127
128/* Parse type for timeval */
129static const struct ng_parse_struct_field ng_source_timeval_type_fields[] =
130{
131 { "tv_sec", &ng_parse_int32_type },
132 { "tv_usec", &ng_parse_int32_type },
133 { NULL }
134};
135const struct ng_parse_type ng_source_timeval_type = {
136 &ng_parse_struct_type,
137 &ng_source_timeval_type_fields
138};
139
140/* Parse type for struct ng_source_stats */
141static const struct ng_parse_struct_field ng_source_stats_type_fields[]
142 = NG_SOURCE_STATS_TYPE_INFO;
143static const struct ng_parse_type ng_source_stats_type = {
144 &ng_parse_struct_type,
145 &ng_source_stats_type_fields
146};
147
148/* List of commands and how to convert arguments to/from ASCII */
149static const struct ng_cmdlist ng_source_cmds[] = {
150 {
151 NGM_SOURCE_COOKIE,
152 NGM_SOURCE_GET_STATS,
153 "getstats",
154 NULL,
155 &ng_source_stats_type
156 },
157 {
158 NGM_SOURCE_COOKIE,
159 NGM_SOURCE_CLR_STATS,
160 "clrstats",
161 NULL,
162 NULL
163 },
164 {
165 NGM_SOURCE_COOKIE,
166 NGM_SOURCE_GETCLR_STATS,
167 "getclrstats",
168 NULL,
169 &ng_source_stats_type
170 },
171 {
172 NGM_SOURCE_COOKIE,
173 NGM_SOURCE_START,
174 "start",
175 &ng_parse_uint64_type,
176 NULL
177 },
178 {
179 NGM_SOURCE_COOKIE,
180 NGM_SOURCE_STOP,
181 "stop",
182 NULL,
183 NULL
184 },
185 {
186 NGM_SOURCE_COOKIE,
187 NGM_SOURCE_CLR_DATA,
188 "clrdata",
189 NULL,
190 NULL
191 },
192 { 0 }
193};
194
195/* Netgraph type descriptor */
196static struct ng_type ng_source_typestruct = {
197 NG_VERSION,
198 NG_SOURCE_NODE_TYPE,
199 NULL, /* module event handler */
200 ng_source_constructor,
201 ng_source_rcvmsg,
202 ng_source_rmnode,
203 ng_source_newhook,
204 NULL, /* findhook */
205 NULL,
206 ng_source_rcvdata, /* rcvdata */
207 ng_source_disconnect,
208 ng_source_cmds
209};
210NETGRAPH_INIT(source, &ng_source_typestruct);
211
212/*
213 * Node constructor
214 */
215static int
216ng_source_constructor(node_p node)
217{
218 sc_p sc;
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 NG_NODE_SET_PRIVATE(node, sc);
226 sc->node = node;
227 sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */
228 callout_handle_init(&sc->intr_ch); /* XXX fix.. will
229 cause problems. */
230 return (0);
231}
232
233/*
234 * Add a hook
235 */
236static int
237ng_source_newhook(node_p node, hook_p hook, const char *name)
238{
239 sc_p sc;
240
241 sc = NG_NODE_PRIVATE(node);
242 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
243 if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) {
244 sc->input.hook = hook;
245 NG_HOOK_SET_PRIVATE(hook, &sc->input);
246 } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) {
247 sc->output.hook = hook;
248 NG_HOOK_SET_PRIVATE(hook, &sc->output);
249 sc->output_ifp = 0;
250 bzero(&sc->stats, sizeof(sc->stats));
251 } else
252 return (EINVAL);
253 return (0);
254}
255
256/*
257 * Receive a control message
258 */
259static int
260ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
261{
262 sc_p sc;
263 struct ng_mesg *resp = NULL;
264 int error = 0;
265 struct ng_mesg *msg;
266
267 sc = NG_NODE_PRIVATE(node);
268 NGI_GET_MSG(item, msg);
269 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
270 switch (msg->header.typecookie) {
271 case NGM_SOURCE_COOKIE:
272 if (msg->header.flags & NGF_RESP) {
273 error = EINVAL;
274 break;
275 }
276 switch (msg->header.cmd) {
277 case NGM_SOURCE_GET_STATS:
278 case NGM_SOURCE_CLR_STATS:
279 case NGM_SOURCE_GETCLR_STATS:
280 {
281 struct ng_source_stats *stats;
282
283 if (msg->header.cmd != NGM_SOURCE_CLR_STATS) {
284 NG_MKRESPONSE(resp, msg,
285 sizeof(*stats), M_NOWAIT);
286 if (resp == NULL) {
287 error = ENOMEM;
288 goto done;
289 }
290 sc->stats.queueOctets = sc->queueOctets;
291 sc->stats.queueFrames = sc->snd_queue.ifq_len;
292 if ((sc->node->nd_flags & NG_SOURCE_ACTIVE)
293 && !timevalisset(&sc->stats.endTime)) {
294 getmicrotime(&sc->stats.elapsedTime);
295 timevalsub(&sc->stats.elapsedTime,
296 &sc->stats.startTime);
297 }
298 stats = (struct ng_source_stats *)resp->data;
299 bcopy(&sc->stats, stats, sizeof(* stats));
300 }
301 if (msg->header.cmd != NGM_SOURCE_GET_STATS)
302 bzero(&sc->stats, sizeof(sc->stats));
303 }
304 break;
305 case NGM_SOURCE_START:
306 {
307 u_int64_t packets = *(u_int64_t *)msg->data;
308 if (sc->output.hook == NULL) {
309 printf("%s: start on node with no output hook\n"
310 , __FUNCTION__);
311 error = EINVAL;
312 break;
313 }
314 /* TODO validation of packets */
315 sc->packets = packets;
316 ng_source_start(sc);
317 }
318 break;
319 case NGM_SOURCE_STOP:
320 ng_source_stop(sc);
321 break;
322 case NGM_SOURCE_CLR_DATA:
323 ng_source_clr_data(sc);
324 break;
325 default:
326 error = EINVAL;
327 break;
328 }
329 break;
330 case NGM_ETHER_COOKIE:
331 if (!(msg->header.flags & NGF_RESP)) {
332 error = EINVAL;
333 break;
334 }
335 switch (msg->header.cmd) {
336 case NGM_ETHER_GET_IFINDEX:
337 if (ng_source_store_output_ifp(sc, msg) == 0) {
338 ng_source_set_autosrc(sc, 0);
339 sc->node->nd_flags |= NG_SOURCE_ACTIVE;
340 timevalclear(&sc->stats.elapsedTime);
341 timevalclear(&sc->stats.endTime);
342 getmicrotime(&sc->stats.startTime);
343 sc->intr_ch = timeout(ng_source_intr, sc, 0);
344 }
345 break;
346 default:
347 error = EINVAL;
348 }
349 break;
350 default:
351 error = EINVAL;
352 break;
353 }
354
355done:
356 /* Take care of synchronous response, if any */
357 NG_RESPOND_MSG(error, node, item, resp);
358 /* Free the message and return */
359 NG_FREE_MSG(msg);
360 return (error);
361}
362
363/*
364 * Receive data on a hook
365 *
366 * If data comes in the input hook, enqueue it on the send queue.
367 * If data comes in the output hook, discard it.
368 */
369static int
370ng_source_rcvdata(hook_p hook, item_p item)
371{
372 sc_p sc;
373 struct source_hookinfo *hinfo;
374 int error = 0;
375 struct mbuf *m;
376
377 sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
378 NGI_GET_M(item, m);
379 NG_FREE_ITEM(item);
380 hinfo = NG_HOOK_PRIVATE(hook);
381 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
382 KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__));
383
384 /* Which hook? */
385 if (hinfo == &sc->output) {
386 /* discard */
387 NG_FREE_M(m);
388 return (error);
389 }
390 KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__));
391
392 if ((m->m_flags & M_PKTHDR) == 0) {
393 printf("%s: mbuf without PKTHDR\n", __FUNCTION__);
394 NG_FREE_M(m);
395 return (EINVAL);
396 }
397
398 /* enque packet */
399 /* XXX should we check IF_QFULL() ? */
400 IF_ENQUEUE(&sc->snd_queue, m);
401 sc->queueOctets += m->m_pkthdr.len;
402
403 return (0);
404}
405
406/*
407 * Shutdown processing
408 */
409static int
410ng_source_rmnode(node_p node)
411{
412 sc_p sc;
413
414 sc = NG_NODE_PRIVATE(node);
415 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
416 node->nd_flags |= NG_INVALID;
417 ng_source_stop(sc);
418 ng_source_clr_data(sc);
419 NG_NODE_SET_PRIVATE(node, NULL);
420 NG_NODE_UNREF(node);
421 FREE(sc, M_NETGRAPH);
422 return (0);
423}
424
425/*
426 * Hook disconnection
427 */
428static int
429ng_source_disconnect(hook_p hook)
430{
431 struct source_hookinfo *hinfo;
432 sc_p sc;
433
434 hinfo = NG_HOOK_PRIVATE(hook);
435 sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
436 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
437 hinfo->hook = NULL;
438 if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output)
439 ng_rmnode_self(NG_HOOK_NODE(hook));
440 return (0);
441}
442
443/*
444 *
445 * Ask out neighbour on the output hook side to send us it's interface
446 * information.
447 */
448static void
449ng_source_request_output_ifp(sc_p sc)
450{
451 struct ng_mesg *msg;
452 int error = 0;
453
454 sc->output_ifp = NULL;
455
456 /* Ask the attached node for the connected interface's index */
457 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT);
458 if (msg == NULL)
459 return (ENOBUFS);
460
461 NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL);
462 return (error);
463}
464
465/*
466 * Set sc->output_ifp to point to the the struct ifnet of the interface
467 * reached via our output hook.
468 */
469static int
470ng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg)
471{
472 struct ifnet *ifp;
473 u_int32_t if_index;
474 int s;
475
476 if (msg->header.arglen < sizeof(u_int32_t))
477 return (EINVAL);
478
479 if_index = *(u_int32_t *)msg->data;
480 /* Could use ifindex2ifnet[if_index] except that we have no
481 * way of verifying if_index is valid since if_indexlim is
482 * local to if_attach()
483 */
484 IFNET_RLOCK();
485 TAILQ_FOREACH(ifp, &ifnet, if_link) {
486 if (ifp->if_index == if_index)
487 break;
488 }
489 IFNET_RUNLOCK();
490
491 if (ifp == NULL) {
492 printf("%s: can't find interface %d\n", __FUNCTION__, if_index);
493 return (EINVAL);
494 }
495 sc->output_ifp = ifp;
496
497#if 1
498 /* XXX mucking with a drivers ifqueue size is ugly but we need it
499 * to queue a lot of packets to get close to line rate on a gigabit
500 * interface with small packets.
501 * XXX we should restore the original value at stop or disconnect
502 */
503 s = splimp(); /* XXX is this required? */
504 if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN)
505 {
506 printf("ng_source: changing ifq_maxlen from %d to %d\n",
507 ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN);
508 ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN;
509 }
510 splx(s);
511#endif
512 return (0);
513}
514
515/*
516 * Set the attached ethernet node's ethernet source address override flag.
517 */
518static int
519ng_source_set_autosrc(sc_p sc, u_int32_t flag)
520{
521 struct ng_mesg *msg;
522 int error = 0;
523
524 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC,
525 sizeof (u_int32_t), M_NOWAIT);
526 if (msg == NULL)
527 return(ENOBUFS);
528
529 *(u_int32_t *)msg->data = flag;
530 NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL);
531 return (error);
532}
533
534/*
535 * Clear out the data we've queued
536 */
537static void
538ng_source_clr_data (sc_p sc)
539{
540 struct mbuf *m;
541
542 for (;;) {
543 IF_DEQUEUE(&sc->snd_queue, m);
544 if (m == NULL)
545 break;
546 NG_FREE_M(m);
547 }
548 sc->queueOctets = 0;
549}
550
551/*
552 * Start sending queued data out the output hook
553 */
554static void
555ng_source_start (sc_p sc)
556{
557 KASSERT(sc->output.hook != NULL,
558 ("%s: output hook unconnected", __FUNCTION__));
559 if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) &&
560 (sc->output_ifp == NULL))
561 ng_source_request_output_ifp(sc);
562}
563
564/*
565 * Stop sending queued data out the output hook
566 */
567static void
568ng_source_stop (sc_p sc)
569{
570 if (sc->node->nd_flags & NG_SOURCE_ACTIVE) {
571 untimeout(ng_source_intr, sc, sc->intr_ch);
572 sc->node->nd_flags &= ~NG_SOURCE_ACTIVE;
573 getmicrotime(&sc->stats.endTime);
574 sc->stats.elapsedTime = sc->stats.endTime;
575 timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime);
576 /* XXX should set this to the initial value instead */
577 ng_source_set_autosrc(sc, 1);
578 }
579}
580
581/*
582 * While active called every NG_SOURCE_INTR_TICKS ticks.
583 * Sends as many packets as the interface connected to our
584 * output hook is able to enqueue.
585 */
586static void
587ng_source_intr (void *arg)
588{
589 sc_p sc = (sc_p) arg;
590 struct ifqueue *ifq;
591 int packets;
592
593 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
594
595 callout_handle_init(&sc->intr_ch);
596 if (sc->packets == 0 || sc->output.hook == NULL
597 || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) {
598 ng_source_stop(sc);
599 return;
600 }
601
602 ifq = &sc->output_ifp->if_snd;
603 packets = ifq->ifq_maxlen - ifq->ifq_len;
604 ng_source_send(sc, packets, NULL);
605 if (sc->packets == 0) {
606 int s = splnet();
607 ng_source_stop(sc);
608 splx(s);
609 } else
610 sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS);
611}
612
613/*
614 * Send packets out our output hook
615 */
616static int
617ng_source_send (sc_p sc, int tosend, int *sent_p)
618{
619 struct ifqueue tmp_queue;
620 struct mbuf *m, *m2;
621 int sent = 0;
622 int error = 0;
623 int s, s2;
624
625 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__));
626 KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__));
627 KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE,
628 ("%s: inactive node", __FUNCTION__));
629
630 if ((u_int64_t)tosend > sc->packets)
631 tosend = sc->packets;
632
633 /* Copy the required number of packets to a temporary queue */
634 bzero (&tmp_queue, sizeof (tmp_queue));
635 for (sent = 0; error == 0 && sent < tosend; ++sent) {
636 s = splnet();
637 IF_DEQUEUE(&sc->snd_queue, m);
638 splx(s);
639 if (m == NULL)
640 break;
641
642 /* duplicate the packet */
643 m2 = m_copypacket(m, M_NOWAIT);
643 m2 = m_copypacket(m, M_DONTWAIT);
644 if (m2 == NULL) {
645 s = splnet();
646 IF_PREPEND(&sc->snd_queue, m);
647 splx(s);
648 error = ENOBUFS;
649 break;
650 }
651
652 /* re-enqueue the original packet for us */
653 s = splnet();
654 IF_ENQUEUE(&sc->snd_queue, m);
655 splx(s);
656
657 /* queue the copy for sending at smplimp */
658 IF_ENQUEUE(&tmp_queue, m2);
659 }
660
661 sent = 0;
662 s = splimp();
663 for (;;) {
664 IF_DEQUEUE(&tmp_queue, m2);
665 if (m2 == NULL)
666 break;
667 if (error == 0) {
668 ++sent;
669 sc->stats.outFrames++;
670 sc->stats.outOctets += m2->m_pkthdr.len;
671 s2 = splnet();
672 NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
673 splx(s2);
674 } else {
675 NG_FREE_M(m2);
676 }
677 }
678 splx(s);
679
680 sc->packets -= sent;
681 if (sent_p != NULL)
682 *sent_p = sent;
683 return (error);
684}
644 if (m2 == NULL) {
645 s = splnet();
646 IF_PREPEND(&sc->snd_queue, m);
647 splx(s);
648 error = ENOBUFS;
649 break;
650 }
651
652 /* re-enqueue the original packet for us */
653 s = splnet();
654 IF_ENQUEUE(&sc->snd_queue, m);
655 splx(s);
656
657 /* queue the copy for sending at smplimp */
658 IF_ENQUEUE(&tmp_queue, m2);
659 }
660
661 sent = 0;
662 s = splimp();
663 for (;;) {
664 IF_DEQUEUE(&tmp_queue, m2);
665 if (m2 == NULL)
666 break;
667 if (error == 0) {
668 ++sent;
669 sc->stats.outFrames++;
670 sc->stats.outOctets += m2->m_pkthdr.len;
671 s2 = splnet();
672 NG_SEND_DATA_ONLY(error, sc->output.hook, m2);
673 splx(s2);
674 } else {
675 NG_FREE_M(m2);
676 }
677 }
678 splx(s);
679
680 sc->packets -= sent;
681 if (sent_p != NULL)
682 *sent_p = sent;
683 return (error);
684}