ng_source.c revision 138268
191041Sobrien/* 291041Sobrien * ng_source.c 3130561Sobrien * 491041Sobrien * Copyright 2002 Sandvine Inc. 591041Sobrien * All rights reserved. 691041Sobrien * 791041Sobrien * Subject to the following obligations and disclaimer of warranty, use and 891041Sobrien * redistribution of this software, in source or object code forms, with or 991041Sobrien * without modifications are expressly permitted by Sandvine Inc.; provided, 1091041Sobrien * however, that: 1191041Sobrien * 1. Any and all reproductions of the source or object code must include the 12130561Sobrien * copyright notice above and the following disclaimer of warranties; and 13130561Sobrien * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 1491041Sobrien * trademarks, including the mark "SANDVINE" on advertising, endorsements, 1591041Sobrien * or otherwise except as such appears in the above copyright notice or in 1691041Sobrien * the software. 1791041Sobrien * 1891041Sobrien * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 1991041Sobrien * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 2091041Sobrien * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 2191041Sobrien * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 2291041Sobrien * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 2391041Sobrien * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 2491041Sobrien * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 2591041Sobrien * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 2691041Sobrien * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2791041Sobrien * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 2891041Sobrien * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 2991041Sobrien * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3091041Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3191041Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3291041Sobrien * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 3391041Sobrien * DAMAGE. 3491041Sobrien * 3591041Sobrien * Author: Dave Chapeskie <dchapeskie@sandvine.com> 3691041Sobrien */ 3791041Sobrien 3891041Sobrien#include <sys/cdefs.h> 3991041Sobrien__FBSDID("$FreeBSD: head/sys/netgraph/ng_source.c 138268 2004-12-01 11:56:32Z glebius $"); 4091041Sobrien 4191041Sobrien/* 4291041Sobrien * This node is used for high speed packet geneneration. It queues 4391041Sobrien * all data recieved on it's 'input' hook and when told to start via 4491041Sobrien * a control message it sends the packets out it's 'output' hook. In 4591041Sobrien * this way this node can be preloaded with a packet stream which is 4691041Sobrien * continuously sent. 4791041Sobrien * 4891041Sobrien * Currently it just copies the mbufs as required. It could do various 4991041Sobrien * tricks to try and avoid this. Probably the best performance would 5091041Sobrien * be achieved by modifying the appropriate drivers to be told to 5191041Sobrien * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 5291041Sobrien * transmit descriptors) under control of this node; perhaps via some 5391041Sobrien * flag in the mbuf or some such. The node would peak at an appropriate 5491041Sobrien * ifnet flag to see if such support is available for the connected 5591041Sobrien * interface. 5691041Sobrien */ 5791041Sobrien 5891041Sobrien#include <sys/param.h> 5991041Sobrien#include <sys/systm.h> 6091041Sobrien#include <sys/errno.h> 6191041Sobrien#include <sys/kernel.h> 6291041Sobrien#include <sys/malloc.h> 6391041Sobrien#include <sys/mbuf.h> 6491041Sobrien#include <sys/socket.h> 65130561Sobrien#include <net/if.h> 6691041Sobrien#include <net/if_var.h> 67130561Sobrien#include <netgraph/ng_message.h> 6891041Sobrien#include <netgraph/netgraph.h> 69130561Sobrien#include <netgraph/ng_parse.h> 70130561Sobrien#include <netgraph/ng_ether.h> 71130561Sobrien#include <netgraph/ng_source.h> 72130561Sobrien 73130561Sobrien#define NG_SOURCE_INTR_TICKS 1 7491041Sobrien#define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 75130561Sobrien 76130561Sobrien 77130561Sobrien/* Per hook info */ 7891041Sobrienstruct source_hookinfo { 79130561Sobrien hook_p hook; 8091041Sobrien}; 81130561Sobrien 8291041Sobrien/* Per node info */ 8391041Sobrienstruct privdata { 84130561Sobrien node_p node; 8591041Sobrien struct source_hookinfo input; 86130561Sobrien struct source_hookinfo output; 87130561Sobrien struct ng_source_stats stats; 88130561Sobrien struct ifqueue snd_queue; /* packets to send */ 8991041Sobrien struct ifnet *output_ifp; 9091041Sobrien struct callout intr_ch; 9191041Sobrien u_int64_t packets; /* packets to send */ 9291041Sobrien u_int32_t queueOctets; 9391041Sobrien}; 9491041Sobrientypedef struct privdata *sc_p; 9591041Sobrien 9691041Sobrien/* Node flags */ 97130561Sobrien#define NG_SOURCE_ACTIVE (NGF_TYPE1) 9891041Sobrien 9991041Sobrien/* Netgraph methods */ 10091041Sobrienstatic ng_constructor_t ng_source_constructor; 10191041Sobrienstatic ng_rcvmsg_t ng_source_rcvmsg; 10291041Sobrienstatic ng_shutdown_t ng_source_rmnode; 10391041Sobrienstatic ng_newhook_t ng_source_newhook; 10491041Sobrienstatic ng_rcvdata_t ng_source_rcvdata; 10591041Sobrienstatic ng_disconnect_t ng_source_disconnect; 10691041Sobrien 10791041Sobrien/* Other functions */ 10891041Sobrienstatic void ng_source_intr(node_p, hook_p, void *, int); 10991041Sobrienstatic int ng_source_request_output_ifp (sc_p); 11091041Sobrienstatic void ng_source_clr_data (sc_p); 11191041Sobrienstatic void ng_source_start (sc_p); 11291041Sobrienstatic void ng_source_stop (sc_p); 11391041Sobrienstatic int ng_source_send (sc_p, int, int *); 11491041Sobrienstatic int ng_source_store_output_ifp(sc_p sc, 11591041Sobrien struct ng_mesg *msg); 11691041Sobrien 11791041Sobrien 11891041Sobrien/* Parse type for timeval */ 11991041Sobrienstatic const struct ng_parse_struct_field ng_source_timeval_type_fields[] = { 120130561Sobrien { "tv_sec", &ng_parse_int32_type }, 12191041Sobrien { "tv_usec", &ng_parse_int32_type }, 12291041Sobrien { NULL } 12391041Sobrien}; 12491041Sobrienconst struct ng_parse_type ng_source_timeval_type = { 12591041Sobrien &ng_parse_struct_type, 12691041Sobrien &ng_source_timeval_type_fields 12791041Sobrien}; 12891041Sobrien 12991041Sobrien/* Parse type for struct ng_source_stats */ 13091041Sobrienstatic const struct ng_parse_struct_field ng_source_stats_type_fields[] 13191041Sobrien = NG_SOURCE_STATS_TYPE_INFO; 13291041Sobrienstatic const struct ng_parse_type ng_source_stats_type = { 13391041Sobrien &ng_parse_struct_type, 13491041Sobrien &ng_source_stats_type_fields 13591041Sobrien}; 13691041Sobrien 13791041Sobrien/* List of commands and how to convert arguments to/from ASCII */ 13891041Sobrienstatic const struct ng_cmdlist ng_source_cmds[] = { 13991041Sobrien { 14091041Sobrien NGM_SOURCE_COOKIE, 14191041Sobrien NGM_SOURCE_GET_STATS, 14291041Sobrien "getstats", 143130561Sobrien NULL, 14491041Sobrien &ng_source_stats_type 14591041Sobrien }, 14691041Sobrien { 14791041Sobrien NGM_SOURCE_COOKIE, 14891041Sobrien NGM_SOURCE_CLR_STATS, 14991041Sobrien "clrstats", 15091041Sobrien NULL, 151130561Sobrien NULL 15291041Sobrien }, 15391041Sobrien { 15491041Sobrien NGM_SOURCE_COOKIE, 15591041Sobrien NGM_SOURCE_GETCLR_STATS, 15691041Sobrien "getclrstats", 15791041Sobrien NULL, 15891041Sobrien &ng_source_stats_type 15991041Sobrien }, 16091041Sobrien { 16191041Sobrien NGM_SOURCE_COOKIE, 16291041Sobrien NGM_SOURCE_START, 16391041Sobrien "start", 16491041Sobrien &ng_parse_uint64_type, 16591041Sobrien NULL 16691041Sobrien }, 167130561Sobrien { 16891041Sobrien NGM_SOURCE_COOKIE, 16991041Sobrien NGM_SOURCE_STOP, 17091041Sobrien "stop", 17191041Sobrien NULL, 17291041Sobrien NULL 17391041Sobrien }, 17491041Sobrien { 17591041Sobrien NGM_SOURCE_COOKIE, 17691041Sobrien NGM_SOURCE_CLR_DATA, 17791041Sobrien "clrdata", 17891041Sobrien NULL, 17991041Sobrien NULL 18091041Sobrien }, 18191041Sobrien { 18291041Sobrien NGM_SOURCE_COOKIE, 18391041Sobrien NGM_SOURCE_START_NOW, 18491041Sobrien "start_now", 18591041Sobrien &ng_parse_uint64_type, 18691041Sobrien NULL 18791041Sobrien }, 18891041Sobrien { 0 } 18991041Sobrien}; 190130561Sobrien 19191041Sobrien/* Netgraph type descriptor */ 19291041Sobrienstatic struct ng_type ng_source_typestruct = { 19391041Sobrien .version = NG_ABI_VERSION, 19491041Sobrien .name = NG_SOURCE_NODE_TYPE, 19591041Sobrien .constructor = ng_source_constructor, 19691041Sobrien .rcvmsg = ng_source_rcvmsg, 19791041Sobrien .shutdown = ng_source_rmnode, 19891041Sobrien .newhook = ng_source_newhook, 19991041Sobrien .rcvdata = ng_source_rcvdata, 20091041Sobrien .disconnect = ng_source_disconnect, 20191041Sobrien .cmdlist = ng_source_cmds, 20291041Sobrien}; 20391041SobrienNETGRAPH_INIT(source, &ng_source_typestruct); 20491041Sobrien 20591041Sobrienstatic int ng_source_set_autosrc(sc_p, u_int32_t); 20691041Sobrien 20791041Sobrien/* 20891041Sobrien * Node constructor 20991041Sobrien */ 21091041Sobrienstatic int 21191041Sobrienng_source_constructor(node_p node) 21291041Sobrien{ 21391041Sobrien sc_p sc; 21491041Sobrien 21591041Sobrien sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 21691041Sobrien if (sc == NULL) 21791041Sobrien return (ENOMEM); 21891041Sobrien 21991041Sobrien NG_NODE_SET_PRIVATE(node, sc); 22091041Sobrien sc->node = node; 22191041Sobrien sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 222130561Sobrien ng_callout_init(&sc->intr_ch); 223130561Sobrien 22491041Sobrien return (0); 22591041Sobrien} 22691041Sobrien 22791041Sobrien/* 22891041Sobrien * Add a hook 22991041Sobrien */ 23091041Sobrienstatic int 23191041Sobrienng_source_newhook(node_p node, hook_p hook, const char *name) 23291041Sobrien{ 23391041Sobrien sc_p sc; 23491041Sobrien 23591041Sobrien sc = NG_NODE_PRIVATE(node); 23691041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 23791041Sobrien if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 238130561Sobrien sc->input.hook = hook; 23991041Sobrien NG_HOOK_SET_PRIVATE(hook, &sc->input); 24091041Sobrien } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 24191041Sobrien sc->output.hook = hook; 24291041Sobrien NG_HOOK_SET_PRIVATE(hook, &sc->output); 24391041Sobrien sc->output_ifp = 0; 24491041Sobrien bzero(&sc->stats, sizeof(sc->stats)); 24591041Sobrien } else 24691041Sobrien return (EINVAL); 24791041Sobrien return (0); 24891041Sobrien} 24991041Sobrien 25091041Sobrien/* 25191041Sobrien * Receive a control message 25291041Sobrien */ 25391041Sobrienstatic int 25491041Sobrienng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 25591041Sobrien{ 25691041Sobrien sc_p sc; 25791041Sobrien struct ng_mesg *resp = NULL; 25891041Sobrien int error = 0; 25991041Sobrien struct ng_mesg *msg; 26091041Sobrien 26191041Sobrien sc = NG_NODE_PRIVATE(node); 26291041Sobrien NGI_GET_MSG(item, msg); 26391041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 26491041Sobrien switch (msg->header.typecookie) { 26591041Sobrien case NGM_SOURCE_COOKIE: 26691041Sobrien if (msg->header.flags & NGF_RESP) { 26791041Sobrien error = EINVAL; 26891041Sobrien break; 26991041Sobrien } 270130561Sobrien switch (msg->header.cmd) { 27191041Sobrien case NGM_SOURCE_GET_STATS: 27291041Sobrien case NGM_SOURCE_CLR_STATS: 27391041Sobrien case NGM_SOURCE_GETCLR_STATS: 27491041Sobrien { 27591041Sobrien struct ng_source_stats *stats; 27691041Sobrien 27791041Sobrien if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 27891041Sobrien NG_MKRESPONSE(resp, msg, 27991041Sobrien sizeof(*stats), M_NOWAIT); 28091041Sobrien if (resp == NULL) { 28191041Sobrien error = ENOMEM; 28291041Sobrien goto done; 283130561Sobrien } 28491041Sobrien sc->stats.queueOctets = sc->queueOctets; 28591041Sobrien sc->stats.queueFrames = sc->snd_queue.ifq_len; 28691041Sobrien if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 28791041Sobrien && !timevalisset(&sc->stats.endTime)) { 288130561Sobrien getmicrotime(&sc->stats.elapsedTime); 289130561Sobrien timevalsub(&sc->stats.elapsedTime, 290130561Sobrien &sc->stats.startTime); 29191041Sobrien } 29291041Sobrien stats = (struct ng_source_stats *)resp->data; 29391041Sobrien bcopy(&sc->stats, stats, sizeof(* stats)); 29491041Sobrien } 29591041Sobrien if (msg->header.cmd != NGM_SOURCE_GET_STATS) 29691041Sobrien bzero(&sc->stats, sizeof(sc->stats)); 29791041Sobrien } 29891041Sobrien break; 29991041Sobrien case NGM_SOURCE_START: 30091041Sobrien { 30191041Sobrien u_int64_t packets = *(u_int64_t *)msg->data; 30291041Sobrien if (sc->output.hook == NULL) { 30391041Sobrien printf("%s: start on node with no output hook\n" 30491041Sobrien , __func__); 30591041Sobrien error = EINVAL; 30691041Sobrien break; 30791041Sobrien } 30891041Sobrien /* TODO validation of packets */ 30991041Sobrien sc->packets = packets; 31091041Sobrien ng_source_start(sc); 31191041Sobrien } 31291041Sobrien break; 31391041Sobrien case NGM_SOURCE_START_NOW: 31491041Sobrien { 31591041Sobrien u_int64_t packets = *(u_int64_t *)msg->data; 31691041Sobrien if (sc->output.hook == NULL) { 31791041Sobrien printf("%s: start on node with no output hook\n" 31891041Sobrien , __func__); 31991041Sobrien error = EINVAL; 32091041Sobrien break; 32191041Sobrien } 32291041Sobrien if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 32391041Sobrien error = EBUSY; 32491041Sobrien break; 32591041Sobrien } 32691041Sobrien /* TODO validation of packets */ 32791041Sobrien sc->packets = packets; 32891041Sobrien sc->output_ifp = NULL; 32991041Sobrien 33091041Sobrien sc->node->nd_flags |= NG_SOURCE_ACTIVE; 33191041Sobrien timevalclear(&sc->stats.elapsedTime); 33291041Sobrien timevalclear(&sc->stats.endTime); 33391041Sobrien getmicrotime(&sc->stats.startTime); 33491041Sobrien ng_callout(&sc->intr_ch, node, NULL, 0, 33591041Sobrien ng_source_intr, sc, 0); 33691041Sobrien } 33791041Sobrien break; 33891041Sobrien case NGM_SOURCE_STOP: 33991041Sobrien ng_source_stop(sc); 34091041Sobrien break; 34191041Sobrien case NGM_SOURCE_CLR_DATA: 34291041Sobrien ng_source_clr_data(sc); 34391041Sobrien break; 34491041Sobrien default: 34591041Sobrien error = EINVAL; 34691041Sobrien break; 34791041Sobrien } 34891041Sobrien break; 34991041Sobrien case NGM_ETHER_COOKIE: 35091041Sobrien if (!(msg->header.flags & NGF_RESP)) { 35191041Sobrien error = EINVAL; 35291041Sobrien break; 35391041Sobrien } 35491041Sobrien switch (msg->header.cmd) { 35591041Sobrien case NGM_ETHER_GET_IFINDEX: 35691041Sobrien if (ng_source_store_output_ifp(sc, msg) == 0) { 35791041Sobrien ng_source_set_autosrc(sc, 0); 35891041Sobrien sc->node->nd_flags |= NG_SOURCE_ACTIVE; 359130561Sobrien timevalclear(&sc->stats.elapsedTime); 36091041Sobrien timevalclear(&sc->stats.endTime); 36191041Sobrien getmicrotime(&sc->stats.startTime); 36291041Sobrien ng_callout(&sc->intr_ch, node, NULL, 0, 36391041Sobrien ng_source_intr, sc, 0); 364130561Sobrien } 36591041Sobrien break; 36691041Sobrien default: 36791041Sobrien error = EINVAL; 36891041Sobrien } 369130561Sobrien break; 37091041Sobrien default: 37191041Sobrien error = EINVAL; 37291041Sobrien break; 37391041Sobrien } 37491041Sobrien 37591041Sobriendone: 37691041Sobrien /* Take care of synchronous response, if any */ 37791041Sobrien NG_RESPOND_MSG(error, node, item, resp); 37891041Sobrien /* Free the message and return */ 37991041Sobrien NG_FREE_MSG(msg); 38091041Sobrien return (error); 38191041Sobrien} 38291041Sobrien 38391041Sobrien/* 38491041Sobrien * Receive data on a hook 38591041Sobrien * 38691041Sobrien * If data comes in the input hook, enqueue it on the send queue. 38791041Sobrien * If data comes in the output hook, discard it. 38891041Sobrien */ 38991041Sobrienstatic int 39091041Sobrienng_source_rcvdata(hook_p hook, item_p item) 39191041Sobrien{ 39291041Sobrien sc_p sc; 39391041Sobrien struct source_hookinfo *hinfo; 39491041Sobrien int error = 0; 39591041Sobrien struct mbuf *m; 39691041Sobrien 39791041Sobrien sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 398130561Sobrien NGI_GET_M(item, m); 39991041Sobrien NG_FREE_ITEM(item); 40091041Sobrien hinfo = NG_HOOK_PRIVATE(hook); 40191041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 40291041Sobrien KASSERT(hinfo != NULL, ("%s: null hook info", __func__)); 40391041Sobrien 40491041Sobrien /* Which hook? */ 40591041Sobrien if (hinfo == &sc->output) { 40691041Sobrien /* discard */ 40791041Sobrien NG_FREE_M(m); 40891041Sobrien return (error); 40991041Sobrien } 41091041Sobrien KASSERT(hinfo == &sc->input, ("%s: no hook!", __func__)); 41191041Sobrien 41291041Sobrien if ((m->m_flags & M_PKTHDR) == 0) { 41391041Sobrien printf("%s: mbuf without PKTHDR\n", __func__); 41491041Sobrien NG_FREE_M(m); 41591041Sobrien return (EINVAL); 41691041Sobrien } 41791041Sobrien 41891041Sobrien /* enque packet */ 41991041Sobrien /* XXX should we check IF_QFULL() ? */ 42091041Sobrien _IF_ENQUEUE(&sc->snd_queue, m); 42191041Sobrien sc->queueOctets += m->m_pkthdr.len; 42291041Sobrien 42391041Sobrien return (0); 42491041Sobrien} 42591041Sobrien 42691041Sobrien/* 42791041Sobrien * Shutdown processing 42891041Sobrien */ 42991041Sobrienstatic int 43091041Sobrienng_source_rmnode(node_p node) 43191041Sobrien{ 43291041Sobrien sc_p sc; 43391041Sobrien 43491041Sobrien sc = NG_NODE_PRIVATE(node); 43591041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 43691041Sobrien ng_source_stop(sc); 43791041Sobrien ng_source_clr_data(sc); 43891041Sobrien NG_NODE_SET_PRIVATE(node, NULL); 43991041Sobrien NG_NODE_UNREF(node); 44091041Sobrien free(sc, M_NETGRAPH); 44191041Sobrien return (0); 44291041Sobrien} 44391041Sobrien 44491041Sobrien/* 44591041Sobrien * Hook disconnection 44691041Sobrien */ 44791041Sobrienstatic int 44891041Sobrienng_source_disconnect(hook_p hook) 44991041Sobrien{ 45091041Sobrien struct source_hookinfo *hinfo; 45191041Sobrien sc_p sc; 45291041Sobrien 45391041Sobrien hinfo = NG_HOOK_PRIVATE(hook); 45491041Sobrien sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 45591041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 45691041Sobrien hinfo->hook = NULL; 45791041Sobrien if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hinfo == &sc->output) 45891041Sobrien ng_rmnode_self(NG_HOOK_NODE(hook)); 45991041Sobrien return (0); 46091041Sobrien} 46191041Sobrien 46291041Sobrien/* 46391041Sobrien * 46491041Sobrien * Ask out neighbour on the output hook side to send us it's interface 46591041Sobrien * information. 46691041Sobrien */ 46791041Sobrienstatic int 46891041Sobrienng_source_request_output_ifp(sc_p sc) 46991041Sobrien{ 47091041Sobrien struct ng_mesg *msg; 47191041Sobrien int error = 0; 47291041Sobrien 47391041Sobrien sc->output_ifp = NULL; 47491041Sobrien 47591041Sobrien /* Ask the attached node for the connected interface's index */ 47691041Sobrien NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, 0, M_NOWAIT); 47791041Sobrien if (msg == NULL) 47891041Sobrien return (ENOBUFS); 47991041Sobrien 48091041Sobrien NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, 0); 48191041Sobrien return (error); 48291041Sobrien} 48391041Sobrien 48491041Sobrien/* 48591041Sobrien * Set sc->output_ifp to point to the the struct ifnet of the interface 48691041Sobrien * reached via our output hook. 48791041Sobrien */ 48891041Sobrienstatic int 48991041Sobrienng_source_store_output_ifp(sc_p sc, struct ng_mesg *msg) 49091041Sobrien{ 49191041Sobrien struct ifnet *ifp; 49291041Sobrien u_int32_t if_index; 49391041Sobrien int s; 49491041Sobrien 49591041Sobrien if (msg->header.arglen < sizeof(u_int32_t)) 49691041Sobrien return (EINVAL); 49791041Sobrien 49891041Sobrien if_index = *(u_int32_t *)msg->data; 499130561Sobrien /* Could use ifindex2ifnet[if_index] except that we have no 500130561Sobrien * way of verifying if_index is valid since if_indexlim is 50191041Sobrien * local to if_attach() 50291041Sobrien */ 50391041Sobrien IFNET_RLOCK(); 50491041Sobrien TAILQ_FOREACH(ifp, &ifnet, if_link) { 505130561Sobrien if (ifp->if_index == if_index) 50691041Sobrien break; 50791041Sobrien } 50891041Sobrien IFNET_RUNLOCK(); 50991041Sobrien 51091041Sobrien if (ifp == NULL) { 51191041Sobrien printf("%s: can't find interface %d\n", __func__, if_index); 51291041Sobrien return (EINVAL); 51391041Sobrien } 51491041Sobrien sc->output_ifp = ifp; 51591041Sobrien 51691041Sobrien#if 1 51791041Sobrien /* XXX mucking with a drivers ifqueue size is ugly but we need it 51891041Sobrien * to queue a lot of packets to get close to line rate on a gigabit 51991041Sobrien * interface with small packets. 52091041Sobrien * XXX we should restore the original value at stop or disconnect 52191041Sobrien */ 52291041Sobrien s = splimp(); /* XXX is this required? */ 52391041Sobrien if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) { 52491041Sobrien printf("ng_source: changing ifq_maxlen from %d to %d\n", 52591041Sobrien ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 526130561Sobrien ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 52791041Sobrien } 52891041Sobrien splx(s); 52991041Sobrien#endif 53091041Sobrien return (0); 53191041Sobrien} 53291041Sobrien 53391041Sobrien/* 53491041Sobrien * Set the attached ethernet node's ethernet source address override flag. 53591041Sobrien */ 536130561Sobrienstatic int 53791041Sobrienng_source_set_autosrc(sc_p sc, u_int32_t flag) 53891041Sobrien{ 53991041Sobrien struct ng_mesg *msg; 54091041Sobrien int error = 0; 54191041Sobrien 542130561Sobrien NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 543130561Sobrien sizeof (u_int32_t), M_NOWAIT); 54491041Sobrien if (msg == NULL) 54591041Sobrien return(ENOBUFS); 54691041Sobrien 547130561Sobrien *(u_int32_t *)msg->data = flag; 54891041Sobrien NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output.hook, 0); 54991041Sobrien return (error); 55091041Sobrien} 55191041Sobrien 552130561Sobrien/* 55391041Sobrien * Clear out the data we've queued 55491041Sobrien */ 55591041Sobrienstatic void 55691041Sobrienng_source_clr_data (sc_p sc) 55791041Sobrien{ 55891041Sobrien struct mbuf *m; 559130561Sobrien 56091041Sobrien for (;;) { 56191041Sobrien _IF_DEQUEUE(&sc->snd_queue, m); 56291041Sobrien if (m == NULL) 563130561Sobrien break; 56491041Sobrien NG_FREE_M(m); 56591041Sobrien } 56691041Sobrien sc->queueOctets = 0; 56791041Sobrien} 568130561Sobrien 56991041Sobrien/* 57091041Sobrien * Start sending queued data out the output hook 57191041Sobrien */ 57291041Sobrienstatic void 57391041Sobrienng_source_start (sc_p sc) 57491041Sobrien{ 57591041Sobrien KASSERT(sc->output.hook != NULL, 57691041Sobrien ("%s: output hook unconnected", __func__)); 577130561Sobrien if (((sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) && 57891041Sobrien (sc->output_ifp == NULL)) 57991041Sobrien ng_source_request_output_ifp(sc); 58091041Sobrien} 58191041Sobrien 58291041Sobrien/* 58391041Sobrien * Stop sending queued data out the output hook 58491041Sobrien */ 585130561Sobrienstatic void 58691041Sobrienng_source_stop (sc_p sc) 58791041Sobrien{ 58891041Sobrien if (sc->node->nd_flags & NG_SOURCE_ACTIVE) { 58991041Sobrien ng_uncallout(&sc->intr_ch, sc->node); 59091041Sobrien sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 59191041Sobrien getmicrotime(&sc->stats.endTime); 592130561Sobrien sc->stats.elapsedTime = sc->stats.endTime; 59391041Sobrien timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 59491041Sobrien /* XXX should set this to the initial value instead */ 59591041Sobrien ng_source_set_autosrc(sc, 1); 59691041Sobrien } 59791041Sobrien} 59891041Sobrien 59991041Sobrien/* 60091041Sobrien * While active called every NG_SOURCE_INTR_TICKS ticks. 60191041Sobrien * Sends as many packets as the interface connected to our 60291041Sobrien * output hook is able to enqueue. 60391041Sobrien */ 60491041Sobrienstatic void 60591041Sobrienng_source_intr(node_p node, hook_p hook, void *arg1, int arg2) 60691041Sobrien{ 60791041Sobrien sc_p sc = (sc_p)arg1; 60891041Sobrien struct ifqueue *ifq; 60991041Sobrien int packets; 61091041Sobrien 61191041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 61291041Sobrien 61391041Sobrien if (sc->packets == 0 || sc->output.hook == NULL 61491041Sobrien || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 61591041Sobrien ng_source_stop(sc); 61691041Sobrien return; 61791041Sobrien } 61891041Sobrien 61991041Sobrien if (sc->output_ifp != NULL) { 62091041Sobrien ifq = &sc->output_ifp->if_snd; 62191041Sobrien packets = ifq->ifq_maxlen - ifq->ifq_len; 62291041Sobrien } else 62391041Sobrien packets = sc->snd_queue.ifq_len; 62491041Sobrien 62591041Sobrien ng_source_send(sc, packets, NULL); 62691041Sobrien if (sc->packets == 0) 62791041Sobrien ng_source_stop(sc); 62891041Sobrien else 62991041Sobrien ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS, 63091041Sobrien ng_source_intr, sc, 0); 63191041Sobrien} 632130561Sobrien 63391041Sobrien/* 63491041Sobrien * Send packets out our output hook 63591041Sobrien */ 63691041Sobrienstatic int 63791041Sobrienng_source_send (sc_p sc, int tosend, int *sent_p) 63891041Sobrien{ 63991041Sobrien struct ifqueue tmp_queue; 640130561Sobrien struct mbuf *m, *m2; 64191041Sobrien int sent = 0; 64291041Sobrien int error = 0; 64391041Sobrien 64491041Sobrien KASSERT(sc != NULL, ("%s: null node private", __func__)); 64591041Sobrien KASSERT(tosend >= 0, ("%s: negative tosend param", __func__)); 64691041Sobrien KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 64791041Sobrien ("%s: inactive node", __func__)); 64891041Sobrien 64991041Sobrien if ((u_int64_t)tosend > sc->packets) 65091041Sobrien tosend = sc->packets; 65191041Sobrien 65291041Sobrien /* Copy the required number of packets to a temporary queue */ 65391041Sobrien bzero (&tmp_queue, sizeof (tmp_queue)); 65491041Sobrien for (sent = 0; error == 0 && sent < tosend; ++sent) { 65591041Sobrien _IF_DEQUEUE(&sc->snd_queue, m); 65691041Sobrien if (m == NULL) 65791041Sobrien break; 65891041Sobrien 65991041Sobrien /* duplicate the packet */ 660130561Sobrien m2 = m_copypacket(m, M_DONTWAIT); 66191041Sobrien if (m2 == NULL) { 66291041Sobrien _IF_PREPEND(&sc->snd_queue, m); 66391041Sobrien error = ENOBUFS; 66491041Sobrien break; 66591041Sobrien } 66691041Sobrien 66791041Sobrien /* re-enqueue the original packet for us */ 66891041Sobrien _IF_ENQUEUE(&sc->snd_queue, m); 66991041Sobrien 670130561Sobrien /* queue the copy for sending at smplimp */ 67191041Sobrien _IF_ENQUEUE(&tmp_queue, m2); 67291041Sobrien } 673130561Sobrien 674130561Sobrien sent = 0; 67591041Sobrien for (;;) { 67691041Sobrien _IF_DEQUEUE(&tmp_queue, m2); 67791041Sobrien if (m2 == NULL) 67891041Sobrien break; 67991041Sobrien if (error == 0) { 68091041Sobrien ++sent; 68191041Sobrien sc->stats.outFrames++; 68291041Sobrien sc->stats.outOctets += m2->m_pkthdr.len; 683130561Sobrien NG_SEND_DATA_ONLY(error, sc->output.hook, m2); 68491041Sobrien if (error) 68591041Sobrien printf("%s: error=%d\n", __func__, error); 68691041Sobrien } else { 687130561Sobrien NG_FREE_M(m2); 68891041Sobrien } 68991041Sobrien } 69091041Sobrien 69191041Sobrien sc->packets -= sent; 69291041Sobrien if (sent_p != NULL) 69391041Sobrien *sent_p = sent; 69491041Sobrien return (error); 69591041Sobrien} 69691041Sobrien