1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include "cgroup_helpers.h" 4 5 static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title, const char *name) 6 { 7 enum bpf_attach_type attach_type; 8 enum bpf_prog_type prog_type; 9 struct bpf_program *prog; 10 int err; 11 12 err = libbpf_prog_type_by_name(title, &prog_type, &attach_type); 13 if (err) { 14 log_err("Failed to deduct types for %s BPF program", title); 15 return -1; 16 } 17 18 prog = bpf_object__find_program_by_name(obj, name); 19 if (!prog) { 20 log_err("Failed to find %s BPF program", name); 21 return -1; 22 } 23 24 err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd, 25 attach_type, BPF_F_ALLOW_MULTI); 26 if (err) { 27 log_err("Failed to attach %s BPF program", name); 28 return -1; 29 } 30 31 return 0; 32 } 33 34 static int prog_detach(struct bpf_object *obj, int cgroup_fd, const char *title, const char *name) 35 { 36 enum bpf_attach_type attach_type; 37 enum bpf_prog_type prog_type; 38 struct bpf_program *prog; 39 int err; 40 41 err = libbpf_prog_type_by_name(title, &prog_type, &attach_type); 42 if (err) 43 return -1; 44 45 prog = bpf_object__find_program_by_name(obj, name); 46 if (!prog) 47 return -1; 48 49 err = bpf_prog_detach2(bpf_program__fd(prog), cgroup_fd, 50 attach_type); 51 if (err) 52 return -1; 53 54 return 0; 55 } 56 57 static int run_getsockopt_test(struct bpf_object *obj, int cg_parent, 58 int cg_child, int sock_fd) 59 { 60 socklen_t optlen; 61 __u8 buf; 62 int err; 63 64 /* Set IP_TOS to the expected value (0x80). */ 65 66 buf = 0x80; 67 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 68 if (err < 0) { 69 log_err("Failed to call setsockopt(IP_TOS)"); 70 goto detach; 71 } 72 73 buf = 0x00; 74 optlen = 1; 75 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 76 if (err) { 77 log_err("Failed to call getsockopt(IP_TOS)"); 78 goto detach; 79 } 80 81 if (buf != 0x80) { 82 log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf); 83 err = -1; 84 goto detach; 85 } 86 87 /* Attach child program and make sure it returns new value: 88 * - kernel: -> 0x80 89 * - child: 0x80 -> 0x90 90 */ 91 92 err = prog_attach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child"); 93 if (err) 94 goto detach; 95 96 buf = 0x00; 97 optlen = 1; 98 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 99 if (err) { 100 log_err("Failed to call getsockopt(IP_TOS)"); 101 goto detach; 102 } 103 104 if (buf != 0x90) { 105 log_err("Unexpected getsockopt 0x%x != 0x90", buf); 106 err = -1; 107 goto detach; 108 } 109 110 /* Attach parent program and make sure it returns new value: 111 * - kernel: -> 0x80 112 * - child: 0x80 -> 0x90 113 * - parent: 0x90 -> 0xA0 114 */ 115 116 err = prog_attach(obj, cg_parent, "cgroup/getsockopt", "_getsockopt_parent"); 117 if (err) 118 goto detach; 119 120 buf = 0x00; 121 optlen = 1; 122 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 123 if (err) { 124 log_err("Failed to call getsockopt(IP_TOS)"); 125 goto detach; 126 } 127 128 if (buf != 0xA0) { 129 log_err("Unexpected getsockopt 0x%x != 0xA0", buf); 130 err = -1; 131 goto detach; 132 } 133 134 /* Setting unexpected initial sockopt should return EPERM: 135 * - kernel: -> 0x40 136 * - child: unexpected 0x40, EPERM 137 * - parent: unexpected 0x40, EPERM 138 */ 139 140 buf = 0x40; 141 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 142 if (err < 0) { 143 log_err("Failed to call setsockopt(IP_TOS)"); 144 goto detach; 145 } 146 147 buf = 0x00; 148 optlen = 1; 149 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 150 if (!err) { 151 log_err("Unexpected success from getsockopt(IP_TOS)"); 152 goto detach; 153 } 154 155 /* Detach child program and make sure we still get EPERM: 156 * - kernel: -> 0x40 157 * - parent: unexpected 0x40, EPERM 158 */ 159 160 err = prog_detach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child"); 161 if (err) { 162 log_err("Failed to detach child program"); 163 goto detach; 164 } 165 166 buf = 0x00; 167 optlen = 1; 168 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 169 if (!err) { 170 log_err("Unexpected success from getsockopt(IP_TOS)"); 171 goto detach; 172 } 173 174 /* Set initial value to the one the parent program expects: 175 * - kernel: -> 0x90 176 * - parent: 0x90 -> 0xA0 177 */ 178 179 buf = 0x90; 180 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 181 if (err < 0) { 182 log_err("Failed to call setsockopt(IP_TOS)"); 183 goto detach; 184 } 185 186 buf = 0x00; 187 optlen = 1; 188 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 189 if (err) { 190 log_err("Failed to call getsockopt(IP_TOS)"); 191 goto detach; 192 } 193 194 if (buf != 0xA0) { 195 log_err("Unexpected getsockopt 0x%x != 0xA0", buf); 196 err = -1; 197 goto detach; 198 } 199 200 detach: 201 prog_detach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child"); 202 prog_detach(obj, cg_parent, "cgroup/getsockopt", "_getsockopt_parent"); 203 204 return err; 205 } 206 207 static int run_setsockopt_test(struct bpf_object *obj, int cg_parent, 208 int cg_child, int sock_fd) 209 { 210 socklen_t optlen; 211 __u8 buf; 212 int err; 213 214 /* Set IP_TOS to the expected value (0x80). */ 215 216 buf = 0x80; 217 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 218 if (err < 0) { 219 log_err("Failed to call setsockopt(IP_TOS)"); 220 goto detach; 221 } 222 223 buf = 0x00; 224 optlen = 1; 225 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 226 if (err) { 227 log_err("Failed to call getsockopt(IP_TOS)"); 228 goto detach; 229 } 230 231 if (buf != 0x80) { 232 log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf); 233 err = -1; 234 goto detach; 235 } 236 237 /* Attach child program and make sure it adds 0x10. */ 238 239 err = prog_attach(obj, cg_child, "cgroup/setsockopt", "_setsockopt"); 240 if (err) 241 goto detach; 242 243 buf = 0x80; 244 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 245 if (err < 0) { 246 log_err("Failed to call setsockopt(IP_TOS)"); 247 goto detach; 248 } 249 250 buf = 0x00; 251 optlen = 1; 252 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 253 if (err) { 254 log_err("Failed to call getsockopt(IP_TOS)"); 255 goto detach; 256 } 257 258 if (buf != 0x80 + 0x10) { 259 log_err("Unexpected getsockopt 0x%x != 0x80 + 0x10", buf); 260 err = -1; 261 goto detach; 262 } 263 264 /* Attach parent program and make sure it adds another 0x10. */ 265 266 err = prog_attach(obj, cg_parent, "cgroup/setsockopt", "_setsockopt"); 267 if (err) 268 goto detach; 269 270 buf = 0x80; 271 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 272 if (err < 0) { 273 log_err("Failed to call setsockopt(IP_TOS)"); 274 goto detach; 275 } 276 277 buf = 0x00; 278 optlen = 1; 279 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 280 if (err) { 281 log_err("Failed to call getsockopt(IP_TOS)"); 282 goto detach; 283 } 284 285 if (buf != 0x80 + 2 * 0x10) { 286 log_err("Unexpected getsockopt 0x%x != 0x80 + 2 * 0x10", buf); 287 err = -1; 288 goto detach; 289 } 290 291 detach: 292 prog_detach(obj, cg_child, "cgroup/setsockopt", "_setsockopt"); 293 prog_detach(obj, cg_parent, "cgroup/setsockopt", "_setsockopt"); 294 295 return err; 296 } 297 298 void test_sockopt_multi(void) 299 { 300 int cg_parent = -1, cg_child = -1; 301 struct bpf_object *obj = NULL; 302 int sock_fd = -1; 303 int err = -1; 304 305 cg_parent = test__join_cgroup("/parent"); 306 if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent")) 307 goto out; 308 309 cg_child = test__join_cgroup("/parent/child"); 310 if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child")) 311 goto out; 312 313 obj = bpf_object__open_file("sockopt_multi.bpf.o", NULL); 314 if (!ASSERT_OK_PTR(obj, "obj_load")) 315 goto out; 316 317 err = bpf_object__load(obj); 318 if (!ASSERT_OK(err, "obj_load")) 319 goto out; 320 321 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 322 if (!ASSERT_GE(sock_fd, 0, "socket")) 323 goto out; 324 325 ASSERT_OK(run_getsockopt_test(obj, cg_parent, cg_child, sock_fd), "getsockopt_test"); 326 ASSERT_OK(run_setsockopt_test(obj, cg_parent, cg_child, sock_fd), "setsockopt_test"); 327 328 out: 329 close(sock_fd); 330 bpf_object__close(obj); 331 close(cg_child); 332 close(cg_parent); 333 } 334