netsend-0.0~svnr250/0000755000175000017500000000000010776173365013734 5ustar martinmartinnetsend-0.0~svnr250/tcp_md5sig.h0000644000175000017500000000102710776173116016135 0ustar martinmartin#include #include #ifndef TCP_MD5SIG_MAXKEYLEN #define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ #define TCP_MD5SIG_MAXKEYLEN 80 struct tcp_md5sig { struct sockaddr_storage tcpm_addr; /* address associated */ uint16_t __tcpm_pad1; /* zero */ uint16_t tcpm_keylen; /* key length */ uint32_t __tcpm_pad2; /* zero */ uint8_t tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ }; #endif netsend-0.0~svnr250/TODO0000644000175000017500000000202110776173116014411 0ustar martinmartin# $Id: TODO 183 2007-11-22 02:43:20Z fwest $ o Extend the configure script to figure out the host operating system (exit != linux) and save major, minor and patchlevel to config.h. This offers the ability to substitute sendfile with splice and to exclude dccp header values o replace gettimeofday() with clock_gettime() o Socket option processing IP: SO_DEBUG SO_DONTROUTE SO_BROADCAST SO_REUSEADDR SO_KEEPALIVE SO_TYPE SO_ERROR SO_OOBINLINE SO_NO_CHECK SO_PRIORITY SO_LINGER SO_BSDCOMPAT ;-) SO_TIMESTAMP SO_RCVTIMEO SO_SNDTIMEO SO_RCVLOWAT SO_SNDLOWAT SO_PASSCRED SO_PEERCRED SO_PEERNAME SO_ACCEPTCONN SO_PEERSEC TCP: TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT TCP_SYNCNT TCP_LINGER2 TCP_DEFER_ACCEPT TCP_WINDOW_CLAMP TCP_QUICKACK TCP_INFO o Collect Failures and document for user help 1. ERROR [receive.c:202]: setsockopt (IP_ADD_MEMBERSHIP) failed (No such device) a) No kernel support b) No route (sudo ip r add 244.0.0.0/8 dev eth0) o Many more - your ideas here! ;-) netsend-0.0~svnr250/proto_tipc.c0000644000175000017500000000717510776173116016266 0ustar martinmartin/* * netsend - a high performance filetransfer and diagnostic tool * http://netsend.berlios.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include "global.h" #include "debug.h" #include "proto_tipc.h" #include "xfuncs.h" extern struct opts opts; extern struct sock_callbacks sock_callbacks; #ifdef HAVE_AF_TIPC #define NETSEND_TIPC_SERVER 231337 /* * TIPC does not support connect() for SOCK_DGRAM and SOCK_RDM at this time, * so we need to use sendto instead of write */ static struct sockaddr_tipc *sendto_dest_addr; static struct sockaddr_tipc sa_tipc_get(void) { struct sockaddr_tipc sa_tipc = { .family = AF_TIPC,.scope = TIPC_ZONE_SCOPE }; assert(opts.family == AF_TIPC); sa_tipc.addr.nameseq.type = NETSEND_TIPC_SERVER; return sa_tipc; } int tipc_socket_bind(void) { int fd; struct sockaddr_tipc sa_tipc = sa_tipc_get(); sa_tipc.addrtype = TIPC_ADDR_NAMESEQ; fd = socket(AF_TIPC, opts.socktype, opts.protocol); if (fd < 0) err_sys_die(EXIT_FAILNET, "socket"); if (bind(fd, (struct sockaddr*)&sa_tipc, sizeof(sa_tipc))) err_sys_die(EXIT_FAILNET, "bind"); sock_callbacks.cb_listen = tipc_listen; switch (opts.socktype) { case SOCK_RDM: case SOCK_DGRAM: assert(sendto_dest_addr == NULL); sendto_dest_addr = xmalloc(sizeof(*sendto_dest_addr)); *sendto_dest_addr = sa_tipc; sock_callbacks.cb_write = tipc_write; } return fd; } int tipc_socket_connect(void) { int fd; struct sockaddr_tipc sa_tipc = sa_tipc_get(); sa_tipc.addrtype = TIPC_ADDR_NAME; fd = socket(AF_TIPC, opts.socktype, opts.protocol); if (fd < 0) err_sys_die(EXIT_FAILNET, "socket"); switch (opts.socktype) { case SOCK_STREAM: case SOCK_SEQPACKET: if (connect(fd, (struct sockaddr*)&sa_tipc, sizeof(sa_tipc))) err_sys_die(EXIT_FAILNET, "connect"); return fd; case SOCK_RDM: case SOCK_DGRAM: assert(sendto_dest_addr == NULL); sendto_dest_addr = xmalloc(sizeof(*sendto_dest_addr)); *sendto_dest_addr = sa_tipc; sock_callbacks.cb_write = tipc_write; sock_callbacks.cb_accept = tipc_accept; return fd; } return -1; } int tipc_listen(int sockfd, int log) { assert(opts.family == AF_TIPC); switch (opts.socktype) { case SOCK_RDM: case SOCK_DGRAM: return 0; case SOCK_STREAM: case SOCK_SEQPACKET: return listen(sockfd, log); } return -1; } int tipc_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { assert(opts.family == AF_TIPC); switch (opts.socktype) { case SOCK_RDM: case SOCK_DGRAM: return sockfd; case SOCK_STREAM: case SOCK_SEQPACKET: return accept(sockfd, addr, addrlen); } return -1; } ssize_t tipc_write(int sockfd, const void *buf, size_t buflen) { assert(sendto_dest_addr != NULL); return sendto(sockfd, buf, buflen, 0, (struct sockaddr*)sendto_dest_addr, (socklen_t) sizeof(*sendto_dest_addr)); } #endif /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/netsend.pod0000644000175000017500000000771110776173116016100 0ustar martinmartin=head1 NAME netsend - a speedy filetransfer and network diagnostic program =head1 SYNOPSIS netsend [OPTIONS] PROTOCOL MODE { COMMAND | HELP } =head1 DESCRIPTION =head1 PROTOCOL Protocol is one of =over 4 =item B, B, B, B, B or B. =back When using tipc, you must also specify a socket type, e.g. B. =head1 MODE Mode is either B or B. =head1 OPTIONS =over 4 =item B<-r Nn,Nd,Nm,Nf> Round trip probes options: Nn - Number of iterations of round trip probes. Default is to perform 10 attempts. Don't set to less then 5 because measurement results will not very predicating. Nd - Size of rtt payload. This is the number of bytes piggybacking (plus the netsend rtt header). Default is 500 byte, maybe your mtu minus netsend header minus protocol header (tcp, udp) will better fit for your needs. Nm - for the round trip time probes netsend calculates a deviation. With this flag you can adjust the filter. Default is 4. Lower values drain more probes out, so be carefully with this option. For example: if you have measured rtt probes of 4, 5, 5, 6 and 15 ms. The average is 7. Covariance is 16.4 and deviation is 4.04. If you select 2 here as the multiplier, then you filter all rtt probes with higher values then 8.08 (for this example you filer 15ms out). This will help to discard some nonesense probes who are evoked through cold code paths (cache misses, page faults, ...) or network anomalies. Use this option carefully! -f forces to don't perform rtt probes but take N milliseconds as average value. With this option you can figure out the behaviour of satelite links (e.g you say -D500f) =item B<-b> followed by a number: sets read/write buffer size to use. Default is 8192 for read/write and size_of_file_to_send for mmap/sendfile. =item B<-m> followed by a memadvise(2) option: normal, sequential, random, willneed, dontneed, noreuse. =item B<-p> followed by a number: set TCP/UDP/DCCP/SCTP port to use. Default is 6666. =item B<-P> followed by scheduling policy: sched_rr, sched_fifo, sched_batch or sched_other =item B<-s> followed by a setsockopt(2) optname and optval. netsend maps setsockopt levels and optlen internally. running 'netsend -s list' will print a list of all setsockopt optnames currently recognized by netsend. =item B<-T> followed by either human or machine: sets output format =item B<-u> followed by the transmit function to use. One of sendfile, mmap, splice or rw. When not specified, rw (read/write) is used. Note that not all protocols support all transfer methods, e.g. TIPCs connectionless sockets (SOCK_RDM and SOCK_DGRAM) do not support the sendfile system call. Also, the amount of data that can be sent in a single operation may be limited by the network protocol used. =back =head1 EXAMPLES =over 1 Listen for incoming SCTP connections, incoming data goes to stdout: =over 4 ./netsend -T human -v stressful sctp receive =back =over 1 Send file largefile via TCP with output in machine parseable format: =over 4 ./netsend -T machine tcp transmit largefile host.example.org =back =over 1 Receive data via TCP with MD5SIG from peer 10.0.0.1: =over 4 ./netsend tcp transmit -C largefile ffff::10.0.0.1 ./netsend tcp receive -C ffff::10.0.0.1 =back =head1 EXIT STATUS netsend returns a zero exist status if it succeeds. Non zero is returned in case of failure. Following failure codes are implemented: 0 - succeed 1 - failure in memory handling 2 - command line option error 3 - failure which fit in any categories 4 - network error 5 - failure in netsend header (maybe corrupted hardware) 6 - netsend internal error (should never happen[tm]) =head1 AUTHOR Hagen Paul Pfeifer Florian Westphal =head1 SEE ALSO http://netsend.berlios.de netsend-0.0~svnr250/file.c0000644000175000017500000000463710776173116015023 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "global.h" #include "xfuncs.h" extern struct opts opts; int open_input_file(void) { int fd, ret; struct stat stat_buf; if (!strncmp(opts.infile, "-", 1)) return STDIN_FILENO; /* open a regular file and take the content as our source. */ ret = stat(opts.infile, &stat_buf); if (ret == -1) err_sys_die(EXIT_FAILMISC, "Can't stat file %s", opts.infile); #ifdef O_NOATIME fd = open(opts.infile, O_RDONLY|O_NOATIME); #else fd = open(opts.infile, O_RDONLY); #endif if (fd == -1) err_msg_die(EXIT_FAILMISC, "Can't open input file: %s", opts.infile); return fd; } /* open our outfile */ int open_output_file(void) { int fd = 0; if (!opts.outfile) return STDOUT_FILENO; if (!strncmp(opts.outfile, "-", 1)) return STDOUT_FILENO; umask(0); fd = open(opts.outfile, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP); if (fd == -1) { struct stat s; if (errno != EEXIST) err_sys_die(EXIT_FAILOPT, "Can't create outputfile: %s", opts.outfile); fd = open(opts.outfile, O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (fd == -1) err_sys_die(EXIT_FAILOPT, "Can't open outputfile: %s", opts.outfile); xfstat(fd, &s, opts.outfile); if (S_ISREG(s.st_mode)) /* symblic link that pointed to regular file */ err_sys_die(EXIT_FAILOPT, "Can't create outputfile: %s", opts.outfile); /* else file is a named pipe, socket, etc. */ } return fd; } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/error.c0000644000175000017500000000507410776173116015231 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include /* TODO: extend our configure script to loop for declaration */ #define HAVE_BACKTRACE #include #include #include "global.h" #define MAXERRMSG 1024 extern struct opts opts; void msg(const int level, const char *format, ...) { va_list ap; struct timeval tv; if (opts.verbose < level) return; if (opts.verbose > LOUDISH) { gettimeofday(&tv, NULL); fprintf(stderr, "[%ld.%06ld] ", tv.tv_sec, tv.tv_usec); } va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputs("\n", stderr); } static void err_doit(int sys_error, const char *file, const int line_no, const char *fmt, va_list ap) { int errno_save; char buf[MAXERRMSG]; errno_save = errno; vsnprintf(buf, sizeof buf -1, fmt, ap); if (sys_error) { size_t len = strlen(buf); snprintf(buf + len, sizeof buf - len, " (%s)", strerror(errno_save)); } fprintf(stderr, "ERROR [%s:%d]: %s\n", file, line_no, buf); fflush(NULL); } void x_err_ret(const char *file, int line_no, const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(0, file, line_no, fmt, ap); va_end(ap); return; } void x_err_sys(const char *file, int line_no, const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(1, file, line_no, fmt, ap); va_end(ap); } void print_bt(void) { void *bt[128]; int bt_size; char **bt_syms; int i; bt_size = backtrace(bt, 128); bt_syms = backtrace_symbols(bt, bt_size); fputs("BACKTRACE:\n", stderr); for(i = 1; i < bt_size; i++) { fprintf(stderr, "#%2d %s\n", i - 1, bt_syms[i]); } fputs("\n", stderr); free(bt_syms); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/getopt.c0000644000175000017500000007437410776173116015413 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "xfuncs.h" /* This is the overall parsing procedure: * * parse_opts() * - set some default values (e.g. program name) * - parse the global (protocol independent arguments) * - find the proper transport level protocol and branch * to this protocol (parse_tcp_opts, parse_tipc_opt, ...) * - set values gathered through parsing process (e.g. the cli * switch -6 turn AF_INET into AF_INET6 * parse_XXX_opts() * - set protocol specific default values * - parse protocol specific options * - branch to receive or transmit mode and parse * even more options, ... ok, ok enough parsing * dump_opts() * - if the user select -d(ump) netsend will display * the user selected and default values * dump_XXX_opts() * - print protocol specific options and default. */ extern struct opts opts; extern struct conf_map_t memadvice_map[]; extern struct conf_map_t io_call_map[]; extern struct socket_options socket_options[]; /* The following array contains the whole cli usage screen. * E.g.: help_str[HELP_STR_GLOBAL] points out to the global * screen, where help_str[HELP_STR_TCP] only points out * to the tcp specific command line usage. There are some helper * function which wraps the access functionality to this array. * See print_usage() and print_complete_usage() - try to access * the array via these functions! */ static const char const help_str[][4096] = { #define HELP_STR_GLOBAL 0 "Usage: netsend [OPTIONS] PROTOCOL MODE { COMMAND | HELP } [filename] [hostname]\n" " OPTIONS := { -T FORMAT | -6 | -4 | -n | -d | -r RTTPROBE | -P SCHED-POLICY | -N level\n" " -m MEM-ADVISORY | -V[version] | -v[erbose] LEVEL | -h[elp] | -a[ll-options] }\n" " -p PORT -s SETSOCKOPT_OPTNAME _OPTVAL -b READWRITE_BUFSIZE -u SEND-ROUTINE\n" " -P \n" " PROTOCOL := { tcp | udp | dccp | tipc | sctp | udplite }\n" " COMMAND := { UDP-OPTIONS | UDPL-OPTIONS | SCTP-OPTIONS | DCCP-OPTIONS | TIPC-OPTIONS | TCP-OPTIONS }\n" " MODE := { receive | transmit }\n" " FORMAT := { human | machine }\n" " SEND-ROUTINE := { mmap | sendfile | splice | rw }\n" " RTTPROBE := { 10n,10d,10m,10f }\n" " MEM-ADVISORY := { normal | sequential | random | willneed | dontneed | noreuse }\n" " SCHED-POLICY := { sched_rr | sched_fifo | sched_batch | sched_other } priority\n" " LEVEL := { quitscent | gentle | loudish | stressful }", #define HELP_STR_TCP 1 " CC-ALGORITHM := -s TCP_CONGESTION { bic | cubic | highspeed | htcp | hybla | scalable | vegas | westwood | reno }\n" " TCP_MD5SIG := -C [ peer-IP-Address ] (receive mode only)", #define HELP_STR_UDP 2 " UDP-OPTIONS := [ FIXME ]", #define HELP_STR_UDPLITE 3 " UDPL-OPTIONS := [ -C ]", #define HELP_STR_SCTP 4 " SCTP_DISABLE_FRAGMENTS " #define HELP_STR_DCCP 5 " DCCP-OPTIONS := { }", #define HELP_STR_TIPC 6 " TIPC-OPTIONS := { -t TIPCSOCKTYP }\n" " TIPCSOCKTYP := { sock_rdm | sock_dgram | sock_stream | sock_seqpacket }", #define HELP_STR_MAX HELP_STR_TIPC /* since here additional help strings are located - * Normally these are additional, smart or quick help strings * for the user. */ #define HELP_STR_VERBOSE_LEVEL 7 " LEVEL := { quitscent | gentle | loudish | stressful }", #define HELP_STR_SCHED_POLICY 8 " SCHED-POLICY := { fifo | rr | batch | other }", #define HELP_STR_MEM_ADVICE 9 " MEM-ADVISORY := { normal | sequential | random | willneed | dontneed | noreuse }", #define HELP_STR_IO_ADVICE 10 " IO-CALL := { mmap | sendfile | splice | rw }" }; /** * print_usage is our quick failure routine for cli parsing. * You can specify an additional prefix, the mode for what the * the function should print the usage screen {all or protocol * specific} and last but not least if this function should be * the exit function */ static void print_usage(const char const *prefix_str, unsigned int mode, int should_exit) { if (prefix_str != NULL) fprintf(stderr, "%s\n%s\n", prefix_str, help_str[mode]); else fprintf(stderr, "%s\n", help_str[mode]); if (should_exit) exit(EXIT_FAILOPT); } static int parse_yesno(const char *optname, const char *optval) { if (strcmp(optval, "1") == 0) return 1; if (strcmp(optval, "0") == 0) return 0; if (strcasecmp(optval, "on") == 0) return 1; if (strcasecmp(optval, "yes") == 0) return 1; if (strcasecmp(optval, "no") == 0) return 0; if (strcasecmp(optval, "off") == 0) return 0; err_msg("%s: unrecognized optval \"%s\" (only 0/1 allowed); assuming 0", optname, optval); return 0; } /* return number of characters parsed (ie amount of digits) */ static int scan_int(const char *str, int *val) { char *endptr; long num; size_t parsed; num = strtol(str, &endptr, 0); parsed = endptr - str; if (parsed) { if (num > INT_MAX) err_msg("%s > INT_MAX", str); if (num < INT_MIN) err_msg("%s < INT_MIN", str); *val = (int) num; } return parsed; } static const char *setsockopt_optvaltype_tostr(enum sockopt_val_types x) { switch (x) { case SVT_BOOL: return "[ 0 | 1 ]"; case SVT_INT: return "number"; case SVT_STR: return "string"; } return ""; } static const char *setsockopt_level_tostr(int level) { switch (level) { case SOL_SOCKET: return "SOL_SOCKET"; case SOL_TCP: return "SOL_TCP"; case IPPROTO_SCTP: return "IPROTO_SCTP"; case IPPROTO_UDPLITE: return "IPPROTO_UDPLITE"; } return NULL; } static void die_print_setsockopts(void) { unsigned i; fputs("Known setsockopt optnames:\n", stderr); fputs("level\toptname\t\toptval\n", stderr); for (i = 0; socket_options[i].sockopt_name; i++) { fprintf(stderr, "%s\t%s\t\t%s\n", setsockopt_level_tostr(socket_options[i].level), socket_options[i].sockopt_name, setsockopt_optvaltype_tostr(socket_options[i].sockopt_type)); } exit(EXIT_FAILOPT); } static void parse_setsockopt_name(const char *optname, const char *optval) { unsigned i; for (i = 0; socket_options[i].sockopt_name; i++) { if (strcasecmp(optname, socket_options[i].sockopt_name)) continue; switch (socket_options[i].sockopt_type) { case SVT_BOOL: socket_options[i].value = parse_yesno(optname, optval); socket_options[i].user_issue++; return; case SVT_INT: if (scan_int(optval, &socket_options[i].value)) socket_options[i].user_issue++; else err_msg("%s: unrecognized optval \"%s\" " "(integer argument required);skipped", optval, optname); return; case SVT_STR: socket_options[i].value_ptr = optval; socket_options[i].user_issue++; return; default: err_msg("WARNING: Internal error: unrecognized " "sockopt_type (%s %s) (%s:%u)", optval, optname, __FILE__, __LINE__); return; } } err_msg("Unrecognized sockopt \"%s\" ignored", optname ); } /* parse_tcp_opt set all tcp default values * within optsp and parse all tcp related options * ac is the number of arguments from MODE and av is * the correspond pointer into the array vector. We parse all tcp * related options first and within the switch/case statement we handle * transmit | receive specific options. */ static int parse_tcp_opt(int ac, char *av[], struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_TCP; /* tcp has some default values too, set them here */ optsp->perform_rtt_probe = 1; optsp->protocol = IPPROTO_TCP; optsp->socktype = SOCK_STREAM; while (av[0] && av[0][0] == '-') { if (av[0][1] == 'C') optsp->tcp_use_md5sig = true; if (optsp->workmode == MODE_RECEIVE) { /* need peer ip address */ if (!av[1] || av[1][0] == '-') err_msg_die(EXIT_FAILOPT, "Option -C needs an argument (Peer IP Address)"); optsp->tcp_md5sig_peeraddr = av[1]; ac--; av++; } ac--; av++; } if (optsp->tcp_use_md5sig) msg(GENTLE, "Enabled TCP_MD5SIG option"); /* Now parse all transmit | receive specific code, plus the most * important options: the file- and hostname */ switch (optsp->workmode) { case MODE_TRANSMIT: /* sanity check first */ if (ac <= 1) print_usage("tcp transmit mode required file and destination address\n", HELP_STR_GLOBAL, 1); optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_RECEIVE: switch (ac) { case 0: /* nothing to do */ break; case 2: opts.hostname = xstrdup(av[1]); /* fallthrough */ case 1: opts.outfile = xstrdup(av[0]); break; default: err_msg("You specify to many arguments!"); print_usage(NULL, HELP_STR_GLOBAL, 1); break; }; break; default: err_msg_die(EXIT_FAILINT, "Internal, programmed error - unknown tranmit mode: %d\n", optsp->workmode); } return SUCCESS; } static void dump_tcp_opt(struct opts *optsp) { switch (optsp->workmode) { case MODE_TRANSMIT: fprintf(stdout, "# workmode: transmit\n"); fprintf(stdout, "# destination host: %s\n", optsp->hostname); fprintf(stdout, "# filename: %s\n", optsp->infile); break; case MODE_RECEIVE: fprintf(stdout, "# workmode: receive\n"); break; default: err_msg_die(EXIT_FAILMISC, "Programmed Failure"); break; }; if (optsp->perform_rtt_probe) { fprintf(stdout, "# perform rtt probe: true\n"); } else { fprintf(stdout, "# perform rtt probe: false\n"); } } #ifdef HAVE_AF_TIPC #include static const struct { int socktype; const char *sockname; } socktype_map[] = { { SOCK_RDM, "SOCK_RDM" }, { SOCK_DGRAM, "SOCK_DGRAM" }, { SOCK_STREAM, "SOCK_STREAM" }, { SOCK_SEQPACKET, "SOCK_SEQPACKET" } }; static void tipc_print_socktypes(void) { unsigned i; for (i=0; i < ARRAY_SIZE(socktype_map); i++) fprintf(stderr, "%s\n", socktype_map[i].sockname); } #endif /* HAVE_AF_TIPC */ static int parse_tipc_opt(int ac, char *av[],struct opts *optsp) { #ifdef HAVE_AF_TIPC unsigned i; /* memorize protocol */ optsp->ns_proto = NS_PROTO_TIPC; optsp->family = AF_TIPC; optsp->socktype = 0; if (optsp->workmode == MODE_TRANSMIT) { if (ac <= 1) { print_usage("TIPC transmit mode requires socket type and input file name\n", HELP_STR_TIPC, 1); goto out; } --ac; optsp->infile = av[ac]; } while (ac--) { for (i=0; i < ARRAY_SIZE(socktype_map); i++) { if (strcasecmp(socktype_map[i].sockname, av[ac]) == 0) break; } if (i < ARRAY_SIZE(socktype_map)) { optsp->socktype = socktype_map[i].socktype; break; } } if (optsp->socktype) return SUCCESS; out: fputs("You must specify a TIPC socket type. Known values:\n", stderr); tipc_print_socktypes(); exit(EXIT_FAILOPT); #endif return FAILURE; } static void dump_tipc_opt(struct opts *optsp __attribute__((unused))) { } static int parse_sctp_opt(int ac, char *av[],struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_SCTP; optsp->perform_rtt_probe = 1; optsp->protocol = IPPROTO_SCTP; optsp->socktype = SOCK_STREAM; /* this do/while loop parse options in the form '-x'. * After the do/while loop the parse fork into transmit, * receive specific code. */ for (;;) { #define FIRST_ARG_INDEX 0 /* break if we reach the end of the OPTIONS */ if (!av[FIRST_ARG_INDEX] || av[FIRST_ARG_INDEX][0] != '-') break; if (!av[FIRST_ARG_INDEX][1] || !isalnum(av[FIRST_ARG_INDEX][1])) print_usage(NULL, HELP_STR_TCP, 1); } #undef FIRST_ARG_INDEX switch (optsp->workmode) { case MODE_TRANSMIT: /* sanity check first */ if (ac <= 1) print_usage("sctp transmit mode requires file and destination address\n", HELP_STR_SCTP, 1); optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_RECEIVE: switch (ac) { case 0: /* nothing to do */ break; case 2: opts.hostname = xstrdup(av[1]); /* fallthrough */ case 1: opts.outfile = xstrdup(av[0]); break; default: err_msg("You specify to many arguments!"); print_usage(NULL, HELP_STR_GLOBAL, 1); break; }; break; default: err_msg_die(EXIT_FAILINT, "Internal, programmed error - unknown tranmit mode: %d\n", optsp->workmode); } return SUCCESS; } static void dump_sctp_opt(struct opts *optsp __attribute__((unused))) { } static int parse_dccp_opt(int ac, char *av[],struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_DCCP; optsp->protocol = IPPROTO_DCCP; optsp->socktype = SOCK_DCCP; switch (optsp->workmode) { case MODE_RECEIVE: break; case MODE_TRANSMIT: if (ac <= 1) { print_usage("dccp transmit mode requires file and destination address\n", HELP_STR_DCCP, 1); return FAILURE; } optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_NONE: return FAILURE; } return SUCCESS; } static void dump_dccp_opt(struct opts *optsp __attribute__((unused))) { } static int parse_udplite_opt(int ac, char *av[],struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_UDPLITE; optsp->protocol = IPPROTO_UDPLITE; optsp->socktype = SOCK_DGRAM; optsp->udplite_checksum_coverage = LONG_MAX; /* this do/while loop parse options in the form '-x'. * After the do/while loop the parse fork into transmit, * receive specific code. */ do { char *endptr; /* break if we reach the end of the OPTIONS or we see * the special option '-' -> this indicate the special * output file "stdout" (therefore no option ;-) */ if ((!av[0] || av[0][0] != '-') || (av[0] && av[0][0] == '-' && !av[0][1])) break; if (!av[0][1] || !isalnum(av[0][1])) print_usage(NULL, HELP_STR_TCP, 1); if (av[0][1] == 'C') { if (!av[1]) { print_usage("UDPLite option C requires an argument\n", HELP_STR_UDPLITE, 1); return FAILURE; } optsp->udplite_checksum_coverage = strtol(av[1], &endptr, 10); if ((errno == ERANGE && (optsp->udplite_checksum_coverage == LONG_MAX || optsp->udplite_checksum_coverage == LONG_MIN)) || (errno != 0 && optsp->udplite_checksum_coverage == 0)) { print_usage("UDPLite option C requires an numeric argument\n", HELP_STR_UDPLITE, 1); } msg(GENTLE, "parse UDPLite request to cover %ld bytes", optsp->udplite_checksum_coverage); ac -= 2; av += 2; continue; } ac--; av++; } while (1); switch (optsp->workmode) { case MODE_RECEIVE: switch (ac) { case 0: /* nothing to do */ break; case 2: opts.hostname = xstrdup(av[1]); /* fallthrough */ case 1: opts.outfile = xstrdup(av[0]); break; default: err_msg("You specify to many arguments!"); print_usage(NULL, HELP_STR_GLOBAL, 1); break; }; break; break; case MODE_TRANSMIT: if (ac <= 1) { print_usage("UDP Lite transmit mode requires file and destination address\n", HELP_STR_UDPLITE, 1); return FAILURE; } optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_NONE: return FAILURE; } return SUCCESS; } static void dump_udplite_opt(struct opts *optsp __attribute__((unused))) { } static int parse_udp_opt(int ac, char *av[],struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_UDP; optsp->protocol = IPPROTO_UDP; optsp->socktype = SOCK_DGRAM; switch (optsp->workmode) { case MODE_RECEIVE: switch (ac) { case 0: /* nothing to do */ break; case 2: opts.hostname = xstrdup(av[1]); /* fall through */ case 1: opts.outfile = xstrdup(av[0]); break; default: err_msg("You specified to many arguments!"); print_usage(NULL, HELP_STR_GLOBAL, 1); break; }; break; break; case MODE_TRANSMIT: if (ac <= 1) { print_usage("UDP transmit mode requires file and destination address\n", HELP_STR_UDP, 1); return FAILURE; } optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_NONE: return FAILURE; } return SUCCESS; } static void dump_udp_opt(struct opts *optsp __attribute__((unused))) { } struct __protocol_map_t { int protocol; const char *protoname; const char *helptext; int (*parse_proto)(int, char *[], struct opts *); void (*dump_proto)(struct opts *); } protocol_map[] = { { 0, "tipc", help_str[HELP_STR_TIPC] , parse_tipc_opt, dump_tipc_opt }, { 0, "sctp", help_str[HELP_STR_SCTP] , parse_sctp_opt, dump_sctp_opt }, { 0, "dccp", help_str[HELP_STR_DCCP] , parse_dccp_opt, dump_dccp_opt }, { 0, "udplite", help_str[HELP_STR_UDPLITE], parse_udplite_opt, dump_udplite_opt }, { 0, "udp", help_str[HELP_STR_UDP] , parse_udp_opt, dump_udp_opt }, { 0, "tcp", help_str[HELP_STR_TCP] , parse_tcp_opt, dump_tcp_opt }, { 0, NULL, NULL, NULL, NULL }, }; #ifndef SCHED_BATCH /* since 2.6.16 */ # define SCHED_BATCH 3 #endif struct sched_policymap_t { const int no; const char *name; } sched_policymap[] = { { SCHED_OTHER, "SCHED_OTHER" }, { SCHED_FIFO, "SCHED_FIFO" }, { SCHED_RR, "SCHED_RR" }, { SCHED_BATCH, "SCHED_BATCH" }, { 0, NULL }, }; struct __short_opts_t { const char *name; unsigned long bitmask; unsigned int min_cmp_length; } short_opt[] = { { "Version", SOPTS_VERSION, 1 }, { "n", SOPTS_NUMERIC, 1 }, { "4", SOPTS_IPV4, 1 }, { "6", SOPTS_IPV6, 1 }, { NULL, ' ', 0 }, }; static void print_complete_usage(void) { int i; for (i = 0; i <= HELP_STR_MAX; i++) fprintf(stderr, "%s\n", help_str[i]); } static int parse_rtt_string(const char *rtt_cmd, struct opts *optsp __attribute__((unused))) { const char *tok = rtt_cmd; char *what; while (tok) { long value = strtol(tok, &what, 10); switch (*what) { case 'n': opts.rtt_probe_opt.iterations = value; if (value <= 0 || value > 100) { fprintf(stderr, "You want %ld rtt probe iterations - that's not sensible! " "Valid range is between 1 and 100 probe iterations\n", value); return FAILURE; } break; case 'd': opts.rtt_probe_opt.data_size = value; if (value <= 0) { fprintf(stderr, "%ld not a valid data size for our rtt probe\n", value); return FAILURE; } break; case 'm': opts.rtt_probe_opt.deviation_filter = value; if (value < 0 || value > 50) { fprintf(stderr, "%ldms are nonsensical for the filter multiplier (default is %d)\n", value, DEFAULT_RTT_FILTER); return FAILURE; } break; case 'f': opts.rtt_probe_opt.force_ms = value; if (value < 0) { fprintf(stderr, "%ldms are nonsensical for a rtt\n", value); return FAILURE; } break; default: fprintf(stderr, "short rtt option %s in %s not supported: %c not recognized\n", tok, rtt_cmd, *what); return FAILURE; } if (*what) what++; if (*what && *what != ',') { fprintf(stderr, "rtt options must be comma separated, got %c: %s\n", *what, tok); return FAILURE; } tok = strchr(tok, ','); if (tok) tok++; } return SUCCESS; } static void dump_opts(struct opts *optsp __attribute__((unused))) { } /* parse_opts will parse all command line * arguments, fill struct opts with default and * user selected values and return if no error * occurred. In the case of an error parse_opts * will exit with exit value EXIT_FAILOPTS */ int parse_opts(int ac, char *av[], struct opts *optsp) { char *tmp; int ret, i, dump_defaults = 0; /* Zero out opts struct and set program name */ memset(optsp, 0, sizeof(struct opts)); if ((tmp = strrchr(av[0], '/')) != NULL) { tmp++; optsp->me = xstrdup(tmp); } else { optsp->me = xstrdup(av[0]); } /* first of all: set default values in optsp */ optsp->ns_proto = NS_PROTO_UNSPEC; optsp->port = xstrdup(DEFAULT_PORT); optsp->buffer_size = 0; /* 0 means that a _autodetection_ takes place */ optsp->workmode = MODE_NONE; optsp->stat_unit = BYTE_UNIT; optsp->stat_prefix = STAT_PREFIX_BINARY; optsp->family = AF_UNSPEC; /* per default only one transmit, respective receive * thread will do the whole work */ optsp->threads = 1; /* if opts.nice is equal INT_MAX nobody change something - hopefully */ optsp->nice = INT_MAX; /* Catch a special case: * 1) the user want all options -a.* * 2) the user want help -h.* */ if ((ac == 2) && ((av[1][0] == '-') && (&av[1][1] != NULL))) { if (av[1][1] == 'a') { print_complete_usage(); exit(0); } else { print_usage(NULL, HELP_STR_GLOBAL, 1); } } /* We need at least 2 arguments: transport protocol * and transport mode (transmit | receive) */ if (ac < 3) print_usage(NULL, HELP_STR_GLOBAL, 1); /* Pre-control OPTIONS - if any * The loop differentiate between short OPTIONS like -6 * and OPTIONS with an argument like -T { human | machine }. * The first part of the do/while loop check former and the * second half the later ones. */ do { #define FIRST_ARG_INDEX 1 /* break if we reach the end of the OPTIONS */ if (!av[FIRST_ARG_INDEX] || av[FIRST_ARG_INDEX][0] != '-') break; if (!av[FIRST_ARG_INDEX][1] || !isalnum(av[FIRST_ARG_INDEX][1])) print_usage(NULL, HELP_STR_GLOBAL, 1); if (!strncmp(&av[FIRST_ARG_INDEX][1], "h", 1)) { print_usage(NULL, HELP_STR_GLOBAL, 0); exit(0); } /* iterate over _short options_ and match relevant short options */ for (i = 0; short_opt[i].name; i++) { if (!strncmp(&av[FIRST_ARG_INDEX][1], short_opt[i].name, short_opt[i].min_cmp_length)) { optsp->short_opts_mask |= short_opt[i].bitmask; break; } } /* look for special options -all-options */ if (!strncmp(&av[FIRST_ARG_INDEX][1], "all-options", strlen(&av[FIRST_ARG_INDEX][1])) ) { print_complete_usage(); exit(1); } else if (!strncmp(&av[FIRST_ARG_INDEX][1], "help", strlen(&av[FIRST_ARG_INDEX][1])) ) { print_usage(NULL, HELP_STR_GLOBAL, 0); exit(0); } /* look for "-d" - this means that we should not fire netsend, * instead we parse all options, exit if something isn't proper * and print all declared user options and all default values. * This is an nice option for automated test - but only qualified * as a additional command. For an machine parseable format the -t * machine is the preferred option */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "d"))) ++dump_defaults; /* -T { human | machine } */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "T"))) { if (!av[2]) { print_usage(NULL, HELP_STR_GLOBAL, 1); } if (!strcmp(&av[FIRST_ARG_INDEX + 1][0], "human")) { optsp->statistics++; av += 2; ac -= 2; continue; } else if (!strcmp(&av[FIRST_ARG_INDEX + 1][0], "machine")) { optsp->machine_parseable++; av += 2; ac -= 2; continue; } else { print_usage(NULL, HELP_STR_GLOBAL, 1); exit(1); } } /* -b bufsize: -b readwritebufsize */ if (av[FIRST_ARG_INDEX][1] == 'b') { if (!av[2]) print_usage(NULL, HELP_STR_GLOBAL, 1); if (!scan_int(av[2], &optsp->buffer_size)) err_msg_die(EXIT_FAILOPT, "-b: writebuffersize must be a number"); av += 2; ac -= 2; continue; } /* -m memory advice */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "m")) ) { if (!av[FIRST_ARG_INDEX + 1]) print_usage(NULL, HELP_STR_GLOBAL, 1); for (i = 0; i <= MEMADV_MAX; i++ ) { if (!strcasecmp(&av[FIRST_ARG_INDEX + 1][0], memadvice_map[i].conf_string)) { optsp->mem_advice = memadvice_map[i].conf_code; optsp->change_mem_advise++; } } if (!optsp->change_mem_advise) /* option error */ print_usage(NULL, HELP_STR_MEM_ADVICE, 1); av += 2; ac -= 2; continue; } /* -u write-function */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "u")) ) { if (!av[FIRST_ARG_INDEX + 1]) print_usage(NULL, HELP_STR_GLOBAL, 1); for (i = 0; i <= IO_MAX; i++ ) { if (!strcasecmp(&av[FIRST_ARG_INDEX + 1][0], io_call_map[i].conf_string)) { optsp->io_call = io_call_map[i].conf_code; break; } } if (i > IO_MAX) /* option error */ print_usage(NULL, HELP_STR_IO_ADVICE, 1); av += 2; ac -= 2; continue; } /* -r rtt probe */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "r")) ) { if (!av[FIRST_ARG_INDEX + 1]) { print_usage(NULL, HELP_STR_GLOBAL, 1); } if (parse_rtt_string(av[FIRST_ARG_INDEX + 1], optsp) != SUCCESS) print_usage(NULL, HELP_STR_GLOBAL, 1); av += 2; ac -= 2; continue; } /* -P threads */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "P")) ) { char *endptr; if (!av[FIRST_ARG_INDEX + 1]) { print_usage(NULL, HELP_STR_GLOBAL, 1); } optsp->threads = strtol(&av[FIRST_ARG_INDEX + 1][0], &endptr, 10); /* sanity checks */ if ((errno == ERANGE && (optsp->threads == LONG_MAX || optsp->threads == LONG_MIN)) || (errno != 0 && optsp->threads == 0)) { } if (endptr == &av[FIRST_ARG_INDEX + 1][0]) { //FIXME err_msg("No digits were found\n"); exit(EXIT_FAILOPT); } if (optsp->threads <= 0) { print_usage("Number of threads must be greater then 0", HELP_STR_GLOBAL, 1); } av += 2; ac -= 2; continue; } /* -N nice-level */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "N")) ) { char *endptr; if (!av[FIRST_ARG_INDEX + 1]) { print_usage(NULL, HELP_STR_GLOBAL, 1); } optsp->nice = strtol(&av[FIRST_ARG_INDEX + 1][0], &endptr, 10); if ((errno == ERANGE && (optsp->nice == LONG_MAX || optsp->nice == LONG_MIN)) || (errno != 0 && optsp->nice == 0)) { } if (endptr == &av[FIRST_ARG_INDEX + 1][0]) { //FIXME err_msg("No digits were found\n"); exit(EXIT_FAILOPT); } av += 2; ac -= 2; continue; } /* -s setsockopt options: -s OPTNAME [ OPTVAL ] */ if (av[FIRST_ARG_INDEX][1] == 's') { const char *optval, *optname = av[FIRST_ARG_INDEX + 1]; if (!optname || *optname == '-' || !*optname) { fputs("need optname after -s\n", stderr); die_print_setsockopts(); } optval = av[FIRST_ARG_INDEX + 2]; if (!optval || !*optval) { fputs("need optval after optname\n", stderr); die_print_setsockopts(); } parse_setsockopt_name(optname, optval); av += 3; ac -= 3; continue; } /* -p port: set TCP/UDP/DCCP/SCTP port number to use */ if (av[FIRST_ARG_INDEX][1] == 'p') { if (!av[2]) print_usage(NULL, HELP_STR_GLOBAL, 1); optsp->port = strdup(av[2]); av += 2; ac -= 2; continue; } /* -v { quitscent | gentle | loudish | stressful } */ if ((!strcmp(&av[FIRST_ARG_INDEX][1], "v")) ) { if (!av[FIRST_ARG_INDEX + 1]) { print_usage(NULL, HELP_STR_GLOBAL, 1); } if (!strcmp(&av[FIRST_ARG_INDEX + 1][0], "quitscent")) { optsp->verbose = 0; av += 2; ac -= 2; continue; } else if (!strcmp(&av[FIRST_ARG_INDEX + 1][0], "gentle")) { optsp->verbose = 1; av += 2; ac -= 2; continue; } else if (!strcmp(&av[FIRST_ARG_INDEX + 1][0], "loudish")) { optsp->verbose = 2; av += 2; ac -= 2; continue; } else if (!strcmp(&av[FIRST_ARG_INDEX + 1][0], "stressful")) { optsp->verbose = 3; av += 2; ac -= 2; continue; } else { print_usage("ERROR" , HELP_STR_VERBOSE_LEVEL, 0); print_usage(NULL, HELP_STR_GLOBAL, 1); exit(1); } } /* scheduler policy: -P { RR | FIFO | BATCH | OTHER } priority */ if ((!strcmp(&av[FIRST_ARG_INDEX + 1][1], "P")) ) { if (!av[FIRST_ARG_INDEX + 1] && !av[FIRST_ARG_INDEX + 2]) { print_usage(NULL, HELP_STR_SCHED_POLICY, 1); } for (i = 0; sched_policymap[i].name; i++) { if (!strcasecmp(&av[FIRST_ARG_INDEX + 1][0], sched_policymap[i].name)) { optsp->sched_policy = sched_policymap[i].no; optsp->sched_user++; } } if (!optsp->sched_user) /* option error */ print_usage(NULL, HELP_STR_SCHED_POLICY, 1); /* OK - the policy seems fine. Now look for a valid * nice level */ if (!strcasecmp(&av[FIRST_ARG_INDEX + 2][0], "MAX")) { optsp->priority = sched_get_priority_max(optsp->sched_policy); } else if (!strcasecmp(&av[FIRST_ARG_INDEX + 2][0], "MIN")) { optsp->priority = sched_get_priority_min(optsp->sched_policy); } else { optsp->priority = strtol(&av[FIRST_ARG_INDEX + 2][0], (char **)NULL, 10); if (!(optsp->priority <= sched_get_priority_max(optsp->sched_policy) && optsp->priority >= sched_get_priority_min(optsp->sched_policy))) { fprintf(stderr, "Priority not valid!\nValid values are in the range between" " %d and %d for %s (you selected %d)\n", sched_get_priority_min(optsp->sched_policy), sched_get_priority_max(optsp->sched_policy), &av[FIRST_ARG_INDEX + 1][0], optsp->priority); print_usage(NULL, HELP_STR_SCHED_POLICY, 1); } } /* FINE - all options are valid until now ... */ av += 3; ac -= 3; continue; } av++; ac--; } while (1); /* set values gathered in the parsing process */ if (optsp->short_opts_mask & SOPTS_IPV4) optsp->family = AF_INET; /* -6 had precedence */ if (optsp->short_opts_mask & SOPTS_IPV6) optsp->family = AF_INET6; /* we need at least two arguments: * PROTOCOL and MODE */ if (ac < 3) print_usage(NULL, HELP_STR_GLOBAL, 1); /* now we branch to our final, protocol specific parse routine */ for (i = 0; protocol_map[i].protoname; i++) { if (!strcasecmp(protocol_map[i].protoname, av[FIRST_ARG_INDEX])) { if (!strncasecmp(av[FIRST_ARG_INDEX + 1], "transmit", strlen(av[2]))) { optsp->workmode = MODE_TRANSMIT; ret = protocol_map[i].parse_proto(ac - 3, av + 3, optsp); if (dump_defaults) { dump_opts(optsp); protocol_map[i].dump_proto(optsp); } return ret; } else if (!strncasecmp(av[FIRST_ARG_INDEX + 1], "receive", strlen(av[FIRST_ARG_INDEX + 1]))) { optsp->workmode = MODE_RECEIVE; ret = protocol_map[i].parse_proto(ac - 3, av + 3, optsp); if (dump_defaults) { dump_opts(optsp); protocol_map[i].dump_proto(optsp); } return ret; } else { print_usage("MODE isn't permitted:\n", HELP_STR_GLOBAL, 1); } } } print_usage("PROTOCOL isn't permitted!\n", HELP_STR_GLOBAL, 1); exit(EXIT_FAILOPT); return 0; } #undef FIRST_ARG_INDEX /* vim:set ts=4 sw=4 sts=4 tw=78 ff=unix noet: */ netsend-0.0~svnr250/main.c0000644000175000017500000001132110776173116015014 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include "analyze.h" #include "global.h" #include "proto_tipc.h" #include "proto_udp_recv.h" struct conf_map_t memadvice_map[] = { { MEMADV_NORMAL, "normal" }, { MEMADV_RANDOM, "random" }, { MEMADV_SEQUENTIAL, "sequential" }, { MEMADV_WILLNEED, "willneed" }, { MEMADV_DONTNEED, "dontneed" }, { MEMADV_NOREUSE, "noreuse" } }; struct conf_map_t io_call_map[] = { { IO_MMAP, "mmap" }, { IO_SENDFILE, "sendfile" }, { IO_SPLICE, "splice" }, { IO_RW, "rw" }, }; struct socket_options socket_options[] = { {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, SVT_BOOL, 0, {0}}, {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, SVT_BOOL, 0, {0}}, {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, SVT_BOOL, 0, {0}}, {"TCP_NODELAY", SOL_TCP, TCP_NODELAY, SVT_BOOL, 0, {0}}, {"TCP_CONGESTION", SOL_TCP, TCP_CONGESTION, SVT_STR, 0, {0}}, {"TCP_CORK", SOL_TCP, TCP_CORK, SVT_BOOL, 0, {0}}, {"SCTP_DISABLE_FRAGMENTS", IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, SVT_BOOL, 0, {0}}, {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, SVT_INT, 0, {0}}, {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, SVT_INT, 0, {0}}, {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, SVT_INT, 0, {0}}, {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, SVT_INT, 0, {0}}, {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, SVT_INT, 0, {0}}, {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, SVT_INT, 0, {0}}, {"UDPLITE_SEND_CSCOV", IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, SVT_INT, 0, {8}}, {NULL, 0, 0, 0, 0, {0}} }; struct opts opts; struct net_stat net_stat; struct sock_callbacks sock_callbacks = { .cb_read = read, .cb_write = write, .cb_accept = accept, .cb_listen = listen }; #define MAX_STATLEN 4096 static void ignore_sigpipe(void) { struct sigaction sa = { .sa_handler = SIG_IGN }; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } int main(int argc, char *argv[]) { int ret = EXIT_OK; /* parse_opts will exit if an error occurr */ parse_opts(argc, argv, &opts); msg(GENTLE, PROGRAMNAME " - " VERSIONSTRING); if (opts.sched_user) { struct sched_param sp; sp.sched_priority = opts.priority; if (sched_setscheduler(0, opts.sched_policy, &sp)) { err_sys("sched_setscheduler()"); } } if ((opts.nice != INT_MAX) && (nice(opts.nice) == -1)) { err_sys("nice()"); } switch (opts.protocol) { case IPPROTO_UDP: case IPPROTO_UDPLITE: sock_callbacks.cb_listen = udp_listen; } ignore_sigpipe(); /* Branch to final workmode ... */ switch (opts.workmode) { case MODE_TRANSMIT: switch (opts.ns_proto) { case NS_PROTO_TCP: msg(LOUDISH, "branch to tcp_trans_mode()"); tcp_trans_mode(); break; case NS_PROTO_UDP: msg(LOUDISH, "branch to udp_trans_mode()"); udp_trans_mode(); break; case NS_PROTO_UDPLITE: msg(LOUDISH, "branch to udplite_trans_mode()"); udplite_trans_mode(); break; case NS_PROTO_SCTP: msg(LOUDISH, "branch to udplite_trans_mode()"); sctp_trans_mode(); break; case NS_PROTO_DCCP: msg(LOUDISH, "branch to udplite_trans_mode()"); dccp_trans_mode(); break; case NS_PROTO_TIPC: msg(LOUDISH, "branch to udplite_trans_mode()"); tipc_trans_mode(); break; default: err_msg_die(EXIT_FAILINT, "Programmed Error"); break; } break; case MODE_RECEIVE: receive_mode(); break; default: err_msg_die(EXIT_FAILMISC, "Programmed Failure"); } if (opts.statistics || opts.machine_parseable) { char buf[MAX_STATLEN]; if (opts.machine_parseable) gen_machine_analyse(buf, MAX_STATLEN); else gen_human_analyse(buf, MAX_STATLEN); fputs(buf, stderr); fflush(stderr); } return ret; } /* vim:set ts=4 sw=4 sts=4 tw=78 ff=unix noet: */ netsend-0.0~svnr250/error.h0000644000175000017500000000324510776173116015234 0ustar martinmartin/* ** $Id: error.h 136 2007-06-02 14:34:03Z fwest $ ** ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* error handling macros */ #define err_msg(format, args...) \ do { \ x_err_ret(__FILE__, __LINE__, format , ## args); \ } while (0) #define err_sys(format, args...) \ do { \ x_err_sys(__FILE__, __LINE__, format , ## args); \ } while (0) #define err_sys_die(exitcode, format, args...) \ do { \ x_err_sys(__FILE__, __LINE__, format , ## args); \ exit( exitcode ); \ } while (0) #define err_msg_die(exitcode, format, args...) \ do { \ x_err_ret(__FILE__, __LINE__, format , ## args); \ exit( exitcode ); \ } while (0) enum { QUITSCENT = 0, GENTLE, LOUDISH, STRESSFUL }; /*** Interface ***/ /* error.c */ void x_err_ret(const char *file, int line_no, const char *, ...); void x_err_sys(const char *file, int line_no, const char *, ...); void msg(const int, const char *, ...); void print_bt(void); netsend-0.0~svnr250/proto_dccp_trans.c0000644000175000017500000001077710776173116017451 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct sock_callbacks sock_callbacks; /* Creates our server socket and initialize ** options */ static int init_dccp_trans(void) { bool use_multicast = false; int fd = -1, ret; struct addrinfo hosthints, *hostres, *addrtmp; struct protoent *protoent; memset(&hosthints, 0, sizeof(struct addrinfo)); /* probe our values */ hosthints.ai_family = opts.family; hosthints.ai_socktype = opts.socktype; hosthints.ai_protocol = IPPROTO_DCCP; hosthints.ai_flags = AI_ADDRCONFIG; xgetaddrinfo(opts.hostname, opts.port, &hosthints, &hostres); addrtmp = hostres; for (addrtmp = hostres; addrtmp != NULL ; addrtmp = addrtmp->ai_next) { if (opts.family != AF_UNSPEC && addrtmp->ai_family != opts.family) { /* user fixed family! */ continue; } fd = socket(addrtmp->ai_family, addrtmp->ai_socktype, addrtmp->ai_protocol); if (fd < 0) { err_sys("socket"); continue; } protoent = getprotobynumber(addrtmp->ai_protocol); if (protoent) msg(LOUDISH, "socket created - protocol %s(%d)", protoent->p_name, protoent->p_proto); if (use_multicast) { int hops_ttl = 30; int on = 1; switch (addrtmp->ai_family) { case AF_INET6: xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops_ttl, sizeof(hops_ttl), "IPV6_MULTICAST_HOPS"); xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(int), "IPV6_MULTICAST_LOOP"); break; case AF_INET: xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&hops_ttl, sizeof(hops_ttl), "IP_MULTICAST_TTL"); xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(int), "IP_MULTICAST_LOOP"); msg(STRESSFUL, "set IP_MULTICAST_LOOP option"); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } set_socketopts(fd); /* Connect to peer ** There are three advantages to call connect for all types ** of our socket protocols (especially udp) ** ** 1. We don't need to specify a destination address (only call write) ** 2. Performance advantages (kernel level) ** 3. Error detection (e.g. destination port unreachable at udp) */ ret = connect(fd, addrtmp->ai_addr, addrtmp->ai_addrlen); if (ret == -1) err_sys_die(EXIT_FAILNET, "Can't connect to %s", opts.hostname); msg(LOUDISH, "socket connected to %s via port %s", opts.hostname, opts.port); } if (fd < 0) err_msg_die(EXIT_FAILNET, "No suitable socket found"); freeaddrinfo(hostres); return fd; } /* ** o initialize server socket ** o fstat and open our sending-file ** o block in socket and wait for client ** o sendfile(2), write(2), ... ** o print diagnostic info */ void dccp_trans_mode(void) { int connected_fd, file_fd; msg(GENTLE, "transmit mode (file: %s - hostname: %s)", opts.infile, opts.hostname); /* check if the transmitted file is present and readable */ file_fd = open_input_file(); connected_fd = init_dccp_trans(); /* fetch sockopt before the first byte */ get_sock_opts(connected_fd, &net_stat); /* construct and send netsend header to peer */ meta_exchange_snd(connected_fd, file_fd); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); trans_start(file_fd, connected_fd); gettimeofday(&opts.endtime, NULL); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/ns_hdr.c0000644000175000017500000003262010776173116015352 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "analyze.h" #include "global.h" #include "ns_hdr.h" #include "debug.h" #include "xfuncs.h" extern struct opts opts; extern struct net_stat net_stat; extern struct sock_callbacks sock_callbacks; static ssize_t writen(int fd, const void *buf, size_t len) { const char *bufptr = buf; ssize_t total = 0; do { ssize_t written = sock_callbacks.cb_write(fd, bufptr, len); if (written < 0) { if (errno == EINTR) continue; break; } total += written; bufptr += written; len -= written; } while (len > 0); return total > 0 ? total : -1; } /* read exactly buflen bytes; only return on fatal error */ static ssize_t readn(int fd, void *buf, size_t buflen) { char *bufptr = buf; ssize_t total = 0; do { ssize_t ret = read(fd, bufptr, buflen); switch (ret) { case -1: if (errno == EINTR) continue; /* fallthru */ case 0: goto out; } total += ret; bufptr += ret; buflen -= ret; } while (buflen > 0); out: return total > 0 ? total : -1; } /* This is the plan: ** send n rtt packets into the wire and wait until all n reply packets ** arrived. If a timeout occur we count this packet as lost */ static int probe_rtt(int peer_fd, int next_hdr, int probe_no, uint16_t backing_data_size) { int i, j, current_next_hdr; double rtt_ms[probe_no + 1], deviation = 0, covariance = 0; double d_tmp = 0; uint16_t packet_len; ssize_t to_write; char rtt_buf[backing_data_size + sizeof(struct ns_rtt_probe)]; struct ns_rtt_probe *ns_rtt_probe = (struct ns_rtt_probe *) rtt_buf; char *data_ptr = rtt_buf + sizeof(struct ns_rtt_probe); if (probe_no <= 0) err_msg_die(EXIT_FAILINT, "Programmed Failure"); memset(ns_rtt_probe, 0, sizeof(struct ns_rtt_probe)); memset(data_ptr, 'A', backing_data_size); /* packet backing data MUST a multiple of four */ packet_len = ((uint16_t)(backing_data_size / 4)) * 4; to_write = packet_len + sizeof(struct ns_rtt_probe); /* we announce packetsize in 4 byte slices (32bit) ** minus nse_nxt_hdr and nse_len header (4 byte) */ ns_rtt_probe->nse_len = htons((to_write - 4) / 4); ns_rtt_probe->ident = htons(getpid() & 0xffff); current_next_hdr = NSE_NXT_RTT_PROBE; /* FIXME: ** The first rtt probe packet had nearly a rtt of additional ** 200ms. So we ignore the first packet silently and calculate ** rtt and variation. I think this increased measurement is caused ** by "cold code paths"[TM], but these is to validate. */ for (i = 0; i <= probe_no; ) { char reply_buf[to_write]; struct ns_rtt_probe *ns_rtt_reply; ssize_t to_read = to_write; struct timeval tv, tv_tmp, tv_res; if (i++ >= probe_no) current_next_hdr = next_hdr; ns_rtt_probe->nse_nxt_hdr = htons(current_next_hdr); ns_rtt_probe->type = (htons((uint16_t)RTT_REQUEST_TYPE)); ns_rtt_probe->seq_no = htons(i); /* set timeval for packet */ if (gettimeofday(&tv, NULL) != 0) err_sys("Can't call gettimeofday"); ns_rtt_probe->sec = htonl(tv.tv_sec); ns_rtt_probe->usec = htonl(tv.tv_usec); /* transmitt rtt probe ... */ if (writen(peer_fd, ns_rtt_probe, to_write) != to_write) err_msg_die(EXIT_FAILHEADER, "Can't send rtt extension header!\n"); /* ... and receive probe */ if (readn(peer_fd, reply_buf, to_read) != to_read) return -1; if (gettimeofday(&tv, NULL) != 0) err_sys("Can't call gettimeofday"); ns_rtt_reply = (struct ns_rtt_probe *) reply_buf; tv_tmp.tv_sec = ntohl(ns_rtt_reply->sec); tv_tmp.tv_usec = ntohl(ns_rtt_reply->usec); subtime(&tv_tmp, &tv, &tv_res); /* sanity check (ident) */ if (ntohs(ns_rtt_reply->ident) != (getpid() & 0xffff)) err_msg("received a unknown rtt probe reply (ident should: %d is: %d)", ntohs(ns_rtt_reply->ident), (getpid() & 0xffff)); /* XXX: if you change something here, then check all counter ** variables, etc */ if (i == 1) continue; rtt_ms[i - 2] = (tv_res.tv_sec * 1000) + ((double)tv_res.tv_usec / 1000); msg(STRESSFUL, "receive rtt reply probe (sequence: %d, len %d, rtt: %.3fms)", ntohs(ns_rtt_reply->seq_no), to_read, rtt_ms[i - 2]); } /* average */ for (j = 0; j < probe_no; j++) net_stat.rtt_probe.usec += rtt_ms[j]; net_stat.rtt_probe.usec /= --j; /* ... covariance and standard deviation */ for (j = 0; j < probe_no; j++) covariance += pow(rtt_ms[j] - net_stat.rtt_probe.usec, 2); covariance /= --j; deviation = sqrt(covariance); d_tmp = 0; /* low and high pass deviation based filter, calculates new rtt average */ for (j = 0, i = 0; j < probe_no; j++) { if (((rtt_ms[j] > net_stat.rtt_probe.usec) && (rtt_ms[j] < (net_stat.rtt_probe.usec + (deviation * opts.rtt_probe_opt.deviation_filter)))) || ((rtt_ms[j] < net_stat.rtt_probe.usec) && (rtt_ms[j] > (net_stat.rtt_probe.usec - (deviation * opts.rtt_probe_opt.deviation_filter))))) { d_tmp += rtt_ms[j]; ++i; } } net_stat.rtt_probe.usec = d_tmp / i; msg(LOUDISH, "average rtt: %.3fms (after filter), covariance: %.3fms^2, standard deviation %.3fms", net_stat.rtt_probe.usec, covariance, deviation); return 0; } #define TIMEOUT_SEC 10 static void timout_handler(int sig_no) { if (sig_no != SIGALRM) err_msg_die(EXIT_FAILINT, "Programmed error (received an unknow signal)"); alarm(TIMEOUT_SEC); msg(STRESSFUL, "timout occure while wait for rtt probe response"); } #define RTT_PAYLOAD_SIZE 500 #define RTT_NO_PROBES 5 static int send_rtt_info(int fd, int next_hdr, struct rtt_probe *rtt_probe) { (void) fd; (void) next_hdr; (void) rtt_probe; return -1; } /** * meta_exchange_snd send header(s) information to the * peer node. We definitive send our netsend header and * 0 or more rtt probe packets to gather the current * round trip time. If we send rtt probes we transmit the * results also to the peer node. */ int meta_exchange_snd(int connected_fd, int file_fd) { int ret = 0; ssize_t len; ssize_t file_size; struct ns_hdr ns_hdr; struct stat stat_buf; int perform_rtt; memset(&ns_hdr, 0, sizeof(struct ns_hdr)); /* fetch file size */ xfstat(file_fd, &stat_buf, opts.infile); file_size = S_ISREG(stat_buf.st_mode) ? stat_buf.st_size : 0; ns_hdr.magic = htons(NS_MAGIC); /* FIXME: catch overflow */ ns_hdr.version = htons((uint16_t) strtol(VERSIONSTRING, (char **)NULL, 10)); ns_hdr.data_size = htonl(file_size); perform_rtt = (opts.rtt_probe_opt.iterations > 0) ? 1 : 0; ns_hdr.nse_nxt_hdr = perform_rtt ? htons(NSE_NXT_RTT_PROBE) : htons(NSE_NXT_DATA); len = sizeof(struct ns_hdr); if (writen(connected_fd, &ns_hdr, len) != len) err_msg_die(EXIT_FAILHEADER, "Can't send netsend header!\n"); /* probe for effective round trip time */ if (opts.rtt_probe_opt.iterations > 0) { int flag_old; struct sigaction sa; /* initialize signalhandler for timeout handling */ sigemptyset(&sa.sa_mask); sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */ sa.sa_handler = timout_handler; if (sigaction(SIGALRM, &sa, NULL) != 0) err_sys("Can't add signal handler"); /* set TCP_NODELAY so tcp writes dont get buffered */ if (opts.protocol == IPPROTO_TCP) { if ((flag_old = set_nodelay(connected_fd, 1)) < 0) { err_sys("Can't set TCP_NODELAY for socket (ret: %d)", flag_old); } } alarm(TIMEOUT_SEC); probe_rtt(connected_fd, NSE_NXT_DATA, opts.rtt_probe_opt.iterations, opts.rtt_probe_opt.data_size); alarm(0); /* and restore TCP_NOPUSH */ if (opts.protocol == IPPROTO_TCP) { if ((set_nodelay(connected_fd, flag_old)) < 0) { err_sys("Can't set TCP_NODELAY for socket"); } } /* transmitt our rtt probe results to our peer */ send_rtt_info(connected_fd, NSE_NXT_DATA, &net_stat.rtt_probe); } /* XXX: add shasum next header if opts.sha, modify nse_nxt_hdr processing */ return ret; } /* probe_rtt read a rtt probe packet, set nse_nxt_hdr to zero, ** nse_len to the current packet size and send it back to origin */ static int process_rtt_probe(int peer_fd, uint16_t nse_len) { int ret = 0; uint16_t *intptr; struct ns_rtt_probe *ns_rtt_probe_ptr; char buf[nse_len * 4 + 4]; ssize_t to_read = nse_len * 4; if (readn(peer_fd, buf + 4, to_read) != to_read) return -1; ns_rtt_probe_ptr = (struct ns_rtt_probe *) buf; msg(STRESSFUL, "process rtt probe (sequence: %d, type: %d packet_size: %d)", ntohs(ns_rtt_probe_ptr->seq_no), ntohs(ns_rtt_probe_ptr->type), to_read); ns_rtt_probe_ptr->type = htons(RTT_REPLY_TYPE); intptr = (uint16_t *)buf; *intptr = 0; intptr = (uint16_t *)buf + sizeof(uint16_t); *intptr = htons(nse_len); if (writen(peer_fd, buf, to_read + 4) != to_read + 4) err_msg_die(EXIT_FAILHEADER, "Can't reply to rtt probe!\n"); return ret; } static int process_rtt_info(int peer_fd, uint16_t nse_len) { char buf[nse_len * 4 + sizeof(uint16_t) * 2]; ssize_t to_read = nse_len * 4; struct ns_rtt_info *ns_rtt_info; if (readn(peer_fd, buf + sizeof(uint16_t) * 2, to_read) != to_read) return -1; ns_rtt_info = (struct ns_rtt_info *)buf; return 0; } static int process_nonxt(int peer_fd, uint16_t nse_len) { char buf[nse_len]; ssize_t to_read = nse_len; if (readn(peer_fd, buf, to_read) != to_read) return -1; return 0; } #define INVALID_EXT_TRESH_NO 8 /* return -1 if a failure occure, zero apart from that */ int meta_exchange_rcv(int peer_fd, struct peer_header_info **hi) { int ret; int invalid_ext_seen = 0; uint16_t extension_type, extension_size; struct peer_header_info *phi; unsigned char *ptr; ssize_t rc = 0, to_read = sizeof(struct ns_hdr); struct ns_hdr ns_hdr; memset(&ns_hdr, 0, sizeof(struct ns_hdr)); /* allocate info header */ phi = xzalloc(sizeof(struct peer_header_info)); *hi = phi; ptr = (unsigned char *) &ns_hdr; msg(STRESSFUL, "fetch general header (%d byte)", sizeof(struct ns_hdr)); /* read minimal ns header */ if (readn(peer_fd, &ptr[rc], to_read) != to_read) return -1; /* ns header is in -> sanity checks and look if peer specified extension header */ if (ntohs(ns_hdr.magic) != NS_MAGIC) { err_msg_die(EXIT_FAILHEADER, "received an corrupted header" "(should %d but is %d)!\n", NS_MAGIC, ntohs(ns_hdr.magic)); } msg(STRESSFUL, "header info (magic: %d, version: %d, data_size: %d)", ntohs(ns_hdr.magic), ntohs(ns_hdr.version), ntohl(ns_hdr.data_size)); phi->data_size = ntohl(ns_hdr.data_size); extension_type = ntohs(ns_hdr.nse_nxt_hdr); if (extension_type == NSE_NXT_DATA) { msg(STRESSFUL, "end of extension header processing (NSE_NXT_DATA, no extension header)"); return 0; } while (invalid_ext_seen < INVALID_EXT_TRESH_NO) { /* FIXME: define some header size macros */ uint16_t common_ext_head[2]; to_read = sizeof(uint16_t) * 2; /* read first 4 octets of extension header, because we now ** there IS a extension header and a extension header is always ** 4 byte */ if (readn(peer_fd, common_ext_head, to_read) != to_read) return -1; extension_size = ntohs(common_ext_head[1]); switch (extension_type) { case NSE_NXT_DATA: msg(STRESSFUL, "end of extension header processing (NSE_NXT_DATA)"); return 0; case NSE_NXT_NONXT: msg(STRESSFUL, "end of extension header processing (NSE_NXT_NONXT)"); return process_nonxt(peer_fd, extension_size); break; case NSE_NXT_DIGEST: msg(STRESSFUL, "next extension header: %s", "NSE_NXT_DIGEST"); err_msg("Not implementet yet: NSE_NXT_DIGEST\n"); break; case NSE_NXT_RTT_PROBE: msg(STRESSFUL, "next extension header: %s", "NSE_NXT_RTT_PROBE"); ret = process_rtt_probe(peer_fd, extension_size); if (ret == -1) return -1; break; case NSE_NXT_RTT_INFO: msg(STRESSFUL, "next extension header: %s", "NSE_NXT_RTT_INFO"); ret = process_rtt_info(peer_fd, extension_size); if (ret == -1) return -1; break; default: ++invalid_ext_seen; err_msg("received an unknown extension type (%d)!\n", extension_type); ret = process_nonxt(peer_fd, extension_size); if (ret == -1) return -1; break; } extension_type = ntohs(common_ext_head[0]); switch (extension_type) { case NSE_NXT_DATA: msg(STRESSFUL, "end of extension header processing (NSE_NXT_DATA)"); return 0; case NSE_NXT_NONXT: msg(STRESSFUL, "end of extension header processing (NSE_NXT_NONXT)"); return process_nonxt(peer_fd, extension_size); break; } }; /* failure if we reach here (failure in previous while loop */ return -1; } #undef INVALID_EXT_TRESH_NO /* vim:set ts=4 sw=4 sts=4 tw=78 ff=unix noet: */ netsend-0.0~svnr250/ns_hdr.h0000644000175000017500000000577110776173116015366 0ustar martinmartin/* ** $Id: ns_hdr.h 136 2007-06-02 14:34:03Z fwest $ ** ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* all values in network byte order */ #define NS_MAGIC 0x67 enum ns_nse_nxt { NSE_NXT_DATA, NSE_NXT_DIGEST, NSE_NXT_RTT_PROBE, NSE_NXT_NONXT, NSE_NXT_RTT_INFO }; struct ns_hdr { uint16_t magic; uint16_t version; uint32_t data_size; /* purely data, without netsend header */ uint16_t nse_nxt_hdr; /* NSE_NXT_DATA for no header */ uint16_t unused; } __attribute__((packed)); /* ** netsend chaining header fields for ancillary information. ** This is a similar mechanism like the ipv6 extension header. ** This information can be used to exchange information like ** data digest, buffersize, etc. ** Like ipv6: each extension header appears only once. If a ** header apears multiple times the last extension header has significance. ** In contrast to ipv6, netsend headers have no order. */ /* ns_nxt_digest is the digest header extension ** Currently netsend support following digest algorithms: ** o NULL (0) ** o SHA (20) ** o SHA256 (32) ** o SHA512 (64) */ struct ns_nxt_digest { uint16_t nse_nxt_hdr; /* next header */ uint16_t nse_len; /* length in units of 4 octets (not including the first 4 octets) */ uint8_t nse_dgst_type; uint8_t nse_dgst_len; /* followed by digest data */ } __attribute__((packed)); /* this is a dummy extension header. it indicates that this ** is the last extension header AND no more data is comming! */ struct ns_nxt_nonxt { uint16_t nse_nxt_hdr; /* next header */ uint16_t nse_len; /* length in units of 4 octets (not including the first 4 octets) */ uint32_t unused; } __attribute__((packed)); /* rtt packet */ enum ns_rtt_type { RTT_REQUEST_TYPE = 0, RTT_REPLY_TYPE }; struct ns_rtt_probe { uint16_t nse_nxt_hdr; /* next header */ uint16_t nse_len; /* length in units of 4 octets (not including the first 4 octets) */ uint16_t type; /* on of ns_rtt_type */ uint16_t unused; /* checksum */ uint16_t ident; /* packetstream identifier */ uint16_t seq_no; uint32_t sec; uint32_t usec; /* variable data */ } __attribute__((packed)); struct ns_rtt_info { uint16_t nse_nxt_hdr; /* next header */ uint16_t nse_len; /* ... you know */ uint32_t sec; uint32_t usec; } __attribute__((packed)); netsend-0.0~svnr250/unit_test.sh0000755000175000017500000001515310776173116016310 0ustar martinmartin#!/bin/sh TESTFILE=$(mktemp /tmp/netsendXXXXXX) NETSEND_BIN=./netsend TEST_FAILED=0 pre() { # generate a file for transfer echo Initialize test environment dd if=/dev/zero of=${TESTFILE} bs=1 count=1 1>/dev/null 2>&1 } post() { echo Cleanup test environment killall -9 netsend 1>/dev/null 2>&1 rm -f ${TESTFILE} } die() { post exit 1 } further_help() { cat < http://netsend.berlios.de EOF } case1() { echo -n "Trivial receive transmit test ..." ${NETSEND_BIN} tcp receive & RPID=$! # give netsend 2 seconds to set up everything and # bind properly sleep 2 ${NETSEND_BIN} tcp transmit ${TESTFILE} localhost & # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case2() { echo -n "IPv4 (AF_INET) enforce test ..." ${NETSEND_BIN} -4 tcp receive 1>/dev/null 2>&1 & RPID=$! # give netsend 2 seconds to set up everything and # bind properly sleep 2 ${NETSEND_BIN} -4 tcp transmit ${TESTFILE} localhost 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then echo testcase failed die fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case3() { echo -n "IPv6 (AF_INET6) enforce test ..." ${NETSEND_BIN} -6 tcp receive 1>/dev/null 2>&1 & RPID=$! # give netsend 2 seconds to set up everything and # bind properly sleep 2 ${NETSEND_BIN} -6 tcp transmit ${TESTFILE} ::1 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then TEST_FAILED=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case4() { echo -n "Statictic output test (both, machine and human) ..." L_ERR=0 # start with human output R_OPT="-T human tcp receive" T_OPT="-T human tcp transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! # give netsend 2 seconds to set up everything and # bind properly sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then L_ERR=1 fi # and machine output R_OPT="-T machine tcp receive" T_OPT="-T machine tcp transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! # give netsend 2 seconds to set up everything and # bind properly sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then L_ERR=1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case5() { echo -n "TCP protocol tests ..." L_ERR=0 R_OPT="tcp receive" T_OPT="tcp transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then L_ERR=1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case6() { echo -n "DCCP protocol tests ..." L_ERR=0 R_OPT="dccp receive" T_OPT="dccp transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then L_ERR=1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case7() { echo -n "SCTP protocol tests ..." L_ERR=0 R_OPT="sctp receive" T_OPT="sctp transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then L_ERR=1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case8() { echo -n "TIPC protocol tests ..." L_ERR=0 R_OPT="tipc receive -t SOCK_STREAM" T_OPT="-u rw tipc transmit -t SOCK_STREAM ${TESTFILE}" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 fi # wait for receiver and check return code wait $RPID if [ $? -ne 0 ] ; then L_ERR=1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case9() { echo -n "UDP protocol tests ..." L_ERR=0 R_OPT="udp receive" T_OPT="udp transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 else # ok, the send process seems fine. The workaround # now comes through the fact the udp even doesn't # know when the end of data is reached. # We therefore kill simple the receiver ;( kill -9 $RPID 1>/dev/null 2>&1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } case10() { echo -n "UDP Lite protocol tests ..." L_ERR=0 R_OPT="udplite receive" T_OPT="udplite transmit ${TESTFILE} localhost" ${NETSEND_BIN} ${R_OPT} 1>/dev/null 2>&1 & RPID=$! sleep 2 ${NETSEND_BIN} ${T_OPT} 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then L_ERR=1 else # ok, the send process seems fine. The workaround # now comes through the fact the udp-lite even doesn't # know when the end of data is reached. # We therefore kill simple the receiver ;( kill -9 $RPID 1>/dev/null 2>&1 fi if [ $L_ERR -ne 0 ] ; then echo failed TEST_FAILED=1 else echo passed fi } echo -e "\nnetsend unit test script - (C) 2007\n" pre trap post INT case1 case2 case3 case4 case5 case6 case7 case8 case9 case10 post if [ $TEST_FAILED -ne 0 ] ; then further_help fi netsend-0.0~svnr250/proto_udplite_recv.c0000644000175000017500000000333610776173116020007 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2008 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" int init_receive_socket_udplite(struct opts *optsp, int connected_fd) { int ret = SUCCESS, i; /* check if there was a command line option to enforce * udplite header checksum */ if (optsp->udplite_checksum_coverage != LONG_MAX) { i = optsp->udplite_checksum_coverage; msg(GENTLE, "set UDPLite checksum coverage for %d bytes", i); ret = setsockopt(connected_fd, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, &i, sizeof(int)); if (ret) { err_sys("setsockopt option %d (name UDPLITE_RECV_CSCOV) failed", UDPLITE_RECV_CSCOV); return FAILURE; } } return ret; } /* vim:set ts=4 sw=4 sts=4 tw=78 ff=unix noet: */ netsend-0.0~svnr250/proto_sctp_trans.c0000644000175000017500000001172710776173116017505 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct sock_callbacks sock_callbacks; /* Creates our server socket and initialize ** options */ static int init_sctp_trans(void) { bool use_multicast = false; int fd = -1, ret; struct addrinfo hosthints, *hostres, *addrtmp; struct protoent *protoent; memset(&hosthints, 0, sizeof(struct addrinfo)); /* probe our values */ hosthints.ai_family = opts.family; hosthints.ai_socktype = opts.socktype; hosthints.ai_protocol = IPPROTO_SCTP; hosthints.ai_flags = AI_ADDRCONFIG; xgetaddrinfo(opts.hostname, opts.port, &hosthints, &hostres); addrtmp = hostres; for (addrtmp = hostres; addrtmp != NULL ; addrtmp = addrtmp->ai_next) { if (opts.family != AF_UNSPEC && addrtmp->ai_family != opts.family) { /* user fixed family! */ continue; } fd = socket(addrtmp->ai_family, addrtmp->ai_socktype, addrtmp->ai_protocol); if (fd < 0) { err_sys("socket"); continue; } protoent = getprotobynumber(addrtmp->ai_protocol); if (protoent) msg(LOUDISH, "socket created - protocol %s(%d)", protoent->p_name, protoent->p_proto); if (use_multicast) { int hops_ttl = 30; int on = 1; switch (addrtmp->ai_family) { case AF_INET6: xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops_ttl, sizeof(hops_ttl), "IPV6_MULTICAST_HOPS"); xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(int), "IPV6_MULTICAST_LOOP"); break; case AF_INET: xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&hops_ttl, sizeof(hops_ttl), "IP_MULTICAST_TTL"); xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(int), "IP_MULTICAST_LOOP"); msg(STRESSFUL, "set IP_MULTICAST_LOOP option"); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } /* We iterate over our commandline argument array - where the user ** set socketoption and set this on our socket ** NOTE: it is necessary to set the soketoption before we call ** connect, which will invoke a syn packet! ** Example: if we set the receive buffer size to a greater value, tcp ** must handle this case and send in the initial packet a window scale ** option! Now you realize why we send the socketoption before we call ** connect. --HGN */ set_socketopts(fd); /* Connect to peer ** There are three advantages to call connect for all types ** of our socket protocols (especially udp) ** ** 1. We don't need to specify a destination address (only call write) ** 2. Performance advantages (kernel level) ** 3. Error detection (e.g. destination port unreachable at udp) */ ret = connect(fd, addrtmp->ai_addr, addrtmp->ai_addrlen); if (ret == -1) err_sys_die(EXIT_FAILNET, "Can't connect to %s", opts.hostname); msg(LOUDISH, "socket connected to %s via port %s", opts.hostname, opts.port); } if (fd < 0) err_msg_die(EXIT_FAILNET, "No suitable socket found"); freeaddrinfo(hostres); return fd; } /* ** o initialize server socket ** o fstat and open our sending-file ** o block in socket and wait for client ** o sendfile(2), write(2), ... ** o print diagnostic info */ void sctp_trans_mode(void) { int connected_fd, file_fd; msg(GENTLE, "transmit mode (file: %s - hostname: %s)", opts.infile, opts.hostname); /* check if the transmitted file is present and readable */ file_fd = open_input_file(); connected_fd = init_sctp_trans(); /* fetch sockopt before the first byte */ get_sock_opts(connected_fd, &net_stat); /* construct and send netsend header to peer */ meta_exchange_snd(connected_fd, file_fd); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); trans_start(file_fd, connected_fd); gettimeofday(&opts.endtime, NULL); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/analyze.c0000644000175000017500000003543710776173116015551 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #ifndef ULLONG_MAX # define ULLONG_MAX 18446744073709551615ULL #endif #include "analyze.h" #include "global.h" #include "xfuncs.h" extern struct net_stat net_stat; extern struct conf_map_t memadvice_map[]; extern struct opts opts; #define T2S(x) ((opts.statistics > 1) ? statistic_map[x].l_name : statistic_map[x].s_name) struct statistic_map_t { const char *s_name; const char *l_name; } statistic_map[] = { #define STAT_MTU 0 { "mtu: ", "Maximum Transfer Unit: " }, #define STAT_RX_CALLS 1 { "rx-calls: ", "Number of read calls: " }, #define STAT_RX_BYTES 2 { "rx-amount: ", "Received data quantum: " }, #define STAT_TX_CALLS 3 { "tx-calls: ", "Number of write calls: " }, #define STAT_TX_BYTES 4 { "tx-amount: ", "Transmitted data quantum: " }, #define STAT_REAL_TIME 5 { "real: ", "Cumulative real time: " }, #define STAT_UTIME 6 { "utime: ", "Cumulative user space time: " }, #define STAT_STIME 7 { "stime: ", "Cumulative kernel space time: " }, #define STAT_CPU_TIME 8 { "cpu: ", "Cumulative us/ks time: " }, #define STAT_CPU_CYCLES 9 { "cpu-ticks: ", "CPU ticks since programm start:" }, #define STAT_THROUGH 10 { "throughput: ", "Throughput: " }, #define STAT_SWAPS 11 { "swaps: ", "Times process swapped: " }, #define STAT_MAXRSS 12 { "maxrss: ", "Resident set memory used: " }, #define STAT_VCS 13 { "voluntary cs:", "Voluntary context switches: " }, #define STAT_NICECS 14 { "nice cs: ", "Nice context switches: " }, }; /* unit stuff */ struct unit_map_t { const char *name_short; const char *name_long; const uint64_t factor; } unit_map[] = { { NULL, NULL, 1 }, /* 2**n (Binary prefixes, IEC 60027-2) */ #define KiB 1 { "KiB", "kibibyte", 1024 }, #define Kibit 2 { "Kibit", "kibit", 128 }, #define MiB 3 { "MiB", "mebibyte", 1048576}, #define Mibit 4 { "Mibit", "mebibit", 131072}, #define GiB 5 { "GiB", "gibibyte", 1073741824LL}, #define Gibit 6 { "Gibit", "gibibit", 134217728LL}, /* 10**n (SI prefixes) */ #define kB 7 { "kB", "kilobyte", 1000 }, #define kb 8 { "kbit", "kilobit", 125 }, #define MB 9 { "MB", "megabyte", 1000000 }, #define Mb 10 { "Mbit", "megabit", 125000 }, #define GB 11 { "GB", "gigabyte", 1000000000LL }, #define Gb 12 { "Gbit", "gigabit", 125000000LL }, }; #define K_UNIT ( (opts.stat_prefix == STAT_PREFIX_SI) ? \ ((opts.stat_unit == BYTE_UNIT) ? kB : kb) : \ ((opts.stat_unit == BYTE_UNIT) ? KiB : Kibit) ) #define M_UNIT ( (opts.stat_prefix == STAT_PREFIX_SI) ? \ ((opts.stat_unit == BYTE_UNIT) ? MB : Mb) : \ ((opts.stat_unit == BYTE_UNIT) ? MiB : Mibit) ) #define G_UNIT ( (opts.stat_prefix == STAT_PREFIX_SI) ? \ ((opts.stat_unit == BYTE_UNIT) ? GB : Gb) : \ ((opts.stat_unit == BYTE_UNIT) ? GiB : Gibit) ) #define UNIT_MAX 128 static char * unit_conv(char *buf, int buf_len, unsigned long long bytes, int unit_scale) { unsigned long long res = bytes / unit_map[unit_scale].factor; xsnprintf(buf, buf_len, "%llu %s", res, (opts.statistics > 1) ? unit_map[unit_scale].name_long : unit_map[unit_scale].name_short); return buf; } #define UNIT_CONV(byte, unit_scale) (unit_conv(unit_buf, UNIT_MAX, byte, unit_scale)) #define UNIT_N2F(x) (unit_map[x].factor) #define UNIT_N2S(x) ((opts.statistics > 1) ? \ unit_map[x].name_long : \ unit_map[x].name_short) static const char *io_call_to_str(enum io_call code) { switch(code) { case IO_SENDFILE: return "sendfile"; case IO_MMAP: return "mmap"; case IO_RW: return "write"; case IO_SPLICE: return "splice"; } return ""; } #ifdef HAVE_RDTSCLL static unsigned long long tsc_diff(unsigned long long end, unsigned long long start) { long long ret = end - start; if (ret >= 0) return ret; return (ULLONG_MAX - start) + end; } #endif void gen_human_analyse(char *buf, unsigned int max_buf_len) { int len, page_size; char unit_buf[UNIT_MAX]; struct timeval tv_tmp; struct utsname utsname; double total_real, total_utime, total_stime, total_cpu; double throughput; page_size = getpagesize(); if (uname(&utsname)) *utsname.nodename = *utsname.release = *utsname.machine = 0; len = xsnprintf(buf, max_buf_len, "\n** %s statistics (%s | %s | %s) ** \n", opts.workmode == MODE_TRANSMIT ? "tx" : "rx", utsname.nodename, utsname.release, utsname.machine); if (opts.workmode == MODE_TRANSMIT) { const char *tx_call_str = io_call_to_str(opts.io_call); /* display system call count */ len += xsnprintf(buf + len, max_buf_len - len, "%s %d (%s)\n", T2S(STAT_TX_CALLS), net_stat.total_tx_calls, tx_call_str); /* display data amount */ len += xsnprintf(buf + len, max_buf_len - len, "%s %llu %s", T2S(STAT_TX_BYTES), opts.stat_unit == BYTE_UNIT ? net_stat.total_tx_bytes : net_stat.total_tx_bytes * 8, opts.stat_unit == BYTE_UNIT ? "Byte" : "Bit"); if ( (net_stat.total_tx_bytes / UNIT_N2F(K_UNIT)) > 1) { /* display Kilo */ len += xsnprintf(buf + len, max_buf_len - len, " (%s", UNIT_CONV(net_stat.total_tx_bytes, K_UNIT)); if ( (net_stat.total_tx_bytes / UNIT_N2F(M_UNIT)) > 1) { /* display mega */ len += xsnprintf(buf + len, max_buf_len - len, ", %s", UNIT_CONV(net_stat.total_tx_bytes, M_UNIT)); } len += xsnprintf(buf + len, max_buf_len - len, "%s", ")"); } len += xsnprintf(buf + len, max_buf_len - len, "%s", "\n"); /* newline */ } else { /* MODE_RECEIVE */ /* display system call count */ len += xsnprintf(buf + len, max_buf_len - len, "%s %d (read)\n", T2S(STAT_RX_CALLS), net_stat.total_rx_calls); /* display data amount */ len += xsnprintf(buf + len, max_buf_len - len, "%s %llu %s", T2S(STAT_RX_BYTES), opts.stat_unit == BYTE_UNIT ? net_stat.total_rx_bytes : net_stat.total_rx_bytes * 8, opts.stat_unit == BYTE_UNIT ? "Byte" : "Bit"); if ( (net_stat.total_rx_bytes / UNIT_N2F(K_UNIT)) > 1) { /* display kilo */ len += xsnprintf(buf + len, max_buf_len - len, " (%s", UNIT_CONV(net_stat.total_rx_bytes, K_UNIT)); if ( (net_stat.total_rx_bytes / UNIT_N2F(M_UNIT)) > 1) { /* display mega */ len += xsnprintf(buf + len, max_buf_len - len, ", %s", UNIT_CONV(net_stat.total_rx_bytes, M_UNIT)); } } len += xsnprintf(buf + len, max_buf_len - len, "%s", ")\n"); /* newline */ } subtime(&net_stat.use_stat_end.time, &net_stat.use_stat_start.time, &tv_tmp); total_real = tv_tmp.tv_sec + ((double) tv_tmp.tv_usec) / 1000000; if (total_real <= 0.0) total_real = 0.00001; /* real time */ len += xsnprintf(buf + len, max_buf_len - len, "%s %.4f sec\n", T2S(STAT_REAL_TIME), total_real); /* user, system, cpu-time & cpu cycles */ subtime(&net_stat.use_stat_end.ru.ru_utime, &net_stat.use_stat_start.ru.ru_utime, &tv_tmp); total_utime = tv_tmp.tv_sec + ((double) tv_tmp.tv_usec) / 1000000; subtime(&net_stat.use_stat_end.ru.ru_stime, &net_stat.use_stat_start.ru.ru_stime, &tv_tmp); total_stime = tv_tmp.tv_sec + ((double) tv_tmp.tv_usec) / 1000000; total_cpu = total_utime + total_stime; if (total_cpu <= 0.0) total_cpu = 0.0001; len += xsnprintf(buf + len, max_buf_len - len, "%s %.4f sec\n", T2S(STAT_UTIME), total_utime); len += xsnprintf(buf + len, max_buf_len - len, "%s %.4f sec\n", T2S(STAT_STIME), total_stime); len += xsnprintf(buf + len, max_buf_len - len, "%s %.4f sec (cpu/real: %.2f%%)\n", T2S(STAT_CPU_TIME), total_cpu, (total_cpu / total_real ) * 100); #ifdef HAVE_RDTSCLL len += xsnprintf(buf + len, max_buf_len - len, "%s %lld cycles\n", T2S(STAT_CPU_CYCLES), tsc_diff(net_stat.use_stat_end.tsc, net_stat.use_stat_start.tsc)); #endif if (opts.verbose >= LOUDISH) { long res; /* test for signed long overflow in subtraction */ if (((net_stat.use_stat_end.ru.ru_nswap ^ net_stat.use_stat_start.ru.ru_nswap) & ((net_stat.use_stat_end.ru.ru_nswap - net_stat.use_stat_start.ru.ru_nswap) ^ net_stat.use_stat_end.ru.ru_nswap)) >> (sizeof(long)*CHAR_BIT-1) ) { err_sys("Overflow in long substraction"); } res = net_stat.use_stat_end.ru.ru_nswap - net_stat.use_stat_start.ru.ru_nswap; /* swaps */ len += xsnprintf(buf + len, max_buf_len - len, "%s %ld \n", T2S(STAT_SWAPS), res); /* voluntary context switches */ res = sublong(net_stat.use_stat_end.ru.ru_nvcsw, net_stat.use_stat_start.ru.ru_nvcsw); len += xsnprintf(buf + len, max_buf_len - len, "%s %ld\n", T2S(STAT_VCS), res); /* nice (involuntary) context switches */ res = sublong(net_stat.use_stat_end.ru.ru_nivcsw, net_stat.use_stat_start.ru.ru_nivcsw); len += xsnprintf(buf + len, max_buf_len - len, "%s %ld\n", T2S(STAT_NICECS), res); #if 0 /* ru_maxrss - bytes of resident set memory used */ res = sublong(net_stat.use_stat_end.ru.ru_maxrss, net_stat.use_stat_start.ru.ru_maxrss); len += xsnprintf(buf + len, max_buf_len - len, "%s %ld bytes\n", T2S(STAT_MAXRSS), res * page_size); #endif } /* throughput (bytes/s)*/ throughput = opts.workmode == MODE_TRANSMIT ? ((double)net_stat.total_tx_bytes) / total_real : ((double)net_stat.total_rx_bytes) / total_real; len += xsnprintf(buf + len, max_buf_len - len, "%s %.5f %s/sec", T2S(STAT_THROUGH), throughput / UNIT_N2F(K_UNIT), UNIT_N2S(K_UNIT)); if ((throughput / UNIT_N2F(M_UNIT)) > 1) { len += xsnprintf(buf + len, max_buf_len - len, " (%.5f %s/sec", (throughput / UNIT_N2F(M_UNIT)), UNIT_N2S(M_UNIT)); if ((throughput / UNIT_N2F(G_UNIT)) >= 1) { len += xsnprintf(buf + len, max_buf_len - len, ", %.5f %s/sec", (throughput / UNIT_N2F(G_UNIT)), UNIT_N2S(G_UNIT)); } len += xsnprintf(buf + len, max_buf_len - len, "%s", ")"); /* newline */ } len += xsnprintf(buf + len, max_buf_len - len, "%s", "\n"); } #undef T2S void gen_machine_analyse(char *buf, unsigned int max_buf_len) { int len = 0, page_size; long res; const char *call_str; struct timeval tv_tmp; double total_real, total_utime, total_stime, total_cpu; page_size = getpagesize(); /* XXX: ** If you change the format or sequence ** 1) increment VERSIONSTRING ** 2) comment these in http://netsend.berlios.de/usag.html */ /* 1. versionstring */ len += xsnprintf(buf + len, max_buf_len - len, "%s ", VERSIONSTRING); /* 2. workmode: rx || tx */ len += xsnprintf(buf + len, max_buf_len - len, "%s ", (opts.workmode == MODE_TRANSMIT) ? "tx" : "rx"); /* 3. utilized io call */ if (opts.workmode == MODE_TRANSMIT) call_str = io_call_to_str(opts.io_call); else call_str = "read"; len += xsnprintf(buf + len, max_buf_len - len, "%s ", call_str); /* memory advise */ len += xsnprintf(buf + len, max_buf_len - len, "%s ", opts.change_mem_advise ? memadvice_map[opts.mem_advice].conf_string : "none"); /* buffer size (only digits that a regex can match this entry ** clean (\d+)) */ len += xsnprintf(buf + len, max_buf_len - len, "%d ", opts.buffer_size ? opts.buffer_size : 0); /* nice level ** FIXME: don't take opts.nice level, call getnice() */ len += xsnprintf(buf + len, max_buf_len - len, "%d ", opts.nice == INT_MAX ? 0 : opts.nice); /* 4. io-function call count */ len += xsnprintf(buf + len, max_buf_len - len, "%d ", (opts.workmode == MODE_TRANSMIT) ? net_stat.total_tx_calls : net_stat.total_rx_calls); /* 5. byte transmitted/received */ len += xsnprintf(buf + len, max_buf_len - len, "%llu ", (opts.workmode == MODE_TRANSMIT) ? net_stat.total_tx_bytes : net_stat.total_rx_bytes); /* 6. realtime */ subtime(&net_stat.use_stat_end.time, &net_stat.use_stat_start.time, &tv_tmp); total_real = tv_tmp.tv_sec + ((double) tv_tmp.tv_usec) / 1000000; if (total_real <= 0.0) total_real = 0.00001; len += xsnprintf(buf + len, max_buf_len - len, "%.4f ", total_real); /* 7. user time */ subtime(&net_stat.use_stat_end.ru.ru_utime, &net_stat.use_stat_start.ru.ru_utime, &tv_tmp); total_utime = tv_tmp.tv_sec + ((double) tv_tmp.tv_usec) / 1000000; len += xsnprintf(buf + len, max_buf_len - len, "%.4f ", total_utime); /* 8. system time */ subtime(&net_stat.use_stat_end.ru.ru_stime, &net_stat.use_stat_start.ru.ru_stime, &tv_tmp); total_stime = tv_tmp.tv_sec + ((double) tv_tmp.tv_usec) / 1000000; len += xsnprintf(buf + len, max_buf_len - len, "%.4f ", total_stime); /* 9. cpu-time */ total_cpu = total_utime + total_stime; if (total_cpu <= 0.0) total_cpu = 0.0001; len += xsnprintf(buf + len, max_buf_len - len, "%.4f ", total_cpu); /* 10. swaps (see http://www.khmere.com/freebsd_book/html/ch07.html */ res = sublong(net_stat.use_stat_end.ru.ru_nswap, net_stat.use_stat_start.ru.ru_nswap); len += xsnprintf(buf + len, max_buf_len - len, "%ld ", res); /* 11. voluntary context switches */ res = sublong(net_stat.use_stat_end.ru.ru_nvcsw, net_stat.use_stat_start.ru.ru_nvcsw); len += xsnprintf(buf + len, max_buf_len - len, "%ld ", res); /* 12. total number of context switches */ res = sublong(net_stat.use_stat_end.ru.ru_nivcsw, net_stat.use_stat_start.ru.ru_nivcsw); len += xsnprintf(buf + len, max_buf_len - len, "%ld ", res); #if 0 /* 11. ru_maxrss - bytes of resident set memory used */ res = sublong(net_stat.use_stat_end.ru.ru_maxrss, net_stat.use_stat_start.ru.ru_maxrss); len += xsnprintf(buf + len, max_buf_len - len, "%ld ", res * page_size); #endif /* trailing newline */ len += xsnprintf(buf + len, max_buf_len - len, "%s", "\n"); } int subtime(struct timeval *op1, struct timeval *op2, struct timeval *result) { int borrow = 0, sign = 0; struct timeval *temp_time; if (TIME_LT(op1, op2)) { temp_time = op1; op1 = op2; op2 = temp_time; sign = 1; } if (op1->tv_usec >= op2->tv_usec) { result->tv_usec = op1->tv_usec-op2->tv_usec; } else { result->tv_usec = (op1->tv_usec + 1000000) - op2->tv_usec; borrow = 1; } result->tv_sec = (op1->tv_sec-op2->tv_sec) - borrow; return sign; } long sublong(long a, long b) { if (((a ^ b) & ((a - b) ^ a)) >> ( sizeof(long) * CHAR_BIT - 1) ) { err_sys("Overflow in long substraction"); } return a - b; } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/trans_common.c0000644000175000017500000002500410776173116016572 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #define _GNU_SOURCE #define _XOPEN_SOURCE 600 /* needed for posix_madvise/fadvise */ #include #include #undef _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct socket_options socket_options[]; extern struct sock_callbacks sock_callbacks; static int get_mem_adv_m(int adv) { switch (adv) { case MEMADV_SEQUENTIAL: return POSIX_MADV_SEQUENTIAL; case MEMADV_DONTNEED: return POSIX_MADV_DONTNEED; case MEMADV_RANDOM: return POSIX_MADV_RANDOM; case MEMADV_NORMAL: return POSIX_MADV_NORMAL; case MEMADV_NOREUSE: /* there is only POSIX_FADV_NOREUSE */ case MEMADV_WILLNEED: return POSIX_MADV_WILLNEED; default: break; } DEBUGPRINTF("adv number %d unknown\n", adv); exit(EXIT_FAILMISC); } static int get_mem_adv_f(int adv) { switch (adv) { case MEMADV_SEQUENTIAL: return POSIX_FADV_SEQUENTIAL; case MEMADV_DONTNEED: return POSIX_FADV_DONTNEED; case MEMADV_RANDOM: return POSIX_FADV_RANDOM; case MEMADV_NORMAL: return POSIX_FADV_NORMAL; case MEMADV_NOREUSE: return POSIX_FADV_NOREUSE; case MEMADV_WILLNEED: return POSIX_FADV_WILLNEED; default: break; } DEBUGPRINTF("adv number %d unknown\n", adv); exit(EXIT_FAILMISC); } static ssize_t write_len(int fd, const void *buf, size_t len) { const char *bufptr = buf; ssize_t total = 0; do { ssize_t written = sock_callbacks.cb_write(fd, bufptr, len); net_stat.total_tx_calls += 1; if (written < 0) { int real_errno; if (errno == EINTR || errno == EAGAIN) continue; real_errno = errno; err_msg("Could not write %u bytes: %s", len, strerror(errno)); if (opts.protocol == IPPROTO_SCTP && real_errno == EMSGSIZE) { err_msg("for SCTP the maximum size of data that can be sent in a " "single send call is limited by SO_SNDBUF.\n" "Either increase send buffer size (-s SO_SNDBUF) or " "lower the write buffer size (-b)"); } errno = real_errno; break; } total += written; bufptr += written; len -= written; } while (len > 0); return total > 0 ? total : -1; } static ssize_t trans_rw(int file_fd, int connected_fd) { int buflen; ssize_t cnt, cnt_coll = 0; unsigned char *buf; msg(STRESSFUL, "send via read/write io operation"); /* user option or default */ buflen = opts.buffer_size ? opts.buffer_size : DEFAULT_BUFSIZE; buf = xmalloc(buflen); if (opts.change_mem_advise && posix_fadvise(file_fd, 0, 0, get_mem_adv_f(opts.mem_advice))) { err_sys("posix_fadvise"); /* do not exit */ } touch_use_stat(TOUCH_BEFORE_OP, &net_stat.use_stat_start); while ((cnt = read(file_fd, buf, buflen)) > 0) { cnt_coll = write_len(connected_fd, buf, cnt); if (cnt_coll == -1) break; /* correct statistics */ net_stat.total_tx_bytes += cnt_coll; /* if we reached a user transfer limit? */ if (opts.multiple_barrier) { unsigned long long limit = buflen * opts.multiple_barrier; if (net_stat.total_tx_bytes >= limit) break; } } touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); free(buf); return cnt_coll; } static ssize_t trans_mmap(int file_fd, int connected_fd) { int ret = 0; ssize_t rc, written = 0, write_cnt; struct stat stat_buf; void *mmap_buf; msg(STRESSFUL, "send via mmap/write io operation"); xfstat(file_fd, &stat_buf, opts.infile); net_stat.total_tx_bytes = 0; touch_use_stat(TOUCH_BEFORE_OP, &net_stat.use_stat_start); mmap_buf = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, file_fd, 0); if (mmap_buf == MAP_FAILED) err_sys_die(EXIT_FAILMISC, "Can't mmap file %s: %s\n", opts.infile, strerror(errno)); if (opts.change_mem_advise && posix_madvise(mmap_buf, stat_buf.st_size, get_mem_adv_m(opts.mem_advice))) err_sys("posix_madvise"); /* do not exit */ /* full or partial write */ write_cnt = opts.buffer_size ? opts.buffer_size : stat_buf.st_size; /* write chunked sized frames */ while (stat_buf.st_size - written >= write_cnt) { char *tmpbuf = mmap_buf; rc = write_len(connected_fd, tmpbuf + written, write_cnt); if (rc == -1) goto write_fail; written += rc; } /* and write remaining bytes, if any */ write_cnt = stat_buf.st_size - written; if (write_cnt > 0) { char *tmpbuf = mmap_buf; rc = write_len(connected_fd, tmpbuf + written, write_cnt); if (rc == -1) { write_fail: touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); net_stat.total_tx_bytes = written; return munmap(mmap_buf, stat_buf.st_size); } written += rc; } touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); if (stat_buf.st_size != written) { fprintf(stderr, "ERROR: Can't flush buffer within write call: %s!\n", strerror(errno)); fprintf(stderr, " size: %ld written %zd\n", (long)stat_buf.st_size, written); } ret = munmap(mmap_buf, stat_buf.st_size); if (ret == -1) err_sys("Can't munmap buffer"); /* correct statistics */ net_stat.total_tx_bytes = stat_buf.st_size; return rc; } #ifdef HAVE_SPLICE static long splice_chunk(int pipe_fd, int fd_out, size_t len, int flags) { long written, total = 0; do { written = splice(pipe_fd, NULL, fd_out, NULL, len, flags); if (written < 0) { err_sys("Failure in splice from pipe"); break; } net_stat.total_tx_calls++; total += written; len -= written; } while (len > 0); net_stat.total_tx_bytes += total; return total; } static ssize_t ss_splice_frompipe(int pipe_fd, int connected_fd, ssize_t write_cnt) { ssize_t written, total = 0; touch_use_stat(TOUCH_BEFORE_OP, &net_stat.use_stat_start); do { written = splice(pipe_fd, NULL, connected_fd, NULL, write_cnt, SPLICE_F_MOVE|SPLICE_F_MORE); if (written < 0) { err_sys("Failure in splice from pipe"); break; } net_stat.total_tx_calls += 1; total += written; } while (written > 0); touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); net_stat.total_tx_bytes = total; return 0; } static ssize_t get_splice_size(int file_fd, struct stat *stat_buf) { ssize_t write_cnt; xfstat(file_fd, stat_buf, opts.infile); if (opts.buffer_size) write_cnt = opts.buffer_size; else if (S_ISREG(stat_buf->st_mode)) write_cnt = stat_buf->st_size; else write_cnt = 65536; if (write_cnt > 65536) write_cnt = 65536; if (opts.buffer_size > 65536) msg(STRESSFUL, "reduced splice buffer length to 64k"); return write_cnt; } #endif static ssize_t trans_splice(int file_fd, int connected_fd) { #ifdef HAVE_SPLICE int pipefds[2]; struct stat stat_buf; ssize_t rc, write_cnt; loff_t offset = 0; msg(STRESSFUL, "send via splice io operation"); write_cnt = get_splice_size(file_fd, &stat_buf); if (S_ISFIFO(stat_buf.st_mode)) return ss_splice_frompipe(file_fd, connected_fd, write_cnt); xpipe(pipefds); touch_use_stat(TOUCH_BEFORE_OP, &net_stat.use_stat_start); /* write chunked sized frames */ while (stat_buf.st_size - offset - 1 >= write_cnt) { rc = splice(file_fd, &offset, pipefds[1], NULL, write_cnt, SPLICE_F_MOVE); if (rc == -1) err_sys_die(EXIT_FAILMISC, "Failure in splice to pipe"); if (splice_chunk(pipefds[0], connected_fd, rc, SPLICE_F_MOVE|SPLICE_F_MORE) < 0) goto finish; } /* and write remaining bytes, if any */ write_cnt = stat_buf.st_size - offset - 1; if (write_cnt >= 0) { rc = splice(file_fd, &offset, pipefds[1], NULL, write_cnt + 1, 0); if (rc == -1) err_sys_die(EXIT_FAILMISC, "Failure in splice to pipe"); splice_chunk(pipefds[0], connected_fd, rc, SPLICE_F_MOVE); } finish: touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); if (offset != stat_buf.st_size) err_msg("Incomplete transfer in splice: %d of %ld bytes", offset , stat_buf.st_size); close(pipefds[0]); close(pipefds[1]); return rc; #else err_msg_die(EXIT_FAILMISC, "splice support not compiled in"); #endif } static ssize_t trans_sendfile(int file_fd, int connected_fd) { struct stat stat_buf; ssize_t rc, write_cnt; off_t offset = 0; msg(STRESSFUL, "send via sendfile io operation"); xfstat(file_fd, &stat_buf, opts.infile); if (stat_buf.st_size == 0) err_msg("%s: empty file", opts.infile); /* full or partial write */ write_cnt = opts.buffer_size ? opts.buffer_size : stat_buf.st_size; touch_use_stat(TOUCH_BEFORE_OP, &net_stat.use_stat_start); /* write chunked sized frames */ while (stat_buf.st_size - offset - 1 >= write_cnt) { rc = sendfile(connected_fd, file_fd, &offset, write_cnt); if (rc == -1) err_sys_die(EXIT_FAILNET, "Failure in sendfile routine"); net_stat.total_tx_calls += 1; } /* and write remaining bytes, if any */ write_cnt = stat_buf.st_size - offset - 1; if (write_cnt >= 0) { rc = sendfile(connected_fd, file_fd, &offset, write_cnt + 1); if (rc == -1) err_sys_die(EXIT_FAILNET, "Failure in sendfile routine"); net_stat.total_tx_calls += 1; } touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); if (offset != stat_buf.st_size) err_msg_die(EXIT_FAILNET, "Incomplete transfer from sendfile: %d of %ld bytes", offset , stat_buf.st_size); /* correct statistics */ net_stat.total_tx_bytes = stat_buf.st_size; return rc; } void trans_start(int file_fd, int connected_fd) { switch (opts.io_call) { case IO_SENDFILE: trans_sendfile(file_fd, connected_fd); break; case IO_SPLICE: trans_splice(file_fd, connected_fd); break; case IO_MMAP: trans_mmap(file_fd, connected_fd); break; case IO_RW: trans_rw(file_fd, connected_fd); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/proto_tcp_trans.c0000644000175000017500000001211610776173116017313 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" #include "tcp_md5sig.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct socket_options socket_options[]; extern struct sock_callbacks sock_callbacks; static void tcp_set_socketopts(int fd, const struct sockaddr *sa) { if (opts.tcp_use_md5sig) { static const char key[] = "netsend"; struct tcp_md5sig sig = { .tcpm_keylen = sizeof(key) }; memcpy(sig.tcpm_key, key, sizeof(key)); memcpy(&sig.tcpm_addr, (const struct sockaddr_storage *) sa, min(sizeof(sig.tcpm_addr), sizeof(*sa))); xsetsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &sig, sizeof(sig), "TCP_MD5SIG"); } set_socketopts(fd); } /* Creates our server socket and initialize ** options */ static int init_tcp_trans(void) { bool use_multicast = false; int fd = -1, ret; struct addrinfo hosthints, *hostres, *addrtmp; struct protoent *protoent; memset(&hosthints, 0, sizeof(struct addrinfo)); /* probe our values */ hosthints.ai_family = opts.family; hosthints.ai_socktype = opts.socktype; hosthints.ai_protocol = IPPROTO_TCP; hosthints.ai_flags = AI_ADDRCONFIG; xgetaddrinfo(opts.hostname, opts.port, &hosthints, &hostres); addrtmp = hostres; for (addrtmp = hostres; addrtmp != NULL ; addrtmp = addrtmp->ai_next) { if (opts.family != AF_UNSPEC && addrtmp->ai_family != opts.family) { /* user fixed family! */ continue; } fd = socket(addrtmp->ai_family, addrtmp->ai_socktype, addrtmp->ai_protocol); if (fd < 0) { err_sys("socket"); continue; } protoent = getprotobynumber(addrtmp->ai_protocol); if (protoent) msg(LOUDISH, "socket created - protocol %s(%d)", protoent->p_name, protoent->p_proto); assert(addrtmp->ai_protocol == IPPROTO_TCP); if (use_multicast) { int hops_ttl = 30; int on = 1; switch (addrtmp->ai_family) { case AF_INET6: xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops_ttl, sizeof(hops_ttl), "IPV6_MULTICAST_HOPS"); xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(int), "IPV6_MULTICAST_LOOP"); break; case AF_INET: xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&hops_ttl, sizeof(hops_ttl), "IP_MULTICAST_TTL"); xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(int), "IP_MULTICAST_LOOP"); msg(STRESSFUL, "set IP_MULTICAST_LOOP option"); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } tcp_set_socketopts(fd, addrtmp->ai_addr); /* Connect to peer ** There are three advantages to call connect for all types ** of our socket protocols (especially udp) ** ** 1. We don't need to specify a destination address (only call write) ** 2. Performance advantages (kernel level) ** 3. Error detection (e.g. destination port unreachable at udp) */ ret = connect(fd, addrtmp->ai_addr, addrtmp->ai_addrlen); if (ret == -1) err_sys_die(EXIT_FAILNET, "Can't connect to %s", opts.hostname); msg(LOUDISH, "socket connected to %s via port %s", opts.hostname, opts.port); } if (fd < 0) err_msg_die(EXIT_FAILNET, "No suitable socket found"); freeaddrinfo(hostres); return fd; } /* ** o initialize server socket ** o fstat and open our sending-file ** o block in socket and wait for client ** o sendfile(2), write(2), ... ** o print diagnostic info */ void tcp_trans_mode(void) { int connected_fd, file_fd; msg(GENTLE, "transmit mode (file: %s - hostname: %s)", opts.infile, opts.hostname); /* check if the transmitted file is present and readable */ file_fd = open_input_file(); connected_fd = init_tcp_trans(); /* fetch sockopt before the first byte */ get_sock_opts(connected_fd, &net_stat); /* construct and send netsend header to peer */ meta_exchange_snd(connected_fd, file_fd); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); trans_start(file_fd, connected_fd); gettimeofday(&opts.endtime, NULL); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/configure0000755000175000017500000001320210776173116015633 0ustar martinmartin#! /bin/bash # $Id: configure 164 2007-07-25 19:21:00Z fwest $ ### Default Values # (change here or use configure option interface (the clean way)) prefix=/usr # version MAJOR_REL=0 MINOR_REL=1 # compiler flags WARNINGS="-Wall -W -Wwrite-strings -Wsign-compare \ -Wpointer-arith -Wcast-qual -Wcast-align \ -Wstrict-prototypes -Wmissing-prototypes \ -Wnested-externs -Winline -Wshadow -Wformat=2" ### Stop Editing Here (expect you know what you are doing) # Developer Notes: # # o Take a look at the end of this file of this configure-script # for the script execution order # # o If you add an test-case note the order (e.g. check_for_alloca) # 1. STDOUT output # 2. config.h output # # o Script improvements, hints or anything else is welcome! # send email to the netsend developers # (http://netsend.berlios.de/deve.html) # internal values c_red="\033[31,1m" c_reset="\033[0m" cc_flags="" # function declarations usage() { cat << EOF Usage: configure [options] Options: [defaults in brackets after descriptions] Configuration: --help print this message --prefix=PREFIX install architecture-independent files in PREFIX (/usr) --enable-debug enable debug information (#define DEBUG and add CFLAGS -g) --verbose enable verbose configure output (hacker-mode[tm]) EOF } parse_args() { ac_prev= for ac_option do if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi case "$ac_option" in -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) ac_optarg= ;; esac case "$ac_option" in --enable-debug | -enable-debug) debug="yes" ;; -help | --help | -h) usage; exit 0; ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix="$ac_optarg" ;; -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } ;; *) ;; esac done } print_config_header() { # stdout stuff echo "*** configure-ng ***" # Make.Rules stuff echo -e "# Make.Rules. Generated by configure script\n" > Make.Rules # config.h stuff echo -n "/* Generated config.h see configure script for" > config.h echo " further information */" >> config.h } create_make_rules() { cflags_save="$CFLAGS" echo "prefix = $prefix" >> Make.Rules echo "exec_prefix = \${prefix}" >> Make.Rules echo >> Make.Rules echo "bindir = \${exec_prefix}/bin" >> Make.Rules # version information echo "MAJOR_REL=$MAJOR_REL" >> Make.Rules echo "MINOR_REL=$MINOR_REL" >> Make.Rules echo >> Make.Rules # compiler flags if [ "$debug" = yes ] ; then echo "XCFLAGS=-g -O0 -DDEBUG" >> Make.Rules cc_flags="-g -O0 -DDEBUG" else echo "XCFLAGS=$cflags_save" >> Make.Rules cc_flags="$cflags_save" fi echo "WARNINGS = $WARNINGS" >> Make.Rules echo "CFLAGS = \$(WARNINGS) \$(XCFLAGS)" >> Make.Rules cc_flags="$WARNINGS $cc_flags" } check_for_alloca() { echo -n "checking for alloca..." TMPDIR=`mktemp -d` cat > "$TMPDIR"/alloca.c < int main(int argc, char **argv){ alloca(23); return 0; } EOF gcc -o /dev/null "$TMPDIR"/alloca.c >/dev/null 2>&1 if [ $? -eq 0 ];then echo " yes" echo "#define HAVE_ALLOCA 1" >>config.h else echo " no" echo "#undef HAVE_ALLOCA" >>config.h fi rm -f "$TMPDIR"/alloca.c rmdir "$TMPDIR" } check_for_rdtscll() { echo -n "checking for rdtscll..." TMPDIR=`mktemp -d` cat > "$TMPDIR"/rdtscll.c </dev/null 2>&1 if [ $? -eq 0 ];then echo " yes" echo "/* uncomment if you know TSC to be stable on this system */" >>config.h echo "/* #define HAVE_RDTSCLL 1 */" >>config.h else echo " no" echo "#undef HAVE_RDTSCLL" >>config.h fi rm -f "$TMPDIR"/rdtscll.c rmdir "$TMPDIR" } check_for_af_tipc() { echo -n "checking for TIPC support..." TMPDIR=`mktemp -d` cat > "$TMPDIR"/tipc.c < #include int main(int argc, char **argv){ struct sockaddr_tipc s; int af = AF_TIPC; return 0; } EOF gcc -o /dev/null "$TMPDIR"/tipc.c >/dev/null 2>&1 if [ $? -eq 0 ];then echo " yes" echo "#define HAVE_AF_TIPC 1" >>config.h else echo " no" echo "#undef HAVE_AF_TIPC" >>config.h fi rm -f "$TMPDIR"/tipc.c rmdir "$TMPDIR" } check_for_splice() { echo -n "checking for splice..." TMPDIR=`mktemp -d` cat > "$TMPDIR"/splice.c < #include int main(void) { return splice(0, NULL, 1, NULL, 42, SPLICE_F_MOVE|SPLICE_F_MORE); } EOF gcc -o /dev/null "$TMPDIR"/splice.c >/dev/null 2>&1 if [ $? -eq 0 ];then echo " yes" echo "#define HAVE_SPLICE 1" >>config.h else echo " no" echo "#undef HAVE_SPLICE" >>config.h fi rm -f "$TMPDIR"/splice.c rmdir "$TMPDIR" } print_config() { echo echo -e "\nConfigure picked following defaults, options and paths:" echo -e "Please validate this values (at least it isn't my box ;-)" echo echo " Prefix: $prefix" if [ "$debug" = yes ] ; then echo " Debug: yes" else echo " Debug: no" fi echo " Compiler Flags: $cc_flags" echo } parse_args $* print_config_header create_make_rules check_for_alloca check_for_rdtscll check_for_splice check_for_af_tipc print_config # vim:set ts=2 sw=2 tw=78 noet: netsend-0.0~svnr250/analyze.h0000644000175000017500000000245610776173116015551 0ustar martinmartin/* ** $Id: analyze.h 205 2007-12-22 10:34:23Z fwest $ ** ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ void gen_human_analyse(char *, unsigned int); void gen_machine_analyse(char *, unsigned int); long sublong(long, long); #define TIME_GT(x,y) (x->tv_sec > y->tv_sec || (x->tv_sec == y->tv_sec && x->tv_usec > y->tv_usec)) #define TIME_LT(x,y) (x->tv_sec < y->tv_sec || (x->tv_sec == y->tv_sec && x->tv_usec < y->tv_usec)) int subtime(struct timeval *, struct timeval *, struct timeval *); netsend-0.0~svnr250/proto_udp_trans.c0000644000175000017500000001172610776173116017323 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct sock_callbacks sock_callbacks; /* Creates our server socket and initialize ** options */ static int init_udp_trans(void) { bool use_multicast = false; int fd = -1, ret; struct addrinfo hosthints, *hostres, *addrtmp; struct protoent *protoent; memset(&hosthints, 0, sizeof(struct addrinfo)); /* probe our values */ hosthints.ai_family = opts.family; hosthints.ai_socktype = opts.socktype; hosthints.ai_protocol = IPPROTO_UDP; hosthints.ai_flags = AI_ADDRCONFIG; xgetaddrinfo(opts.hostname, opts.port, &hosthints, &hostres); addrtmp = hostres; for (addrtmp = hostres; addrtmp != NULL ; addrtmp = addrtmp->ai_next) { if (opts.family != AF_UNSPEC && addrtmp->ai_family != opts.family) { /* user fixed family! */ continue; } fd = socket(addrtmp->ai_family, addrtmp->ai_socktype, addrtmp->ai_protocol); if (fd < 0) { err_sys("socket"); continue; } protoent = getprotobynumber(addrtmp->ai_protocol); if (protoent) msg(LOUDISH, "socket created - protocol %s(%d)", protoent->p_name, protoent->p_proto); /* mulicast checks */ assert(addrtmp->ai_protocol == IPPROTO_UDP); switch (addrtmp->ai_family) { case AF_INET6: if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *) addrtmp->ai_addr)->sin6_addr)) { use_multicast = true; } break; case AF_INET: if (IN_MULTICAST(ntohl(((struct sockaddr_in *) addrtmp->ai_addr)->sin_addr.s_addr))) { use_multicast = true; } break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } if (use_multicast) { int hops_ttl = 30; int on = 1; switch (addrtmp->ai_family) { case AF_INET6: xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops_ttl, sizeof(hops_ttl), "IPV6_MULTICAST_HOPS"); xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(int), "IPV6_MULTICAST_LOOP"); break; case AF_INET: xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&hops_ttl, sizeof(hops_ttl), "IP_MULTICAST_TTL"); xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(int), "IP_MULTICAST_LOOP"); msg(STRESSFUL, "set IP_MULTICAST_LOOP option"); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } set_socketopts(fd); /* Connect to peer ** There are three advantages to call connect for all types ** of our socket protocols (especially udp) ** ** 1. We don't need to specify a destination address (only call write) ** 2. Performance advantages (kernel level) ** 3. Error detection (e.g. destination port unreachable at udp) */ ret = connect(fd, addrtmp->ai_addr, addrtmp->ai_addrlen); if (ret == -1) err_sys_die(EXIT_FAILNET, "Can't connect to %s", opts.hostname); msg(LOUDISH, "socket connected to %s via port %s", opts.hostname, opts.port); } if (fd < 0) err_msg_die(EXIT_FAILNET, "No suitable socket found"); freeaddrinfo(hostres); return fd; } /* ** o initialize server socket ** o fstat and open our sending-file ** o block in socket and wait for client ** o sendfile(2), write(2), ... ** o print diagnostic info */ void udp_trans_mode(void) { int connected_fd, file_fd; msg(GENTLE, "transmit mode (file: %s - hostname: %s)", opts.infile, opts.hostname); /* check if the transmitted file is present and readable */ file_fd = open_input_file(); connected_fd = init_udp_trans(); /* fetch sockopt before the first byte */ get_sock_opts(connected_fd, &net_stat); /* construct and send netsend header to peer */ meta_exchange_snd(connected_fd, file_fd); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); trans_start(file_fd, connected_fd); gettimeofday(&opts.endtime, NULL); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/xfuncs.h0000644000175000017500000000117510776173116015411 0ustar martinmartin#include #include #include #include #include void *xmalloc(size_t len); static inline void *xzalloc(size_t len) { void *p = xmalloc(len); if (p) memset(p, 0, len); return p; } void xgetaddrinfo(const char *, const char *, struct addrinfo *, struct addrinfo **); void xsetsockopt(int, int, int, const void *, socklen_t, const char *); int xsnprintf(char *, size_t , const char *, ...); char *xstrdup(const char *src); void xfstat(int filedes, struct stat *buf, const char *str); void xpipe(int filedes[2]); /* vim:set ts=4 sw=4 sts=4 tw=78 ff=unix noet: */ netsend-0.0~svnr250/proto_udp_recv.c0000644000175000017500000000175110776173116017130 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2007 - Florian Westphal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "proto_udp_recv.h" int udp_listen(int sockfd __attribute__((unused)), int ignored __attribute__((unused))) { return 0; } netsend-0.0~svnr250/proto_udp_recv.h0000644000175000017500000000004210776173116017125 0ustar martinmartinint udp_listen(int sockfd, int); netsend-0.0~svnr250/net.c0000644000175000017500000001127310776173116014664 0ustar martinmartin/* ** ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "proto_tipc.h" #include "global.h" extern struct opts opts; extern struct socket_options socket_options[]; /* set TCP_NODELAY opption on socket ** return the previous value (0, 1) or ** -1 if a error occur */ int set_nodelay(int fd, int flag) { int ret = 0; socklen_t ret_size; if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &ret, &ret_size) < 0) return -1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0) return -1; return ret; } static int get_ip_sock_opts(int fd, struct net_stat *ns) { (void) fd; (void) ns; return 0; } static int get_tcp_sock_opts(int fd, struct net_stat *ns) { int ret; socklen_t len; /* NOTE: ** ipv4/tcp.c:tcp_getsockopt() returns ** tp->mss_cache_std; ** if (!val && ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) ** val = tp->rx_opt.user_mss; */ len = sizeof(ns->sock_stat.mss); ret = getsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &ns->sock_stat.mss, &len); if ((ret == -1) || (ns->sock_stat.mss <= 0)) { fprintf(stderr, "Can't determine mss for socket (mss: %d): %s " "(fall back to 1500 bytes)\n", ns->sock_stat.mss, strerror(errno)); ns->sock_stat.mss = 1500; } return 0; } /* get_sock_opts() appoint some socket specific ** values for further use ... (hopefully ;-) ** Values are determined by hand for the possibility ** to change something ** We should call this function after socket creation ** and at the and off our transmit/receive phase ** --HGN */ int get_sock_opts(int fd, struct net_stat *ns) { int ret; #ifdef HAVE_AF_TIPC if (opts.family == AF_TIPC) return 0; #endif ret = get_ip_sock_opts(fd, ns); if (ret != 0) { return ret; } switch (opts.protocol) { case IPPROTO_TCP: return get_tcp_sock_opts(fd, ns); break; case IPPROTO_UDP: case IPPROTO_UDPLITE: case IPPROTO_DCCP: case IPPROTO_SCTP: return 0; default: err_msg_die(EXIT_FAILMISC, "Programmed Failure"); } return 0; } /* * performs all socketopts specified, except * for some highly protocol dependant options (e.g. TCP_MD5SIG). */ void set_socketopts(int fd) { int i, ret; const void *optval; socklen_t optlen; /* loop over all selectable socket options */ for (i = 0; socket_options[i].sockopt_name; i++) { if (!socket_options[i].user_issue) continue; /* * this switch statement checks that the particular * socket option matches our selected socket-type */ switch (socket_options[i].level) { case SOL_SOCKET: break; /* works on every socket */ /* fall-through begins here ... */ case IPPROTO_TCP: if (opts.protocol == IPPROTO_TCP) break; case IPPROTO_UDP: if (opts.protocol == IPPROTO_UDP) break; case IPPROTO_UDPLITE: if (opts.protocol == IPPROTO_UDPLITE) break; case IPPROTO_SCTP: if (opts.protocol == IPPROTO_SCTP) break; case SOL_DCCP: if (opts.protocol == IPPROTO_DCCP) break; default: /* and exit if socketoption and sockettype did not match */ err_msg_die(EXIT_FAILMISC, "You selected an socket option which isn't " "compatible with this particular socket option"); } /* ... and do the dirty: set the socket options */ switch (socket_options[i].sockopt_type) { case SVT_BOOL: case SVT_INT: optlen = sizeof(socket_options[i].value); optval = &socket_options[i].value; break; case SVT_STR: optlen = strlen(socket_options[i].value_ptr) + 1; optval = socket_options[i].value_ptr; break; default: err_msg_die(EXIT_FAILNET, "Unknown sockopt_type %d\n", socket_options[i].sockopt_type); } ret = setsockopt(fd, socket_options[i].level, socket_options[i].option, optval, optlen); if (ret) err_sys("setsockopt option %d (name %s) failed", socket_options[i].sockopt_type, socket_options[i].sockopt_name); } } netsend-0.0~svnr250/xfuncs.c0000644000175000017500000000713210776173116015403 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #ifndef ULLONG_MAX # define ULLONG_MAX 18446744073709551615ULL #endif #include "global.h" #include "xfuncs.h" /* Simple malloc wrapper - prevent error checking */ void * xmalloc(size_t size) { void *ptr = malloc(size); if (!ptr) err_msg_die(EXIT_FAILMEM, "Out of mem: %s!\n", strerror(errno)); return ptr; } void xgetaddrinfo(const char *node, const char *service, struct addrinfo *hints, struct addrinfo **res) { int ret, ai_protocol, ai_socktype; struct addrinfo *a; bool dccp_sctp_fixup = false; if (hints) { /* getaddrinfo() does not support DCCP/SCTP yet, so fix things up manually 8-/ */ switch (hints->ai_protocol) { case IPPROTO_DCCP: case IPPROTO_UDPLITE: case IPPROTO_SCTP: ai_protocol = hints->ai_protocol; ai_socktype = hints->ai_socktype; hints->ai_protocol = IPPROTO_TCP; hints->ai_socktype = hints->ai_protocol == IPPROTO_DCCP ? SOCK_DGRAM : SOCK_STREAM; dccp_sctp_fixup = true; } } ret = getaddrinfo(node, service, hints, res); if (ret != 0) { err_msg_die(EXIT_FAILNET, "Call to getaddrinfo() failed: %s!\n", (ret == EAI_SYSTEM) ? strerror(errno) : gai_strerror(ret)); } /* check if we meddled with *hints behind getaddrinfos back and fix things up again */ if (!dccp_sctp_fixup) return; /* no we did not */ /* yes, need fixup */ for (a = *res; a != NULL ; a = a->ai_next) { a->ai_protocol = ai_protocol; a->ai_socktype = ai_socktype; } } void xsetsockopt(int s, int level, int optname, const void *optval, socklen_t optlen, const char *str) { int ret = setsockopt(s, level, optname, optval, optlen); if (ret) err_sys_die(EXIT_FAILNET, "Can't set socketoption %s", str); } int xsnprintf(char *str, size_t size, const char *format, ...) { va_list ap; int len; va_start(ap, format); len = vsnprintf(str, size, format, ap); va_end(ap); if (len < 0 || ((size_t)len) >= size) err_msg_die(EXIT_FAILINT, "buflen %u not sufficient (ret %d)", size, len); return len; } char *xstrdup(const char *src) { size_t len; char *duplicate; if (src == NULL) return NULL; len = strlen(src) + 1; if (!len) /* integer overflow */ err_msg_die(EXIT_FAILINT, "xstrdup: string execeeds size_t range"); duplicate = xmalloc(len); memcpy(duplicate, src, len); return duplicate; } void xfstat(int filedes, struct stat *buf, const char *s) { if (fstat(filedes, buf)) err_sys_die(EXIT_FAILMISC, "Can't fstat file %s", s); } void xpipe(int filedes[2]) { if (pipe(filedes)) err_sys_die(EXIT_FAILMISC, "Can't create pipe"); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/proto_udplite_trans.c0000644000175000017500000001271310776173116020176 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct sock_callbacks sock_callbacks; static void udplite_set_socketopts(int fd) { int i, ret; /* first - UDPLite specific socket options */ if (opts.udplite_checksum_coverage != LONG_MAX) { /* FIXME: add some check here - long to int */ i = opts.udplite_checksum_coverage; msg(GENTLE, "set UDPLite checksum coverage for %d bytes", i); ret = setsockopt(fd, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, &i, sizeof(int)); if (ret) err_sys("setsockopt option %d (name UDPLITE_SEND_CSCOV) failed", UDPLITE_SEND_CSCOV); } set_socketopts(fd); } /* Creates our server socket and initialize ** options */ static int init_udplite_trans(void) { bool use_multicast = false; int fd = -1, ret; struct addrinfo hosthints, *hostres, *addrtmp; struct protoent *protoent; memset(&hosthints, 0, sizeof(struct addrinfo)); /* probe our values */ hosthints.ai_family = opts.family; hosthints.ai_socktype = opts.socktype; hosthints.ai_protocol = IPPROTO_UDPLITE; hosthints.ai_flags = AI_ADDRCONFIG; xgetaddrinfo(opts.hostname, opts.port, &hosthints, &hostres); addrtmp = hostres; for (addrtmp = hostres; addrtmp != NULL ; addrtmp = addrtmp->ai_next) { if (opts.family != AF_UNSPEC && addrtmp->ai_family != opts.family) { /* user fixed family! */ continue; } fd = socket(addrtmp->ai_family, addrtmp->ai_socktype, addrtmp->ai_protocol); if (fd < 0) { err_sys("socket"); continue; } protoent = getprotobynumber(addrtmp->ai_protocol); if (protoent) msg(LOUDISH, "socket created - protocol %s(%d)", protoent->p_name, protoent->p_proto); /* mulicast checks */ switch (addrtmp->ai_family) { case AF_INET6: if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *) addrtmp->ai_addr)->sin6_addr)) { use_multicast = true; } break; case AF_INET: if (IN_MULTICAST(ntohl(((struct sockaddr_in *) addrtmp->ai_addr)->sin_addr.s_addr))) { use_multicast = true; } break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } if (use_multicast) { int hops_ttl = 30; int on = 1; switch (addrtmp->ai_family) { case AF_INET6: xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops_ttl, sizeof(hops_ttl), "IPV6_MULTICAST_HOPS"); xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(int), "IPV6_MULTICAST_LOOP"); break; case AF_INET: xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&hops_ttl, sizeof(hops_ttl), "IP_MULTICAST_TTL"); xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(int), "IP_MULTICAST_LOOP"); msg(STRESSFUL, "set IP_MULTICAST_LOOP option"); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } udplite_set_socketopts(fd); /* Connect to peer ** There are three advantages to call connect for all types ** of our socket protocols (especially udp) ** ** 1. We don't need to specify a destination address (only call write) ** 2. Performance advantages (kernel level) ** 3. Error detection (e.g. destination port unreachable at udp) */ ret = connect(fd, addrtmp->ai_addr, addrtmp->ai_addrlen); if (ret == -1) err_sys_die(EXIT_FAILNET, "Can't connect to %s", opts.hostname); msg(LOUDISH, "socket connected to %s via port %s", opts.hostname, opts.port); } if (fd < 0) err_msg_die(EXIT_FAILNET, "No suitable socket found"); freeaddrinfo(hostres); return fd; } /* ** o initialize server socket ** o fstat and open our sending-file ** o block in socket and wait for client ** o sendfile(2), write(2), ... ** o print diagnostic info */ void udplite_trans_mode(void) { int connected_fd, file_fd; msg(GENTLE, "transmit mode (file: %s - hostname: %s)", opts.infile, opts.hostname); /* check if the transmitted file is present and readable */ file_fd = open_input_file(); connected_fd = init_udplite_trans(); /* fetch sockopt before the first byte */ get_sock_opts(connected_fd, &net_stat); /* construct and send netsend header to peer */ meta_exchange_snd(connected_fd, file_fd); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); trans_start(file_fd, connected_fd); gettimeofday(&opts.endtime, NULL); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/debug.h0000644000175000017500000000057710776173116015176 0ustar martinmartin/* * $Id: debug.h 37 2006-06-16 21:40:42Z hgndgtl $ */ #ifndef NETSEND_DEBUG_HDR #define NETSEND_DEBUG_HDR #ifdef DEBUG # include # define DEBUGPRINTF( fmt, ... ) fprintf(stderr, "DEBUG: %s:%u - " fmt, __FILE__, __LINE__, __VA_ARGS__) #else # define DEBUGPRINTF( fmt, ... ) do { } while(0) # define NDEBUG #endif #include #endif /* NETSEND_DEBUG_HDR */ netsend-0.0~svnr250/global.h0000644000175000017500000002171510776173116015345 0ustar martinmartin/* ** $Id: global.h 249 2008-03-23 23:05:13Z fwest $ ** ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "config.h" #include "error.h" #ifdef HAVE_RDTSCLL # include # ifndef rdtscll #define rdtscll(val) \ __asm__ __volatile__("rdtsc" : "=A" (val)) # endif #endif /* HAVE_RDTSCLL */ #ifndef SOCK_DCCP # define SOCK_DCCP 6 #endif #ifndef SOL_DCCP # define SOL_DCCP 269 #endif #ifndef IPPROTO_DCCP # define IPPROTO_DCCP 33 #endif #ifndef DCCP_SOCKOPT_SERVICE # define DCCP_SOCKOPT_SERVICE 2 #endif #ifndef DCCP_SOCKOPT_CCID_RX_INFO # define DCCP_SOCKOPT_CCID_RX_INFO 128 #endif #ifndef DCCP_SOCKOPT_CCID_TX_INFO # define DCCP_SOCKOPT_CCID_TX_INFO 192 #endif #ifndef TCP_CONGESTION # define TCP_CONGESTION 13 #endif #ifndef SOL_SCTP # define SOL_SCTP 132 #endif #ifndef SCTP_MAXSEG # define SCTP_MAXSEG 13 #endif #ifndef IPPROTO_UDPLITE # define IPPROTO_UDPLITE 136 #endif #ifndef SCTP_DISABLE_FRAGMENTS # define SCTP_DISABLE_FRAGMENTS 8 #endif #ifndef UDPLITE_SEND_CSCOV # define UDPLITE_SEND_CSCOV 10 #endif #ifndef UDPLITE_RECV_CSCOV # define UDPLITE_RECV_CSCOV 11 #endif /* Our makros start here */ #define NIPQUAD(addr) ((unsigned char *)&addr)[0], \ ((unsigned char *)&addr)[1], \ ((unsigned char *)&addr)[2], \ ((unsigned char *)&addr)[3] /* Forces a function to be always inlined ** 'must inline' - so that they get inlined even ** if optimizing for size */ #undef __always_inline #if __GNUC_PREREQ (3,2) # define __always_inline __inline __attribute__ ((__always_inline__)) #else # define __always_inline __inline #endif #define min(x,y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; }) #define max(x,y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x > _y ? _x : _y; }) #if !defined likely && !defined unlikely # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) #endif /* determine the size of an array */ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* Netsend return codes */ #define EXIT_OK 0 #define EXIT_FAILMEM 1 #define EXIT_FAILOPT 2 #define EXIT_FAILMISC 3 #define EXIT_FAILNET 4 #define EXIT_FAILHEADER 6 #define EXIT_FAILINT 7 /* INTernal error */ #define SUCCESS 0 #define FAILURE -1 /* Verbose levels */ #define VL_QUITSCENT(x) (x) #define VL_GENTLE(x) (x >= 1) #define VL_LOUDISH(x) (x >= 2) #define VL_STRESSFUL(x) (x >= 3) #define PROGRAMNAME "netsend" #define VERSIONSTRING "002" /* Default values */ #define DEFAULT_PORT "6666" #define BACKLOG 1 #define DEFAULT_BUFSIZE (8 * 1024) enum sockopt_val_types { SVT_BOOL = 0, SVT_INT, SVT_STR }; struct socket_options { const char *sockopt_name; int level; int option; int sockopt_type; int user_issue; union { int value; const char *value_ptr; }; }; struct conf_map_t { int conf_code; const char *conf_string; }; /* supported posix_madvise / posix_fadvice hints */ enum memaccess_advice { MEMADV_NORMAL = 0, MEMADV_RANDOM, MEMADV_SEQUENTIAL, MEMADV_WILLNEED, MEMADV_DONTNEED, MEMADV_NOREUSE /* POSIX_FADV_NOREUSE */ }; #define MEMADV_MAX MEMADV_NOREUSE /* Supported io operations */ enum io_call { IO_RW,/* 0=default xmit method */ IO_SENDFILE, IO_MMAP, IO_SPLICE }; #define IO_MAX IO_SPLICE /* Centralize our statistic data */ struct use_stat { struct timeval time; struct rusage ru; #ifdef HAVE_RDTSCLL long long tsc; #endif }; struct sock_stat { /* tcp attributes */ uint16_t mss; int keep_alive; /* ip attributes */ }; #define __USE_MISC 1 #include /* for struct tcp_info */ struct net_stat { struct rtt_probe { double usec; double variance; } rtt_probe; struct tcp_info tcp_info; struct sock_stat sock_stat; unsigned int total_rx_calls; unsigned long long total_rx_bytes; unsigned int total_tx_calls; unsigned long long total_tx_bytes; struct use_stat use_stat_start; struct use_stat use_stat_end; }; /* this struct collect all information * sent by the peer (transmiter) and contain * information like data size, rtt information, * ... */ struct peer_header_info { unsigned int data_size; /* < the size of the incoming data */ }; /* Command-line options */ #define BIT_UNIT 1 #define BYTE_UNIT 2 #define STAT_PREFIX_SI 1 #define STAT_PREFIX_BINARY 2 enum workmode { MODE_NONE = 0, MODE_TRANSMIT, MODE_RECEIVE }; struct sock_callbacks { ssize_t (*cb_read)(int, void *, size_t); ssize_t (*cb_write)(int, const void *, size_t); int (*cb_listen)(int,int); int (*cb_accept)(int,struct sockaddr *addr,socklen_t *); }; /* command line arguments */ #define HDR_MSK_SOCKOPT (1 << 0) #define HDR_MSK_DIGEST (1 << 1) /* bitmask set for short_opts_mask */ #define SOPTS_VERSION (1 << 1) #define SOPTS_NUMERIC (1 << 2) #define SOPTS_IPV4 (1 << 3) #define SOPTS_IPV6 (1 << 4) enum ns_proto { NS_PROTO_UNSPEC = 0, NS_PROTO_TCP, NS_PROTO_UDP, NS_PROTO_UDPLITE, NS_PROTO_DCCP, NS_PROTO_TIPC, NS_PROTO_SCTP }; struct opts { unsigned long short_opts_mask; enum ns_proto ns_proto; int family; int protocol; int socktype; int reuse; int nodelay; int mem_advice; int change_mem_advise; long ext_hdr_mask; long threads; /* < number of threads to parallelize transmit stream */ int verbose; int statistics; int machine_parseable; int stat_unit; int stat_prefix; char *me; char *port; char *hostname; char *infile; char *outfile; enum workmode workmode; enum io_call io_call; /* if user set multiple_barrier then ** (buffer_size * multiple_barrier) ** is the maximum transfer amount */ int buffer_size; int multiple_barrier; struct timeval starttime; struct timeval endtime; int sched_user; /* this is true if user wan't to change scheduling */ int sched_policy; int priority; long nice; long int udplite_checksum_coverage; bool tcp_use_md5sig; const char *tcp_md5sig_peeraddr; /* receive mode: need ip addr of peer allowed to connect */ #define DEFAULT_RTT_FILTER 4 /* this stores option for the rtt probe commandline option '-R' */ struct rtt_probe_opt { int iterations; int data_size; int deviation_filter; int force_ms; } rtt_probe_opt; int perform_rtt_probe; }; /*** Interface ***/ enum where_send { TOUCH_BEFORE_OP = 0, TOUCH_AFTER_OP }; /* Gcc is smart enough to realize that argument 'where' is static ** at compile time and reorder the branch - this is tested! ** Through this optimization our rdtscll call is closer ** to send routine and therefor accurater. ** --HGN */ static inline void touch_use_stat(enum where_send where, struct use_stat *use_stat) { if (where == TOUCH_BEFORE_OP) { if (getrusage(RUSAGE_SELF, &use_stat->ru) < 0) err_sys("Failure in getrusage()"); if (gettimeofday(&use_stat->time, NULL) < 0) err_sys("Failure in gettimeofday()"); #ifdef HAVE_RDTSCLL rdtscll(use_stat->tsc); #endif } else { /* TOUCH_AFTER_OP */ #ifdef HAVE_RDTSCLL rdtscll(use_stat->tsc); #endif if (gettimeofday(&use_stat->time, NULL) < 0) err_sys("Failure in gettimeofday()"); if (getrusage(RUSAGE_SELF, &use_stat->ru) < 0) err_sys("Failure in getrusage()"); } return; }; /* file.c */ int open_input_file(void); int open_output_file(void); /* getopt.c */ void usage(void); int parse_opts(int, char **, struct opts *); /* net.c */ int get_sock_opts(int, struct net_stat *); int set_nodelay(int, int); int get_tcp_info(int, struct tcp_info *); void set_socketopts(int fd); /* ns_hdr.c */ int meta_exchange_snd(int, int); int meta_exchange_rcv(int, struct peer_header_info **); /* receive.c */ void receive_mode(void); /* transmit.c */ void transmit_mode(void); void tcp_trans_mode(void); void udp_trans_mode(void); void udplite_trans_mode(void); void tipc_trans_mode(void); void dccp_trans_mode(void); void sctp_trans_mode(void); /* proto_udplite_recv.c */ int init_receive_socket_udplite(struct opts *, int); /* trans_common.c */ void trans_start(int, int); /* vim:set ts=4 sw=4 sts=4 tw=78 ff=unix noet: */ netsend-0.0~svnr250/Makefile0000644000175000017500000000404110776173116015365 0ustar martinmartin# $Id: Makefile 229 2008-03-22 13:32:56Z hgndgtl $ ifeq ($(shell test \! -f Make.Rules || echo yes),yes) include Make.Rules endif TARGET = netsend OBJECTS = analyze.o error.o file.o \ getopt.o main.o net.o \ proto_tipc.o proto_udp_recv.o \ receive.o trans_common.o \ ns_hdr.o xfuncs.o proto_tcp_trans.o \ proto_udp_trans.o proto_udplite_trans.o \ proto_udplite_recv.o proto_dccp_trans.o \ proto_sctp_trans.o proto_tipc_trans.o POD = netsend.pod MAN = netsend.1 LIBS = -lm # Inline workaround: # max-inline-insns-single specified the maximum size # of a function (counted in internal gcc instructions). # Default: 300 CFLAGS += --param max-inline-insns-single=400 # XXX: add path configure DESTDIR=/usr BINDIR=/bin all: config.h $(TARGET) config.h: Make.Rules Make.Rules: configure @if [ ! -f Make.Rules ] ; then \ echo "No Make.Rules file present" ; \ echo "Hint: call ./configure script" ; \ echo "./configure --help for more information" ; \ exit 1 ; fi config.h: @bash configure $(TARGET): $(OBJECTS) $(CC) $(LIBS) $(CFLAGS) -o $(TARGET) $(OBJECTS) %.o: %.c analyze.h error.h global.h xfuncs.h Makefile $(CC) $(CFLAGS) -c $< -o $@ install: all install $(TARGET) $(DESTDIR)$(BINDIR) uninstall: rm $(DESTDIR)$(BINDIR)/$(TARGET) clean : @rm -rf $(TARGET) $(OBJECTS) core *~ distclean: clean @rm -f config.h Make.Rules $(MAN) man: $(POD) pod2man -d $(TARGET) -c $(TARGET) $(POD) > $(MAN) test: unit_test.sh $(TARGET) @./unit_test.sh DISTNAME=$(TARGET) release: @if [ ! -f Make.Rules ]; then echo $(MAKE) Make.Rules first ;exit 1 ;fi @if [ ! -L ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL) ]; then \ echo generating ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL) link ; \ ln -sf $(DISTNAME) ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL) ; \ echo to ../$(DISTNAME) . ; fi @diff ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL)/Make.Rules Make.Rules $(MAKE) distclean cd .. ; tar zvfc $(DISTNAME)-$(MAJOR_REL).$(MINOR_REL).tar.gz \ --exclude .svn --exclude '.#*' \ $(DISTNAME)-$(MAJOR_REL).$(MINOR_REL)/* netsend-0.0~svnr250/receive.c0000644000175000017500000002313010776173116015513 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "tcp_md5sig.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct socket_options socket_options[]; extern struct sock_callbacks sock_callbacks; /* This is our inner receive function. ** It reads from a connected socket descriptor ** and write to the file descriptor */ static ssize_t cs_read(int file_fd, int connected_fd, struct peer_header_info *phi) { int buflen; ssize_t rc; char *buf; /* user option or default(DEFAULT_BUFSIZE) */ buflen = (opts.buffer_size == 0) ? DEFAULT_BUFSIZE : opts.buffer_size; buf = xmalloc(buflen); touch_use_stat(TOUCH_BEFORE_OP, &net_stat.use_stat_start); /* main client loop */ while ((rc = read(connected_fd, buf, buflen)) > 0) { ssize_t ret; net_stat.total_rx_calls++; net_stat.total_rx_bytes += rc; do { ret = write(file_fd, buf, rc); } while (ret == -1 && errno == EINTR); if (ret != rc) { err_sys("write failed"); break; } if (net_stat.total_rx_bytes >= phi->data_size && phi->data_size != 0) { /* we are at the end of the * announced data amount. Protocols like * TCP indicate the end of the data stream. * Datagramm based protocols like UDP or * UDPLite not. So we break here if we received * the former announced amount of data */ break; } } touch_use_stat(TOUCH_AFTER_OP, &net_stat.use_stat_end); free(buf); return rc; } static void set_multicast4(int fd, struct ip_mreq *mreq) { int on = 1; xsetsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(int), "IP_MULTICAST_LOOP"); msg(STRESSFUL, "set IP_MULTICAST_LOOP option"); xsetsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq, sizeof(*mreq), "IP_ADD_MEMBERSHIP"); msg(GENTLE, "add membership to IPv4 multicast group"); } static void set_multicast6(int fd, struct ipv6_mreq *mreq6) { int on = 1; xsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(int), "IPV6_MULTICAST_LOOP"); msg(STRESSFUL, "set IPV6_MULTICAST_LOOP option"); xsetsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, mreq6, sizeof(*mreq6), "IPV6_JOIN_GROUP"); msg(GENTLE, "join IPv6 multicast group"); } static int socket_bind(struct addrinfo *a) { int ret, on = 1; int fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol); if (fd < 0) return -1; /* For multicast sockets it is maybe necessary to set * socketoption SO_REUSEADDR, cause multiple receiver on * the same host will bind to this local socket. * In all other cases: there is no penalty - hopefully! ;-) */ xsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on), "SO_REUSEADDR"); ret = bind(fd, a->ai_addr, a->ai_addrlen); if (ret) { err_msg("bind failed"); close(fd); return -1; } return fd; } #ifdef HAVE_AF_TIPC static int instigate_cs_tipc(void) { int fd = tipc_socket_bind(); if (fd < 0) err_sys_die(EXIT_FAILNET, "tipc_socket_bind"); if (sock_callbacks.cb_listen(fd, BACKLOG)) err_sys_die(EXIT_FAILNET, "listen(fd: %d, backlog: %d) failed", fd, BACKLOG); return fd; } #endif /* Creates our receive socket and initialize ** options ** ** XXX: at the moment we can't release ourself from the mulicast channel ** because struct ip_mreq and struct ipv6_mreq is function local. But at ** the moment this doesn't really matter because netsend deliever the file ** and exit - it isn't a uptime daemon. */ static int instigate_cs(void) { char *hostname = NULL; bool use_multicast = false; int fd = -1, ret; struct addrinfo hosthints, *hostres, *addrtmp; struct ip_mreq mreq; struct ipv6_mreq mreq6; #ifdef HAVE_AF_TIPC if (opts.family == AF_TIPC) return instigate_cs_tipc(); #endif memset(&hosthints, 0, sizeof(struct addrinfo)); hosthints.ai_family = opts.family; hosthints.ai_socktype = opts.socktype; hosthints.ai_protocol = opts.protocol; hosthints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* Check if the user want to bind to a ** multicast channel. We must implement this check ** here because if something fail we set hostname to ** NULL and initialize a standard udp socket */ if (opts.hostname && opts.protocol == IPPROTO_UDP) { hostname = opts.hostname; if (inet_pton(AF_INET, hostname, &mreq.imr_multiaddr) <= 0) { if (inet_pton(AF_INET6, hostname, &mreq6.ipv6mr_multiaddr) <= 0) err_msg_die(EXIT_FAILNET, "You didn't specify an valid multicast address (%s)!", hostname); /* IPv6 */ if (!IN6_IS_ADDR_MULTICAST(&mreq6.ipv6mr_multiaddr)) err_msg_die(EXIT_FAILNET, "You didn't specify an valid IPv6 multicast address (%s)!", hostname); hosthints.ai_family = AF_INET6; mreq6.ipv6mr_interface = 0; } else { /* IPv4 */ if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) err_msg_die(EXIT_FAILNET, "You didn't specify an valid IPv4 multicast address (%s)!", hostname); hosthints.ai_family = AF_INET; mreq.imr_interface.s_addr = INADDR_ANY; /* no look if our user specify strict ipv6 address (-6) but ** deliver us with a (valid) ipv4 multicast address */ if (opts.family == AF_INET6) err_msg_die(EXIT_FAILOPT, "You specify strict ipv6 support (-6) add a " "IPv4 multicast address!"); } use_multicast = true; hosthints.ai_flags = AI_NUMERICHOST | AI_ADDRCONFIG; } /* probe our values */ xgetaddrinfo(hostname, opts.port, &hosthints, &hostres); for (addrtmp = hostres; addrtmp != NULL ; addrtmp = addrtmp->ai_next) { if (opts.family != AF_UNSPEC && addrtmp->ai_family != opts.family) { /* user fixed family! */ continue; } fd = socket_bind(addrtmp); if (fd < 0) continue; if (use_multicast) { switch (addrtmp->ai_family) { case AF_INET6: set_multicast6(fd, &mreq6); break; case AF_INET: set_multicast4(fd, &mreq); break; default: err_msg_die(EXIT_FAILINT, "Programmed Failure"); } } break; } if (fd < 0) err_msg_die(EXIT_FAILNET, "Don't found a suitable address for binding, giving up " "(TIP: start program with strace(2) to find the problen\n"); ret = sock_callbacks.cb_listen(fd, BACKLOG); if (ret < 0) err_sys_die(EXIT_FAILNET, "listen(fd: %d, backlog: %d) failed", fd, BACKLOG); freeaddrinfo(hostres); return fd; } static void tcp_set_md5sig_option(int fd) { static const char key[] = "netsend"; struct tcp_md5sig sig = { .tcpm_keylen = sizeof(key) }; struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG }; struct addrinfo *res0; assert(opts.protocol == IPPROTO_TCP); memcpy(sig.tcpm_key, key, sizeof(key)); xgetaddrinfo(opts.tcp_md5sig_peeraddr, NULL, &hints, &res0); memcpy(&sig.tcpm_addr, res0->ai_addr, min(sizeof(sig.tcpm_addr), res0->ai_addrlen)); freeaddrinfo(res0); xsetsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &sig, sizeof(sig), "TCP_MD5SIG"); } /* *** Main Client Routine *** ** ** o initialize client socket ** o allocate receive buffer ** o receive routine ** o write file, print diagnostic info and exit */ void receive_mode(void) { int ret, file_fd, connected_fd = -1, server_fd; struct sockaddr_storage sa; struct peer_header_info *phi = NULL; socklen_t sa_len = sizeof(sa); msg(GENTLE, "receiver mode"); file_fd = open_output_file(); connected_fd = server_fd = instigate_cs(); #ifdef HAVE_AF_TIPC if (opts.family == AF_TIPC) { connected_fd = tipc_accept(server_fd, (struct sockaddr *) &sa, &sa_len); if (connected_fd == -1) err_sys_die(EXIT_FAILNET, "accept"); } #endif switch (opts.protocol) { case IPPROTO_TCP: case IPPROTO_DCCP: case IPPROTO_SCTP: { char peer[1024]; if (opts.tcp_use_md5sig) tcp_set_md5sig_option(server_fd); connected_fd = accept(server_fd, (struct sockaddr *) &sa, &sa_len); if (connected_fd == -1) { err_sys("accept error"); exit(EXIT_FAILNET); } ret = getnameinfo((struct sockaddr *)&sa, sa_len, peer, sizeof(peer), NULL, 0, 0); if (ret != 0) err_msg_die(EXIT_FAILNET, "getnameinfo error: %s", gai_strerror(ret)); msg(GENTLE, "accept from %s", peer); } break; case IPPROTO_UDPLITE: ret = init_receive_socket_udplite(&opts, connected_fd); if (ret != SUCCESS) { err_msg_die(EXIT_FAILNET, "failure in initial phase of receive socket creation"); } break; } /* read netsend header */ meta_exchange_rcv(connected_fd, &phi); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); msg(LOUDISH, "block in read"); cs_read(file_fd, connected_fd, phi); msg(LOUDISH, "done"); gettimeofday(&opts.endtime, NULL); /* FIXME: print statistic here */ /* We sync the file descriptor here because in a worst ** case this call block and sophisticate the time ** measurement. */ fsync(file_fd); free(phi); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/proto_tipc_trans.c0000644000175000017500000000450310776173116017465 0ustar martinmartin/* ** netsend - a high performance filetransfer and diagnostic tool ** http://netsend.berlios.de ** ** ** Copyright (C) 2006 - Hagen Paul Pfeifer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include "debug.h" #include "global.h" #include "xfuncs.h" #include "proto_tipc.h" extern struct opts opts; extern struct net_stat net_stat; extern struct conf_map_t io_call_map[]; extern struct sock_callbacks sock_callbacks; /* Creates our server socket and initialize ** options */ static int init_tipc_trans(void) { int fd; assert(opts.family == AF_TIPC); fd = tipc_socket_connect(); if (fd < 0) err_sys_die(EXIT_FAILNET, "tipc_socket_connect"); return fd; } /* ** o initialize server socket ** o fstat and open our sending-file ** o block in socket and wait for client ** o sendfile(2), write(2), ... ** o print diagnostic info */ void tipc_trans_mode(void) { int connected_fd, file_fd; msg(GENTLE, "transmit mode (file: %s - hostname: %s)", opts.infile, opts.hostname); /* check if the transmitted file is present and readable */ file_fd = open_input_file(); connected_fd = init_tipc_trans(); /* fetch sockopt before the first byte */ get_sock_opts(connected_fd, &net_stat); /* construct and send netsend header to peer */ meta_exchange_snd(connected_fd, file_fd); /* take the transmit start time for diff */ gettimeofday(&opts.starttime, NULL); trans_start(file_fd, connected_fd); gettimeofday(&opts.endtime, NULL); } /* vim:set ts=4 sw=4 tw=78 noet: */ netsend-0.0~svnr250/proto_tipc.h0000644000175000017500000000102510776173116016257 0ustar martinmartin/* * $Id: proto_tipc.h 167 2007-07-25 22:26:02Z fwest $ */ #include #include #include "config.h" #ifdef HAVE_AF_TIPC #include int tipc_socket_bind(void); ssize_t tipc_write(int fd, const void *buf, size_t count); int tipc_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int tipc_listen(int sockfd, int); int tipc_socket_connect(void); #else static inline int tipc_socket_bind(void) { return -1; } static inline int tipc_socket_connect(void) { return -1; } #endif