449 lines · 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 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 SOF_FLAG(SOF_TIMESTAMPING_OPT_RX_FILTER),61 SOF_FLAG(SOF_TIMESTAMPING_RAW_HARDWARE),62};63 64static 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 70static 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 = true96 },97 {98 { .so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE99 | SOF_TIMESTAMPING_RX_HARDWARE },100 {}101 },102 {103 { .so_timestamping = SOF_TIMESTAMPING_RAW_HARDWARE104 | SOF_TIMESTAMPING_OPT_RX_FILTER },105 {}106 },107 {108 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE109 | SOF_TIMESTAMPING_OPT_RX_FILTER },110 {}111 },112 {113 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE114 | SOF_TIMESTAMPING_RX_SOFTWARE115 | SOF_TIMESTAMPING_OPT_RX_FILTER },116 { .swtstamp = true }117 },118 {119 { .so_timestamping = SOF_TIMESTAMPING_SOFTWARE120 | SOF_TIMESTAMPING_RX_SOFTWARE },121 { .swtstamp = true }122 },123 {124 { .so_timestamp = 1, .so_timestamping = SOF_TIMESTAMPING_SOFTWARE125 | SOF_TIMESTAMPING_RX_SOFTWARE },126 { .tstamp = true, .swtstamp = true }127 },128};129 130static 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 143static int next_port = 19999;144static int op_size = 10 * 1024;145 146void 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 180void 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 193bool 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 VALIDATE266 267 free(recv_iov.iov_base);268 269 return failed;270}271 272void 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 295bool 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 371int 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}449