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