ng_source.c revision 106319
1106266Sjulian/* 2106266Sjulian * ng_source.c 3106266Sjulian * 4106266Sjulian * Copyright 2002 Sandvine Inc. 5106266Sjulian * All rights reserved. 6106266Sjulian * 7106266Sjulian * Subject to the following obligations and disclaimer of warranty, use and 8106266Sjulian * redistribution of this software, in source or object code forms, with or 9106319Sjulian * without modifications are expressly permitted by Sandvine Inc.; provided, 10106266Sjulian * however, that: 11106319Sjulian * 1. Any and all reproductions of the source or object code must include the 12106319Sjulian * copyright notice above and the following disclaimer of warranties; and 13106266Sjulian * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 14106319Sjulian * trademarks, including the mark "SANDVINE" on advertising, endorsements, 15106319Sjulian * or otherwise except as such appears in the above copyright notice or in 16106266Sjulian * the software. 17106266Sjulian * 18106266Sjulian * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 19106319Sjulian * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 20106319Sjulian * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 21106319Sjulian * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 22106266Sjulian * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 23106266Sjulian * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 24106266Sjulian * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 25106266Sjulian * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 26106266Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 27106319Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28106266Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 29106266Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 30106266Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31106266Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32106266Sjulian * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 33106266Sjulian * DAMAGE. 34106266Sjulian * 35106266Sjulian * Author: Dave Chapeskie <dchapeskie@sandvine.com> 36106266Sjulian * 37106266Sjulian * $FreeBSD: head/sys/netgraph/ng_source.c 106319 2002-11-02 01:26:28Z julian $ 38106266Sjulian */ 39106266Sjulian 40106266Sjulian/* 41106266Sjulian * This node is used for high speed packet geneneration. It queues 42106266Sjulian * all data recieved on it's 'input' hook and when told to start via 43106266Sjulian * a control message it sends the packets out it's 'output' hook. In 44106266Sjulian * this way this node can be preloaded with a packet stream which is 45106266Sjulian * continuously sent. 46106266Sjulian * 47106266Sjulian * Currently it just copies the mbufs as required. It could do various 48106266Sjulian * tricks to try and avoid this. Probably the best performance would 49106266Sjulian * be achieved by modifying the appropriate drivers to be told to 50106266Sjulian * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 51106266Sjulian * transmit descriptors) under control of this node; perhaps via some 52106266Sjulian * flag in the mbuf or some such. The node would peak at an appropriate 53106266Sjulian * ifnet flag to see if such support is available for the connected 54106266Sjulian * interface. 55106266Sjulian */ 56106266Sjulian 57106266Sjulian#include <sys/param.h> 58106266Sjulian#include <sys/systm.h> 59106266Sjulian#include <sys/errno.h> 60106266Sjulian#include <sys/kernel.h> 61106266Sjulian#include <sys/malloc.h> 62106266Sjulian#include <sys/mbuf.h> 63106266Sjulian#include <sys/socket.h> 64106266Sjulian#include <net/if.h> 65106266Sjulian#include <net/if_var.h> 66106266Sjulian#include <netgraph/ng_message.h> 67106266Sjulian#include <netgraph/netgraph.h> 68106266Sjulian#include <netgraph/ng_parse.h> 69106266Sjulian#include <netgraph/ng_ether.h> 70106266Sjulian#include <netgraph/ng_source.h> 71106266Sjulian 72106266Sjulian#define NG_SOURCE_INTR_TICKS 1 73106266Sjulian#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 74106266Sjulian 75106266Sjulian 76106266Sjulian/* Per hook info */ 77106266Sjulianstruct source_hookinfo { 78106266Sjulian hook_p hook; 79106266Sjulian}; 80106266Sjulian 81106266Sjulian/* Per node info */ 82106266Sjulianstruct privdata { 83106266Sjulian node_p node; 84106266Sjulian struct source_hookinfo input; 85106266Sjulian struct source_hookinfo output; 86106266Sjulian struct ng_source_stats stats; 87106319Sjulian struct ifqueue snd_queue; /* packets to send */ 88106266Sjulian struct ifnet *output_ifp; 89106266Sjulian struct callout_handle intr_ch; 90106319Sjulian u_int64_t packets; /* packets to send */ 91106266Sjulian u_int32_t queueOctets; 92106266Sjulian}; 93106266Sjuliantypedef struct privdata *sc_p; 94106266Sjulian 95106266Sjulian/* Node flags */ 96106266Sjulian#define NG_SOURCE_ACTIVE (NGF_TYPE1) 97106266Sjulian 98106266Sjulian/* XXX */ 99106266Sjulian#if 1 100106266Sjulian#undef KASSERT 101106266Sjulian#define KASSERT(expr,msg) do { \ 102106266Sjulian if (!(expr)) { \ 103106266Sjulian printf msg ; \ 104106266Sjulian panic("Assertion"); \ 105106266Sjulian } \ 106106266Sjulian } while(0) 107106266Sjulian#endif 108106266Sjulian 109106266Sjulian/* Netgraph methods */ 110106266Sjulianstatic ng_constructor_t ng_source_constructor; 111106266Sjulianstatic ng_rcvmsg_t ng_source_rcvmsg; 112106266Sjulianstatic ng_shutdown_t ng_source_rmnode; 113106266Sjulianstatic ng_newhook_t ng_source_newhook; 114106266Sjulianstatic ng_rcvdata_t ng_source_rcvdata; 115106266Sjulianstatic ng_disconnect_t ng_source_disconnect; 116106266Sjulian 117106266Sjulian/* Other functions */ 118106266Sjulianstatic timeout_t ng_source_intr; 119106266Sjulianstatic int ng_source_get_output_ifp (sc_p); 120106266Sjulianstatic void ng_source_clr_data (sc_p); 121106266Sjulianstatic void ng_source_start (sc_p); 122106266Sjulianstatic void ng_source_stop (sc_p); 123106266Sjulianstatic int ng_source_send (sc_p, int, int *); 124106266Sjulian 125106266Sjulian 126106266Sjulian/* Parse type for timeval */ 127106266Sjulianstatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = 128106266Sjulian{ 129106266Sjulian { "tv_sec", &ng_parse_int32_type }, 130106266Sjulian { "tv_usec", &ng_parse_int32_type }, 131106266Sjulian { NULL } 132106266Sjulian}; 133106266Sjulianconst struct ng_parse_type ng_source_timeval_type = { 134106266Sjulian &ng_parse_struct_type, 135106266Sjulian &ng_source_timeval_type_fields 136106266Sjulian}; 137106266Sjulian 138106266Sjulian/* Parse type for struct ng_source_stats */ 139106266Sjulianstatic const struct ng_parse_struct_field ng_source_stats_type_fields[] 140106266Sjulian = NG_SOURCE_STATS_TYPE_INFO; 141106266Sjulianstatic const struct ng_parse_type ng_source_stats_type = { 142106266Sjulian &ng_parse_struct_type, 143106266Sjulian &ng_source_stats_type_fields 144106266Sjulian}; 145106266Sjulian 146106266Sjulian/* List of commands and how to convert arguments to/from ASCII */ 147106266Sjulianstatic const struct ng_cmdlist ng_source_cmds[] = { 148106266Sjulian { 149106266Sjulian NGM_SOURCE_COOKIE, 150106266Sjulian NGM_SOURCE_GET_STATS, 151106266Sjulian "getstats", 152106266Sjulian NULL, 153106266Sjulian &ng_source_stats_type 154106266Sjulian }, 155106266Sjulian { 156106266Sjulian NGM_SOURCE_COOKIE, 157106266Sjulian NGM_SOURCE_CLR_STATS, 158106266Sjulian "clrstats", 159106266Sjulian NULL, 160106266Sjulian NULL 161106266Sjulian }, 162106266Sjulian { 163106266Sjulian NGM_SOURCE_COOKIE, 164106266Sjulian NGM_SOURCE_GETCLR_STATS, 165106266Sjulian "getclrstats", 166106266Sjulian NULL, 167106266Sjulian &ng_source_stats_type 168106266Sjulian }, 169106266Sjulian { 170106266Sjulian NGM_SOURCE_COOKIE, 171106266Sjulian NGM_SOURCE_START, 172106266Sjulian "start", 173106266Sjulian &ng_parse_uint64_type, 174106266Sjulian NULL 175106266Sjulian }, 176106266Sjulian { 177106266Sjulian NGM_SOURCE_COOKIE, 178106266Sjulian NGM_SOURCE_STOP, 179106266Sjulian "stop", 180106266Sjulian NULL, 181106266Sjulian NULL 182106266Sjulian }, 183106266Sjulian { 184106266Sjulian NGM_SOURCE_COOKIE, 185106266Sjulian NGM_SOURCE_CLR_DATA, 186106266Sjulian "clrdata", 187106266Sjulian NULL, 188106266Sjulian NULL 189106266Sjulian }, 190106266Sjulian { 0 } 191106266Sjulian}; 192106266Sjulian 193106266Sjulian/* Netgraph type descriptor */ 194106266Sjulianstatic struct ng_type ng_source_typestruct = { 195106266Sjulian NG_VERSION, 196106266Sjulian NG_SOURCE_NODE_TYPE, 197106266Sjulian NULL, /* module event handler */ 198106266Sjulian ng_source_constructor, 199106266Sjulian ng_source_rcvmsg, 200106266Sjulian ng_source_rmnode, 201106266Sjulian ng_source_newhook, 202106266Sjulian NULL, /* findhook */ 203106266Sjulian NULL, 204106266Sjulian ng_source_rcvdata, /* rcvdata */ 205106266Sjulian ng_source_rcvdata, /* rcvdataq */ 206106266Sjulian ng_source_disconnect, 207106266Sjulian ng_source_cmds 208106266Sjulian}; 209106266SjulianNETGRAPH_INIT(source, &ng_source_typestruct); 210106266Sjulian 211106266Sjulian/* 212106266Sjulian * Node constructor 213106266Sjulian */ 214106266Sjulianstatic int 215106266Sjulianng_source_constructor(node_p *nodep) 216106266Sjulian{ 217106266Sjulian sc_p sc; 218106266Sjulian int error = 0; 219106266Sjulian 220106266Sjulian MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT); 221106266Sjulian if (sc == NULL) 222106266Sjulian return (ENOMEM); 223106266Sjulian bzero(sc, sizeof(*sc)); 224106266Sjulian 225106266Sjulian if ((error = ng_make_node_common(&ng_source_typestruct, nodep))) { 226106266Sjulian FREE(sc, M_NETGRAPH); 227106266Sjulian return (error); 228106266Sjulian } 229106266Sjulian (*nodep)->private = sc; 230106266Sjulian sc->node = *nodep; 231106266Sjulian sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 232106266Sjulian callout_handle_init(&sc->intr_ch); 233106266Sjulian return (0); 234106266Sjulian} 235106266Sjulian 236106266Sjulian/* 237106266Sjulian * Add a hook 238106266Sjulian */ 239106266Sjulianstatic int 240106266Sjulianng_source_newhook(node_p node, hook_p hook, const char *name) 241106266Sjulian{ 242106266Sjulian const sc_p sc = node->private; 243106266Sjulian 244106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 245106266Sjulian if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 246106266Sjulian sc->input.hook = hook; 247106266Sjulian hook->private = &sc->input; 248106266Sjulian } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 249106266Sjulian sc->output.hook = hook; 250106266Sjulian hook->private = &sc->output; 251106266Sjulian sc->output_ifp = 0; 252106266Sjulian bzero(&sc->stats, sizeof(sc->stats)); 253106266Sjulian } else 254106266Sjulian return (EINVAL); 255106266Sjulian return (0); 256106266Sjulian} 257106266Sjulian 258106266Sjulian/* 259106266Sjulian * Receive a control message 260106266Sjulian */ 261106266Sjulianstatic int 262106266Sjulianng_source_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, 263106266Sjulian struct ng_mesg **rptr) 264106266Sjulian{ 265106266Sjulian const sc_p sc = node->private; 266106266Sjulian struct ng_mesg *resp = NULL; 267106266Sjulian int error = 0; 268106266Sjulian 269106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 270106266Sjulian switch (msg->header.typecookie) { 271106266Sjulian case NGM_SOURCE_COOKIE: 272106266Sjulian switch (msg->header.cmd) { 273106266Sjulian case NGM_SOURCE_GET_STATS: 274106266Sjulian case NGM_SOURCE_CLR_STATS: 275106266Sjulian case NGM_SOURCE_GETCLR_STATS: 276106266Sjulian { 277106266Sjulian struct ng_source_stats *stats; 278106266Sjulian 279106266Sjulian if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 280106266Sjulian NG_MKRESPONSE(resp, msg, 281106266Sjulian sizeof(*stats), M_NOWAIT); 282106266Sjulian if (resp == NULL) { 283106266Sjulian error = ENOMEM; 284106266Sjulian goto done; 285106266Sjulian } 286106266Sjulian sc->stats.queueOctets = sc->queueOctets; 287106319Sjulian sc->stats.queueFrames = sc->snd_queue.ifq_len; 288106266Sjulian if ((sc->node->flags & NG_SOURCE_ACTIVE) 289106266Sjulian && !timevalisset(&sc->stats.endTime)) { 290106319Sjulian getmicrotime(&sc->stats.elapsedTime); 291106266Sjulian timevalsub(&sc->stats.elapsedTime, 292106319Sjulian &sc->stats.startTime); 293106266Sjulian } 294106319Sjulian stats = (struct ng_source_stats *)resp->data; 295106266Sjulian bcopy(&sc->stats, stats, sizeof(* stats)); 296106266Sjulian } 297106266Sjulian if (msg->header.cmd != NGM_SOURCE_GET_STATS) 298106266Sjulian bzero(&sc->stats, sizeof(sc->stats)); 299106266Sjulian } 300106266Sjulian break; 301106266Sjulian case NGM_SOURCE_START: 302106266Sjulian { 303106266Sjulian u_int64_t packets = *(u_int64_t *)msg->data; 304106266Sjulian if (sc->output.hook == NULL) { 305106319Sjulian printf("%s: start on node with no output hook\n" 306106319Sjulian , __FUNCTION__); 307106266Sjulian error = EINVAL; 308106266Sjulian break; 309106266Sjulian } 310106266Sjulian /* TODO validation of packets */ 311106266Sjulian sc->packets = packets; 312106266Sjulian ng_source_start(sc); 313106266Sjulian } 314106266Sjulian break; 315106266Sjulian case NGM_SOURCE_STOP: 316106266Sjulian ng_source_stop(sc); 317106266Sjulian break; 318106266Sjulian case NGM_SOURCE_CLR_DATA: 319106266Sjulian ng_source_clr_data(sc); 320106266Sjulian break; 321106266Sjulian default: 322106266Sjulian error = EINVAL; 323106266Sjulian break; 324106266Sjulian } 325106266Sjulian break; 326106266Sjulian default: 327106266Sjulian error = EINVAL; 328106266Sjulian break; 329106266Sjulian } 330106266Sjulian if (rptr) 331106266Sjulian *rptr = resp; 332106266Sjulian else if (resp) 333106266Sjulian FREE(resp, M_NETGRAPH); 334106266Sjulian 335106266Sjuliandone: 336106266Sjulian FREE(msg, M_NETGRAPH); 337106266Sjulian return (error); 338106266Sjulian} 339106266Sjulian 340106266Sjulian/* 341106266Sjulian * Receive data on a hook 342106266Sjulian * 343106266Sjulian * If data comes in the input hook, enqueue it on the send queue. 344106266Sjulian * If data comes in the output hook, discard it. 345106266Sjulian */ 346106266Sjulianstatic int 347106266Sjulianng_source_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 348106266Sjulian{ 349106266Sjulian const sc_p sc = hook->node->private; 350106319Sjulian struct source_hookinfo *const hinfo; 351106266Sjulian int error = 0; 352106266Sjulian 353106319Sjulian hinfo = (struct source_hookinfo *) hook->private; 354106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 355106266Sjulian KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__)); 356106266Sjulian 357106266Sjulian /* Which hook? */ 358106266Sjulian if (hinfo == &sc->output) { 359106266Sjulian /* discard */ 360106266Sjulian NG_FREE_DATA(m, meta); 361106266Sjulian return (error); 362106266Sjulian } 363106266Sjulian KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__)); 364106266Sjulian 365106266Sjulian if ((m->m_flags & M_PKTHDR) == 0) { 366106266Sjulian printf("%s: mbuf without PKTHDR\n", __FUNCTION__); 367106266Sjulian NG_FREE_DATA(m, meta); 368106266Sjulian return (EINVAL); 369106266Sjulian } 370106266Sjulian 371106266Sjulian /* XXX we discard the meta data for now */ 372106266Sjulian NG_FREE_META(meta); 373106266Sjulian 374106266Sjulian /* enque packet */ 375106266Sjulian /* XXX should we check IF_QFULL() ? */ 376106266Sjulian IF_ENQUEUE(&sc->snd_queue, m); 377106266Sjulian sc->queueOctets += m->m_pkthdr.len; 378106266Sjulian 379106266Sjulian return (0); 380106266Sjulian} 381106266Sjulian 382106266Sjulian/* 383106266Sjulian * Shutdown processing 384106266Sjulian */ 385106266Sjulianstatic int 386106266Sjulianng_source_rmnode(node_p node) 387106266Sjulian{ 388106266Sjulian const sc_p sc = node->private; 389106266Sjulian 390106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 391106266Sjulian node->flags |= NG_INVALID; 392106266Sjulian ng_source_stop(sc); 393106266Sjulian ng_cutlinks(node); 394106266Sjulian ng_source_clr_data(sc); 395106266Sjulian ng_unname(node); 396106266Sjulian node->private = NULL; 397106266Sjulian ng_unref(sc->node); 398106266Sjulian FREE(sc, M_NETGRAPH); 399106266Sjulian return (0); 400106266Sjulian} 401106266Sjulian 402106266Sjulian/* 403106266Sjulian * Hook disconnection 404106266Sjulian */ 405106266Sjulianstatic int 406106266Sjulianng_source_disconnect(hook_p hook) 407106266Sjulian{ 408106319Sjulian struct source_hookinfo *const hinfo; 409106319Sjulian sc_p sc; 410106266Sjulian 411106319Sjulian hinfo = (struct source_hookinfo *) hook->private; 412106319Sjulian sc = (sc_p) hinfo->hook->node->private; 413106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 414106266Sjulian hinfo->hook = NULL; 415106266Sjulian if (hook->node->numhooks == 0 || hinfo == &sc->output) 416106266Sjulian ng_rmnode(hook->node); 417106266Sjulian return (0); 418106266Sjulian} 419106266Sjulian 420106266Sjulian/* 421106266Sjulian * Set sc->output_ifp to point to the the struct ifnet of the interface 422106266Sjulian * reached via our output hook. 423106266Sjulian */ 424106266Sjulianstatic int 425106266Sjulianng_source_get_output_ifp(sc_p sc) 426106266Sjulian{ 427106266Sjulian struct ng_mesg *msg, *rsp; 428106266Sjulian struct ifnet *ifp; 429106266Sjulian u_int32_t if_index; 430106266Sjulian int error = 0; 431106266Sjulian int s; 432106266Sjulian 433106266Sjulian sc->output_ifp = NULL; 434106266Sjulian 435106266Sjulian /* Ask the attached node for the connected interface's index */ 436106319Sjulian NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT); 437106266Sjulian if (msg == NULL) 438106266Sjulian return (ENOBUFS); 439106266Sjulian 440106266Sjulian error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, &rsp); 441106266Sjulian if (error != 0) 442106266Sjulian return (error); 443106266Sjulian 444106266Sjulian if (rsp == NULL) 445106266Sjulian return (EINVAL); 446106266Sjulian 447106266Sjulian if (rsp->header.arglen < sizeof(u_int32_t)) 448106266Sjulian return (EINVAL); 449106266Sjulian 450106266Sjulian if_index = *(u_int32_t *)rsp->data; 451106266Sjulian /* Could use ifindex2ifnet[if_index] except that we have no 452106266Sjulian * way of verifying if_index is valid since if_indexlim is 453106266Sjulian * local to if_attach() 454106266Sjulian */ 455106266Sjulian TAILQ_FOREACH(ifp, &ifnet, if_link) { 456106266Sjulian if (ifp->if_index == if_index) 457106266Sjulian break; 458106266Sjulian } 459106266Sjulian 460106266Sjulian if (ifp == NULL) { 461106319Sjulian printf("%s: can't find interface %d\n", __FUNCTION__, if_index); 462106266Sjulian return (EINVAL); 463106266Sjulian } 464106266Sjulian sc->output_ifp = ifp; 465106266Sjulian 466106266Sjulian#if 1 467106266Sjulian /* XXX mucking with a drivers ifqueue size is ugly but we need it 468106266Sjulian * to queue a lot of packets to get close to line rate on a gigabit 469106266Sjulian * interface with small packets. 470106266Sjulian * XXX we should restore the original value at stop or disconnect 471106266Sjulian */ 472106266Sjulian s = splimp(); /* XXX is this required? */ 473106266Sjulian if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) 474106266Sjulian { 475106266Sjulian printf("ng_source: changing ifq_maxlen from %d to %d\n", 476106319Sjulian ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 477106266Sjulian ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 478106266Sjulian } 479106266Sjulian splx(s); 480106266Sjulian#endif 481106266Sjulian return (0); 482106266Sjulian} 483106266Sjulian 484106266Sjulian/* 485106266Sjulian * Set the attached ethernet node's ethernet source address override flag. 486106266Sjulian */ 487106266Sjulianstatic int 488106266Sjulianng_source_set_autosrc(sc_p sc, u_int32_t flag) 489106266Sjulian{ 490106266Sjulian struct ng_mesg *msg; 491106266Sjulian int error = 0; 492106266Sjulian 493106266Sjulian NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 494106266Sjulian sizeof (u_int32_t), M_NOWAIT); 495106266Sjulian if (msg == NULL) 496106266Sjulian return(ENOBUFS); 497106266Sjulian 498106266Sjulian *(u_int32_t *)msg->data = flag; 499106266Sjulian error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, NULL); 500106266Sjulian return (error); 501106266Sjulian} 502106266Sjulian 503106266Sjulian/* 504106266Sjulian * Clear out the data we've queued 505106266Sjulian */ 506106266Sjulianstatic void 507106266Sjulianng_source_clr_data (sc_p sc) 508106266Sjulian{ 509106266Sjulian struct mbuf *m; 510106266Sjulian 511106266Sjulian SPLASSERT(net, __FUNCTION__); 512106266Sjulian for (;;) { 513106266Sjulian IF_DEQUEUE(&sc->snd_queue, m); 514106266Sjulian if (m == NULL) 515106266Sjulian break; 516106266Sjulian NG_FREE_M(m); 517106266Sjulian } 518106266Sjulian sc->queueOctets = 0; 519106266Sjulian} 520106266Sjulian 521106266Sjulian/* 522106266Sjulian * Start sending queued data out the output hook 523106266Sjulian */ 524106266Sjulianstatic void 525106266Sjulianng_source_start (sc_p sc) 526106266Sjulian{ 527106266Sjulian SPLASSERT(net, __FUNCTION__); 528106266Sjulian KASSERT(sc->output.hook != NULL, 529106266Sjulian ("%s: output hook unconnected", __FUNCTION__)); 530106266Sjulian if ((sc->node->flags & NG_SOURCE_ACTIVE) == 0) { 531106319Sjulian if (sc->output_ifp == NULL && ng_source_get_output_ifp(sc) != 0) 532106266Sjulian return; 533106266Sjulian ng_source_set_autosrc(sc, 0); 534106266Sjulian sc->node->flags |= NG_SOURCE_ACTIVE; 535106266Sjulian timevalclear(&sc->stats.elapsedTime); 536106266Sjulian timevalclear(&sc->stats.endTime); 537106266Sjulian getmicrotime(&sc->stats.startTime); 538106266Sjulian sc->intr_ch = timeout(ng_source_intr, sc, 0); 539106266Sjulian } 540106266Sjulian} 541106266Sjulian 542106266Sjulian/* 543106266Sjulian * Stop sending queued data out the output hook 544106266Sjulian */ 545106266Sjulianstatic void 546106266Sjulianng_source_stop (sc_p sc) 547106266Sjulian{ 548106266Sjulian SPLASSERT(net, __FUNCTION__); 549106266Sjulian if (sc->node->flags & NG_SOURCE_ACTIVE) { 550106266Sjulian untimeout(ng_source_intr, sc, sc->intr_ch); 551106266Sjulian sc->node->flags &= ~NG_SOURCE_ACTIVE; 552106266Sjulian getmicrotime(&sc->stats.endTime); 553106266Sjulian sc->stats.elapsedTime = sc->stats.endTime; 554106266Sjulian timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 555106266Sjulian /* XXX should set this to the initial value instead */ 556106266Sjulian ng_source_set_autosrc(sc, 1); 557106266Sjulian } 558106266Sjulian} 559106266Sjulian 560106266Sjulian/* 561106266Sjulian * While active called every NG_SOURCE_INTR_TICKS ticks. 562106266Sjulian * Sends as many packets as the interface connected to our 563106266Sjulian * output hook is able to enqueue. 564106266Sjulian */ 565106266Sjulianstatic void 566106266Sjulianng_source_intr (void *arg) 567106266Sjulian{ 568106266Sjulian const sc_p sc = (sc_p) arg; 569106266Sjulian struct ifqueue *ifq; 570106266Sjulian int packets; 571106266Sjulian 572106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 573106266Sjulian 574106266Sjulian callout_handle_init(&sc->intr_ch); 575106266Sjulian if (sc->packets == 0 || sc->output.hook == NULL 576106266Sjulian || (sc->node->flags & NG_SOURCE_ACTIVE) == 0) { 577106266Sjulian ng_source_stop(sc); 578106266Sjulian return; 579106266Sjulian } 580106266Sjulian 581106266Sjulian ifq = &sc->output_ifp->if_snd; 582106266Sjulian packets = ifq->ifq_maxlen - ifq->ifq_len; 583106266Sjulian ng_source_send(sc, packets, NULL); 584106266Sjulian if (sc->packets == 0) { 585106266Sjulian int s = splnet(); 586106266Sjulian ng_source_stop(sc); 587106266Sjulian splx(s); 588106266Sjulian } else 589106319Sjulian sc->intr_ch = timeout(ng_source_intr, sc, NG_SOURCE_INTR_TICKS); 590106266Sjulian} 591106266Sjulian 592106266Sjulian/* 593106266Sjulian * Send packets out our output hook 594106266Sjulian */ 595106266Sjulianstatic int 596106266Sjulianng_source_send (sc_p sc, int tosend, int *sent_p) 597106266Sjulian{ 598106266Sjulian struct ifqueue tmp_queue; 599106266Sjulian struct mbuf *m, *m2; 600106266Sjulian int sent = 0; 601106266Sjulian int error = 0; 602106266Sjulian int s, s2; 603106266Sjulian 604106266Sjulian KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 605106266Sjulian KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__)); 606106266Sjulian KASSERT(sc->node->flags & NG_SOURCE_ACTIVE, 607106266Sjulian ("%s: inactive node", __FUNCTION__)); 608106266Sjulian 609106266Sjulian if ((u_int64_t)tosend > sc->packets) 610106266Sjulian tosend = sc->packets; 611106266Sjulian 612106266Sjulian /* Copy the required number of packets to a temporary queue */ 613106266Sjulian bzero (&tmp_queue, sizeof (tmp_queue)); 614106266Sjulian for (sent = 0; error == 0 && sent < tosend; ++sent) { 615106266Sjulian s = splnet(); 616106266Sjulian IF_DEQUEUE(&sc->snd_queue, m); 617106266Sjulian splx(s); 618106266Sjulian if (m == NULL) 619106266Sjulian break; 620106266Sjulian 621106266Sjulian /* duplicate the packet */ 622106266Sjulian m2 = m_copypacket(m, M_NOWAIT); 623106266Sjulian if (m2 == NULL) { 624106266Sjulian s = splnet(); 625106266Sjulian IF_PREPEND(&sc->snd_queue, m); 626106266Sjulian splx(s); 627106266Sjulian error = ENOBUFS; 628106266Sjulian break; 629106266Sjulian } 630106266Sjulian 631106266Sjulian /* re-enqueue the original packet for us */ 632106266Sjulian s = splnet(); 633106266Sjulian IF_ENQUEUE(&sc->snd_queue, m); 634106266Sjulian splx(s); 635106266Sjulian 636106266Sjulian /* queue the copy for sending at smplimp */ 637106266Sjulian IF_ENQUEUE(&tmp_queue, m2); 638106266Sjulian } 639106266Sjulian 640106266Sjulian sent = 0; 641106266Sjulian s = splimp(); 642106266Sjulian for (;;) { 643106266Sjulian IF_DEQUEUE(&tmp_queue, m2); 644106266Sjulian if (m2 == NULL) 645106266Sjulian break; 646106266Sjulian if (error == 0) { 647106266Sjulian ++sent; 648106266Sjulian sc->stats.outFrames++; 649106266Sjulian sc->stats.outOctets += m2->m_pkthdr.len; 650106266Sjulian s2 = splnet(); 651106266Sjulian NG_SEND_DATA_ONLY(error, sc->output.hook, m2); 652106266Sjulian splx(s2); 653106266Sjulian } else { 654106266Sjulian NG_FREE_M(m2); 655106266Sjulian } 656106266Sjulian } 657106266Sjulian splx(s); 658106266Sjulian 659106266Sjulian sc->packets -= sent; 660106266Sjulian if (sent_p != NULL) 661106266Sjulian *sent_p = sent; 662106266Sjulian return (error); 663106266Sjulian} 664