brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 16ac4df Raw
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