tcpreplay_api.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  1. /* $Id$ */
  2. /*
  3. * Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
  4. * Copyright (c) 2013-2017 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  5. *
  6. * The Tcpreplay Suite of tools is free software: you can redistribute it
  7. * and/or modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation, either version 3 of the
  9. * License, or with the authors permission any later version.
  10. *
  11. * The Tcpreplay Suite is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with the Tcpreplay Suite. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "config.h"
  20. #include "defines.h"
  21. #include "common.h"
  22. #include <ctype.h>
  23. #include <fcntl.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <sys/types.h>
  28. #include <unistd.h>
  29. #include <errno.h>
  30. #include <stdarg.h>
  31. #include "tcpreplay_api.h"
  32. #include "send_packets.h"
  33. #include "replay.h"
  34. #ifdef TCPREPLAY_EDIT
  35. #include "tcpreplay_edit_opts.h"
  36. #else
  37. #include "tcpreplay_opts.h"
  38. #endif
  39. /**
  40. * \brief Returns a string describing the last error.
  41. *
  42. * Value when the last call does not result in an error is undefined
  43. * (may be NULL, may be garbage)
  44. */
  45. char *
  46. tcpreplay_geterr(tcpreplay_t *ctx)
  47. {
  48. assert(ctx);
  49. return(ctx->errstr);
  50. }
  51. /**
  52. * \brief Returns a string describing the last warning.
  53. *
  54. * Value when the last call does not result in an warning is undefined
  55. * (may be NULL, may be garbage)
  56. */
  57. char *
  58. tcpreplay_getwarn(tcpreplay_t *ctx)
  59. {
  60. assert(ctx);
  61. return(ctx->warnstr);
  62. }
  63. /**
  64. * \brief Initialize a new tcpreplay context
  65. *
  66. * Allocates memory and stuff like that. Always returns a buffer or completely
  67. * fails by calling exit() on malloc failure.
  68. */
  69. tcpreplay_t *
  70. tcpreplay_init()
  71. {
  72. tcpreplay_t *ctx;
  73. /* allocations will reset everything to zeros */
  74. ctx = safe_malloc(sizeof(tcpreplay_t));
  75. ctx->options = safe_malloc(sizeof(tcpreplay_opt_t));
  76. /* replay packets only once */
  77. ctx->options->loop = 1;
  78. /* Default mode is to replay pcap once in real-time */
  79. ctx->options->speed.mode = speed_multiplier;
  80. ctx->options->speed.multiplier = 1.0;
  81. /* Set the default timing method */
  82. ctx->options->accurate = accurate_gtod;
  83. /* set the default MTU size */
  84. ctx->options->mtu = DEFAULT_MTU;
  85. /* disable periodic statistics */
  86. ctx->options->stats = -1;
  87. /* disable limit send */
  88. ctx->options->limit_send = -1;
  89. /* default unique-loops */
  90. ctx->options->unique_loops = 1.0;
  91. #ifdef ENABLE_VERBOSE
  92. /* clear out tcpdump struct */
  93. ctx->options->tcpdump = (tcpdump_t *)safe_malloc(sizeof(tcpdump_t));
  94. #endif
  95. if (fcntl(STDERR_FILENO, F_SETFL, O_NONBLOCK) < 0)
  96. tcpreplay_setwarn(ctx, "Unable to set STDERR to non-blocking: %s", strerror(errno));
  97. #ifdef ENABLE_PCAP_FINDALLDEVS
  98. ctx->intlist = get_interface_list();
  99. #else
  100. ctx->intlist = NULL;
  101. #endif
  102. /* set up flows - on by default*/
  103. ctx->options->flow_stats = 1;
  104. ctx->flow_hash_table = flow_hash_table_init(DEFAULT_FLOW_HASH_BUCKET_SIZE);
  105. ctx->sp_type = SP_TYPE_NONE;
  106. ctx->intf1dlt = -1;
  107. ctx->intf2dlt = -1;
  108. ctx->abort = false;
  109. ctx->first_time = true;
  110. return ctx;
  111. }
  112. /**
  113. * \brief Parses the GNU AutoOpts options for tcpreplay
  114. *
  115. * If you're using AutoOpts with tcpreplay_api, then just call this after
  116. * optionProcess() and it will parse all the options for you. As always,
  117. * returns 0 on success, and -1 on error & -2 on warning.
  118. */
  119. int
  120. tcpreplay_post_args(tcpreplay_t *ctx, int argc)
  121. {
  122. char *temp, *intname;
  123. char *ebuf;
  124. tcpreplay_opt_t *options;
  125. int warn = 0;
  126. float n;
  127. int ret = 0;
  128. options = ctx->options;
  129. dbg(2, "tcpreplay_post_args: parsing command arguments");
  130. ebuf = safe_malloc(SENDPACKET_ERRBUF_SIZE);
  131. #ifdef DEBUG
  132. if (HAVE_OPT(DBUG))
  133. debug = OPT_VALUE_DBUG;
  134. #else
  135. if (HAVE_OPT(DBUG)) {
  136. warn ++;
  137. tcpreplay_setwarn(ctx, "%s", "not configured with --enable-debug. Debugging disabled.");
  138. }
  139. #endif
  140. options->loop = OPT_VALUE_LOOP;
  141. options->loopdelay_ms = OPT_VALUE_LOOPDELAY_MS;
  142. if (HAVE_OPT(LIMIT))
  143. options->limit_send = OPT_VALUE_LIMIT;
  144. if (HAVE_OPT(DURATION))
  145. options->limit_time = OPT_VALUE_DURATION;
  146. if (HAVE_OPT(TOPSPEED)) {
  147. options->speed.mode = speed_topspeed;
  148. options->speed.speed = 0;
  149. } else if (HAVE_OPT(PPS)) {
  150. n = atof(OPT_ARG(PPS));
  151. options->speed.speed = (COUNTER)(n * 60.0 * 60.0); /* convert to packets per hour */
  152. options->speed.mode = speed_packetrate;
  153. options->speed.pps_multi = OPT_VALUE_PPS_MULTI;
  154. } else if (HAVE_OPT(ONEATATIME)) {
  155. options->speed.mode = speed_oneatatime;
  156. options->speed.speed = 0;
  157. } else if (HAVE_OPT(MBPS)) {
  158. n = atof(OPT_ARG(MBPS));
  159. if (n) {
  160. options->speed.mode = speed_mbpsrate;
  161. options->speed.speed = (COUNTER)(n * 1000000.0); /* convert to bps */
  162. } else {
  163. options->speed.mode = speed_topspeed;
  164. options->speed.speed = 0;
  165. }
  166. } else if (HAVE_OPT(MULTIPLIER)) {
  167. options->speed.mode = speed_multiplier;
  168. options->speed.multiplier = atof(OPT_ARG(MULTIPLIER));
  169. }
  170. if (HAVE_OPT(MAXSLEEP)) {
  171. options->maxsleep.tv_sec = OPT_VALUE_MAXSLEEP / 1000;
  172. options->maxsleep.tv_nsec = (OPT_VALUE_MAXSLEEP % 1000) * 1000;
  173. }
  174. #ifdef ENABLE_VERBOSE
  175. if (HAVE_OPT(VERBOSE))
  176. options->verbose = 1;
  177. if (HAVE_OPT(DECODE))
  178. options->tcpdump->args = safe_strdup(OPT_ARG(DECODE));
  179. #endif
  180. if (HAVE_OPT(STATS))
  181. options->stats = OPT_VALUE_STATS;
  182. /*
  183. * preloading the pcap before the first run
  184. */
  185. if (HAVE_OPT(PRELOAD_PCAP)) {
  186. options->preload_pcap = true;
  187. }
  188. /* Dual file mode */
  189. if (HAVE_OPT(DUALFILE)) {
  190. options->dualfile = true;
  191. if (argc < 2) {
  192. tcpreplay_seterr(ctx, "%s", "--dualfile mode requires at least two pcap files");
  193. ret = -1;
  194. goto out;
  195. }
  196. if (argc % 2 != 0) {
  197. tcpreplay_seterr(ctx, "%s", "--dualfile mode requires an even number of pcap files");
  198. ret = -1;
  199. goto out;
  200. }
  201. }
  202. #ifdef HAVE_NETMAP
  203. options->netmap_delay = OPT_VALUE_NM_DELAY;
  204. #endif
  205. if (HAVE_OPT(NETMAP)) {
  206. #ifdef HAVE_NETMAP
  207. options->netmap = 1;
  208. ctx->sp_type = SP_TYPE_NETMAP;
  209. #else
  210. err(-1, "--netmap feature was not compiled in. See INSTALL.");
  211. #endif
  212. }
  213. if (HAVE_OPT(UNIQUE_IP))
  214. options->unique_ip = 1;
  215. if (HAVE_OPT(UNIQUE_IP_LOOPS)) {
  216. options->unique_loops = atof(OPT_ARG(UNIQUE_IP_LOOPS));
  217. if (options->unique_loops < 1.0) {
  218. tcpreplay_seterr(ctx, "%s", "--unique-ip-loops requires loop count >= 1.0");
  219. ret = -1;
  220. goto out;
  221. }
  222. }
  223. /* flow statistics */
  224. if (HAVE_OPT(NO_FLOW_STATS))
  225. options->flow_stats = 0;
  226. if (HAVE_OPT(FLOW_EXPIRY)) {
  227. options->flow_expiry = OPT_VALUE_FLOW_EXPIRY;
  228. }
  229. if (HAVE_OPT(TIMER)) {
  230. if (strcmp(OPT_ARG(TIMER), "select") == 0) {
  231. #ifdef HAVE_SELECT
  232. options->accurate = accurate_select;
  233. #else
  234. tcpreplay_seterr(ctx, "%s", "tcpreplay_api not compiled with select support");
  235. ret = -1;
  236. goto out;
  237. #endif
  238. } else if (strcmp(OPT_ARG(TIMER), "gtod") == 0) {
  239. options->accurate = accurate_gtod;
  240. } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) {
  241. options->accurate = accurate_nanosleep;
  242. } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) {
  243. tcpreplay_seterr(ctx, "%s", "abstime is deprecated");
  244. ret = -1;
  245. goto out;
  246. } else {
  247. tcpreplay_seterr(ctx, "Unsupported timer mode: %s", OPT_ARG(TIMER));
  248. ret = -1;
  249. goto out;
  250. }
  251. }
  252. #ifdef HAVE_RDTSC
  253. if (HAVE_OPT(RDTSC_CLICKS)) {
  254. rdtsc_calibrate(OPT_VALUE_RDTSC_CLICKS);
  255. }
  256. #endif
  257. if (HAVE_OPT(PKTLEN)) {
  258. options->use_pkthdr_len = true;
  259. warn ++;
  260. tcpreplay_setwarn(ctx, "%s", "--pktlen may cause problems. Use with caution.");
  261. }
  262. if ((intname = get_interface(ctx->intlist, OPT_ARG(INTF1))) == NULL) {
  263. if (!strncmp(OPT_ARG(INTF1), "netmap:", 7) || !strncmp(OPT_ARG(INTF1), "vale", 4))
  264. tcpreplay_seterr(ctx, "Unable to connect to netmap interface %s. Ensure netmap module is installed (see INSTALL).",
  265. OPT_ARG(INTF1));
  266. else
  267. tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF1));
  268. ret = -1;
  269. goto out;
  270. }
  271. if (!strncmp(intname, "netmap:", 7) || !strncmp(intname, "vale:", 5)) {
  272. #ifdef HAVE_NETMAP
  273. options->netmap = 1;
  274. ctx->sp_type = SP_TYPE_NETMAP;
  275. #else
  276. tcpreplay_seterr(ctx, "%s", "tcpreplay_api not compiled with netmap support");
  277. ret = -1;
  278. goto out;
  279. #endif
  280. }
  281. options->intf1_name = safe_strdup(intname);
  282. /* open interfaces for writing */
  283. if ((ctx->intf1 = sendpacket_open(options->intf1_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) {
  284. tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf1_name, ebuf);
  285. ret = -1;
  286. goto out;
  287. }
  288. #if defined HAVE_NETMAP
  289. ctx->intf1->netmap_delay = ctx->options->netmap_delay;
  290. #endif
  291. ctx->intf1dlt = sendpacket_get_dlt(ctx->intf1);
  292. if (HAVE_OPT(INTF2)) {
  293. if (!HAVE_OPT(CACHEFILE) && !HAVE_OPT(DUALFILE)) {
  294. tcpreplay_seterr(ctx, "--intf2=%s requires either --cachefile or --dualfile", OPT_ARG(INTF2));
  295. ret = -1;
  296. goto out;
  297. }
  298. if ((intname = get_interface(ctx->intlist, OPT_ARG(INTF2))) == NULL) {
  299. tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF2));
  300. ret = -1;
  301. goto out;
  302. }
  303. options->intf2_name = safe_strdup(intname);
  304. /* open interface for writing */
  305. if ((ctx->intf2 = sendpacket_open(options->intf2_name, ebuf, TCPR_DIR_S2C, ctx->sp_type, ctx)) == NULL) {
  306. tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf2_name, ebuf);
  307. }
  308. #if defined HAVE_NETMAP
  309. ctx->intf2->netmap_delay = ctx->options->netmap_delay;
  310. #endif
  311. ctx->intf2dlt = sendpacket_get_dlt(ctx->intf2);
  312. if (ctx->intf2dlt != ctx->intf1dlt) {
  313. tcpreplay_seterr(ctx, "DLT type mismatch for %s (%s) and %s (%s)",
  314. options->intf1_name, pcap_datalink_val_to_name(ctx->intf1dlt),
  315. options->intf2_name, pcap_datalink_val_to_name(ctx->intf2dlt));
  316. ret = -1;
  317. goto out;
  318. }
  319. }
  320. if (HAVE_OPT(CACHEFILE)) {
  321. temp = safe_strdup(OPT_ARG(CACHEFILE));
  322. options->cache_packets = read_cache(&options->cachedata, temp,
  323. &options->comment);
  324. safe_free(temp);
  325. }
  326. /* return -2 on warnings */
  327. if (warn > 0)
  328. ret = -2;
  329. out:
  330. safe_free(ebuf);
  331. return ret;
  332. }
  333. /**
  334. * Closes & free's all memory related to a tcpreplay context
  335. */
  336. void
  337. tcpreplay_close(tcpreplay_t *ctx)
  338. {
  339. tcpreplay_opt_t *options;
  340. interface_list_t *intlist, *intlistnext;
  341. packet_cache_t *packet_cache, *next;
  342. assert(ctx);
  343. assert(ctx->options);
  344. options = ctx->options;
  345. safe_free(options->intf1_name);
  346. safe_free(options->intf2_name);
  347. sendpacket_close(ctx->intf1);
  348. if (ctx->intf2 != NULL)
  349. sendpacket_close(ctx->intf2);
  350. safe_free(options->cachedata);
  351. safe_free(options->comment);
  352. #ifdef ENABLE_VERBOSE
  353. safe_free(options->tcpdump_args);
  354. tcpdump_close(options->tcpdump);
  355. #endif
  356. /* free the flow hash table */
  357. flow_hash_table_release(ctx->flow_hash_table);
  358. /* free the file cache */
  359. packet_cache = options->file_cache->packet_cache;
  360. while (packet_cache != NULL) {
  361. next = packet_cache->next;
  362. safe_free(packet_cache->pktdata);
  363. safe_free(packet_cache);
  364. packet_cache = next;
  365. }
  366. /* free our interface list */
  367. if (ctx->intlist != NULL) {
  368. intlist = ctx->intlist;
  369. while (intlist != NULL) {
  370. intlistnext = intlist->next;
  371. safe_free(intlist);
  372. intlist = intlistnext;
  373. }
  374. }
  375. }
  376. /**
  377. * \brief Specifies an interface to use for sending.
  378. *
  379. * You may call this up to two (2) times with different interfaces
  380. * when using a tcpprep cache file or dualfile mode. Note, both interfaces
  381. * must use the same DLT type
  382. */
  383. int
  384. tcpreplay_set_interface(tcpreplay_t *ctx, tcpreplay_intf intf, char *value)
  385. {
  386. static int int1dlt = -1, int2dlt = -1;
  387. char *intname;
  388. char *ebuf;
  389. int ret = 0;
  390. assert(ctx);
  391. assert(value);
  392. ebuf = safe_malloc(SENDPACKET_ERRBUF_SIZE);
  393. if (intf == intf1) {
  394. if ((intname = get_interface(ctx->intlist, value)) == NULL) {
  395. if (!strncmp(OPT_ARG(INTF1), "netmap:", 7) || !strncmp(OPT_ARG(INTF1), "vale", 4))
  396. tcpreplay_seterr(ctx, "Unable to connect to netmap interface %s. Ensure netmap module is installed (see INSTALL).",
  397. value);
  398. else
  399. tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", value);
  400. ret = -1;
  401. goto out;
  402. }
  403. ctx->options->intf1_name = safe_strdup(intname);
  404. /* open interfaces for writing */
  405. if ((ctx->intf1 = sendpacket_open(ctx->options->intf1_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) {
  406. tcpreplay_seterr(ctx, "Can't open %s: %s", ctx->options->intf1_name, ebuf);
  407. ret = -1;
  408. goto out;
  409. }
  410. int1dlt = sendpacket_get_dlt(ctx->intf1);
  411. } else if (intf == intf2) {
  412. if ((intname = get_interface(ctx->intlist, value)) == NULL) {
  413. tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", ctx->options->intf2_name);
  414. ret = -1;
  415. goto out;
  416. }
  417. ctx->options->intf2_name = safe_strdup(intname);
  418. /* open interface for writing */
  419. if ((ctx->intf2 = sendpacket_open(ctx->options->intf2_name, ebuf, TCPR_DIR_S2C, ctx->sp_type, ctx)) == NULL) {
  420. tcpreplay_seterr(ctx, "Can't open %s: %s", ctx->options->intf2_name, ebuf);
  421. ret = -1;
  422. goto out;
  423. }
  424. int2dlt = sendpacket_get_dlt(ctx->intf2);
  425. }
  426. /*
  427. * If both interfaces are selected, then make sure both interfaces use
  428. * the same DLT type
  429. */
  430. if (int1dlt != -1 && int2dlt != -1) {
  431. if (int1dlt != int2dlt) {
  432. tcpreplay_seterr(ctx, "DLT type mismatch for %s (%s) and %s (%s)",
  433. ctx->options->intf1_name, pcap_datalink_val_to_name(int1dlt),
  434. ctx->options->intf2_name, pcap_datalink_val_to_name(int2dlt));
  435. ret = -1;
  436. goto out;
  437. }
  438. }
  439. out:
  440. safe_free(ebuf);
  441. return ret;
  442. }
  443. /**
  444. * Set the replay speed mode.
  445. */
  446. int
  447. tcpreplay_set_speed_mode(tcpreplay_t *ctx, tcpreplay_speed_mode value)
  448. {
  449. assert(ctx);
  450. ctx->options->speed.mode = value;
  451. return 0;
  452. }
  453. /**
  454. * Set the approprate speed value. Value is interpreted based on
  455. * how tcpreplay_set_speed_mode() value
  456. */
  457. int
  458. tcpreplay_set_speed_speed(tcpreplay_t *ctx, COUNTER value)
  459. {
  460. assert(ctx);
  461. ctx->options->speed.speed = value;
  462. return 0;
  463. }
  464. /**
  465. * Sending under packets/sec requires an integer value, not float.
  466. * you must first call tcpreplay_set_speed_mode(ctx, speed_packetrate)
  467. */
  468. int
  469. tcpreplay_set_speed_pps_multi(tcpreplay_t *ctx, int value)
  470. {
  471. assert(ctx);
  472. ctx->options->speed.pps_multi = value;
  473. return 0;
  474. }
  475. /**
  476. * How many times should we loop through all the pcap files?
  477. */
  478. int
  479. tcpreplay_set_loop(tcpreplay_t *ctx, u_int32_t value)
  480. {
  481. assert(ctx);
  482. ctx->options->loop = value;
  483. return 0;
  484. }
  485. /**
  486. * Set the unique IP address flag
  487. */
  488. int
  489. tcpreplay_set_unique_ip(tcpreplay_t *ctx, bool value)
  490. {
  491. assert(ctx);
  492. ctx->options->unique_ip = value;
  493. return 0;
  494. }
  495. int
  496. tcpreplay_set_unique_ip_loops(tcpreplay_t *ctx, int value)
  497. {
  498. assert(ctx);
  499. ctx->options->unique_loops = value;
  500. return 0;
  501. }
  502. /**
  503. * Set netmap mode
  504. */
  505. int
  506. tcpreplay_set_netmap(tcpreplay_t *ctx, bool value)
  507. {
  508. assert(ctx);
  509. #ifdef HAVE_NETMAP
  510. ctx->options->netmap = value;
  511. return 0;
  512. #else
  513. warn("netmap not compiled in");
  514. return -1;
  515. #endif
  516. }
  517. /**
  518. * Tell tcpreplay to ignore the snaplen (default) and use the "actual"
  519. * packet len instead
  520. */
  521. int
  522. tcpreplay_set_use_pkthdr_len(tcpreplay_t *ctx, bool value)
  523. {
  524. assert(ctx);
  525. ctx->options->use_pkthdr_len = value;
  526. return 0;
  527. }
  528. /**
  529. * Override the outbound MTU
  530. */
  531. int
  532. tcpreplay_set_mtu(tcpreplay_t *ctx, int value)
  533. {
  534. assert(ctx);
  535. ctx->options->mtu = value;
  536. return 0;
  537. }
  538. /**
  539. * Sets the accurate timing mode
  540. */
  541. int
  542. tcpreplay_set_accurate(tcpreplay_t *ctx, tcpreplay_accurate value)
  543. {
  544. assert(ctx);
  545. ctx->options->accurate = value;
  546. return 0;
  547. }
  548. /**
  549. * Sets the number of seconds between printing stats
  550. */
  551. int
  552. tcpreplay_set_stats(tcpreplay_t *ctx, int value)
  553. {
  554. assert(ctx);
  555. ctx->options->stats = value;
  556. return 0;
  557. }
  558. /**
  559. * \brief Enable or disable dual file mode
  560. *
  561. * In dual file mode, we read two files at the same time and use
  562. * one file for each interface.
  563. */
  564. int
  565. tcpreplay_set_dualfile(tcpreplay_t *ctx, bool value)
  566. {
  567. assert(ctx);
  568. ctx->options->dualfile = value;
  569. return 0;
  570. }
  571. /**
  572. * \brief Enable or disable preloading the file cache
  573. *
  574. * Note: This is a global option and forces all pcaps
  575. * to be preloaded for this context. If you turn this
  576. * on, then it forces set_file_cache(true)
  577. */
  578. int
  579. tcpreplay_set_preload_pcap(tcpreplay_t *ctx, bool value)
  580. {
  581. assert(ctx);
  582. ctx->options->preload_pcap = value;
  583. return 0;
  584. }
  585. /**
  586. * \brief Add a pcap file to be sent via tcpreplay
  587. *
  588. * One or more pcap files can be added. Each file will be replayed
  589. * in order
  590. */
  591. int
  592. tcpreplay_add_pcapfile(tcpreplay_t *ctx, char *pcap_file)
  593. {
  594. assert(ctx);
  595. assert(pcap_file);
  596. if (ctx->options->source_cnt < MAX_FILES) {
  597. ctx->options->sources[ctx->options->source_cnt].filename = safe_strdup(pcap_file);
  598. ctx->options->sources[ctx->options->source_cnt].type = source_filename;
  599. /*
  600. * prepare the cache info data struct. This doesn't actually enable
  601. * file caching for this pcap (that is controlled globally via
  602. * tcpreplay_set_file_cache())
  603. */
  604. ctx->options->file_cache[ctx->options->source_cnt].index = ctx->options->source_cnt;
  605. ctx->options->file_cache[ctx->options->source_cnt].cached = false;
  606. ctx->options->file_cache[ctx->options->source_cnt].packet_cache = NULL;
  607. ctx->options->source_cnt += 1;
  608. } else {
  609. tcpreplay_seterr(ctx, "Unable to add more then %u files", MAX_FILES);
  610. return -1;
  611. }
  612. return 0;
  613. }
  614. /**
  615. * Limit the total number of packets to send
  616. */
  617. int
  618. tcpreplay_set_limit_send(tcpreplay_t *ctx, COUNTER value)
  619. {
  620. assert(ctx);
  621. ctx->options->limit_send = value;
  622. return 0;
  623. }
  624. /**
  625. * \brief Specify the tcpprep cache file to use for replaying with two NICs
  626. *
  627. * Note: this only works if you have a single pcap file
  628. * returns -1 on error
  629. */
  630. int
  631. tcpreplay_set_tcpprep_cache(tcpreplay_t *ctx, char *file)
  632. {
  633. assert(ctx);
  634. char *tcpprep_file;
  635. if (ctx->options->source_cnt > 1) {
  636. tcpreplay_seterr(ctx, "%s", "Unable to use tcpprep cache file with a single pcap file");
  637. return -1;
  638. }
  639. tcpprep_file = safe_strdup(file);
  640. ctx->options->cache_packets = read_cache(&ctx->options->cachedata,
  641. tcpprep_file, &ctx->options->comment);
  642. free(tcpprep_file);
  643. return 0;
  644. }
  645. /*
  646. * Verbose mode requires fork() and tcpdump binary, hence won't work
  647. * under Win32 without Cygwin
  648. */
  649. /**
  650. * Enable verbose mode
  651. */
  652. int
  653. tcpreplay_set_verbose(tcpreplay_t *ctx, bool value)
  654. {
  655. assert(ctx);
  656. #ifdef ENABLE_VERBOSE
  657. ctx->options->verbose = value;
  658. return 0;
  659. #else
  660. tcpreplay_seterr(ctx, "%s", "verbose mode not supported");
  661. return -1;
  662. #endif
  663. }
  664. /**
  665. * \brief Set the arguments to be passed to tcpdump
  666. *
  667. * Specify the additional argument to be passed to tcpdump when enabling
  668. * verbose mode. See TCPDUMP_ARGS in tcpdump.h for the default options
  669. */
  670. int
  671. tcpreplay_set_tcpdump_args(tcpreplay_t *ctx, char *value)
  672. {
  673. assert(ctx);
  674. #ifdef ENABLE_VERBOSE
  675. assert(value);
  676. ctx->options->tcpdump_args = safe_strdup(value);
  677. return 0;
  678. #else
  679. tcpreplay_seterr(ctx, "%s", "verbose mode not supported");
  680. return -1;
  681. #endif
  682. }
  683. /**
  684. * \brief Set the path to the tcpdump binary
  685. *
  686. * In order to support the verbose feature, tcpreplay needs to know where
  687. * tcpdump lives
  688. */
  689. int
  690. tcpreplay_set_tcpdump(tcpreplay_t *ctx, tcpdump_t *value)
  691. {
  692. assert(ctx);
  693. #ifdef ENABLE_VERBOSE
  694. assert(value);
  695. ctx->options->verbose = true;
  696. ctx->options->tcpdump = value;
  697. return 0;
  698. #else
  699. tcpreplay_seterr(ctx, "%s", "verbose mode not supported");
  700. return -1;
  701. #endif
  702. }
  703. /**
  704. * \brief Set the callback function for handing manual iteration
  705. *
  706. * Obviously for this to work, you need to first set speed_mode = speed_oneatatime
  707. * returns 0 on success, < 0 on error
  708. */
  709. int
  710. tcpreplay_set_manual_callback(tcpreplay_t *ctx, tcpreplay_manual_callback callback)
  711. {
  712. assert(ctx);
  713. assert(callback);
  714. if (ctx->options->speed.mode != speed_oneatatime) {
  715. tcpreplay_seterr(ctx, "%s",
  716. "Unable to set manual callback because speed mode is not 'speed_oneatatime'");
  717. return -1;
  718. }
  719. ctx->options->speed.manual_callback = callback;
  720. return 0;
  721. }
  722. /**
  723. * \brief return the number of packets sent so far
  724. */
  725. COUNTER
  726. tcpreplay_get_pkts_sent(tcpreplay_t *ctx)
  727. {
  728. assert(ctx);
  729. ctx->static_stats.pkts_sent = ctx->stats.pkts_sent;
  730. return ctx->static_stats.pkts_sent;
  731. }
  732. /**
  733. * \brief return the number of bytes sent so far
  734. */
  735. COUNTER
  736. tcpreplay_get_bytes_sent(tcpreplay_t *ctx)
  737. {
  738. assert(ctx);
  739. ctx->static_stats.bytes_sent = ctx->stats.bytes_sent;
  740. return ctx->static_stats.bytes_sent;
  741. }
  742. /**
  743. * \brief return the number of failed attempts to send a packet
  744. */
  745. COUNTER
  746. tcpreplay_get_failed(tcpreplay_t *ctx)
  747. {
  748. assert(ctx);
  749. ctx->static_stats.failed = ctx->stats.failed;
  750. return ctx->static_stats.failed;
  751. }
  752. /**
  753. * \brief returns a pointer to the timeval structure of when replay first started
  754. */
  755. const struct timeval *
  756. tcpreplay_get_start_time(tcpreplay_t *ctx)
  757. {
  758. assert(ctx);
  759. memcpy(&ctx->static_stats.start_time, &ctx->stats.start_time, sizeof(ctx->stats.start_time));
  760. return &ctx->static_stats.start_time;
  761. }
  762. /**
  763. * \brief returns a pointer to the timeval structure of when replay finished
  764. */
  765. const struct timeval *
  766. tcpreplay_get_end_time(tcpreplay_t *ctx)
  767. {
  768. assert(ctx);
  769. memcpy(&ctx->static_stats.end_time, &ctx->stats.end_time, sizeof(ctx->stats.end_time));
  770. return &ctx->static_stats.end_time;
  771. }
  772. /**
  773. * \brief Internal function to set the tcpreplay error string
  774. *
  775. * Used to set the error string when there is an error, result is retrieved
  776. * using tcpedit_geterr(). You shouldn't ever actually call this, but use
  777. * tcpreplay_seterr() which is a macro wrapping this instead.
  778. */
  779. void
  780. __tcpreplay_seterr(tcpreplay_t *ctx, const char *func,
  781. const int line, const char *file, const char *fmt, ...)
  782. {
  783. va_list ap;
  784. char errormsg[TCPREPLAY_ERRSTR_LEN];
  785. assert(ctx);
  786. assert(file);
  787. assert(func);
  788. assert(line);
  789. va_start(ap, fmt);
  790. if (fmt != NULL) {
  791. (void)vsnprintf(errormsg,
  792. (TCPREPLAY_ERRSTR_LEN - 1), fmt, ap);
  793. }
  794. va_end(ap);
  795. #ifdef DEBUG
  796. snprintf(ctx->errstr, (TCPREPLAY_ERRSTR_LEN -1), "From %s:%s() line %d:\n%s",
  797. file, func, line, errormsg);
  798. #else
  799. snprintf(ctx->errstr, (TCPREPLAY_ERRSTR_LEN -1), "%s", errormsg);
  800. #endif
  801. }
  802. /**
  803. * \brief Internal function to set the tcpedit warning string
  804. *
  805. * Used to set the warning string when there is an non-fatal issue, result is retrieved
  806. * using tcpedit_getwarn().
  807. */
  808. void
  809. tcpreplay_setwarn(tcpreplay_t *ctx, const char *fmt, ...)
  810. {
  811. va_list ap;
  812. assert(ctx);
  813. va_start(ap, fmt);
  814. if (fmt != NULL)
  815. (void)vsnprintf(ctx->warnstr, (TCPREPLAY_ERRSTR_LEN - 1), fmt, ap);
  816. va_end(ap);
  817. }
  818. /**
  819. * \brief Does all the prep work before calling tcpreplay_replay()
  820. *
  821. * Technically this validates our config options, preloads the tcpprep
  822. * cache file, loads the packet cache and anything else which might
  823. * cause a delay for starting to send packets with tcpreplay_replay()
  824. */
  825. int
  826. tcpreplay_prepare(tcpreplay_t *ctx)
  827. {
  828. char *intname, *ebuf;
  829. int int1dlt, int2dlt, i;
  830. int ret = 0;
  831. assert(ctx);
  832. ebuf = safe_malloc(SENDPACKET_ERRBUF_SIZE);
  833. /*
  834. * First, process the validations, basically the same we do in
  835. * tcpreplay_post_args() and AutoOpts
  836. */
  837. if (ctx->options->intf1_name == NULL) {
  838. tcpreplay_seterr(ctx, "%s", "You must specify at least one network interface");
  839. ret = -1;
  840. goto out;
  841. }
  842. if (ctx->options->source_cnt == 0) {
  843. tcpreplay_seterr(ctx, "%s", "You must specify at least one source pcap");
  844. ret = -1;
  845. goto out;
  846. }
  847. if (ctx->options->dualfile) {
  848. if (!(ctx->options->source_cnt >= 2)) {
  849. tcpreplay_seterr(ctx, "%s", "Dual file mode requires 2 or more pcap files");
  850. ret = -1;
  851. goto out;
  852. }
  853. if (ctx->options->source_cnt % 2 != 0) {
  854. tcpreplay_seterr(ctx, "%s", "Dual file mode requires an even number of pcap files");
  855. ret = -1;
  856. goto out;
  857. }
  858. }
  859. if (ctx->options->dualfile && ctx->options->cachedata != NULL) {
  860. tcpreplay_seterr(ctx, "%s", "Can't use dual file mode and tcpprep cache file together");
  861. ret = -1;
  862. goto out;
  863. }
  864. if ((ctx->options->dualfile || ctx->options->cachedata != NULL) &&
  865. ctx->options->intf2_name == NULL) {
  866. tcpreplay_seterr(ctx, "%s", "dual file mode and tcpprep cache files require two interfaces");
  867. }
  868. #ifndef HAVE_SELECT
  869. if (ctx->options->accurate == accurate_select) {
  870. tcpreplay_seterr(ctx, "%s", "tcpreplay_api not compiled with select support");
  871. ret = -1;
  872. goto out;
  873. }
  874. #endif
  875. if ((intname = get_interface(ctx->intlist, ctx->options->intf1_name)) == NULL) {
  876. if (!strncmp(OPT_ARG(INTF1), "netmap:", 7) || !strncmp(OPT_ARG(INTF1), "vale", 4))
  877. tcpreplay_seterr(ctx, "Unable to connect to netmap interface %s. Ensure netmap module is installed (see INSTALL).",
  878. OPT_ARG(INTF1));
  879. else
  880. tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF1));
  881. ret = -1;
  882. goto out;
  883. }
  884. /* open interfaces for writing */
  885. if ((ctx->intf1 = sendpacket_open(ctx->options->intf1_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) {
  886. tcpreplay_seterr(ctx, "Can't open %s: %s", ctx->options->intf1_name, ebuf);
  887. ret = -1;
  888. goto out;
  889. }
  890. int1dlt = sendpacket_get_dlt(ctx->intf1);
  891. if (ctx->options->intf2_name != NULL) {
  892. if ((intname = get_interface(ctx->intlist, ctx->options->intf2_name)) == NULL) {
  893. tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF2));
  894. ret = -1;
  895. goto out;
  896. }
  897. /* open interfaces for writing */
  898. if ((ctx->intf2 = sendpacket_open(ctx->options->intf2_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) {
  899. tcpreplay_seterr(ctx, "Can't open %s: %s", ctx->options->intf2_name, ebuf);
  900. ret = -1;
  901. goto out;
  902. }
  903. int2dlt = sendpacket_get_dlt(ctx->intf2);
  904. if (int2dlt != int1dlt) {
  905. tcpreplay_seterr(ctx, "DLT type mismatch for %s (%s) and %s (%s)",
  906. ctx->options->intf1_name, pcap_datalink_val_to_name(int1dlt),
  907. ctx->options->intf2_name, pcap_datalink_val_to_name(int2dlt));
  908. ret = -1;
  909. goto out;
  910. }
  911. }
  912. /*
  913. * Setup up the file cache, if required
  914. */
  915. if (ctx->options->preload_pcap) {
  916. /* Initialise each of the file cache structures */
  917. for (i = 0; i < ctx->options->source_cnt; i++) {
  918. ctx->options->file_cache[i].index = i;
  919. ctx->options->file_cache[i].cached = FALSE;
  920. ctx->options->file_cache[i].packet_cache = NULL;
  921. }
  922. }
  923. out:
  924. safe_free(ebuf);
  925. return ret;
  926. }
  927. /**
  928. * \brief sends the traffic out the interfaces
  929. *
  930. * Designed to be called in a separate thread if you need to. Blocks until
  931. * the replay is complete or you call tcpreplay_abort() in another thread.
  932. */
  933. int
  934. tcpreplay_replay(tcpreplay_t *ctx)
  935. {
  936. int rcode, loop, total_loops;
  937. char buf[64];
  938. assert(ctx);
  939. if (!ctx->options->source_cnt) {
  940. tcpreplay_seterr(ctx, "invalid source count: %d", ctx->options->source_cnt);
  941. return -1;
  942. }
  943. if (ctx->options->dualfile && ctx->options->source_cnt < 2) {
  944. tcpreplay_seterr(ctx, "invalid dualfile source count: %d", ctx->options->source_cnt);
  945. return -1;
  946. }
  947. init_timestamp(&ctx->stats.last_time);
  948. init_timestamp(&ctx->stats.last_print);
  949. init_timestamp(&ctx->stats.end_time);
  950. if (gettimeofday(&ctx->stats.start_time, NULL) < 0) {
  951. tcpreplay_seterr(ctx, "gettimeofday() failed: %s", strerror(errno));
  952. return -1;
  953. }
  954. if (ctx->options->stats >= 0) {
  955. if (format_date_time(&ctx->stats.start_time, buf, sizeof(buf)) > 0)
  956. printf("Test start: %s ...\n", buf);
  957. }
  958. ctx->running = true;
  959. total_loops = ctx->options->loop;
  960. loop = 0;
  961. /* main loop, when not looping forever (or until abort) */
  962. if (ctx->options->loop > 0) {
  963. while (ctx->options->loop-- && !ctx->abort) { /* limited loop */
  964. ++loop;
  965. if (ctx->options->stats == 0) {
  966. if (!ctx->unique_iteration || loop == ctx->unique_iteration)
  967. printf("Loop %d of %d...\n", loop, total_loops);
  968. else
  969. printf("Loop %d of %d (" COUNTER_SPEC " unique)...\n",
  970. loop, total_loops,
  971. ctx->unique_iteration);
  972. }
  973. if ((rcode = tcpr_replay_index(ctx)) < 0)
  974. return rcode;
  975. if (ctx->options->loop > 0) {
  976. if (!ctx->abort && ctx->options->loopdelay_ms > 0) {
  977. usleep(ctx->options->loopdelay_ms * 1000);
  978. gettimeofday(&ctx->stats.end_time, NULL);
  979. }
  980. if (ctx->options->stats == 0)
  981. packet_stats(&ctx->stats);
  982. }
  983. }
  984. } else {
  985. while (!ctx->abort) { /* loop forever unless user aborts */
  986. ++loop;
  987. if (ctx->options->stats == 0) {
  988. if (!ctx->unique_iteration || loop == ctx->unique_iteration)
  989. printf("Loop %d...\n", loop);
  990. else
  991. printf("Loop %d (" COUNTER_SPEC " unique)...\n", loop,
  992. ctx->unique_iteration);
  993. }
  994. if ((rcode = tcpr_replay_index(ctx)) < 0)
  995. return rcode;
  996. if (ctx->options->stats == 0 && !ctx->abort)
  997. packet_stats(&ctx->stats);
  998. }
  999. }
  1000. ctx->running = false;
  1001. if (ctx->options->stats >= 0) {
  1002. if (format_date_time(&ctx->stats.end_time, buf, sizeof(buf)) > 0)
  1003. printf("Test complete: %s\n", buf);
  1004. }
  1005. return 0;
  1006. }
  1007. /**
  1008. * \brief Abort the tcpreplay_replay execution.
  1009. *
  1010. * This might take a little while since tcpreplay_replay() only checks this
  1011. * once per packet (sleeping between packets can cause delays), however,
  1012. * this function returns once the signal has been sent and does not block
  1013. */
  1014. int
  1015. tcpreplay_abort(tcpreplay_t *ctx)
  1016. {
  1017. assert(ctx);
  1018. ctx->abort = true;
  1019. printf("sendpacket_abort\n");
  1020. if (ctx->intf1 != NULL)
  1021. sendpacket_abort(ctx->intf1);
  1022. if (ctx->intf2 != NULL)
  1023. sendpacket_abort(ctx->intf2);
  1024. return 0;
  1025. }
  1026. /**
  1027. * \brief Temporarily suspend tcpreplay_replay()
  1028. *
  1029. * This might take a little while since tcpreplay_replay() only checks this
  1030. * once per packet (sleeping between packets can cause delays), however,
  1031. * this function returns once the signal has been sent and does not block
  1032. *
  1033. * Note that suspending a running context can create odd timing
  1034. */
  1035. int
  1036. tcpreplay_suspend(tcpreplay_t *ctx)
  1037. {
  1038. assert(ctx);
  1039. ctx->suspend = true;
  1040. return 0;
  1041. }
  1042. /**
  1043. * \brief Restart tcpreplay_replay() after suspend
  1044. *
  1045. * Causes the worker thread to restart sending packets
  1046. */
  1047. int
  1048. tcpreplay_restart(tcpreplay_t *ctx)
  1049. {
  1050. assert(ctx);
  1051. ctx->suspend = false;
  1052. return 0;
  1053. }
  1054. /**
  1055. * \brief Tells you if the given tcpreplay context is currently suspended
  1056. *
  1057. * Suspended == running, but not sending packets
  1058. */
  1059. bool
  1060. tcpreplay_is_suspended(tcpreplay_t *ctx)
  1061. {
  1062. assert(ctx);
  1063. return ctx->suspend;
  1064. }
  1065. /**
  1066. * \brief Tells you if the tcpreplay context is running (not yet finished)
  1067. *
  1068. * Returns true even if it is suspended
  1069. */
  1070. bool
  1071. tcpreplay_is_running(tcpreplay_t *ctx)
  1072. {
  1073. assert(ctx);
  1074. return ctx->running;
  1075. }
  1076. /**
  1077. * \brief returns the current statistics during or after a replay
  1078. *
  1079. * For performance reasons, I don't bother to put a mutex around this and you
  1080. * don't need to either. Just realize that your values may be off by one until
  1081. * tcreplay_replay() returns.
  1082. */
  1083. const tcpreplay_stats_t *
  1084. tcpreplay_get_stats(tcpreplay_t *ctx)
  1085. {
  1086. const tcpreplay_stats_t *ptr;
  1087. assert(ctx);
  1088. /* copy stats over so they don't change while caller is using the buffer */
  1089. memcpy(&ctx->static_stats, &ctx->stats, sizeof(tcpreplay_stats_t));
  1090. ptr = &ctx->static_stats;
  1091. return ptr;
  1092. }
  1093. /**
  1094. * \brief returns the current number of sources/files to be sent
  1095. */
  1096. int
  1097. tcpreplay_get_source_count(tcpreplay_t *ctx)
  1098. {
  1099. assert(ctx);
  1100. return ctx->options->source_cnt;
  1101. }
  1102. /**
  1103. * \brief Returns the current source id being replayed
  1104. */
  1105. int
  1106. tcpreplay_get_current_source(tcpreplay_t *ctx)
  1107. {
  1108. assert(ctx);
  1109. return ctx->current_source;
  1110. }
  1111. /* vim: set tabstop=8 expandtab shiftwidth=4 softtabstop=4: */
  1112. /**
  1113. * \brief Sets printing of flow statistics
  1114. */
  1115. int tcpreplay_set_flow_stats(tcpreplay_t *ctx, bool value)
  1116. {
  1117. assert(ctx);
  1118. ctx->options->flow_stats = value;
  1119. return 0;
  1120. }
  1121. /**
  1122. * \brief Sets the flow expiry in seconds
  1123. */
  1124. int tcpreplay_set_flow_expiry(tcpreplay_t *ctx, int value)
  1125. {
  1126. assert(ctx);
  1127. ctx->options->flow_expiry = value;
  1128. return 0;
  1129. }
  1130. /**
  1131. * \brief Get whether to printof flow statistics
  1132. */
  1133. bool tcpreplay_get_flow_stats(tcpreplay_t *ctx)
  1134. {
  1135. assert(ctx);
  1136. return ctx->options->flow_stats;
  1137. return 0;
  1138. }
  1139. /**
  1140. * \brief Gets the flow expiry in seconds
  1141. */
  1142. int tcpreplay_get_flow_expiry(tcpreplay_t *ctx)
  1143. {
  1144. assert(ctx);
  1145. return ctx->options->flow_expiry;
  1146. }