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