1585ff168SJulian Elischer /* 2585ff168SJulian Elischer * ng_source.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 6585ff168SJulian Elischer * Copyright 2002 Sandvine Inc. 7585ff168SJulian Elischer * All rights reserved. 8585ff168SJulian Elischer * 9585ff168SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 10585ff168SJulian Elischer * redistribution of this software, in source or object code forms, with or 114b52f283SJulian Elischer * without modifications are expressly permitted by Sandvine Inc.; provided, 12585ff168SJulian Elischer * however, that: 134b52f283SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 144b52f283SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 15585ff168SJulian Elischer * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 164b52f283SJulian Elischer * trademarks, including the mark "SANDVINE" on advertising, endorsements, 174b52f283SJulian Elischer * or otherwise except as such appears in the above copyright notice or in 18585ff168SJulian Elischer * the software. 19585ff168SJulian Elischer * 20585ff168SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 214b52f283SJulian Elischer * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 224b52f283SJulian Elischer * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 234b52f283SJulian Elischer * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 24585ff168SJulian Elischer * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 25585ff168SJulian Elischer * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 26585ff168SJulian Elischer * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 27585ff168SJulian Elischer * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 28585ff168SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 294b52f283SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30585ff168SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31585ff168SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32585ff168SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33585ff168SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34585ff168SJulian Elischer * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 35585ff168SJulian Elischer * DAMAGE. 36585ff168SJulian Elischer * 37585ff168SJulian Elischer * Author: Dave Chapeskie <dchapeskie@sandvine.com> 38585ff168SJulian Elischer */ 39585ff168SJulian Elischer 4076bd5857SHartmut Brandt #include <sys/cdefs.h> 4176bd5857SHartmut Brandt __FBSDID("$FreeBSD$"); 4276bd5857SHartmut Brandt 43585ff168SJulian Elischer /* 44585ff168SJulian Elischer * This node is used for high speed packet geneneration. It queues 45585ff168SJulian Elischer * all data recieved on it's 'input' hook and when told to start via 46585ff168SJulian Elischer * a control message it sends the packets out it's 'output' hook. In 47585ff168SJulian Elischer * this way this node can be preloaded with a packet stream which is 48585ff168SJulian Elischer * continuously sent. 49585ff168SJulian Elischer * 50585ff168SJulian Elischer * Currently it just copies the mbufs as required. It could do various 51585ff168SJulian Elischer * tricks to try and avoid this. Probably the best performance would 52585ff168SJulian Elischer * be achieved by modifying the appropriate drivers to be told to 53585ff168SJulian Elischer * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 54585ff168SJulian Elischer * transmit descriptors) under control of this node; perhaps via some 55585ff168SJulian Elischer * flag in the mbuf or some such. The node would peak at an appropriate 56585ff168SJulian Elischer * ifnet flag to see if such support is available for the connected 57585ff168SJulian Elischer * interface. 58585ff168SJulian Elischer */ 59585ff168SJulian Elischer 60585ff168SJulian Elischer #include <sys/param.h> 61585ff168SJulian Elischer #include <sys/systm.h> 62585ff168SJulian Elischer #include <sys/errno.h> 63585ff168SJulian Elischer #include <sys/kernel.h> 64585ff168SJulian Elischer #include <sys/malloc.h> 65585ff168SJulian Elischer #include <sys/mbuf.h> 66585ff168SJulian Elischer #include <sys/socket.h> 67585ff168SJulian Elischer #include <net/if.h> 68585ff168SJulian Elischer #include <net/if_var.h> 69585ff168SJulian Elischer #include <netgraph/ng_message.h> 70585ff168SJulian Elischer #include <netgraph/netgraph.h> 71585ff168SJulian Elischer #include <netgraph/ng_parse.h> 72585ff168SJulian Elischer #include <netgraph/ng_ether.h> 73585ff168SJulian Elischer #include <netgraph/ng_source.h> 74585ff168SJulian Elischer 75585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS 1 76585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 77585ff168SJulian Elischer 78585ff168SJulian Elischer 79585ff168SJulian Elischer /* Per hook info */ 80585ff168SJulian Elischer struct source_hookinfo { 81585ff168SJulian Elischer hook_p hook; 82585ff168SJulian Elischer }; 83585ff168SJulian Elischer 84585ff168SJulian Elischer /* Per node info */ 85585ff168SJulian Elischer struct privdata { 86585ff168SJulian Elischer node_p node; 87585ff168SJulian Elischer struct source_hookinfo input; 88585ff168SJulian Elischer struct source_hookinfo output; 89585ff168SJulian Elischer struct ng_source_stats stats; 904b52f283SJulian Elischer struct ifqueue snd_queue; /* packets to send */ 91585ff168SJulian Elischer struct ifnet *output_ifp; 9230bef41bSGleb Smirnoff struct callout intr_ch; 934b52f283SJulian Elischer u_int64_t packets; /* packets to send */ 94585ff168SJulian Elischer u_int32_t queueOctets; 95585ff168SJulian Elischer }; 96585ff168SJulian Elischer typedef struct privdata *sc_p; 97585ff168SJulian Elischer 98585ff168SJulian Elischer /* Node flags */ 99585ff168SJulian Elischer #define NG_SOURCE_ACTIVE (NGF_TYPE1) 100585ff168SJulian Elischer 101585ff168SJulian Elischer /* Netgraph methods */ 102585ff168SJulian Elischer static ng_constructor_t ng_source_constructor; 103585ff168SJulian Elischer static ng_rcvmsg_t ng_source_rcvmsg; 104585ff168SJulian Elischer static ng_shutdown_t ng_source_rmnode; 105585ff168SJulian Elischer static ng_newhook_t ng_source_newhook; 106585ff168SJulian Elischer static ng_rcvdata_t ng_source_rcvdata; 107585ff168SJulian Elischer static ng_disconnect_t ng_source_disconnect; 108585ff168SJulian Elischer 109585ff168SJulian Elischer /* Other functions */ 110a1adb510SHartmut Brandt static void ng_source_intr(node_p, hook_p, void *, int); 1114321c507SHartmut Brandt static int ng_source_request_output_ifp (sc_p); 112585ff168SJulian Elischer static void ng_source_clr_data (sc_p); 113585ff168SJulian Elischer static void ng_source_start (sc_p); 114585ff168SJulian Elischer static void ng_source_stop (sc_p); 115585ff168SJulian Elischer static int ng_source_send (sc_p, int, int *); 116b655e33dSJulian Elischer static int ng_source_store_output_ifp(sc_p sc, 117b655e33dSJulian Elischer struct ng_mesg *msg); 118585ff168SJulian Elischer 119585ff168SJulian Elischer 120585ff168SJulian Elischer /* Parse type for timeval */ 12176bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = { 122585ff168SJulian Elischer { "tv_sec", &ng_parse_int32_type }, 123585ff168SJulian Elischer { "tv_usec", &ng_parse_int32_type }, 124585ff168SJulian Elischer { NULL } 125585ff168SJulian Elischer }; 126585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = { 127585ff168SJulian Elischer &ng_parse_struct_type, 128585ff168SJulian Elischer &ng_source_timeval_type_fields 129585ff168SJulian Elischer }; 130585ff168SJulian Elischer 131585ff168SJulian Elischer /* Parse type for struct ng_source_stats */ 132585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[] 133585ff168SJulian Elischer = NG_SOURCE_STATS_TYPE_INFO; 134585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = { 135585ff168SJulian Elischer &ng_parse_struct_type, 136585ff168SJulian Elischer &ng_source_stats_type_fields 137585ff168SJulian Elischer }; 138585ff168SJulian Elischer 139585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */ 140585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = { 141585ff168SJulian Elischer { 142585ff168SJulian Elischer NGM_SOURCE_COOKIE, 143585ff168SJulian Elischer NGM_SOURCE_GET_STATS, 144585ff168SJulian Elischer "getstats", 145585ff168SJulian Elischer NULL, 146585ff168SJulian Elischer &ng_source_stats_type 147585ff168SJulian Elischer }, 148585ff168SJulian Elischer { 149585ff168SJulian Elischer NGM_SOURCE_COOKIE, 150585ff168SJulian Elischer NGM_SOURCE_CLR_STATS, 151585ff168SJulian Elischer "clrstats", 152585ff168SJulian Elischer NULL, 153585ff168SJulian Elischer NULL 154585ff168SJulian Elischer }, 155585ff168SJulian Elischer { 156585ff168SJulian Elischer NGM_SOURCE_COOKIE, 157585ff168SJulian Elischer NGM_SOURCE_GETCLR_STATS, 158585ff168SJulian Elischer "getclrstats", 159585ff168SJulian Elischer NULL, 160585ff168SJulian Elischer &ng_source_stats_type 161585ff168SJulian Elischer }, 162585ff168SJulian Elischer { 163585ff168SJulian Elischer NGM_SOURCE_COOKIE, 164585ff168SJulian Elischer NGM_SOURCE_START, 165585ff168SJulian Elischer "start", 166585ff168SJulian Elischer &ng_parse_uint64_type, 167585ff168SJulian Elischer NULL 168585ff168SJulian Elischer }, 169585ff168SJulian Elischer { 170585ff168SJulian Elischer NGM_SOURCE_COOKIE, 171585ff168SJulian Elischer NGM_SOURCE_STOP, 172585ff168SJulian Elischer "stop", 173585ff168SJulian Elischer NULL, 174585ff168SJulian Elischer NULL 175585ff168SJulian Elischer }, 176585ff168SJulian Elischer { 177585ff168SJulian Elischer NGM_SOURCE_COOKIE, 178585ff168SJulian Elischer NGM_SOURCE_CLR_DATA, 179585ff168SJulian Elischer "clrdata", 180585ff168SJulian Elischer NULL, 181585ff168SJulian Elischer NULL 182585ff168SJulian Elischer }, 183f5d15522SHartmut Brandt { 184f5d15522SHartmut Brandt NGM_SOURCE_COOKIE, 185f5d15522SHartmut Brandt NGM_SOURCE_START_NOW, 186f5d15522SHartmut Brandt "start_now", 187f5d15522SHartmut Brandt &ng_parse_uint64_type, 188f5d15522SHartmut Brandt NULL 189f5d15522SHartmut Brandt }, 190585ff168SJulian Elischer { 0 } 191585ff168SJulian Elischer }; 192585ff168SJulian Elischer 193585ff168SJulian Elischer /* Netgraph type descriptor */ 194585ff168SJulian Elischer static struct ng_type ng_source_typestruct = { 195f8aae777SJulian Elischer .version = NG_ABI_VERSION, 196f8aae777SJulian Elischer .name = NG_SOURCE_NODE_TYPE, 197f8aae777SJulian Elischer .constructor = ng_source_constructor, 198f8aae777SJulian Elischer .rcvmsg = ng_source_rcvmsg, 199f8aae777SJulian Elischer .shutdown = ng_source_rmnode, 200f8aae777SJulian Elischer .newhook = ng_source_newhook, 201f8aae777SJulian Elischer .rcvdata = ng_source_rcvdata, 202f8aae777SJulian Elischer .disconnect = ng_source_disconnect, 203f8aae777SJulian Elischer .cmdlist = ng_source_cmds, 204585ff168SJulian Elischer }; 205585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct); 206585ff168SJulian Elischer 2072cafef3eSHartmut Brandt static int ng_source_set_autosrc(sc_p, u_int32_t); 2082cafef3eSHartmut Brandt 209585ff168SJulian Elischer /* 210585ff168SJulian Elischer * Node constructor 211585ff168SJulian Elischer */ 212585ff168SJulian Elischer static int 2135968e29eSJulian Elischer ng_source_constructor(node_p node) 214585ff168SJulian Elischer { 215585ff168SJulian Elischer sc_p sc; 216585ff168SJulian Elischer 217b1b70498SHartmut Brandt sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 218585ff168SJulian Elischer if (sc == NULL) 219585ff168SJulian Elischer return (ENOMEM); 220585ff168SJulian Elischer 2215968e29eSJulian Elischer NG_NODE_SET_PRIVATE(node, sc); 2225968e29eSJulian Elischer sc->node = node; 223585ff168SJulian Elischer sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 22430bef41bSGleb Smirnoff ng_callout_init(&sc->intr_ch); 22530bef41bSGleb Smirnoff 226585ff168SJulian Elischer return (0); 227585ff168SJulian Elischer } 228585ff168SJulian Elischer 229585ff168SJulian Elischer /* 230585ff168SJulian Elischer * Add a hook 231585ff168SJulian Elischer */ 232585ff168SJulian Elischer static int 233585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name) 234585ff168SJulian Elischer { 2355968e29eSJulian Elischer sc_p sc; 236585ff168SJulian Elischer 2375968e29eSJulian Elischer sc = NG_NODE_PRIVATE(node); 23876bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 239585ff168SJulian Elischer if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 240585ff168SJulian Elischer sc->input.hook = hook; 2415968e29eSJulian Elischer NG_HOOK_SET_PRIVATE(hook, &sc->input); 242585ff168SJulian Elischer } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 243585ff168SJulian Elischer sc->output.hook = hook; 2445968e29eSJulian Elischer NG_HOOK_SET_PRIVATE(hook, &sc->output); 245585ff168SJulian Elischer sc->output_ifp = 0; 246585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 247585ff168SJulian Elischer } else 248585ff168SJulian Elischer return (EINVAL); 249585ff168SJulian Elischer return (0); 250585ff168SJulian Elischer } 251585ff168SJulian Elischer 252585ff168SJulian Elischer /* 253585ff168SJulian Elischer * Receive a control message 254585ff168SJulian Elischer */ 255585ff168SJulian Elischer static int 2565968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 257585ff168SJulian Elischer { 2585968e29eSJulian Elischer sc_p sc; 259585ff168SJulian Elischer struct ng_mesg *resp = NULL; 260585ff168SJulian Elischer int error = 0; 2615968e29eSJulian Elischer struct ng_mesg *msg; 262585ff168SJulian Elischer 2635968e29eSJulian Elischer sc = NG_NODE_PRIVATE(node); 2645968e29eSJulian Elischer NGI_GET_MSG(item, msg); 26576bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 266585ff168SJulian Elischer switch (msg->header.typecookie) { 267585ff168SJulian Elischer case NGM_SOURCE_COOKIE: 268b655e33dSJulian Elischer if (msg->header.flags & NGF_RESP) { 269b655e33dSJulian Elischer error = EINVAL; 270b655e33dSJulian Elischer break; 271b655e33dSJulian Elischer } 272585ff168SJulian Elischer switch (msg->header.cmd) { 273585ff168SJulian Elischer case NGM_SOURCE_GET_STATS: 274585ff168SJulian Elischer case NGM_SOURCE_CLR_STATS: 275585ff168SJulian Elischer case NGM_SOURCE_GETCLR_STATS: 276585ff168SJulian Elischer { 277585ff168SJulian Elischer struct ng_source_stats *stats; 278585ff168SJulian Elischer 279585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 280585ff168SJulian Elischer NG_MKRESPONSE(resp, msg, 281585ff168SJulian Elischer sizeof(*stats), M_NOWAIT); 282585ff168SJulian Elischer if (resp == NULL) { 283585ff168SJulian Elischer error = ENOMEM; 284585ff168SJulian Elischer goto done; 285585ff168SJulian Elischer } 286585ff168SJulian Elischer sc->stats.queueOctets = sc->queueOctets; 2874b52f283SJulian Elischer sc->stats.queueFrames = sc->snd_queue.ifq_len; 2885968e29eSJulian Elischer if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 289585ff168SJulian Elischer && !timevalisset(&sc->stats.endTime)) { 290585ff168SJulian Elischer getmicrotime(&sc->stats.elapsedTime); 291585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, 292585ff168SJulian Elischer &sc->stats.startTime); 293585ff168SJulian Elischer } 2944b52f283SJulian Elischer stats = (struct ng_source_stats *)resp->data; 295585ff168SJulian Elischer bcopy(&sc->stats, stats, sizeof(* stats)); 296585ff168SJulian Elischer } 297585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_GET_STATS) 298585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 299585ff168SJulian Elischer } 300585ff168SJulian Elischer break; 301585ff168SJulian Elischer case NGM_SOURCE_START: 302585ff168SJulian Elischer { 303585ff168SJulian Elischer u_int64_t packets = *(u_int64_t *)msg->data; 304585ff168SJulian Elischer if (sc->output.hook == NULL) { 3054b52f283SJulian Elischer printf("%s: start on node with no output hook\n" 30676bd5857SHartmut Brandt , __func__); 307585ff168SJulian Elischer error = EINVAL; 308585ff168SJulian Elischer break; 309585ff168SJulian Elischer } 310585ff168SJulian Elischer /* TODO validation of packets */ 311585ff168SJulian Elischer sc->packets = packets; 312585ff168SJulian Elischer ng_source_start(sc); 313585ff168SJulian Elischer } 314585ff168SJulian Elischer break; 315f5d15522SHartmut Brandt case NGM_SOURCE_START_NOW: 316f5d15522SHartmut Brandt { 317f5d15522SHartmut Brandt u_int64_t packets = *(u_int64_t *)msg->data; 318f5d15522SHartmut Brandt if (sc->output.hook == NULL) { 319f5d15522SHartmut Brandt printf("%s: start on node with no output hook\n" 32076bd5857SHartmut Brandt , __func__); 321f5d15522SHartmut Brandt error = EINVAL; 322f5d15522SHartmut Brandt break; 323f5d15522SHartmut Brandt } 324f5d15522SHartmut Brandt if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 325f5d15522SHartmut Brandt error = EBUSY; 326f5d15522SHartmut Brandt break; 327f5d15522SHartmut Brandt } 328f5d15522SHartmut Brandt /* TODO validation of packets */ 329f5d15522SHartmut Brandt sc->packets = packets; 330f5d15522SHartmut Brandt sc->output_ifp = NULL; 331f5d15522SHartmut Brandt 332f5d15522SHartmut Brandt sc->node->nd_flags |= NG_SOURCE_ACTIVE; 333f5d15522SHartmut Brandt timevalclear(&sc->stats.elapsedTime); 334f5d15522SHartmut Brandt timevalclear(&sc->stats.endTime); 335f5d15522SHartmut Brandt getmicrotime(&sc->stats.startTime); 336f9d9e1b4SGleb Smirnoff ng_callout(&sc->intr_ch, node, NULL, 0, 337a1adb510SHartmut Brandt ng_source_intr, sc, 0); 338f5d15522SHartmut Brandt } 339f5d15522SHartmut Brandt break; 340585ff168SJulian Elischer case NGM_SOURCE_STOP: 341585ff168SJulian Elischer ng_source_stop(sc); 342585ff168SJulian Elischer break; 343585ff168SJulian Elischer case NGM_SOURCE_CLR_DATA: 344585ff168SJulian Elischer ng_source_clr_data(sc); 345585ff168SJulian Elischer break; 346585ff168SJulian Elischer default: 347585ff168SJulian Elischer error = EINVAL; 348585ff168SJulian Elischer break; 349585ff168SJulian Elischer } 350585ff168SJulian Elischer break; 351b655e33dSJulian Elischer case NGM_ETHER_COOKIE: 352b655e33dSJulian Elischer if (!(msg->header.flags & NGF_RESP)) { 353b655e33dSJulian Elischer error = EINVAL; 354b655e33dSJulian Elischer break; 355b655e33dSJulian Elischer } 356b655e33dSJulian Elischer switch (msg->header.cmd) { 357b655e33dSJulian Elischer case NGM_ETHER_GET_IFINDEX: 358b655e33dSJulian Elischer if (ng_source_store_output_ifp(sc, msg) == 0) { 359b655e33dSJulian Elischer ng_source_set_autosrc(sc, 0); 360b655e33dSJulian Elischer sc->node->nd_flags |= NG_SOURCE_ACTIVE; 361b655e33dSJulian Elischer timevalclear(&sc->stats.elapsedTime); 362b655e33dSJulian Elischer timevalclear(&sc->stats.endTime); 363b655e33dSJulian Elischer getmicrotime(&sc->stats.startTime); 364f9d9e1b4SGleb Smirnoff ng_callout(&sc->intr_ch, node, NULL, 0, 365a1adb510SHartmut Brandt ng_source_intr, sc, 0); 366b655e33dSJulian Elischer } 367b655e33dSJulian Elischer break; 368b655e33dSJulian Elischer default: 369b655e33dSJulian Elischer error = EINVAL; 370b655e33dSJulian Elischer } 371b655e33dSJulian Elischer break; 372585ff168SJulian Elischer default: 373585ff168SJulian Elischer error = EINVAL; 374585ff168SJulian Elischer break; 375585ff168SJulian Elischer } 376585ff168SJulian Elischer 377585ff168SJulian Elischer done: 3785968e29eSJulian Elischer /* Take care of synchronous response, if any */ 3795968e29eSJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 3805968e29eSJulian Elischer /* Free the message and return */ 3815968e29eSJulian Elischer NG_FREE_MSG(msg); 382585ff168SJulian Elischer return (error); 383585ff168SJulian Elischer } 384585ff168SJulian Elischer 385585ff168SJulian Elischer /* 386585ff168SJulian Elischer * Receive data on a hook 387585ff168SJulian Elischer * 388585ff168SJulian Elischer * If data comes in the input hook, enqueue it on the send queue. 389585ff168SJulian Elischer * If data comes in the output hook, discard it. 390585ff168SJulian Elischer */ 391585ff168SJulian Elischer static int 3925968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item) 393585ff168SJulian Elischer { 3945968e29eSJulian Elischer sc_p sc; 3955968e29eSJulian Elischer struct source_hookinfo *hinfo; 396585ff168SJulian Elischer int error = 0; 3975968e29eSJulian Elischer struct mbuf *m; 398585ff168SJulian Elischer 3995968e29eSJulian Elischer sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 4005968e29eSJulian Elischer NGI_GET_M(item, m); 4015968e29eSJulian Elischer NG_FREE_ITEM(item); 4025968e29eSJulian Elischer hinfo = NG_HOOK_PRIVATE(hook); 40376bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 40476bd5857SHartmut Brandt KASSERT(hinfo != NULL, ("%s: null hook info", __func__)); 405585ff168SJulian Elischer 406585ff168SJulian Elischer /* Which hook? */ 407585ff168SJulian Elischer if (hinfo == &sc->output) { 408585ff168SJulian Elischer /* discard */ 4095968e29eSJulian Elischer NG_FREE_M(m); 410585ff168SJulian Elischer return (error); 411585ff168SJulian Elischer } 41276bd5857SHartmut Brandt KASSERT(hinfo == &sc->input, ("%s: no hook!", __func__)); 413585ff168SJulian Elischer 414585ff168SJulian Elischer if ((m->m_flags & M_PKTHDR) == 0) { 41576bd5857SHartmut Brandt printf("%s: mbuf without PKTHDR\n", __func__); 4165968e29eSJulian Elischer NG_FREE_M(m); 417585ff168SJulian Elischer return (EINVAL); 418585ff168SJulian Elischer } 419585ff168SJulian Elischer 420585ff168SJulian Elischer /* enque packet */ 421585ff168SJulian Elischer /* XXX should we check IF_QFULL() ? */ 42281a4ef81SHartmut Brandt _IF_ENQUEUE(&sc->snd_queue, m); 423585ff168SJulian Elischer sc->queueOctets += m->m_pkthdr.len; 424585ff168SJulian Elischer 425585ff168SJulian Elischer return (0); 426585ff168SJulian Elischer } 427585ff168SJulian Elischer 428585ff168SJulian Elischer /* 429585ff168SJulian Elischer * Shutdown processing 430585ff168SJulian Elischer */ 431585ff168SJulian Elischer static int 432585ff168SJulian Elischer ng_source_rmnode(node_p node) 433585ff168SJulian Elischer { 4345968e29eSJulian Elischer sc_p sc; 435585ff168SJulian Elischer 4365968e29eSJulian Elischer sc = NG_NODE_PRIVATE(node); 43776bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 438585ff168SJulian Elischer ng_source_stop(sc); 439585ff168SJulian Elischer ng_source_clr_data(sc); 4405968e29eSJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 4415968e29eSJulian Elischer NG_NODE_UNREF(node); 44276bd5857SHartmut Brandt free(sc, M_NETGRAPH); 443585ff168SJulian Elischer return (0); 444585ff168SJulian Elischer } 445585ff168SJulian Elischer 446585ff168SJulian Elischer /* 447585ff168SJulian Elischer * Hook disconnection 448585ff168SJulian Elischer */ 449585ff168SJulian Elischer static int 450585ff168SJulian Elischer ng_source_disconnect(hook_p hook) 451585ff168SJulian Elischer { 4525968e29eSJulian Elischer struct source_hookinfo *hinfo; 4534b52f283SJulian Elischer sc_p sc; 454585ff168SJulian Elischer 4555968e29eSJulian Elischer hinfo = NG_HOOK_PRIVATE(hook); 4565968e29eSJulian Elischer sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 45776bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 458585ff168SJulian Elischer hinfo->hook = NULL; 4595968e29eSJulian Elischer if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output) 4605968e29eSJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); 461585ff168SJulian Elischer return (0); 462585ff168SJulian Elischer } 463585ff168SJulian Elischer 464585ff168SJulian Elischer /* 465b655e33dSJulian Elischer * 466b655e33dSJulian Elischer * Ask out neighbour on the output hook side to send us it's interface 467b655e33dSJulian Elischer * information. 468585ff168SJulian Elischer */ 4694321c507SHartmut Brandt static int 470b655e33dSJulian Elischer ng_source_request_output_ifp(sc_p sc) 471585ff168SJulian Elischer { 472b655e33dSJulian Elischer struct ng_mesg *msg; 473585ff168SJulian Elischer int error = 0; 474585ff168SJulian Elischer 475585ff168SJulian Elischer sc->output_ifp = NULL; 476585ff168SJulian Elischer 477585ff168SJulian Elischer /* Ask the attached node for the connected interface's index */ 4784b52f283SJulian Elischer NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT); 479585ff168SJulian Elischer if (msg == NULL) 480585ff168SJulian Elischer return (ENOBUFS); 481585ff168SJulian Elischer 4824e0dea9aSHartmut Brandt NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, 0); 483585ff168SJulian Elischer return (error); 484b655e33dSJulian Elischer } 485585ff168SJulian Elischer 486b655e33dSJulian Elischer /* 487b655e33dSJulian Elischer * Set sc->output_ifp to point to the the struct ifnet of the interface 488b655e33dSJulian Elischer * reached via our output hook. 489b655e33dSJulian Elischer */ 490b655e33dSJulian Elischer static int 491b655e33dSJulian Elischer ng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg) 492b655e33dSJulian Elischer { 493b655e33dSJulian Elischer struct ifnet *ifp; 494b655e33dSJulian Elischer u_int32_t if_index; 495b655e33dSJulian Elischer int s; 496b655e33dSJulian Elischer 497b655e33dSJulian Elischer if (msg->header.arglen < sizeof(u_int32_t)) 498585ff168SJulian Elischer return (EINVAL); 499585ff168SJulian Elischer 500b655e33dSJulian Elischer if_index = *(u_int32_t *)msg->data; 501585ff168SJulian Elischer /* Could use ifindex2ifnet[if_index] except that we have no 502585ff168SJulian Elischer * way of verifying if_index is valid since if_indexlim is 503585ff168SJulian Elischer * local to if_attach() 504585ff168SJulian Elischer */ 505b30a244cSJeffrey Hsu IFNET_RLOCK(); 506585ff168SJulian Elischer TAILQ_FOREACH(ifp, &ifnet, if_link) { 507585ff168SJulian Elischer if (ifp->if_index == if_index) 508585ff168SJulian Elischer break; 509585ff168SJulian Elischer } 510b30a244cSJeffrey Hsu IFNET_RUNLOCK(); 511585ff168SJulian Elischer 512585ff168SJulian Elischer if (ifp == NULL) { 51376bd5857SHartmut Brandt printf("%s: can't find interface %d\n", __func__, if_index); 514585ff168SJulian Elischer return (EINVAL); 515585ff168SJulian Elischer } 516585ff168SJulian Elischer sc->output_ifp = ifp; 517585ff168SJulian Elischer 518585ff168SJulian Elischer #if 1 519585ff168SJulian Elischer /* XXX mucking with a drivers ifqueue size is ugly but we need it 520585ff168SJulian Elischer * to queue a lot of packets to get close to line rate on a gigabit 521585ff168SJulian Elischer * interface with small packets. 522585ff168SJulian Elischer * XXX we should restore the original value at stop or disconnect 523585ff168SJulian Elischer */ 524585ff168SJulian Elischer s = splimp(); /* XXX is this required? */ 52576bd5857SHartmut Brandt if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) { 526585ff168SJulian Elischer printf("ng_source: changing ifq_maxlen from %d to %d\n", 5274b52f283SJulian Elischer ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 528585ff168SJulian Elischer ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 529585ff168SJulian Elischer } 530585ff168SJulian Elischer splx(s); 531585ff168SJulian Elischer #endif 532585ff168SJulian Elischer return (0); 533585ff168SJulian Elischer } 534585ff168SJulian Elischer 535585ff168SJulian Elischer /* 536585ff168SJulian Elischer * Set the attached ethernet node's ethernet source address override flag. 537585ff168SJulian Elischer */ 538585ff168SJulian Elischer static int 539585ff168SJulian Elischer ng_source_set_autosrc(sc_p sc, u_int32_t flag) 540585ff168SJulian Elischer { 541585ff168SJulian Elischer struct ng_mesg *msg; 542585ff168SJulian Elischer int error = 0; 543585ff168SJulian Elischer 544585ff168SJulian Elischer NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 545585ff168SJulian Elischer sizeof (u_int32_t), M_NOWAIT); 546585ff168SJulian Elischer if (msg == NULL) 547585ff168SJulian Elischer return(ENOBUFS); 548585ff168SJulian Elischer 549585ff168SJulian Elischer *(u_int32_t *)msg->data = flag; 5504e0dea9aSHartmut Brandt NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, 0); 551585ff168SJulian Elischer return (error); 552585ff168SJulian Elischer } 553585ff168SJulian Elischer 554585ff168SJulian Elischer /* 555585ff168SJulian Elischer * Clear out the data we've queued 556585ff168SJulian Elischer */ 557585ff168SJulian Elischer static void 558585ff168SJulian Elischer ng_source_clr_data (sc_p sc) 559585ff168SJulian Elischer { 560585ff168SJulian Elischer struct mbuf *m; 561585ff168SJulian Elischer 562585ff168SJulian Elischer for (;;) { 56381a4ef81SHartmut Brandt _IF_DEQUEUE(&sc->snd_queue, m); 564585ff168SJulian Elischer if (m == NULL) 565585ff168SJulian Elischer break; 566585ff168SJulian Elischer NG_FREE_M(m); 567585ff168SJulian Elischer } 568585ff168SJulian Elischer sc->queueOctets = 0; 569585ff168SJulian Elischer } 570585ff168SJulian Elischer 571585ff168SJulian Elischer /* 572585ff168SJulian Elischer * Start sending queued data out the output hook 573585ff168SJulian Elischer */ 574585ff168SJulian Elischer static void 575585ff168SJulian Elischer ng_source_start (sc_p sc) 576585ff168SJulian Elischer { 577585ff168SJulian Elischer KASSERT(sc->output.hook != NULL, 57876bd5857SHartmut Brandt ("%s: output hook unconnected", __func__)); 579b655e33dSJulian Elischer if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) && 580b655e33dSJulian Elischer (sc->output_ifp == NULL)) 581b655e33dSJulian Elischer ng_source_request_output_ifp(sc); 582585ff168SJulian Elischer } 583585ff168SJulian Elischer 584585ff168SJulian Elischer /* 585585ff168SJulian Elischer * Stop sending queued data out the output hook 586585ff168SJulian Elischer */ 587585ff168SJulian Elischer static void 588585ff168SJulian Elischer ng_source_stop (sc_p sc) 589585ff168SJulian Elischer { 5905968e29eSJulian Elischer if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 591f9d9e1b4SGleb Smirnoff ng_uncallout(&sc->intr_ch, sc->node); 5925968e29eSJulian Elischer sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 593585ff168SJulian Elischer getmicrotime(&sc->stats.endTime); 594585ff168SJulian Elischer sc->stats.elapsedTime = sc->stats.endTime; 595585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 596585ff168SJulian Elischer /* XXX should set this to the initial value instead */ 597585ff168SJulian Elischer ng_source_set_autosrc(sc, 1); 598585ff168SJulian Elischer } 599585ff168SJulian Elischer } 600585ff168SJulian Elischer 601585ff168SJulian Elischer /* 602585ff168SJulian Elischer * While active called every NG_SOURCE_INTR_TICKS ticks. 603585ff168SJulian Elischer * Sends as many packets as the interface connected to our 604585ff168SJulian Elischer * output hook is able to enqueue. 605585ff168SJulian Elischer */ 606585ff168SJulian Elischer static void 607a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2) 608585ff168SJulian Elischer { 609a1adb510SHartmut Brandt sc_p sc = (sc_p)arg1; 610585ff168SJulian Elischer struct ifqueue *ifq; 611585ff168SJulian Elischer int packets; 612585ff168SJulian Elischer 61376bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 614585ff168SJulian Elischer 615585ff168SJulian Elischer if (sc->packets == 0 || sc->output.hook == NULL 6165968e29eSJulian Elischer || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 617585ff168SJulian Elischer ng_source_stop(sc); 618585ff168SJulian Elischer return; 619585ff168SJulian Elischer } 620585ff168SJulian Elischer 621f5d15522SHartmut Brandt if (sc->output_ifp != NULL) { 622585ff168SJulian Elischer ifq = &sc->output_ifp->if_snd; 623585ff168SJulian Elischer packets = ifq->ifq_maxlen - ifq->ifq_len; 624f5d15522SHartmut Brandt } else 625f5d15522SHartmut Brandt packets = sc->snd_queue.ifq_len; 626f5d15522SHartmut Brandt 627585ff168SJulian Elischer ng_source_send(sc, packets, NULL); 628a1adb510SHartmut Brandt if (sc->packets == 0) 629585ff168SJulian Elischer ng_source_stop(sc); 630a1adb510SHartmut Brandt else 631f9d9e1b4SGleb Smirnoff ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS, 632d312eaf5SGleb Smirnoff ng_source_intr, sc, 0); 633585ff168SJulian Elischer } 634585ff168SJulian Elischer 635585ff168SJulian Elischer /* 636585ff168SJulian Elischer * Send packets out our output hook 637585ff168SJulian Elischer */ 638585ff168SJulian Elischer static int 639585ff168SJulian Elischer ng_source_send (sc_p sc, int tosend, int *sent_p) 640585ff168SJulian Elischer { 641585ff168SJulian Elischer struct ifqueue tmp_queue; 642585ff168SJulian Elischer struct mbuf *m, *m2; 643585ff168SJulian Elischer int sent = 0; 644585ff168SJulian Elischer int error = 0; 645585ff168SJulian Elischer 64676bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 64776bd5857SHartmut Brandt KASSERT(tosend >= 0, ("%s: negative tosend param", __func__)); 6485968e29eSJulian Elischer KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 64976bd5857SHartmut Brandt ("%s: inactive node", __func__)); 650585ff168SJulian Elischer 651585ff168SJulian Elischer if ((u_int64_t)tosend > sc->packets) 652585ff168SJulian Elischer tosend = sc->packets; 653585ff168SJulian Elischer 654585ff168SJulian Elischer /* Copy the required number of packets to a temporary queue */ 655585ff168SJulian Elischer bzero (&tmp_queue, sizeof (tmp_queue)); 656585ff168SJulian Elischer for (sent = 0; error == 0 && sent < tosend; ++sent) { 65781a4ef81SHartmut Brandt _IF_DEQUEUE(&sc->snd_queue, m); 658585ff168SJulian Elischer if (m == NULL) 659585ff168SJulian Elischer break; 660585ff168SJulian Elischer 661585ff168SJulian Elischer /* duplicate the packet */ 662a163d034SWarner Losh m2 = m_copypacket(m, M_DONTWAIT); 663585ff168SJulian Elischer if (m2 == NULL) { 66481a4ef81SHartmut Brandt _IF_PREPEND(&sc->snd_queue, m); 665585ff168SJulian Elischer error = ENOBUFS; 666585ff168SJulian Elischer break; 667585ff168SJulian Elischer } 668585ff168SJulian Elischer 669585ff168SJulian Elischer /* re-enqueue the original packet for us */ 67081a4ef81SHartmut Brandt _IF_ENQUEUE(&sc->snd_queue, m); 671585ff168SJulian Elischer 672585ff168SJulian Elischer /* queue the copy for sending at smplimp */ 67381a4ef81SHartmut Brandt _IF_ENQUEUE(&tmp_queue, m2); 674585ff168SJulian Elischer } 675585ff168SJulian Elischer 676585ff168SJulian Elischer sent = 0; 677585ff168SJulian Elischer for (;;) { 67881a4ef81SHartmut Brandt _IF_DEQUEUE(&tmp_queue, m2); 679585ff168SJulian Elischer if (m2 == NULL) 680585ff168SJulian Elischer break; 681585ff168SJulian Elischer if (error == 0) { 682585ff168SJulian Elischer ++sent; 683585ff168SJulian Elischer sc->stats.outFrames++; 684585ff168SJulian Elischer sc->stats.outOctets += m2->m_pkthdr.len; 685585ff168SJulian Elischer NG_SEND_DATA_ONLY(error, sc->output.hook, m2); 686a1adb510SHartmut Brandt if (error) 687a1adb510SHartmut Brandt printf("%s: error=%d\n", __func__, error); 688585ff168SJulian Elischer } else { 689585ff168SJulian Elischer NG_FREE_M(m2); 690585ff168SJulian Elischer } 691585ff168SJulian Elischer } 692585ff168SJulian Elischer 693585ff168SJulian Elischer sc->packets -= sent; 694585ff168SJulian Elischer if (sent_p != NULL) 695585ff168SJulian Elischer *sent_p = sent; 696585ff168SJulian Elischer return (error); 697585ff168SJulian Elischer } 698