1312296Ssobomax/*- 2312296Ssobomax * Copyright (c) 2017 Maksym Sobolyev <sobomax@FreeBSD.org> 3312296Ssobomax * All rights reserved. 4312296Ssobomax * 5312296Ssobomax * Redistribution and use in source and binary forms, with or without 6312296Ssobomax * modification, are permitted provided that the following conditions 7312296Ssobomax * are met: 8312296Ssobomax * 1. Redistributions of source code must retain the above copyright 9312296Ssobomax * notice, this list of conditions and the following disclaimer. 10312296Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 11312296Ssobomax * notice, this list of conditions and the following disclaimer in the 12312296Ssobomax * documentation and/or other materials provided with the distribution. 13312296Ssobomax * 14312296Ssobomax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15312296Ssobomax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16312296Ssobomax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17312296Ssobomax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18312296Ssobomax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19312296Ssobomax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20312296Ssobomax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21312296Ssobomax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22312296Ssobomax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23312296Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24312296Ssobomax * SUCH DAMAGE. 25312296Ssobomax */ 26312296Ssobomax 27312296Ssobomax/* 28312296Ssobomax * The test that setups two processes A and B and make A sending 29312296Ssobomax * B UDP packet(s) and B send it back. The time of sending is recorded 30312296Ssobomax * in the payload and time of the arrival is either determined by 31312296Ssobomax * reading clock after recv() completes or using kernel-supplied 32312296Ssobomax * via recvmsg(). End-to-end time t(A->B->A) is then calculated 33312296Ssobomax * and compared against time for both t(A->B) + t(B->A) to make 34312296Ssobomax * sure it makes sense. 35312296Ssobomax */ 36312296Ssobomax 37312296Ssobomax#include <sys/cdefs.h> 38312296Ssobomax__FBSDID("$FreeBSD: stable/11/tools/regression/sockets/udp_pingpong/udp_pingpong.c 312296 2017-01-16 17:46:38Z sobomax $"); 39312296Ssobomax 40312296Ssobomax#include <sys/types.h> 41312296Ssobomax#include <sys/socket.h> 42312296Ssobomax#include <sys/wait.h> 43312296Ssobomax#include <sys/time.h> 44312296Ssobomax#include <netinet/in.h> 45312296Ssobomax#include <arpa/inet.h> 46312296Ssobomax#include <err.h> 47312296Ssobomax#include <poll.h> 48312296Ssobomax#include <stdio.h> 49312296Ssobomax#include <stdlib.h> 50312296Ssobomax#include <string.h> 51312296Ssobomax#include <strings.h> 52312296Ssobomax#include <time.h> 53312296Ssobomax#include <unistd.h> 54312296Ssobomax 55312296Ssobomax#define NPKTS 1000 56312296Ssobomax#define PKT_SIZE 128 57312296Ssobomax/* Timeout to receive pong on the side A, 100ms */ 58312296Ssobomax#define SRECV_TIMEOUT (1 * 100) 59312296Ssobomax/* 60312296Ssobomax * Timeout to receive ping on the side B. 4x as large as on the side A, 61312296Ssobomax * so that in the case of packet loss the side A will have a chance to 62312296Ssobomax * realize that and send few more before B bails out. 63312296Ssobomax */ 64312296Ssobomax#define RRECV_TIMEOUT (SRECV_TIMEOUT * 4) 65312296Ssobomax#define MIN_NRECV ((NPKTS * 99) / 100) /* 99% */ 66312296Ssobomax 67312296Ssobomax//#define SIMULATE_PLOSS 68312296Ssobomax 69312296Ssobomaxstruct trip_ts { 70312296Ssobomax struct timespec sent; 71312296Ssobomax struct timespec recvd; 72312296Ssobomax}; 73312296Ssobomax 74312296Ssobomaxstruct test_pkt { 75312296Ssobomax int pnum; 76312296Ssobomax struct trip_ts tss[2]; 77312296Ssobomax int lost; 78312296Ssobomax unsigned char data[PKT_SIZE]; 79312296Ssobomax}; 80312296Ssobomax 81312296Ssobomaxstruct test_ctx { 82312296Ssobomax const char *name; 83312296Ssobomax int fds[2]; 84312296Ssobomax struct pollfd pfds[2]; 85312296Ssobomax union { 86312296Ssobomax struct sockaddr_in v4; 87312296Ssobomax struct sockaddr_in6 v6; 88312296Ssobomax } sin[2]; 89312296Ssobomax struct test_pkt test_pkts[NPKTS]; 90312296Ssobomax int nsent; 91312296Ssobomax int nrecvd; 92312296Ssobomax clockid_t clock; 93312296Ssobomax int use_recvmsg; 94312296Ssobomax int ts_type; 95312296Ssobomax}; 96312296Ssobomax 97312296Ssobomaxstruct rtt { 98312296Ssobomax struct timespec a2b; 99312296Ssobomax struct timespec b2a; 100312296Ssobomax struct timespec e2e; 101312296Ssobomax struct timespec a2b_b2a; 102312296Ssobomax}; 103312296Ssobomax 104312296Ssobomax#define SEC(x) ((x)->tv_sec) 105312296Ssobomax#define NSEC(x) ((x)->tv_nsec) 106312296Ssobomax#define NSEC_MAX 1000000000L 107312296Ssobomax#define NSEC_IN_USEC 1000L 108312296Ssobomax 109312296Ssobomax#define timespecsub2(r, v, u) \ 110312296Ssobomax do { \ 111312296Ssobomax SEC(r) = SEC(v) - SEC(u); \ 112312296Ssobomax NSEC(r) = NSEC(v) - NSEC(u); \ 113312296Ssobomax if (NSEC(r) < 0 && (SEC(r) > 0 || NSEC(r) <= -NSEC_MAX)) { \ 114312296Ssobomax SEC(r)--; \ 115312296Ssobomax NSEC(r) += NSEC_MAX; \ 116312296Ssobomax } \ 117312296Ssobomax } while (0); 118312296Ssobomax 119312296Ssobomax#define timespecadd2(r, v, u) \ 120312296Ssobomax do { \ 121312296Ssobomax SEC(r) = SEC(v) + SEC(u); \ 122312296Ssobomax NSEC(r) = NSEC(v) + NSEC(u); \ 123312296Ssobomax if (NSEC(r) >= NSEC_MAX) { \ 124312296Ssobomax SEC(r)++; \ 125312296Ssobomax NSEC(r) -= NSEC_MAX; \ 126312296Ssobomax } \ 127312296Ssobomax } while (0); 128312296Ssobomax 129312296Ssobomax#define timespeccmp(t, c, u) \ 130312296Ssobomax ((SEC(t) == SEC(u)) ? \ 131312296Ssobomax (NSEC(t) c NSEC(u)) : \ 132312296Ssobomax (SEC(t) c SEC(u))) 133312296Ssobomax 134312296Ssobomax#define timeval2timespec(tv, ts) \ 135312296Ssobomax do { \ 136312296Ssobomax SEC(ts) = (tv)->tv_sec; \ 137312296Ssobomax NSEC(ts) = (tv)->tv_usec * NSEC_IN_USEC; \ 138312296Ssobomax } while (0); 139312296Ssobomax 140312296Ssobomaxstatic const struct timespec zero_ts; 141312296Ssobomax/* 0.01s, should be more than enough for the loopback communication */ 142312296Ssobomaxstatic const struct timespec max_ts = {.tv_nsec = (NSEC_MAX / 100)}; 143312296Ssobomax 144312296Ssobomaxenum ts_types {TT_TIMESTAMP = -2, TT_BINTIME = -1, 145312296Ssobomax TT_REALTIME_MICRO = SO_TS_REALTIME_MICRO, TT_TS_BINTIME = SO_TS_BINTIME, 146312296Ssobomax TT_REALTIME = SO_TS_REALTIME, TT_MONOTONIC = SO_TS_MONOTONIC}; 147312296Ssobomax 148312296Ssobomaxstatic clockid_t 149312296Ssobomaxget_clock_type(struct test_ctx *tcp) 150312296Ssobomax{ 151312296Ssobomax switch (tcp->ts_type) { 152312296Ssobomax case TT_TIMESTAMP: 153312296Ssobomax case TT_BINTIME: 154312296Ssobomax case TT_REALTIME_MICRO: 155312296Ssobomax case TT_TS_BINTIME: 156312296Ssobomax case TT_REALTIME: 157312296Ssobomax return (CLOCK_REALTIME); 158312296Ssobomax 159312296Ssobomax case TT_MONOTONIC: 160312296Ssobomax return (CLOCK_MONOTONIC); 161312296Ssobomax } 162312296Ssobomax abort(); 163312296Ssobomax} 164312296Ssobomax 165312296Ssobomaxstatic int 166312296Ssobomaxget_scm_type(struct test_ctx *tcp) 167312296Ssobomax{ 168312296Ssobomax switch (tcp->ts_type) { 169312296Ssobomax case TT_TIMESTAMP: 170312296Ssobomax case TT_REALTIME_MICRO: 171312296Ssobomax return (SCM_TIMESTAMP); 172312296Ssobomax 173312296Ssobomax case TT_BINTIME: 174312296Ssobomax case TT_TS_BINTIME: 175312296Ssobomax return (SCM_BINTIME); 176312296Ssobomax 177312296Ssobomax case TT_REALTIME: 178312296Ssobomax return (SCM_REALTIME); 179312296Ssobomax 180312296Ssobomax case TT_MONOTONIC: 181312296Ssobomax return (SCM_MONOTONIC); 182312296Ssobomax } 183312296Ssobomax abort(); 184312296Ssobomax} 185312296Ssobomax 186312296Ssobomaxstatic size_t 187312296Ssobomaxget_scm_size(struct test_ctx *tcp) 188312296Ssobomax{ 189312296Ssobomax switch (tcp->ts_type) { 190312296Ssobomax case TT_TIMESTAMP: 191312296Ssobomax case TT_REALTIME_MICRO: 192312296Ssobomax return (sizeof(struct timeval)); 193312296Ssobomax 194312296Ssobomax case TT_BINTIME: 195312296Ssobomax case TT_TS_BINTIME: 196312296Ssobomax return (sizeof(struct bintime)); 197312296Ssobomax 198312296Ssobomax case TT_REALTIME: 199312296Ssobomax case TT_MONOTONIC: 200312296Ssobomax return (sizeof(struct timespec)); 201312296Ssobomax } 202312296Ssobomax abort(); 203312296Ssobomax} 204312296Ssobomax 205312296Ssobomaxstatic void 206312296Ssobomaxsetup_ts_sockopt(struct test_ctx *tcp, int fd) 207312296Ssobomax{ 208312296Ssobomax int rval, oname1, oname2, sval1, sval2; 209312296Ssobomax 210312296Ssobomax oname1 = SO_TIMESTAMP; 211312296Ssobomax oname2 = -1; 212312296Ssobomax sval2 = -1; 213312296Ssobomax 214312296Ssobomax switch (tcp->ts_type) { 215312296Ssobomax case TT_REALTIME_MICRO: 216312296Ssobomax case TT_TS_BINTIME: 217312296Ssobomax case TT_REALTIME: 218312296Ssobomax case TT_MONOTONIC: 219312296Ssobomax oname2 = SO_TS_CLOCK; 220312296Ssobomax sval2 = tcp->ts_type; 221312296Ssobomax break; 222312296Ssobomax 223312296Ssobomax case TT_TIMESTAMP: 224312296Ssobomax break; 225312296Ssobomax 226312296Ssobomax case TT_BINTIME: 227312296Ssobomax oname1 = SO_BINTIME; 228312296Ssobomax break; 229312296Ssobomax 230312296Ssobomax default: 231312296Ssobomax abort(); 232312296Ssobomax } 233312296Ssobomax 234312296Ssobomax sval1 = 1; 235312296Ssobomax rval = setsockopt(fd, SOL_SOCKET, oname1, &sval1, 236312296Ssobomax sizeof(sval1)); 237312296Ssobomax if (rval != 0) { 238312296Ssobomax err(1, "%s: setup_udp: setsockopt(%d, %d, 1)", tcp->name, 239312296Ssobomax fd, oname1); 240312296Ssobomax } 241312296Ssobomax if (oname2 == -1) 242312296Ssobomax return; 243312296Ssobomax rval = setsockopt(fd, SOL_SOCKET, oname2, &sval2, 244312296Ssobomax sizeof(sval2)); 245312296Ssobomax if (rval != 0) { 246312296Ssobomax err(1, "%s: setup_udp: setsockopt(%d, %d, %d)", 247312296Ssobomax tcp->name, fd, oname2, sval2); 248312296Ssobomax } 249312296Ssobomax} 250312296Ssobomax 251312296Ssobomax 252312296Ssobomaxstatic void 253312296Ssobomaxsetup_udp(struct test_ctx *tcp) 254312296Ssobomax{ 255312296Ssobomax int i; 256312296Ssobomax socklen_t sin_len, af_len; 257312296Ssobomax 258312296Ssobomax af_len = sizeof(tcp->sin[0].v4); 259312296Ssobomax for (i = 0; i < 2; i++) { 260312296Ssobomax tcp->sin[i].v4.sin_len = af_len; 261312296Ssobomax tcp->sin[i].v4.sin_family = AF_INET; 262312296Ssobomax tcp->sin[i].v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 263312296Ssobomax tcp->fds[i] = socket(PF_INET, SOCK_DGRAM, 0); 264312296Ssobomax if (tcp->fds[i] < 0) 265312296Ssobomax err(1, "%s: setup_udp: socket", tcp->name); 266312296Ssobomax if (bind(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], af_len) < 0) 267312296Ssobomax err(1, "%s: setup_udp: bind(%s, %d)", tcp->name, 268312296Ssobomax inet_ntoa(tcp->sin[i].v4.sin_addr), 0); 269312296Ssobomax sin_len = af_len; 270312296Ssobomax if (getsockname(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], &sin_len) < 0) 271312296Ssobomax err(1, "%s: setup_udp: getsockname(%d)", tcp->name, tcp->fds[i]); 272312296Ssobomax if (tcp->use_recvmsg != 0) { 273312296Ssobomax setup_ts_sockopt(tcp, tcp->fds[i]); 274312296Ssobomax } 275312296Ssobomax 276312296Ssobomax tcp->pfds[i].fd = tcp->fds[i]; 277312296Ssobomax tcp->pfds[i].events = POLLIN; 278312296Ssobomax } 279312296Ssobomax 280312296Ssobomax if (connect(tcp->fds[0], (struct sockaddr *)&tcp->sin[1], af_len) < 0) 281312296Ssobomax err(1, "%s: setup_udp: connect(%s, %d)", tcp->name, 282312296Ssobomax inet_ntoa(tcp->sin[1].v4.sin_addr), ntohs(tcp->sin[1].v4.sin_port)); 283312296Ssobomax if (connect(tcp->fds[1], (struct sockaddr *)&tcp->sin[0], af_len) < 0) 284312296Ssobomax err(1, "%s: setup_udp: connect(%s, %d)", tcp->name, 285312296Ssobomax inet_ntoa(tcp->sin[0].v4.sin_addr), ntohs(tcp->sin[0].v4.sin_port)); 286312296Ssobomax} 287312296Ssobomax 288312296Ssobomaxstatic char * 289312296Ssobomaxinet_ntoa6(const void *sin6_addr) 290312296Ssobomax{ 291312296Ssobomax static char straddr[INET6_ADDRSTRLEN]; 292312296Ssobomax 293312296Ssobomax inet_ntop(AF_INET6, sin6_addr, straddr, sizeof(straddr)); 294312296Ssobomax return (straddr); 295312296Ssobomax} 296312296Ssobomax 297312296Ssobomaxstatic void 298312296Ssobomaxsetup_udp6(struct test_ctx *tcp) 299312296Ssobomax{ 300312296Ssobomax int i; 301312296Ssobomax socklen_t sin_len, af_len; 302312296Ssobomax 303312296Ssobomax af_len = sizeof(tcp->sin[0].v6); 304312296Ssobomax for (i = 0; i < 2; i++) { 305312296Ssobomax tcp->sin[i].v6.sin6_len = af_len; 306312296Ssobomax tcp->sin[i].v6.sin6_family = AF_INET6; 307312296Ssobomax tcp->sin[i].v6.sin6_addr = in6addr_loopback; 308312296Ssobomax tcp->fds[i] = socket(PF_INET6, SOCK_DGRAM, 0); 309312296Ssobomax if (tcp->fds[i] < 0) 310312296Ssobomax err(1, "%s: setup_udp: socket", tcp->name); 311312296Ssobomax if (bind(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], af_len) < 0) 312312296Ssobomax err(1, "%s: setup_udp: bind(%s, %d)", tcp->name, 313312296Ssobomax inet_ntoa6(&tcp->sin[i].v6.sin6_addr), 0); 314312296Ssobomax sin_len = af_len; 315312296Ssobomax if (getsockname(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], &sin_len) < 0) 316312296Ssobomax err(1, "%s: setup_udp: getsockname(%d)", tcp->name, tcp->fds[i]); 317312296Ssobomax if (tcp->use_recvmsg != 0) { 318312296Ssobomax setup_ts_sockopt(tcp, tcp->fds[i]); 319312296Ssobomax } 320312296Ssobomax 321312296Ssobomax tcp->pfds[i].fd = tcp->fds[i]; 322312296Ssobomax tcp->pfds[i].events = POLLIN; 323312296Ssobomax } 324312296Ssobomax 325312296Ssobomax if (connect(tcp->fds[0], (struct sockaddr *)&tcp->sin[1], af_len) < 0) 326312296Ssobomax err(1, "%s: setup_udp: connect(%s, %d)", tcp->name, 327312296Ssobomax inet_ntoa6(&tcp->sin[1].v6.sin6_addr), 328312296Ssobomax ntohs(tcp->sin[1].v6.sin6_port)); 329312296Ssobomax if (connect(tcp->fds[1], (struct sockaddr *)&tcp->sin[0], af_len) < 0) 330312296Ssobomax err(1, "%s: setup_udp: connect(%s, %d)", tcp->name, 331312296Ssobomax inet_ntoa6(&tcp->sin[0].v6.sin6_addr), 332312296Ssobomax ntohs(tcp->sin[0].v6.sin6_port)); 333312296Ssobomax} 334312296Ssobomax 335312296Ssobomaxstatic void 336312296Ssobomaxteardown_udp(struct test_ctx *tcp) 337312296Ssobomax{ 338312296Ssobomax 339312296Ssobomax close(tcp->fds[0]); 340312296Ssobomax close(tcp->fds[1]); 341312296Ssobomax} 342312296Ssobomax 343312296Ssobomaxstatic void 344312296Ssobomaxsend_pkt(struct test_ctx *tcp, int pnum, int fdidx, const char *face) 345312296Ssobomax{ 346312296Ssobomax ssize_t r; 347312296Ssobomax size_t slen; 348312296Ssobomax 349312296Ssobomax slen = sizeof(tcp->test_pkts[pnum]); 350312296Ssobomax clock_gettime(get_clock_type(tcp), &tcp->test_pkts[pnum].tss[fdidx].sent); 351312296Ssobomax r = send(tcp->fds[fdidx], &tcp->test_pkts[pnum], slen, 0); 352312296Ssobomax if (r < 0) { 353312296Ssobomax err(1, "%s: %s: send(%d)", tcp->name, face, tcp->fds[fdidx]); 354312296Ssobomax } 355312296Ssobomax if (r < (ssize_t)slen) { 356312296Ssobomax errx(1, "%s: %s: send(%d): short send", tcp->name, face, 357312296Ssobomax tcp->fds[fdidx]); 358312296Ssobomax } 359312296Ssobomax tcp->nsent += 1; 360312296Ssobomax} 361312296Ssobomax 362312296Ssobomax#define PDATA(tcp, i) ((tcp)->test_pkts[(i)].data) 363312296Ssobomax 364312296Ssobomaxstatic void 365312296Ssobomaxhdr_extract_ts(struct test_ctx *tcp, struct msghdr *mhp, struct timespec *tp) 366312296Ssobomax{ 367312296Ssobomax int scm_type; 368312296Ssobomax size_t scm_size; 369312296Ssobomax union { 370312296Ssobomax struct timespec ts; 371312296Ssobomax struct bintime bt; 372312296Ssobomax struct timeval tv; 373312296Ssobomax } tdata; 374312296Ssobomax struct cmsghdr *cmsg; 375312296Ssobomax 376312296Ssobomax scm_type = get_scm_type(tcp); 377312296Ssobomax scm_size = get_scm_size(tcp); 378312296Ssobomax for (cmsg = CMSG_FIRSTHDR(mhp); cmsg != NULL; 379312296Ssobomax cmsg = CMSG_NXTHDR(mhp, cmsg)) { 380312296Ssobomax if ((cmsg->cmsg_level == SOL_SOCKET) && 381312296Ssobomax (cmsg->cmsg_type == scm_type)) { 382312296Ssobomax memcpy(&tdata, CMSG_DATA(cmsg), scm_size); 383312296Ssobomax break; 384312296Ssobomax } 385312296Ssobomax } 386312296Ssobomax if (cmsg == NULL) { 387312296Ssobomax abort(); 388312296Ssobomax } 389312296Ssobomax switch (tcp->ts_type) { 390312296Ssobomax case TT_REALTIME: 391312296Ssobomax case TT_MONOTONIC: 392312296Ssobomax *tp = tdata.ts; 393312296Ssobomax break; 394312296Ssobomax 395312296Ssobomax case TT_TIMESTAMP: 396312296Ssobomax case TT_REALTIME_MICRO: 397312296Ssobomax timeval2timespec(&tdata.tv, tp); 398312296Ssobomax break; 399312296Ssobomax 400312296Ssobomax case TT_BINTIME: 401312296Ssobomax case TT_TS_BINTIME: 402312296Ssobomax bintime2timespec(&tdata.bt, tp); 403312296Ssobomax break; 404312296Ssobomax 405312296Ssobomax default: 406312296Ssobomax abort(); 407312296Ssobomax } 408312296Ssobomax} 409312296Ssobomax 410312296Ssobomaxstatic void 411312296Ssobomaxrecv_pkt_recvmsg(struct test_ctx *tcp, int fdidx, const char *face, void *buf, 412312296Ssobomax size_t rlen, struct timespec *tp) 413312296Ssobomax{ 414312296Ssobomax /* We use a union to make sure hdr is aligned */ 415312296Ssobomax union { 416312296Ssobomax struct cmsghdr hdr; 417312296Ssobomax unsigned char buf[CMSG_SPACE(1024)]; 418312296Ssobomax } cmsgbuf; 419312296Ssobomax struct msghdr msg; 420312296Ssobomax struct iovec iov; 421312296Ssobomax ssize_t rval; 422312296Ssobomax 423312296Ssobomax memset(&msg, '\0', sizeof(msg)); 424312296Ssobomax iov.iov_base = buf; 425312296Ssobomax iov.iov_len = rlen; 426312296Ssobomax msg.msg_iov = &iov; 427312296Ssobomax msg.msg_iovlen = 1; 428312296Ssobomax msg.msg_control = cmsgbuf.buf; 429312296Ssobomax msg.msg_controllen = sizeof(cmsgbuf.buf); 430312296Ssobomax 431312296Ssobomax rval = recvmsg(tcp->fds[fdidx], &msg, 0); 432312296Ssobomax if (rval < 0) { 433312296Ssobomax err(1, "%s: %s: recvmsg(%d)", tcp->name, face, tcp->fds[fdidx]); 434312296Ssobomax } 435312296Ssobomax if (rval < (ssize_t)rlen) { 436312296Ssobomax errx(1, "%s: %s: recvmsg(%d): short recv", tcp->name, face, 437312296Ssobomax tcp->fds[fdidx]); 438312296Ssobomax } 439312296Ssobomax 440312296Ssobomax hdr_extract_ts(tcp, &msg, tp); 441312296Ssobomax} 442312296Ssobomax 443312296Ssobomaxstatic void 444312296Ssobomaxrecv_pkt_recv(struct test_ctx *tcp, int fdidx, const char *face, void *buf, 445312296Ssobomax size_t rlen, struct timespec *tp) 446312296Ssobomax{ 447312296Ssobomax ssize_t rval; 448312296Ssobomax 449312296Ssobomax rval = recv(tcp->fds[fdidx], buf, rlen, 0); 450312296Ssobomax clock_gettime(get_clock_type(tcp), tp); 451312296Ssobomax if (rval < 0) { 452312296Ssobomax err(1, "%s: %s: recv(%d)", tcp->name, face, tcp->fds[fdidx]); 453312296Ssobomax } 454312296Ssobomax if (rval < (ssize_t)rlen) { 455312296Ssobomax errx(1, "%s: %s: recv(%d): short recv", tcp->name, face, 456312296Ssobomax tcp->fds[fdidx]); 457312296Ssobomax } 458312296Ssobomax} 459312296Ssobomax 460312296Ssobomaxstatic int 461312296Ssobomaxrecv_pkt(struct test_ctx *tcp, int fdidx, const char *face, int tout) 462312296Ssobomax{ 463312296Ssobomax int pr; 464312296Ssobomax struct test_pkt recv_buf; 465312296Ssobomax size_t rlen; 466312296Ssobomax 467312296Ssobomax pr = poll(&tcp->pfds[fdidx], 1, tout); 468312296Ssobomax if (pr < 0) { 469312296Ssobomax err(1, "%s: %s: poll(%d)", tcp->name, face, tcp->fds[fdidx]); 470312296Ssobomax } 471312296Ssobomax if (pr == 0) { 472312296Ssobomax return (-1); 473312296Ssobomax } 474312296Ssobomax if(tcp->pfds[fdidx].revents != POLLIN) { 475312296Ssobomax errx(1, "%s: %s: poll(%d): unexpected result", tcp->name, face, 476312296Ssobomax tcp->fds[fdidx]); 477312296Ssobomax } 478312296Ssobomax rlen = sizeof(recv_buf); 479312296Ssobomax if (tcp->use_recvmsg == 0) { 480312296Ssobomax recv_pkt_recv(tcp, fdidx, face, &recv_buf, rlen, 481312296Ssobomax &recv_buf.tss[fdidx].recvd); 482312296Ssobomax } else { 483312296Ssobomax recv_pkt_recvmsg(tcp, fdidx, face, &recv_buf, rlen, 484312296Ssobomax &recv_buf.tss[fdidx].recvd); 485312296Ssobomax } 486312296Ssobomax if (recv_buf.pnum < 0 || recv_buf.pnum >= NPKTS || 487312296Ssobomax memcmp(recv_buf.data, PDATA(tcp, recv_buf.pnum), PKT_SIZE) != 0) { 488312296Ssobomax errx(1, "%s: %s: recv(%d): corrupted data, packet %d", tcp->name, 489312296Ssobomax face, tcp->fds[fdidx], recv_buf.pnum); 490312296Ssobomax } 491312296Ssobomax tcp->nrecvd += 1; 492312296Ssobomax memcpy(tcp->test_pkts[recv_buf.pnum].tss, recv_buf.tss, 493312296Ssobomax sizeof(recv_buf.tss)); 494312296Ssobomax tcp->test_pkts[recv_buf.pnum].lost = 0; 495312296Ssobomax return (recv_buf.pnum); 496312296Ssobomax} 497312296Ssobomax 498312296Ssobomaxstatic void 499312296Ssobomaxtest_server(struct test_ctx *tcp) 500312296Ssobomax{ 501312296Ssobomax int i, j; 502312296Ssobomax 503312296Ssobomax for (i = 0; i < NPKTS; i++) { 504312296Ssobomax send_pkt(tcp, i, 0, __FUNCTION__); 505312296Ssobomax j = recv_pkt(tcp, 0, __FUNCTION__, SRECV_TIMEOUT); 506312296Ssobomax if (j < 0) { 507312296Ssobomax warnx("packet %d is lost", i); 508312296Ssobomax /* timeout */ 509312296Ssobomax continue; 510312296Ssobomax } 511312296Ssobomax } 512312296Ssobomax} 513312296Ssobomax 514312296Ssobomaxstatic void 515312296Ssobomaxtest_client(struct test_ctx *tcp) 516312296Ssobomax{ 517312296Ssobomax int i, j; 518312296Ssobomax 519312296Ssobomax for (i = 0; i < NPKTS; i++) { 520312296Ssobomax j = recv_pkt(tcp, 1, __FUNCTION__, RRECV_TIMEOUT); 521312296Ssobomax if (j < 0) { 522312296Ssobomax /* timeout */ 523312296Ssobomax return; 524312296Ssobomax } 525312296Ssobomax#if defined(SIMULATE_PLOSS) 526312296Ssobomax if ((i % 99) == 0) { 527312296Ssobomax warnx("dropping packet %d", i); 528312296Ssobomax continue; 529312296Ssobomax } 530312296Ssobomax#endif 531312296Ssobomax send_pkt(tcp, j, 1, __FUNCTION__); 532312296Ssobomax } 533312296Ssobomax} 534312296Ssobomax 535312296Ssobomaxstatic void 536312296Ssobomaxcalc_rtt(struct test_pkt *tpp, struct rtt *rttp) 537312296Ssobomax{ 538312296Ssobomax 539312296Ssobomax timespecsub2(&rttp->a2b, &tpp->tss[1].recvd, &tpp->tss[0].sent); 540312296Ssobomax timespecsub2(&rttp->b2a, &tpp->tss[0].recvd, &tpp->tss[1].sent); 541312296Ssobomax timespecadd2(&rttp->a2b_b2a, &rttp->a2b, &rttp->b2a); 542312296Ssobomax timespecsub2(&rttp->e2e, &tpp->tss[0].recvd, &tpp->tss[0].sent); 543312296Ssobomax} 544312296Ssobomax 545312296Ssobomaxstatic void 546312296Ssobomaxtest_run(int ts_type, int use_ipv6, int use_recvmsg, const char *name) 547312296Ssobomax{ 548312296Ssobomax struct test_ctx test_ctx; 549312296Ssobomax pid_t pid, cpid; 550312296Ssobomax int i, j, status; 551312296Ssobomax 552312296Ssobomax printf("Testing %s via %s: ", name, (use_ipv6 == 0) ? "IPv4" : "IPv6"); 553312296Ssobomax fflush(stdout); 554312296Ssobomax bzero(&test_ctx, sizeof(test_ctx)); 555312296Ssobomax test_ctx.name = name; 556312296Ssobomax test_ctx.use_recvmsg = use_recvmsg; 557312296Ssobomax test_ctx.ts_type = ts_type; 558312296Ssobomax if (use_ipv6 == 0) { 559312296Ssobomax setup_udp(&test_ctx); 560312296Ssobomax } else { 561312296Ssobomax setup_udp6(&test_ctx); 562312296Ssobomax } 563312296Ssobomax for (i = 0; i < NPKTS; i++) { 564312296Ssobomax test_ctx.test_pkts[i].pnum = i; 565312296Ssobomax test_ctx.test_pkts[i].lost = 1; 566312296Ssobomax for (j = 0; j < PKT_SIZE; j++) { 567312296Ssobomax test_ctx.test_pkts[i].data[j] = (unsigned char)random(); 568312296Ssobomax } 569312296Ssobomax } 570312296Ssobomax cpid = fork(); 571312296Ssobomax if (cpid < 0) { 572312296Ssobomax err(1, "%s: fork()", test_ctx.name); 573312296Ssobomax } 574312296Ssobomax if (cpid == 0) { 575312296Ssobomax test_client(&test_ctx); 576312296Ssobomax exit(0); 577312296Ssobomax } 578312296Ssobomax test_server(&test_ctx); 579312296Ssobomax pid = waitpid(cpid, &status, 0); 580312296Ssobomax if (pid == (pid_t)-1) { 581312296Ssobomax err(1, "%s: waitpid(%d)", test_ctx.name, cpid); 582312296Ssobomax } 583312296Ssobomax 584312296Ssobomax if (WIFEXITED(status)) { 585312296Ssobomax if (WEXITSTATUS(status) != EXIT_SUCCESS) { 586312296Ssobomax errx(1, "client exit status is %d", 587312296Ssobomax WEXITSTATUS(status)); 588312296Ssobomax } 589312296Ssobomax } else { 590312296Ssobomax if (WIFSIGNALED(status)) 591312296Ssobomax errx(1, "abnormal termination of client, signal %d%s", 592312296Ssobomax WTERMSIG(status), WCOREDUMP(status) ? 593312296Ssobomax " (core file generated)" : ""); 594312296Ssobomax else 595312296Ssobomax errx(1, "termination of client, unknown status"); 596312296Ssobomax } 597312296Ssobomax if (test_ctx.nrecvd < MIN_NRECV) { 598312296Ssobomax errx(1, "packet loss is too high %d received out of %d, min %d", 599312296Ssobomax test_ctx.nrecvd, test_ctx.nsent, MIN_NRECV); 600312296Ssobomax } 601312296Ssobomax for (i = 0; i < NPKTS; i++) { 602312296Ssobomax struct rtt rtt; 603312296Ssobomax if (test_ctx.test_pkts[i].lost != 0) { 604312296Ssobomax continue; 605312296Ssobomax } 606312296Ssobomax calc_rtt(&test_ctx.test_pkts[i], &rtt); 607312296Ssobomax if (!timespeccmp(&rtt.e2e, >, &rtt.a2b_b2a)) 608312296Ssobomax errx(1, "end-to-end trip time is too small"); 609312296Ssobomax if (!timespeccmp(&rtt.e2e, <, &max_ts)) 610312296Ssobomax errx(1, "end-to-end trip time is too large"); 611312296Ssobomax if (!timespeccmp(&rtt.a2b, >, &zero_ts)) 612312296Ssobomax errx(1, "A2B trip time is not positive"); 613312296Ssobomax if (!timespeccmp(&rtt.b2a, >, &zero_ts)) 614312296Ssobomax errx(1, "B2A trip time is not positive"); 615312296Ssobomax } 616312296Ssobomax teardown_udp(&test_ctx); 617312296Ssobomax} 618312296Ssobomax 619312296Ssobomaxint 620312296Ssobomaxmain(void) 621312296Ssobomax{ 622312296Ssobomax int i; 623312296Ssobomax 624312296Ssobomax srandomdev(); 625312296Ssobomax 626312296Ssobomax for (i = 0; i < 2; i++) { 627312296Ssobomax test_run(0, i, 0, "send()/recv()"); 628312296Ssobomax printf("OK\n"); 629312296Ssobomax test_run(TT_TIMESTAMP, i, 1, 630312296Ssobomax "send()/recvmsg(), setsockopt(SO_TIMESTAMP, 1)"); 631312296Ssobomax printf("OK\n"); 632312296Ssobomax if (i == 0) { 633312296Ssobomax test_run(TT_BINTIME, i, 1, 634312296Ssobomax "send()/recvmsg(), setsockopt(SO_BINTIME, 1)"); 635312296Ssobomax printf("OK\n"); 636312296Ssobomax } 637312296Ssobomax test_run(TT_REALTIME_MICRO, i, 1, 638312296Ssobomax "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_REALTIME_MICRO)"); 639312296Ssobomax printf("OK\n"); 640312296Ssobomax test_run(TT_TS_BINTIME, i, 1, 641312296Ssobomax "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_BINTIME)"); 642312296Ssobomax printf("OK\n"); 643312296Ssobomax test_run(TT_REALTIME, i, 1, 644312296Ssobomax "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_REALTIME)"); 645312296Ssobomax printf("OK\n"); 646312296Ssobomax test_run(TT_MONOTONIC, i, 1, 647312296Ssobomax "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_MONOTONIC)"); 648312296Ssobomax printf("OK\n"); 649312296Ssobomax } 650312296Ssobomax exit(0); 651312296Ssobomax} 652