ng_source.c revision 167156
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 167156 2007-03-01 23:16:17Z emaste $"); 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 80167156Semaste#define mtod_off(m,off,t) ((t)(mtod((m),caddr_t)+(off))) 81167156Semaste 82106266Sjulian/* Per node info */ 83106266Sjulianstruct privdata { 84106266Sjulian node_p node; 85144674Sglebius hook_p input; 86144674Sglebius hook_p output; 87106266Sjulian struct ng_source_stats stats; 88106319Sjulian struct ifqueue snd_queue; /* packets to send */ 89106266Sjulian struct ifnet *output_ifp; 90137138Sglebius struct callout intr_ch; 91144674Sglebius uint64_t packets; /* packets to send */ 92144674Sglebius uint32_t queueOctets; 93167156Semaste struct ng_source_embed_info embed_timestamp; 94106266Sjulian}; 95106266Sjuliantypedef struct privdata *sc_p; 96106266Sjulian 97106266Sjulian/* Node flags */ 98106266Sjulian#define NG_SOURCE_ACTIVE (NGF_TYPE1) 99106266Sjulian 100106266Sjulian/* Netgraph methods */ 101106266Sjulianstatic ng_constructor_t ng_source_constructor; 102106266Sjulianstatic ng_rcvmsg_t ng_source_rcvmsg; 103106266Sjulianstatic ng_shutdown_t ng_source_rmnode; 104106266Sjulianstatic ng_newhook_t ng_source_newhook; 105144674Sglebiusstatic ng_connect_t ng_source_connect; 106106266Sjulianstatic ng_rcvdata_t ng_source_rcvdata; 107106266Sjulianstatic ng_disconnect_t ng_source_disconnect; 108106266Sjulian 109106266Sjulian/* Other functions */ 110125243Shartistatic void ng_source_intr(node_p, hook_p, void *, int); 111106266Sjulianstatic void ng_source_clr_data (sc_p); 112144674Sglebiusstatic int ng_source_start (sc_p, uint64_t); 113106266Sjulianstatic void ng_source_stop (sc_p); 114106266Sjulianstatic int ng_source_send (sc_p, int, int *); 115144674Sglebiusstatic int ng_source_store_output_ifp(sc_p, char *); 116167156Semastestatic void ng_source_packet_mod(sc_p, struct mbuf *, 117167156Semaste int, int, caddr_t, int); 118167156Semastestatic int ng_source_dup_mod(sc_p, struct mbuf *, 119167156Semaste struct mbuf **); 120106266Sjulian 121106266Sjulian/* Parse type for timeval */ 122125077Shartistatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = { 123106266Sjulian { "tv_sec", &ng_parse_int32_type }, 124106266Sjulian { "tv_usec", &ng_parse_int32_type }, 125106266Sjulian { NULL } 126106266Sjulian}; 127106266Sjulianconst struct ng_parse_type ng_source_timeval_type = { 128106266Sjulian &ng_parse_struct_type, 129106266Sjulian &ng_source_timeval_type_fields 130106266Sjulian}; 131106266Sjulian 132106266Sjulian/* Parse type for struct ng_source_stats */ 133106266Sjulianstatic const struct ng_parse_struct_field ng_source_stats_type_fields[] 134106266Sjulian = NG_SOURCE_STATS_TYPE_INFO; 135106266Sjulianstatic const struct ng_parse_type ng_source_stats_type = { 136106266Sjulian &ng_parse_struct_type, 137106266Sjulian &ng_source_stats_type_fields 138106266Sjulian}; 139106266Sjulian 140167156Semaste/* Parse type for struct ng_source_embed_info */ 141167156Semastestatic const struct ng_parse_struct_field ng_source_embed_type_fields[] = 142167156Semaste NG_SOURCE_EMBED_TYPE_INFO; 143167156Semastestatic const struct ng_parse_type ng_source_embed_type = { 144167156Semaste &ng_parse_struct_type, 145167156Semaste &ng_source_embed_type_fields 146167156Semaste}; 147167156Semaste 148106266Sjulian/* List of commands and how to convert arguments to/from ASCII */ 149106266Sjulianstatic const struct ng_cmdlist ng_source_cmds[] = { 150106266Sjulian { 151106266Sjulian NGM_SOURCE_COOKIE, 152106266Sjulian NGM_SOURCE_GET_STATS, 153106266Sjulian "getstats", 154106266Sjulian NULL, 155106266Sjulian &ng_source_stats_type 156106266Sjulian }, 157106266Sjulian { 158106266Sjulian NGM_SOURCE_COOKIE, 159106266Sjulian NGM_SOURCE_CLR_STATS, 160106266Sjulian "clrstats", 161106266Sjulian NULL, 162106266Sjulian NULL 163106266Sjulian }, 164106266Sjulian { 165106266Sjulian NGM_SOURCE_COOKIE, 166106266Sjulian NGM_SOURCE_GETCLR_STATS, 167106266Sjulian "getclrstats", 168106266Sjulian NULL, 169106266Sjulian &ng_source_stats_type 170106266Sjulian }, 171106266Sjulian { 172106266Sjulian NGM_SOURCE_COOKIE, 173106266Sjulian NGM_SOURCE_START, 174106266Sjulian "start", 175106266Sjulian &ng_parse_uint64_type, 176106266Sjulian NULL 177106266Sjulian }, 178106266Sjulian { 179106266Sjulian NGM_SOURCE_COOKIE, 180106266Sjulian NGM_SOURCE_STOP, 181106266Sjulian "stop", 182106266Sjulian NULL, 183106266Sjulian NULL 184106266Sjulian }, 185106266Sjulian { 186106266Sjulian NGM_SOURCE_COOKIE, 187106266Sjulian NGM_SOURCE_CLR_DATA, 188106266Sjulian "clrdata", 189106266Sjulian NULL, 190106266Sjulian NULL 191106266Sjulian }, 192125033Sharti { 193125033Sharti NGM_SOURCE_COOKIE, 194144674Sglebius NGM_SOURCE_SETIFACE, 195144674Sglebius "setiface", 196144674Sglebius &ng_parse_string_type, 197125033Sharti NULL 198125033Sharti }, 199153690Sglebius { 200153690Sglebius NGM_SOURCE_COOKIE, 201153690Sglebius NGM_SOURCE_SETPPS, 202153690Sglebius "setpps", 203153690Sglebius &ng_parse_uint32_type, 204153690Sglebius NULL 205153690Sglebius }, 206167156Semaste { 207167156Semaste NGM_SOURCE_COOKIE, 208167156Semaste NGM_SOURCE_SET_TIMESTAMP, 209167156Semaste "settimestamp", 210167156Semaste &ng_source_embed_type, 211167156Semaste NULL 212167156Semaste }, 213167156Semaste { 214167156Semaste NGM_SOURCE_COOKIE, 215167156Semaste NGM_SOURCE_GET_TIMESTAMP, 216167156Semaste "gettimestamp", 217167156Semaste NULL, 218167156Semaste &ng_source_embed_type 219167156Semaste }, 220106266Sjulian { 0 } 221106266Sjulian}; 222106266Sjulian 223106266Sjulian/* Netgraph type descriptor */ 224106266Sjulianstatic struct ng_type ng_source_typestruct = { 225129823Sjulian .version = NG_ABI_VERSION, 226129823Sjulian .name = NG_SOURCE_NODE_TYPE, 227129823Sjulian .constructor = ng_source_constructor, 228129823Sjulian .rcvmsg = ng_source_rcvmsg, 229129823Sjulian .shutdown = ng_source_rmnode, 230129823Sjulian .newhook = ng_source_newhook, 231144674Sglebius .connect = ng_source_connect, 232129823Sjulian .rcvdata = ng_source_rcvdata, 233129823Sjulian .disconnect = ng_source_disconnect, 234129823Sjulian .cmdlist = ng_source_cmds, 235106266Sjulian}; 236106266SjulianNETGRAPH_INIT(source, &ng_source_typestruct); 237106266Sjulian 238144674Sglebiusstatic int ng_source_set_autosrc(sc_p, uint32_t); 239125032Sharti 240106266Sjulian/* 241106266Sjulian * Node constructor 242106266Sjulian */ 243106266Sjulianstatic int 244106321Sjulianng_source_constructor(node_p node) 245106266Sjulian{ 246106266Sjulian sc_p sc; 247106266Sjulian 248125030Sharti sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 249106266Sjulian if (sc == NULL) 250106266Sjulian return (ENOMEM); 251106266Sjulian 252106321Sjulian NG_NODE_SET_PRIVATE(node, sc); 253106321Sjulian sc->node = node; 254106266Sjulian sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 255137138Sglebius ng_callout_init(&sc->intr_ch); 256137138Sglebius 257106266Sjulian return (0); 258106266Sjulian} 259106266Sjulian 260106266Sjulian/* 261106266Sjulian * Add a hook 262106266Sjulian */ 263106266Sjulianstatic int 264106266Sjulianng_source_newhook(node_p node, hook_p hook, const char *name) 265106266Sjulian{ 266144674Sglebius sc_p sc = NG_NODE_PRIVATE(node); 267106266Sjulian 268106266Sjulian if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 269144674Sglebius sc->input = hook; 270106266Sjulian } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 271144674Sglebius sc->output = hook; 272106266Sjulian sc->output_ifp = 0; 273106266Sjulian bzero(&sc->stats, sizeof(sc->stats)); 274106266Sjulian } else 275106266Sjulian return (EINVAL); 276144674Sglebius 277106266Sjulian return (0); 278106266Sjulian} 279106266Sjulian 280106266Sjulian/* 281144674Sglebius * Hook has been added 282144674Sglebius */ 283144674Sglebiusstatic int 284144674Sglebiusng_source_connect(hook_p hook) 285144674Sglebius{ 286144674Sglebius sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 287144674Sglebius struct ng_mesg *msg; 288144674Sglebius int dummy_error = 0; 289144674Sglebius 290144674Sglebius /* 291144674Sglebius * If this is "output" hook, then request information 292144674Sglebius * from our downstream. 293144674Sglebius */ 294144674Sglebius if (hook == sc->output) { 295144674Sglebius NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME, 296144674Sglebius 0, M_NOWAIT); 297144674Sglebius if (msg == NULL) 298144674Sglebius return (ENOBUFS); 299144674Sglebius 300144674Sglebius /* 301144674Sglebius * Our hook and peer hook have HK_INVALID flag set, 302144674Sglebius * so we can't use NG_SEND_MSG_HOOK() macro here. 303144674Sglebius */ 304144674Sglebius NG_SEND_MSG_ID(dummy_error, sc->node, msg, 305144674Sglebius NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node)); 306144674Sglebius } 307144674Sglebius 308144674Sglebius return (0); 309144674Sglebius} 310144674Sglebius 311144674Sglebius/* 312106266Sjulian * Receive a control message 313106266Sjulian */ 314106266Sjulianstatic int 315106321Sjulianng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 316106266Sjulian{ 317144674Sglebius sc_p sc = NG_NODE_PRIVATE(node); 318144674Sglebius struct ng_mesg *msg, *resp = NULL; 319106266Sjulian int error = 0; 320106266Sjulian 321106321Sjulian NGI_GET_MSG(item, msg); 322144674Sglebius 323106266Sjulian switch (msg->header.typecookie) { 324106266Sjulian case NGM_SOURCE_COOKIE: 325106435Sjulian if (msg->header.flags & NGF_RESP) { 326106435Sjulian error = EINVAL; 327106435Sjulian break; 328106435Sjulian } 329106266Sjulian switch (msg->header.cmd) { 330106266Sjulian case NGM_SOURCE_GET_STATS: 331106266Sjulian case NGM_SOURCE_CLR_STATS: 332106266Sjulian case NGM_SOURCE_GETCLR_STATS: 333106266Sjulian { 334106266Sjulian struct ng_source_stats *stats; 335106266Sjulian 336106266Sjulian if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 337106266Sjulian NG_MKRESPONSE(resp, msg, 338106266Sjulian sizeof(*stats), M_NOWAIT); 339106266Sjulian if (resp == NULL) { 340106266Sjulian error = ENOMEM; 341106266Sjulian goto done; 342106266Sjulian } 343106266Sjulian sc->stats.queueOctets = sc->queueOctets; 344106319Sjulian sc->stats.queueFrames = sc->snd_queue.ifq_len; 345106321Sjulian if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 346106266Sjulian && !timevalisset(&sc->stats.endTime)) { 347106319Sjulian getmicrotime(&sc->stats.elapsedTime); 348106266Sjulian timevalsub(&sc->stats.elapsedTime, 349106319Sjulian &sc->stats.startTime); 350106266Sjulian } 351106319Sjulian stats = (struct ng_source_stats *)resp->data; 352106266Sjulian bcopy(&sc->stats, stats, sizeof(* stats)); 353106266Sjulian } 354106266Sjulian if (msg->header.cmd != NGM_SOURCE_GET_STATS) 355106266Sjulian bzero(&sc->stats, sizeof(sc->stats)); 356106266Sjulian } 357106266Sjulian break; 358106266Sjulian case NGM_SOURCE_START: 359106266Sjulian { 360144674Sglebius uint64_t packets; 361144674Sglebius 362144674Sglebius if (msg->header.arglen != sizeof(uint64_t)) { 363106266Sjulian error = EINVAL; 364106266Sjulian break; 365106266Sjulian } 366125033Sharti 367144674Sglebius packets = *(uint64_t *)msg->data; 368144674Sglebius 369144674Sglebius error = ng_source_start(sc, packets); 370144674Sglebius 371144674Sglebius break; 372125033Sharti } 373106266Sjulian case NGM_SOURCE_STOP: 374106266Sjulian ng_source_stop(sc); 375106266Sjulian break; 376106266Sjulian case NGM_SOURCE_CLR_DATA: 377106266Sjulian ng_source_clr_data(sc); 378106266Sjulian break; 379144674Sglebius case NGM_SOURCE_SETIFACE: 380144674Sglebius { 381144674Sglebius char *ifname = (char *)msg->data; 382144674Sglebius 383144674Sglebius if (msg->header.arglen < 2) { 384144674Sglebius error = EINVAL; 385144674Sglebius break; 386144674Sglebius } 387144674Sglebius 388144674Sglebius ng_source_store_output_ifp(sc, ifname); 389144674Sglebius break; 390144674Sglebius } 391153690Sglebius case NGM_SOURCE_SETPPS: 392153690Sglebius { 393153690Sglebius uint32_t pps; 394153690Sglebius 395153690Sglebius if (msg->header.arglen != sizeof(uint32_t)) { 396153690Sglebius error = EINVAL; 397153690Sglebius break; 398153690Sglebius } 399153690Sglebius 400153690Sglebius pps = *(uint32_t *)msg->data; 401153690Sglebius 402153690Sglebius sc->stats.maxPps = pps; 403153690Sglebius 404153690Sglebius break; 405153690Sglebius } 406167156Semaste case NGM_SOURCE_SET_TIMESTAMP: 407167156Semaste { 408167156Semaste struct ng_source_embed_info *embed; 409167156Semaste 410167156Semaste embed = (struct ng_source_embed_info *)msg->data; 411167156Semaste bcopy(embed, &sc->embed_timestamp, sizeof(*embed)); 412167156Semaste 413167156Semaste break; 414167156Semaste } 415167156Semaste case NGM_SOURCE_GET_TIMESTAMP: 416167156Semaste { 417167156Semaste struct ng_source_embed_info *embed; 418167156Semaste 419167156Semaste NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT); 420167156Semaste if (resp == NULL) { 421167156Semaste error = ENOMEM; 422167156Semaste goto done; 423167156Semaste } 424167156Semaste embed = (struct ng_source_embed_info *)resp->data; 425167156Semaste bcopy(&sc->embed_timestamp, embed, sizeof(*embed)); 426167156Semaste 427167156Semaste break; 428167156Semaste } 429106266Sjulian default: 430106266Sjulian error = EINVAL; 431106266Sjulian break; 432106266Sjulian } 433106266Sjulian break; 434106435Sjulian case NGM_ETHER_COOKIE: 435106435Sjulian if (!(msg->header.flags & NGF_RESP)) { 436106435Sjulian error = EINVAL; 437106435Sjulian break; 438106435Sjulian } 439106435Sjulian switch (msg->header.cmd) { 440144674Sglebius case NGM_ETHER_GET_IFNAME: 441144674Sglebius { 442144674Sglebius char *ifname = (char *)msg->data; 443144674Sglebius 444144674Sglebius if (msg->header.arglen < 2) { 445144674Sglebius error = EINVAL; 446144674Sglebius break; 447144674Sglebius } 448144674Sglebius 449144674Sglebius if (ng_source_store_output_ifp(sc, ifname) == 0) 450106435Sjulian ng_source_set_autosrc(sc, 0); 451106435Sjulian break; 452144674Sglebius } 453106435Sjulian default: 454106435Sjulian error = EINVAL; 455106435Sjulian } 456106435Sjulian break; 457106266Sjulian default: 458106266Sjulian error = EINVAL; 459106266Sjulian break; 460106266Sjulian } 461106266Sjulian 462106266Sjuliandone: 463144674Sglebius /* Take care of synchronous response, if any. */ 464106321Sjulian NG_RESPOND_MSG(error, node, item, resp); 465144674Sglebius /* Free the message and return. */ 466106321Sjulian NG_FREE_MSG(msg); 467106266Sjulian return (error); 468106266Sjulian} 469106266Sjulian 470106266Sjulian/* 471106266Sjulian * Receive data on a hook 472106266Sjulian * 473106266Sjulian * If data comes in the input hook, enqueue it on the send queue. 474106266Sjulian * If data comes in the output hook, discard it. 475106266Sjulian */ 476106266Sjulianstatic int 477106321Sjulianng_source_rcvdata(hook_p hook, item_p item) 478106266Sjulian{ 479144674Sglebius sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 480144674Sglebius struct mbuf *m; 481106266Sjulian int error = 0; 482106266Sjulian 483106321Sjulian NGI_GET_M(item, m); 484106321Sjulian NG_FREE_ITEM(item); 485106266Sjulian 486106266Sjulian /* Which hook? */ 487144674Sglebius if (hook == sc->output) { 488106266Sjulian /* discard */ 489106321Sjulian NG_FREE_M(m); 490106266Sjulian return (error); 491106266Sjulian } 492144674Sglebius KASSERT(hook == sc->input, ("%s: no hook!", __func__)); 493106266Sjulian 494144674Sglebius /* Enqueue packet. */ 495106266Sjulian /* XXX should we check IF_QFULL() ? */ 496125031Sharti _IF_ENQUEUE(&sc->snd_queue, m); 497106266Sjulian sc->queueOctets += m->m_pkthdr.len; 498106266Sjulian 499106266Sjulian return (0); 500106266Sjulian} 501106266Sjulian 502106266Sjulian/* 503106266Sjulian * Shutdown processing 504106266Sjulian */ 505106266Sjulianstatic int 506106266Sjulianng_source_rmnode(node_p node) 507106266Sjulian{ 508144674Sglebius sc_p sc = NG_NODE_PRIVATE(node); 509106266Sjulian 510106266Sjulian ng_source_stop(sc); 511106266Sjulian ng_source_clr_data(sc); 512106321Sjulian NG_NODE_SET_PRIVATE(node, NULL); 513106321Sjulian NG_NODE_UNREF(node); 514125077Sharti free(sc, M_NETGRAPH); 515144674Sglebius 516106266Sjulian return (0); 517106266Sjulian} 518106266Sjulian 519106266Sjulian/* 520106266Sjulian * Hook disconnection 521106266Sjulian */ 522106266Sjulianstatic int 523106266Sjulianng_source_disconnect(hook_p hook) 524106266Sjulian{ 525106319Sjulian sc_p sc; 526106266Sjulian 527106321Sjulian sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 528125077Sharti KASSERT(sc != NULL, ("%s: null node private", __func__)); 529144674Sglebius if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output) 530106321Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); 531106266Sjulian return (0); 532106266Sjulian} 533106266Sjulian 534106266Sjulian/* 535106435Sjulian * Set sc->output_ifp to point to the the struct ifnet of the interface 536106435Sjulian * reached via our output hook. 537106435Sjulian */ 538106435Sjulianstatic int 539144674Sglebiusng_source_store_output_ifp(sc_p sc, char *ifname) 540106435Sjulian{ 541106435Sjulian struct ifnet *ifp; 542106435Sjulian int s; 543106266Sjulian 544144674Sglebius ifp = ifunit(ifname); 545106266Sjulian 546106266Sjulian if (ifp == NULL) { 547125077Sharti printf("%s: can't find interface %d\n", __func__, if_index); 548106266Sjulian return (EINVAL); 549106266Sjulian } 550106266Sjulian sc->output_ifp = ifp; 551106266Sjulian 552106266Sjulian#if 1 553106266Sjulian /* XXX mucking with a drivers ifqueue size is ugly but we need it 554106266Sjulian * to queue a lot of packets to get close to line rate on a gigabit 555106266Sjulian * interface with small packets. 556106266Sjulian * XXX we should restore the original value at stop or disconnect 557106266Sjulian */ 558106266Sjulian s = splimp(); /* XXX is this required? */ 559125077Sharti if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) { 560106266Sjulian printf("ng_source: changing ifq_maxlen from %d to %d\n", 561106319Sjulian ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 562106266Sjulian ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 563106266Sjulian } 564106266Sjulian splx(s); 565106266Sjulian#endif 566106266Sjulian return (0); 567106266Sjulian} 568106266Sjulian 569106266Sjulian/* 570106266Sjulian * Set the attached ethernet node's ethernet source address override flag. 571106266Sjulian */ 572106266Sjulianstatic int 573144674Sglebiusng_source_set_autosrc(sc_p sc, uint32_t flag) 574106266Sjulian{ 575106266Sjulian struct ng_mesg *msg; 576106266Sjulian int error = 0; 577106266Sjulian 578106266Sjulian NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 579144674Sglebius sizeof (uint32_t), M_NOWAIT); 580106266Sjulian if (msg == NULL) 581106266Sjulian return(ENOBUFS); 582106266Sjulian 583144674Sglebius *(uint32_t *)msg->data = flag; 584144674Sglebius NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0); 585106266Sjulian return (error); 586106266Sjulian} 587106266Sjulian 588106266Sjulian/* 589106266Sjulian * Clear out the data we've queued 590106266Sjulian */ 591106266Sjulianstatic void 592106266Sjulianng_source_clr_data (sc_p sc) 593106266Sjulian{ 594106266Sjulian struct mbuf *m; 595106266Sjulian 596106266Sjulian for (;;) { 597125031Sharti _IF_DEQUEUE(&sc->snd_queue, m); 598106266Sjulian if (m == NULL) 599106266Sjulian break; 600106266Sjulian NG_FREE_M(m); 601106266Sjulian } 602106266Sjulian sc->queueOctets = 0; 603106266Sjulian} 604106266Sjulian 605106266Sjulian/* 606106266Sjulian * Start sending queued data out the output hook 607106266Sjulian */ 608144674Sglebiusstatic int 609144674Sglebiusng_source_start(sc_p sc, uint64_t packets) 610106266Sjulian{ 611144674Sglebius if (sc->output_ifp == NULL) { 612144674Sglebius printf("ng_source: start without iface configured\n"); 613144674Sglebius return (ENXIO); 614144674Sglebius } 615144674Sglebius 616144674Sglebius if (sc->node->nd_flags & NG_SOURCE_ACTIVE) 617144674Sglebius return (EBUSY); 618144674Sglebius 619144674Sglebius sc->node->nd_flags |= NG_SOURCE_ACTIVE; 620144674Sglebius 621144674Sglebius sc->packets = packets; 622144674Sglebius timevalclear(&sc->stats.elapsedTime); 623144674Sglebius timevalclear(&sc->stats.endTime); 624144674Sglebius getmicrotime(&sc->stats.startTime); 625153690Sglebius getmicrotime(&sc->stats.lastTime); 626144674Sglebius ng_callout(&sc->intr_ch, sc->node, NULL, 0, 627144674Sglebius ng_source_intr, sc, 0); 628144674Sglebius 629144674Sglebius return (0); 630106266Sjulian} 631106266Sjulian 632106266Sjulian/* 633106266Sjulian * Stop sending queued data out the output hook 634106266Sjulian */ 635106266Sjulianstatic void 636144674Sglebiusng_source_stop(sc_p sc) 637106266Sjulian{ 638144674Sglebius ng_uncallout(&sc->intr_ch, sc->node); 639144674Sglebius sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 640144674Sglebius getmicrotime(&sc->stats.endTime); 641144674Sglebius sc->stats.elapsedTime = sc->stats.endTime; 642144674Sglebius timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 643106266Sjulian} 644106266Sjulian 645106266Sjulian/* 646106266Sjulian * While active called every NG_SOURCE_INTR_TICKS ticks. 647106266Sjulian * Sends as many packets as the interface connected to our 648106266Sjulian * output hook is able to enqueue. 649106266Sjulian */ 650106266Sjulianstatic void 651125243Sharting_source_intr(node_p node, hook_p hook, void *arg1, int arg2) 652106266Sjulian{ 653125243Sharti sc_p sc = (sc_p)arg1; 654106266Sjulian struct ifqueue *ifq; 655106266Sjulian int packets; 656106266Sjulian 657125077Sharti KASSERT(sc != NULL, ("%s: null node private", __func__)); 658106266Sjulian 659144674Sglebius if (sc->packets == 0 || sc->output == NULL 660106321Sjulian || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 661106266Sjulian ng_source_stop(sc); 662106266Sjulian return; 663106266Sjulian } 664106266Sjulian 665125033Sharti if (sc->output_ifp != NULL) { 666141745Sru ifq = (struct ifqueue *)&sc->output_ifp->if_snd; 667125033Sharti packets = ifq->ifq_maxlen - ifq->ifq_len; 668125033Sharti } else 669125033Sharti packets = sc->snd_queue.ifq_len; 670125033Sharti 671153690Sglebius if (sc->stats.maxPps != 0) { 672153690Sglebius struct timeval now, elapsed; 673153690Sglebius uint64_t usec; 674153690Sglebius int maxpkt; 675153690Sglebius 676153690Sglebius getmicrotime(&now); 677153690Sglebius elapsed = now; 678153690Sglebius timevalsub(&elapsed, &sc->stats.lastTime); 679153690Sglebius usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec; 680153690Sglebius maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000; 681153690Sglebius sc->stats.lastTime = now; 682153690Sglebius if (packets > maxpkt) 683153690Sglebius packets = maxpkt; 684153690Sglebius } 685153690Sglebius 686106266Sjulian ng_source_send(sc, packets, NULL); 687125243Sharti if (sc->packets == 0) 688106266Sjulian ng_source_stop(sc); 689125243Sharti else 690138268Sglebius ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS, 691137136Sglebius ng_source_intr, sc, 0); 692106266Sjulian} 693106266Sjulian 694106266Sjulian/* 695154707Sglebius * Send packets out our output hook. 696106266Sjulian */ 697106266Sjulianstatic int 698154707Sglebiusng_source_send(sc_p sc, int tosend, int *sent_p) 699106266Sjulian{ 700106266Sjulian struct mbuf *m, *m2; 701154707Sglebius int sent; 702106266Sjulian int error = 0; 703106266Sjulian 704125077Sharti KASSERT(tosend >= 0, ("%s: negative tosend param", __func__)); 705106321Sjulian KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 706125077Sharti ("%s: inactive node", __func__)); 707106266Sjulian 708144674Sglebius if ((uint64_t)tosend > sc->packets) 709106266Sjulian tosend = sc->packets; 710106266Sjulian 711154707Sglebius /* Go through the queue sending packets one by one. */ 712106266Sjulian for (sent = 0; error == 0 && sent < tosend; ++sent) { 713125031Sharti _IF_DEQUEUE(&sc->snd_queue, m); 714106266Sjulian if (m == NULL) 715106266Sjulian break; 716106266Sjulian 717167156Semaste /* Duplicate and modify the packet. */ 718167156Semaste error = ng_source_dup_mod(sc, m, &m2); 719167156Semaste if (error) { 720167156Semaste if (error == ENOBUFS) 721167156Semaste _IF_PREPEND(&sc->snd_queue, m); 722167156Semaste else 723167156Semaste _IF_ENQUEUE(&sc->snd_queue, m); 724106266Sjulian break; 725106266Sjulian } 726106266Sjulian 727144674Sglebius /* Re-enqueue the original packet for us. */ 728125031Sharti _IF_ENQUEUE(&sc->snd_queue, m); 729106266Sjulian 730154707Sglebius sc->stats.outFrames++; 731154707Sglebius sc->stats.outOctets += m2->m_pkthdr.len; 732154707Sglebius NG_SEND_DATA_ONLY(error, sc->output, m2); 733154707Sglebius if (error) 734106266Sjulian break; 735106266Sjulian } 736106266Sjulian 737106266Sjulian sc->packets -= sent; 738106266Sjulian if (sent_p != NULL) 739106266Sjulian *sent_p = sent; 740106266Sjulian return (error); 741106266Sjulian} 742167156Semaste 743167156Semaste/* 744167156Semaste * Modify packet in 'm' by changing 'len' bytes starting at 'offset' 745167156Semaste * to data in 'cp'. 746167156Semaste * 747167156Semaste * The packet data in 'm' must be in a contiguous buffer in a single mbuf. 748167156Semaste */ 749167156Semastestatic void 750167156Semasteng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp, 751167156Semaste int flags) 752167156Semaste{ 753167156Semaste if (len == 0) 754167156Semaste return; 755167156Semaste 756167156Semaste /* Can't modify beyond end of packet. */ 757167156Semaste /* TODO: Pad packet for this case. */ 758167156Semaste if (offset + len > m->m_len) 759167156Semaste return; 760167156Semaste 761167156Semaste bcopy(cp, mtod_off(m, offset, caddr_t), len); 762167156Semaste} 763167156Semaste 764167156Semastestatic int 765167156Semasteng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr) 766167156Semaste{ 767167156Semaste struct mbuf *m; 768167156Semaste struct ng_source_embed_info *ts; 769167156Semaste int modify; 770167156Semaste int error = 0; 771167156Semaste 772167156Semaste /* Are we going to modify packets? */ 773167156Semaste modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE; 774167156Semaste 775167156Semaste /* Duplicate the packet. */ 776167156Semaste if (modify) 777167156Semaste m = m_dup(m0, M_DONTWAIT); 778167156Semaste else 779167156Semaste m = m_copypacket(m0, M_DONTWAIT); 780167156Semaste if (m == NULL) { 781167156Semaste error = ENOBUFS; 782167156Semaste goto done; 783167156Semaste } 784167156Semaste *m_ptr = m; 785167156Semaste 786167156Semaste if (!modify) 787167156Semaste goto done; 788167156Semaste 789167156Semaste /* Modify the copied packet for sending. */ 790167156Semaste KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__)); 791167156Semaste 792167156Semaste ts = &sc->embed_timestamp; 793167156Semaste if (ts->flags & NGM_SOURCE_EMBED_ENABLE) { 794167156Semaste struct timeval now; 795167156Semaste getmicrotime(&now); 796167156Semaste now.tv_sec = htonl(now.tv_sec); 797167156Semaste now.tv_usec = htonl(now.tv_usec); 798167156Semaste ng_source_packet_mod(sc, m, ts->offset, sizeof (now), 799167156Semaste (caddr_t)&now, ts->flags); 800167156Semaste } 801167156Semaste 802167156Semastedone: 803167156Semaste return(error); 804167156Semaste} 805