staticvoid die_usage(void)
{
fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-f offset] [-i file] [-I num] [-j] [-l] " "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-r num] [-R num] " "[-s MPTCP|TCP] [-S num] [-t num] [-T num] [-w sec] connect_address\n");
fprintf(stderr, "\t-6 use ipv6\n");
fprintf(stderr, "\t-c cmsg -- test cmsg type \n");
fprintf(stderr, "\t-f offset -- stop the I/O after receiving and sending the specified amount " "of bytes. If there are unread bytes in the receive queue, that will cause a MPTCP " "fastclose at close/shutdown. If offset is negative, expect the peer to close before " "all the local data as been sent, thus toleration errors on write and EPIPE signals\n");
fprintf(stderr, "\t-i file -- read the data to send from the given file instead of stdin");
fprintf(stderr, "\t-I num -- repeat the transfer 'num' times. In listen mode accepts num " "incoming connections, in client mode, disconnect and reconnect to the server\n");
fprintf(stderr, "\t-j -- add additional sleep at connection start and tear down " "-- for MPJ tests\n");
fprintf(stderr, "\t-l -- listens mode, accepts incoming connection\n");
fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
fprintf(stderr, "\t-M mark -- set socket packet mark\n");
fprintf(stderr, "\t-o option -- test sockopt );
fprintf(stderr, "\t-p num -- use port num\n");
fprintf(stderr, "\t-P [saveWithPeek|saveAfterPeek] -- save data with/after MSG_PEEK form tcp socket\n");
fprintf(stderr, "\t-r num -- enable slow mode, limiting each write to num bytes " "-- for remove addr tests\n");
fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n");
fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n");
fprintf(stderr, "\t-t num -- set poll timeout to num\n");
fprintf(stderr, "\t-T num -- set expected runtime to num ms\n");
fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n"); exit(1);
}
/* glibc starts to support MPTCP since v2.42. * For older versions, use IPPROTO_TCP to resolve, * and use TCP/MPTCP to create socket. * Link: https://sourceware.org/git/?p=glibc.git;a=commit;h=a8e9022e0f82
*/ if (err == EAI_SOCKTYPE) {
hints->ai_protocol = IPPROTO_TCP; goto again;
}
errstr = getxinfo_strerr(err);
fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
node ? node : "", service ? service : "", errstr); exit(1);
}
}
staticvoid set_rcvbuf(int fd, unsignedint size)
{ int err;
#define X(m) xerror("%s:%u: %s: failed for proto %d at line %u", __FILE__, __LINE__, (m), proto, line) staticvoid sock_test_tcpulp(int sock, int proto, unsignedint line)
{
socklen_t buflen = 8; char buf[8] = ""; int ret = getsockopt(sock, IPPROTO_TCP, TCP_ULP, buf, &buflen);
if (ret != 0)
X("getsockopt");
if (buflen > 0) { if (strcmp(buf, "mptcp") != 0)
xerror("unexpected ULP '%s' for proto %d at line %u", buf, proto, line);
ret = do_ulp_so(sock, "tls"); if (ret == 0)
X("setsockopt");
} elseif (proto == IPPROTO_MPTCP) {
ret = do_ulp_so(sock, "tls"); if (ret != -1)
X("setsockopt");
}
ret = do_ulp_so(sock, "mptcp"); if (ret != -1)
X("setsockopt");
if (ret <= 0) { if (ret == 0 && tcp_inq.expect_eof) return ret;
if (ret == 0 && cfg_cmsg_types.tcp_inq) if (last_hint != 1 && last_hint != 0)
xerror("EOF but last tcp_inq hint was %u\n", last_hint);
return ret;
}
if (tcp_inq.expect_eof)
xerror("expected EOF, last_hint %u, now %u\n",
last_hint, tcp_inq.last);
if (msg.msg_controllen && !cfg_cmsg_types.cmsg_enabled)
xerror("got %lu bytes of cmsg data, expected 0\n",
(unsignedlong)msg.msg_controllen);
if (msg.msg_controllen == 0 && cfg_cmsg_types.cmsg_enabled)
xerror("%s\n", "got no cmsg data");
if (msg.msg_controllen)
process_cmsg(&msg);
if (cfg_cmsg_types.tcp_inq) { if ((size_t)ret < len && last_hint > (unsignedint)ret) { if (ret + 1 != (int)last_hint) { int next = read(fd, msg_buf, sizeof(msg_buf));
xerror("read %u of %u, last_hint was %u tcp_inq hint now %u next_read returned %d/%m\n",
ret, (unsignedint)len, last_hint, tcp_inq.last, next);
} else {
tcp_inq.expect_eof = true;
}
}
}
return ret;
}
static ssize_t do_rnd_read(constint fd, char *buf, const size_t len)
{ int ret = 0; char tmp[16384];
size_t cap = rand();
cap &= 0xffff;
if (cap == 0)
cap = 1; elseif (cap > len)
cap = len;
if (cfg_peek == CFG_WITH_PEEK) {
ret = recv(fd, buf, cap, MSG_PEEK);
ret = (ret < 0) ? ret : read(fd, tmp, ret);
} elseif (cfg_peek == CFG_AFTER_PEEK) {
ret = recv(fd, buf, cap, MSG_PEEK);
ret = (ret < 0) ? ret : read(fd, buf, cap);
} elseif (cfg_cmsg_types.cmsg_enabled) {
ret = do_recvmsg_cmsg(fd, buf, cap);
} else {
ret = read(fd, buf, cap);
}
staticvoid shut_wr(int fd)
{ /* Close our write side, ev. give some time * for address notification and/or checking * the current status
*/ if (cfg_wait)
usleep(cfg_wait);
switch (poll(&fds, 1, poll_timeout)) { case -1: if (errno == EINTR) continue;
perror("poll"); return 1; case 0:
fprintf(stderr, "%s: poll timed out (events: " "POLLIN %u, POLLOUT %u)\n", __func__,
fds.events & POLLIN, fds.events & POLLOUT); return 2;
}
if (fds.revents & POLLIN) {
ssize_t rb = sizeof(rbuf);
/* limit the total amount of read data to the trunc value*/ if (cfg_truncate > 0) { if (rb + total_rlen > cfg_truncate)
rb = cfg_truncate - total_rlen;
len = read(peerfd, rbuf, rb);
} else {
len = do_rnd_read(peerfd, rbuf, sizeof(rbuf));
} if (len == 0) { /* no more data to receive: * peer has closed its write side
*/
fds.events &= ~POLLIN;
if ((fds.events & POLLOUT) == 0) {
*in_closed_after_out = true; /* and nothing more to send */ break;
}
/* Else, still have data to transmit */
} elseif (len < 0) { if (cfg_rcv_trunc) return 0;
perror("read"); return 3;
}
total_rlen += len;
do_write(outfd, rbuf, len);
}
if (fds.revents & POLLOUT) { if (winfo->len == 0) {
winfo->off = 0;
winfo->len = read(infd, winfo->buf, sizeof(winfo->buf));
}
if (winfo->len > 0) {
ssize_t bw;
/* limit the total amount of written data to the trunc value */ if (cfg_truncate > 0 && winfo->len + total_wlen > cfg_truncate)
winfo->len = cfg_truncate - total_wlen;
if (clock_gettime(CLOCK_MONOTONIC, &end) < 0)
xerror("can not fetch end time %d", errno);
delta_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_nsec - start.tv_nsec) / 1000000; if (delta_ms > cfg_time) {
xerror("transfer slower than expected! runtime %d ms, expected %d ms",
delta_ms, cfg_time);
}
/* show the runtime only if this end shutdown(wr) before receiving the EOF, * (that is, if this end got the longer runtime)
*/ if (in_closed_after_out)
fprintf(stderr, "%d", delta_ms);
}
/* * wait until the pending data is completely flushed and all * the sockets reached the closed status. * disconnect will bypass/ignore/drop any pending data.
*/ for (i = 0; ; i += msec_sleep) { /* closed socket are not listed by 'ss' */ if (system(cmd) != 0) break;
if (i > poll_timeout)
xerror("timeout while waiting for spool to complete");
usleep(msec_sleep * 1000);
}
if (cfg_rcvbuf)
set_rcvbuf(fd, cfg_rcvbuf); if (cfg_sndbuf)
set_sndbuf(fd, cfg_sndbuf); if (cfg_cmsg_types.cmsg_enabled)
apply_cmsg_types(fd, &cfg_cmsg_types);
if (cfg_input && !cfg_sockopt_types.mptfo) {
fd_in = open(cfg_input, O_RDONLY); if (fd_in < 0)
xerror("can't open %s:%d", cfg_input, errno);
}
ret = copyfd_io(fd_in, fd, 1, 0, &winfo); if (ret) goto out;
/* the socket could be unblocking at this point, we need the * connect to be blocking
*/
set_nonblock(fd, false); if (connect(fd, peer->ai_addr, peer->ai_addrlen))
xerror("can't reconnect: %d", errno); if (cfg_input)
close(fd_in);
memset(&winfo, 0, sizeof(winfo)); goto again;
} else {
close(fd);
}
out: if (cfg_input)
close(fd_in); return ret;
}
int parse_proto(constchar *proto)
{ if (!strcasecmp(proto, "MPTCP")) return IPPROTO_MPTCP; if (!strcasecmp(proto, "TCP")) return IPPROTO_TCP;
int parse_mode(constchar *mode)
{ if (!strcasecmp(mode, "poll")) return CFG_MODE_POLL; if (!strcasecmp(mode, "mmap")) return CFG_MODE_MMAP; if (!strcasecmp(mode, "sendfile")) return CFG_MODE_SENDFILE;
fprintf(stderr, "Unknown test mode: %s\n", mode);
fprintf(stderr, "Supported modes are:\n");
fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
die_usage();
/* silence compiler warning */ return 0;
}
int parse_peek(constchar *mode)
{ if (!strcasecmp(mode, "saveWithPeek")) return CFG_WITH_PEEK; if (!strcasecmp(mode, "saveAfterPeek")) return CFG_AFTER_PEEK;
fprintf(stderr, "Unknown: %s\n", mode);
fprintf(stderr, "Supported MSG_PEEK mode are:\n");
fprintf(stderr, "\t\t\"saveWithPeek\" - recv data with flags 'MSG_PEEK' and save the peek data into file\n");
fprintf(stderr, "\t\t\"saveAfterPeek\" - read and save data into file after recv with flags 'MSG_PEEK'\n");
if (listen_mode) { int fd = sock_listen_mptcp(cfg_host, cfg_port);
if (fd < 0) return 1;
if (cfg_rcvbuf)
set_rcvbuf(fd, cfg_rcvbuf); if (cfg_sndbuf)
set_sndbuf(fd, cfg_sndbuf); if (cfg_mark)
set_mark(fd, cfg_mark); if (cfg_cmsg_types.cmsg_enabled)
apply_cmsg_types(fd, &cfg_cmsg_types);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.