ng_source.c revision 125031
1210284Sjmallett/* 2232812Sjmallett * ng_source.c 3215990Sjmallett * 4210284Sjmallett * Copyright 2002 Sandvine Inc. 5210284Sjmallett * All rights reserved. 6215990Sjmallett * 7215990Sjmallett * Subject to the following obligations and disclaimer of warranty, use and 8215990Sjmallett * redistribution of this software, in source or object code forms, with or 9210284Sjmallett * without modifications are expressly permitted by Sandvine Inc.; provided, 10215990Sjmallett * however, that: 11215990Sjmallett * 1. Any and all reproductions of the source or object code must include the 12210284Sjmallett * copyright notice above and the following disclaimer of warranties; and 13215990Sjmallett * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 14215990Sjmallett * trademarks, including the mark "SANDVINE" on advertising, endorsements, 15215990Sjmallett * or otherwise except as such appears in the above copyright notice or in 16215990Sjmallett * the software. 17215990Sjmallett * 18232812Sjmallett * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 19215990Sjmallett * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 20215990Sjmallett * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 21215990Sjmallett * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 22215990Sjmallett * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 23215990Sjmallett * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 24215990Sjmallett * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 25215990Sjmallett * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 26215990Sjmallett * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 27215990Sjmallett * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28215990Sjmallett * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 29232812Sjmallett * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 30215990Sjmallett * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31215990Sjmallett * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32215990Sjmallett * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 33215990Sjmallett * DAMAGE. 34215990Sjmallett * 35215990Sjmallett * Author: Dave Chapeskie <dchapeskie@sandvine.com> 36215990Sjmallett * 37215990Sjmallett * $FreeBSD: head/sys/netgraph/ng_source.c 125031 2004-01-26 14:46:35Z harti $ 38210284Sjmallett */ 39210284Sjmallett 40210284Sjmallett/* 41210284Sjmallett * This node is used for high speed packet geneneration. It queues 42210284Sjmallett * all data recieved on it's 'input' hook and when told to start via 43210284Sjmallett * a control message it sends the packets out it's 'output' hook. In 44210284Sjmallett * this way this node can be preloaded with a packet stream which is 45215990Sjmallett * continuously sent. 46210284Sjmallett * 47210284Sjmallett * Currently it just copies the mbufs as required. It could do various 48210284Sjmallett * tricks to try and avoid this. Probably the best performance would 49210284Sjmallett * be achieved by modifying the appropriate drivers to be told to 50210284Sjmallett * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 51210284Sjmallett * transmit descriptors) under control of this node; perhaps via some 52210284Sjmallett * flag in the mbuf or some such. The node would peak at an appropriate 53210284Sjmallett * ifnet flag to see if such support is available for the connected 54210284Sjmallett * interface. 55210284Sjmallett */ 56210284Sjmallett 57210284Sjmallett#include <sys/param.h> 58210284Sjmallett#include <sys/systm.h> 59210284Sjmallett#include <sys/errno.h> 60210284Sjmallett#include <sys/kernel.h> 61210284Sjmallett#include <sys/malloc.h> 62210284Sjmallett#include <sys/mbuf.h> 63210284Sjmallett#include <sys/socket.h> 64210284Sjmallett#include <net/if.h> 65215990Sjmallett#include <net/if_var.h> 66210284Sjmallett#include <netgraph/ng_message.h> 67210284Sjmallett#include <netgraph/netgraph.h> 68210284Sjmallett#include <netgraph/ng_parse.h> 69210284Sjmallett#include <netgraph/ng_ether.h> 70210284Sjmallett#include <netgraph/ng_source.h> 71210284Sjmallett 72210284Sjmallett#define NG_SOURCE_INTR_TICKS 1 73210284Sjmallett#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 74210284Sjmallett 75210284Sjmallett 76210284Sjmallett/* Per hook info */ 77210284Sjmallettstruct source_hookinfo { 78210284Sjmallett hook_p hook; 79210284Sjmallett}; 80210284Sjmallett 81210284Sjmallett/* Per node info */ 82210284Sjmallettstruct privdata { 83210284Sjmallett node_p node; 84210284Sjmallett struct source_hookinfo input; 85210284Sjmallett struct source_hookinfo output; 86210284Sjmallett struct ng_source_stats stats; 87210284Sjmallett struct ifqueue snd_queue; /* packets to send */ 88210284Sjmallett struct ifnet *output_ifp; 89210284Sjmallett struct callout_handle intr_ch; 90210284Sjmallett u_int64_t packets; /* packets to send */ 91232812Sjmallett u_int32_t queueOctets; 92210284Sjmallett}; 93210284Sjmalletttypedef struct privdata *sc_p; 94210284Sjmallett 95210284Sjmallett/* Node flags */ 96210284Sjmallett#define NG_SOURCE_ACTIVE (NGF_TYPE1) 97215990Sjmallett 98210284Sjmallett/* XXX */ 99210284Sjmallett#if 1 100210311Sjmallett#undef KASSERT 101215990Sjmallett#define KASSERT(expr,msg) do { \ 102210284Sjmallett if (!(expr)) { \ 103210284Sjmallett printf msg ; \ 104210284Sjmallett panic("Assertion"); \ 105210284Sjmallett } \ 106210284Sjmallett } while(0) 107210284Sjmallett#endif 108210284Sjmallett 109210284Sjmallett/* Netgraph methods */ 110210284Sjmallettstatic ng_constructor_t ng_source_constructor; 111210284Sjmallettstatic ng_rcvmsg_t ng_source_rcvmsg; 112210284Sjmallettstatic ng_shutdown_t ng_source_rmnode; 113210284Sjmallettstatic ng_newhook_t ng_source_newhook; 114210284Sjmallettstatic ng_rcvdata_t ng_source_rcvdata; 115210284Sjmallettstatic ng_disconnect_t ng_source_disconnect; 116210284Sjmallett 117210284Sjmallett/* Other functions */ 118210284Sjmallettstatic timeout_t ng_source_intr; 119210284Sjmallettstatic int ng_source_request_output_ifp (sc_p); 120210284Sjmallettstatic void ng_source_clr_data (sc_p); 121210284Sjmallettstatic void ng_source_start (sc_p); 122210284Sjmallettstatic void ng_source_stop (sc_p); 123210284Sjmallettstatic int ng_source_send (sc_p, int, int *); 124210284Sjmallettstatic int ng_source_store_output_ifp(sc_p sc, 125210284Sjmallett struct ng_mesg *msg); 126210284Sjmallett 127210284Sjmallett 128232812Sjmallett/* Parse type for timeval */ 129210284Sjmallettstatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = 130210284Sjmallett{ 131210284Sjmallett { "tv_sec", &ng_parse_int32_type }, 132210284Sjmallett { "tv_usec", &ng_parse_int32_type }, 133210284Sjmallett { NULL } 134210284Sjmallett}; 135210284Sjmallettconst struct ng_parse_type ng_source_timeval_type = { 136210284Sjmallett &ng_parse_struct_type, 137215990Sjmallett &ng_source_timeval_type_fields 138210284Sjmallett}; 139210284Sjmallett 140210284Sjmallett/* Parse type for struct ng_source_stats */ 141210284Sjmallettstatic const struct ng_parse_struct_field ng_source_stats_type_fields[] 142210284Sjmallett = NG_SOURCE_STATS_TYPE_INFO; 143210284Sjmallettstatic const struct ng_parse_type ng_source_stats_type = { 144210284Sjmallett &ng_parse_struct_type, 145210284Sjmallett &ng_source_stats_type_fields 146210284Sjmallett}; 147210284Sjmallett 148210284Sjmallett/* List of commands and how to convert arguments to/from ASCII */ 149210284Sjmallettstatic const struct ng_cmdlist ng_source_cmds[] = { 150210284Sjmallett { 151210284Sjmallett NGM_SOURCE_COOKIE, 152210284Sjmallett NGM_SOURCE_GET_STATS, 153210284Sjmallett "getstats", 154210284Sjmallett NULL, 155210284Sjmallett &ng_source_stats_type 156210284Sjmallett }, 157210284Sjmallett { 158210284Sjmallett NGM_SOURCE_COOKIE, 159210284Sjmallett NGM_SOURCE_CLR_STATS, 160215990Sjmallett "clrstats", 161210284Sjmallett NULL, 162210284Sjmallett NULL 163210284Sjmallett }, 164215990Sjmallett { 165210284Sjmallett NGM_SOURCE_COOKIE, 166215990Sjmallett NGM_SOURCE_GETCLR_STATS, 167215990Sjmallett "getclrstats", 168210284Sjmallett NULL, 169210284Sjmallett &ng_source_stats_type 170210284Sjmallett }, 171210284Sjmallett { 172210284Sjmallett NGM_SOURCE_COOKIE, 173210284Sjmallett NGM_SOURCE_START, 174210284Sjmallett "start", 175210284Sjmallett &ng_parse_uint64_type, 176210284Sjmallett NULL 177210311Sjmallett }, 178210311Sjmallett { 179210284Sjmallett NGM_SOURCE_COOKIE, 180210284Sjmallett NGM_SOURCE_STOP, 181210284Sjmallett "stop", 182210284Sjmallett NULL, 183210284Sjmallett NULL 184210284Sjmallett }, 185210284Sjmallett { 186210284Sjmallett NGM_SOURCE_COOKIE, 187210284Sjmallett NGM_SOURCE_CLR_DATA, 188210284Sjmallett "clrdata", 189210284Sjmallett NULL, 190210284Sjmallett NULL 191210284Sjmallett }, 192210284Sjmallett { 0 } 193210284Sjmallett}; 194210284Sjmallett 195210284Sjmallett/* Netgraph type descriptor */ 196210284Sjmallettstatic struct ng_type ng_source_typestruct = { 197210284Sjmallett NG_ABI_VERSION, 198210284Sjmallett NG_SOURCE_NODE_TYPE, 199210284Sjmallett NULL, /* module event handler */ 200210284Sjmallett ng_source_constructor, 201210284Sjmallett ng_source_rcvmsg, 202210284Sjmallett ng_source_rmnode, 203210284Sjmallett ng_source_newhook, 204210284Sjmallett NULL, /* findhook */ 205210284Sjmallett NULL, 206210284Sjmallett ng_source_rcvdata, /* rcvdata */ 207210284Sjmallett ng_source_disconnect, 208210284Sjmallett ng_source_cmds 209210284Sjmallett}; 210210284SjmallettNETGRAPH_INIT(source, &ng_source_typestruct); 211210284Sjmallett 212210284Sjmallett/* 213210284Sjmallett * Node constructor 214210284Sjmallett */ 215210284Sjmallettstatic int 216215990Sjmallettng_source_constructor(node_p node) 217210284Sjmallett{ 218210284Sjmallett sc_p sc; 219210284Sjmallett 220210284Sjmallett sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 221210284Sjmallett if (sc == NULL) 222210284Sjmallett return (ENOMEM); 223210284Sjmallett 224210284Sjmallett NG_NODE_SET_PRIVATE(node, sc); 225210284Sjmallett sc->node = node; 226210284Sjmallett sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 227210284Sjmallett callout_handle_init(&sc->intr_ch); /* XXX fix.. will 228210284Sjmallett cause problems. */ 229210284Sjmallett return (0); 230210284Sjmallett} 231210284Sjmallett 232210284Sjmallett/* 233210284Sjmallett * Add a hook 234210284Sjmallett */ 235210284Sjmallettstatic int 236210284Sjmallettng_source_newhook(node_p node, hook_p hook, const char *name) 237232812Sjmallett{ 238210284Sjmallett sc_p sc; 239210284Sjmallett 240210284Sjmallett sc = NG_NODE_PRIVATE(node); 241210284Sjmallett KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 242210284Sjmallett if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 243210284Sjmallett sc->input.hook = hook; 244210284Sjmallett NG_HOOK_SET_PRIVATE(hook, &sc->input); 245210284Sjmallett } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 246210284Sjmallett sc->output.hook = hook; 247210284Sjmallett NG_HOOK_SET_PRIVATE(hook, &sc->output); 248210284Sjmallett sc->output_ifp = 0; 249210284Sjmallett bzero(&sc->stats, sizeof(sc->stats)); 250210284Sjmallett } else 251210284Sjmallett return (EINVAL); 252210284Sjmallett return (0); 253210284Sjmallett} 254210284Sjmallett 255210284Sjmallett/* 256210284Sjmallett * Receive a control message 257210284Sjmallett */ 258210284Sjmallettstatic int 259210284Sjmallettng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 260210284Sjmallett{ 261210284Sjmallett sc_p sc; 262210284Sjmallett struct ng_mesg *resp = NULL; 263210284Sjmallett int error = 0; 264210284Sjmallett struct ng_mesg *msg; 265210284Sjmallett 266210284Sjmallett sc = NG_NODE_PRIVATE(node); 267210284Sjmallett NGI_GET_MSG(item, msg); 268210284Sjmallett KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 269210284Sjmallett switch (msg->header.typecookie) { 270210284Sjmallett case NGM_SOURCE_COOKIE: 271210284Sjmallett if (msg->header.flags & NGF_RESP) { 272210284Sjmallett error = EINVAL; 273210284Sjmallett break; 274210284Sjmallett } 275210284Sjmallett switch (msg->header.cmd) { 276210284Sjmallett case NGM_SOURCE_GET_STATS: 277210284Sjmallett case NGM_SOURCE_CLR_STATS: 278210284Sjmallett case NGM_SOURCE_GETCLR_STATS: 279210284Sjmallett { 280210284Sjmallett struct ng_source_stats *stats; 281210284Sjmallett 282210284Sjmallett if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 283210284Sjmallett NG_MKRESPONSE(resp, msg, 284210284Sjmallett sizeof(*stats), M_NOWAIT); 285210284Sjmallett if (resp == NULL) { 286210284Sjmallett error = ENOMEM; 287210284Sjmallett goto done; 288210284Sjmallett } 289210284Sjmallett sc->stats.queueOctets = sc->queueOctets; 290210284Sjmallett sc->stats.queueFrames = sc->snd_queue.ifq_len; 291210284Sjmallett if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 292210284Sjmallett && !timevalisset(&sc->stats.endTime)) { 293210284Sjmallett getmicrotime(&sc->stats.elapsedTime); 294210284Sjmallett timevalsub(&sc->stats.elapsedTime, 295210284Sjmallett &sc->stats.startTime); 296210284Sjmallett } 297210284Sjmallett stats = (struct ng_source_stats *)resp->data; 298210284Sjmallett bcopy(&sc->stats, stats, sizeof(* stats)); 299215990Sjmallett } 300215990Sjmallett if (msg->header.cmd != NGM_SOURCE_GET_STATS) 301215990Sjmallett bzero(&sc->stats, sizeof(sc->stats)); 302215990Sjmallett } 303215990Sjmallett break; 304215990Sjmallett case NGM_SOURCE_START: 305210284Sjmallett { 306210284Sjmallett u_int64_t packets = *(u_int64_t *)msg->data; 307210284Sjmallett if (sc->output.hook == NULL) { 308210284Sjmallett printf("%s: start on node with no output hook\n" 309210284Sjmallett , __FUNCTION__); 310210284Sjmallett error = EINVAL; 311210284Sjmallett break; 312210284Sjmallett } 313210284Sjmallett /* TODO validation of packets */ 314210284Sjmallett sc->packets = packets; 315210284Sjmallett ng_source_start(sc); 316210284Sjmallett } 317210284Sjmallett break; 318210284Sjmallett case NGM_SOURCE_STOP: 319210284Sjmallett ng_source_stop(sc); 320210284Sjmallett break; 321210284Sjmallett case NGM_SOURCE_CLR_DATA: 322210284Sjmallett ng_source_clr_data(sc); 323210284Sjmallett break; 324210284Sjmallett default: 325210284Sjmallett error = EINVAL; 326210284Sjmallett break; 327210284Sjmallett } 328210284Sjmallett break; 329210284Sjmallett case NGM_ETHER_COOKIE: 330210284Sjmallett if (!(msg->header.flags & NGF_RESP)) { 331215990Sjmallett error = EINVAL; 332210284Sjmallett break; 333210284Sjmallett } 334210284Sjmallett switch (msg->header.cmd) { 335210284Sjmallett case NGM_ETHER_GET_IFINDEX: 336210284Sjmallett if (ng_source_store_output_ifp(sc, msg) == 0) { 337210284Sjmallett ng_source_set_autosrc(sc, 0); 338210284Sjmallett sc->node->nd_flags |= NG_SOURCE_ACTIVE; 339210284Sjmallett timevalclear(&sc->stats.elapsedTime); 340215990Sjmallett timevalclear(&sc->stats.endTime); 341210284Sjmallett getmicrotime(&sc->stats.startTime); 342210284Sjmallett sc->intr_ch = timeout(ng_source_intr, sc, 0); 343210284Sjmallett } 344210284Sjmallett break; 345210284Sjmallett default: 346210284Sjmallett error = EINVAL; 347210284Sjmallett } 348210284Sjmallett break; 349210284Sjmallett default: 350210284Sjmallett error = EINVAL; 351210284Sjmallett break; 352210284Sjmallett } 353210284Sjmallett 354210284Sjmallettdone: 355210284Sjmallett /* Take care of synchronous response, if any */ 356210284Sjmallett NG_RESPOND_MSG(error, node, item, resp); 357210284Sjmallett /* Free the message and return */ 358210284Sjmallett NG_FREE_MSG(msg); 359210284Sjmallett return (error); 360210284Sjmallett} 361210284Sjmallett 362210284Sjmallett/* 363210284Sjmallett * Receive data on a hook 364210284Sjmallett * 365210284Sjmallett * If data comes in the input hook, enqueue it on the send queue. 366210284Sjmallett * If data comes in the output hook, discard it. 367210284Sjmallett */ 368210284Sjmallettstatic int 369210284Sjmallettng_source_rcvdata(hook_p hook, item_p item) 370210284Sjmallett{ 371210284Sjmallett sc_p sc; 372210284Sjmallett struct source_hookinfo *hinfo; 373210284Sjmallett int error = 0; 374210284Sjmallett struct mbuf *m; 375210284Sjmallett 376210284Sjmallett sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 377210284Sjmallett NGI_GET_M(item, m); 378210284Sjmallett NG_FREE_ITEM(item); 379210284Sjmallett hinfo = NG_HOOK_PRIVATE(hook); 380210284Sjmallett KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 381210284Sjmallett KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__)); 382210284Sjmallett 383210284Sjmallett /* Which hook? */ 384210284Sjmallett if (hinfo == &sc->output) { 385210284Sjmallett /* discard */ 386210284Sjmallett NG_FREE_M(m); 387210284Sjmallett return (error); 388215990Sjmallett } 389210284Sjmallett KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__)); 390210284Sjmallett 391210284Sjmallett if ((m->m_flags & M_PKTHDR) == 0) { 392210284Sjmallett printf("%s: mbuf without PKTHDR\n", __FUNCTION__); 393210284Sjmallett NG_FREE_M(m); 394210284Sjmallett return (EINVAL); 395210284Sjmallett } 396210284Sjmallett 397210284Sjmallett /* enque packet */ 398210284Sjmallett /* XXX should we check IF_QFULL() ? */ 399210284Sjmallett _IF_ENQUEUE(&sc->snd_queue, m); 400210284Sjmallett sc->queueOctets += m->m_pkthdr.len; 401210284Sjmallett 402210284Sjmallett return (0); 403210284Sjmallett} 404210284Sjmallett 405210284Sjmallett/* 406210284Sjmallett * Shutdown processing 407210284Sjmallett */ 408210284Sjmallettstatic int 409210284Sjmallettng_source_rmnode(node_p node) 410210284Sjmallett{ 411210284Sjmallett sc_p sc; 412210284Sjmallett 413210284Sjmallett sc = NG_NODE_PRIVATE(node); 414210284Sjmallett KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 415210284Sjmallett node->nd_flags |= NG_INVALID; 416210284Sjmallett ng_source_stop(sc); 417210284Sjmallett ng_source_clr_data(sc); 418210284Sjmallett NG_NODE_SET_PRIVATE(node, NULL); 419210284Sjmallett NG_NODE_UNREF(node); 420210284Sjmallett FREE(sc, M_NETGRAPH); 421210284Sjmallett return (0); 422210284Sjmallett} 423210284Sjmallett 424210284Sjmallett/* 425210284Sjmallett * Hook disconnection 426210284Sjmallett */ 427210284Sjmallettstatic int 428210284Sjmallettng_source_disconnect(hook_p hook) 429210284Sjmallett{ 430210284Sjmallett struct source_hookinfo *hinfo; 431210284Sjmallett sc_p sc; 432210284Sjmallett 433210284Sjmallett hinfo = NG_HOOK_PRIVATE(hook); 434210284Sjmallett sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 435210284Sjmallett KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 436210284Sjmallett hinfo->hook = NULL; 437210284Sjmallett if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output) 438210284Sjmallett ng_rmnode_self(NG_HOOK_NODE(hook)); 439210284Sjmallett return (0); 440210284Sjmallett} 441210284Sjmallett 442210284Sjmallett/* 443210284Sjmallett * 444210284Sjmallett * Ask out neighbour on the output hook side to send us it's interface 445210284Sjmallett * information. 446210284Sjmallett */ 447210284Sjmallettstatic int 448210284Sjmallettng_source_request_output_ifp(sc_p sc) 449210284Sjmallett{ 450210284Sjmallett struct ng_mesg *msg; 451210284Sjmallett int error = 0; 452210284Sjmallett 453210284Sjmallett sc->output_ifp = NULL; 454210284Sjmallett 455210284Sjmallett /* Ask the attached node for the connected interface's index */ 456210284Sjmallett NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT); 457210284Sjmallett if (msg == NULL) 458210284Sjmallett return (ENOBUFS); 459210284Sjmallett 460210284Sjmallett NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL); 461210284Sjmallett return (error); 462210284Sjmallett} 463210284Sjmallett 464210284Sjmallett/* 465210284Sjmallett * Set sc->output_ifp to point to the the struct ifnet of the interface 466210284Sjmallett * reached via our output hook. 467210284Sjmallett */ 468210284Sjmallettstatic int 469210284Sjmallettng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg) 470210284Sjmallett{ 471210284Sjmallett struct ifnet *ifp; 472210284Sjmallett u_int32_t if_index; 473210284Sjmallett int s; 474210284Sjmallett 475210284Sjmallett if (msg->header.arglen < sizeof(u_int32_t)) 476210284Sjmallett return (EINVAL); 477215990Sjmallett 478210284Sjmallett if_index = *(u_int32_t *)msg->data; 479210284Sjmallett /* Could use ifindex2ifnet[if_index] except that we have no 480210284Sjmallett * way of verifying if_index is valid since if_indexlim is 481210284Sjmallett * local to if_attach() 482210284Sjmallett */ 483210284Sjmallett IFNET_RLOCK(); 484210284Sjmallett TAILQ_FOREACH(ifp, &ifnet, if_link) { 485210284Sjmallett if (ifp->if_index == if_index) 486210284Sjmallett break; 487210284Sjmallett } 488210284Sjmallett IFNET_RUNLOCK(); 489210284Sjmallett 490210284Sjmallett if (ifp == NULL) { 491210284Sjmallett printf("%s: can't find interface %d\n", __FUNCTION__, if_index); 492210284Sjmallett return (EINVAL); 493210284Sjmallett } 494210284Sjmallett sc->output_ifp = ifp; 495210284Sjmallett 496210284Sjmallett#if 1 497210284Sjmallett /* XXX mucking with a drivers ifqueue size is ugly but we need it 498210284Sjmallett * to queue a lot of packets to get close to line rate on a gigabit 499210284Sjmallett * interface with small packets. 500210284Sjmallett * XXX we should restore the original value at stop or disconnect 501210284Sjmallett */ 502210284Sjmallett s = splimp(); /* XXX is this required? */ 503210284Sjmallett if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) 504210284Sjmallett { 505210284Sjmallett printf("ng_source: changing ifq_maxlen from %d to %d\n", 506210284Sjmallett ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 507210284Sjmallett ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 508210284Sjmallett } 509210284Sjmallett splx(s); 510210284Sjmallett#endif 511210284Sjmallett return (0); 512210284Sjmallett} 513210284Sjmallett 514210284Sjmallett/* 515210284Sjmallett * Set the attached ethernet node's ethernet source address override flag. 516210284Sjmallett */ 517210284Sjmallettstatic int 518210284Sjmallettng_source_set_autosrc(sc_p sc, u_int32_t flag) 519210284Sjmallett{ 520210284Sjmallett struct ng_mesg *msg; 521210284Sjmallett int error = 0; 522210284Sjmallett 523210284Sjmallett NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 524210284Sjmallett sizeof (u_int32_t), M_NOWAIT); 525210284Sjmallett if (msg == NULL) 526210284Sjmallett return(ENOBUFS); 527210284Sjmallett 528210284Sjmallett *(u_int32_t *)msg->data = flag; 529210284Sjmallett NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, NULL); 530210284Sjmallett return (error); 531210284Sjmallett} 532210284Sjmallett 533210284Sjmallett/* 534210284Sjmallett * Clear out the data we've queued 535210284Sjmallett */ 536210284Sjmallettstatic void 537210284Sjmallettng_source_clr_data (sc_p sc) 538210284Sjmallett{ 539210284Sjmallett struct mbuf *m; 540210284Sjmallett 541210284Sjmallett for (;;) { 542210284Sjmallett _IF_DEQUEUE(&sc->snd_queue, m); 543210284Sjmallett if (m == NULL) 544210284Sjmallett break; 545210284Sjmallett NG_FREE_M(m); 546210284Sjmallett } 547210284Sjmallett sc->queueOctets = 0; 548210284Sjmallett} 549210284Sjmallett 550210284Sjmallett/* 551210284Sjmallett * Start sending queued data out the output hook 552210284Sjmallett */ 553210284Sjmallettstatic void 554210284Sjmallettng_source_start (sc_p sc) 555210284Sjmallett{ 556210284Sjmallett KASSERT(sc->output.hook != NULL, 557210284Sjmallett ("%s: output hook unconnected", __FUNCTION__)); 558210284Sjmallett if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) && 559210284Sjmallett (sc->output_ifp == NULL)) 560210284Sjmallett ng_source_request_output_ifp(sc); 561210284Sjmallett} 562210284Sjmallett 563210284Sjmallett/* 564210284Sjmallett * Stop sending queued data out the output hook 565210284Sjmallett */ 566210284Sjmallettstatic void 567210284Sjmallettng_source_stop (sc_p sc) 568215990Sjmallett{ 569210284Sjmallett if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 570210284Sjmallett untimeout(ng_source_intr, sc, sc->intr_ch); 571210284Sjmallett sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 572210284Sjmallett getmicrotime(&sc->stats.endTime); 573210284Sjmallett sc->stats.elapsedTime = sc->stats.endTime; 574210284Sjmallett timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 575210284Sjmallett /* XXX should set this to the initial value instead */ 576210284Sjmallett ng_source_set_autosrc(sc, 1); 577210284Sjmallett } 578210284Sjmallett} 579210284Sjmallett 580210284Sjmallett/* 581210284Sjmallett * While active called every NG_SOURCE_INTR_TICKS ticks. 582210284Sjmallett * Sends as many packets as the interface connected to our 583210284Sjmallett * output hook is able to enqueue. 584210284Sjmallett */ 585210284Sjmallettstatic void 586210284Sjmallettng_source_intr (void *arg) 587210284Sjmallett{ 588210284Sjmallett sc_p sc = (sc_p) arg; 589210284Sjmallett struct ifqueue *ifq; 590210284Sjmallett int packets; 591210284Sjmallett 592210284Sjmallett KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 593210284Sjmallett 594210284Sjmallett callout_handle_init(&sc->intr_ch); 595210284Sjmallett if (sc->packets == 0 || sc->output.hook == NULL 596210284Sjmallett || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 597210284Sjmallett ng_source_stop(sc); 598210284Sjmallett return; 599210284Sjmallett } 600210284Sjmallett 601210284Sjmallett ifq = &sc->output_ifp->if_snd; 602210284Sjmallett packets = ifq->ifq_maxlen - ifq->ifq_len; 603210284Sjmallett ng_source_send(sc, packets, NULL); 604210284Sjmallett if (sc->packets == 0) { 605210284Sjmallett int s = splnet(); 606210284Sjmallett ng_source_stop(sc); 607210284Sjmallett splx(s); 608210284Sjmallett } else 609210284Sjmallett sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS); 610210284Sjmallett} 611210284Sjmallett 612210284Sjmallett/* 613210284Sjmallett * Send packets out our output hook 614210284Sjmallett */ 615static int 616ng_source_send (sc_p sc, int tosend, int *sent_p) 617{ 618 struct ifqueue tmp_queue; 619 struct mbuf *m, *m2; 620 int sent = 0; 621 int error = 0; 622 int s, s2; 623 624 KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 625 KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__)); 626 KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 627 ("%s: inactive node", __FUNCTION__)); 628 629 if ((u_int64_t)tosend > sc->packets) 630 tosend = sc->packets; 631 632 /* Copy the required number of packets to a temporary queue */ 633 bzero (&tmp_queue, sizeof (tmp_queue)); 634 for (sent = 0; error == 0 && sent < tosend; ++sent) { 635 s = splnet(); 636 _IF_DEQUEUE(&sc->snd_queue, m); 637 splx(s); 638 if (m == NULL) 639 break; 640 641 /* duplicate the packet */ 642 m2 = m_copypacket(m, M_DONTWAIT); 643 if (m2 == NULL) { 644 s = splnet(); 645 _IF_PREPEND(&sc->snd_queue, m); 646 splx(s); 647 error = ENOBUFS; 648 break; 649 } 650 651 /* re-enqueue the original packet for us */ 652 s = splnet(); 653 _IF_ENQUEUE(&sc->snd_queue, m); 654 splx(s); 655 656 /* queue the copy for sending at smplimp */ 657 _IF_ENQUEUE(&tmp_queue, m2); 658 } 659 660 sent = 0; 661 s = splimp(); 662 for (;;) { 663 _IF_DEQUEUE(&tmp_queue, m2); 664 if (m2 == NULL) 665 break; 666 if (error == 0) { 667 ++sent; 668 sc->stats.outFrames++; 669 sc->stats.outOctets += m2->m_pkthdr.len; 670 s2 = splnet(); 671 NG_SEND_DATA_ONLY(error, sc->output.hook, m2); 672 splx(s2); 673 } else { 674 NG_FREE_M(m2); 675 } 676 } 677 splx(s); 678 679 sc->packets -= sent; 680 if (sent_p != NULL) 681 *sent_p = sent; 682 return (error); 683} 684