1#include <errno.h>
2#include <error.h>
3#include <getopt.h>
4#include <stdbool.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include <sys/time.h>
11#include <sys/socket.h>
12#include <sys/select.h>
13#include <sys/ioctl.h>
14#include <arpa/inet.h>
15#include <net/if.h>
16
17#include <asm/types.h>
18#include <linux/net_tstamp.h>
19#include <linux/errqueue.h>
20
21#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
22
23struct options {
24 int so_timestamp;
25 int so_timestampns;
26 int so_timestamping;
27};
28
29struct tstamps {
30 bool tstamp;
31 bool tstampns;
32 bool swtstamp;
33 bool hwtstamp;
34};
35
36struct socket_type {
37 char *friendly_name;
38 int type;
39 int protocol;
40 bool enabled;
41};
42
43struct test_case {
44 struct options sockopt;
45 struct tstamps expected;
46 bool enabled;
47 bool warn_on_fail;
48};
49
50struct sof_flag {
51 int mask;
52 char *name;
53};
54
55static 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),
60};
61
62static struct socket_type socket_types[] = {
63 { "ip", SOCK_RAW, IPPROTO_EGP },
64 { "udp", SOCK_DGRAM, IPPROTO_UDP },
65 { "tcp", SOCK_STREAM, IPPROTO_TCP },
66};
67
68static struct test_case test_cases[] = {
69 { {}, {} },
70 {
71 { .so_timestamp = 1 },
72 { .tstamp = true }
73 },
74 {
75 { .so_timestampns = 1 },
76 { .tstampns = true }
77 },
78 {
79 { .so_timestamp = 1, .so_timestampns = 1 },
80 { .tstampns = true }
81 },
82 {
83 { .so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE },
84 {}
85 },
86 {
87
88 { .so_timestamping = SOF_TIMESTAMPING_RX_HARDWARE },
89 {}
90 },
91 {
92 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE },
93 .warn_on_fail = true
94 },
95 {
96 { .so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE
97 | SOF_TIMESTAMPING_RX_HARDWARE },
98 {}
99 },
100 {
101 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE
102 | SOF_TIMESTAMPING_RX_SOFTWARE },
103 { .swtstamp = true }
104 },
105 {
106 { .so_timestamp = 1, .so_timestamping = SOF_TIMESTAMPING_SOFTWARE
107 | SOF_TIMESTAMPING_RX_SOFTWARE },
108 { .tstamp = true, .swtstamp = true }
109 },
110};
111
112static 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 },
123};
124
125static int next_port = 19999;
126static int op_size = 10 * 1024;
127
128void print_test_case(struct test_case *t)
129{
130 int f = 0;
131
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);
142 printf("}");
143 }
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)
152 printf("0");
153 if (t->expected.swtstamp && t->expected.hwtstamp)
154 printf(",");
155 if (t->expected.hwtstamp)
156 printf("2");
157 printf("}");
158 }
159 printf("}\n");
160}
161
162void do_send(int src)
163{
164 int r;
165 char *buf = malloc(op_size);
166
167 memset(buf, 'z', op_size);
168 r = write(src, buf, op_size);
169 if (r < 0)
170 error(1, errno, "Failed to sendmsg");
171
172 free(buf);
173}
174
175bool do_recv(int rcv, int read_size, struct tstamps expected)
176{
177 const int CMSG_SIZE = 1024;
178
179 struct scm_timestamping *ts;
180 struct tstamps actual = {};
181 char cmsg_buf[CMSG_SIZE];
182 struct iovec recv_iov;
183 struct cmsghdr *cmsg;
184 bool failed = false;
185 struct msghdr hdr;
186 int flags = 0;
187 int r;
188
189 memset(&hdr, 0, sizeof(hdr));
190 hdr.msg_iov = &recv_iov;
191 hdr.msg_iovlen = 1;
192 recv_iov.iov_base = malloc(read_size);
193 recv_iov.iov_len = read_size;
194
195 hdr.msg_control = cmsg_buf;
196 hdr.msg_controllen = sizeof(cmsg_buf);
197
198 r = recvmsg(rcv, &hdr, flags);
199 if (r < 0)
200 error(1, errno, "Failed to recvmsg");
201 if (r != read_size)
202 error(1, 0, "Only received %d bytes of payload.", r);
203
204 if (hdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
205 error(1, 0, "Message was truncated.");
206
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",
211 cmsg->cmsg_level);
212 switch (cmsg->cmsg_type) {
213 case SCM_TIMESTAMP:
214 actual.tstamp = true;
215 break;
216 case SCM_TIMESTAMPNS:
217 actual.tstampns = true;
218 break;
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;
225 break;
226 default:
227 error(1, 0, "Unexpected cmsg_type %d", cmsg->cmsg_type);
228 }
229 }
230
231#define VALIDATE(field) \
232 do { \
233 if (expected.field != actual.field) { \
234 if (expected.field) \
235 error(0, 0, "Expected " #field " to be set."); \
236 else \
237 error(0, 0, \
238 "Expected " #field " to not be set."); \
239 failed = true; \
240 } \
241 } while (0)
242
243 VALIDATE(tstamp);
244 VALIDATE(tstampns);
245 VALIDATE(swtstamp);
246 VALIDATE(hwtstamp);
247#undef VALIDATE
248
249 free(recv_iov.iov_base);
250
251 return failed;
252}
253
254void config_so_flags(int rcv, struct options o)
255{
256 int on = 1;
257
258 if (setsockopt(rcv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
259 error(1, errno, "Failed to enable SO_REUSEADDR");
260
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");
265
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");
270
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");
275}
276
277bool run_test_case(struct socket_type *s, int test_num, char ip_version,
278 bool strict)
279{
280 union {
281 struct sockaddr_in6 addr6;
282 struct sockaddr_in addr4;
283 struct sockaddr addr_un;
284 } addr;
285 int read_size = op_size;
286 int src, dst, rcv, port;
287 socklen_t addr_size;
288 bool failed = false;
289
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;
299 } else {
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);
304 }
305 printf("Starting testcase %d over ipv%c...\n", test_num, ip_version);
306 src = socket(addr.addr_un.sa_family, s->type,
307 s->protocol);
308 if (src < 0)
309 error(1, errno, "Failed to open src socket");
310
311 dst = socket(addr.addr_un.sa_family, s->type,
312 s->protocol);
313 if (dst < 0)
314 error(1, errno, "Failed to open dst socket");
315
316 if (bind(dst, &addr.addr_un, addr_size) < 0)
317 error(1, errno, "Failed to bind to port %d", port);
318
319 if (s->type == SOCK_STREAM && (listen(dst, 1) < 0))
320 error(1, errno, "Failed to listen");
321
322 if (connect(src, &addr.addr_un, addr_size) < 0)
323 error(1, errno, "Failed to connect");
324
325 if (s->type == SOCK_STREAM) {
326 rcv = accept(dst, NULL, NULL);
327 if (rcv < 0)
328 error(1, errno, "Failed to accept");
329 close(dst);
330 } else {
331 rcv = dst;
332 }
333
334 config_so_flags(rcv, test_cases[test_num].sockopt);
335 usleep(20000);
336 do_send(src);
337
338 failed = do_recv(rcv, read_size, test_cases[test_num].expected);
339
340 close(rcv);
341 close(src);
342
343 if (failed) {
344 printf("FAILURE in testcase %d over ipv%c ", test_num,
345 ip_version);
346 print_test_case(&test_cases[test_num]);
347 if (!strict && test_cases[test_num].warn_on_fail)
348 failed = false;
349 }
350 return failed;
351}
352
353int main(int argc, char **argv)
354{
355 bool all_protocols = true;
356 bool all_tests = true;
357 bool cfg_ipv4 = false;
358 bool cfg_ipv6 = false;
359 bool strict = false;
360 int arg_index = 0;
361 int failures = 0;
362 int s, t, opt;
363
364 while ((opt = getopt_long(argc, argv, "", long_options,
365 &arg_index)) != -1) {
366 switch (opt) {
367 case 'l':
368 for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
369 printf("%d\t", t);
370 print_test_case(&test_cases[t]);
371 }
372 return 0;
373 case 'n':
374 t = atoi(optarg);
375 if (t >= ARRAY_SIZE(test_cases))
376 error(1, 0, "Invalid test case: %d", t);
377 all_tests = false;
378 test_cases[t].enabled = true;
379 break;
380 case 's':
381 op_size = atoi(optarg);
382 break;
383 case 't':
384 all_protocols = false;
385 socket_types[2].enabled = true;
386 break;
387 case 'u':
388 all_protocols = false;
389 socket_types[1].enabled = true;
390 break;
391 case 'i':
392 all_protocols = false;
393 socket_types[0].enabled = true;
394 break;
395 case 'S':
396 strict = true;
397 break;
398 case '4':
399 cfg_ipv4 = true;
400 break;
401 case '6':
402 cfg_ipv6 = true;
403 break;
404 default:
405 error(1, 0, "Failed to parse parameters.");
406 }
407 }
408
409 for (s = 0; s < ARRAY_SIZE(socket_types); s++) {
410 if (!all_protocols && !socket_types[s].enabled)
411 continue;
412
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)
416 continue;
417 if (cfg_ipv4 || !cfg_ipv6)
418 if (run_test_case(&socket_types[s], t, '4',
419 strict))
420 failures++;
421 if (cfg_ipv6 || !cfg_ipv4)
422 if (run_test_case(&socket_types[s], t, '6',
423 strict))
424 failures++;
425 }
426 }
427 if (!failures)
428 printf("PASSED.\n");
429 return failures;
430}
431