1585ff168SJulian Elischer /* 2585ff168SJulian Elischer * ng_source.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 6d8f5d037SGleb Smirnoff * Copyright (c) 2005 Gleb Smirnoff <glebius@FreeBSD.org> 7585ff168SJulian Elischer * Copyright 2002 Sandvine Inc. 8585ff168SJulian Elischer * All rights reserved. 9585ff168SJulian Elischer * 10585ff168SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 11585ff168SJulian Elischer * redistribution of this software, in source or object code forms, with or 124b52f283SJulian Elischer * without modifications are expressly permitted by Sandvine Inc.; provided, 13585ff168SJulian Elischer * however, that: 144b52f283SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 154b52f283SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 16585ff168SJulian Elischer * 2. No rights are granted, in any manner or form, to use Sandvine Inc. 174b52f283SJulian Elischer * trademarks, including the mark "SANDVINE" on advertising, endorsements, 184b52f283SJulian Elischer * or otherwise except as such appears in the above copyright notice or in 19585ff168SJulian Elischer * the software. 20585ff168SJulian Elischer * 21585ff168SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY SANDVINE "AS IS", AND TO THE MAXIMUM 224b52f283SJulian Elischer * EXTENT PERMITTED BY LAW, SANDVINE MAKES NO REPRESENTATIONS OR WARRANTIES, 234b52f283SJulian Elischer * EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, 244b52f283SJulian Elischer * ANY AND ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 25585ff168SJulian Elischer * PURPOSE, OR NON-INFRINGEMENT. SANDVINE DOES NOT WARRANT, GUARANTEE, OR 26585ff168SJulian Elischer * MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE 27585ff168SJulian Elischer * USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY 28585ff168SJulian Elischer * OR OTHERWISE. IN NO EVENT SHALL SANDVINE BE LIABLE FOR ANY DAMAGES 29585ff168SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 304b52f283SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31585ff168SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32585ff168SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33585ff168SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34585ff168SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35585ff168SJulian Elischer * THIS SOFTWARE, EVEN IF SANDVINE IS ADVISED OF THE POSSIBILITY OF SUCH 36585ff168SJulian Elischer * DAMAGE. 37585ff168SJulian Elischer * 38d11f4f5dSEd Maste * Author: Dave Chapeskie 39585ff168SJulian Elischer */ 40585ff168SJulian Elischer 4176bd5857SHartmut Brandt #include <sys/cdefs.h> 4276bd5857SHartmut Brandt __FBSDID("$FreeBSD$"); 4376bd5857SHartmut Brandt 44585ff168SJulian Elischer /* 45585ff168SJulian Elischer * This node is used for high speed packet geneneration. It queues 462b0ffc02SBosko Milekic * all data recieved on its 'input' hook and when told to start via 472b0ffc02SBosko Milekic * a control message it sends the packets out its 'output' hook. In 482b0ffc02SBosko Milekic * this way this node can be preloaded with a packet stream which it 492b0ffc02SBosko Milekic * can then send continuously as fast as possible. 50585ff168SJulian Elischer * 51585ff168SJulian Elischer * Currently it just copies the mbufs as required. It could do various 52585ff168SJulian Elischer * tricks to try and avoid this. Probably the best performance would 53585ff168SJulian Elischer * be achieved by modifying the appropriate drivers to be told to 54585ff168SJulian Elischer * self-re-enqueue packets (e.g. the if_bge driver could reuse the same 55585ff168SJulian Elischer * transmit descriptors) under control of this node; perhaps via some 562b0ffc02SBosko Milekic * flag in the mbuf or some such. The node could peek at an appropriate 57585ff168SJulian Elischer * ifnet flag to see if such support is available for the connected 58585ff168SJulian Elischer * interface. 59585ff168SJulian Elischer */ 60585ff168SJulian Elischer 61585ff168SJulian Elischer #include <sys/param.h> 62585ff168SJulian Elischer #include <sys/systm.h> 63585ff168SJulian Elischer #include <sys/errno.h> 64585ff168SJulian Elischer #include <sys/kernel.h> 65585ff168SJulian Elischer #include <sys/malloc.h> 66585ff168SJulian Elischer #include <sys/mbuf.h> 67585ff168SJulian Elischer #include <sys/socket.h> 68d8f5d037SGleb Smirnoff #include <sys/syslog.h> 69585ff168SJulian Elischer #include <net/if.h> 70585ff168SJulian Elischer #include <net/if_var.h> 71530c0060SRobert Watson #include <net/vnet.h> 72585ff168SJulian Elischer #include <netgraph/ng_message.h> 73585ff168SJulian Elischer #include <netgraph/netgraph.h> 74585ff168SJulian Elischer #include <netgraph/ng_parse.h> 75585ff168SJulian Elischer #include <netgraph/ng_ether.h> 76585ff168SJulian Elischer #include <netgraph/ng_source.h> 77585ff168SJulian Elischer 78585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS 1 79585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 80585ff168SJulian Elischer 815f87dd69SEd Maste #define mtod_off(m,off,t) ((t)(mtod((m),caddr_t)+(off))) 825f87dd69SEd Maste 83585ff168SJulian Elischer /* Per node info */ 84585ff168SJulian Elischer struct privdata { 85585ff168SJulian Elischer node_p node; 86d8f5d037SGleb Smirnoff hook_p input; 87d8f5d037SGleb Smirnoff hook_p output; 88585ff168SJulian Elischer struct ng_source_stats stats; 894b52f283SJulian Elischer struct ifqueue snd_queue; /* packets to send */ 90577421ebSEd Maste struct mbuf *last_packet; /* last pkt in queue */ 91585ff168SJulian Elischer struct ifnet *output_ifp; 9230bef41bSGleb Smirnoff struct callout intr_ch; 93d8f5d037SGleb Smirnoff uint64_t packets; /* packets to send */ 94d8f5d037SGleb Smirnoff uint32_t queueOctets; 955f87dd69SEd Maste struct ng_source_embed_info embed_timestamp; 96577421ebSEd Maste struct ng_source_embed_cnt_info embed_counter[NG_SOURCE_COUNTERS]; 97585ff168SJulian Elischer }; 98585ff168SJulian Elischer typedef struct privdata *sc_p; 99585ff168SJulian Elischer 100585ff168SJulian Elischer /* Node flags */ 101585ff168SJulian Elischer #define NG_SOURCE_ACTIVE (NGF_TYPE1) 102585ff168SJulian Elischer 103585ff168SJulian Elischer /* Netgraph methods */ 104585ff168SJulian Elischer static ng_constructor_t ng_source_constructor; 105585ff168SJulian Elischer static ng_rcvmsg_t ng_source_rcvmsg; 106585ff168SJulian Elischer static ng_shutdown_t ng_source_rmnode; 107585ff168SJulian Elischer static ng_newhook_t ng_source_newhook; 108d8f5d037SGleb Smirnoff static ng_connect_t ng_source_connect; 109585ff168SJulian Elischer static ng_rcvdata_t ng_source_rcvdata; 110585ff168SJulian Elischer static ng_disconnect_t ng_source_disconnect; 111585ff168SJulian Elischer 112585ff168SJulian Elischer /* Other functions */ 113a1adb510SHartmut Brandt static void ng_source_intr(node_p, hook_p, void *, int); 114585ff168SJulian Elischer static void ng_source_clr_data (sc_p); 115d8f5d037SGleb Smirnoff static int ng_source_start (sc_p, uint64_t); 116585ff168SJulian Elischer static void ng_source_stop (sc_p); 117585ff168SJulian Elischer static int ng_source_send (sc_p, int, int *); 118d8f5d037SGleb Smirnoff static int ng_source_store_output_ifp(sc_p, char *); 1195f87dd69SEd Maste static void ng_source_packet_mod(sc_p, struct mbuf *, 1205f87dd69SEd Maste int, int, caddr_t, int); 121577421ebSEd Maste static void ng_source_mod_counter(sc_p sc, 122577421ebSEd Maste struct ng_source_embed_cnt_info *cnt, 123577421ebSEd Maste struct mbuf *m, int increment); 1245f87dd69SEd Maste static int ng_source_dup_mod(sc_p, struct mbuf *, 1255f87dd69SEd Maste struct mbuf **); 126585ff168SJulian Elischer 127585ff168SJulian Elischer /* Parse type for timeval */ 12876bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = { 129585ff168SJulian Elischer { "tv_sec", &ng_parse_int32_type }, 130585ff168SJulian Elischer { "tv_usec", &ng_parse_int32_type }, 131585ff168SJulian Elischer { NULL } 132585ff168SJulian Elischer }; 133585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = { 134585ff168SJulian Elischer &ng_parse_struct_type, 135585ff168SJulian Elischer &ng_source_timeval_type_fields 136585ff168SJulian Elischer }; 137585ff168SJulian Elischer 138585ff168SJulian Elischer /* Parse type for struct ng_source_stats */ 139585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[] 140585ff168SJulian Elischer = NG_SOURCE_STATS_TYPE_INFO; 141585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = { 142585ff168SJulian Elischer &ng_parse_struct_type, 143585ff168SJulian Elischer &ng_source_stats_type_fields 144585ff168SJulian Elischer }; 145585ff168SJulian Elischer 1465f87dd69SEd Maste /* Parse type for struct ng_source_embed_info */ 1475f87dd69SEd Maste static const struct ng_parse_struct_field ng_source_embed_type_fields[] = 1485f87dd69SEd Maste NG_SOURCE_EMBED_TYPE_INFO; 1495f87dd69SEd Maste static const struct ng_parse_type ng_source_embed_type = { 1505f87dd69SEd Maste &ng_parse_struct_type, 1515f87dd69SEd Maste &ng_source_embed_type_fields 1525f87dd69SEd Maste }; 1535f87dd69SEd Maste 154577421ebSEd Maste /* Parse type for struct ng_source_embed_cnt_info */ 155577421ebSEd Maste static const struct ng_parse_struct_field ng_source_embed_cnt_type_fields[] = 156577421ebSEd Maste NG_SOURCE_EMBED_CNT_TYPE_INFO; 157577421ebSEd Maste static const struct ng_parse_type ng_source_embed_cnt_type = { 158577421ebSEd Maste &ng_parse_struct_type, 159577421ebSEd Maste &ng_source_embed_cnt_type_fields 160577421ebSEd Maste }; 161577421ebSEd Maste 162585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */ 163585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = { 164585ff168SJulian Elischer { 165585ff168SJulian Elischer NGM_SOURCE_COOKIE, 166585ff168SJulian Elischer NGM_SOURCE_GET_STATS, 167585ff168SJulian Elischer "getstats", 168585ff168SJulian Elischer NULL, 169585ff168SJulian Elischer &ng_source_stats_type 170585ff168SJulian Elischer }, 171585ff168SJulian Elischer { 172585ff168SJulian Elischer NGM_SOURCE_COOKIE, 173585ff168SJulian Elischer NGM_SOURCE_CLR_STATS, 174585ff168SJulian Elischer "clrstats", 175585ff168SJulian Elischer NULL, 176585ff168SJulian Elischer NULL 177585ff168SJulian Elischer }, 178585ff168SJulian Elischer { 179585ff168SJulian Elischer NGM_SOURCE_COOKIE, 180585ff168SJulian Elischer NGM_SOURCE_GETCLR_STATS, 181585ff168SJulian Elischer "getclrstats", 182585ff168SJulian Elischer NULL, 183585ff168SJulian Elischer &ng_source_stats_type 184585ff168SJulian Elischer }, 185585ff168SJulian Elischer { 186585ff168SJulian Elischer NGM_SOURCE_COOKIE, 187585ff168SJulian Elischer NGM_SOURCE_START, 188585ff168SJulian Elischer "start", 189585ff168SJulian Elischer &ng_parse_uint64_type, 190585ff168SJulian Elischer NULL 191585ff168SJulian Elischer }, 192585ff168SJulian Elischer { 193585ff168SJulian Elischer NGM_SOURCE_COOKIE, 194585ff168SJulian Elischer NGM_SOURCE_STOP, 195585ff168SJulian Elischer "stop", 196585ff168SJulian Elischer NULL, 197585ff168SJulian Elischer NULL 198585ff168SJulian Elischer }, 199585ff168SJulian Elischer { 200585ff168SJulian Elischer NGM_SOURCE_COOKIE, 201585ff168SJulian Elischer NGM_SOURCE_CLR_DATA, 202585ff168SJulian Elischer "clrdata", 203585ff168SJulian Elischer NULL, 204585ff168SJulian Elischer NULL 205585ff168SJulian Elischer }, 206f5d15522SHartmut Brandt { 207f5d15522SHartmut Brandt NGM_SOURCE_COOKIE, 208d8f5d037SGleb Smirnoff NGM_SOURCE_SETIFACE, 209d8f5d037SGleb Smirnoff "setiface", 210d8f5d037SGleb Smirnoff &ng_parse_string_type, 211f5d15522SHartmut Brandt NULL 212f5d15522SHartmut Brandt }, 21372235857SGleb Smirnoff { 21472235857SGleb Smirnoff NGM_SOURCE_COOKIE, 21572235857SGleb Smirnoff NGM_SOURCE_SETPPS, 21672235857SGleb Smirnoff "setpps", 21772235857SGleb Smirnoff &ng_parse_uint32_type, 21872235857SGleb Smirnoff NULL 21972235857SGleb Smirnoff }, 2205f87dd69SEd Maste { 2215f87dd69SEd Maste NGM_SOURCE_COOKIE, 2225f87dd69SEd Maste NGM_SOURCE_SET_TIMESTAMP, 2235f87dd69SEd Maste "settimestamp", 2245f87dd69SEd Maste &ng_source_embed_type, 2255f87dd69SEd Maste NULL 2265f87dd69SEd Maste }, 2275f87dd69SEd Maste { 2285f87dd69SEd Maste NGM_SOURCE_COOKIE, 2295f87dd69SEd Maste NGM_SOURCE_GET_TIMESTAMP, 2305f87dd69SEd Maste "gettimestamp", 2315f87dd69SEd Maste NULL, 2325f87dd69SEd Maste &ng_source_embed_type 2335f87dd69SEd Maste }, 234577421ebSEd Maste { 235577421ebSEd Maste NGM_SOURCE_COOKIE, 236577421ebSEd Maste NGM_SOURCE_SET_COUNTER, 237577421ebSEd Maste "setcounter", 238577421ebSEd Maste &ng_source_embed_cnt_type, 239577421ebSEd Maste NULL 240577421ebSEd Maste }, 241577421ebSEd Maste { 242577421ebSEd Maste NGM_SOURCE_COOKIE, 243577421ebSEd Maste NGM_SOURCE_GET_COUNTER, 244577421ebSEd Maste "getcounter", 245577421ebSEd Maste &ng_parse_uint8_type, 246577421ebSEd Maste &ng_source_embed_cnt_type 247577421ebSEd Maste }, 248585ff168SJulian Elischer { 0 } 249585ff168SJulian Elischer }; 250585ff168SJulian Elischer 251585ff168SJulian Elischer /* Netgraph type descriptor */ 252585ff168SJulian Elischer static struct ng_type ng_source_typestruct = { 253f8aae777SJulian Elischer .version = NG_ABI_VERSION, 254f8aae777SJulian Elischer .name = NG_SOURCE_NODE_TYPE, 255f8aae777SJulian Elischer .constructor = ng_source_constructor, 256f8aae777SJulian Elischer .rcvmsg = ng_source_rcvmsg, 257f8aae777SJulian Elischer .shutdown = ng_source_rmnode, 258f8aae777SJulian Elischer .newhook = ng_source_newhook, 259d8f5d037SGleb Smirnoff .connect = ng_source_connect, 260f8aae777SJulian Elischer .rcvdata = ng_source_rcvdata, 261f8aae777SJulian Elischer .disconnect = ng_source_disconnect, 262f8aae777SJulian Elischer .cmdlist = ng_source_cmds, 263585ff168SJulian Elischer }; 264585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct); 265585ff168SJulian Elischer 266d8f5d037SGleb Smirnoff static int ng_source_set_autosrc(sc_p, uint32_t); 2672cafef3eSHartmut Brandt 268585ff168SJulian Elischer /* 269585ff168SJulian Elischer * Node constructor 270585ff168SJulian Elischer */ 271585ff168SJulian Elischer static int 2725968e29eSJulian Elischer ng_source_constructor(node_p node) 273585ff168SJulian Elischer { 274585ff168SJulian Elischer sc_p sc; 275585ff168SJulian Elischer 276*674d86bfSGleb Smirnoff sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); 277585ff168SJulian Elischer 2785968e29eSJulian Elischer NG_NODE_SET_PRIVATE(node, sc); 2795968e29eSJulian Elischer sc->node = node; 280585ff168SJulian Elischer sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 28130bef41bSGleb Smirnoff ng_callout_init(&sc->intr_ch); 28230bef41bSGleb Smirnoff 283585ff168SJulian Elischer return (0); 284585ff168SJulian Elischer } 285585ff168SJulian Elischer 286585ff168SJulian Elischer /* 287585ff168SJulian Elischer * Add a hook 288585ff168SJulian Elischer */ 289585ff168SJulian Elischer static int 290585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name) 291585ff168SJulian Elischer { 292d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(node); 293585ff168SJulian Elischer 294585ff168SJulian Elischer if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 295d8f5d037SGleb Smirnoff sc->input = hook; 296585ff168SJulian Elischer } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 297d8f5d037SGleb Smirnoff sc->output = hook; 298585ff168SJulian Elischer sc->output_ifp = 0; 299585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 300585ff168SJulian Elischer } else 301585ff168SJulian Elischer return (EINVAL); 302d8f5d037SGleb Smirnoff 303d8f5d037SGleb Smirnoff return (0); 304d8f5d037SGleb Smirnoff } 305d8f5d037SGleb Smirnoff 306d8f5d037SGleb Smirnoff /* 307d8f5d037SGleb Smirnoff * Hook has been added 308d8f5d037SGleb Smirnoff */ 309d8f5d037SGleb Smirnoff static int 310d8f5d037SGleb Smirnoff ng_source_connect(hook_p hook) 311d8f5d037SGleb Smirnoff { 312d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 313d8f5d037SGleb Smirnoff struct ng_mesg *msg; 314d8f5d037SGleb Smirnoff int dummy_error = 0; 315d8f5d037SGleb Smirnoff 316d8f5d037SGleb Smirnoff /* 317d8f5d037SGleb Smirnoff * If this is "output" hook, then request information 318d8f5d037SGleb Smirnoff * from our downstream. 319d8f5d037SGleb Smirnoff */ 320d8f5d037SGleb Smirnoff if (hook == sc->output) { 321d8f5d037SGleb Smirnoff NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME, 322d8f5d037SGleb Smirnoff 0, M_NOWAIT); 323d8f5d037SGleb Smirnoff if (msg == NULL) 324d8f5d037SGleb Smirnoff return (ENOBUFS); 325d8f5d037SGleb Smirnoff 326d8f5d037SGleb Smirnoff /* 327d8f5d037SGleb Smirnoff * Our hook and peer hook have HK_INVALID flag set, 328d8f5d037SGleb Smirnoff * so we can't use NG_SEND_MSG_HOOK() macro here. 329d8f5d037SGleb Smirnoff */ 330d8f5d037SGleb Smirnoff NG_SEND_MSG_ID(dummy_error, sc->node, msg, 331d8f5d037SGleb Smirnoff NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node)); 332d8f5d037SGleb Smirnoff } 333d8f5d037SGleb Smirnoff 334585ff168SJulian Elischer return (0); 335585ff168SJulian Elischer } 336585ff168SJulian Elischer 337585ff168SJulian Elischer /* 338585ff168SJulian Elischer * Receive a control message 339585ff168SJulian Elischer */ 340585ff168SJulian Elischer static int 3415968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 342585ff168SJulian Elischer { 343d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(node); 344d8f5d037SGleb Smirnoff struct ng_mesg *msg, *resp = NULL; 345585ff168SJulian Elischer int error = 0; 346585ff168SJulian Elischer 3475968e29eSJulian Elischer NGI_GET_MSG(item, msg); 348d8f5d037SGleb Smirnoff 349585ff168SJulian Elischer switch (msg->header.typecookie) { 350585ff168SJulian Elischer case NGM_SOURCE_COOKIE: 351b655e33dSJulian Elischer if (msg->header.flags & NGF_RESP) { 352b655e33dSJulian Elischer error = EINVAL; 353b655e33dSJulian Elischer break; 354b655e33dSJulian Elischer } 355585ff168SJulian Elischer switch (msg->header.cmd) { 356585ff168SJulian Elischer case NGM_SOURCE_GET_STATS: 357585ff168SJulian Elischer case NGM_SOURCE_CLR_STATS: 358585ff168SJulian Elischer case NGM_SOURCE_GETCLR_STATS: 359585ff168SJulian Elischer { 360585ff168SJulian Elischer struct ng_source_stats *stats; 361585ff168SJulian Elischer 362585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 363585ff168SJulian Elischer NG_MKRESPONSE(resp, msg, 364585ff168SJulian Elischer sizeof(*stats), M_NOWAIT); 365585ff168SJulian Elischer if (resp == NULL) { 366585ff168SJulian Elischer error = ENOMEM; 367585ff168SJulian Elischer goto done; 368585ff168SJulian Elischer } 369585ff168SJulian Elischer sc->stats.queueOctets = sc->queueOctets; 3704b52f283SJulian Elischer sc->stats.queueFrames = sc->snd_queue.ifq_len; 3715968e29eSJulian Elischer if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 372585ff168SJulian Elischer && !timevalisset(&sc->stats.endTime)) { 373585ff168SJulian Elischer getmicrotime(&sc->stats.elapsedTime); 374585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, 375585ff168SJulian Elischer &sc->stats.startTime); 376585ff168SJulian Elischer } 3774b52f283SJulian Elischer stats = (struct ng_source_stats *)resp->data; 378585ff168SJulian Elischer bcopy(&sc->stats, stats, sizeof(* stats)); 379585ff168SJulian Elischer } 380585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_GET_STATS) 381585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 382585ff168SJulian Elischer } 383585ff168SJulian Elischer break; 384585ff168SJulian Elischer case NGM_SOURCE_START: 385585ff168SJulian Elischer { 386d8f5d037SGleb Smirnoff uint64_t packets; 387f5d15522SHartmut Brandt 388d8f5d037SGleb Smirnoff if (msg->header.arglen != sizeof(uint64_t)) { 389d8f5d037SGleb Smirnoff error = EINVAL; 390f5d15522SHartmut Brandt break; 391d8f5d037SGleb Smirnoff } 392d8f5d037SGleb Smirnoff 393d8f5d037SGleb Smirnoff packets = *(uint64_t *)msg->data; 394d8f5d037SGleb Smirnoff 395d8f5d037SGleb Smirnoff error = ng_source_start(sc, packets); 396d8f5d037SGleb Smirnoff 397d8f5d037SGleb Smirnoff break; 398d8f5d037SGleb Smirnoff } 399585ff168SJulian Elischer case NGM_SOURCE_STOP: 400585ff168SJulian Elischer ng_source_stop(sc); 401585ff168SJulian Elischer break; 402585ff168SJulian Elischer case NGM_SOURCE_CLR_DATA: 403585ff168SJulian Elischer ng_source_clr_data(sc); 404585ff168SJulian Elischer break; 405d8f5d037SGleb Smirnoff case NGM_SOURCE_SETIFACE: 406d8f5d037SGleb Smirnoff { 407d8f5d037SGleb Smirnoff char *ifname = (char *)msg->data; 408d8f5d037SGleb Smirnoff 409d8f5d037SGleb Smirnoff if (msg->header.arglen < 2) { 410d8f5d037SGleb Smirnoff error = EINVAL; 411d8f5d037SGleb Smirnoff break; 412d8f5d037SGleb Smirnoff } 413d8f5d037SGleb Smirnoff 414d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc, ifname); 415d8f5d037SGleb Smirnoff break; 416d8f5d037SGleb Smirnoff } 41772235857SGleb Smirnoff case NGM_SOURCE_SETPPS: 41872235857SGleb Smirnoff { 41972235857SGleb Smirnoff uint32_t pps; 42072235857SGleb Smirnoff 42172235857SGleb Smirnoff if (msg->header.arglen != sizeof(uint32_t)) { 42272235857SGleb Smirnoff error = EINVAL; 42372235857SGleb Smirnoff break; 42472235857SGleb Smirnoff } 42572235857SGleb Smirnoff 42672235857SGleb Smirnoff pps = *(uint32_t *)msg->data; 42772235857SGleb Smirnoff 42872235857SGleb Smirnoff sc->stats.maxPps = pps; 42972235857SGleb Smirnoff 43072235857SGleb Smirnoff break; 43172235857SGleb Smirnoff } 4325f87dd69SEd Maste case NGM_SOURCE_SET_TIMESTAMP: 4335f87dd69SEd Maste { 4345f87dd69SEd Maste struct ng_source_embed_info *embed; 4355f87dd69SEd Maste 436ab2e868cSEd Maste if (msg->header.arglen != sizeof(*embed)) { 437ab2e868cSEd Maste error = EINVAL; 438ab2e868cSEd Maste goto done; 439ab2e868cSEd Maste } 4405f87dd69SEd Maste embed = (struct ng_source_embed_info *)msg->data; 4415f87dd69SEd Maste bcopy(embed, &sc->embed_timestamp, sizeof(*embed)); 4425f87dd69SEd Maste 4435f87dd69SEd Maste break; 4445f87dd69SEd Maste } 4455f87dd69SEd Maste case NGM_SOURCE_GET_TIMESTAMP: 4465f87dd69SEd Maste { 4475f87dd69SEd Maste struct ng_source_embed_info *embed; 4485f87dd69SEd Maste 4495f87dd69SEd Maste NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT); 4505f87dd69SEd Maste if (resp == NULL) { 4515f87dd69SEd Maste error = ENOMEM; 4525f87dd69SEd Maste goto done; 4535f87dd69SEd Maste } 4545f87dd69SEd Maste embed = (struct ng_source_embed_info *)resp->data; 4555f87dd69SEd Maste bcopy(&sc->embed_timestamp, embed, sizeof(*embed)); 4565f87dd69SEd Maste 4575f87dd69SEd Maste break; 4585f87dd69SEd Maste } 459577421ebSEd Maste case NGM_SOURCE_SET_COUNTER: 460577421ebSEd Maste { 461577421ebSEd Maste struct ng_source_embed_cnt_info *embed; 462577421ebSEd Maste 463ab2e868cSEd Maste if (msg->header.arglen != sizeof(*embed)) { 464ab2e868cSEd Maste error = EINVAL; 465ab2e868cSEd Maste goto done; 466ab2e868cSEd Maste } 467577421ebSEd Maste embed = (struct ng_source_embed_cnt_info *)msg->data; 468577421ebSEd Maste if (embed->index >= NG_SOURCE_COUNTERS || 469577421ebSEd Maste !(embed->width == 1 || embed->width == 2 || 470577421ebSEd Maste embed->width == 4)) { 471577421ebSEd Maste error = EINVAL; 472577421ebSEd Maste goto done; 473577421ebSEd Maste } 474577421ebSEd Maste bcopy(embed, &sc->embed_counter[embed->index], 475577421ebSEd Maste sizeof(*embed)); 476577421ebSEd Maste 477577421ebSEd Maste break; 478577421ebSEd Maste } 479577421ebSEd Maste case NGM_SOURCE_GET_COUNTER: 480577421ebSEd Maste { 481577421ebSEd Maste uint8_t index = *(uint8_t *)msg->data; 482577421ebSEd Maste struct ng_source_embed_cnt_info *embed; 483577421ebSEd Maste 484577421ebSEd Maste if (index >= NG_SOURCE_COUNTERS) { 485577421ebSEd Maste error = EINVAL; 486577421ebSEd Maste goto done; 487577421ebSEd Maste } 488577421ebSEd Maste NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT); 489577421ebSEd Maste if (resp == NULL) { 490577421ebSEd Maste error = ENOMEM; 491577421ebSEd Maste goto done; 492577421ebSEd Maste } 493577421ebSEd Maste embed = (struct ng_source_embed_cnt_info *)resp->data; 494577421ebSEd Maste bcopy(&sc->embed_counter[index], embed, sizeof(*embed)); 495577421ebSEd Maste 496577421ebSEd Maste break; 497577421ebSEd Maste } 498585ff168SJulian Elischer default: 499585ff168SJulian Elischer error = EINVAL; 500585ff168SJulian Elischer break; 501585ff168SJulian Elischer } 502585ff168SJulian Elischer break; 503b655e33dSJulian Elischer case NGM_ETHER_COOKIE: 504b655e33dSJulian Elischer if (!(msg->header.flags & NGF_RESP)) { 505b655e33dSJulian Elischer error = EINVAL; 506b655e33dSJulian Elischer break; 507b655e33dSJulian Elischer } 508b655e33dSJulian Elischer switch (msg->header.cmd) { 509d8f5d037SGleb Smirnoff case NGM_ETHER_GET_IFNAME: 510d8f5d037SGleb Smirnoff { 511d8f5d037SGleb Smirnoff char *ifname = (char *)msg->data; 512d8f5d037SGleb Smirnoff 513d8f5d037SGleb Smirnoff if (msg->header.arglen < 2) { 514d8f5d037SGleb Smirnoff error = EINVAL; 515b655e33dSJulian Elischer break; 516d8f5d037SGleb Smirnoff } 517d8f5d037SGleb Smirnoff 518d8f5d037SGleb Smirnoff if (ng_source_store_output_ifp(sc, ifname) == 0) 519d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc, 0); 520d8f5d037SGleb Smirnoff break; 521d8f5d037SGleb Smirnoff } 522b655e33dSJulian Elischer default: 523b655e33dSJulian Elischer error = EINVAL; 524b655e33dSJulian Elischer } 525b655e33dSJulian Elischer break; 526585ff168SJulian Elischer default: 527585ff168SJulian Elischer error = EINVAL; 528585ff168SJulian Elischer break; 529585ff168SJulian Elischer } 530585ff168SJulian Elischer 531585ff168SJulian Elischer done: 532d8f5d037SGleb Smirnoff /* Take care of synchronous response, if any. */ 5335968e29eSJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 534d8f5d037SGleb Smirnoff /* Free the message and return. */ 5355968e29eSJulian Elischer NG_FREE_MSG(msg); 536585ff168SJulian Elischer return (error); 537585ff168SJulian Elischer } 538585ff168SJulian Elischer 539585ff168SJulian Elischer /* 540585ff168SJulian Elischer * Receive data on a hook 541585ff168SJulian Elischer * 542585ff168SJulian Elischer * If data comes in the input hook, enqueue it on the send queue. 543585ff168SJulian Elischer * If data comes in the output hook, discard it. 544585ff168SJulian Elischer */ 545585ff168SJulian Elischer static int 5465968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item) 547585ff168SJulian Elischer { 548d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 5495968e29eSJulian Elischer struct mbuf *m; 550d8f5d037SGleb Smirnoff int error = 0; 551585ff168SJulian Elischer 5525968e29eSJulian Elischer NGI_GET_M(item, m); 5535968e29eSJulian Elischer NG_FREE_ITEM(item); 554585ff168SJulian Elischer 555585ff168SJulian Elischer /* Which hook? */ 556d8f5d037SGleb Smirnoff if (hook == sc->output) { 557585ff168SJulian Elischer /* discard */ 5585968e29eSJulian Elischer NG_FREE_M(m); 559585ff168SJulian Elischer return (error); 560585ff168SJulian Elischer } 561d8f5d037SGleb Smirnoff KASSERT(hook == sc->input, ("%s: no hook!", __func__)); 562585ff168SJulian Elischer 563d8f5d037SGleb Smirnoff /* Enqueue packet. */ 564585ff168SJulian Elischer /* XXX should we check IF_QFULL() ? */ 56581a4ef81SHartmut Brandt _IF_ENQUEUE(&sc->snd_queue, m); 566585ff168SJulian Elischer sc->queueOctets += m->m_pkthdr.len; 567577421ebSEd Maste sc->last_packet = m; 568585ff168SJulian Elischer 569585ff168SJulian Elischer return (0); 570585ff168SJulian Elischer } 571585ff168SJulian Elischer 572585ff168SJulian Elischer /* 573585ff168SJulian Elischer * Shutdown processing 574585ff168SJulian Elischer */ 575585ff168SJulian Elischer static int 576585ff168SJulian Elischer ng_source_rmnode(node_p node) 577585ff168SJulian Elischer { 578d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(node); 579585ff168SJulian Elischer 580585ff168SJulian Elischer ng_source_stop(sc); 581585ff168SJulian Elischer ng_source_clr_data(sc); 5825968e29eSJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 5835968e29eSJulian Elischer NG_NODE_UNREF(node); 58476bd5857SHartmut Brandt free(sc, M_NETGRAPH); 585d8f5d037SGleb Smirnoff 586585ff168SJulian Elischer return (0); 587585ff168SJulian Elischer } 588585ff168SJulian Elischer 589585ff168SJulian Elischer /* 590585ff168SJulian Elischer * Hook disconnection 591585ff168SJulian Elischer */ 592585ff168SJulian Elischer static int 593585ff168SJulian Elischer ng_source_disconnect(hook_p hook) 594585ff168SJulian Elischer { 5954b52f283SJulian Elischer sc_p sc; 596585ff168SJulian Elischer 5975968e29eSJulian Elischer sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 59876bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 599d8f5d037SGleb Smirnoff if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output) 6005968e29eSJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); 601585ff168SJulian Elischer return (0); 602585ff168SJulian Elischer } 603585ff168SJulian Elischer 604585ff168SJulian Elischer /* 6056bccea7cSRebecca Cran * Set sc->output_ifp to point to the struct ifnet of the interface 606b655e33dSJulian Elischer * reached via our output hook. 607b655e33dSJulian Elischer */ 608b655e33dSJulian Elischer static int 609d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc_p sc, char *ifname) 610b655e33dSJulian Elischer { 611b655e33dSJulian Elischer struct ifnet *ifp; 612b655e33dSJulian Elischer int s; 613b655e33dSJulian Elischer 614d8f5d037SGleb Smirnoff ifp = ifunit(ifname); 615585ff168SJulian Elischer 616585ff168SJulian Elischer if (ifp == NULL) { 6173ac12c59SMarko Zec printf("%s: can't find interface %s\n", __func__, ifname); 618585ff168SJulian Elischer return (EINVAL); 619585ff168SJulian Elischer } 620585ff168SJulian Elischer sc->output_ifp = ifp; 621585ff168SJulian Elischer 622585ff168SJulian Elischer #if 1 623585ff168SJulian Elischer /* XXX mucking with a drivers ifqueue size is ugly but we need it 624585ff168SJulian Elischer * to queue a lot of packets to get close to line rate on a gigabit 625585ff168SJulian Elischer * interface with small packets. 626585ff168SJulian Elischer * XXX we should restore the original value at stop or disconnect 627585ff168SJulian Elischer */ 628585ff168SJulian Elischer s = splimp(); /* XXX is this required? */ 62976bd5857SHartmut Brandt if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) { 630585ff168SJulian Elischer printf("ng_source: changing ifq_maxlen from %d to %d\n", 6314b52f283SJulian Elischer ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 632585ff168SJulian Elischer ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 633585ff168SJulian Elischer } 634585ff168SJulian Elischer splx(s); 635585ff168SJulian Elischer #endif 636585ff168SJulian Elischer return (0); 637585ff168SJulian Elischer } 638585ff168SJulian Elischer 639585ff168SJulian Elischer /* 640585ff168SJulian Elischer * Set the attached ethernet node's ethernet source address override flag. 641585ff168SJulian Elischer */ 642585ff168SJulian Elischer static int 643d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc_p sc, uint32_t flag) 644585ff168SJulian Elischer { 645585ff168SJulian Elischer struct ng_mesg *msg; 646585ff168SJulian Elischer int error = 0; 647585ff168SJulian Elischer 648585ff168SJulian Elischer NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 649d8f5d037SGleb Smirnoff sizeof (uint32_t), M_NOWAIT); 650585ff168SJulian Elischer if (msg == NULL) 651585ff168SJulian Elischer return(ENOBUFS); 652585ff168SJulian Elischer 653d8f5d037SGleb Smirnoff *(uint32_t *)msg->data = flag; 654d8f5d037SGleb Smirnoff NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0); 655585ff168SJulian Elischer return (error); 656585ff168SJulian Elischer } 657585ff168SJulian Elischer 658585ff168SJulian Elischer /* 659585ff168SJulian Elischer * Clear out the data we've queued 660585ff168SJulian Elischer */ 661585ff168SJulian Elischer static void 662585ff168SJulian Elischer ng_source_clr_data (sc_p sc) 663585ff168SJulian Elischer { 664585ff168SJulian Elischer struct mbuf *m; 665585ff168SJulian Elischer 666585ff168SJulian Elischer for (;;) { 66781a4ef81SHartmut Brandt _IF_DEQUEUE(&sc->snd_queue, m); 668585ff168SJulian Elischer if (m == NULL) 669585ff168SJulian Elischer break; 670585ff168SJulian Elischer NG_FREE_M(m); 671585ff168SJulian Elischer } 672585ff168SJulian Elischer sc->queueOctets = 0; 673ab2e868cSEd Maste sc->last_packet = NULL; 674585ff168SJulian Elischer } 675585ff168SJulian Elischer 676585ff168SJulian Elischer /* 677585ff168SJulian Elischer * Start sending queued data out the output hook 678585ff168SJulian Elischer */ 679d8f5d037SGleb Smirnoff static int 680d8f5d037SGleb Smirnoff ng_source_start(sc_p sc, uint64_t packets) 681585ff168SJulian Elischer { 682d8f5d037SGleb Smirnoff if (sc->output_ifp == NULL) { 683d8f5d037SGleb Smirnoff printf("ng_source: start without iface configured\n"); 684d8f5d037SGleb Smirnoff return (ENXIO); 685d8f5d037SGleb Smirnoff } 686d8f5d037SGleb Smirnoff 687d8f5d037SGleb Smirnoff if (sc->node->nd_flags & NG_SOURCE_ACTIVE) 688d8f5d037SGleb Smirnoff return (EBUSY); 689d8f5d037SGleb Smirnoff 690d8f5d037SGleb Smirnoff sc->node->nd_flags |= NG_SOURCE_ACTIVE; 691d8f5d037SGleb Smirnoff 692d8f5d037SGleb Smirnoff sc->packets = packets; 693d8f5d037SGleb Smirnoff timevalclear(&sc->stats.elapsedTime); 694d8f5d037SGleb Smirnoff timevalclear(&sc->stats.endTime); 695d8f5d037SGleb Smirnoff getmicrotime(&sc->stats.startTime); 69672235857SGleb Smirnoff getmicrotime(&sc->stats.lastTime); 697d8f5d037SGleb Smirnoff ng_callout(&sc->intr_ch, sc->node, NULL, 0, 698d8f5d037SGleb Smirnoff ng_source_intr, sc, 0); 699d8f5d037SGleb Smirnoff 700d8f5d037SGleb Smirnoff return (0); 701585ff168SJulian Elischer } 702585ff168SJulian Elischer 703585ff168SJulian Elischer /* 704585ff168SJulian Elischer * Stop sending queued data out the output hook 705585ff168SJulian Elischer */ 706585ff168SJulian Elischer static void 707585ff168SJulian Elischer ng_source_stop(sc_p sc) 708585ff168SJulian Elischer { 709f9d9e1b4SGleb Smirnoff ng_uncallout(&sc->intr_ch, sc->node); 7105968e29eSJulian Elischer sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 711585ff168SJulian Elischer getmicrotime(&sc->stats.endTime); 712585ff168SJulian Elischer sc->stats.elapsedTime = sc->stats.endTime; 713585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 714585ff168SJulian Elischer } 715585ff168SJulian Elischer 716585ff168SJulian Elischer /* 717585ff168SJulian Elischer * While active called every NG_SOURCE_INTR_TICKS ticks. 718585ff168SJulian Elischer * Sends as many packets as the interface connected to our 719585ff168SJulian Elischer * output hook is able to enqueue. 720585ff168SJulian Elischer */ 721585ff168SJulian Elischer static void 722a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2) 723585ff168SJulian Elischer { 724a1adb510SHartmut Brandt sc_p sc = (sc_p)arg1; 725585ff168SJulian Elischer struct ifqueue *ifq; 726585ff168SJulian Elischer int packets; 727585ff168SJulian Elischer 72876bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 729585ff168SJulian Elischer 730d8f5d037SGleb Smirnoff if (sc->packets == 0 || sc->output == NULL 7315968e29eSJulian Elischer || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 732585ff168SJulian Elischer ng_source_stop(sc); 733585ff168SJulian Elischer return; 734585ff168SJulian Elischer } 735585ff168SJulian Elischer 736f5d15522SHartmut Brandt if (sc->output_ifp != NULL) { 7370572dfacSRuslan Ermilov ifq = (struct ifqueue *)&sc->output_ifp->if_snd; 738585ff168SJulian Elischer packets = ifq->ifq_maxlen - ifq->ifq_len; 739f5d15522SHartmut Brandt } else 740f5d15522SHartmut Brandt packets = sc->snd_queue.ifq_len; 741f5d15522SHartmut Brandt 74272235857SGleb Smirnoff if (sc->stats.maxPps != 0) { 74372235857SGleb Smirnoff struct timeval now, elapsed; 74472235857SGleb Smirnoff uint64_t usec; 74572235857SGleb Smirnoff int maxpkt; 74672235857SGleb Smirnoff 74772235857SGleb Smirnoff getmicrotime(&now); 74872235857SGleb Smirnoff elapsed = now; 74972235857SGleb Smirnoff timevalsub(&elapsed, &sc->stats.lastTime); 75072235857SGleb Smirnoff usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec; 75172235857SGleb Smirnoff maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000; 75272235857SGleb Smirnoff sc->stats.lastTime = now; 75372235857SGleb Smirnoff if (packets > maxpkt) 75472235857SGleb Smirnoff packets = maxpkt; 75572235857SGleb Smirnoff } 75672235857SGleb Smirnoff 757585ff168SJulian Elischer ng_source_send(sc, packets, NULL); 758a1adb510SHartmut Brandt if (sc->packets == 0) 759585ff168SJulian Elischer ng_source_stop(sc); 760a1adb510SHartmut Brandt else 761f9d9e1b4SGleb Smirnoff ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS, 762d312eaf5SGleb Smirnoff ng_source_intr, sc, 0); 763585ff168SJulian Elischer } 764585ff168SJulian Elischer 765585ff168SJulian Elischer /* 766205aefa3SGleb Smirnoff * Send packets out our output hook. 767585ff168SJulian Elischer */ 768585ff168SJulian Elischer static int 769585ff168SJulian Elischer ng_source_send(sc_p sc, int tosend, int *sent_p) 770585ff168SJulian Elischer { 771585ff168SJulian Elischer struct mbuf *m, *m2; 772205aefa3SGleb Smirnoff int sent; 773585ff168SJulian Elischer int error = 0; 774585ff168SJulian Elischer 77576bd5857SHartmut Brandt KASSERT(tosend >= 0, ("%s: negative tosend param", __func__)); 7765968e29eSJulian Elischer KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 77776bd5857SHartmut Brandt ("%s: inactive node", __func__)); 778585ff168SJulian Elischer 779d8f5d037SGleb Smirnoff if ((uint64_t)tosend > sc->packets) 780585ff168SJulian Elischer tosend = sc->packets; 781585ff168SJulian Elischer 782205aefa3SGleb Smirnoff /* Go through the queue sending packets one by one. */ 783585ff168SJulian Elischer for (sent = 0; error == 0 && sent < tosend; ++sent) { 78481a4ef81SHartmut Brandt _IF_DEQUEUE(&sc->snd_queue, m); 785585ff168SJulian Elischer if (m == NULL) 786585ff168SJulian Elischer break; 787585ff168SJulian Elischer 7885f87dd69SEd Maste /* Duplicate and modify the packet. */ 7895f87dd69SEd Maste error = ng_source_dup_mod(sc, m, &m2); 7905f87dd69SEd Maste if (error) { 7915f87dd69SEd Maste if (error == ENOBUFS) 79281a4ef81SHartmut Brandt _IF_PREPEND(&sc->snd_queue, m); 7935f87dd69SEd Maste else 7945f87dd69SEd Maste _IF_ENQUEUE(&sc->snd_queue, m); 795585ff168SJulian Elischer break; 796585ff168SJulian Elischer } 797585ff168SJulian Elischer 798d8f5d037SGleb Smirnoff /* Re-enqueue the original packet for us. */ 79981a4ef81SHartmut Brandt _IF_ENQUEUE(&sc->snd_queue, m); 800585ff168SJulian Elischer 801585ff168SJulian Elischer sc->stats.outFrames++; 802585ff168SJulian Elischer sc->stats.outOctets += m2->m_pkthdr.len; 803d8f5d037SGleb Smirnoff NG_SEND_DATA_ONLY(error, sc->output, m2); 804a1adb510SHartmut Brandt if (error) 805205aefa3SGleb Smirnoff break; 806585ff168SJulian Elischer } 807585ff168SJulian Elischer 808585ff168SJulian Elischer sc->packets -= sent; 809585ff168SJulian Elischer if (sent_p != NULL) 810585ff168SJulian Elischer *sent_p = sent; 811585ff168SJulian Elischer return (error); 812585ff168SJulian Elischer } 8135f87dd69SEd Maste 8145f87dd69SEd Maste /* 8155f87dd69SEd Maste * Modify packet in 'm' by changing 'len' bytes starting at 'offset' 8165f87dd69SEd Maste * to data in 'cp'. 8175f87dd69SEd Maste * 8185f87dd69SEd Maste * The packet data in 'm' must be in a contiguous buffer in a single mbuf. 8195f87dd69SEd Maste */ 8205f87dd69SEd Maste static void 8215f87dd69SEd Maste ng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp, 8225f87dd69SEd Maste int flags) 8235f87dd69SEd Maste { 8245f87dd69SEd Maste if (len == 0) 8255f87dd69SEd Maste return; 8265f87dd69SEd Maste 8275f87dd69SEd Maste /* Can't modify beyond end of packet. */ 8285f87dd69SEd Maste /* TODO: Pad packet for this case. */ 8295f87dd69SEd Maste if (offset + len > m->m_len) 8305f87dd69SEd Maste return; 8315f87dd69SEd Maste 8325f87dd69SEd Maste bcopy(cp, mtod_off(m, offset, caddr_t), len); 8335f87dd69SEd Maste } 8345f87dd69SEd Maste 835577421ebSEd Maste static void 836577421ebSEd Maste ng_source_mod_counter(sc_p sc, struct ng_source_embed_cnt_info *cnt, 837577421ebSEd Maste struct mbuf *m, int increment) 838577421ebSEd Maste { 839577421ebSEd Maste caddr_t cp; 840577421ebSEd Maste uint32_t val; 841577421ebSEd Maste 842577421ebSEd Maste val = htonl(cnt->next_val); 843577421ebSEd Maste cp = (caddr_t)&val + sizeof(val) - cnt->width; 844577421ebSEd Maste ng_source_packet_mod(sc, m, cnt->offset, cnt->width, cp, cnt->flags); 845577421ebSEd Maste 846577421ebSEd Maste if (increment) { 847577421ebSEd Maste cnt->next_val += increment; 848577421ebSEd Maste 849577421ebSEd Maste if (increment > 0 && cnt->next_val > cnt->max_val) { 850577421ebSEd Maste cnt->next_val = cnt->min_val - 1 + 851577421ebSEd Maste (cnt->next_val - cnt->max_val); 852577421ebSEd Maste if (cnt->next_val > cnt->max_val) 853577421ebSEd Maste cnt->next_val = cnt->max_val; 854577421ebSEd Maste } else if (increment < 0 && cnt->next_val < cnt->min_val) { 855577421ebSEd Maste cnt->next_val = cnt->max_val + 1 + 856577421ebSEd Maste (cnt->next_val - cnt->min_val); 857577421ebSEd Maste if (cnt->next_val < cnt->min_val) 858577421ebSEd Maste cnt->next_val = cnt->max_val; 859577421ebSEd Maste } 860577421ebSEd Maste } 861577421ebSEd Maste } 862577421ebSEd Maste 8635f87dd69SEd Maste static int 8645f87dd69SEd Maste ng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr) 8655f87dd69SEd Maste { 8665f87dd69SEd Maste struct mbuf *m; 867577421ebSEd Maste struct ng_source_embed_cnt_info *cnt; 8685f87dd69SEd Maste struct ng_source_embed_info *ts; 8695f87dd69SEd Maste int modify; 8705f87dd69SEd Maste int error = 0; 871577421ebSEd Maste int i, increment; 8725f87dd69SEd Maste 8735f87dd69SEd Maste /* Are we going to modify packets? */ 8745f87dd69SEd Maste modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE; 875577421ebSEd Maste for (i = 0; !modify && i < NG_SOURCE_COUNTERS; ++i) 876577421ebSEd Maste modify = sc->embed_counter[i].flags & NGM_SOURCE_EMBED_ENABLE; 8775f87dd69SEd Maste 8785f87dd69SEd Maste /* Duplicate the packet. */ 8795f87dd69SEd Maste if (modify) 8805f87dd69SEd Maste m = m_dup(m0, M_DONTWAIT); 8815f87dd69SEd Maste else 8825f87dd69SEd Maste m = m_copypacket(m0, M_DONTWAIT); 8835f87dd69SEd Maste if (m == NULL) { 8845f87dd69SEd Maste error = ENOBUFS; 8855f87dd69SEd Maste goto done; 8865f87dd69SEd Maste } 8875f87dd69SEd Maste *m_ptr = m; 8885f87dd69SEd Maste 8895f87dd69SEd Maste if (!modify) 8905f87dd69SEd Maste goto done; 8915f87dd69SEd Maste 8925f87dd69SEd Maste /* Modify the copied packet for sending. */ 8935f87dd69SEd Maste KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__)); 8945f87dd69SEd Maste 895577421ebSEd Maste for (i = 0; i < NG_SOURCE_COUNTERS; ++i) { 896577421ebSEd Maste cnt = &sc->embed_counter[i]; 897577421ebSEd Maste if (cnt->flags & NGM_SOURCE_EMBED_ENABLE) { 898577421ebSEd Maste if ((cnt->flags & NGM_SOURCE_INC_CNT_PER_LIST) == 0 || 899577421ebSEd Maste sc->last_packet == m0) 900577421ebSEd Maste increment = cnt->increment; 901577421ebSEd Maste else 902577421ebSEd Maste increment = 0; 903577421ebSEd Maste ng_source_mod_counter(sc, cnt, m, increment); 904577421ebSEd Maste } 905577421ebSEd Maste } 906577421ebSEd Maste 9075f87dd69SEd Maste ts = &sc->embed_timestamp; 9085f87dd69SEd Maste if (ts->flags & NGM_SOURCE_EMBED_ENABLE) { 9095f87dd69SEd Maste struct timeval now; 9105f87dd69SEd Maste getmicrotime(&now); 9115f87dd69SEd Maste now.tv_sec = htonl(now.tv_sec); 9125f87dd69SEd Maste now.tv_usec = htonl(now.tv_usec); 9135f87dd69SEd Maste ng_source_packet_mod(sc, m, ts->offset, sizeof (now), 9145f87dd69SEd Maste (caddr_t)&now, ts->flags); 9155f87dd69SEd Maste } 9165f87dd69SEd Maste 9175f87dd69SEd Maste done: 9185f87dd69SEd Maste return(error); 9195f87dd69SEd Maste } 920