Page MenuHomeFreeBSD

D9171.diff
No OneTemporary

D9171.diff

Index: head/lib/libc/sys/getsockopt.2
===================================================================
--- head/lib/libc/sys/getsockopt.2
+++ head/lib/libc/sys/getsockopt.2
@@ -187,6 +187,7 @@
.It Dv SO_LISTENQLEN Ta "get complete queue length of the socket (get only)"
.It Dv SO_LISTENINCQLEN Ta "get incomplete queue length of the socket (get only)"
.It Dv SO_USER_COOKIE Ta "set the 'so_user_cookie' value for the socket (uint32_t, set only)"
+.It Dv SO_TS_CLOCK Ta "set specific format of timestamp returned by SO_TIMESTAMP"
.El
.Pp
.Dv SO_DEBUG
@@ -435,7 +436,7 @@
.Dv SO_BINTIME .
The
.Vt cmsghdr
-fields have the following values for TIMESTAMP:
+fields have the following values for TIMESTAMP by default:
.Bd -literal
cmsg_len = CMSG_LEN(sizeof(struct timeval));
cmsg_level = SOL_SOCKET;
@@ -450,6 +451,24 @@
cmsg_type = SCM_BINTIME;
.Ed
.Pp
+Additional timestamp types are available by following
+.Dv SO_TIMESTAMP
+with
+.Dv SO_TS_CLOCK ,
+which requests specific timestamp format to be returned instead of
+.Dv SCM_TIMESTAMP when
+.Dv SO_TIMESTAMP is enabled.
+The following
+.Dv SO_TS_CLOCK
+values are recognized in
+.Fx :
+.Bl -column SO_TS_CLOCK -offset indent
+.It Dv SO_TS_REALTIME_MICRO Ta "realtime (SCM_TIMESTAMP, struct timeval), default"
+.It Dv SO_TS_BINTIME Ta "realtime (SCM_BINTIME, struct bintime)"
+.It Dv SO_TS_REALTIME Ta "realtime (SCM_REALTIME, struct timespec)"
+.It Dv SO_TS_MONOTONIC Ta "monotonic time (SCM_MONOTONIC, struct timespec)"
+.El
+.Pp
.Dv SO_ACCEPTCONN ,
.Dv SO_TYPE ,
.Dv SO_PROTOCOL
Index: head/sys/kern/uipc_socket.c
===================================================================
--- head/sys/kern/uipc_socket.c
+++ head/sys/kern/uipc_socket.c
@@ -2687,6 +2687,18 @@
#endif
break;
+ case SO_TS_CLOCK:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error)
+ goto bad;
+ if (optval < 0 || optval > SO_TS_CLOCK_MAX) {
+ error = EINVAL;
+ goto bad;
+ }
+ so->so_ts_clock = optval;
+ break;
+
default:
if (V_socket_hhh[HHOOK_SOCKET_OPT]->hhh_nhooks > 0)
error = hhook_run_socket(so, sopt,
@@ -2874,6 +2886,10 @@
optval = so->so_incqlen;
goto integer;
+ case SO_TS_CLOCK:
+ optval = so->so_ts_clock;
+ goto integer;
+
default:
if (V_socket_hhh[HHOOK_SOCKET_OPT]->hhh_nhooks > 0)
error = hhook_run_socket(so, sopt,
Index: head/sys/kern/uipc_usrreq.c
===================================================================
--- head/sys/kern/uipc_usrreq.c
+++ head/sys/kern/uipc_usrreq.c
@@ -1899,6 +1899,7 @@
struct filedescent *fde, **fdep, *fdev;
struct file *fp;
struct timeval *tv;
+ struct timespec *ts;
int i, *fdp;
void *data;
socklen_t clen = control->m_len, datalen;
@@ -2019,6 +2020,30 @@
bintime(bt);
break;
+ case SCM_REALTIME:
+ *controlp = sbcreatecontrol(NULL, sizeof(*ts),
+ SCM_REALTIME, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ ts = (struct timespec *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ nanotime(ts);
+ break;
+
+ case SCM_MONOTONIC:
+ *controlp = sbcreatecontrol(NULL, sizeof(*ts),
+ SCM_MONOTONIC, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ ts = (struct timespec *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ nanouptime(ts);
+ break;
+
default:
error = EINVAL;
goto out;
Index: head/sys/netinet/ip_input.c
===================================================================
--- head/sys/netinet/ip_input.c
+++ head/sys/netinet/ip_input.c
@@ -1157,30 +1157,48 @@
icmp_error(mcopy, type, code, dest.s_addr, mtu);
}
+#define CHECK_SO_CT(sp, ct) \
+ (((sp->so_options & SO_TIMESTAMP) && (sp->so_ts_clock == ct)) ? 1 : 0)
+
void
ip_savecontrol(struct inpcb *inp, struct mbuf **mp, struct ip *ip,
struct mbuf *m)
{
- if (inp->inp_socket->so_options & (SO_BINTIME | SO_TIMESTAMP)) {
+ if ((inp->inp_socket->so_options & SO_BINTIME) ||
+ CHECK_SO_CT(inp->inp_socket, SO_TS_BINTIME)) {
struct bintime bt;
bintime(&bt);
- if (inp->inp_socket->so_options & SO_BINTIME) {
- *mp = sbcreatecontrol((caddr_t)&bt, sizeof(bt),
- SCM_BINTIME, SOL_SOCKET);
- if (*mp)
- mp = &(*mp)->m_next;
- }
- if (inp->inp_socket->so_options & SO_TIMESTAMP) {
- struct timeval tv;
+ *mp = sbcreatecontrol((caddr_t)&bt, sizeof(bt),
+ SCM_BINTIME, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ if (CHECK_SO_CT(inp->inp_socket, SO_TS_REALTIME_MICRO)) {
+ struct timeval tv;
- bintime2timeval(&bt, &tv);
- *mp = sbcreatecontrol((caddr_t)&tv, sizeof(tv),
- SCM_TIMESTAMP, SOL_SOCKET);
- if (*mp)
- mp = &(*mp)->m_next;
- }
+ microtime(&tv);
+ *mp = sbcreatecontrol((caddr_t)&tv, sizeof(tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ } else if (CHECK_SO_CT(inp->inp_socket, SO_TS_REALTIME)) {
+ struct timespec ts;
+
+ nanotime(&ts);
+ *mp = sbcreatecontrol((caddr_t)&ts, sizeof(ts),
+ SCM_REALTIME, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ } else if (CHECK_SO_CT(inp->inp_socket, SO_TS_MONOTONIC)) {
+ struct timespec ts;
+
+ nanouptime(&ts);
+ *mp = sbcreatecontrol((caddr_t)&ts, sizeof(ts),
+ SCM_MONOTONIC, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
}
if (inp->inp_flags & INP_RECVDSTADDR) {
*mp = sbcreatecontrol((caddr_t)&ip->ip_dst,
Index: head/sys/netinet6/ip6_input.c
===================================================================
--- head/sys/netinet6/ip6_input.c
+++ head/sys/netinet6/ip6_input.c
@@ -1226,13 +1226,48 @@
#ifdef SO_TIMESTAMP
if ((inp->inp_socket->so_options & SO_TIMESTAMP) != 0) {
- struct timeval tv;
+ union {
+ struct timeval tv;
+ struct bintime bt;
+ struct timespec ts;
+ } t;
+
+ switch (inp->inp_socket->so_ts_clock) {
+ case SO_TS_REALTIME_MICRO:
+ microtime(&t.tv);
+ *mp = sbcreatecontrol((caddr_t) &t.tv, sizeof(t.tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ break;
- microtime(&tv);
- *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
- SCM_TIMESTAMP, SOL_SOCKET);
- if (*mp)
- mp = &(*mp)->m_next;
+ case SO_TS_BINTIME:
+ bintime(&t.bt);
+ *mp = sbcreatecontrol((caddr_t)&t.bt, sizeof(t.bt),
+ SCM_BINTIME, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ break;
+
+ case SO_TS_REALTIME:
+ nanotime(&t.ts);
+ *mp = sbcreatecontrol((caddr_t)&t.ts, sizeof(t.ts),
+ SCM_REALTIME, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ break;
+
+ case SO_TS_MONOTONIC:
+ nanouptime(&t.ts);
+ *mp = sbcreatecontrol((caddr_t)&t.ts, sizeof(t.ts),
+ SCM_MONOTONIC, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ break;
+
+ default:
+ panic("unknown (corrupted) so_ts_clock");
+ }
}
#endif
Index: head/sys/sys/socket.h
===================================================================
--- head/sys/sys/socket.h
+++ head/sys/sys/socket.h
@@ -158,6 +158,16 @@
#define SO_USER_COOKIE 0x1015 /* user cookie (dummynet etc.) */
#define SO_PROTOCOL 0x1016 /* get socket protocol (Linux name) */
#define SO_PROTOTYPE SO_PROTOCOL /* alias for SO_PROTOCOL (SunOS name) */
+#define SO_TS_CLOCK 0x1017 /* clock type used for SO_TIMESTAMP */
+#endif
+
+#if __BSD_VISIBLE
+#define SO_TS_REALTIME_MICRO 0 /* microsecond resolution, realtime */
+#define SO_TS_BINTIME 1 /* sub-nanosecond resolution, realtime */
+#define SO_TS_REALTIME 2 /* nanosecond resolution, realtime */
+#define SO_TS_MONOTONIC 3 /* nanosecond resolution, monotonic */
+#define SO_TS_DEFAULT SO_TS_REALTIME_MICRO
+#define SO_TS_CLOCK_MAX SO_TS_MONOTONIC
#endif
/*
@@ -534,6 +544,8 @@
#define SCM_TIMESTAMP 0x02 /* timestamp (struct timeval) */
#define SCM_CREDS 0x03 /* process creds (struct cmsgcred) */
#define SCM_BINTIME 0x04 /* timestamp (struct bintime) */
+#define SCM_REALTIME 0x05 /* timestamp (struct timespec) */
+#define SCM_MONOTONIC 0x06 /* timestamp (struct timespec) */
#endif
#if __BSD_VISIBLE
Index: head/sys/sys/socketvar.h
===================================================================
--- head/sys/sys/socketvar.h
+++ head/sys/sys/socketvar.h
@@ -127,6 +127,8 @@
int so_fibnum; /* routing domain for this socket */
uint32_t so_user_cookie;
+ int so_ts_clock; /* type of the clock used for timestamps */
+
void *so_pspare[2]; /* packet pacing / general use */
int so_ispare[2]; /* packet pacing / general use */
};
Index: head/tools/regression/sockets/udp_pingpong/Makefile
===================================================================
--- head/tools/regression/sockets/udp_pingpong/Makefile
+++ head/tools/regression/sockets/udp_pingpong/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= udp_pingpong
+MAN=
+WARNS?= 6
+
+.include <bsd.prog.mk>
Index: head/tools/regression/sockets/udp_pingpong/udp_pingpong.c
===================================================================
--- head/tools/regression/sockets/udp_pingpong/udp_pingpong.c
+++ head/tools/regression/sockets/udp_pingpong/udp_pingpong.c
@@ -0,0 +1,651 @@
+/*-
+ * Copyright (c) 2017 Maksym Sobolyev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The test that setups two processes A and B and make A sending
+ * B UDP packet(s) and B send it back. The time of sending is recorded
+ * in the payload and time of the arrival is either determined by
+ * reading clock after recv() completes or using kernel-supplied
+ * via recvmsg(). End-to-end time t(A->B->A) is then calculated
+ * and compared against time for both t(A->B) + t(B->A) to make
+ * sure it makes sense.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#define NPKTS 1000
+#define PKT_SIZE 128
+/* Timeout to receive pong on the side A, 100ms */
+#define SRECV_TIMEOUT (1 * 100)
+/*
+ * Timeout to receive ping on the side B. 4x as large as on the side A,
+ * so that in the case of packet loss the side A will have a chance to
+ * realize that and send few more before B bails out.
+ */
+#define RRECV_TIMEOUT (SRECV_TIMEOUT * 4)
+#define MIN_NRECV ((NPKTS * 99) / 100) /* 99% */
+
+//#define SIMULATE_PLOSS
+
+struct trip_ts {
+ struct timespec sent;
+ struct timespec recvd;
+};
+
+struct test_pkt {
+ int pnum;
+ struct trip_ts tss[2];
+ int lost;
+ unsigned char data[PKT_SIZE];
+};
+
+struct test_ctx {
+ const char *name;
+ int fds[2];
+ struct pollfd pfds[2];
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } sin[2];
+ struct test_pkt test_pkts[NPKTS];
+ int nsent;
+ int nrecvd;
+ clockid_t clock;
+ int use_recvmsg;
+ int ts_type;
+};
+
+struct rtt {
+ struct timespec a2b;
+ struct timespec b2a;
+ struct timespec e2e;
+ struct timespec a2b_b2a;
+};
+
+#define SEC(x) ((x)->tv_sec)
+#define NSEC(x) ((x)->tv_nsec)
+#define NSEC_MAX 1000000000L
+#define NSEC_IN_USEC 1000L
+
+#define timespecsub2(r, v, u) \
+ do { \
+ SEC(r) = SEC(v) - SEC(u); \
+ NSEC(r) = NSEC(v) - NSEC(u); \
+ if (NSEC(r) < 0 && (SEC(r) > 0 || NSEC(r) <= -NSEC_MAX)) { \
+ SEC(r)--; \
+ NSEC(r) += NSEC_MAX; \
+ } \
+ } while (0);
+
+#define timespecadd2(r, v, u) \
+ do { \
+ SEC(r) = SEC(v) + SEC(u); \
+ NSEC(r) = NSEC(v) + NSEC(u); \
+ if (NSEC(r) >= NSEC_MAX) { \
+ SEC(r)++; \
+ NSEC(r) -= NSEC_MAX; \
+ } \
+ } while (0);
+
+#define timespeccmp(t, c, u) \
+ ((SEC(t) == SEC(u)) ? \
+ (NSEC(t) c NSEC(u)) : \
+ (SEC(t) c SEC(u)))
+
+#define timeval2timespec(tv, ts) \
+ do { \
+ SEC(ts) = (tv)->tv_sec; \
+ NSEC(ts) = (tv)->tv_usec * NSEC_IN_USEC; \
+ } while (0);
+
+static const struct timespec zero_ts;
+/* 0.01s, should be more than enough for the loopback communication */
+static const struct timespec max_ts = {.tv_nsec = (NSEC_MAX / 100)};
+
+enum ts_types {TT_TIMESTAMP = -2, TT_BINTIME = -1,
+ TT_REALTIME_MICRO = SO_TS_REALTIME_MICRO, TT_TS_BINTIME = SO_TS_BINTIME,
+ TT_REALTIME = SO_TS_REALTIME, TT_MONOTONIC = SO_TS_MONOTONIC};
+
+static clockid_t
+get_clock_type(struct test_ctx *tcp)
+{
+ switch (tcp->ts_type) {
+ case TT_TIMESTAMP:
+ case TT_BINTIME:
+ case TT_REALTIME_MICRO:
+ case TT_TS_BINTIME:
+ case TT_REALTIME:
+ return (CLOCK_REALTIME);
+
+ case TT_MONOTONIC:
+ return (CLOCK_MONOTONIC);
+ }
+ abort();
+}
+
+static int
+get_scm_type(struct test_ctx *tcp)
+{
+ switch (tcp->ts_type) {
+ case TT_TIMESTAMP:
+ case TT_REALTIME_MICRO:
+ return (SCM_TIMESTAMP);
+
+ case TT_BINTIME:
+ case TT_TS_BINTIME:
+ return (SCM_BINTIME);
+
+ case TT_REALTIME:
+ return (SCM_REALTIME);
+
+ case TT_MONOTONIC:
+ return (SCM_MONOTONIC);
+ }
+ abort();
+}
+
+static size_t
+get_scm_size(struct test_ctx *tcp)
+{
+ switch (tcp->ts_type) {
+ case TT_TIMESTAMP:
+ case TT_REALTIME_MICRO:
+ return (sizeof(struct timeval));
+
+ case TT_BINTIME:
+ case TT_TS_BINTIME:
+ return (sizeof(struct bintime));
+
+ case TT_REALTIME:
+ case TT_MONOTONIC:
+ return (sizeof(struct timespec));
+ }
+ abort();
+}
+
+static void
+setup_ts_sockopt(struct test_ctx *tcp, int fd)
+{
+ int rval, oname1, oname2, sval1, sval2;
+
+ oname1 = SO_TIMESTAMP;
+ oname2 = -1;
+ sval2 = -1;
+
+ switch (tcp->ts_type) {
+ case TT_REALTIME_MICRO:
+ case TT_TS_BINTIME:
+ case TT_REALTIME:
+ case TT_MONOTONIC:
+ oname2 = SO_TS_CLOCK;
+ sval2 = tcp->ts_type;
+ break;
+
+ case TT_TIMESTAMP:
+ break;
+
+ case TT_BINTIME:
+ oname1 = SO_BINTIME;
+ break;
+
+ default:
+ abort();
+ }
+
+ sval1 = 1;
+ rval = setsockopt(fd, SOL_SOCKET, oname1, &sval1,
+ sizeof(sval1));
+ if (rval != 0) {
+ err(1, "%s: setup_udp: setsockopt(%d, %d, 1)", tcp->name,
+ fd, oname1);
+ }
+ if (oname2 == -1)
+ return;
+ rval = setsockopt(fd, SOL_SOCKET, oname2, &sval2,
+ sizeof(sval2));
+ if (rval != 0) {
+ err(1, "%s: setup_udp: setsockopt(%d, %d, %d)",
+ tcp->name, fd, oname2, sval2);
+ }
+}
+
+
+static void
+setup_udp(struct test_ctx *tcp)
+{
+ int i;
+ socklen_t sin_len, af_len;
+
+ af_len = sizeof(tcp->sin[0].v4);
+ for (i = 0; i < 2; i++) {
+ tcp->sin[i].v4.sin_len = af_len;
+ tcp->sin[i].v4.sin_family = AF_INET;
+ tcp->sin[i].v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ tcp->fds[i] = socket(PF_INET, SOCK_DGRAM, 0);
+ if (tcp->fds[i] < 0)
+ err(1, "%s: setup_udp: socket", tcp->name);
+ if (bind(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], af_len) < 0)
+ err(1, "%s: setup_udp: bind(%s, %d)", tcp->name,
+ inet_ntoa(tcp->sin[i].v4.sin_addr), 0);
+ sin_len = af_len;
+ if (getsockname(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], &sin_len) < 0)
+ err(1, "%s: setup_udp: getsockname(%d)", tcp->name, tcp->fds[i]);
+ if (tcp->use_recvmsg != 0) {
+ setup_ts_sockopt(tcp, tcp->fds[i]);
+ }
+
+ tcp->pfds[i].fd = tcp->fds[i];
+ tcp->pfds[i].events = POLLIN;
+ }
+
+ if (connect(tcp->fds[0], (struct sockaddr *)&tcp->sin[1], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa(tcp->sin[1].v4.sin_addr), ntohs(tcp->sin[1].v4.sin_port));
+ if (connect(tcp->fds[1], (struct sockaddr *)&tcp->sin[0], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa(tcp->sin[0].v4.sin_addr), ntohs(tcp->sin[0].v4.sin_port));
+}
+
+static char *
+inet_ntoa6(const void *sin6_addr)
+{
+ static char straddr[INET6_ADDRSTRLEN];
+
+ inet_ntop(AF_INET6, sin6_addr, straddr, sizeof(straddr));
+ return (straddr);
+}
+
+static void
+setup_udp6(struct test_ctx *tcp)
+{
+ int i;
+ socklen_t sin_len, af_len;
+
+ af_len = sizeof(tcp->sin[0].v6);
+ for (i = 0; i < 2; i++) {
+ tcp->sin[i].v6.sin6_len = af_len;
+ tcp->sin[i].v6.sin6_family = AF_INET6;
+ tcp->sin[i].v6.sin6_addr = in6addr_loopback;
+ tcp->fds[i] = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (tcp->fds[i] < 0)
+ err(1, "%s: setup_udp: socket", tcp->name);
+ if (bind(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], af_len) < 0)
+ err(1, "%s: setup_udp: bind(%s, %d)", tcp->name,
+ inet_ntoa6(&tcp->sin[i].v6.sin6_addr), 0);
+ sin_len = af_len;
+ if (getsockname(tcp->fds[i], (struct sockaddr *)&tcp->sin[i], &sin_len) < 0)
+ err(1, "%s: setup_udp: getsockname(%d)", tcp->name, tcp->fds[i]);
+ if (tcp->use_recvmsg != 0) {
+ setup_ts_sockopt(tcp, tcp->fds[i]);
+ }
+
+ tcp->pfds[i].fd = tcp->fds[i];
+ tcp->pfds[i].events = POLLIN;
+ }
+
+ if (connect(tcp->fds[0], (struct sockaddr *)&tcp->sin[1], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa6(&tcp->sin[1].v6.sin6_addr),
+ ntohs(tcp->sin[1].v6.sin6_port));
+ if (connect(tcp->fds[1], (struct sockaddr *)&tcp->sin[0], af_len) < 0)
+ err(1, "%s: setup_udp: connect(%s, %d)", tcp->name,
+ inet_ntoa6(&tcp->sin[0].v6.sin6_addr),
+ ntohs(tcp->sin[0].v6.sin6_port));
+}
+
+static void
+teardown_udp(struct test_ctx *tcp)
+{
+
+ close(tcp->fds[0]);
+ close(tcp->fds[1]);
+}
+
+static void
+send_pkt(struct test_ctx *tcp, int pnum, int fdidx, const char *face)
+{
+ ssize_t r;
+ size_t slen;
+
+ slen = sizeof(tcp->test_pkts[pnum]);
+ clock_gettime(get_clock_type(tcp), &tcp->test_pkts[pnum].tss[fdidx].sent);
+ r = send(tcp->fds[fdidx], &tcp->test_pkts[pnum], slen, 0);
+ if (r < 0) {
+ err(1, "%s: %s: send(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (r < (ssize_t)slen) {
+ errx(1, "%s: %s: send(%d): short send", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+ tcp->nsent += 1;
+}
+
+#define PDATA(tcp, i) ((tcp)->test_pkts[(i)].data)
+
+static void
+hdr_extract_ts(struct test_ctx *tcp, struct msghdr *mhp, struct timespec *tp)
+{
+ int scm_type;
+ size_t scm_size;
+ union {
+ struct timespec ts;
+ struct bintime bt;
+ struct timeval tv;
+ } tdata;
+ struct cmsghdr *cmsg;
+
+ scm_type = get_scm_type(tcp);
+ scm_size = get_scm_size(tcp);
+ for (cmsg = CMSG_FIRSTHDR(mhp); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(mhp, cmsg)) {
+ if ((cmsg->cmsg_level == SOL_SOCKET) &&
+ (cmsg->cmsg_type == scm_type)) {
+ memcpy(&tdata, CMSG_DATA(cmsg), scm_size);
+ break;
+ }
+ }
+ if (cmsg == NULL) {
+ abort();
+ }
+ switch (tcp->ts_type) {
+ case TT_REALTIME:
+ case TT_MONOTONIC:
+ *tp = tdata.ts;
+ break;
+
+ case TT_TIMESTAMP:
+ case TT_REALTIME_MICRO:
+ timeval2timespec(&tdata.tv, tp);
+ break;
+
+ case TT_BINTIME:
+ case TT_TS_BINTIME:
+ bintime2timespec(&tdata.bt, tp);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+static void
+recv_pkt_recvmsg(struct test_ctx *tcp, int fdidx, const char *face, void *buf,
+ size_t rlen, struct timespec *tp)
+{
+ /* We use a union to make sure hdr is aligned */
+ union {
+ struct cmsghdr hdr;
+ unsigned char buf[CMSG_SPACE(1024)];
+ } cmsgbuf;
+ struct msghdr msg;
+ struct iovec iov;
+ ssize_t rval;
+
+ memset(&msg, '\0', sizeof(msg));
+ iov.iov_base = buf;
+ iov.iov_len = rlen;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ rval = recvmsg(tcp->fds[fdidx], &msg, 0);
+ if (rval < 0) {
+ err(1, "%s: %s: recvmsg(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (rval < (ssize_t)rlen) {
+ errx(1, "%s: %s: recvmsg(%d): short recv", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+
+ hdr_extract_ts(tcp, &msg, tp);
+}
+
+static void
+recv_pkt_recv(struct test_ctx *tcp, int fdidx, const char *face, void *buf,
+ size_t rlen, struct timespec *tp)
+{
+ ssize_t rval;
+
+ rval = recv(tcp->fds[fdidx], buf, rlen, 0);
+ clock_gettime(get_clock_type(tcp), tp);
+ if (rval < 0) {
+ err(1, "%s: %s: recv(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (rval < (ssize_t)rlen) {
+ errx(1, "%s: %s: recv(%d): short recv", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+}
+
+static int
+recv_pkt(struct test_ctx *tcp, int fdidx, const char *face, int tout)
+{
+ int pr;
+ struct test_pkt recv_buf;
+ size_t rlen;
+
+ pr = poll(&tcp->pfds[fdidx], 1, tout);
+ if (pr < 0) {
+ err(1, "%s: %s: poll(%d)", tcp->name, face, tcp->fds[fdidx]);
+ }
+ if (pr == 0) {
+ return (-1);
+ }
+ if(tcp->pfds[fdidx].revents != POLLIN) {
+ errx(1, "%s: %s: poll(%d): unexpected result", tcp->name, face,
+ tcp->fds[fdidx]);
+ }
+ rlen = sizeof(recv_buf);
+ if (tcp->use_recvmsg == 0) {
+ recv_pkt_recv(tcp, fdidx, face, &recv_buf, rlen,
+ &recv_buf.tss[fdidx].recvd);
+ } else {
+ recv_pkt_recvmsg(tcp, fdidx, face, &recv_buf, rlen,
+ &recv_buf.tss[fdidx].recvd);
+ }
+ if (recv_buf.pnum < 0 || recv_buf.pnum >= NPKTS ||
+ memcmp(recv_buf.data, PDATA(tcp, recv_buf.pnum), PKT_SIZE) != 0) {
+ errx(1, "%s: %s: recv(%d): corrupted data, packet %d", tcp->name,
+ face, tcp->fds[fdidx], recv_buf.pnum);
+ }
+ tcp->nrecvd += 1;
+ memcpy(tcp->test_pkts[recv_buf.pnum].tss, recv_buf.tss,
+ sizeof(recv_buf.tss));
+ tcp->test_pkts[recv_buf.pnum].lost = 0;
+ return (recv_buf.pnum);
+}
+
+static void
+test_server(struct test_ctx *tcp)
+{
+ int i, j;
+
+ for (i = 0; i < NPKTS; i++) {
+ send_pkt(tcp, i, 0, __FUNCTION__);
+ j = recv_pkt(tcp, 0, __FUNCTION__, SRECV_TIMEOUT);
+ if (j < 0) {
+ warnx("packet %d is lost", i);
+ /* timeout */
+ continue;
+ }
+ }
+}
+
+static void
+test_client(struct test_ctx *tcp)
+{
+ int i, j;
+
+ for (i = 0; i < NPKTS; i++) {
+ j = recv_pkt(tcp, 1, __FUNCTION__, RRECV_TIMEOUT);
+ if (j < 0) {
+ /* timeout */
+ return;
+ }
+#if defined(SIMULATE_PLOSS)
+ if ((i % 99) == 0) {
+ warnx("dropping packet %d", i);
+ continue;
+ }
+#endif
+ send_pkt(tcp, j, 1, __FUNCTION__);
+ }
+}
+
+static void
+calc_rtt(struct test_pkt *tpp, struct rtt *rttp)
+{
+
+ timespecsub2(&rttp->a2b, &tpp->tss[1].recvd, &tpp->tss[0].sent);
+ timespecsub2(&rttp->b2a, &tpp->tss[0].recvd, &tpp->tss[1].sent);
+ timespecadd2(&rttp->a2b_b2a, &rttp->a2b, &rttp->b2a);
+ timespecsub2(&rttp->e2e, &tpp->tss[0].recvd, &tpp->tss[0].sent);
+}
+
+static void
+test_run(int ts_type, int use_ipv6, int use_recvmsg, const char *name)
+{
+ struct test_ctx test_ctx;
+ pid_t pid, cpid;
+ int i, j, status;
+
+ printf("Testing %s via %s: ", name, (use_ipv6 == 0) ? "IPv4" : "IPv6");
+ fflush(stdout);
+ bzero(&test_ctx, sizeof(test_ctx));
+ test_ctx.name = name;
+ test_ctx.use_recvmsg = use_recvmsg;
+ test_ctx.ts_type = ts_type;
+ if (use_ipv6 == 0) {
+ setup_udp(&test_ctx);
+ } else {
+ setup_udp6(&test_ctx);
+ }
+ for (i = 0; i < NPKTS; i++) {
+ test_ctx.test_pkts[i].pnum = i;
+ test_ctx.test_pkts[i].lost = 1;
+ for (j = 0; j < PKT_SIZE; j++) {
+ test_ctx.test_pkts[i].data[j] = (unsigned char)random();
+ }
+ }
+ cpid = fork();
+ if (cpid < 0) {
+ err(1, "%s: fork()", test_ctx.name);
+ }
+ if (cpid == 0) {
+ test_client(&test_ctx);
+ exit(0);
+ }
+ test_server(&test_ctx);
+ pid = waitpid(cpid, &status, 0);
+ if (pid == (pid_t)-1) {
+ err(1, "%s: waitpid(%d)", test_ctx.name, cpid);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ errx(1, "client exit status is %d",
+ WEXITSTATUS(status));
+ }
+ } else {
+ if (WIFSIGNALED(status))
+ errx(1, "abnormal termination of client, signal %d%s",
+ WTERMSIG(status), WCOREDUMP(status) ?
+ " (core file generated)" : "");
+ else
+ errx(1, "termination of client, unknown status");
+ }
+ if (test_ctx.nrecvd < MIN_NRECV) {
+ errx(1, "packet loss is too high %d received out of %d, min %d",
+ test_ctx.nrecvd, test_ctx.nsent, MIN_NRECV);
+ }
+ for (i = 0; i < NPKTS; i++) {
+ struct rtt rtt;
+ if (test_ctx.test_pkts[i].lost != 0) {
+ continue;
+ }
+ calc_rtt(&test_ctx.test_pkts[i], &rtt);
+ if (!timespeccmp(&rtt.e2e, >, &rtt.a2b_b2a))
+ errx(1, "end-to-end trip time is too small");
+ if (!timespeccmp(&rtt.e2e, <, &max_ts))
+ errx(1, "end-to-end trip time is too large");
+ if (!timespeccmp(&rtt.a2b, >, &zero_ts))
+ errx(1, "A2B trip time is not positive");
+ if (!timespeccmp(&rtt.b2a, >, &zero_ts))
+ errx(1, "B2A trip time is not positive");
+ }
+ teardown_udp(&test_ctx);
+}
+
+int
+main(void)
+{
+ int i;
+
+ srandomdev();
+
+ for (i = 0; i < 2; i++) {
+ test_run(0, i, 0, "send()/recv()");
+ printf("OK\n");
+ test_run(TT_TIMESTAMP, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TIMESTAMP, 1)");
+ printf("OK\n");
+ if (i == 0) {
+ test_run(TT_BINTIME, i, 1,
+ "send()/recvmsg(), setsockopt(SO_BINTIME, 1)");
+ printf("OK\n");
+ }
+ test_run(TT_REALTIME_MICRO, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_REALTIME_MICRO)");
+ printf("OK\n");
+ test_run(TT_TS_BINTIME, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_BINTIME)");
+ printf("OK\n");
+ test_run(TT_REALTIME, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_REALTIME)");
+ printf("OK\n");
+ test_run(TT_MONOTONIC, i, 1,
+ "send()/recvmsg(), setsockopt(SO_TS_CLOCK, SO_TS_MONOTONIC)");
+ printf("OK\n");
+ }
+ exit(0);
+}
Index: head/tools/regression/sockets/unix_cmsg/Makefile
===================================================================
--- head/tools/regression/sockets/unix_cmsg/Makefile
+++ head/tools/regression/sockets/unix_cmsg/Makefile
@@ -16,8 +16,14 @@
REXP_timeval= 's|%%TTYPE%%|timeval|g ; s|%%DTYPE%%|timeval|g ; \
s|%%SCM_TTYPE%%|SCM_TIMESTAMP|g ; \
s|%%MAJ_MEMB%%|tv_sec|g ; s|%%MIN_MEMB%%|tv_usec|g'
+REXP_timespec_real= 's|%%TTYPE%%|timespec_real|g ; s|%%DTYPE%%|timespec|g ; \
+ s|%%SCM_TTYPE%%|SCM_REALTIME|g ; \
+ s|%%MAJ_MEMB%%|tv_sec|g ; s|%%MIN_MEMB%%|tv_nsec|g'
+REXP_timespec_mono= 's|%%TTYPE%%|timespec_mono|g ; s|%%DTYPE%%|timespec|g ; \
+ s|%%SCM_TTYPE%%|SCM_MONOTONIC|g ; \
+ s|%%MAJ_MEMB%%|tv_sec|g ; s|%%MIN_MEMB%%|tv_nsec|g'
-.for ttype in bintime timeval
+.for ttype in bintime timeval timespec_real timespec_mono
AUTOSRCS+= t_${ttype}.h t_${ttype}.c
t_${ttype}.o: t_${ttype}.c t_${ttype}.h
Index: head/tools/regression/sockets/unix_cmsg/unix_cmsg.c
===================================================================
--- head/tools/regression/sockets/unix_cmsg/unix_cmsg.c
+++ head/tools/regression/sockets/unix_cmsg/unix_cmsg.c
@@ -52,6 +52,8 @@
#include "t_sockcred.h"
#include "t_cmsgcred_sockcred.h"
#include "t_cmsg_len.h"
+#include "t_timespec_real.h"
+#include "t_timespec_mono.h"
/*
* There are tables with tests descriptions and pointers to test
@@ -117,7 +119,19 @@
{
.func = t_peercred,
.desc = "Check LOCAL_PEERCRED socket option"
+ },
+#if defined(SCM_REALTIME)
+ {
+ .func = t_timespec_real,
+ .desc = "Sending, receiving realtime"
+ },
+#endif
+#if defined(SCM_MONOTONIC)
+ {
+ .func = t_timespec_mono,
+ .desc = "Sending, receiving monotonic time (uptime)"
}
+#endif
};
#define TEST_STREAM_TBL_SIZE \
@@ -152,6 +166,18 @@
{
.func = t_cmsg_len,
.desc = "Check cmsghdr.cmsg_len"
+ },
+#endif
+#if defined(SCM_REALTIME)
+ {
+ .func = t_timespec_real,
+ .desc = "Sending, receiving realtime"
+ },
+#endif
+#if defined(SCM_MONOTONIC)
+ {
+ .func = t_timespec_mono,
+ .desc = "Sending, receiving monotonic time (uptime)"
}
#endif
};

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 24, 8:43 PM (12 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32090914
Default Alt Text
D9171.diff (30 KB)

Event Timeline