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 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 }; 61 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 }, 66 }; 67 68 static 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 /* Loopback device does not support hw timestamps. */ 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 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 { NULL, 0, NULL, 0 }, 121 }; 122 123 static int next_port = 19999; 124 static int op_size = 10 * 1024; 125 126 void print_test_case(struct test_case *t) 127 { 128 int f = 0; 129 130 printf("sockopts {"); 131 if (t->sockopt.so_timestamp) 132 printf(" SO_TIMESTAMP "); 133 if (t->sockopt.so_timestampns) 134 printf(" SO_TIMESTAMPNS "); 135 if (t->sockopt.so_timestamping) { 136 printf(" SO_TIMESTAMPING: {"); 137 for (f = 0; f < ARRAY_SIZE(sof_flags); f++) 138 if (t->sockopt.so_timestamping & sof_flags[f].mask) 139 printf(" %s |", sof_flags[f].name); 140 printf("}"); 141 } 142 printf("} expected cmsgs: {"); 143 if (t->expected.tstamp) 144 printf(" SCM_TIMESTAMP "); 145 if (t->expected.tstampns) 146 printf(" SCM_TIMESTAMPNS "); 147 if (t->expected.swtstamp || t->expected.hwtstamp) { 148 printf(" SCM_TIMESTAMPING {"); 149 if (t->expected.swtstamp) 150 printf("0"); 151 if (t->expected.swtstamp && t->expected.hwtstamp) 152 printf(","); 153 if (t->expected.hwtstamp) 154 printf("2"); 155 printf("}"); 156 } 157 printf("}\n"); 158 } 159 160 void do_send(int src) 161 { 162 int r; 163 char *buf = malloc(op_size); 164 165 memset(buf, 'z', op_size); 166 r = write(src, buf, op_size); 167 if (r < 0) 168 error(1, errno, "Failed to sendmsg"); 169 170 free(buf); 171 } 172 173 bool do_recv(int rcv, int read_size, struct tstamps expected) 174 { 175 const int CMSG_SIZE = 1024; 176 177 struct scm_timestamping *ts; 178 struct tstamps actual = {}; 179 char cmsg_buf[CMSG_SIZE]; 180 struct iovec recv_iov; 181 struct cmsghdr *cmsg; 182 bool failed = false; 183 struct msghdr hdr; 184 int flags = 0; 185 int r; 186 187 memset(&hdr, 0, sizeof(hdr)); 188 hdr.msg_iov = &recv_iov; 189 hdr.msg_iovlen = 1; 190 recv_iov.iov_base = malloc(read_size); 191 recv_iov.iov_len = read_size; 192 193 hdr.msg_control = cmsg_buf; 194 hdr.msg_controllen = sizeof(cmsg_buf); 195 196 r = recvmsg(rcv, &hdr, flags); 197 if (r < 0) 198 error(1, errno, "Failed to recvmsg"); 199 if (r != read_size) 200 error(1, 0, "Only received %d bytes of payload.", r); 201 202 if (hdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) 203 error(1, 0, "Message was truncated."); 204 205 for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL; 206 cmsg = CMSG_NXTHDR(&hdr, cmsg)) { 207 if (cmsg->cmsg_level != SOL_SOCKET) 208 error(1, 0, "Unexpected cmsg_level %d", 209 cmsg->cmsg_level); 210 switch (cmsg->cmsg_type) { 211 case SCM_TIMESTAMP: 212 actual.tstamp = true; 213 break; 214 case SCM_TIMESTAMPNS: 215 actual.tstampns = true; 216 break; 217 case SCM_TIMESTAMPING: 218 ts = (struct scm_timestamping *)CMSG_DATA(cmsg); 219 actual.swtstamp = !!ts->ts[0].tv_sec; 220 if (ts->ts[1].tv_sec != 0) 221 error(0, 0, "ts[1] should not be set."); 222 actual.hwtstamp = !!ts->ts[2].tv_sec; 223 break; 224 default: 225 error(1, 0, "Unexpected cmsg_type %d", cmsg->cmsg_type); 226 } 227 } 228 229 #define VALIDATE(field) \ 230 do { \ 231 if (expected.field != actual.field) { \ 232 if (expected.field) \ 233 error(0, 0, "Expected " #field " to be set."); \ 234 else \ 235 error(0, 0, \ 236 "Expected " #field " to not be set."); \ 237 failed = true; \ 238 } \ 239 } while (0) 240 241 VALIDATE(tstamp); 242 VALIDATE(tstampns); 243 VALIDATE(swtstamp); 244 VALIDATE(hwtstamp); 245 #undef VALIDATE 246 247 free(recv_iov.iov_base); 248 249 return failed; 250 } 251 252 void config_so_flags(int rcv, struct options o) 253 { 254 int on = 1; 255 256 if (setsockopt(rcv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 257 error(1, errno, "Failed to enable SO_REUSEADDR"); 258 259 if (o.so_timestamp && 260 setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMP, 261 &o.so_timestamp, sizeof(o.so_timestamp)) < 0) 262 error(1, errno, "Failed to enable SO_TIMESTAMP"); 263 264 if (o.so_timestampns && 265 setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPNS, 266 &o.so_timestampns, sizeof(o.so_timestampns)) < 0) 267 error(1, errno, "Failed to enable SO_TIMESTAMPNS"); 268 269 if (o.so_timestamping && 270 setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPING, 271 &o.so_timestamping, sizeof(o.so_timestamping)) < 0) 272 error(1, errno, "Failed to set SO_TIMESTAMPING"); 273 } 274 275 bool run_test_case(struct socket_type s, struct test_case t) 276 { 277 int port = (s.type == SOCK_RAW) ? 0 : next_port++; 278 int read_size = op_size; 279 struct sockaddr_in addr; 280 bool failed = false; 281 int src, dst, rcv; 282 283 src = socket(AF_INET, s.type, s.protocol); 284 if (src < 0) 285 error(1, errno, "Failed to open src socket"); 286 287 dst = socket(AF_INET, s.type, s.protocol); 288 if (dst < 0) 289 error(1, errno, "Failed to open dst socket"); 290 291 memset(&addr, 0, sizeof(addr)); 292 addr.sin_family = AF_INET; 293 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 294 addr.sin_port = htons(port); 295 296 if (bind(dst, (struct sockaddr *)&addr, sizeof(addr)) < 0) 297 error(1, errno, "Failed to bind to port %d", port); 298 299 if (s.type == SOCK_STREAM && (listen(dst, 1) < 0)) 300 error(1, errno, "Failed to listen"); 301 302 if (connect(src, (struct sockaddr *)&addr, sizeof(addr)) < 0) 303 error(1, errno, "Failed to connect"); 304 305 if (s.type == SOCK_STREAM) { 306 rcv = accept(dst, NULL, NULL); 307 if (rcv < 0) 308 error(1, errno, "Failed to accept"); 309 close(dst); 310 } else { 311 rcv = dst; 312 } 313 314 config_so_flags(rcv, t.sockopt); 315 usleep(20000); /* setsockopt for SO_TIMESTAMPING is asynchronous */ 316 do_send(src); 317 318 if (s.type == SOCK_RAW) 319 read_size += 20; /* for IP header */ 320 failed = do_recv(rcv, read_size, t.expected); 321 322 close(rcv); 323 close(src); 324 325 return failed; 326 } 327 328 int main(int argc, char **argv) 329 { 330 bool all_protocols = true; 331 bool all_tests = true; 332 bool strict = false; 333 int arg_index = 0; 334 int failures = 0; 335 int s, t; 336 char opt; 337 338 while ((opt = getopt_long(argc, argv, "", long_options, 339 &arg_index)) != -1) { 340 switch (opt) { 341 case 'l': 342 for (t = 0; t < ARRAY_SIZE(test_cases); t++) { 343 printf("%d\t", t); 344 print_test_case(&test_cases[t]); 345 } 346 return 0; 347 case 'n': 348 t = atoi(optarg); 349 if (t >= ARRAY_SIZE(test_cases)) 350 error(1, 0, "Invalid test case: %d", t); 351 all_tests = false; 352 test_cases[t].enabled = true; 353 break; 354 case 's': 355 op_size = atoi(optarg); 356 break; 357 case 't': 358 all_protocols = false; 359 socket_types[2].enabled = true; 360 break; 361 case 'u': 362 all_protocols = false; 363 socket_types[1].enabled = true; 364 break; 365 case 'i': 366 all_protocols = false; 367 socket_types[0].enabled = true; 368 break; 369 case 'S': 370 strict = true; 371 break; 372 default: 373 error(1, 0, "Failed to parse parameters."); 374 } 375 } 376 377 for (s = 0; s < ARRAY_SIZE(socket_types); s++) { 378 if (!all_protocols && !socket_types[s].enabled) 379 continue; 380 381 printf("Testing %s...\n", socket_types[s].friendly_name); 382 for (t = 0; t < ARRAY_SIZE(test_cases); t++) { 383 if (!all_tests && !test_cases[t].enabled) 384 continue; 385 386 printf("Starting testcase %d...\n", t); 387 if (run_test_case(socket_types[s], test_cases[t])) { 388 if (strict || !test_cases[t].warn_on_fail) 389 failures++; 390 printf("FAILURE in test case "); 391 print_test_case(&test_cases[t]); 392 } 393 } 394 } 395 if (!failures) 396 printf("PASSED.\n"); 397 return failures; 398 } 399