File Index
Files and directories
Sign in
FILE

net_timestamping.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/net_timestamping.c C
  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
 11static const char addr4_str[] = "127.0.0.1";
 12static const char addr6_str[] = "::1";
 13static struct net_timestamping *skel;
 14static const int cfg_payload_len = 30;
 15static struct timespec usr_ts;
 16static u64 delay_tolerance_nsec = 10000000000; /* 10 seconds */
 17int SK_TS_SCHED;
 18int SK_TS_TXSW;
 19int SK_TS_ACK;
 20
 21static int64_t timespec_to_ns64(struct timespec *ts)
 22{
 23	return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
 24}
 25
 26static 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
 38static 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
 48static 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
 73static 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
102static 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
133static 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
146static 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
219out:
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
229void 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}
~/ Read-only file preview.