root/tools/bpf/bpftool/net.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_attach_type
  2. dump_link_nlmsg
  3. dump_class_qdisc_nlmsg
  4. dump_filter_nlmsg
  5. show_dev_tc_bpf
  6. query_flow_dissector
  7. net_parse_dev
  8. do_attach_detach_xdp
  9. do_attach
  10. do_detach
  11. do_show
  12. do_help
  13. do_net

   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 }

/* [<][>][^][v][top][bottom][index][help] */