1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include "cgroup_helpers.h" 4 5 #include <linux/tcp.h> 6 #include "sockopt_sk.skel.h" 7 8 #ifndef SOL_TCP 9 #define SOL_TCP IPPROTO_TCP 10 #endif 11 12 #define SOL_CUSTOM 0xdeadbeef 13 14 static int getsetsockopt(void) 15 { 16 int fd, err; 17 union { 18 char u8[4]; 19 __u32 u32; 20 char cc[16]; /* TCP_CA_NAME_MAX */ 21 struct tcp_zerocopy_receive zc; 22 } buf = {}; 23 socklen_t optlen; 24 char *big_buf = NULL; 25 26 fd = socket(AF_INET, SOCK_STREAM, 0); 27 if (fd < 0) { 28 log_err("Failed to create socket"); 29 return -1; 30 } 31 32 /* IP_TOS - BPF bypass */ 33 34 optlen = getpagesize() * 2; 35 big_buf = calloc(1, optlen); 36 if (!big_buf) { 37 log_err("Couldn't allocate two pages"); 38 goto err; 39 } 40 41 *(int *)big_buf = 0x08; 42 err = setsockopt(fd, SOL_IP, IP_TOS, big_buf, optlen); 43 if (err) { 44 log_err("Failed to call setsockopt(IP_TOS)"); 45 goto err; 46 } 47 48 memset(big_buf, 0, optlen); 49 optlen = 1; 50 err = getsockopt(fd, SOL_IP, IP_TOS, big_buf, &optlen); 51 if (err) { 52 log_err("Failed to call getsockopt(IP_TOS)"); 53 goto err; 54 } 55 56 if (*big_buf != 0x08) { 57 log_err("Unexpected getsockopt(IP_TOS) optval 0x%x != 0x08", 58 (int)*big_buf); 59 goto err; 60 } 61 62 /* IP_TTL - EPERM */ 63 64 buf.u8[0] = 1; 65 err = setsockopt(fd, SOL_IP, IP_TTL, &buf, 1); 66 if (!err || errno != EPERM) { 67 log_err("Unexpected success from setsockopt(IP_TTL)"); 68 goto err; 69 } 70 71 /* SOL_CUSTOM - handled by BPF */ 72 73 buf.u8[0] = 0x01; 74 err = setsockopt(fd, SOL_CUSTOM, 0, &buf, 1); 75 if (err) { 76 log_err("Failed to call setsockopt"); 77 goto err; 78 } 79 80 buf.u32 = 0x00; 81 optlen = 4; 82 err = getsockopt(fd, SOL_CUSTOM, 0, &buf, &optlen); 83 if (err) { 84 log_err("Failed to call getsockopt"); 85 goto err; 86 } 87 88 if (optlen != 1) { 89 log_err("Unexpected optlen %d != 1", optlen); 90 goto err; 91 } 92 if (buf.u8[0] != 0x01) { 93 log_err("Unexpected buf[0] 0x%02x != 0x01", buf.u8[0]); 94 goto err; 95 } 96 97 /* IP_FREEBIND - BPF can't access optval past PAGE_SIZE */ 98 99 optlen = getpagesize() * 2; 100 memset(big_buf, 0, optlen); 101 102 err = setsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, optlen); 103 if (err != 0) { 104 log_err("Failed to call setsockopt, ret=%d", err); 105 goto err; 106 } 107 108 err = getsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, &optlen); 109 if (err != 0) { 110 log_err("Failed to call getsockopt, ret=%d", err); 111 goto err; 112 } 113 114 if (optlen != 1 || *(__u8 *)big_buf != 0x55) { 115 log_err("Unexpected IP_FREEBIND getsockopt, optlen=%d, optval=0x%x", 116 optlen, *(__u8 *)big_buf); 117 } 118 119 /* SO_SNDBUF is overwritten */ 120 121 buf.u32 = 0x01010101; 122 err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, 4); 123 if (err) { 124 log_err("Failed to call setsockopt(SO_SNDBUF)"); 125 goto err; 126 } 127 128 buf.u32 = 0x00; 129 optlen = 4; 130 err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, &optlen); 131 if (err) { 132 log_err("Failed to call getsockopt(SO_SNDBUF)"); 133 goto err; 134 } 135 136 if (buf.u32 != 0x55AA*2) { 137 log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2", 138 buf.u32); 139 goto err; 140 } 141 142 /* TCP_CONGESTION can extend the string */ 143 144 strcpy(buf.cc, "nv"); 145 err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv")); 146 if (err) { 147 log_err("Failed to call setsockopt(TCP_CONGESTION)"); 148 goto err; 149 } 150 151 152 optlen = sizeof(buf.cc); 153 err = getsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, &optlen); 154 if (err) { 155 log_err("Failed to call getsockopt(TCP_CONGESTION)"); 156 goto err; 157 } 158 159 if (strcmp(buf.cc, "cubic") != 0) { 160 log_err("Unexpected getsockopt(TCP_CONGESTION) %s != %s", 161 buf.cc, "cubic"); 162 goto err; 163 } 164 165 /* TCP_ZEROCOPY_RECEIVE triggers */ 166 memset(&buf, 0, sizeof(buf)); 167 optlen = sizeof(buf.zc); 168 err = getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &buf, &optlen); 169 if (err) { 170 log_err("Unexpected getsockopt(TCP_ZEROCOPY_RECEIVE) err=%d errno=%d", 171 err, errno); 172 goto err; 173 } 174 175 memset(&buf, 0, sizeof(buf)); 176 buf.zc.address = 12345; /* Not page aligned. Rejected by tcp_zerocopy_receive() */ 177 optlen = sizeof(buf.zc); 178 errno = 0; 179 err = getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &buf, &optlen); 180 if (errno != EINVAL) { 181 log_err("Unexpected getsockopt(TCP_ZEROCOPY_RECEIVE) err=%d errno=%d", 182 err, errno); 183 goto err; 184 } 185 186 free(big_buf); 187 close(fd); 188 return 0; 189 err: 190 free(big_buf); 191 close(fd); 192 return -1; 193 } 194 195 static void run_test(int cgroup_fd) 196 { 197 struct sockopt_sk *skel; 198 199 skel = sockopt_sk__open_and_load(); 200 if (!ASSERT_OK_PTR(skel, "skel_load")) 201 goto cleanup; 202 203 skel->bss->page_size = getpagesize(); 204 205 skel->links._setsockopt = 206 bpf_program__attach_cgroup(skel->progs._setsockopt, cgroup_fd); 207 if (!ASSERT_OK_PTR(skel->links._setsockopt, "setsockopt_link")) 208 goto cleanup; 209 210 skel->links._getsockopt = 211 bpf_program__attach_cgroup(skel->progs._getsockopt, cgroup_fd); 212 if (!ASSERT_OK_PTR(skel->links._getsockopt, "getsockopt_link")) 213 goto cleanup; 214 215 ASSERT_OK(getsetsockopt(), "getsetsockopt"); 216 217 cleanup: 218 sockopt_sk__destroy(skel); 219 } 220 221 void test_sockopt_sk(void) 222 { 223 int cgroup_fd; 224 225 cgroup_fd = test__join_cgroup("/sockopt_sk"); 226 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /sockopt_sk")) 227 return; 228 229 run_test(cgroup_fd); 230 close(cgroup_fd); 231 } 232