1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
4 #include "network_helpers.h"
5 
6 struct tcp_rtt_storage {
7 	__u32 invoked;
8 	__u32 dsack_dups;
9 	__u32 delivered;
10 	__u32 delivered_ce;
11 	__u32 icsk_retransmits;
12 };
13 
send_byte(int fd)14 static void send_byte(int fd)
15 {
16 	char b = 0x55;
17 
18 	if (CHECK_FAIL(write(fd, &b, sizeof(b)) != 1))
19 		perror("Failed to send single byte");
20 }
21 
wait_for_ack(int fd,int retries)22 static int wait_for_ack(int fd, int retries)
23 {
24 	struct tcp_info info;
25 	socklen_t optlen;
26 	int i, err;
27 
28 	for (i = 0; i < retries; i++) {
29 		optlen = sizeof(info);
30 		err = getsockopt(fd, SOL_TCP, TCP_INFO, &info, &optlen);
31 		if (err < 0) {
32 			log_err("Failed to lookup TCP stats");
33 			return err;
34 		}
35 
36 		if (info.tcpi_unacked == 0)
37 			return 0;
38 
39 		usleep(10);
40 	}
41 
42 	log_err("Did not receive ACK");
43 	return -1;
44 }
45 
verify_sk(int map_fd,int client_fd,const char * msg,__u32 invoked,__u32 dsack_dups,__u32 delivered,__u32 delivered_ce,__u32 icsk_retransmits)46 static int verify_sk(int map_fd, int client_fd, const char *msg, __u32 invoked,
47 		     __u32 dsack_dups, __u32 delivered, __u32 delivered_ce,
48 		     __u32 icsk_retransmits)
49 {
50 	int err = 0;
51 	struct tcp_rtt_storage val;
52 
53 	if (CHECK_FAIL(bpf_map_lookup_elem(map_fd, &client_fd, &val) < 0)) {
54 		perror("Failed to read socket storage");
55 		return -1;
56 	}
57 
58 	if (val.invoked != invoked) {
59 		log_err("%s: unexpected bpf_tcp_sock.invoked %d != %d",
60 			msg, val.invoked, invoked);
61 		err++;
62 	}
63 
64 	if (val.dsack_dups != dsack_dups) {
65 		log_err("%s: unexpected bpf_tcp_sock.dsack_dups %d != %d",
66 			msg, val.dsack_dups, dsack_dups);
67 		err++;
68 	}
69 
70 	if (val.delivered != delivered) {
71 		log_err("%s: unexpected bpf_tcp_sock.delivered %d != %d",
72 			msg, val.delivered, delivered);
73 		err++;
74 	}
75 
76 	if (val.delivered_ce != delivered_ce) {
77 		log_err("%s: unexpected bpf_tcp_sock.delivered_ce %d != %d",
78 			msg, val.delivered_ce, delivered_ce);
79 		err++;
80 	}
81 
82 	if (val.icsk_retransmits != icsk_retransmits) {
83 		log_err("%s: unexpected bpf_tcp_sock.icsk_retransmits %d != %d",
84 			msg, val.icsk_retransmits, icsk_retransmits);
85 		err++;
86 	}
87 
88 	return err;
89 }
90 
91 
run_test(int cgroup_fd,int server_fd)92 static int run_test(int cgroup_fd, int server_fd)
93 {
94 	struct bpf_prog_load_attr attr = {
95 		.prog_type = BPF_PROG_TYPE_SOCK_OPS,
96 		.file = "./tcp_rtt.o",
97 		.expected_attach_type = BPF_CGROUP_SOCK_OPS,
98 	};
99 	struct bpf_object *obj;
100 	struct bpf_map *map;
101 	int client_fd;
102 	int prog_fd;
103 	int map_fd;
104 	int err;
105 
106 	err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
107 	if (err) {
108 		log_err("Failed to load BPF object");
109 		return -1;
110 	}
111 
112 	map = bpf_map__next(NULL, obj);
113 	map_fd = bpf_map__fd(map);
114 
115 	err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
116 	if (err) {
117 		log_err("Failed to attach BPF program");
118 		goto close_bpf_object;
119 	}
120 
121 	client_fd = connect_to_fd(server_fd, 0);
122 	if (client_fd < 0) {
123 		err = -1;
124 		goto close_bpf_object;
125 	}
126 
127 	err += verify_sk(map_fd, client_fd, "syn-ack",
128 			 /*invoked=*/1,
129 			 /*dsack_dups=*/0,
130 			 /*delivered=*/1,
131 			 /*delivered_ce=*/0,
132 			 /*icsk_retransmits=*/0);
133 
134 	send_byte(client_fd);
135 	if (wait_for_ack(client_fd, 100) < 0) {
136 		err = -1;
137 		goto close_client_fd;
138 	}
139 
140 
141 	err += verify_sk(map_fd, client_fd, "first payload byte",
142 			 /*invoked=*/2,
143 			 /*dsack_dups=*/0,
144 			 /*delivered=*/2,
145 			 /*delivered_ce=*/0,
146 			 /*icsk_retransmits=*/0);
147 
148 close_client_fd:
149 	close(client_fd);
150 
151 close_bpf_object:
152 	bpf_object__close(obj);
153 	return err;
154 }
155 
test_tcp_rtt(void)156 void test_tcp_rtt(void)
157 {
158 	int server_fd, cgroup_fd;
159 
160 	cgroup_fd = test__join_cgroup("/tcp_rtt");
161 	if (CHECK_FAIL(cgroup_fd < 0))
162 		return;
163 
164 	server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
165 	if (CHECK_FAIL(server_fd < 0))
166 		goto close_cgroup_fd;
167 
168 	CHECK_FAIL(run_test(cgroup_fd, server_fd));
169 
170 	close(server_fd);
171 
172 close_cgroup_fd:
173 	close(cgroup_fd);
174 }
175