1585ff168SJulian Elischer /* 2585ff168SJulian Elischer * ng_source.c 3585ff168SJulian Elischer * 4585ff168SJulian Elischer * Copyright 2002 Sandvine Inc. 5585ff168SJulian Elischer * All rights reserved. 6585ff168SJulian Elischer * 7585ff168SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 8585ff168SJulian Elischer * redistribution of this software, in source or object code forms, with or 9585ff168SJulian Elischer * without modifications are expressly permitted by Sandvine Inc.; 10585ff168SJulian Elischer provided, 11585ff168SJulian Elischer * however, that: 12585ff168SJulian Elischer * 1. Any and all reproductions of the source or object code must include 13585ff168SJulian Elischer the 14585ff168SJulian Elischer * copyright notice above and the following disclaimer of warranties; 15585ff168SJulian Elischer and 16585ff168SJulian Elischer * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 17585ff168SJulian Elischer * trademarks, including the mark "SANDVINE" on advertising, 18585ff168SJulian Elischer endorsements, 19585ff168SJulian Elischer * or otherwise except as such appears in the above copyright notice or 20585ff168SJulian Elischer in 21585ff168SJulian Elischer * the software. 22585ff168SJulian Elischer * 23585ff168SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 24585ff168SJulian Elischer * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR 25585ff168SJulian Elischer WARRANTIES, 26585ff168SJulian Elischer * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT 27585ff168SJulian Elischer LIMITATION, 28585ff168SJulian Elischer * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 29585ff168SJulian Elischer PARTICULAR 30585ff168SJulian Elischer * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 31585ff168SJulian Elischer * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 32585ff168SJulian Elischer * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 33585ff168SJulian Elischer * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 34585ff168SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 35585ff168SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 36585ff168SJulian Elischer EXEMPLARY, 37585ff168SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 38585ff168SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 39585ff168SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 40585ff168SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 41585ff168SJulian Elischer * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 42585ff168SJulian Elischer * DAMAGE. 43585ff168SJulian Elischer * 44585ff168SJulian Elischer * Author: Dave Chapeskie <dchapeskie@sandvine.com> 45585ff168SJulian Elischer * 46585ff168SJulian Elischer * $FreeBSD$ 47585ff168SJulian Elischer */ 48585ff168SJulian Elischer 49585ff168SJulian Elischer /* 50585ff168SJulian Elischer * This node is used for high speed packet geneneration. It queues 51585ff168SJulian Elischer * all data recieved on it's 'input' hook and when told to start via 52585ff168SJulian Elischer * a control message it sends the packets out it's 'output' hook. In 53585ff168SJulian Elischer * this way this node can be preloaded with a packet stream which is 54585ff168SJulian Elischer * continuously sent. 55585ff168SJulian Elischer * 56585ff168SJulian Elischer * Currently it just copies the mbufs as required. It could do various 57585ff168SJulian Elischer * tricks to try and avoid this. Probably the best performance would 58585ff168SJulian Elischer * be achieved by modifying the appropriate drivers to be told to 59585ff168SJulian Elischer * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 60585ff168SJulian Elischer * transmit descriptors) under control of this node; perhaps via some 61585ff168SJulian Elischer * flag in the mbuf or some such. The node would peak at an appropriate 62585ff168SJulian Elischer * ifnet flag to see if such support is available for the connected 63585ff168SJulian Elischer * interface. 64585ff168SJulian Elischer */ 65585ff168SJulian Elischer 66585ff168SJulian Elischer #include <sys/param.h> 67585ff168SJulian Elischer #include <sys/systm.h> 68585ff168SJulian Elischer #include <sys/errno.h> 69585ff168SJulian Elischer #include <sys/kernel.h> 70585ff168SJulian Elischer #include <sys/malloc.h> 71585ff168SJulian Elischer #include <sys/mbuf.h> 72585ff168SJulian Elischer #include <sys/socket.h> 73585ff168SJulian Elischer #include <net/if.h> 74585ff168SJulian Elischer #include <net/if_var.h> 75585ff168SJulian Elischer #include <netgraph/ng_message.h> 76585ff168SJulian Elischer #include <netgraph/netgraph.h> 77585ff168SJulian Elischer #include <netgraph/ng_parse.h> 78585ff168SJulian Elischer #include <netgraph/ng_ether.h> 79585ff168SJulian Elischer #include <netgraph/ng_source.h> 80585ff168SJulian Elischer 81585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS 1 82585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 83585ff168SJulian Elischer 84585ff168SJulian Elischer 85585ff168SJulian Elischer /* Per hook info */ 86585ff168SJulian Elischer struct source_hookinfo { 87585ff168SJulian Elischer hook_p hook; 88585ff168SJulian Elischer }; 89585ff168SJulian Elischer 90585ff168SJulian Elischer /* Per node info */ 91585ff168SJulian Elischer struct privdata { 92585ff168SJulian Elischer node_p node; 93585ff168SJulian Elischer struct source_hookinfo input; 94585ff168SJulian Elischer struct source_hookinfo output; 95585ff168SJulian Elischer struct ng_source_stats stats; 96585ff168SJulian Elischer struct ifqueue snd_queue; /* packets to send 97585ff168SJulian Elischer */ 98585ff168SJulian Elischer struct ifnet *output_ifp; 99585ff168SJulian Elischer struct callout_handle intr_ch; 100585ff168SJulian Elischer u_int64_t packets; /* packets to send 101585ff168SJulian Elischer */ 102585ff168SJulian Elischer u_int32_t queueOctets; 103585ff168SJulian Elischer }; 104585ff168SJulian Elischer typedef struct privdata *sc_p; 105585ff168SJulian Elischer 106585ff168SJulian Elischer /* Node flags */ 107585ff168SJulian Elischer #define NG_SOURCE_ACTIVE (NGF_TYPE1) 108585ff168SJulian Elischer 109585ff168SJulian Elischer /* XXX */ 110585ff168SJulian Elischer #if 1 111585ff168SJulian Elischer #undef KASSERT 112585ff168SJulian Elischer #define KASSERT(expr,msg) do { \ 113585ff168SJulian Elischer if (!(expr)) { \ 114585ff168SJulian Elischer printf msg ; \ 115585ff168SJulian Elischer panic("Assertion"); \ 116585ff168SJulian Elischer } \ 117585ff168SJulian Elischer } while(0) 118585ff168SJulian Elischer #endif 119585ff168SJulian Elischer 120585ff168SJulian Elischer /* Netgraph methods */ 121585ff168SJulian Elischer static ng_constructor_t ng_source_constructor; 122585ff168SJulian Elischer static ng_rcvmsg_t ng_source_rcvmsg; 123585ff168SJulian Elischer static ng_shutdown_t ng_source_rmnode; 124585ff168SJulian Elischer static ng_newhook_t ng_source_newhook; 125585ff168SJulian Elischer static ng_rcvdata_t ng_source_rcvdata; 126585ff168SJulian Elischer static ng_disconnect_t ng_source_disconnect; 127585ff168SJulian Elischer 128585ff168SJulian Elischer /* Other functions */ 129585ff168SJulian Elischer static timeout_t ng_source_intr; 130585ff168SJulian Elischer static int ng_source_get_output_ifp (sc_p); 131585ff168SJulian Elischer static void ng_source_clr_data (sc_p); 132585ff168SJulian Elischer static void ng_source_start (sc_p); 133585ff168SJulian Elischer static void ng_source_stop (sc_p); 134585ff168SJulian Elischer static int ng_source_send (sc_p, int, int *); 135585ff168SJulian Elischer 136585ff168SJulian Elischer 137585ff168SJulian Elischer /* Parse type for timeval */ 138585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = 139585ff168SJulian Elischer { 140585ff168SJulian Elischer { "tv_sec", &ng_parse_int32_type }, 141585ff168SJulian Elischer { "tv_usec", &ng_parse_int32_type }, 142585ff168SJulian Elischer { NULL } 143585ff168SJulian Elischer }; 144585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = { 145585ff168SJulian Elischer &ng_parse_struct_type, 146585ff168SJulian Elischer &ng_source_timeval_type_fields 147585ff168SJulian Elischer }; 148585ff168SJulian Elischer 149585ff168SJulian Elischer /* Parse type for struct ng_source_stats */ 150585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[] 151585ff168SJulian Elischer = NG_SOURCE_STATS_TYPE_INFO; 152585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = { 153585ff168SJulian Elischer &ng_parse_struct_type, 154585ff168SJulian Elischer &ng_source_stats_type_fields 155585ff168SJulian Elischer }; 156585ff168SJulian Elischer 157585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */ 158585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = { 159585ff168SJulian Elischer { 160585ff168SJulian Elischer NGM_SOURCE_COOKIE, 161585ff168SJulian Elischer NGM_SOURCE_GET_STATS, 162585ff168SJulian Elischer "getstats", 163585ff168SJulian Elischer NULL, 164585ff168SJulian Elischer &ng_source_stats_type 165585ff168SJulian Elischer }, 166585ff168SJulian Elischer { 167585ff168SJulian Elischer NGM_SOURCE_COOKIE, 168585ff168SJulian Elischer NGM_SOURCE_CLR_STATS, 169585ff168SJulian Elischer "clrstats", 170585ff168SJulian Elischer NULL, 171585ff168SJulian Elischer NULL 172585ff168SJulian Elischer }, 173585ff168SJulian Elischer { 174585ff168SJulian Elischer NGM_SOURCE_COOKIE, 175585ff168SJulian Elischer NGM_SOURCE_GETCLR_STATS, 176585ff168SJulian Elischer "getclrstats", 177585ff168SJulian Elischer NULL, 178585ff168SJulian Elischer &ng_source_stats_type 179585ff168SJulian Elischer }, 180585ff168SJulian Elischer { 181585ff168SJulian Elischer NGM_SOURCE_COOKIE, 182585ff168SJulian Elischer NGM_SOURCE_START, 183585ff168SJulian Elischer "start", 184585ff168SJulian Elischer &ng_parse_uint64_type, 185585ff168SJulian Elischer NULL 186585ff168SJulian Elischer }, 187585ff168SJulian Elischer { 188585ff168SJulian Elischer NGM_SOURCE_COOKIE, 189585ff168SJulian Elischer NGM_SOURCE_STOP, 190585ff168SJulian Elischer "stop", 191585ff168SJulian Elischer NULL, 192585ff168SJulian Elischer NULL 193585ff168SJulian Elischer }, 194585ff168SJulian Elischer { 195585ff168SJulian Elischer NGM_SOURCE_COOKIE, 196585ff168SJulian Elischer NGM_SOURCE_CLR_DATA, 197585ff168SJulian Elischer "clrdata", 198585ff168SJulian Elischer NULL, 199585ff168SJulian Elischer NULL 200585ff168SJulian Elischer }, 201585ff168SJulian Elischer { 0 } 202585ff168SJulian Elischer }; 203585ff168SJulian Elischer 204585ff168SJulian Elischer /* Netgraph type descriptor */ 205585ff168SJulian Elischer static struct ng_type ng_source_typestruct = { 206585ff168SJulian Elischer NG_VERSION, 207585ff168SJulian Elischer NG_SOURCE_NODE_TYPE, 208585ff168SJulian Elischer NULL, /* module event handler */ 209585ff168SJulian Elischer ng_source_constructor, 210585ff168SJulian Elischer ng_source_rcvmsg, 211585ff168SJulian Elischer ng_source_rmnode, 212585ff168SJulian Elischer ng_source_newhook, 213585ff168SJulian Elischer NULL, /* findhook */ 214585ff168SJulian Elischer NULL, 215585ff168SJulian Elischer ng_source_rcvdata, /* rcvdata */ 216585ff168SJulian Elischer ng_source_rcvdata, /* rcvdataq */ 217585ff168SJulian Elischer ng_source_disconnect, 218585ff168SJulian Elischer ng_source_cmds 219585ff168SJulian Elischer }; 220585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct); 221585ff168SJulian Elischer 222585ff168SJulian Elischer /* 223585ff168SJulian Elischer * Node constructor 224585ff168SJulian Elischer */ 225585ff168SJulian Elischer static int 226585ff168SJulian Elischer ng_source_constructor(node_p *nodep) 227585ff168SJulian Elischer { 228585ff168SJulian Elischer sc_p sc; 229585ff168SJulian Elischer int error = 0; 230585ff168SJulian Elischer 231585ff168SJulian Elischer MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT); 232585ff168SJulian Elischer if (sc == NULL) 233585ff168SJulian Elischer return (ENOMEM); 234585ff168SJulian Elischer bzero(sc, sizeof(*sc)); 235585ff168SJulian Elischer 236585ff168SJulian Elischer if ((error = ng_make_node_common(&ng_source_typestruct, nodep))) { 237585ff168SJulian Elischer FREE(sc, M_NETGRAPH); 238585ff168SJulian Elischer return (error); 239585ff168SJulian Elischer } 240585ff168SJulian Elischer (*nodep)->private = sc; 241585ff168SJulian Elischer sc->node = *nodep; 242585ff168SJulian Elischer sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 243585ff168SJulian Elischer callout_handle_init(&sc->intr_ch); 244585ff168SJulian Elischer return (0); 245585ff168SJulian Elischer } 246585ff168SJulian Elischer 247585ff168SJulian Elischer /* 248585ff168SJulian Elischer * Add a hook 249585ff168SJulian Elischer */ 250585ff168SJulian Elischer static int 251585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name) 252585ff168SJulian Elischer { 253585ff168SJulian Elischer const sc_p sc = node->private; 254585ff168SJulian Elischer 255585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 256585ff168SJulian Elischer if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 257585ff168SJulian Elischer sc->input.hook = hook; 258585ff168SJulian Elischer hook->private = &sc->input; 259585ff168SJulian Elischer } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 260585ff168SJulian Elischer sc->output.hook = hook; 261585ff168SJulian Elischer hook->private = &sc->output; 262585ff168SJulian Elischer sc->output_ifp = 0; 263585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 264585ff168SJulian Elischer } else 265585ff168SJulian Elischer return (EINVAL); 266585ff168SJulian Elischer return (0); 267585ff168SJulian Elischer } 268585ff168SJulian Elischer 269585ff168SJulian Elischer /* 270585ff168SJulian Elischer * Receive a control message 271585ff168SJulian Elischer */ 272585ff168SJulian Elischer static int 273585ff168SJulian Elischer ng_source_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, 274585ff168SJulian Elischer struct ng_mesg **rptr) 275585ff168SJulian Elischer { 276585ff168SJulian Elischer const sc_p sc = node->private; 277585ff168SJulian Elischer struct ng_mesg *resp = NULL; 278585ff168SJulian Elischer int error = 0; 279585ff168SJulian Elischer 280585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 281585ff168SJulian Elischer switch (msg->header.typecookie) { 282585ff168SJulian Elischer case NGM_SOURCE_COOKIE: 283585ff168SJulian Elischer switch (msg->header.cmd) { 284585ff168SJulian Elischer case NGM_SOURCE_GET_STATS: 285585ff168SJulian Elischer case NGM_SOURCE_CLR_STATS: 286585ff168SJulian Elischer case NGM_SOURCE_GETCLR_STATS: 287585ff168SJulian Elischer { 288585ff168SJulian Elischer struct ng_source_stats *stats; 289585ff168SJulian Elischer 290585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 291585ff168SJulian Elischer NG_MKRESPONSE(resp, msg, 292585ff168SJulian Elischer sizeof(*stats), M_NOWAIT); 293585ff168SJulian Elischer if (resp == NULL) { 294585ff168SJulian Elischer error = ENOMEM; 295585ff168SJulian Elischer goto done; 296585ff168SJulian Elischer } 297585ff168SJulian Elischer sc->stats.queueOctets = sc->queueOctets; 298585ff168SJulian Elischer sc->stats.queueFrames = 299585ff168SJulian Elischer sc->snd_queue.ifq_len; 300585ff168SJulian Elischer if ((sc->node->flags & NG_SOURCE_ACTIVE) 301585ff168SJulian Elischer && !timevalisset(&sc->stats.endTime)) { 302585ff168SJulian Elischer 303585ff168SJulian Elischer getmicrotime(&sc->stats.elapsedTime); 304585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, 305585ff168SJulian Elischer 306585ff168SJulian Elischer &sc->stats.startTime); 307585ff168SJulian Elischer } 308585ff168SJulian Elischer stats = (struct ng_source_stats 309585ff168SJulian Elischer *)resp->data; 310585ff168SJulian Elischer bcopy(&sc->stats, stats, sizeof(* stats)); 311585ff168SJulian Elischer } 312585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_GET_STATS) 313585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 314585ff168SJulian Elischer } 315585ff168SJulian Elischer break; 316585ff168SJulian Elischer case NGM_SOURCE_START: 317585ff168SJulian Elischer { 318585ff168SJulian Elischer u_int64_t packets = *(u_int64_t *)msg->data; 319585ff168SJulian Elischer if (sc->output.hook == NULL) { 320585ff168SJulian Elischer printf("%s: start on node with no output 321585ff168SJulian Elischer hook\n", __FUNCTION__); 322585ff168SJulian Elischer error = EINVAL; 323585ff168SJulian Elischer break; 324585ff168SJulian Elischer } 325585ff168SJulian Elischer /* TODO validation of packets */ 326585ff168SJulian Elischer sc->packets = packets; 327585ff168SJulian Elischer ng_source_start(sc); 328585ff168SJulian Elischer } 329585ff168SJulian Elischer break; 330585ff168SJulian Elischer case NGM_SOURCE_STOP: 331585ff168SJulian Elischer ng_source_stop(sc); 332585ff168SJulian Elischer break; 333585ff168SJulian Elischer case NGM_SOURCE_CLR_DATA: 334585ff168SJulian Elischer ng_source_clr_data(sc); 335585ff168SJulian Elischer break; 336585ff168SJulian Elischer default: 337585ff168SJulian Elischer error = EINVAL; 338585ff168SJulian Elischer break; 339585ff168SJulian Elischer } 340585ff168SJulian Elischer break; 341585ff168SJulian Elischer default: 342585ff168SJulian Elischer error = EINVAL; 343585ff168SJulian Elischer break; 344585ff168SJulian Elischer } 345585ff168SJulian Elischer if (rptr) 346585ff168SJulian Elischer *rptr = resp; 347585ff168SJulian Elischer else if (resp) 348585ff168SJulian Elischer FREE(resp, M_NETGRAPH); 349585ff168SJulian Elischer 350585ff168SJulian Elischer done: 351585ff168SJulian Elischer FREE(msg, M_NETGRAPH); 352585ff168SJulian Elischer return (error); 353585ff168SJulian Elischer } 354585ff168SJulian Elischer 355585ff168SJulian Elischer /* 356585ff168SJulian Elischer * Receive data on a hook 357585ff168SJulian Elischer * 358585ff168SJulian Elischer * If data comes in the input hook, enqueue it on the send queue. 359585ff168SJulian Elischer * If data comes in the output hook, discard it. 360585ff168SJulian Elischer */ 361585ff168SJulian Elischer static int 362585ff168SJulian Elischer ng_source_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 363585ff168SJulian Elischer { 364585ff168SJulian Elischer const sc_p sc = hook->node->private; 365585ff168SJulian Elischer struct source_hookinfo *const hinfo = (struct source_hookinfo *) 366585ff168SJulian Elischer hook->private; 367585ff168SJulian Elischer int error = 0; 368585ff168SJulian Elischer 369585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 370585ff168SJulian Elischer KASSERT(hinfo != NULL, ("%s: null hook info", __FUNCTION__)); 371585ff168SJulian Elischer 372585ff168SJulian Elischer /* Which hook? */ 373585ff168SJulian Elischer if (hinfo == &sc->output) { 374585ff168SJulian Elischer /* discard */ 375585ff168SJulian Elischer NG_FREE_DATA(m, meta); 376585ff168SJulian Elischer return (error); 377585ff168SJulian Elischer } 378585ff168SJulian Elischer KASSERT(hinfo == &sc->input, ("%s: no hook!", __FUNCTION__)); 379585ff168SJulian Elischer 380585ff168SJulian Elischer if ((m->m_flags & M_PKTHDR) == 0) { 381585ff168SJulian Elischer printf("%s: mbuf without PKTHDR\n", __FUNCTION__); 382585ff168SJulian Elischer NG_FREE_DATA(m, meta); 383585ff168SJulian Elischer return (EINVAL); 384585ff168SJulian Elischer } 385585ff168SJulian Elischer 386585ff168SJulian Elischer /* XXX we discard the meta data for now */ 387585ff168SJulian Elischer NG_FREE_META(meta); 388585ff168SJulian Elischer 389585ff168SJulian Elischer /* enque packet */ 390585ff168SJulian Elischer /* XXX should we check IF_QFULL() ? */ 391585ff168SJulian Elischer IF_ENQUEUE(&sc->snd_queue, m); 392585ff168SJulian Elischer sc->queueOctets += m->m_pkthdr.len; 393585ff168SJulian Elischer 394585ff168SJulian Elischer return (0); 395585ff168SJulian Elischer } 396585ff168SJulian Elischer 397585ff168SJulian Elischer /* 398585ff168SJulian Elischer * Shutdown processing 399585ff168SJulian Elischer */ 400585ff168SJulian Elischer static int 401585ff168SJulian Elischer ng_source_rmnode(node_p node) 402585ff168SJulian Elischer { 403585ff168SJulian Elischer const sc_p sc = node->private; 404585ff168SJulian Elischer 405585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 406585ff168SJulian Elischer node->flags |= NG_INVALID; 407585ff168SJulian Elischer ng_source_stop(sc); 408585ff168SJulian Elischer ng_cutlinks(node); 409585ff168SJulian Elischer ng_source_clr_data(sc); 410585ff168SJulian Elischer ng_unname(node); 411585ff168SJulian Elischer node->private = NULL; 412585ff168SJulian Elischer ng_unref(sc->node); 413585ff168SJulian Elischer FREE(sc, M_NETGRAPH); 414585ff168SJulian Elischer return (0); 415585ff168SJulian Elischer } 416585ff168SJulian Elischer 417585ff168SJulian Elischer /* 418585ff168SJulian Elischer * Hook disconnection 419585ff168SJulian Elischer */ 420585ff168SJulian Elischer static int 421585ff168SJulian Elischer ng_source_disconnect(hook_p hook) 422585ff168SJulian Elischer { 423585ff168SJulian Elischer struct source_hookinfo *const hinfo = (struct source_hookinfo *) 424585ff168SJulian Elischer hook->private; 425585ff168SJulian Elischer sc_p sc = (sc_p) hinfo->hook->node->private; 426585ff168SJulian Elischer 427585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 428585ff168SJulian Elischer hinfo->hook = NULL; 429585ff168SJulian Elischer if (hook->node->numhooks == 0 || hinfo == &sc->output) 430585ff168SJulian Elischer ng_rmnode(hook->node); 431585ff168SJulian Elischer return (0); 432585ff168SJulian Elischer } 433585ff168SJulian Elischer 434585ff168SJulian Elischer /* 435585ff168SJulian Elischer * Set sc->output_ifp to point to the the struct ifnet of the interface 436585ff168SJulian Elischer * reached via our output hook. 437585ff168SJulian Elischer */ 438585ff168SJulian Elischer static int 439585ff168SJulian Elischer ng_source_get_output_ifp(sc_p sc) 440585ff168SJulian Elischer { 441585ff168SJulian Elischer struct ng_mesg *msg, *rsp; 442585ff168SJulian Elischer struct ifnet *ifp; 443585ff168SJulian Elischer u_int32_t if_index; 444585ff168SJulian Elischer int error = 0; 445585ff168SJulian Elischer int s; 446585ff168SJulian Elischer 447585ff168SJulian Elischer sc->output_ifp = NULL; 448585ff168SJulian Elischer 449585ff168SJulian Elischer /* Ask the attached node for the connected interface's index */ 450585ff168SJulian Elischer NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, 451585ff168SJulian Elischer M_NOWAIT); 452585ff168SJulian Elischer if (msg == NULL) 453585ff168SJulian Elischer return (ENOBUFS); 454585ff168SJulian Elischer 455585ff168SJulian Elischer error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, &rsp); 456585ff168SJulian Elischer if (error != 0) 457585ff168SJulian Elischer return (error); 458585ff168SJulian Elischer 459585ff168SJulian Elischer if (rsp == NULL) 460585ff168SJulian Elischer return (EINVAL); 461585ff168SJulian Elischer 462585ff168SJulian Elischer if (rsp->header.arglen < sizeof(u_int32_t)) 463585ff168SJulian Elischer return (EINVAL); 464585ff168SJulian Elischer 465585ff168SJulian Elischer if_index = *(u_int32_t *)rsp->data; 466585ff168SJulian Elischer /* Could use ifindex2ifnet[if_index] except that we have no 467585ff168SJulian Elischer * way of verifying if_index is valid since if_indexlim is 468585ff168SJulian Elischer * local to if_attach() 469585ff168SJulian Elischer */ 470585ff168SJulian Elischer TAILQ_FOREACH(ifp, &ifnet, if_link) { 471585ff168SJulian Elischer if (ifp->if_index == if_index) 472585ff168SJulian Elischer break; 473585ff168SJulian Elischer } 474585ff168SJulian Elischer 475585ff168SJulian Elischer if (ifp == NULL) { 476585ff168SJulian Elischer printf("%s: can't find interface %d\n", __FUNCTION__, 477585ff168SJulian Elischer if_index); 478585ff168SJulian Elischer return (EINVAL); 479585ff168SJulian Elischer } 480585ff168SJulian Elischer sc->output_ifp = ifp; 481585ff168SJulian Elischer 482585ff168SJulian Elischer #if 1 483585ff168SJulian Elischer /* XXX mucking with a drivers ifqueue size is ugly but we need it 484585ff168SJulian Elischer * to queue a lot of packets to get close to line rate on a gigabit 485585ff168SJulian Elischer * interface with small packets. 486585ff168SJulian Elischer * XXX we should restore the original value at stop or disconnect 487585ff168SJulian Elischer */ 488585ff168SJulian Elischer s = splimp(); /* XXX is this required? */ 489585ff168SJulian Elischer if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) 490585ff168SJulian Elischer { 491585ff168SJulian Elischer printf("ng_source: changing ifq_maxlen from %d to %d\n", 492585ff168SJulian Elischer ifp->if_snd.ifq_maxlen, 493585ff168SJulian Elischer NG_SOURCE_DRIVER_IFQ_MAXLEN); 494585ff168SJulian Elischer ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 495585ff168SJulian Elischer } 496585ff168SJulian Elischer splx(s); 497585ff168SJulian Elischer #endif 498585ff168SJulian Elischer return (0); 499585ff168SJulian Elischer } 500585ff168SJulian Elischer 501585ff168SJulian Elischer /* 502585ff168SJulian Elischer * Set the attached ethernet node's ethernet source address override flag. 503585ff168SJulian Elischer */ 504585ff168SJulian Elischer static int 505585ff168SJulian Elischer ng_source_set_autosrc(sc_p sc, u_int32_t flag) 506585ff168SJulian Elischer { 507585ff168SJulian Elischer struct ng_mesg *msg; 508585ff168SJulian Elischer int error = 0; 509585ff168SJulian Elischer 510585ff168SJulian Elischer NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 511585ff168SJulian Elischer sizeof (u_int32_t), M_NOWAIT); 512585ff168SJulian Elischer if (msg == NULL) 513585ff168SJulian Elischer return(ENOBUFS); 514585ff168SJulian Elischer 515585ff168SJulian Elischer *(u_int32_t *)msg->data = flag; 516585ff168SJulian Elischer error = ng_send_msg(sc->node, msg, NG_SOURCE_HOOK_OUTPUT, NULL); 517585ff168SJulian Elischer return (error); 518585ff168SJulian Elischer } 519585ff168SJulian Elischer 520585ff168SJulian Elischer /* 521585ff168SJulian Elischer * Clear out the data we've queued 522585ff168SJulian Elischer */ 523585ff168SJulian Elischer static void 524585ff168SJulian Elischer ng_source_clr_data (sc_p sc) 525585ff168SJulian Elischer { 526585ff168SJulian Elischer struct mbuf *m; 527585ff168SJulian Elischer 528585ff168SJulian Elischer SPLASSERT(net, __FUNCTION__); 529585ff168SJulian Elischer for (;;) { 530585ff168SJulian Elischer IF_DEQUEUE(&sc->snd_queue, m); 531585ff168SJulian Elischer if (m == NULL) 532585ff168SJulian Elischer break; 533585ff168SJulian Elischer NG_FREE_M(m); 534585ff168SJulian Elischer } 535585ff168SJulian Elischer sc->queueOctets = 0; 536585ff168SJulian Elischer } 537585ff168SJulian Elischer 538585ff168SJulian Elischer /* 539585ff168SJulian Elischer * Start sending queued data out the output hook 540585ff168SJulian Elischer */ 541585ff168SJulian Elischer static void 542585ff168SJulian Elischer ng_source_start (sc_p sc) 543585ff168SJulian Elischer { 544585ff168SJulian Elischer SPLASSERT(net, __FUNCTION__); 545585ff168SJulian Elischer KASSERT(sc->output.hook != NULL, 546585ff168SJulian Elischer ("%s: output hook unconnected", __FUNCTION__)); 547585ff168SJulian Elischer if ((sc->node->flags & NG_SOURCE_ACTIVE) == 0) { 548585ff168SJulian Elischer if (sc->output_ifp == NULL && ng_source_get_output_ifp(sc) 549585ff168SJulian Elischer != 0) 550585ff168SJulian Elischer return; 551585ff168SJulian Elischer ng_source_set_autosrc(sc, 0); 552585ff168SJulian Elischer sc->node->flags |= NG_SOURCE_ACTIVE; 553585ff168SJulian Elischer timevalclear(&sc->stats.elapsedTime); 554585ff168SJulian Elischer timevalclear(&sc->stats.endTime); 555585ff168SJulian Elischer getmicrotime(&sc->stats.startTime); 556585ff168SJulian Elischer sc->intr_ch = timeout(ng_source_intr, sc, 0); 557585ff168SJulian Elischer } 558585ff168SJulian Elischer } 559585ff168SJulian Elischer 560585ff168SJulian Elischer /* 561585ff168SJulian Elischer * Stop sending queued data out the output hook 562585ff168SJulian Elischer */ 563585ff168SJulian Elischer static void 564585ff168SJulian Elischer ng_source_stop (sc_p sc) 565585ff168SJulian Elischer { 566585ff168SJulian Elischer SPLASSERT(net, __FUNCTION__); 567585ff168SJulian Elischer if (sc->node->flags & NG_SOURCE_ACTIVE) { 568585ff168SJulian Elischer untimeout(ng_source_intr, sc, sc->intr_ch); 569585ff168SJulian Elischer sc->node->flags &= ~NG_SOURCE_ACTIVE; 570585ff168SJulian Elischer getmicrotime(&sc->stats.endTime); 571585ff168SJulian Elischer sc->stats.elapsedTime = sc->stats.endTime; 572585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 573585ff168SJulian Elischer /* XXX should set this to the initial value instead */ 574585ff168SJulian Elischer ng_source_set_autosrc(sc, 1); 575585ff168SJulian Elischer } 576585ff168SJulian Elischer } 577585ff168SJulian Elischer 578585ff168SJulian Elischer /* 579585ff168SJulian Elischer * While active called every NG_SOURCE_INTR_TICKS ticks. 580585ff168SJulian Elischer * Sends as many packets as the interface connected to our 581585ff168SJulian Elischer * output hook is able to enqueue. 582585ff168SJulian Elischer */ 583585ff168SJulian Elischer static void 584585ff168SJulian Elischer ng_source_intr (void *arg) 585585ff168SJulian Elischer { 586585ff168SJulian Elischer const sc_p sc = (sc_p) arg; 587585ff168SJulian Elischer struct ifqueue *ifq; 588585ff168SJulian Elischer int packets; 589585ff168SJulian Elischer 590585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 591585ff168SJulian Elischer 592585ff168SJulian Elischer callout_handle_init(&sc->intr_ch); 593585ff168SJulian Elischer if (sc->packets == 0 || sc->output.hook == NULL 594585ff168SJulian Elischer || (sc->node->flags & NG_SOURCE_ACTIVE) == 0) { 595585ff168SJulian Elischer ng_source_stop(sc); 596585ff168SJulian Elischer return; 597585ff168SJulian Elischer } 598585ff168SJulian Elischer 599585ff168SJulian Elischer ifq = &sc->output_ifp->if_snd; 600585ff168SJulian Elischer packets = ifq->ifq_maxlen - ifq->ifq_len; 601585ff168SJulian Elischer ng_source_send(sc, packets, NULL); 602585ff168SJulian Elischer if (sc->packets == 0) { 603585ff168SJulian Elischer int s = splnet(); 604585ff168SJulian Elischer ng_source_stop(sc); 605585ff168SJulian Elischer splx(s); 606585ff168SJulian Elischer } else 607585ff168SJulian Elischer sc->intr_ch = timeout(ng_source_intr, sc, 608585ff168SJulian Elischer NG_SOURCE_INTR_TICKS); 609585ff168SJulian Elischer } 610585ff168SJulian Elischer 611585ff168SJulian Elischer /* 612585ff168SJulian Elischer * Send packets out our output hook 613585ff168SJulian Elischer */ 614585ff168SJulian Elischer static int 615585ff168SJulian Elischer ng_source_send (sc_p sc, int tosend, int *sent_p) 616585ff168SJulian Elischer { 617585ff168SJulian Elischer struct ifqueue tmp_queue; 618585ff168SJulian Elischer struct mbuf *m, *m2; 619585ff168SJulian Elischer int sent = 0; 620585ff168SJulian Elischer int error = 0; 621585ff168SJulian Elischer int s, s2; 622585ff168SJulian Elischer 623585ff168SJulian Elischer KASSERT(sc != NULL, ("%s: null node private", __FUNCTION__)); 624585ff168SJulian Elischer KASSERT(tosend >= 0, ("%s: negative tosend param", __FUNCTION__)); 625585ff168SJulian Elischer KASSERT(sc->node->flags & NG_SOURCE_ACTIVE, 626585ff168SJulian Elischer ("%s: inactive node", __FUNCTION__)); 627585ff168SJulian Elischer 628585ff168SJulian Elischer if ((u_int64_t)tosend > sc->packets) 629585ff168SJulian Elischer tosend = sc->packets; 630585ff168SJulian Elischer 631585ff168SJulian Elischer /* Copy the required number of packets to a temporary queue */ 632585ff168SJulian Elischer bzero (&tmp_queue, sizeof (tmp_queue)); 633585ff168SJulian Elischer for (sent = 0; error == 0 && sent < tosend; ++sent) { 634585ff168SJulian Elischer s = splnet(); 635585ff168SJulian Elischer IF_DEQUEUE(&sc->snd_queue, m); 636585ff168SJulian Elischer splx(s); 637585ff168SJulian Elischer if (m == NULL) 638585ff168SJulian Elischer break; 639585ff168SJulian Elischer 640585ff168SJulian Elischer /* duplicate the packet */ 641585ff168SJulian Elischer m2 = m_copypacket(m, M_NOWAIT); 642585ff168SJulian Elischer if (m2 == NULL) { 643585ff168SJulian Elischer s = splnet(); 644585ff168SJulian Elischer IF_PREPEND(&sc->snd_queue, m); 645585ff168SJulian Elischer splx(s); 646585ff168SJulian Elischer error = ENOBUFS; 647585ff168SJulian Elischer break; 648585ff168SJulian Elischer } 649585ff168SJulian Elischer 650585ff168SJulian Elischer /* re-enqueue the original packet for us */ 651585ff168SJulian Elischer s = splnet(); 652585ff168SJulian Elischer IF_ENQUEUE(&sc->snd_queue, m); 653585ff168SJulian Elischer splx(s); 654585ff168SJulian Elischer 655585ff168SJulian Elischer /* queue the copy for sending at smplimp */ 656585ff168SJulian Elischer IF_ENQUEUE(&tmp_queue, m2); 657585ff168SJulian Elischer } 658585ff168SJulian Elischer 659585ff168SJulian Elischer sent = 0; 660585ff168SJulian Elischer s = splimp(); 661585ff168SJulian Elischer for (;;) { 662585ff168SJulian Elischer IF_DEQUEUE(&tmp_queue, m2); 663585ff168SJulian Elischer if (m2 == NULL) 664585ff168SJulian Elischer break; 665585ff168SJulian Elischer if (error == 0) { 666585ff168SJulian Elischer ++sent; 667585ff168SJulian Elischer sc->stats.outFrames++; 668585ff168SJulian Elischer sc->stats.outOctets += m2->m_pkthdr.len; 669585ff168SJulian Elischer s2 = splnet(); 670585ff168SJulian Elischer NG_SEND_DATA_ONLY(error, sc->output.hook, m2); 671585ff168SJulian Elischer splx(s2); 672585ff168SJulian Elischer } else { 673585ff168SJulian Elischer NG_FREE_M(m2); 674585ff168SJulian Elischer } 675585ff168SJulian Elischer } 676585ff168SJulian Elischer splx(s); 677585ff168SJulian Elischer 678585ff168SJulian Elischer sc->packets -= sent; 679585ff168SJulian Elischer if (sent_p != NULL) 680585ff168SJulian Elischer *sent_p = sent; 681585ff168SJulian Elischer return (error); 682585ff168SJulian Elischer } 683