1 // SPDX-License-Identifier: GPL-2.0+ 2 // Copyright (C) 2017 Facebook 3 // Author: Roman Gushchin <guro@fb.com> 4 5 #include <fcntl.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <sys/stat.h> 9 #include <sys/types.h> 10 #include <unistd.h> 11 12 #include <bpf.h> 13 14 #include "main.h" 15 16 #define HELP_SPEC_ATTACH_FLAGS \ 17 "ATTACH_FLAGS := { multi | override }" 18 19 #define HELP_SPEC_ATTACH_TYPES \ 20 " ATTACH_TYPE := { ingress | egress | sock_create |\n" \ 21 " sock_ops | device | bind4 | bind6 |\n" \ 22 " post_bind4 | post_bind6 | connect4 |\n" \ 23 " connect6 }" 24 25 static const char * const attach_type_strings[] = { 26 [BPF_CGROUP_INET_INGRESS] = "ingress", 27 [BPF_CGROUP_INET_EGRESS] = "egress", 28 [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create", 29 [BPF_CGROUP_SOCK_OPS] = "sock_ops", 30 [BPF_CGROUP_DEVICE] = "device", 31 [BPF_CGROUP_INET4_BIND] = "bind4", 32 [BPF_CGROUP_INET6_BIND] = "bind6", 33 [BPF_CGROUP_INET4_CONNECT] = "connect4", 34 [BPF_CGROUP_INET6_CONNECT] = "connect6", 35 [BPF_CGROUP_INET4_POST_BIND] = "post_bind4", 36 [BPF_CGROUP_INET6_POST_BIND] = "post_bind6", 37 [__MAX_BPF_ATTACH_TYPE] = NULL, 38 }; 39 40 static enum bpf_attach_type parse_attach_type(const char *str) 41 { 42 enum bpf_attach_type type; 43 44 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { 45 if (attach_type_strings[type] && 46 is_prefix(str, attach_type_strings[type])) 47 return type; 48 } 49 50 return __MAX_BPF_ATTACH_TYPE; 51 } 52 53 static int show_bpf_prog(int id, const char *attach_type_str, 54 const char *attach_flags_str) 55 { 56 struct bpf_prog_info info = {}; 57 __u32 info_len = sizeof(info); 58 int prog_fd; 59 60 prog_fd = bpf_prog_get_fd_by_id(id); 61 if (prog_fd < 0) 62 return -1; 63 64 if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) { 65 close(prog_fd); 66 return -1; 67 } 68 69 if (json_output) { 70 jsonw_start_object(json_wtr); 71 jsonw_uint_field(json_wtr, "id", info.id); 72 jsonw_string_field(json_wtr, "attach_type", 73 attach_type_str); 74 jsonw_string_field(json_wtr, "attach_flags", 75 attach_flags_str); 76 jsonw_string_field(json_wtr, "name", info.name); 77 jsonw_end_object(json_wtr); 78 } else { 79 printf("%-8u %-15s %-15s %-15s\n", info.id, 80 attach_type_str, 81 attach_flags_str, 82 info.name); 83 } 84 85 close(prog_fd); 86 return 0; 87 } 88 89 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) 90 { 91 __u32 prog_ids[1024] = {0}; 92 char *attach_flags_str; 93 __u32 prog_cnt, iter; 94 __u32 attach_flags; 95 char buf[32]; 96 int ret; 97 98 prog_cnt = ARRAY_SIZE(prog_ids); 99 ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids, 100 &prog_cnt); 101 if (ret) 102 return ret; 103 104 if (prog_cnt == 0) 105 return 0; 106 107 switch (attach_flags) { 108 case BPF_F_ALLOW_MULTI: 109 attach_flags_str = "multi"; 110 break; 111 case BPF_F_ALLOW_OVERRIDE: 112 attach_flags_str = "override"; 113 break; 114 case 0: 115 attach_flags_str = ""; 116 break; 117 default: 118 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags); 119 attach_flags_str = buf; 120 } 121 122 for (iter = 0; iter < prog_cnt; iter++) 123 show_bpf_prog(prog_ids[iter], attach_type_strings[type], 124 attach_flags_str); 125 126 return 0; 127 } 128 129 static int do_show(int argc, char **argv) 130 { 131 enum bpf_attach_type type; 132 int cgroup_fd; 133 int ret = -1; 134 135 if (argc < 1) { 136 p_err("too few parameters for cgroup show"); 137 goto exit; 138 } else if (argc > 1) { 139 p_err("too many parameters for cgroup show"); 140 goto exit; 141 } 142 143 cgroup_fd = open(argv[0], O_RDONLY); 144 if (cgroup_fd < 0) { 145 p_err("can't open cgroup %s", argv[1]); 146 goto exit; 147 } 148 149 if (json_output) 150 jsonw_start_array(json_wtr); 151 else 152 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType", 153 "AttachFlags", "Name"); 154 155 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { 156 /* 157 * Not all attach types may be supported, so it's expected, 158 * that some requests will fail. 159 * If we were able to get the show for at least one 160 * attach type, let's return 0. 161 */ 162 if (show_attached_bpf_progs(cgroup_fd, type) == 0) 163 ret = 0; 164 } 165 166 if (json_output) 167 jsonw_end_array(json_wtr); 168 169 close(cgroup_fd); 170 exit: 171 return ret; 172 } 173 174 static int do_attach(int argc, char **argv) 175 { 176 enum bpf_attach_type attach_type; 177 int cgroup_fd, prog_fd; 178 int attach_flags = 0; 179 int ret = -1; 180 int i; 181 182 if (argc < 4) { 183 p_err("too few parameters for cgroup attach"); 184 goto exit; 185 } 186 187 cgroup_fd = open(argv[0], O_RDONLY); 188 if (cgroup_fd < 0) { 189 p_err("can't open cgroup %s", argv[1]); 190 goto exit; 191 } 192 193 attach_type = parse_attach_type(argv[1]); 194 if (attach_type == __MAX_BPF_ATTACH_TYPE) { 195 p_err("invalid attach type"); 196 goto exit_cgroup; 197 } 198 199 argc -= 2; 200 argv = &argv[2]; 201 prog_fd = prog_parse_fd(&argc, &argv); 202 if (prog_fd < 0) 203 goto exit_cgroup; 204 205 for (i = 0; i < argc; i++) { 206 if (is_prefix(argv[i], "multi")) { 207 attach_flags |= BPF_F_ALLOW_MULTI; 208 } else if (is_prefix(argv[i], "override")) { 209 attach_flags |= BPF_F_ALLOW_OVERRIDE; 210 } else { 211 p_err("unknown option: %s", argv[i]); 212 goto exit_cgroup; 213 } 214 } 215 216 if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) { 217 p_err("failed to attach program"); 218 goto exit_prog; 219 } 220 221 if (json_output) 222 jsonw_null(json_wtr); 223 224 ret = 0; 225 226 exit_prog: 227 close(prog_fd); 228 exit_cgroup: 229 close(cgroup_fd); 230 exit: 231 return ret; 232 } 233 234 static int do_detach(int argc, char **argv) 235 { 236 enum bpf_attach_type attach_type; 237 int prog_fd, cgroup_fd; 238 int ret = -1; 239 240 if (argc < 4) { 241 p_err("too few parameters for cgroup detach"); 242 goto exit; 243 } 244 245 cgroup_fd = open(argv[0], O_RDONLY); 246 if (cgroup_fd < 0) { 247 p_err("can't open cgroup %s", argv[1]); 248 goto exit; 249 } 250 251 attach_type = parse_attach_type(argv[1]); 252 if (attach_type == __MAX_BPF_ATTACH_TYPE) { 253 p_err("invalid attach type"); 254 goto exit_cgroup; 255 } 256 257 argc -= 2; 258 argv = &argv[2]; 259 prog_fd = prog_parse_fd(&argc, &argv); 260 if (prog_fd < 0) 261 goto exit_cgroup; 262 263 if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) { 264 p_err("failed to detach program"); 265 goto exit_prog; 266 } 267 268 if (json_output) 269 jsonw_null(json_wtr); 270 271 ret = 0; 272 273 exit_prog: 274 close(prog_fd); 275 exit_cgroup: 276 close(cgroup_fd); 277 exit: 278 return ret; 279 } 280 281 static int do_help(int argc, char **argv) 282 { 283 if (json_output) { 284 jsonw_null(json_wtr); 285 return 0; 286 } 287 288 fprintf(stderr, 289 "Usage: %s %s { show | list } CGROUP\n" 290 " %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" 291 " %s %s detach CGROUP ATTACH_TYPE PROG\n" 292 " %s %s help\n" 293 "\n" 294 HELP_SPEC_ATTACH_TYPES "\n" 295 " " HELP_SPEC_ATTACH_FLAGS "\n" 296 " " HELP_SPEC_PROGRAM "\n" 297 " " HELP_SPEC_OPTIONS "\n" 298 "", 299 bin_name, argv[-2], bin_name, argv[-2], 300 bin_name, argv[-2], bin_name, argv[-2]); 301 302 return 0; 303 } 304 305 static const struct cmd cmds[] = { 306 { "show", do_show }, 307 { "list", do_show }, 308 { "attach", do_attach }, 309 { "detach", do_detach }, 310 { "help", do_help }, 311 { 0 } 312 }; 313 314 int do_cgroup(int argc, char **argv) 315 { 316 return cmd_select(cmds, argc, argv, do_help); 317 } 318