ng_source.c revision 154707
1106266Sjulian/* 2106266Sjulian * ng_source.c 3139823Simp */ 4139823Simp 5139823Simp/*- 6144674Sglebius * Copyright (c) 2005 Gleb Smirnoff <glebius@FreeBSD.org> 7106266Sjulian * Copyright 2002 Sandvine Inc. 8106266Sjulian * All rights reserved. 9106266Sjulian * 10106266Sjulian * Subject to the following obligations and disclaimer of warranty, use and 11106266Sjulian * redistribution of this software, in source or object code forms, with or 12106319Sjulian * without modifications are expressly permitted by Sandvine Inc.; provided, 13106266Sjulian * however, that: 14106319Sjulian * 1. Any and all reproductions of the source or object code must include the 15106319Sjulian * copyright notice above and the following disclaimer of warranties; and 16106266Sjulian * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 17106319Sjulian * trademarks, including the mark "SANDVINE" on advertising, endorsements, 18106319Sjulian * or otherwise except as such appears in the above copyright notice or in 19106266Sjulian * the software. 20106266Sjulian * 21106266Sjulian * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 22106319Sjulian * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 23106319Sjulian * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 24106319Sjulian * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 25106266Sjulian * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 26106266Sjulian * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 27106266Sjulian * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 28106266Sjulian * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 29106266Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30106319Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31106266Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32106266Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33106266Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34106266Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35106266Sjulian * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 36106266Sjulian * DAMAGE. 37106266Sjulian * 38106266Sjulian * Author: Dave Chapeskie <dchapeskie@sandvine.com> 39106266Sjulian */ 40106266Sjulian 41125077Sharti#include <sys/cdefs.h> 42125077Sharti__FBSDID("$FreeBSD: head/sys/netgraph/ng_source.c 154707 2006-01-23 10:28:04Z glebius $"); 43125077Sharti 44106266Sjulian/* 45106266Sjulian * This node is used for high speed packet geneneration. It queues 46143387Sbmilekic * all data recieved on its 'input' hook and when told to start via 47143387Sbmilekic * a control message it sends the packets out its 'output' hook. In 48143387Sbmilekic * this way this node can be preloaded with a packet stream which it 49143387Sbmilekic * can then send continuously as fast as possible. 50106266Sjulian * 51106266Sjulian * Currently it just copies the mbufs as required. It could do various 52106266Sjulian * tricks to try and avoid this. Probably the best performance would 53106266Sjulian * be achieved by modifying the appropriate drivers to be told to 54106266Sjulian * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 55106266Sjulian * transmit descriptors) under control of this node; perhaps via some 56143387Sbmilekic * flag in the mbuf or some such. The node could peek at an appropriate 57106266Sjulian * ifnet flag to see if such support is available for the connected 58106266Sjulian * interface. 59106266Sjulian */ 60106266Sjulian 61106266Sjulian#include <sys/param.h> 62106266Sjulian#include <sys/systm.h> 63106266Sjulian#include <sys/errno.h> 64106266Sjulian#include <sys/kernel.h> 65106266Sjulian#include <sys/malloc.h> 66106266Sjulian#include <sys/mbuf.h> 67106266Sjulian#include <sys/socket.h> 68144674Sglebius#include <sys/syslog.h> 69106266Sjulian#include <net/if.h> 70106266Sjulian#include <net/if_var.h> 71106266Sjulian#include <netgraph/ng_message.h> 72106266Sjulian#include <netgraph/netgraph.h> 73106266Sjulian#include <netgraph/ng_parse.h> 74106266Sjulian#include <netgraph/ng_ether.h> 75106266Sjulian#include <netgraph/ng_source.h> 76106266Sjulian 77106266Sjulian#define NG_SOURCE_INTR_TICKS 1 78106266Sjulian#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 79106266Sjulian 80106266Sjulian/* Per node info */ 81106266Sjulianstruct privdata { 82106266Sjulian node_p node; 83144674Sglebius hook_p input; 84144674Sglebius hook_p output; 85106266Sjulian struct ng_source_stats stats; 86106319Sjulian struct ifqueue snd_queue; /* packets to send */ 87106266Sjulian struct ifnet *output_ifp; 88137138Sglebius struct callout intr_ch; 89144674Sglebius uint64_t packets; /* packets to send */ 90144674Sglebius uint32_t queueOctets; 91106266Sjulian}; 92106266Sjuliantypedef struct privdata *sc_p; 93106266Sjulian 94106266Sjulian/* Node flags */ 95106266Sjulian#define NG_SOURCE_ACTIVE (NGF_TYPE1) 96106266Sjulian 97106266Sjulian/* Netgraph methods */ 98106266Sjulianstatic ng_constructor_t ng_source_constructor; 99106266Sjulianstatic ng_rcvmsg_t ng_source_rcvmsg; 100106266Sjulianstatic ng_shutdown_t ng_source_rmnode; 101106266Sjulianstatic ng_newhook_t ng_source_newhook; 102144674Sglebiusstatic ng_connect_t ng_source_connect; 103106266Sjulianstatic ng_rcvdata_t ng_source_rcvdata; 104106266Sjulianstatic ng_disconnect_t ng_source_disconnect; 105106266Sjulian 106106266Sjulian/* Other functions */ 107125243Shartistatic void ng_source_intr(node_p, hook_p, void *, int); 108106266Sjulianstatic void ng_source_clr_data (sc_p); 109144674Sglebiusstatic int ng_source_start (sc_p, uint64_t); 110106266Sjulianstatic void ng_source_stop (sc_p); 111106266Sjulianstatic int ng_source_send (sc_p, int, int *); 112144674Sglebiusstatic int ng_source_store_output_ifp(sc_p, char *); 113106266Sjulian 114106266Sjulian/* Parse type for timeval */ 115125077Shartistatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = { 116106266Sjulian { "tv_sec", &ng_parse_int32_type }, 117106266Sjulian { "tv_usec", &ng_parse_int32_type }, 118106266Sjulian { NULL } 119106266Sjulian}; 120106266Sjulianconst struct ng_parse_type ng_source_timeval_type = { 121106266Sjulian &ng_parse_struct_type, 122106266Sjulian &ng_source_timeval_type_fields 123106266Sjulian}; 124106266Sjulian 125106266Sjulian/* Parse type for struct ng_source_stats */ 126106266Sjulianstatic const struct ng_parse_struct_field ng_source_stats_type_fields[] 127106266Sjulian = NG_SOURCE_STATS_TYPE_INFO; 128106266Sjulianstatic const struct ng_parse_type ng_source_stats_type = { 129106266Sjulian &ng_parse_struct_type, 130106266Sjulian &ng_source_stats_type_fields 131106266Sjulian}; 132106266Sjulian 133106266Sjulian/* List of commands and how to convert arguments to/from ASCII */ 134106266Sjulianstatic const struct ng_cmdlist ng_source_cmds[] = { 135106266Sjulian { 136106266Sjulian NGM_SOURCE_COOKIE, 137106266Sjulian NGM_SOURCE_GET_STATS, 138106266Sjulian "getstats", 139106266Sjulian NULL, 140106266Sjulian &ng_source_stats_type 141106266Sjulian }, 142106266Sjulian { 143106266Sjulian NGM_SOURCE_COOKIE, 144106266Sjulian NGM_SOURCE_CLR_STATS, 145106266Sjulian "clrstats", 146106266Sjulian NULL, 147106266Sjulian NULL 148106266Sjulian }, 149106266Sjulian { 150106266Sjulian NGM_SOURCE_COOKIE, 151106266Sjulian NGM_SOURCE_GETCLR_STATS, 152106266Sjulian "getclrstats", 153106266Sjulian NULL, 154106266Sjulian &ng_source_stats_type 155106266Sjulian }, 156106266Sjulian { 157106266Sjulian NGM_SOURCE_COOKIE, 158106266Sjulian NGM_SOURCE_START, 159106266Sjulian "start", 160106266Sjulian &ng_parse_uint64_type, 161106266Sjulian NULL 162106266Sjulian }, 163106266Sjulian { 164106266Sjulian NGM_SOURCE_COOKIE, 165106266Sjulian NGM_SOURCE_STOP, 166106266Sjulian "stop", 167106266Sjulian NULL, 168106266Sjulian NULL 169106266Sjulian }, 170106266Sjulian { 171106266Sjulian NGM_SOURCE_COOKIE, 172106266Sjulian NGM_SOURCE_CLR_DATA, 173106266Sjulian "clrdata", 174106266Sjulian NULL, 175106266Sjulian NULL 176106266Sjulian }, 177125033Sharti { 178125033Sharti NGM_SOURCE_COOKIE, 179144674Sglebius NGM_SOURCE_SETIFACE, 180144674Sglebius "setiface", 181144674Sglebius &ng_parse_string_type, 182125033Sharti NULL 183125033Sharti }, 184153690Sglebius { 185153690Sglebius NGM_SOURCE_COOKIE, 186153690Sglebius NGM_SOURCE_SETPPS, 187153690Sglebius "setpps", 188153690Sglebius &ng_parse_uint32_type, 189153690Sglebius NULL 190153690Sglebius }, 191106266Sjulian { 0 } 192106266Sjulian}; 193106266Sjulian 194106266Sjulian/* Netgraph type descriptor */ 195106266Sjulianstatic struct ng_type ng_source_typestruct = { 196129823Sjulian .version = NG_ABI_VERSION, 197129823Sjulian .name = NG_SOURCE_NODE_TYPE, 198129823Sjulian .constructor = ng_source_constructor, 199129823Sjulian .rcvmsg = ng_source_rcvmsg, 200129823Sjulian .shutdown = ng_source_rmnode, 201129823Sjulian .newhook = ng_source_newhook, 202144674Sglebius .connect = ng_source_connect, 203129823Sjulian .rcvdata = ng_source_rcvdata, 204129823Sjulian .disconnect = ng_source_disconnect, 205129823Sjulian .cmdlist = ng_source_cmds, 206106266Sjulian}; 207106266SjulianNETGRAPH_INIT(source, &ng_source_typestruct); 208106266Sjulian 209144674Sglebiusstatic int ng_source_set_autosrc(sc_p, uint32_t); 210125032Sharti 211106266Sjulian/* 212106266Sjulian * Node constructor 213106266Sjulian */ 214106266Sjulianstatic int 215106321Sjulianng_source_constructor(node_p node) 216106266Sjulian{ 217106266Sjulian sc_p sc; 218106266Sjulian 219125030Sharti sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 220106266Sjulian if (sc == NULL) 221106266Sjulian return (ENOMEM); 222106266Sjulian 223106321Sjulian NG_NODE_SET_PRIVATE(node, sc); 224106321Sjulian sc->node = node; 225106266Sjulian sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 226137138Sglebius ng_callout_init(&sc->intr_ch); 227137138Sglebius 228106266Sjulian return (0); 229106266Sjulian} 230106266Sjulian 231106266Sjulian/* 232106266Sjulian * Add a hook 233106266Sjulian */ 234106266Sjulianstatic int 235106266Sjulianng_source_newhook(node_p node, hook_p hook, const char *name) 236106266Sjulian{ 237144674Sglebius sc_p sc = NG_NODE_PRIVATE(node); 238106266Sjulian 239106266Sjulian if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 240144674Sglebius sc->input = hook; 241106266Sjulian } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 242144674Sglebius sc->output = hook; 243106266Sjulian sc->output_ifp = 0; 244106266Sjulian bzero(&sc->stats, sizeof(sc->stats)); 245106266Sjulian } else 246106266Sjulian return (EINVAL); 247144674Sglebius 248106266Sjulian return (0); 249106266Sjulian} 250106266Sjulian 251106266Sjulian/* 252144674Sglebius * Hook has been added 253144674Sglebius */ 254144674Sglebiusstatic int 255144674Sglebiusng_source_connect(hook_p hook) 256144674Sglebius{ 257144674Sglebius sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 258144674Sglebius struct ng_mesg *msg; 259144674Sglebius int dummy_error = 0; 260144674Sglebius 261144674Sglebius /* 262144674Sglebius * If this is "output" hook, then request information 263144674Sglebius * from our downstream. 264144674Sglebius */ 265144674Sglebius if (hook == sc->output) { 266144674Sglebius NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME, 267144674Sglebius 0, M_NOWAIT); 268144674Sglebius if (msg == NULL) 269144674Sglebius return (ENOBUFS); 270144674Sglebius 271144674Sglebius /* 272144674Sglebius * Our hook and peer hook have HK_INVALID flag set, 273144674Sglebius * so we can't use NG_SEND_MSG_HOOK() macro here. 274144674Sglebius */ 275144674Sglebius NG_SEND_MSG_ID(dummy_error, sc->node, msg, 276144674Sglebius NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node)); 277144674Sglebius } 278144674Sglebius 279144674Sglebius return (0); 280144674Sglebius} 281144674Sglebius 282144674Sglebius/* 283106266Sjulian * Receive a control message 284106266Sjulian */ 285106266Sjulianstatic int 286106321Sjulianng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 287106266Sjulian{ 288144674Sglebius sc_p sc = NG_NODE_PRIVATE(node); 289144674Sglebius struct ng_mesg *msg, *resp = NULL; 290106266Sjulian int error = 0; 291106266Sjulian 292106321Sjulian NGI_GET_MSG(item, msg); 293144674Sglebius 294106266Sjulian switch (msg->header.typecookie) { 295106266Sjulian case NGM_SOURCE_COOKIE: 296106435Sjulian if (msg->header.flags & NGF_RESP) { 297106435Sjulian error = EINVAL; 298106435Sjulian break; 299106435Sjulian } 300106266Sjulian switch (msg->header.cmd) { 301106266Sjulian case NGM_SOURCE_GET_STATS: 302106266Sjulian case NGM_SOURCE_CLR_STATS: 303106266Sjulian case NGM_SOURCE_GETCLR_STATS: 304106266Sjulian { 305106266Sjulian struct ng_source_stats *stats; 306106266Sjulian 307106266Sjulian if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 308106266Sjulian NG_MKRESPONSE(resp, msg, 309106266Sjulian sizeof(*stats), M_NOWAIT); 310106266Sjulian if (resp == NULL) { 311106266Sjulian error = ENOMEM; 312106266Sjulian goto done; 313106266Sjulian } 314106266Sjulian sc->stats.queueOctets = sc->queueOctets; 315106319Sjulian sc->stats.queueFrames = sc->snd_queue.ifq_len; 316106321Sjulian if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 317106266Sjulian && !timevalisset(&sc->stats.endTime)) { 318106319Sjulian getmicrotime(&sc->stats.elapsedTime); 319106266Sjulian timevalsub(&sc->stats.elapsedTime, 320106319Sjulian &sc->stats.startTime); 321106266Sjulian } 322106319Sjulian stats = (struct ng_source_stats *)resp->data; 323106266Sjulian bcopy(&sc->stats, stats, sizeof(* stats)); 324106266Sjulian } 325106266Sjulian if (msg->header.cmd != NGM_SOURCE_GET_STATS) 326106266Sjulian bzero(&sc->stats, sizeof(sc->stats)); 327106266Sjulian } 328106266Sjulian break; 329106266Sjulian case NGM_SOURCE_START: 330106266Sjulian { 331144674Sglebius uint64_t packets; 332144674Sglebius 333144674Sglebius if (msg->header.arglen != sizeof(uint64_t)) { 334106266Sjulian error = EINVAL; 335106266Sjulian break; 336106266Sjulian } 337125033Sharti 338144674Sglebius packets = *(uint64_t *)msg->data; 339144674Sglebius 340144674Sglebius error = ng_source_start(sc, packets); 341144674Sglebius 342144674Sglebius break; 343125033Sharti } 344106266Sjulian case NGM_SOURCE_STOP: 345106266Sjulian ng_source_stop(sc); 346106266Sjulian break; 347106266Sjulian case NGM_SOURCE_CLR_DATA: 348106266Sjulian ng_source_clr_data(sc); 349106266Sjulian break; 350144674Sglebius case NGM_SOURCE_SETIFACE: 351144674Sglebius { 352144674Sglebius char *ifname = (char *)msg->data; 353144674Sglebius 354144674Sglebius if (msg->header.arglen < 2) { 355144674Sglebius error = EINVAL; 356144674Sglebius break; 357144674Sglebius } 358144674Sglebius 359144674Sglebius ng_source_store_output_ifp(sc, ifname); 360144674Sglebius break; 361144674Sglebius } 362153690Sglebius case NGM_SOURCE_SETPPS: 363153690Sglebius { 364153690Sglebius uint32_t pps; 365153690Sglebius 366153690Sglebius if (msg->header.arglen != sizeof(uint32_t)) { 367153690Sglebius error = EINVAL; 368153690Sglebius break; 369153690Sglebius } 370153690Sglebius 371153690Sglebius pps = *(uint32_t *)msg->data; 372153690Sglebius 373153690Sglebius sc->stats.maxPps = pps; 374153690Sglebius 375153690Sglebius break; 376153690Sglebius } 377106266Sjulian default: 378106266Sjulian error = EINVAL; 379106266Sjulian break; 380106266Sjulian } 381106266Sjulian break; 382106435Sjulian case NGM_ETHER_COOKIE: 383106435Sjulian if (!(msg->header.flags & NGF_RESP)) { 384106435Sjulian error = EINVAL; 385106435Sjulian break; 386106435Sjulian } 387106435Sjulian switch (msg->header.cmd) { 388144674Sglebius case NGM_ETHER_GET_IFNAME: 389144674Sglebius { 390144674Sglebius char *ifname = (char *)msg->data; 391144674Sglebius 392144674Sglebius if (msg->header.arglen < 2) { 393144674Sglebius error = EINVAL; 394144674Sglebius break; 395144674Sglebius } 396144674Sglebius 397144674Sglebius if (ng_source_store_output_ifp(sc, ifname) == 0) 398106435Sjulian ng_source_set_autosrc(sc, 0); 399106435Sjulian break; 400144674Sglebius } 401106435Sjulian default: 402106435Sjulian error = EINVAL; 403106435Sjulian } 404106435Sjulian break; 405106266Sjulian default: 406106266Sjulian error = EINVAL; 407106266Sjulian break; 408106266Sjulian } 409106266Sjulian 410106266Sjuliandone: 411144674Sglebius /* Take care of synchronous response, if any. */ 412106321Sjulian NG_RESPOND_MSG(error, node, item, resp); 413144674Sglebius /* Free the message and return. */ 414106321Sjulian NG_FREE_MSG(msg); 415106266Sjulian return (error); 416106266Sjulian} 417106266Sjulian 418106266Sjulian/* 419106266Sjulian * Receive data on a hook 420106266Sjulian * 421106266Sjulian * If data comes in the input hook, enqueue it on the send queue. 422106266Sjulian * If data comes in the output hook, discard it. 423106266Sjulian */ 424106266Sjulianstatic int 425106321Sjulianng_source_rcvdata(hook_p hook, item_p item) 426106266Sjulian{ 427144674Sglebius sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 428144674Sglebius struct mbuf *m; 429106266Sjulian int error = 0; 430106266Sjulian 431106321Sjulian NGI_GET_M(item, m); 432106321Sjulian NG_FREE_ITEM(item); 433106266Sjulian 434106266Sjulian /* Which hook? */ 435144674Sglebius if (hook == sc->output) { 436106266Sjulian /* discard */ 437106321Sjulian NG_FREE_M(m); 438106266Sjulian return (error); 439106266Sjulian } 440144674Sglebius KASSERT(hook == sc->input, ("%s: no hook!", __func__)); 441106266Sjulian 442144674Sglebius /* Enqueue packet. */ 443106266Sjulian /* XXX should we check IF_QFULL() ? */ 444125031Sharti _IF_ENQUEUE(&sc->snd_queue, m); 445106266Sjulian sc->queueOctets += m->m_pkthdr.len; 446106266Sjulian 447106266Sjulian return (0); 448106266Sjulian} 449106266Sjulian 450106266Sjulian/* 451106266Sjulian * Shutdown processing 452106266Sjulian */ 453106266Sjulianstatic int 454106266Sjulianng_source_rmnode(node_p node) 455106266Sjulian{ 456144674Sglebius sc_p sc = NG_NODE_PRIVATE(node); 457106266Sjulian 458106266Sjulian ng_source_stop(sc); 459106266Sjulian ng_source_clr_data(sc); 460106321Sjulian NG_NODE_SET_PRIVATE(node, NULL); 461106321Sjulian NG_NODE_UNREF(node); 462125077Sharti free(sc, M_NETGRAPH); 463144674Sglebius 464106266Sjulian return (0); 465106266Sjulian} 466106266Sjulian 467106266Sjulian/* 468106266Sjulian * Hook disconnection 469106266Sjulian */ 470106266Sjulianstatic int 471106266Sjulianng_source_disconnect(hook_p hook) 472106266Sjulian{ 473106319Sjulian sc_p sc; 474106266Sjulian 475106321Sjulian sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 476125077Sharti KASSERT(sc != NULL, ("%s: null node private", __func__)); 477144674Sglebius if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output) 478106321Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); 479106266Sjulian return (0); 480106266Sjulian} 481106266Sjulian 482106266Sjulian/* 483106435Sjulian * Set sc->output_ifp to point to the the struct ifnet of the interface 484106435Sjulian * reached via our output hook. 485106435Sjulian */ 486106435Sjulianstatic int 487144674Sglebiusng_source_store_output_ifp(sc_p sc, char *ifname) 488106435Sjulian{ 489106435Sjulian struct ifnet *ifp; 490106435Sjulian int s; 491106266Sjulian 492144674Sglebius ifp = ifunit(ifname); 493106266Sjulian 494106266Sjulian if (ifp == NULL) { 495125077Sharti printf("%s: can't find interface %d\n", __func__, if_index); 496106266Sjulian return (EINVAL); 497106266Sjulian } 498106266Sjulian sc->output_ifp = ifp; 499106266Sjulian 500106266Sjulian#if 1 501106266Sjulian /* XXX mucking with a drivers ifqueue size is ugly but we need it 502106266Sjulian * to queue a lot of packets to get close to line rate on a gigabit 503106266Sjulian * interface with small packets. 504106266Sjulian * XXX we should restore the original value at stop or disconnect 505106266Sjulian */ 506106266Sjulian s = splimp(); /* XXX is this required? */ 507125077Sharti if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) { 508106266Sjulian printf("ng_source: changing ifq_maxlen from %d to %d\n", 509106319Sjulian ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 510106266Sjulian ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 511106266Sjulian } 512106266Sjulian splx(s); 513106266Sjulian#endif 514106266Sjulian return (0); 515106266Sjulian} 516106266Sjulian 517106266Sjulian/* 518106266Sjulian * Set the attached ethernet node's ethernet source address override flag. 519106266Sjulian */ 520106266Sjulianstatic int 521144674Sglebiusng_source_set_autosrc(sc_p sc, uint32_t flag) 522106266Sjulian{ 523106266Sjulian struct ng_mesg *msg; 524106266Sjulian int error = 0; 525106266Sjulian 526106266Sjulian NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 527144674Sglebius sizeof (uint32_t), M_NOWAIT); 528106266Sjulian if (msg == NULL) 529106266Sjulian return(ENOBUFS); 530106266Sjulian 531144674Sglebius *(uint32_t *)msg->data = flag; 532144674Sglebius NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0); 533106266Sjulian return (error); 534106266Sjulian} 535106266Sjulian 536106266Sjulian/* 537106266Sjulian * Clear out the data we've queued 538106266Sjulian */ 539106266Sjulianstatic void 540106266Sjulianng_source_clr_data (sc_p sc) 541106266Sjulian{ 542106266Sjulian struct mbuf *m; 543106266Sjulian 544106266Sjulian for (;;) { 545125031Sharti _IF_DEQUEUE(&sc->snd_queue, m); 546106266Sjulian if (m == NULL) 547106266Sjulian break; 548106266Sjulian NG_FREE_M(m); 549106266Sjulian } 550106266Sjulian sc->queueOctets = 0; 551106266Sjulian} 552106266Sjulian 553106266Sjulian/* 554106266Sjulian * Start sending queued data out the output hook 555106266Sjulian */ 556144674Sglebiusstatic int 557144674Sglebiusng_source_start(sc_p sc, uint64_t packets) 558106266Sjulian{ 559144674Sglebius if (sc->output_ifp == NULL) { 560144674Sglebius printf("ng_source: start without iface configured\n"); 561144674Sglebius return (ENXIO); 562144674Sglebius } 563144674Sglebius 564144674Sglebius if (sc->node->nd_flags & NG_SOURCE_ACTIVE) 565144674Sglebius return (EBUSY); 566144674Sglebius 567144674Sglebius sc->node->nd_flags |= NG_SOURCE_ACTIVE; 568144674Sglebius 569144674Sglebius sc->packets = packets; 570144674Sglebius timevalclear(&sc->stats.elapsedTime); 571144674Sglebius timevalclear(&sc->stats.endTime); 572144674Sglebius getmicrotime(&sc->stats.startTime); 573153690Sglebius getmicrotime(&sc->stats.lastTime); 574144674Sglebius ng_callout(&sc->intr_ch, sc->node, NULL, 0, 575144674Sglebius ng_source_intr, sc, 0); 576144674Sglebius 577144674Sglebius return (0); 578106266Sjulian} 579106266Sjulian 580106266Sjulian/* 581106266Sjulian * Stop sending queued data out the output hook 582106266Sjulian */ 583106266Sjulianstatic void 584144674Sglebiusng_source_stop(sc_p sc) 585106266Sjulian{ 586144674Sglebius ng_uncallout(&sc->intr_ch, sc->node); 587144674Sglebius sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 588144674Sglebius getmicrotime(&sc->stats.endTime); 589144674Sglebius sc->stats.elapsedTime = sc->stats.endTime; 590144674Sglebius timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 591106266Sjulian} 592106266Sjulian 593106266Sjulian/* 594106266Sjulian * While active called every NG_SOURCE_INTR_TICKS ticks. 595106266Sjulian * Sends as many packets as the interface connected to our 596106266Sjulian * output hook is able to enqueue. 597106266Sjulian */ 598106266Sjulianstatic void 599125243Sharting_source_intr(node_p node, hook_p hook, void *arg1, int arg2) 600106266Sjulian{ 601125243Sharti sc_p sc = (sc_p)arg1; 602106266Sjulian struct ifqueue *ifq; 603106266Sjulian int packets; 604106266Sjulian 605125077Sharti KASSERT(sc != NULL, ("%s: null node private", __func__)); 606106266Sjulian 607144674Sglebius if (sc->packets == 0 || sc->output == NULL 608106321Sjulian || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 609106266Sjulian ng_source_stop(sc); 610106266Sjulian return; 611106266Sjulian } 612106266Sjulian 613125033Sharti if (sc->output_ifp != NULL) { 614141745Sru ifq = (struct ifqueue *)&sc->output_ifp->if_snd; 615125033Sharti packets = ifq->ifq_maxlen - ifq->ifq_len; 616125033Sharti } else 617125033Sharti packets = sc->snd_queue.ifq_len; 618125033Sharti 619153690Sglebius if (sc->stats.maxPps != 0) { 620153690Sglebius struct timeval now, elapsed; 621153690Sglebius uint64_t usec; 622153690Sglebius int maxpkt; 623153690Sglebius 624153690Sglebius getmicrotime(&now); 625153690Sglebius elapsed = now; 626153690Sglebius timevalsub(&elapsed, &sc->stats.lastTime); 627153690Sglebius usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec; 628153690Sglebius maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000; 629153690Sglebius sc->stats.lastTime = now; 630153690Sglebius if (packets > maxpkt) 631153690Sglebius packets = maxpkt; 632153690Sglebius } 633153690Sglebius 634106266Sjulian ng_source_send(sc, packets, NULL); 635125243Sharti if (sc->packets == 0) 636106266Sjulian ng_source_stop(sc); 637125243Sharti else 638138268Sglebius ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS, 639137136Sglebius ng_source_intr, sc, 0); 640106266Sjulian} 641106266Sjulian 642106266Sjulian/* 643154707Sglebius * Send packets out our output hook. 644106266Sjulian */ 645106266Sjulianstatic int 646154707Sglebiusng_source_send(sc_p sc, int tosend, int *sent_p) 647106266Sjulian{ 648106266Sjulian struct mbuf *m, *m2; 649154707Sglebius int sent; 650106266Sjulian int error = 0; 651106266Sjulian 652125077Sharti KASSERT(tosend >= 0, ("%s: negative tosend param", __func__)); 653106321Sjulian KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 654125077Sharti ("%s: inactive node", __func__)); 655106266Sjulian 656144674Sglebius if ((uint64_t)tosend > sc->packets) 657106266Sjulian tosend = sc->packets; 658106266Sjulian 659154707Sglebius /* Go through the queue sending packets one by one. */ 660106266Sjulian for (sent = 0; error == 0 && sent < tosend; ++sent) { 661125031Sharti _IF_DEQUEUE(&sc->snd_queue, m); 662106266Sjulian if (m == NULL) 663106266Sjulian break; 664106266Sjulian 665154707Sglebius /* Duplicate the packet. */ 666111119Simp m2 = m_copypacket(m, M_DONTWAIT); 667106266Sjulian if (m2 == NULL) { 668125031Sharti _IF_PREPEND(&sc->snd_queue, m); 669106266Sjulian error = ENOBUFS; 670106266Sjulian break; 671106266Sjulian } 672106266Sjulian 673144674Sglebius /* Re-enqueue the original packet for us. */ 674125031Sharti _IF_ENQUEUE(&sc->snd_queue, m); 675106266Sjulian 676154707Sglebius sc->stats.outFrames++; 677154707Sglebius sc->stats.outOctets += m2->m_pkthdr.len; 678154707Sglebius NG_SEND_DATA_ONLY(error, sc->output, m2); 679154707Sglebius if (error) 680106266Sjulian break; 681106266Sjulian } 682106266Sjulian 683106266Sjulian sc->packets -= sent; 684106266Sjulian if (sent_p != NULL) 685106266Sjulian *sent_p = sent; 686106266Sjulian return (error); 687106266Sjulian} 688