1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 // Copyright (C) 2018 Facebook 3 4 #define _GNU_SOURCE 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <libbpf.h> 11 #include <net/if.h> 12 #include <linux/if.h> 13 #include <linux/rtnetlink.h> 14 #include <linux/tc_act/tc_bpf.h> 15 #include <sys/socket.h> 16 #include <sys/stat.h> 17 #include <sys/types.h> 18 19 #include <bpf.h> 20 #include <nlattr.h> 21 #include "main.h" 22 #include "netlink_dumper.h" 23 24 struct ip_devname_ifindex { 25 char devname[64]; 26 int ifindex; 27 }; 28 29 struct bpf_netdev_t { 30 struct ip_devname_ifindex *devices; 31 int used_len; 32 int array_len; 33 int filter_idx; 34 }; 35 36 struct tc_kind_handle { 37 char kind[64]; 38 int handle; 39 }; 40 41 struct bpf_tcinfo_t { 42 struct tc_kind_handle *handle_array; 43 int used_len; 44 int array_len; 45 bool is_qdisc; 46 }; 47 48 struct bpf_filter_t { 49 const char *kind; 50 const char *devname; 51 int ifindex; 52 }; 53 54 struct bpf_attach_info { 55 __u32 flow_dissector_id; 56 }; 57 58 enum net_attach_type { 59 NET_ATTACH_TYPE_XDP, 60 NET_ATTACH_TYPE_XDP_GENERIC, 61 NET_ATTACH_TYPE_XDP_DRIVER, 62 NET_ATTACH_TYPE_XDP_OFFLOAD, 63 }; 64 65 static const char * const attach_type_strings[] = { 66 [NET_ATTACH_TYPE_XDP] = "xdp", 67 [NET_ATTACH_TYPE_XDP_GENERIC] = "xdpgeneric", 68 [NET_ATTACH_TYPE_XDP_DRIVER] = "xdpdrv", 69 [NET_ATTACH_TYPE_XDP_OFFLOAD] = "xdpoffload", 70 }; 71 72 const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings); 73 74 static enum net_attach_type parse_attach_type(const char *str) 75 { 76 enum net_attach_type type; 77 78 for (type = 0; type < net_attach_type_size; type++) { 79 if (attach_type_strings[type] && 80 is_prefix(str, attach_type_strings[type])) 81 return type; 82 } 83 84 return net_attach_type_size; 85 } 86 87 static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) 88 { 89 struct bpf_netdev_t *netinfo = cookie; 90 struct ifinfomsg *ifinfo = msg; 91 92 if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index) 93 return 0; 94 95 if (netinfo->used_len == netinfo->array_len) { 96 netinfo->devices = realloc(netinfo->devices, 97 (netinfo->array_len + 16) * 98 sizeof(struct ip_devname_ifindex)); 99 if (!netinfo->devices) 100 return -ENOMEM; 101 102 netinfo->array_len += 16; 103 } 104 netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index; 105 snprintf(netinfo->devices[netinfo->used_len].devname, 106 sizeof(netinfo->devices[netinfo->used_len].devname), 107 "%s", 108 tb[IFLA_IFNAME] 109 ? libbpf_nla_getattr_str(tb[IFLA_IFNAME]) 110 : ""); 111 netinfo->used_len++; 112 113 return do_xdp_dump(ifinfo, tb); 114 } 115 116 static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb) 117 { 118 struct bpf_tcinfo_t *tcinfo = cookie; 119 struct tcmsg *info = msg; 120 121 if (tcinfo->is_qdisc) { 122 /* skip clsact qdisc */ 123 if (tb[TCA_KIND] && 124 strcmp(libbpf_nla_data(tb[TCA_KIND]), "clsact") == 0) 125 return 0; 126 if (info->tcm_handle == 0) 127 return 0; 128 } 129 130 if (tcinfo->used_len == tcinfo->array_len) { 131 tcinfo->handle_array = realloc(tcinfo->handle_array, 132 (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle)); 133 if (!tcinfo->handle_array) 134 return -ENOMEM; 135 136 tcinfo->array_len += 16; 137 } 138 tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle; 139 snprintf(tcinfo->handle_array[tcinfo->used_len].kind, 140 sizeof(tcinfo->handle_array[tcinfo->used_len].kind), 141 "%s", 142 tb[TCA_KIND] 143 ? libbpf_nla_getattr_str(tb[TCA_KIND]) 144 : "unknown"); 145 tcinfo->used_len++; 146 147 return 0; 148 } 149 150 static int dump_filter_nlmsg(void *cookie, void *msg, struct nlattr **tb) 151 { 152 const struct bpf_filter_t *filter_info = cookie; 153 154 return do_filter_dump((struct tcmsg *)msg, tb, filter_info->kind, 155 filter_info->devname, filter_info->ifindex); 156 } 157 158 static int show_dev_tc_bpf(int sock, unsigned int nl_pid, 159 struct ip_devname_ifindex *dev) 160 { 161 struct bpf_filter_t filter_info; 162 struct bpf_tcinfo_t tcinfo; 163 int i, handle, ret = 0; 164 165 tcinfo.handle_array = NULL; 166 tcinfo.used_len = 0; 167 tcinfo.array_len = 0; 168 169 tcinfo.is_qdisc = false; 170 ret = libbpf_nl_get_class(sock, nl_pid, dev->ifindex, 171 dump_class_qdisc_nlmsg, &tcinfo); 172 if (ret) 173 goto out; 174 175 tcinfo.is_qdisc = true; 176 ret = libbpf_nl_get_qdisc(sock, nl_pid, dev->ifindex, 177 dump_class_qdisc_nlmsg, &tcinfo); 178 if (ret) 179 goto out; 180 181 filter_info.devname = dev->devname; 182 filter_info.ifindex = dev->ifindex; 183 for (i = 0; i < tcinfo.used_len; i++) { 184 filter_info.kind = tcinfo.handle_array[i].kind; 185 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, 186 tcinfo.handle_array[i].handle, 187 dump_filter_nlmsg, &filter_info); 188 if (ret) 189 goto out; 190 } 191 192 /* root, ingress and egress handle */ 193 handle = TC_H_ROOT; 194 filter_info.kind = "root"; 195 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, 196 dump_filter_nlmsg, &filter_info); 197 if (ret) 198 goto out; 199 200 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 201 filter_info.kind = "clsact/ingress"; 202 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, 203 dump_filter_nlmsg, &filter_info); 204 if (ret) 205 goto out; 206 207 handle = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); 208 filter_info.kind = "clsact/egress"; 209 ret = libbpf_nl_get_filter(sock, nl_pid, dev->ifindex, handle, 210 dump_filter_nlmsg, &filter_info); 211 if (ret) 212 goto out; 213 214 out: 215 free(tcinfo.handle_array); 216 return 0; 217 } 218 219 static int query_flow_dissector(struct bpf_attach_info *attach_info) 220 { 221 __u32 attach_flags; 222 __u32 prog_ids[1]; 223 __u32 prog_cnt; 224 int err; 225 int fd; 226 227 fd = open("/proc/self/ns/net", O_RDONLY); 228 if (fd < 0) { 229 p_err("can't open /proc/self/ns/net: %s", 230 strerror(errno)); 231 return -1; 232 } 233 prog_cnt = ARRAY_SIZE(prog_ids); 234 err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0, 235 &attach_flags, prog_ids, &prog_cnt); 236 close(fd); 237 if (err) { 238 if (errno == EINVAL) { 239 /* Older kernel's don't support querying 240 * flow dissector programs. 241 */ 242 errno = 0; 243 return 0; 244 } 245 p_err("can't query prog: %s", strerror(errno)); 246 return -1; 247 } 248 249 if (prog_cnt == 1) 250 attach_info->flow_dissector_id = prog_ids[0]; 251 252 return 0; 253 } 254 255 static int net_parse_dev(int *argc, char ***argv) 256 { 257 int ifindex; 258 259 if (is_prefix(**argv, "dev")) { 260 NEXT_ARGP(); 261 262 ifindex = if_nametoindex(**argv); 263 if (!ifindex) 264 p_err("invalid devname %s", **argv); 265 266 NEXT_ARGP(); 267 } else { 268 p_err("expected 'dev', got: '%s'?", **argv); 269 return -1; 270 } 271 272 return ifindex; 273 } 274 275 static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type, 276 int ifindex, bool overwrite) 277 { 278 __u32 flags = 0; 279 280 if (!overwrite) 281 flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 282 if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC) 283 flags |= XDP_FLAGS_SKB_MODE; 284 if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER) 285 flags |= XDP_FLAGS_DRV_MODE; 286 if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD) 287 flags |= XDP_FLAGS_HW_MODE; 288 289 return bpf_set_link_xdp_fd(ifindex, progfd, flags); 290 } 291 292 static int do_attach(int argc, char **argv) 293 { 294 enum net_attach_type attach_type; 295 int progfd, ifindex, err = 0; 296 bool overwrite = false; 297 298 /* parse attach args */ 299 if (!REQ_ARGS(5)) 300 return -EINVAL; 301 302 attach_type = parse_attach_type(*argv); 303 if (attach_type == net_attach_type_size) { 304 p_err("invalid net attach/detach type: %s", *argv); 305 return -EINVAL; 306 } 307 NEXT_ARG(); 308 309 progfd = prog_parse_fd(&argc, &argv); 310 if (progfd < 0) 311 return -EINVAL; 312 313 ifindex = net_parse_dev(&argc, &argv); 314 if (ifindex < 1) { 315 close(progfd); 316 return -EINVAL; 317 } 318 319 if (argc) { 320 if (is_prefix(*argv, "overwrite")) { 321 overwrite = true; 322 } else { 323 p_err("expected 'overwrite', got: '%s'?", *argv); 324 close(progfd); 325 return -EINVAL; 326 } 327 } 328 329 /* attach xdp prog */ 330 if (is_prefix("xdp", attach_type_strings[attach_type])) 331 err = do_attach_detach_xdp(progfd, attach_type, ifindex, 332 overwrite); 333 334 if (err < 0) { 335 p_err("interface %s attach failed: %s", 336 attach_type_strings[attach_type], strerror(-err)); 337 return err; 338 } 339 340 if (json_output) 341 jsonw_null(json_wtr); 342 343 return 0; 344 } 345 346 static int do_detach(int argc, char **argv) 347 { 348 enum net_attach_type attach_type; 349 int progfd, ifindex, err = 0; 350 351 /* parse detach args */ 352 if (!REQ_ARGS(3)) 353 return -EINVAL; 354 355 attach_type = parse_attach_type(*argv); 356 if (attach_type == net_attach_type_size) { 357 p_err("invalid net attach/detach type: %s", *argv); 358 return -EINVAL; 359 } 360 NEXT_ARG(); 361 362 ifindex = net_parse_dev(&argc, &argv); 363 if (ifindex < 1) 364 return -EINVAL; 365 366 /* detach xdp prog */ 367 progfd = -1; 368 if (is_prefix("xdp", attach_type_strings[attach_type])) 369 err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL); 370 371 if (err < 0) { 372 p_err("interface %s detach failed: %s", 373 attach_type_strings[attach_type], strerror(-err)); 374 return err; 375 } 376 377 if (json_output) 378 jsonw_null(json_wtr); 379 380 return 0; 381 } 382 383 static int do_show(int argc, char **argv) 384 { 385 struct bpf_attach_info attach_info = {}; 386 int i, sock, ret, filter_idx = -1; 387 struct bpf_netdev_t dev_array; 388 unsigned int nl_pid; 389 char err_buf[256]; 390 391 if (argc == 2) { 392 filter_idx = net_parse_dev(&argc, &argv); 393 if (filter_idx < 1) 394 return -1; 395 } else if (argc != 0) { 396 usage(); 397 } 398 399 ret = query_flow_dissector(&attach_info); 400 if (ret) 401 return -1; 402 403 sock = libbpf_netlink_open(&nl_pid); 404 if (sock < 0) { 405 fprintf(stderr, "failed to open netlink sock\n"); 406 return -1; 407 } 408 409 dev_array.devices = NULL; 410 dev_array.used_len = 0; 411 dev_array.array_len = 0; 412 dev_array.filter_idx = filter_idx; 413 414 if (json_output) 415 jsonw_start_array(json_wtr); 416 NET_START_OBJECT; 417 NET_START_ARRAY("xdp", "%s:\n"); 418 ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); 419 NET_END_ARRAY("\n"); 420 421 if (!ret) { 422 NET_START_ARRAY("tc", "%s:\n"); 423 for (i = 0; i < dev_array.used_len; i++) { 424 ret = show_dev_tc_bpf(sock, nl_pid, 425 &dev_array.devices[i]); 426 if (ret) 427 break; 428 } 429 NET_END_ARRAY("\n"); 430 } 431 432 NET_START_ARRAY("flow_dissector", "%s:\n"); 433 if (attach_info.flow_dissector_id > 0) 434 NET_DUMP_UINT("id", "id %u", attach_info.flow_dissector_id); 435 NET_END_ARRAY("\n"); 436 437 NET_END_OBJECT; 438 if (json_output) 439 jsonw_end_array(json_wtr); 440 441 if (ret) { 442 if (json_output) 443 jsonw_null(json_wtr); 444 libbpf_strerror(ret, err_buf, sizeof(err_buf)); 445 fprintf(stderr, "Error: %s\n", err_buf); 446 } 447 free(dev_array.devices); 448 close(sock); 449 return ret; 450 } 451 452 static int do_help(int argc, char **argv) 453 { 454 if (json_output) { 455 jsonw_null(json_wtr); 456 return 0; 457 } 458 459 fprintf(stderr, 460 "Usage: %s %s { show | list } [dev <devname>]\n" 461 " %s %s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n" 462 " %s %s detach ATTACH_TYPE dev <devname>\n" 463 " %s %s help\n" 464 "\n" 465 " " HELP_SPEC_PROGRAM "\n" 466 " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n" 467 "\n" 468 "Note: Only xdp and tc attachments are supported now.\n" 469 " For progs attached to cgroups, use \"bpftool cgroup\"\n" 470 " to dump program attachments. For program types\n" 471 " sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n" 472 " consult iproute2.\n", 473 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 474 bin_name, argv[-2]); 475 476 return 0; 477 } 478 479 static const struct cmd cmds[] = { 480 { "show", do_show }, 481 { "list", do_show }, 482 { "attach", do_attach }, 483 { "detach", do_detach }, 484 { "help", do_help }, 485 { 0 } 486 }; 487 488 int do_net(int argc, char **argv) 489 { 490 return cmd_select(cmds, argc, argv, do_help); 491 } 492