11 #include <sys/socket.h>
12 #include <sys/select.h>
13 #include <sys/ioctl.h>
14 #include <arpa/inet.h>
17 #include <asm/types.h>
18 #include <linux/net_tstamp.h>
19 #include <linux/errqueue.h>
21 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
44 struct options sockopt
;
45 struct tstamps expected
;
55 static struct sof_flag sof_flags
[] = {
56 #define SOF_FLAG(f) { f, #f }
57 SOF_FLAG(SOF_TIMESTAMPING_SOFTWARE
),
58 SOF_FLAG(SOF_TIMESTAMPING_RX_SOFTWARE
),
59 SOF_FLAG(SOF_TIMESTAMPING_RX_HARDWARE
),
62 static struct socket_type socket_types
[] = {
63 { "ip", SOCK_RAW
, IPPROTO_EGP
},
64 { "udp", SOCK_DGRAM
, IPPROTO_UDP
},
65 { "tcp", SOCK_STREAM
, IPPROTO_TCP
},
68 static struct test_case test_cases
[] = {
71 { .so_timestamp
= 1 },
75 { .so_timestampns
= 1 },
79 { .so_timestamp
= 1, .so_timestampns
= 1 },
83 { .so_timestamping
= SOF_TIMESTAMPING_RX_SOFTWARE
},
87 /* Loopback device does not support hw timestamps. */
88 { .so_timestamping
= SOF_TIMESTAMPING_RX_HARDWARE
},
92 { .so_timestamping
= SOF_TIMESTAMPING_SOFTWARE
},
96 { .so_timestamping
= SOF_TIMESTAMPING_RX_SOFTWARE
97 | SOF_TIMESTAMPING_RX_HARDWARE
},
101 { .so_timestamping
= SOF_TIMESTAMPING_SOFTWARE
102 | SOF_TIMESTAMPING_RX_SOFTWARE
},
106 { .so_timestamp
= 1, .so_timestamping
= SOF_TIMESTAMPING_SOFTWARE
107 | SOF_TIMESTAMPING_RX_SOFTWARE
},
108 { .tstamp
= true, .swtstamp
= true }
112 static struct option long_options
[] = {
113 { "list_tests", no_argument
, 0, 'l' },
114 { "test_num", required_argument
, 0, 'n' },
115 { "op_size", required_argument
, 0, 's' },
116 { "tcp", no_argument
, 0, 't' },
117 { "udp", no_argument
, 0, 'u' },
118 { "ip", no_argument
, 0, 'i' },
119 { "strict", no_argument
, 0, 'S' },
120 { "ipv4", no_argument
, 0, '4' },
121 { "ipv6", no_argument
, 0, '6' },
122 { NULL
, 0, NULL
, 0 },
125 static int next_port
= 19999;
126 static int op_size
= 10 * 1024;
128 void print_test_case(struct test_case
*t
)
132 printf("sockopts {");
133 if (t
->sockopt
.so_timestamp
)
134 printf(" SO_TIMESTAMP ");
135 if (t
->sockopt
.so_timestampns
)
136 printf(" SO_TIMESTAMPNS ");
137 if (t
->sockopt
.so_timestamping
) {
138 printf(" SO_TIMESTAMPING: {");
139 for (f
= 0; f
< ARRAY_SIZE(sof_flags
); f
++)
140 if (t
->sockopt
.so_timestamping
& sof_flags
[f
].mask
)
141 printf(" %s |", sof_flags
[f
].name
);
144 printf("} expected cmsgs: {");
145 if (t
->expected
.tstamp
)
146 printf(" SCM_TIMESTAMP ");
147 if (t
->expected
.tstampns
)
148 printf(" SCM_TIMESTAMPNS ");
149 if (t
->expected
.swtstamp
|| t
->expected
.hwtstamp
) {
150 printf(" SCM_TIMESTAMPING {");
151 if (t
->expected
.swtstamp
)
153 if (t
->expected
.swtstamp
&& t
->expected
.hwtstamp
)
155 if (t
->expected
.hwtstamp
)
162 void do_send(int src
)
165 char *buf
= malloc(op_size
);
167 memset(buf
, 'z', op_size
);
168 r
= write(src
, buf
, op_size
);
170 error(1, errno
, "Failed to sendmsg");
175 bool do_recv(int rcv
, int read_size
, struct tstamps expected
)
177 const int CMSG_SIZE
= 1024;
179 struct scm_timestamping
*ts
;
180 struct tstamps actual
= {};
181 char cmsg_buf
[CMSG_SIZE
];
182 struct iovec recv_iov
;
183 struct cmsghdr
*cmsg
;
189 memset(&hdr
, 0, sizeof(hdr
));
190 hdr
.msg_iov
= &recv_iov
;
192 recv_iov
.iov_base
= malloc(read_size
);
193 recv_iov
.iov_len
= read_size
;
195 hdr
.msg_control
= cmsg_buf
;
196 hdr
.msg_controllen
= sizeof(cmsg_buf
);
198 r
= recvmsg(rcv
, &hdr
, flags
);
200 error(1, errno
, "Failed to recvmsg");
202 error(1, 0, "Only received %d bytes of payload.", r
);
204 if (hdr
.msg_flags
& (MSG_TRUNC
| MSG_CTRUNC
))
205 error(1, 0, "Message was truncated.");
207 for (cmsg
= CMSG_FIRSTHDR(&hdr
); cmsg
!= NULL
;
208 cmsg
= CMSG_NXTHDR(&hdr
, cmsg
)) {
209 if (cmsg
->cmsg_level
!= SOL_SOCKET
)
210 error(1, 0, "Unexpected cmsg_level %d",
212 switch (cmsg
->cmsg_type
) {
214 actual
.tstamp
= true;
216 case SCM_TIMESTAMPNS
:
217 actual
.tstampns
= true;
219 case SCM_TIMESTAMPING
:
220 ts
= (struct scm_timestamping
*)CMSG_DATA(cmsg
);
221 actual
.swtstamp
= !!ts
->ts
[0].tv_sec
;
222 if (ts
->ts
[1].tv_sec
!= 0)
223 error(0, 0, "ts[1] should not be set.");
224 actual
.hwtstamp
= !!ts
->ts
[2].tv_sec
;
227 error(1, 0, "Unexpected cmsg_type %d", cmsg
->cmsg_type
);
231 #define VALIDATE(field) \
233 if (expected.field != actual.field) { \
234 if (expected.field) \
235 error(0, 0, "Expected " #field " to be set."); \
238 "Expected " #field " to not be set."); \
249 free(recv_iov
.iov_base
);
254 void config_so_flags(int rcv
, struct options o
)
258 if (setsockopt(rcv
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) < 0)
259 error(1, errno
, "Failed to enable SO_REUSEADDR");
261 if (o
.so_timestamp
&&
262 setsockopt(rcv
, SOL_SOCKET
, SO_TIMESTAMP
,
263 &o
.so_timestamp
, sizeof(o
.so_timestamp
)) < 0)
264 error(1, errno
, "Failed to enable SO_TIMESTAMP");
266 if (o
.so_timestampns
&&
267 setsockopt(rcv
, SOL_SOCKET
, SO_TIMESTAMPNS
,
268 &o
.so_timestampns
, sizeof(o
.so_timestampns
)) < 0)
269 error(1, errno
, "Failed to enable SO_TIMESTAMPNS");
271 if (o
.so_timestamping
&&
272 setsockopt(rcv
, SOL_SOCKET
, SO_TIMESTAMPING
,
273 &o
.so_timestamping
, sizeof(o
.so_timestamping
)) < 0)
274 error(1, errno
, "Failed to set SO_TIMESTAMPING");
277 bool run_test_case(struct socket_type
*s
, int test_num
, char ip_version
,
281 struct sockaddr_in6 addr6
;
282 struct sockaddr_in addr4
;
283 struct sockaddr addr_un
;
285 int read_size
= op_size
;
286 int src
, dst
, rcv
, port
;
290 port
= (s
->type
== SOCK_RAW
) ? 0 : next_port
++;
291 memset(&addr
, 0, sizeof(addr
));
292 if (ip_version
== '4') {
293 addr
.addr4
.sin_family
= AF_INET
;
294 addr
.addr4
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
295 addr
.addr4
.sin_port
= htons(port
);
296 addr_size
= sizeof(addr
.addr4
);
297 if (s
->type
== SOCK_RAW
)
298 read_size
+= 20; /* for IPv4 header */
300 addr
.addr6
.sin6_family
= AF_INET6
;
301 addr
.addr6
.sin6_addr
= in6addr_loopback
;
302 addr
.addr6
.sin6_port
= htons(port
);
303 addr_size
= sizeof(addr
.addr6
);
305 printf("Starting testcase %d over ipv%c...\n", test_num
, ip_version
);
306 src
= socket(addr
.addr_un
.sa_family
, s
->type
,
309 error(1, errno
, "Failed to open src socket");
311 dst
= socket(addr
.addr_un
.sa_family
, s
->type
,
314 error(1, errno
, "Failed to open dst socket");
316 if (bind(dst
, &addr
.addr_un
, addr_size
) < 0)
317 error(1, errno
, "Failed to bind to port %d", port
);
319 if (s
->type
== SOCK_STREAM
&& (listen(dst
, 1) < 0))
320 error(1, errno
, "Failed to listen");
322 if (connect(src
, &addr
.addr_un
, addr_size
) < 0)
323 error(1, errno
, "Failed to connect");
325 if (s
->type
== SOCK_STREAM
) {
326 rcv
= accept(dst
, NULL
, NULL
);
328 error(1, errno
, "Failed to accept");
334 config_so_flags(rcv
, test_cases
[test_num
].sockopt
);
335 usleep(20000); /* setsockopt for SO_TIMESTAMPING is asynchronous */
338 failed
= do_recv(rcv
, read_size
, test_cases
[test_num
].expected
);
344 printf("FAILURE in testcase %d over ipv%c ", test_num
,
346 print_test_case(&test_cases
[test_num
]);
347 if (!strict
&& test_cases
[test_num
].warn_on_fail
)
353 int main(int argc
, char **argv
)
355 bool all_protocols
= true;
356 bool all_tests
= true;
357 bool cfg_ipv4
= false;
358 bool cfg_ipv6
= false;
364 while ((opt
= getopt_long(argc
, argv
, "", long_options
,
365 &arg_index
)) != -1) {
368 for (t
= 0; t
< ARRAY_SIZE(test_cases
); t
++) {
370 print_test_case(&test_cases
[t
]);
375 if (t
>= ARRAY_SIZE(test_cases
))
376 error(1, 0, "Invalid test case: %d", t
);
378 test_cases
[t
].enabled
= true;
381 op_size
= atoi(optarg
);
384 all_protocols
= false;
385 socket_types
[2].enabled
= true;
388 all_protocols
= false;
389 socket_types
[1].enabled
= true;
392 all_protocols
= false;
393 socket_types
[0].enabled
= true;
405 error(1, 0, "Failed to parse parameters.");
409 for (s
= 0; s
< ARRAY_SIZE(socket_types
); s
++) {
410 if (!all_protocols
&& !socket_types
[s
].enabled
)
413 printf("Testing %s...\n", socket_types
[s
].friendly_name
);
414 for (t
= 0; t
< ARRAY_SIZE(test_cases
); t
++) {
415 if (!all_tests
&& !test_cases
[t
].enabled
)
417 if (cfg_ipv4
|| !cfg_ipv6
)
418 if (run_test_case(&socket_types
[s
], t
, '4',
421 if (cfg_ipv6
|| !cfg_ipv4
)
422 if (run_test_case(&socket_types
[s
], t
, '6',