Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf, selftests: Add cgroup v1 net_cls classid helpers

Minimal set of helpers for net_cls classid cgroupv1 management in order
to set an id, join from a process, initiate setup and teardown. cgroupv2
helpers are left as-is, but reused where possible.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20210913230759.2313-2-daniel@iogearbox.net

authored by

Daniel Borkmann and committed by
Alexei Starovoitov
d8079d80 8520e224

+143 -14
+128 -9
tools/testing/selftests/bpf/cgroup_helpers.c
··· 12 12 #include <unistd.h> 13 13 #include <ftw.h> 14 14 15 - 16 15 #include "cgroup_helpers.h" 17 16 18 17 /* 19 18 * To avoid relying on the system setup, when setup_cgroup_env is called 20 - * we create a new mount namespace, and cgroup namespace. The cgroup2 21 - * root is mounted at CGROUP_MOUNT_PATH 19 + * we create a new mount namespace, and cgroup namespace. The cgroupv2 20 + * root is mounted at CGROUP_MOUNT_PATH. Unfortunately, most people don't 21 + * have cgroupv2 enabled at this point in time. It's easier to create our 22 + * own mount namespace and manage it ourselves. We assume /mnt exists. 22 23 * 23 - * Unfortunately, most people don't have cgroupv2 enabled at this point in time. 24 - * It's easier to create our own mount namespace and manage it ourselves. 25 - * 26 - * We assume /mnt exists. 24 + * Related cgroupv1 helpers are named *classid*(), since we only use the 25 + * net_cls controller for tagging net_cls.classid. We assume the default 26 + * mount under /sys/fs/cgroup/net_cls, which should be the case for the 27 + * vast majority of users. 27 28 */ 28 29 29 30 #define WALK_FD_LIMIT 16 31 + 30 32 #define CGROUP_MOUNT_PATH "/mnt" 33 + #define CGROUP_MOUNT_DFLT "/sys/fs/cgroup" 34 + #define NETCLS_MOUNT_PATH CGROUP_MOUNT_DFLT "/net_cls" 31 35 #define CGROUP_WORK_DIR "/cgroup-test-work-dir" 36 + 32 37 #define format_cgroup_path(buf, path) \ 33 38 snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \ 34 39 CGROUP_WORK_DIR, path) 40 + 41 + #define format_classid_path(buf) \ 42 + snprintf(buf, sizeof(buf), "%s%s", NETCLS_MOUNT_PATH, \ 43 + CGROUP_WORK_DIR) 35 44 36 45 /** 37 46 * enable_all_controllers() - Enable all available cgroup v2 controllers ··· 148 139 return 0; 149 140 } 150 141 151 - 152 - static int join_cgroup_from_top(char *cgroup_path) 142 + static int join_cgroup_from_top(const char *cgroup_path) 153 143 { 154 144 char cgroup_procs_path[PATH_MAX + 1]; 155 145 pid_t pid = getpid(); ··· 320 312 return -EINVAL; 321 313 } 322 314 return cg_fd; 315 + } 316 + 317 + /** 318 + * setup_classid_environment() - Setup the cgroupv1 net_cls environment 319 + * 320 + * After calling this function, cleanup_classid_environment should be called 321 + * once testing is complete. 322 + * 323 + * This function will print an error to stderr and return 1 if it is unable 324 + * to setup the cgroup environment. If setup is successful, 0 is returned. 325 + */ 326 + int setup_classid_environment(void) 327 + { 328 + char cgroup_workdir[PATH_MAX + 1]; 329 + 330 + format_classid_path(cgroup_workdir); 331 + 332 + if (mount("tmpfs", CGROUP_MOUNT_DFLT, "tmpfs", 0, NULL) && 333 + errno != EBUSY) { 334 + log_err("mount cgroup base"); 335 + return 1; 336 + } 337 + 338 + if (mkdir(NETCLS_MOUNT_PATH, 0777) && errno != EEXIST) { 339 + log_err("mkdir cgroup net_cls"); 340 + return 1; 341 + } 342 + 343 + if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls") && 344 + errno != EBUSY) { 345 + log_err("mount cgroup net_cls"); 346 + return 1; 347 + } 348 + 349 + cleanup_classid_environment(); 350 + 351 + if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) { 352 + log_err("mkdir cgroup work dir"); 353 + return 1; 354 + } 355 + 356 + return 0; 357 + } 358 + 359 + /** 360 + * set_classid() - Set a cgroupv1 net_cls classid 361 + * @id: the numeric classid 362 + * 363 + * Writes the passed classid into the cgroup work dir's net_cls.classid 364 + * file in order to later on trigger socket tagging. 365 + * 366 + * On success, it returns 0, otherwise on failure it returns 1. If there 367 + * is a failure, it prints the error to stderr. 368 + */ 369 + int set_classid(unsigned int id) 370 + { 371 + char cgroup_workdir[PATH_MAX - 42]; 372 + char cgroup_classid_path[PATH_MAX + 1]; 373 + int fd, rc = 0; 374 + 375 + format_classid_path(cgroup_workdir); 376 + snprintf(cgroup_classid_path, sizeof(cgroup_classid_path), 377 + "%s/net_cls.classid", cgroup_workdir); 378 + 379 + fd = open(cgroup_classid_path, O_WRONLY); 380 + if (fd < 0) { 381 + log_err("Opening cgroup classid: %s", cgroup_classid_path); 382 + return 1; 383 + } 384 + 385 + if (dprintf(fd, "%u\n", id) < 0) { 386 + log_err("Setting cgroup classid"); 387 + rc = 1; 388 + } 389 + 390 + close(fd); 391 + return rc; 392 + } 393 + 394 + /** 395 + * join_classid() - Join a cgroupv1 net_cls classid 396 + * 397 + * This function expects the cgroup work dir to be already created, as we 398 + * join it here. This causes the process sockets to be tagged with the given 399 + * net_cls classid. 400 + * 401 + * On success, it returns 0, otherwise on failure it returns 1. 402 + */ 403 + int join_classid(void) 404 + { 405 + char cgroup_workdir[PATH_MAX + 1]; 406 + 407 + format_classid_path(cgroup_workdir); 408 + return join_cgroup_from_top(cgroup_workdir); 409 + } 410 + 411 + /** 412 + * cleanup_classid_environment() - Cleanup the cgroupv1 net_cls environment 413 + * 414 + * At call time, it moves the calling process to the root cgroup, and then 415 + * runs the deletion process. 416 + * 417 + * On failure, it will print an error to stderr, and try to continue. 418 + */ 419 + void cleanup_classid_environment(void) 420 + { 421 + char cgroup_workdir[PATH_MAX + 1]; 422 + 423 + format_classid_path(cgroup_workdir); 424 + join_cgroup_from_top(NETCLS_MOUNT_PATH); 425 + nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT); 323 426 }
+15 -5
tools/testing/selftests/bpf/cgroup_helpers.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 #ifndef __CGROUP_HELPERS_H 3 3 #define __CGROUP_HELPERS_H 4 + 4 5 #include <errno.h> 5 6 #include <string.h> 6 7 ··· 9 8 #define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ 10 9 __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 11 10 12 - 11 + /* cgroupv2 related */ 13 12 int cgroup_setup_and_join(const char *path); 14 13 int create_and_get_cgroup(const char *path); 15 - int join_cgroup(const char *path); 16 - int setup_cgroup_environment(void); 17 - void cleanup_cgroup_environment(void); 18 14 unsigned long long get_cgroup_id(const char *path); 19 15 20 - #endif 16 + int join_cgroup(const char *path); 17 + 18 + int setup_cgroup_environment(void); 19 + void cleanup_cgroup_environment(void); 20 + 21 + /* cgroupv1 related */ 22 + int set_classid(unsigned int id); 23 + int join_classid(void); 24 + 25 + int setup_classid_environment(void); 26 + void cleanup_classid_environment(void); 27 + 28 + #endif /* __CGROUP_HELPERS_H */