PageRenderTime 26ms CodeModel.GetById 3ms RepoModel.GetById 0ms app.codeStats 0ms

/ndisc6-1.0.1/src/tcpspray.c

#
C | 446 lines | 358 code | 69 blank | 19 comment | 57 complexity | c62a21998866ae1e8d10f24c8c0797a5 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * tcpspray.c - Address family independant complete rewrite of tcpspray
  3. * Plus, this file has a clear copyright statement.
  4. * $Id: tcpspray.c 650 2010-05-01 08:08:34Z remi $
  5. */
  6. /*************************************************************************
  7. * Copyright Š 2006-2007 R?Šmi Denis-Courmont. *
  8. * This program is free software: you can redistribute and/or modify *
  9. * it under the terms of the GNU General Public License as published by *
  10. * the Free Software Foundation, versions 2 or 3 of the license. *
  11. * *
  12. * This program is distributed in the hope that it will be useful, *
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  15. * GNU General Public License for more details. *
  16. * *
  17. * You should have received a copy of the GNU General Public License *
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>. *
  19. *************************************************************************/
  20. #ifdef HAVE_CONFIG_H
  21. # include <config.h>
  22. #endif
  23. #include <gettext.h>
  24. #include <stdbool.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <stdint.h> // uint8_t, SIZE_MAX
  28. #include <limits.h> // SIZE_MAX on Solaris (non-standard)
  29. #ifndef SIZE_MAX
  30. # define SIZE_MAX SIZE_T_MAX // FreeBSD 4.x workaround
  31. #endif
  32. #include <stdlib.h>
  33. #include <sys/types.h>
  34. #include <unistd.h>
  35. #include <errno.h>
  36. #include <sys/socket.h>
  37. #include <time.h>
  38. #include <sys/time.h>
  39. #include <sys/wait.h>
  40. #include <fcntl.h>
  41. #include <netdb.h>
  42. #include <netinet/in.h>
  43. #include <locale.h>
  44. #ifdef HAVE_GETOPT_H
  45. # include <getopt.h>
  46. #endif
  47. #include "gettime.h"
  48. #ifndef AI_IDN
  49. # define AI_IDN 0
  50. #endif
  51. static int family = 0;
  52. static unsigned verbose = 0;
  53. static int tcpconnect (const char *host, const char *serv)
  54. {
  55. struct addrinfo hints, *res;
  56. memset (&hints, 0, sizeof (hints));
  57. hints.ai_family = family;
  58. hints.ai_socktype = SOCK_STREAM;
  59. hints.ai_protocol = IPPROTO_TCP;
  60. hints.ai_flags = AI_IDN;
  61. int val = getaddrinfo (host, serv, &hints, &res);
  62. if (val)
  63. {
  64. fprintf (stderr, _("%s port %s: %s\n"), host, serv,
  65. gai_strerror (val));
  66. return -1;
  67. }
  68. val = -1;
  69. for (struct addrinfo *p = res; (p != NULL) && (val == -1); p = p->ai_next)
  70. {
  71. val = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
  72. if (val == -1)
  73. {
  74. perror ("socket");
  75. continue;
  76. }
  77. setsockopt (val, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
  78. fcntl (val, F_SETFD, FD_CLOEXEC);
  79. if (connect (val, p->ai_addr, p->ai_addrlen))
  80. {
  81. fprintf (stderr, _("%s port %s: %s\n"), host, serv,
  82. strerror (errno));
  83. close (val);
  84. val = -1;
  85. continue;
  86. }
  87. }
  88. freeaddrinfo (res);
  89. return val;
  90. }
  91. static void
  92. print_duration (const char *msg,
  93. const struct timespec *end, const struct timespec *start,
  94. unsigned long bytes)
  95. {
  96. double duration = ((double)end->tv_sec)
  97. + ((double)end->tv_nsec) / 1000000000
  98. - ((double)start->tv_sec)
  99. - ((double)start->tv_nsec) / 1000000000;
  100. printf (_("%s %lu %s in %f %s"), gettext (msg),
  101. bytes, ngettext ("byte", "bytes", bytes),
  102. duration, ngettext ("second", "seconds", duration));
  103. if (duration > 0)
  104. printf (_(" (%0.3f kbytes/s)"), bytes / (duration * 1024));
  105. }
  106. static int
  107. tcpspray (const char *host, const char *serv, unsigned long n, size_t blen,
  108. unsigned delay_us, const char *fillname, bool echo)
  109. {
  110. if (serv == NULL)
  111. serv = echo ? "echo" : "discard";
  112. int fd = tcpconnect (host, serv);
  113. if (fd == -1)
  114. return -1;
  115. uint8_t block[blen];
  116. memset (block, 0, blen);
  117. if (fillname != NULL)
  118. {
  119. FILE *stream = fopen (fillname, "r");
  120. if (stream == NULL)
  121. {
  122. perror (fillname);
  123. close (fd);
  124. return -1;
  125. }
  126. size_t res = fread (block, 1, blen, stream);
  127. if (res < blen)
  128. {
  129. fprintf (stderr, _("Warning: \"%s\" is too small (%zu %s) "
  130. "to fill block of %zu %s.\n"), fillname,
  131. res, ngettext ("byte", "bytes", res),
  132. blen, ngettext ("byte", "bytes", blen));
  133. }
  134. fclose (stream);
  135. }
  136. if (verbose)
  137. {
  138. printf (_("Sending %ju %s with blocksize %zu %s\n"),
  139. (uintmax_t)n * blen, ngettext ("byte", "bytes", n * blen),
  140. blen, ngettext ("byte", "bytes", n * blen));
  141. }
  142. pid_t child = -1;
  143. if (echo)
  144. {
  145. child = fork ();
  146. switch (child)
  147. {
  148. case 0:
  149. for (unsigned i = 0; i < n; i++)
  150. {
  151. ssize_t val = recv (fd, block, blen, MSG_WAITALL);
  152. if (val != (ssize_t)blen)
  153. {
  154. fprintf (stderr, _("Receive error: %s\n"),
  155. (val == -1) ? strerror (errno)
  156. : _("Connection closed by peer"));
  157. exit (1);
  158. }
  159. if (verbose)
  160. fputs ("\b \b", stdout);
  161. }
  162. exit (0);
  163. case -1:
  164. perror ("fork");
  165. close (fd);
  166. return -1;
  167. }
  168. }
  169. else
  170. shutdown (fd, SHUT_RD);
  171. struct timespec delay_ts = { 0, 0 };
  172. if (delay_us)
  173. {
  174. div_t d = div (delay_us, 1000000);
  175. delay_ts.tv_sec = d.quot;
  176. delay_ts.tv_nsec = d.rem * 1000;
  177. }
  178. struct timespec start, end;
  179. mono_gettime (&start);
  180. for (unsigned i = 0; i < n; i++)
  181. {
  182. ssize_t val = write (fd, block, blen);
  183. if (val != (ssize_t)blen)
  184. {
  185. fprintf (stderr, _("Cannot send data: %s\n"),
  186. (val == -1) ? strerror (errno)
  187. : _("Connection closed by peer"));
  188. goto abort;
  189. }
  190. if (verbose)
  191. fputc ('.', stdout);
  192. if (delay_us && mono_nanosleep (&delay_ts))
  193. goto abort;
  194. }
  195. mono_gettime (&end);
  196. shutdown (fd, SHUT_WR);
  197. close (fd);
  198. if (child != -1)
  199. {
  200. int status;
  201. while (wait (&status) == -1);
  202. if (!WIFEXITED (status) || WEXITSTATUS (status))
  203. {
  204. fprintf (stderr, _("Child process returned an error"));
  205. return -1;
  206. }
  207. struct timespec end_recv;
  208. mono_gettime (&end_recv);
  209. print_duration (N_("Received"), &end_recv, &start, blen * n);
  210. }
  211. puts ("");
  212. print_duration (N_("Transmitted"), &end, &start, blen * n);
  213. puts ("");
  214. return 0;
  215. abort:
  216. close (fd);
  217. if (child != -1)
  218. {
  219. kill (child, SIGTERM);
  220. while (wait (NULL) == -1);
  221. }
  222. return -1;
  223. }
  224. static int
  225. quick_usage (const char *path)
  226. {
  227. fprintf (stderr, _("Try \"%s -h\" for more information.\n"), path);
  228. return 2;
  229. }
  230. static int
  231. usage (const char *path)
  232. {
  233. printf (_(
  234. "Usage: %s [options] <hostname/address> [service/port number]\n"
  235. "Use the discard TCP service at the specified host\n"
  236. "(the default host is the local system, the default service is discard)\n"),
  237. path);
  238. puts (_("\n"
  239. " -4 force usage of the IPv4 protocols family\n"
  240. " -6 force usage of the IPv6 protocols family\n"
  241. " -b specify the block bytes size (default: 1024)\n"
  242. " -d wait for given delay (usec) between each block (default: 0)\n"
  243. " -e perform a duplex test (TCP Echo instead of TCP Discard)\n"
  244. " -f fill sent data blocks with the specified file content\n"
  245. " -h display this help and exit\n"
  246. " -n specify the number of blocks to send (default: 100)\n"
  247. " -V display program version and exit\n"
  248. " -v enable verbose output\n"
  249. ));
  250. return 0;
  251. }
  252. static int
  253. version (void)
  254. {
  255. printf (_(
  256. "tcpspray6: TCP/IP bandwidth tester %s (%s)\n"), VERSION, "$Rev: 650 $");
  257. printf (_(" built %s on %s\n"), __DATE__, PACKAGE_BUILD_HOSTNAME);
  258. printf (_("Configured with: %s\n"), PACKAGE_CONFIGURE_INVOCATION);
  259. puts (_("Written by Remi Denis-Courmont\n"));
  260. printf (_("Copyright (C) %u-%u Remi Denis-Courmont\n"), 2005, 2007);
  261. puts (_("This is free software; see the source for copying conditions.\n"
  262. "There is NO warranty; not even for MERCHANTABILITY or\n"
  263. "FITNESS FOR A PARTICULAR PURPOSE.\n"));
  264. return 0;
  265. }
  266. static const struct option opts[] =
  267. {
  268. { "ipv4", no_argument, NULL, '4' },
  269. { "ipv6", no_argument, NULL, '6' },
  270. { "bsize", required_argument, NULL, 'b' },
  271. { "delay", required_argument, NULL, 'd' },
  272. { "echo", no_argument, NULL, 'e' },
  273. { "file", required_argument, NULL, 'f' },
  274. { "fill", required_argument, NULL, 'f' },
  275. { "help", no_argument, NULL, 'h' },
  276. { "count", required_argument, NULL, 'n' },
  277. { "version", no_argument, NULL, 'V' },
  278. { "verbose", no_argument, NULL, 'v' },
  279. { NULL, 0, NULL, 0 }
  280. };
  281. static const char optstr[] = "46b:d:ef:hn:Vv";
  282. int main (int argc, char *argv[])
  283. {
  284. setlocale (LC_ALL, "");
  285. bindtextdomain (PACKAGE, LOCALEDIR);
  286. textdomain (PACKAGE);
  287. unsigned long block_count = 100;
  288. size_t block_length = 1024;
  289. unsigned delay_ms = 0;
  290. bool echo = false;
  291. const char *fillname = NULL;
  292. int c;
  293. while ((c = getopt_long (argc, argv, optstr, opts, NULL)) != EOF)
  294. {
  295. switch (c)
  296. {
  297. case '4':
  298. family = AF_INET;
  299. break;
  300. case '6':
  301. family = AF_INET6;
  302. break;
  303. case 'b':
  304. {
  305. char *end;
  306. unsigned long value = strtoul (optarg, &end, 0);
  307. if (*end)
  308. errno = EINVAL;
  309. else
  310. if (value > SIZE_MAX)
  311. errno = ERANGE;
  312. if (errno)
  313. {
  314. perror (optarg);
  315. return 2;
  316. }
  317. block_length = (size_t)value;
  318. break;
  319. }
  320. case 'd':
  321. {
  322. char *end;
  323. unsigned long value = strtoul (optarg, &end, 0);
  324. if (*end)
  325. errno = EINVAL;
  326. else
  327. if (value > UINT_MAX)
  328. errno = ERANGE;
  329. if (errno)
  330. {
  331. perror (optarg);
  332. return 2;
  333. }
  334. delay_ms = (unsigned)value;
  335. break;
  336. }
  337. case 'e':
  338. echo = true;
  339. break;
  340. case 'f':
  341. fillname = optarg;
  342. break;
  343. case 'h':
  344. return usage (argv[0]);
  345. case 'n':
  346. {
  347. char *end;
  348. block_count = strtoul (optarg, &end, 0);
  349. if (*end)
  350. errno = EINVAL;
  351. if (errno)
  352. {
  353. perror (optarg);
  354. return 2;
  355. }
  356. break;
  357. }
  358. case 'V':
  359. return version ();
  360. case 'v':
  361. if (verbose < UINT_MAX)
  362. verbose++;
  363. break;
  364. case '?':
  365. default:
  366. return quick_usage (argv[0]);
  367. }
  368. }
  369. if (optind >= argc)
  370. return quick_usage (argv[0]);
  371. const char *hostname = argv[optind++];
  372. const char *servname = (optind < argc) ? argv[optind++] : NULL;
  373. setvbuf (stdout, NULL, _IONBF, 0);
  374. c = tcpspray (hostname, servname, block_count, block_length,
  375. delay_ms, fillname, echo);
  376. return c ? 1 : 0;
  377. }