1 /* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me> 2 * 3 * This program is free software; you can redistribute it and/or 4 * modify it under the terms of version 2 of the GNU General Public 5 * License as published by the Free Software Foundation. 6 */ 7 8 #define _GNU_SOURCE 9 #include <stdio.h> 10 #include <linux/bpf.h> 11 #include <unistd.h> 12 #include "libbpf.h" 13 #include "bpf_load.h" 14 #include <string.h> 15 #include <fcntl.h> 16 #include <errno.h> 17 #include <linux/bpf.h> 18 #include <sched.h> 19 #include <sys/mount.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <linux/limits.h> 23 24 #define CGROUP_MOUNT_PATH "/mnt" 25 #define CGROUP_PATH "/mnt/my-cgroup" 26 27 #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 28 #define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ 29 __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 30 31 static int join_cgroup(char *path) 32 { 33 int fd, rc = 0; 34 pid_t pid = getpid(); 35 char cgroup_path[PATH_MAX + 1]; 36 37 snprintf(cgroup_path, sizeof(cgroup_path), "%s/cgroup.procs", path); 38 39 fd = open(cgroup_path, O_WRONLY); 40 if (fd < 0) { 41 log_err("Opening Cgroup"); 42 return 1; 43 } 44 45 if (dprintf(fd, "%d\n", pid) < 0) { 46 log_err("Joining Cgroup"); 47 rc = 1; 48 } 49 close(fd); 50 return rc; 51 } 52 53 int main(int argc, char **argv) 54 { 55 char filename[256]; 56 int cg2, idx = 0; 57 pid_t remote_pid, local_pid = getpid(); 58 59 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 60 if (load_bpf_file(filename)) { 61 printf("%s", bpf_log_buf); 62 return 1; 63 } 64 65 /* 66 * This is to avoid interfering with existing cgroups. Unfortunately, 67 * most people don't have cgroupv2 enabled at this point in time. 68 * It's easier to create our own mount namespace and manage it 69 * ourselves. 70 */ 71 if (unshare(CLONE_NEWNS)) { 72 log_err("unshare"); 73 return 1; 74 } 75 76 if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) { 77 log_err("mount fakeroot"); 78 return 1; 79 } 80 81 if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) { 82 log_err("mount cgroup2"); 83 return 1; 84 } 85 86 if (mkdir(CGROUP_PATH, 0777) && errno != EEXIST) { 87 log_err("mkdir cgroup"); 88 return 1; 89 } 90 91 cg2 = open(CGROUP_PATH, O_RDONLY); 92 if (cg2 < 0) { 93 log_err("opening target cgroup"); 94 goto cleanup_cgroup_err; 95 } 96 97 if (bpf_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { 98 log_err("Adding target cgroup to map"); 99 goto cleanup_cgroup_err; 100 } 101 if (join_cgroup("/mnt/my-cgroup")) { 102 log_err("Leaving target cgroup"); 103 goto cleanup_cgroup_err; 104 } 105 106 /* 107 * The installed helper program catched the sync call, and should 108 * write it to the map. 109 */ 110 111 sync(); 112 bpf_lookup_elem(map_fd[1], &idx, &remote_pid); 113 114 if (local_pid != remote_pid) { 115 fprintf(stderr, 116 "BPF Helper didn't write correct PID to map, but: %d\n", 117 remote_pid); 118 goto leave_cgroup_err; 119 } 120 121 /* Verify the negative scenario; leave the cgroup */ 122 if (join_cgroup(CGROUP_MOUNT_PATH)) 123 goto leave_cgroup_err; 124 125 remote_pid = 0; 126 bpf_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); 127 128 sync(); 129 bpf_lookup_elem(map_fd[1], &idx, &remote_pid); 130 131 if (local_pid == remote_pid) { 132 fprintf(stderr, "BPF cgroup negative test did not work\n"); 133 goto cleanup_cgroup_err; 134 } 135 136 rmdir(CGROUP_PATH); 137 return 0; 138 139 /* Error condition, cleanup */ 140 leave_cgroup_err: 141 join_cgroup(CGROUP_MOUNT_PATH); 142 cleanup_cgroup_err: 143 rmdir(CGROUP_PATH); 144 return 1; 145 } 146