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 * 38585ff168SJulian Elischer * Author: Dave Chapeskie <dchapeskie@sandvine.com> 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> 71585ff168SJulian Elischer #include <netgraph/ng_message.h> 72585ff168SJulian Elischer #include <netgraph/netgraph.h> 73585ff168SJulian Elischer #include <netgraph/ng_parse.h> 74585ff168SJulian Elischer #include <netgraph/ng_ether.h> 75585ff168SJulian Elischer #include <netgraph/ng_source.h> 76585ff168SJulian Elischer 77585ff168SJulian Elischer #define NG_SOURCE_INTR_TICKS 1 78585ff168SJulian Elischer #define NG_SOURCE_DRIVER_IFQ_MAXLEN (4*1024) 79585ff168SJulian Elischer 805f87dd69SEd Maste #define mtod_off(m,off,t) ((t)(mtod((m),caddr_t)+(off))) 815f87dd69SEd Maste 82585ff168SJulian Elischer /* Per node info */ 83585ff168SJulian Elischer struct privdata { 84585ff168SJulian Elischer node_p node; 85d8f5d037SGleb Smirnoff hook_p input; 86d8f5d037SGleb Smirnoff hook_p output; 87585ff168SJulian Elischer struct ng_source_stats stats; 884b52f283SJulian Elischer struct ifqueue snd_queue; /* packets to send */ 89585ff168SJulian Elischer struct ifnet *output_ifp; 9030bef41bSGleb Smirnoff struct callout intr_ch; 91d8f5d037SGleb Smirnoff uint64_t packets; /* packets to send */ 92d8f5d037SGleb Smirnoff uint32_t queueOctets; 935f87dd69SEd Maste struct ng_source_embed_info embed_timestamp; 94585ff168SJulian Elischer }; 95585ff168SJulian Elischer typedef struct privdata *sc_p; 96585ff168SJulian Elischer 97585ff168SJulian Elischer /* Node flags */ 98585ff168SJulian Elischer #define NG_SOURCE_ACTIVE (NGF_TYPE1) 99585ff168SJulian Elischer 100585ff168SJulian Elischer /* Netgraph methods */ 101585ff168SJulian Elischer static ng_constructor_t ng_source_constructor; 102585ff168SJulian Elischer static ng_rcvmsg_t ng_source_rcvmsg; 103585ff168SJulian Elischer static ng_shutdown_t ng_source_rmnode; 104585ff168SJulian Elischer static ng_newhook_t ng_source_newhook; 105d8f5d037SGleb Smirnoff static ng_connect_t ng_source_connect; 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); 111585ff168SJulian Elischer static void ng_source_clr_data (sc_p); 112d8f5d037SGleb Smirnoff static int ng_source_start (sc_p, uint64_t); 113585ff168SJulian Elischer static void ng_source_stop (sc_p); 114585ff168SJulian Elischer static int ng_source_send (sc_p, int, int *); 115d8f5d037SGleb Smirnoff static int ng_source_store_output_ifp(sc_p, char *); 1165f87dd69SEd Maste static void ng_source_packet_mod(sc_p, struct mbuf *, 1175f87dd69SEd Maste int, int, caddr_t, int); 1185f87dd69SEd Maste static int ng_source_dup_mod(sc_p, struct mbuf *, 1195f87dd69SEd Maste struct mbuf **); 120585ff168SJulian Elischer 121585ff168SJulian Elischer /* Parse type for timeval */ 12276bd5857SHartmut Brandt static const struct ng_parse_struct_field ng_source_timeval_type_fields[] = { 123585ff168SJulian Elischer { "tv_sec", &ng_parse_int32_type }, 124585ff168SJulian Elischer { "tv_usec", &ng_parse_int32_type }, 125585ff168SJulian Elischer { NULL } 126585ff168SJulian Elischer }; 127585ff168SJulian Elischer const struct ng_parse_type ng_source_timeval_type = { 128585ff168SJulian Elischer &ng_parse_struct_type, 129585ff168SJulian Elischer &ng_source_timeval_type_fields 130585ff168SJulian Elischer }; 131585ff168SJulian Elischer 132585ff168SJulian Elischer /* Parse type for struct ng_source_stats */ 133585ff168SJulian Elischer static const struct ng_parse_struct_field ng_source_stats_type_fields[] 134585ff168SJulian Elischer = NG_SOURCE_STATS_TYPE_INFO; 135585ff168SJulian Elischer static const struct ng_parse_type ng_source_stats_type = { 136585ff168SJulian Elischer &ng_parse_struct_type, 137585ff168SJulian Elischer &ng_source_stats_type_fields 138585ff168SJulian Elischer }; 139585ff168SJulian Elischer 1405f87dd69SEd Maste /* Parse type for struct ng_source_embed_info */ 1415f87dd69SEd Maste static const struct ng_parse_struct_field ng_source_embed_type_fields[] = 1425f87dd69SEd Maste NG_SOURCE_EMBED_TYPE_INFO; 1435f87dd69SEd Maste static const struct ng_parse_type ng_source_embed_type = { 1445f87dd69SEd Maste &ng_parse_struct_type, 1455f87dd69SEd Maste &ng_source_embed_type_fields 1465f87dd69SEd Maste }; 1475f87dd69SEd Maste 148585ff168SJulian Elischer /* List of commands and how to convert arguments to/from ASCII */ 149585ff168SJulian Elischer static const struct ng_cmdlist ng_source_cmds[] = { 150585ff168SJulian Elischer { 151585ff168SJulian Elischer NGM_SOURCE_COOKIE, 152585ff168SJulian Elischer NGM_SOURCE_GET_STATS, 153585ff168SJulian Elischer "getstats", 154585ff168SJulian Elischer NULL, 155585ff168SJulian Elischer &ng_source_stats_type 156585ff168SJulian Elischer }, 157585ff168SJulian Elischer { 158585ff168SJulian Elischer NGM_SOURCE_COOKIE, 159585ff168SJulian Elischer NGM_SOURCE_CLR_STATS, 160585ff168SJulian Elischer "clrstats", 161585ff168SJulian Elischer NULL, 162585ff168SJulian Elischer NULL 163585ff168SJulian Elischer }, 164585ff168SJulian Elischer { 165585ff168SJulian Elischer NGM_SOURCE_COOKIE, 166585ff168SJulian Elischer NGM_SOURCE_GETCLR_STATS, 167585ff168SJulian Elischer "getclrstats", 168585ff168SJulian Elischer NULL, 169585ff168SJulian Elischer &ng_source_stats_type 170585ff168SJulian Elischer }, 171585ff168SJulian Elischer { 172585ff168SJulian Elischer NGM_SOURCE_COOKIE, 173585ff168SJulian Elischer NGM_SOURCE_START, 174585ff168SJulian Elischer "start", 175585ff168SJulian Elischer &ng_parse_uint64_type, 176585ff168SJulian Elischer NULL 177585ff168SJulian Elischer }, 178585ff168SJulian Elischer { 179585ff168SJulian Elischer NGM_SOURCE_COOKIE, 180585ff168SJulian Elischer NGM_SOURCE_STOP, 181585ff168SJulian Elischer "stop", 182585ff168SJulian Elischer NULL, 183585ff168SJulian Elischer NULL 184585ff168SJulian Elischer }, 185585ff168SJulian Elischer { 186585ff168SJulian Elischer NGM_SOURCE_COOKIE, 187585ff168SJulian Elischer NGM_SOURCE_CLR_DATA, 188585ff168SJulian Elischer "clrdata", 189585ff168SJulian Elischer NULL, 190585ff168SJulian Elischer NULL 191585ff168SJulian Elischer }, 192f5d15522SHartmut Brandt { 193f5d15522SHartmut Brandt NGM_SOURCE_COOKIE, 194d8f5d037SGleb Smirnoff NGM_SOURCE_SETIFACE, 195d8f5d037SGleb Smirnoff "setiface", 196d8f5d037SGleb Smirnoff &ng_parse_string_type, 197f5d15522SHartmut Brandt NULL 198f5d15522SHartmut Brandt }, 19972235857SGleb Smirnoff { 20072235857SGleb Smirnoff NGM_SOURCE_COOKIE, 20172235857SGleb Smirnoff NGM_SOURCE_SETPPS, 20272235857SGleb Smirnoff "setpps", 20372235857SGleb Smirnoff &ng_parse_uint32_type, 20472235857SGleb Smirnoff NULL 20572235857SGleb Smirnoff }, 2065f87dd69SEd Maste { 2075f87dd69SEd Maste NGM_SOURCE_COOKIE, 2085f87dd69SEd Maste NGM_SOURCE_SET_TIMESTAMP, 2095f87dd69SEd Maste "settimestamp", 2105f87dd69SEd Maste &ng_source_embed_type, 2115f87dd69SEd Maste NULL 2125f87dd69SEd Maste }, 2135f87dd69SEd Maste { 2145f87dd69SEd Maste NGM_SOURCE_COOKIE, 2155f87dd69SEd Maste NGM_SOURCE_GET_TIMESTAMP, 2165f87dd69SEd Maste "gettimestamp", 2175f87dd69SEd Maste NULL, 2185f87dd69SEd Maste &ng_source_embed_type 2195f87dd69SEd Maste }, 220585ff168SJulian Elischer { 0 } 221585ff168SJulian Elischer }; 222585ff168SJulian Elischer 223585ff168SJulian Elischer /* Netgraph type descriptor */ 224585ff168SJulian Elischer static struct ng_type ng_source_typestruct = { 225f8aae777SJulian Elischer .version = NG_ABI_VERSION, 226f8aae777SJulian Elischer .name = NG_SOURCE_NODE_TYPE, 227f8aae777SJulian Elischer .constructor = ng_source_constructor, 228f8aae777SJulian Elischer .rcvmsg = ng_source_rcvmsg, 229f8aae777SJulian Elischer .shutdown = ng_source_rmnode, 230f8aae777SJulian Elischer .newhook = ng_source_newhook, 231d8f5d037SGleb Smirnoff .connect = ng_source_connect, 232f8aae777SJulian Elischer .rcvdata = ng_source_rcvdata, 233f8aae777SJulian Elischer .disconnect = ng_source_disconnect, 234f8aae777SJulian Elischer .cmdlist = ng_source_cmds, 235585ff168SJulian Elischer }; 236585ff168SJulian Elischer NETGRAPH_INIT(source, &ng_source_typestruct); 237585ff168SJulian Elischer 238d8f5d037SGleb Smirnoff static int ng_source_set_autosrc(sc_p, uint32_t); 2392cafef3eSHartmut Brandt 240585ff168SJulian Elischer /* 241585ff168SJulian Elischer * Node constructor 242585ff168SJulian Elischer */ 243585ff168SJulian Elischer static int 2445968e29eSJulian Elischer ng_source_constructor(node_p node) 245585ff168SJulian Elischer { 246585ff168SJulian Elischer sc_p sc; 247585ff168SJulian Elischer 248b1b70498SHartmut Brandt sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 249585ff168SJulian Elischer if (sc == NULL) 250585ff168SJulian Elischer return (ENOMEM); 251585ff168SJulian Elischer 2525968e29eSJulian Elischer NG_NODE_SET_PRIVATE(node, sc); 2535968e29eSJulian Elischer sc->node = node; 254585ff168SJulian Elischer sc->snd_queue.ifq_maxlen = 2048; /* XXX not checked */ 25530bef41bSGleb Smirnoff ng_callout_init(&sc->intr_ch); 25630bef41bSGleb Smirnoff 257585ff168SJulian Elischer return (0); 258585ff168SJulian Elischer } 259585ff168SJulian Elischer 260585ff168SJulian Elischer /* 261585ff168SJulian Elischer * Add a hook 262585ff168SJulian Elischer */ 263585ff168SJulian Elischer static int 264585ff168SJulian Elischer ng_source_newhook(node_p node, hook_p hook, const char *name) 265585ff168SJulian Elischer { 266d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(node); 267585ff168SJulian Elischer 268585ff168SJulian Elischer if (strcmp(name, NG_SOURCE_HOOK_INPUT) == 0) { 269d8f5d037SGleb Smirnoff sc->input = hook; 270585ff168SJulian Elischer } else if (strcmp(name, NG_SOURCE_HOOK_OUTPUT) == 0) { 271d8f5d037SGleb Smirnoff sc->output = hook; 272585ff168SJulian Elischer sc->output_ifp = 0; 273585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 274585ff168SJulian Elischer } else 275585ff168SJulian Elischer return (EINVAL); 276d8f5d037SGleb Smirnoff 277d8f5d037SGleb Smirnoff return (0); 278d8f5d037SGleb Smirnoff } 279d8f5d037SGleb Smirnoff 280d8f5d037SGleb Smirnoff /* 281d8f5d037SGleb Smirnoff * Hook has been added 282d8f5d037SGleb Smirnoff */ 283d8f5d037SGleb Smirnoff static int 284d8f5d037SGleb Smirnoff ng_source_connect(hook_p hook) 285d8f5d037SGleb Smirnoff { 286d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 287d8f5d037SGleb Smirnoff struct ng_mesg *msg; 288d8f5d037SGleb Smirnoff int dummy_error = 0; 289d8f5d037SGleb Smirnoff 290d8f5d037SGleb Smirnoff /* 291d8f5d037SGleb Smirnoff * If this is "output" hook, then request information 292d8f5d037SGleb Smirnoff * from our downstream. 293d8f5d037SGleb Smirnoff */ 294d8f5d037SGleb Smirnoff if (hook == sc->output) { 295d8f5d037SGleb Smirnoff NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME, 296d8f5d037SGleb Smirnoff 0, M_NOWAIT); 297d8f5d037SGleb Smirnoff if (msg == NULL) 298d8f5d037SGleb Smirnoff return (ENOBUFS); 299d8f5d037SGleb Smirnoff 300d8f5d037SGleb Smirnoff /* 301d8f5d037SGleb Smirnoff * Our hook and peer hook have HK_INVALID flag set, 302d8f5d037SGleb Smirnoff * so we can't use NG_SEND_MSG_HOOK() macro here. 303d8f5d037SGleb Smirnoff */ 304d8f5d037SGleb Smirnoff NG_SEND_MSG_ID(dummy_error, sc->node, msg, 305d8f5d037SGleb Smirnoff NG_NODE_ID(NG_PEER_NODE(sc->output)), NG_NODE_ID(sc->node)); 306d8f5d037SGleb Smirnoff } 307d8f5d037SGleb Smirnoff 308585ff168SJulian Elischer return (0); 309585ff168SJulian Elischer } 310585ff168SJulian Elischer 311585ff168SJulian Elischer /* 312585ff168SJulian Elischer * Receive a control message 313585ff168SJulian Elischer */ 314585ff168SJulian Elischer static int 3155968e29eSJulian Elischer ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook) 316585ff168SJulian Elischer { 317d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(node); 318d8f5d037SGleb Smirnoff struct ng_mesg *msg, *resp = NULL; 319585ff168SJulian Elischer int error = 0; 320585ff168SJulian Elischer 3215968e29eSJulian Elischer NGI_GET_MSG(item, msg); 322d8f5d037SGleb Smirnoff 323585ff168SJulian Elischer switch (msg->header.typecookie) { 324585ff168SJulian Elischer case NGM_SOURCE_COOKIE: 325b655e33dSJulian Elischer if (msg->header.flags & NGF_RESP) { 326b655e33dSJulian Elischer error = EINVAL; 327b655e33dSJulian Elischer break; 328b655e33dSJulian Elischer } 329585ff168SJulian Elischer switch (msg->header.cmd) { 330585ff168SJulian Elischer case NGM_SOURCE_GET_STATS: 331585ff168SJulian Elischer case NGM_SOURCE_CLR_STATS: 332585ff168SJulian Elischer case NGM_SOURCE_GETCLR_STATS: 333585ff168SJulian Elischer { 334585ff168SJulian Elischer struct ng_source_stats *stats; 335585ff168SJulian Elischer 336585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_CLR_STATS) { 337585ff168SJulian Elischer NG_MKRESPONSE(resp, msg, 338585ff168SJulian Elischer sizeof(*stats), M_NOWAIT); 339585ff168SJulian Elischer if (resp == NULL) { 340585ff168SJulian Elischer error = ENOMEM; 341585ff168SJulian Elischer goto done; 342585ff168SJulian Elischer } 343585ff168SJulian Elischer sc->stats.queueOctets = sc->queueOctets; 3444b52f283SJulian Elischer sc->stats.queueFrames = sc->snd_queue.ifq_len; 3455968e29eSJulian Elischer if ((sc->node->nd_flags & NG_SOURCE_ACTIVE) 346585ff168SJulian Elischer && !timevalisset(&sc->stats.endTime)) { 347585ff168SJulian Elischer getmicrotime(&sc->stats.elapsedTime); 348585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, 349585ff168SJulian Elischer &sc->stats.startTime); 350585ff168SJulian Elischer } 3514b52f283SJulian Elischer stats = (struct ng_source_stats *)resp->data; 352585ff168SJulian Elischer bcopy(&sc->stats, stats, sizeof(* stats)); 353585ff168SJulian Elischer } 354585ff168SJulian Elischer if (msg->header.cmd != NGM_SOURCE_GET_STATS) 355585ff168SJulian Elischer bzero(&sc->stats, sizeof(sc->stats)); 356585ff168SJulian Elischer } 357585ff168SJulian Elischer break; 358585ff168SJulian Elischer case NGM_SOURCE_START: 359585ff168SJulian Elischer { 360d8f5d037SGleb Smirnoff uint64_t packets; 361f5d15522SHartmut Brandt 362d8f5d037SGleb Smirnoff if (msg->header.arglen != sizeof(uint64_t)) { 363d8f5d037SGleb Smirnoff error = EINVAL; 364f5d15522SHartmut Brandt break; 365d8f5d037SGleb Smirnoff } 366d8f5d037SGleb Smirnoff 367d8f5d037SGleb Smirnoff packets = *(uint64_t *)msg->data; 368d8f5d037SGleb Smirnoff 369d8f5d037SGleb Smirnoff error = ng_source_start(sc, packets); 370d8f5d037SGleb Smirnoff 371d8f5d037SGleb Smirnoff break; 372d8f5d037SGleb Smirnoff } 373585ff168SJulian Elischer case NGM_SOURCE_STOP: 374585ff168SJulian Elischer ng_source_stop(sc); 375585ff168SJulian Elischer break; 376585ff168SJulian Elischer case NGM_SOURCE_CLR_DATA: 377585ff168SJulian Elischer ng_source_clr_data(sc); 378585ff168SJulian Elischer break; 379d8f5d037SGleb Smirnoff case NGM_SOURCE_SETIFACE: 380d8f5d037SGleb Smirnoff { 381d8f5d037SGleb Smirnoff char *ifname = (char *)msg->data; 382d8f5d037SGleb Smirnoff 383d8f5d037SGleb Smirnoff if (msg->header.arglen < 2) { 384d8f5d037SGleb Smirnoff error = EINVAL; 385d8f5d037SGleb Smirnoff break; 386d8f5d037SGleb Smirnoff } 387d8f5d037SGleb Smirnoff 388d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc, ifname); 389d8f5d037SGleb Smirnoff break; 390d8f5d037SGleb Smirnoff } 39172235857SGleb Smirnoff case NGM_SOURCE_SETPPS: 39272235857SGleb Smirnoff { 39372235857SGleb Smirnoff uint32_t pps; 39472235857SGleb Smirnoff 39572235857SGleb Smirnoff if (msg->header.arglen != sizeof(uint32_t)) { 39672235857SGleb Smirnoff error = EINVAL; 39772235857SGleb Smirnoff break; 39872235857SGleb Smirnoff } 39972235857SGleb Smirnoff 40072235857SGleb Smirnoff pps = *(uint32_t *)msg->data; 40172235857SGleb Smirnoff 40272235857SGleb Smirnoff sc->stats.maxPps = pps; 40372235857SGleb Smirnoff 40472235857SGleb Smirnoff break; 40572235857SGleb Smirnoff } 4065f87dd69SEd Maste case NGM_SOURCE_SET_TIMESTAMP: 4075f87dd69SEd Maste { 4085f87dd69SEd Maste struct ng_source_embed_info *embed; 4095f87dd69SEd Maste 4105f87dd69SEd Maste embed = (struct ng_source_embed_info *)msg->data; 4115f87dd69SEd Maste bcopy(embed, &sc->embed_timestamp, sizeof(*embed)); 4125f87dd69SEd Maste 4135f87dd69SEd Maste break; 4145f87dd69SEd Maste } 4155f87dd69SEd Maste case NGM_SOURCE_GET_TIMESTAMP: 4165f87dd69SEd Maste { 4175f87dd69SEd Maste struct ng_source_embed_info *embed; 4185f87dd69SEd Maste 4195f87dd69SEd Maste NG_MKRESPONSE(resp, msg, sizeof(*embed), M_DONTWAIT); 4205f87dd69SEd Maste if (resp == NULL) { 4215f87dd69SEd Maste error = ENOMEM; 4225f87dd69SEd Maste goto done; 4235f87dd69SEd Maste } 4245f87dd69SEd Maste embed = (struct ng_source_embed_info *)resp->data; 4255f87dd69SEd Maste bcopy(&sc->embed_timestamp, embed, sizeof(*embed)); 4265f87dd69SEd Maste 4275f87dd69SEd Maste break; 4285f87dd69SEd Maste } 429585ff168SJulian Elischer default: 430585ff168SJulian Elischer error = EINVAL; 431585ff168SJulian Elischer break; 432585ff168SJulian Elischer } 433585ff168SJulian Elischer break; 434b655e33dSJulian Elischer case NGM_ETHER_COOKIE: 435b655e33dSJulian Elischer if (!(msg->header.flags & NGF_RESP)) { 436b655e33dSJulian Elischer error = EINVAL; 437b655e33dSJulian Elischer break; 438b655e33dSJulian Elischer } 439b655e33dSJulian Elischer switch (msg->header.cmd) { 440d8f5d037SGleb Smirnoff case NGM_ETHER_GET_IFNAME: 441d8f5d037SGleb Smirnoff { 442d8f5d037SGleb Smirnoff char *ifname = (char *)msg->data; 443d8f5d037SGleb Smirnoff 444d8f5d037SGleb Smirnoff if (msg->header.arglen < 2) { 445d8f5d037SGleb Smirnoff error = EINVAL; 446b655e33dSJulian Elischer break; 447d8f5d037SGleb Smirnoff } 448d8f5d037SGleb Smirnoff 449d8f5d037SGleb Smirnoff if (ng_source_store_output_ifp(sc, ifname) == 0) 450d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc, 0); 451d8f5d037SGleb Smirnoff break; 452d8f5d037SGleb Smirnoff } 453b655e33dSJulian Elischer default: 454b655e33dSJulian Elischer error = EINVAL; 455b655e33dSJulian Elischer } 456b655e33dSJulian Elischer break; 457585ff168SJulian Elischer default: 458585ff168SJulian Elischer error = EINVAL; 459585ff168SJulian Elischer break; 460585ff168SJulian Elischer } 461585ff168SJulian Elischer 462585ff168SJulian Elischer done: 463d8f5d037SGleb Smirnoff /* Take care of synchronous response, if any. */ 4645968e29eSJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 465d8f5d037SGleb Smirnoff /* Free the message and return. */ 4665968e29eSJulian Elischer NG_FREE_MSG(msg); 467585ff168SJulian Elischer return (error); 468585ff168SJulian Elischer } 469585ff168SJulian Elischer 470585ff168SJulian Elischer /* 471585ff168SJulian Elischer * Receive data on a hook 472585ff168SJulian Elischer * 473585ff168SJulian Elischer * If data comes in the input hook, enqueue it on the send queue. 474585ff168SJulian Elischer * If data comes in the output hook, discard it. 475585ff168SJulian Elischer */ 476585ff168SJulian Elischer static int 4775968e29eSJulian Elischer ng_source_rcvdata(hook_p hook, item_p item) 478585ff168SJulian Elischer { 479d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 4805968e29eSJulian Elischer struct mbuf *m; 481d8f5d037SGleb Smirnoff int error = 0; 482585ff168SJulian Elischer 4835968e29eSJulian Elischer NGI_GET_M(item, m); 4845968e29eSJulian Elischer NG_FREE_ITEM(item); 485585ff168SJulian Elischer 486585ff168SJulian Elischer /* Which hook? */ 487d8f5d037SGleb Smirnoff if (hook == sc->output) { 488585ff168SJulian Elischer /* discard */ 4895968e29eSJulian Elischer NG_FREE_M(m); 490585ff168SJulian Elischer return (error); 491585ff168SJulian Elischer } 492d8f5d037SGleb Smirnoff KASSERT(hook == sc->input, ("%s: no hook!", __func__)); 493585ff168SJulian Elischer 494d8f5d037SGleb Smirnoff /* Enqueue packet. */ 495585ff168SJulian Elischer /* XXX should we check IF_QFULL() ? */ 49681a4ef81SHartmut Brandt _IF_ENQUEUE(&sc->snd_queue, m); 497585ff168SJulian Elischer sc->queueOctets += m->m_pkthdr.len; 498585ff168SJulian Elischer 499585ff168SJulian Elischer return (0); 500585ff168SJulian Elischer } 501585ff168SJulian Elischer 502585ff168SJulian Elischer /* 503585ff168SJulian Elischer * Shutdown processing 504585ff168SJulian Elischer */ 505585ff168SJulian Elischer static int 506585ff168SJulian Elischer ng_source_rmnode(node_p node) 507585ff168SJulian Elischer { 508d8f5d037SGleb Smirnoff sc_p sc = NG_NODE_PRIVATE(node); 509585ff168SJulian Elischer 510585ff168SJulian Elischer ng_source_stop(sc); 511585ff168SJulian Elischer ng_source_clr_data(sc); 5125968e29eSJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 5135968e29eSJulian Elischer NG_NODE_UNREF(node); 51476bd5857SHartmut Brandt free(sc, M_NETGRAPH); 515d8f5d037SGleb Smirnoff 516585ff168SJulian Elischer return (0); 517585ff168SJulian Elischer } 518585ff168SJulian Elischer 519585ff168SJulian Elischer /* 520585ff168SJulian Elischer * Hook disconnection 521585ff168SJulian Elischer */ 522585ff168SJulian Elischer static int 523585ff168SJulian Elischer ng_source_disconnect(hook_p hook) 524585ff168SJulian Elischer { 5254b52f283SJulian Elischer sc_p sc; 526585ff168SJulian Elischer 5275968e29eSJulian Elischer sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 52876bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 529d8f5d037SGleb Smirnoff if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 || hook == sc->output) 5305968e29eSJulian Elischer ng_rmnode_self(NG_HOOK_NODE(hook)); 531585ff168SJulian Elischer return (0); 532585ff168SJulian Elischer } 533585ff168SJulian Elischer 534585ff168SJulian Elischer /* 535b655e33dSJulian Elischer * Set sc->output_ifp to point to the the struct ifnet of the interface 536b655e33dSJulian Elischer * reached via our output hook. 537b655e33dSJulian Elischer */ 538b655e33dSJulian Elischer static int 539d8f5d037SGleb Smirnoff ng_source_store_output_ifp(sc_p sc, char *ifname) 540b655e33dSJulian Elischer { 541b655e33dSJulian Elischer struct ifnet *ifp; 542b655e33dSJulian Elischer int s; 543b655e33dSJulian Elischer 544d8f5d037SGleb Smirnoff ifp = ifunit(ifname); 545585ff168SJulian Elischer 546585ff168SJulian Elischer if (ifp == NULL) { 54776bd5857SHartmut Brandt printf("%s: can't find interface %d\n", __func__, if_index); 548585ff168SJulian Elischer return (EINVAL); 549585ff168SJulian Elischer } 550585ff168SJulian Elischer sc->output_ifp = ifp; 551585ff168SJulian Elischer 552585ff168SJulian Elischer #if 1 553585ff168SJulian Elischer /* XXX mucking with a drivers ifqueue size is ugly but we need it 554585ff168SJulian Elischer * to queue a lot of packets to get close to line rate on a gigabit 555585ff168SJulian Elischer * interface with small packets. 556585ff168SJulian Elischer * XXX we should restore the original value at stop or disconnect 557585ff168SJulian Elischer */ 558585ff168SJulian Elischer s = splimp(); /* XXX is this required? */ 55976bd5857SHartmut Brandt if (ifp->if_snd.ifq_maxlen < NG_SOURCE_DRIVER_IFQ_MAXLEN) { 560585ff168SJulian Elischer printf("ng_source: changing ifq_maxlen from %d to %d\n", 5614b52f283SJulian Elischer ifp->if_snd.ifq_maxlen, NG_SOURCE_DRIVER_IFQ_MAXLEN); 562585ff168SJulian Elischer ifp->if_snd.ifq_maxlen = NG_SOURCE_DRIVER_IFQ_MAXLEN; 563585ff168SJulian Elischer } 564585ff168SJulian Elischer splx(s); 565585ff168SJulian Elischer #endif 566585ff168SJulian Elischer return (0); 567585ff168SJulian Elischer } 568585ff168SJulian Elischer 569585ff168SJulian Elischer /* 570585ff168SJulian Elischer * Set the attached ethernet node's ethernet source address override flag. 571585ff168SJulian Elischer */ 572585ff168SJulian Elischer static int 573d8f5d037SGleb Smirnoff ng_source_set_autosrc(sc_p sc, uint32_t flag) 574585ff168SJulian Elischer { 575585ff168SJulian Elischer struct ng_mesg *msg; 576585ff168SJulian Elischer int error = 0; 577585ff168SJulian Elischer 578585ff168SJulian Elischer NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, 579d8f5d037SGleb Smirnoff sizeof (uint32_t), M_NOWAIT); 580585ff168SJulian Elischer if (msg == NULL) 581585ff168SJulian Elischer return(ENOBUFS); 582585ff168SJulian Elischer 583d8f5d037SGleb Smirnoff *(uint32_t *)msg->data = flag; 584d8f5d037SGleb Smirnoff NG_SEND_MSG_HOOK(error, sc->node, msg, sc->output, 0); 585585ff168SJulian Elischer return (error); 586585ff168SJulian Elischer } 587585ff168SJulian Elischer 588585ff168SJulian Elischer /* 589585ff168SJulian Elischer * Clear out the data we've queued 590585ff168SJulian Elischer */ 591585ff168SJulian Elischer static void 592585ff168SJulian Elischer ng_source_clr_data (sc_p sc) 593585ff168SJulian Elischer { 594585ff168SJulian Elischer struct mbuf *m; 595585ff168SJulian Elischer 596585ff168SJulian Elischer for (;;) { 59781a4ef81SHartmut Brandt _IF_DEQUEUE(&sc->snd_queue, m); 598585ff168SJulian Elischer if (m == NULL) 599585ff168SJulian Elischer break; 600585ff168SJulian Elischer NG_FREE_M(m); 601585ff168SJulian Elischer } 602585ff168SJulian Elischer sc->queueOctets = 0; 603585ff168SJulian Elischer } 604585ff168SJulian Elischer 605585ff168SJulian Elischer /* 606585ff168SJulian Elischer * Start sending queued data out the output hook 607585ff168SJulian Elischer */ 608d8f5d037SGleb Smirnoff static int 609d8f5d037SGleb Smirnoff ng_source_start(sc_p sc, uint64_t packets) 610585ff168SJulian Elischer { 611d8f5d037SGleb Smirnoff if (sc->output_ifp == NULL) { 612d8f5d037SGleb Smirnoff printf("ng_source: start without iface configured\n"); 613d8f5d037SGleb Smirnoff return (ENXIO); 614d8f5d037SGleb Smirnoff } 615d8f5d037SGleb Smirnoff 616d8f5d037SGleb Smirnoff if (sc->node->nd_flags & NG_SOURCE_ACTIVE) 617d8f5d037SGleb Smirnoff return (EBUSY); 618d8f5d037SGleb Smirnoff 619d8f5d037SGleb Smirnoff sc->node->nd_flags |= NG_SOURCE_ACTIVE; 620d8f5d037SGleb Smirnoff 621d8f5d037SGleb Smirnoff sc->packets = packets; 622d8f5d037SGleb Smirnoff timevalclear(&sc->stats.elapsedTime); 623d8f5d037SGleb Smirnoff timevalclear(&sc->stats.endTime); 624d8f5d037SGleb Smirnoff getmicrotime(&sc->stats.startTime); 62572235857SGleb Smirnoff getmicrotime(&sc->stats.lastTime); 626d8f5d037SGleb Smirnoff ng_callout(&sc->intr_ch, sc->node, NULL, 0, 627d8f5d037SGleb Smirnoff ng_source_intr, sc, 0); 628d8f5d037SGleb Smirnoff 629d8f5d037SGleb Smirnoff return (0); 630585ff168SJulian Elischer } 631585ff168SJulian Elischer 632585ff168SJulian Elischer /* 633585ff168SJulian Elischer * Stop sending queued data out the output hook 634585ff168SJulian Elischer */ 635585ff168SJulian Elischer static void 636585ff168SJulian Elischer ng_source_stop(sc_p sc) 637585ff168SJulian Elischer { 638f9d9e1b4SGleb Smirnoff ng_uncallout(&sc->intr_ch, sc->node); 6395968e29eSJulian Elischer sc->node->nd_flags &= ~NG_SOURCE_ACTIVE; 640585ff168SJulian Elischer getmicrotime(&sc->stats.endTime); 641585ff168SJulian Elischer sc->stats.elapsedTime = sc->stats.endTime; 642585ff168SJulian Elischer timevalsub(&sc->stats.elapsedTime, &sc->stats.startTime); 643585ff168SJulian Elischer } 644585ff168SJulian Elischer 645585ff168SJulian Elischer /* 646585ff168SJulian Elischer * While active called every NG_SOURCE_INTR_TICKS ticks. 647585ff168SJulian Elischer * Sends as many packets as the interface connected to our 648585ff168SJulian Elischer * output hook is able to enqueue. 649585ff168SJulian Elischer */ 650585ff168SJulian Elischer static void 651a1adb510SHartmut Brandt ng_source_intr(node_p node, hook_p hook, void *arg1, int arg2) 652585ff168SJulian Elischer { 653a1adb510SHartmut Brandt sc_p sc = (sc_p)arg1; 654585ff168SJulian Elischer struct ifqueue *ifq; 655585ff168SJulian Elischer int packets; 656585ff168SJulian Elischer 65776bd5857SHartmut Brandt KASSERT(sc != NULL, ("%s: null node private", __func__)); 658585ff168SJulian Elischer 659d8f5d037SGleb Smirnoff if (sc->packets == 0 || sc->output == NULL 6605968e29eSJulian Elischer || (sc->node->nd_flags & NG_SOURCE_ACTIVE) == 0) { 661585ff168SJulian Elischer ng_source_stop(sc); 662585ff168SJulian Elischer return; 663585ff168SJulian Elischer } 664585ff168SJulian Elischer 665f5d15522SHartmut Brandt if (sc->output_ifp != NULL) { 6660572dfacSRuslan Ermilov ifq = (struct ifqueue *)&sc->output_ifp->if_snd; 667585ff168SJulian Elischer packets = ifq->ifq_maxlen - ifq->ifq_len; 668f5d15522SHartmut Brandt } else 669f5d15522SHartmut Brandt packets = sc->snd_queue.ifq_len; 670f5d15522SHartmut Brandt 67172235857SGleb Smirnoff if (sc->stats.maxPps != 0) { 67272235857SGleb Smirnoff struct timeval now, elapsed; 67372235857SGleb Smirnoff uint64_t usec; 67472235857SGleb Smirnoff int maxpkt; 67572235857SGleb Smirnoff 67672235857SGleb Smirnoff getmicrotime(&now); 67772235857SGleb Smirnoff elapsed = now; 67872235857SGleb Smirnoff timevalsub(&elapsed, &sc->stats.lastTime); 67972235857SGleb Smirnoff usec = elapsed.tv_sec * 1000000 + elapsed.tv_usec; 68072235857SGleb Smirnoff maxpkt = (uint64_t)sc->stats.maxPps * usec / 1000000; 68172235857SGleb Smirnoff sc->stats.lastTime = now; 68272235857SGleb Smirnoff if (packets > maxpkt) 68372235857SGleb Smirnoff packets = maxpkt; 68472235857SGleb Smirnoff } 68572235857SGleb Smirnoff 686585ff168SJulian Elischer ng_source_send(sc, packets, NULL); 687a1adb510SHartmut Brandt if (sc->packets == 0) 688585ff168SJulian Elischer ng_source_stop(sc); 689a1adb510SHartmut Brandt else 690f9d9e1b4SGleb Smirnoff ng_callout(&sc->intr_ch, node, NULL, NG_SOURCE_INTR_TICKS, 691d312eaf5SGleb Smirnoff ng_source_intr, sc, 0); 692585ff168SJulian Elischer } 693585ff168SJulian Elischer 694585ff168SJulian Elischer /* 695205aefa3SGleb Smirnoff * Send packets out our output hook. 696585ff168SJulian Elischer */ 697585ff168SJulian Elischer static int 698585ff168SJulian Elischer ng_source_send(sc_p sc, int tosend, int *sent_p) 699585ff168SJulian Elischer { 700585ff168SJulian Elischer struct mbuf *m, *m2; 701205aefa3SGleb Smirnoff int sent; 702585ff168SJulian Elischer int error = 0; 703585ff168SJulian Elischer 70476bd5857SHartmut Brandt KASSERT(tosend >= 0, ("%s: negative tosend param", __func__)); 7055968e29eSJulian Elischer KASSERT(sc->node->nd_flags & NG_SOURCE_ACTIVE, 70676bd5857SHartmut Brandt ("%s: inactive node", __func__)); 707585ff168SJulian Elischer 708d8f5d037SGleb Smirnoff if ((uint64_t)tosend > sc->packets) 709585ff168SJulian Elischer tosend = sc->packets; 710585ff168SJulian Elischer 711205aefa3SGleb Smirnoff /* Go through the queue sending packets one by one. */ 712585ff168SJulian Elischer for (sent = 0; error == 0 && sent < tosend; ++sent) { 71381a4ef81SHartmut Brandt _IF_DEQUEUE(&sc->snd_queue, m); 714585ff168SJulian Elischer if (m == NULL) 715585ff168SJulian Elischer break; 716585ff168SJulian Elischer 7175f87dd69SEd Maste /* Duplicate and modify the packet. */ 7185f87dd69SEd Maste error = ng_source_dup_mod(sc, m, &m2); 7195f87dd69SEd Maste if (error) { 7205f87dd69SEd Maste if (error == ENOBUFS) 72181a4ef81SHartmut Brandt _IF_PREPEND(&sc->snd_queue, m); 7225f87dd69SEd Maste else 7235f87dd69SEd Maste _IF_ENQUEUE(&sc->snd_queue, m); 724585ff168SJulian Elischer break; 725585ff168SJulian Elischer } 726585ff168SJulian Elischer 727d8f5d037SGleb Smirnoff /* Re-enqueue the original packet for us. */ 72881a4ef81SHartmut Brandt _IF_ENQUEUE(&sc->snd_queue, m); 729585ff168SJulian Elischer 730585ff168SJulian Elischer sc->stats.outFrames++; 731585ff168SJulian Elischer sc->stats.outOctets += m2->m_pkthdr.len; 732d8f5d037SGleb Smirnoff NG_SEND_DATA_ONLY(error, sc->output, m2); 733a1adb510SHartmut Brandt if (error) 734205aefa3SGleb Smirnoff break; 735585ff168SJulian Elischer } 736585ff168SJulian Elischer 737585ff168SJulian Elischer sc->packets -= sent; 738585ff168SJulian Elischer if (sent_p != NULL) 739585ff168SJulian Elischer *sent_p = sent; 740585ff168SJulian Elischer return (error); 741585ff168SJulian Elischer } 7425f87dd69SEd Maste 7435f87dd69SEd Maste /* 7445f87dd69SEd Maste * Modify packet in 'm' by changing 'len' bytes starting at 'offset' 7455f87dd69SEd Maste * to data in 'cp'. 7465f87dd69SEd Maste * 7475f87dd69SEd Maste * The packet data in 'm' must be in a contiguous buffer in a single mbuf. 7485f87dd69SEd Maste */ 7495f87dd69SEd Maste static void 7505f87dd69SEd Maste ng_source_packet_mod(sc_p sc, struct mbuf *m, int offset, int len, caddr_t cp, 7515f87dd69SEd Maste int flags) 7525f87dd69SEd Maste { 7535f87dd69SEd Maste if (len == 0) 7545f87dd69SEd Maste return; 7555f87dd69SEd Maste 7565f87dd69SEd Maste /* Can't modify beyond end of packet. */ 7575f87dd69SEd Maste /* TODO: Pad packet for this case. */ 7585f87dd69SEd Maste if (offset + len > m->m_len) 7595f87dd69SEd Maste return; 7605f87dd69SEd Maste 7615f87dd69SEd Maste bcopy(cp, mtod_off(m, offset, caddr_t), len); 7625f87dd69SEd Maste } 7635f87dd69SEd Maste 7645f87dd69SEd Maste static int 7655f87dd69SEd Maste ng_source_dup_mod(sc_p sc, struct mbuf *m0, struct mbuf **m_ptr) 7665f87dd69SEd Maste { 7675f87dd69SEd Maste struct mbuf *m; 7685f87dd69SEd Maste struct ng_source_embed_info *ts; 7695f87dd69SEd Maste int modify; 7705f87dd69SEd Maste int error = 0; 7715f87dd69SEd Maste 7725f87dd69SEd Maste /* Are we going to modify packets? */ 7735f87dd69SEd Maste modify = sc->embed_timestamp.flags & NGM_SOURCE_EMBED_ENABLE; 7745f87dd69SEd Maste 7755f87dd69SEd Maste /* Duplicate the packet. */ 7765f87dd69SEd Maste if (modify) 7775f87dd69SEd Maste m = m_dup(m0, M_DONTWAIT); 7785f87dd69SEd Maste else 7795f87dd69SEd Maste m = m_copypacket(m0, M_DONTWAIT); 7805f87dd69SEd Maste if (m == NULL) { 7815f87dd69SEd Maste error = ENOBUFS; 7825f87dd69SEd Maste goto done; 7835f87dd69SEd Maste } 7845f87dd69SEd Maste *m_ptr = m; 7855f87dd69SEd Maste 7865f87dd69SEd Maste if (!modify) 7875f87dd69SEd Maste goto done; 7885f87dd69SEd Maste 7895f87dd69SEd Maste /* Modify the copied packet for sending. */ 7905f87dd69SEd Maste KASSERT(M_WRITABLE(m), ("%s: packet not writable", __func__)); 7915f87dd69SEd Maste 7925f87dd69SEd Maste ts = &sc->embed_timestamp; 7935f87dd69SEd Maste if (ts->flags & NGM_SOURCE_EMBED_ENABLE) { 7945f87dd69SEd Maste struct timeval now; 7955f87dd69SEd Maste getmicrotime(&now); 7965f87dd69SEd Maste now.tv_sec = htonl(now.tv_sec); 7975f87dd69SEd Maste now.tv_usec = htonl(now.tv_usec); 7985f87dd69SEd Maste ng_source_packet_mod(sc, m, ts->offset, sizeof (now), 7995f87dd69SEd Maste (caddr_t)&now, ts->flags); 8005f87dd69SEd Maste } 8015f87dd69SEd Maste 8025f87dd69SEd Maste done: 8035f87dd69SEd Maste return(error); 8045f87dd69SEd Maste } 805