xref: /linux/tools/testing/selftests/bpf/prog_tests/net_timestamping.c (revision c3b999cad7ec39a5487b20e8b6e737d2ab0c5393)
1 #include <linux/net_tstamp.h>
2 #include <sys/time.h>
3 #include <linux/errqueue.h>
4 #include "test_progs.h"
5 #include "network_helpers.h"
6 #include "net_timestamping.skel.h"
7 
8 #define CG_NAME "/net-timestamping-test"
9 #define NSEC_PER_SEC    1000000000LL
10 
11 static const char addr4_str[] = "127.0.0.1";
12 static const char addr6_str[] = "::1";
13 static struct net_timestamping *skel;
14 static const int cfg_payload_len = 30;
15 static struct timespec usr_ts;
16 static u64 delay_tolerance_nsec = 10000000000; /* 10 seconds */
17 int SK_TS_SCHED;
18 int SK_TS_TXSW;
19 int SK_TS_ACK;
20 
21 static int64_t timespec_to_ns64(struct timespec *ts)
22 {
23 	return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
24 }
25 
26 static void validate_key(int tskey, int tstype)
27 {
28 	static int expected_tskey = -1;
29 
30 	if (tstype == SCM_TSTAMP_SCHED)
31 		expected_tskey = cfg_payload_len - 1;
32 
33 	ASSERT_EQ(expected_tskey, tskey, "tskey mismatch");
34 
35 	expected_tskey = tskey;
36 }
37 
38 static void validate_timestamp(struct timespec *cur, struct timespec *prev)
39 {
40 	int64_t cur_ns, prev_ns;
41 
42 	cur_ns = timespec_to_ns64(cur);
43 	prev_ns = timespec_to_ns64(prev);
44 
45 	ASSERT_LT(cur_ns - prev_ns, delay_tolerance_nsec, "latency");
46 }
47 
48 static void test_socket_timestamp(struct scm_timestamping *tss, int tstype,
49 				  int tskey)
50 {
51 	static struct timespec prev_ts;
52 
53 	validate_key(tskey, tstype);
54 
55 	switch (tstype) {
56 	case SCM_TSTAMP_SCHED:
57 		validate_timestamp(&tss->ts[0], &usr_ts);
58 		SK_TS_SCHED += 1;
59 		break;
60 	case SCM_TSTAMP_SND:
61 		validate_timestamp(&tss->ts[0], &prev_ts);
62 		SK_TS_TXSW += 1;
63 		break;
64 	case SCM_TSTAMP_ACK:
65 		validate_timestamp(&tss->ts[0], &prev_ts);
66 		SK_TS_ACK += 1;
67 		break;
68 	}
69 
70 	prev_ts = tss->ts[0];
71 }
72 
73 static void test_recv_errmsg_cmsg(struct msghdr *msg)
74 {
75 	struct sock_extended_err *serr = NULL;
76 	struct scm_timestamping *tss = NULL;
77 	struct cmsghdr *cm;
78 
79 	for (cm = CMSG_FIRSTHDR(msg);
80 	     cm && cm->cmsg_len;
81 	     cm = CMSG_NXTHDR(msg, cm)) {
82 		if (cm->cmsg_level == SOL_SOCKET &&
83 		    cm->cmsg_type == SCM_TIMESTAMPING) {
84 			tss = (void *)CMSG_DATA(cm);
85 		} else if ((cm->cmsg_level == SOL_IP &&
86 			    cm->cmsg_type == IP_RECVERR) ||
87 			   (cm->cmsg_level == SOL_IPV6 &&
88 			    cm->cmsg_type == IPV6_RECVERR) ||
89 			   (cm->cmsg_level == SOL_PACKET &&
90 			    cm->cmsg_type == PACKET_TX_TIMESTAMP)) {
91 			serr = (void *)CMSG_DATA(cm);
92 			ASSERT_EQ(serr->ee_origin, SO_EE_ORIGIN_TIMESTAMPING,
93 				  "cmsg type");
94 		}
95 
96 		if (serr && tss)
97 			test_socket_timestamp(tss, serr->ee_info,
98 					      serr->ee_data);
99 	}
100 }
101 
102 static bool socket_recv_errmsg(int fd)
103 {
104 	static char ctrl[1024 /* overprovision*/];
105 	char data[cfg_payload_len];
106 	static struct msghdr msg;
107 	struct iovec entry;
108 	int n = 0;
109 
110 	memset(&msg, 0, sizeof(msg));
111 	memset(&entry, 0, sizeof(entry));
112 	memset(ctrl, 0, sizeof(ctrl));
113 
114 	entry.iov_base = data;
115 	entry.iov_len = cfg_payload_len;
116 	msg.msg_iov = &entry;
117 	msg.msg_iovlen = 1;
118 	msg.msg_name = NULL;
119 	msg.msg_namelen = 0;
120 	msg.msg_control = ctrl;
121 	msg.msg_controllen = sizeof(ctrl);
122 
123 	n = recvmsg(fd, &msg, MSG_ERRQUEUE);
124 	if (n == -1)
125 		ASSERT_EQ(errno, EAGAIN, "recvmsg MSG_ERRQUEUE");
126 
127 	if (n >= 0)
128 		test_recv_errmsg_cmsg(&msg);
129 
130 	return n == -1;
131 }
132 
133 static void test_socket_timestamping(int fd)
134 {
135 	while (!socket_recv_errmsg(fd));
136 
137 	ASSERT_EQ(SK_TS_SCHED, 1, "SCM_TSTAMP_SCHED");
138 	ASSERT_EQ(SK_TS_TXSW, 1, "SCM_TSTAMP_SND");
139 	ASSERT_EQ(SK_TS_ACK, 1, "SCM_TSTAMP_ACK");
140 
141 	SK_TS_SCHED = 0;
142 	SK_TS_TXSW = 0;
143 	SK_TS_ACK = 0;
144 }
145 
146 static void test_tcp(int family, bool enable_socket_timestamping)
147 {
148 	struct net_timestamping__bss *bss;
149 	char buf[cfg_payload_len];
150 	int sfd = -1, cfd = -1;
151 	unsigned int sock_opt;
152 	struct netns_obj *ns;
153 	int cg_fd;
154 	int ret;
155 
156 	cg_fd = test__join_cgroup(CG_NAME);
157 	if (!ASSERT_OK_FD(cg_fd, "join cgroup"))
158 		return;
159 
160 	ns = netns_new("net_timestamping_ns", true);
161 	if (!ASSERT_OK_PTR(ns, "create ns"))
162 		goto out;
163 
164 	skel = net_timestamping__open_and_load();
165 	if (!ASSERT_OK_PTR(skel, "open and load skel"))
166 		goto out;
167 
168 	if (!ASSERT_OK(net_timestamping__attach(skel), "attach skel"))
169 		goto out;
170 
171 	skel->links.skops_sockopt =
172 		bpf_program__attach_cgroup(skel->progs.skops_sockopt, cg_fd);
173 	if (!ASSERT_OK_PTR(skel->links.skops_sockopt, "attach cgroup"))
174 		goto out;
175 
176 	bss = skel->bss;
177 	memset(bss, 0, sizeof(*bss));
178 
179 	skel->bss->monitored_pid = getpid();
180 
181 	sfd = start_server(family, SOCK_STREAM,
182 			   family == AF_INET6 ? addr6_str : addr4_str, 0, 0);
183 	if (!ASSERT_OK_FD(sfd, "start_server"))
184 		goto out;
185 
186 	cfd = connect_to_fd(sfd, 0);
187 	if (!ASSERT_OK_FD(cfd, "connect_to_fd_server"))
188 		goto out;
189 
190 	if (enable_socket_timestamping) {
191 		sock_opt = SOF_TIMESTAMPING_SOFTWARE |
192 			   SOF_TIMESTAMPING_OPT_ID |
193 			   SOF_TIMESTAMPING_TX_SCHED |
194 			   SOF_TIMESTAMPING_TX_SOFTWARE |
195 			   SOF_TIMESTAMPING_TX_ACK;
196 		ret = setsockopt(cfd, SOL_SOCKET, SO_TIMESTAMPING,
197 				 (char *) &sock_opt, sizeof(sock_opt));
198 		if (!ASSERT_OK(ret, "setsockopt SO_TIMESTAMPING"))
199 			goto out;
200 
201 		ret = clock_gettime(CLOCK_REALTIME, &usr_ts);
202 		if (!ASSERT_OK(ret, "get user time"))
203 			goto out;
204 	}
205 
206 	ret = write(cfd, buf, sizeof(buf));
207 	if (!ASSERT_EQ(ret, sizeof(buf), "send to server"))
208 		goto out;
209 
210 	if (enable_socket_timestamping)
211 		test_socket_timestamping(cfd);
212 
213 	ASSERT_EQ(bss->nr_active, 1, "nr_active");
214 	ASSERT_EQ(bss->nr_snd, 2, "nr_snd");
215 	ASSERT_EQ(bss->nr_sched, 1, "nr_sched");
216 	ASSERT_EQ(bss->nr_txsw, 1, "nr_txsw");
217 	ASSERT_EQ(bss->nr_ack, 1, "nr_ack");
218 
219 out:
220 	if (sfd >= 0)
221 		close(sfd);
222 	if (cfd >= 0)
223 		close(cfd);
224 	net_timestamping__destroy(skel);
225 	netns_free(ns);
226 	close(cg_fd);
227 }
228 
229 void test_net_timestamping(void)
230 {
231 	if (test__start_subtest("INET4: bpf timestamping"))
232 		test_tcp(AF_INET, false);
233 	if (test__start_subtest("INET4: bpf and socket timestamping"))
234 		test_tcp(AF_INET, true);
235 	if (test__start_subtest("INET6: bpf timestamping"))
236 		test_tcp(AF_INET6, false);
237 	if (test__start_subtest("INET6: bpf and socket timestamping"))
238 		test_tcp(AF_INET6, true);
239 }
240