xref: /linux/tools/testing/selftests/bpf/prog_tests/net_timestamping.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
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  
timespec_to_ns64(struct timespec * ts)21  static int64_t timespec_to_ns64(struct timespec *ts)
22  {
23  	return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
24  }
25  
validate_key(int tskey,int tstype)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  
validate_timestamp(struct timespec * cur,struct timespec * prev)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  
test_socket_timestamp(struct scm_timestamping * tss,int tstype,int tskey)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  
test_recv_errmsg_cmsg(struct msghdr * msg)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  
socket_recv_errmsg(int fd)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  
test_socket_timestamping(int fd)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  
test_tcp(int family,bool enable_socket_timestamping)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  
test_net_timestamping(void)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