]> Git Repo - J-linux.git/blob - tools/testing/selftests/net/rxtimestamp.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / net / rxtimestamp.c
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 #include "../kselftest.h"
22
23 struct options {
24         int so_timestamp;
25         int so_timestampns;
26         int so_timestamping;
27 };
28
29 struct tstamps {
30         bool tstamp;
31         bool tstampns;
32         bool swtstamp;
33         bool hwtstamp;
34 };
35
36 struct socket_type {
37         char *friendly_name;
38         int type;
39         int protocol;
40         bool enabled;
41 };
42
43 struct test_case {
44         struct options sockopt;
45         struct tstamps expected;
46         bool enabled;
47         bool warn_on_fail;
48 };
49
50 struct sof_flag {
51         int mask;
52         char *name;
53 };
54
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),
60         SOF_FLAG(SOF_TIMESTAMPING_OPT_RX_FILTER),
61         SOF_FLAG(SOF_TIMESTAMPING_RAW_HARDWARE),
62 };
63
64 static struct socket_type socket_types[] = {
65         { "ip",         SOCK_RAW,       IPPROTO_EGP },
66         { "udp",        SOCK_DGRAM,     IPPROTO_UDP },
67         { "tcp",        SOCK_STREAM,    IPPROTO_TCP },
68 };
69
70 static struct test_case test_cases[] = {
71         { {}, {} },
72         {
73                 { .so_timestamp = 1 },
74                 { .tstamp = true }
75         },
76         {
77                 { .so_timestampns = 1 },
78                 { .tstampns = true }
79         },
80         {
81                 { .so_timestamp = 1, .so_timestampns = 1 },
82                 { .tstampns = true }
83         },
84         {
85                 { .so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE },
86                 {}
87         },
88         {
89                 /* Loopback device does not support hw timestamps. */
90                 { .so_timestamping = SOF_TIMESTAMPING_RX_HARDWARE },
91                 {}
92         },
93         {
94                 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE },
95                 .warn_on_fail = true
96         },
97         {
98                 { .so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE
99                         | SOF_TIMESTAMPING_RX_HARDWARE },
100                 {}
101         },
102         {
103                 { .so_timestamping = SOF_TIMESTAMPING_RAW_HARDWARE
104                         | SOF_TIMESTAMPING_OPT_RX_FILTER },
105                 {}
106         },
107         {
108                 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE
109                         | SOF_TIMESTAMPING_OPT_RX_FILTER },
110                 {}
111         },
112         {
113                 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE
114                         | SOF_TIMESTAMPING_RX_SOFTWARE
115                         | SOF_TIMESTAMPING_OPT_RX_FILTER },
116                 { .swtstamp = true }
117         },
118         {
119                 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE
120                         | SOF_TIMESTAMPING_RX_SOFTWARE },
121                 { .swtstamp = true }
122         },
123         {
124                 { .so_timestamp = 1, .so_timestamping = SOF_TIMESTAMPING_SOFTWARE
125                         | SOF_TIMESTAMPING_RX_SOFTWARE },
126                 { .tstamp = true, .swtstamp = true }
127         },
128 };
129
130 static struct option long_options[] = {
131         { "list_tests", no_argument, 0, 'l' },
132         { "test_num", required_argument, 0, 'n' },
133         { "op_size", required_argument, 0, 's' },
134         { "tcp", no_argument, 0, 't' },
135         { "udp", no_argument, 0, 'u' },
136         { "ip", no_argument, 0, 'i' },
137         { "strict", no_argument, 0, 'S' },
138         { "ipv4", no_argument, 0, '4' },
139         { "ipv6", no_argument, 0, '6' },
140         { NULL, 0, NULL, 0 },
141 };
142
143 static int next_port = 19999;
144 static int op_size = 10 * 1024;
145
146 void print_test_case(struct test_case *t)
147 {
148         int f = 0;
149
150         printf("sockopts {");
151         if (t->sockopt.so_timestamp)
152                 printf(" SO_TIMESTAMP ");
153         if (t->sockopt.so_timestampns)
154                 printf(" SO_TIMESTAMPNS ");
155         if (t->sockopt.so_timestamping) {
156                 printf(" SO_TIMESTAMPING: {");
157                 for (f = 0; f < ARRAY_SIZE(sof_flags); f++)
158                         if (t->sockopt.so_timestamping & sof_flags[f].mask)
159                                 printf(" %s |", sof_flags[f].name);
160                 printf("}");
161         }
162         printf("} expected cmsgs: {");
163         if (t->expected.tstamp)
164                 printf(" SCM_TIMESTAMP ");
165         if (t->expected.tstampns)
166                 printf(" SCM_TIMESTAMPNS ");
167         if (t->expected.swtstamp || t->expected.hwtstamp) {
168                 printf(" SCM_TIMESTAMPING {");
169                 if (t->expected.swtstamp)
170                         printf("0");
171                 if (t->expected.swtstamp && t->expected.hwtstamp)
172                         printf(",");
173                 if (t->expected.hwtstamp)
174                         printf("2");
175                 printf("}");
176         }
177         printf("}\n");
178 }
179
180 void do_send(int src)
181 {
182         int r;
183         char *buf = malloc(op_size);
184
185         memset(buf, 'z', op_size);
186         r = write(src, buf, op_size);
187         if (r < 0)
188                 error(1, errno, "Failed to sendmsg");
189
190         free(buf);
191 }
192
193 bool do_recv(int rcv, int read_size, struct tstamps expected)
194 {
195         const int CMSG_SIZE = 1024;
196
197         struct scm_timestamping *ts;
198         struct tstamps actual = {};
199         char cmsg_buf[CMSG_SIZE];
200         struct iovec recv_iov;
201         struct cmsghdr *cmsg;
202         bool failed = false;
203         struct msghdr hdr;
204         int flags = 0;
205         int r;
206
207         memset(&hdr, 0, sizeof(hdr));
208         hdr.msg_iov = &recv_iov;
209         hdr.msg_iovlen = 1;
210         recv_iov.iov_base = malloc(read_size);
211         recv_iov.iov_len = read_size;
212
213         hdr.msg_control = cmsg_buf;
214         hdr.msg_controllen = sizeof(cmsg_buf);
215
216         r = recvmsg(rcv, &hdr, flags);
217         if (r < 0)
218                 error(1, errno, "Failed to recvmsg");
219         if (r != read_size)
220                 error(1, 0, "Only received %d bytes of payload.", r);
221
222         if (hdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
223                 error(1, 0, "Message was truncated.");
224
225         for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
226              cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
227                 if (cmsg->cmsg_level != SOL_SOCKET)
228                         error(1, 0, "Unexpected cmsg_level %d",
229                               cmsg->cmsg_level);
230                 switch (cmsg->cmsg_type) {
231                 case SCM_TIMESTAMP:
232                         actual.tstamp = true;
233                         break;
234                 case SCM_TIMESTAMPNS:
235                         actual.tstampns = true;
236                         break;
237                 case SCM_TIMESTAMPING:
238                         ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
239                         actual.swtstamp = !!ts->ts[0].tv_sec;
240                         if (ts->ts[1].tv_sec != 0)
241                                 error(0, 0, "ts[1] should not be set.");
242                         actual.hwtstamp = !!ts->ts[2].tv_sec;
243                         break;
244                 default:
245                         error(1, 0, "Unexpected cmsg_type %d", cmsg->cmsg_type);
246                 }
247         }
248
249 #define VALIDATE(field) \
250         do { \
251                 if (expected.field != actual.field) { \
252                         if (expected.field) \
253                                 error(0, 0, "Expected " #field " to be set."); \
254                         else \
255                                 error(0, 0, \
256                                       "Expected " #field " to not be set."); \
257                         failed = true; \
258                 } \
259         } while (0)
260
261         VALIDATE(tstamp);
262         VALIDATE(tstampns);
263         VALIDATE(swtstamp);
264         VALIDATE(hwtstamp);
265 #undef VALIDATE
266
267         free(recv_iov.iov_base);
268
269         return failed;
270 }
271
272 void config_so_flags(int rcv, struct options o)
273 {
274         int on = 1;
275
276         if (setsockopt(rcv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
277                 error(1, errno, "Failed to enable SO_REUSEADDR");
278
279         if (o.so_timestamp &&
280             setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMP,
281                        &o.so_timestamp, sizeof(o.so_timestamp)) < 0)
282                 error(1, errno, "Failed to enable SO_TIMESTAMP");
283
284         if (o.so_timestampns &&
285             setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPNS,
286                        &o.so_timestampns, sizeof(o.so_timestampns)) < 0)
287                 error(1, errno, "Failed to enable SO_TIMESTAMPNS");
288
289         if (o.so_timestamping &&
290             setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPING,
291                        &o.so_timestamping, sizeof(o.so_timestamping)) < 0)
292                 error(1, errno, "Failed to set SO_TIMESTAMPING");
293 }
294
295 bool run_test_case(struct socket_type *s, int test_num, char ip_version,
296                    bool strict)
297 {
298         union {
299                 struct sockaddr_in6 addr6;
300                 struct sockaddr_in addr4;
301                 struct sockaddr addr_un;
302         } addr;
303         int read_size = op_size;
304         int src, dst, rcv, port;
305         socklen_t addr_size;
306         bool failed = false;
307
308         port = (s->type == SOCK_RAW) ? 0 : next_port++;
309         memset(&addr, 0, sizeof(addr));
310         if (ip_version == '4') {
311                 addr.addr4.sin_family = AF_INET;
312                 addr.addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
313                 addr.addr4.sin_port = htons(port);
314                 addr_size = sizeof(addr.addr4);
315                 if (s->type == SOCK_RAW)
316                         read_size += 20;  /* for IPv4 header */
317         } else {
318                 addr.addr6.sin6_family = AF_INET6;
319                 addr.addr6.sin6_addr = in6addr_loopback;
320                 addr.addr6.sin6_port = htons(port);
321                 addr_size = sizeof(addr.addr6);
322         }
323         printf("Starting testcase %d over ipv%c...\n", test_num, ip_version);
324         src = socket(addr.addr_un.sa_family, s->type,
325                      s->protocol);
326         if (src < 0)
327                 error(1, errno, "Failed to open src socket");
328
329         dst = socket(addr.addr_un.sa_family, s->type,
330                      s->protocol);
331         if (dst < 0)
332                 error(1, errno, "Failed to open dst socket");
333
334         if (bind(dst, &addr.addr_un, addr_size) < 0)
335                 error(1, errno, "Failed to bind to port %d", port);
336
337         if (s->type == SOCK_STREAM && (listen(dst, 1) < 0))
338                 error(1, errno, "Failed to listen");
339
340         if (connect(src, &addr.addr_un, addr_size) < 0)
341                 error(1, errno, "Failed to connect");
342
343         if (s->type == SOCK_STREAM) {
344                 rcv = accept(dst, NULL, NULL);
345                 if (rcv < 0)
346                         error(1, errno, "Failed to accept");
347                 close(dst);
348         } else {
349                 rcv = dst;
350         }
351
352         config_so_flags(rcv, test_cases[test_num].sockopt);
353         usleep(20000); /* setsockopt for SO_TIMESTAMPING is asynchronous */
354         do_send(src);
355
356         failed = do_recv(rcv, read_size, test_cases[test_num].expected);
357
358         close(rcv);
359         close(src);
360
361         if (failed) {
362                 printf("FAILURE in testcase %d over ipv%c ", test_num,
363                        ip_version);
364                 print_test_case(&test_cases[test_num]);
365                 if (!strict && test_cases[test_num].warn_on_fail)
366                         failed = false;
367         }
368         return failed;
369 }
370
371 int main(int argc, char **argv)
372 {
373         bool all_protocols = true;
374         bool all_tests = true;
375         bool cfg_ipv4 = false;
376         bool cfg_ipv6 = false;
377         bool strict = false;
378         int arg_index = 0;
379         int failures = 0;
380         int s, t, opt;
381
382         while ((opt = getopt_long(argc, argv, "", long_options,
383                                   &arg_index)) != -1) {
384                 switch (opt) {
385                 case 'l':
386                         for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
387                                 printf("%d\t", t);
388                                 print_test_case(&test_cases[t]);
389                         }
390                         return 0;
391                 case 'n':
392                         t = atoi(optarg);
393                         if (t >= ARRAY_SIZE(test_cases))
394                                 error(1, 0, "Invalid test case: %d", t);
395                         all_tests = false;
396                         test_cases[t].enabled = true;
397                         break;
398                 case 's':
399                         op_size = atoi(optarg);
400                         break;
401                 case 't':
402                         all_protocols = false;
403                         socket_types[2].enabled = true;
404                         break;
405                 case 'u':
406                         all_protocols = false;
407                         socket_types[1].enabled = true;
408                         break;
409                 case 'i':
410                         all_protocols = false;
411                         socket_types[0].enabled = true;
412                         break;
413                 case 'S':
414                         strict = true;
415                         break;
416                 case '4':
417                         cfg_ipv4 = true;
418                         break;
419                 case '6':
420                         cfg_ipv6 = true;
421                         break;
422                 default:
423                         error(1, 0, "Failed to parse parameters.");
424                 }
425         }
426
427         for (s = 0; s < ARRAY_SIZE(socket_types); s++) {
428                 if (!all_protocols && !socket_types[s].enabled)
429                         continue;
430
431                 printf("Testing %s...\n", socket_types[s].friendly_name);
432                 for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
433                         if (!all_tests && !test_cases[t].enabled)
434                                 continue;
435                         if (cfg_ipv4 || !cfg_ipv6)
436                                 if (run_test_case(&socket_types[s], t, '4',
437                                                   strict))
438                                         failures++;
439                         if (cfg_ipv6 || !cfg_ipv4)
440                                 if (run_test_case(&socket_types[s], t, '6',
441                                                   strict))
442                                         failures++;
443                 }
444         }
445         if (!failures)
446                 printf("PASSED.\n");
447         return failures;
448 }
This page took 0.055798 seconds and 4 git commands to generate.