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