linux/tools/bpf/bpftool/common.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017-2018 Netronome Systems, Inc.
   3 *
   4 * This software is dual licensed under the GNU General License Version 2,
   5 * June 1991 as shown in the file COPYING in the top-level directory of this
   6 * source tree or the BSD 2-Clause License provided below.  You have the
   7 * option to license this software under the complete terms of either license.
   8 *
   9 * The BSD 2-Clause License:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      1. Redistributions of source code must retain the above
  16 *         copyright notice, this list of conditions and the following
  17 *         disclaimer.
  18 *
  19 *      2. Redistributions in binary form must reproduce the above
  20 *         copyright notice, this list of conditions and the following
  21 *         disclaimer in the documentation and/or other materials
  22 *         provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34/* Author: Jakub Kicinski <kubakici@wp.pl> */
  35
  36#include <ctype.h>
  37#include <errno.h>
  38#include <fcntl.h>
  39#include <fts.h>
  40#include <libgen.h>
  41#include <mntent.h>
  42#include <stdbool.h>
  43#include <stdio.h>
  44#include <stdlib.h>
  45#include <string.h>
  46#include <unistd.h>
  47#include <linux/limits.h>
  48#include <linux/magic.h>
  49#include <net/if.h>
  50#include <sys/mount.h>
  51#include <sys/stat.h>
  52#include <sys/types.h>
  53#include <sys/vfs.h>
  54
  55#include <bpf.h>
  56
  57#include "main.h"
  58
  59#ifndef BPF_FS_MAGIC
  60#define BPF_FS_MAGIC            0xcafe4a11
  61#endif
  62
  63void p_err(const char *fmt, ...)
  64{
  65        va_list ap;
  66
  67        va_start(ap, fmt);
  68        if (json_output) {
  69                jsonw_start_object(json_wtr);
  70                jsonw_name(json_wtr, "error");
  71                jsonw_vprintf_enquote(json_wtr, fmt, ap);
  72                jsonw_end_object(json_wtr);
  73        } else {
  74                fprintf(stderr, "Error: ");
  75                vfprintf(stderr, fmt, ap);
  76                fprintf(stderr, "\n");
  77        }
  78        va_end(ap);
  79}
  80
  81void p_info(const char *fmt, ...)
  82{
  83        va_list ap;
  84
  85        if (json_output)
  86                return;
  87
  88        va_start(ap, fmt);
  89        vfprintf(stderr, fmt, ap);
  90        fprintf(stderr, "\n");
  91        va_end(ap);
  92}
  93
  94static bool is_bpffs(char *path)
  95{
  96        struct statfs st_fs;
  97
  98        if (statfs(path, &st_fs) < 0)
  99                return false;
 100
 101        return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
 102}
 103
 104static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
 105{
 106        bool bind_done = false;
 107
 108        while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
 109                if (errno != EINVAL || bind_done) {
 110                        snprintf(buff, bufflen,
 111                                 "mount --make-private %s failed: %s",
 112                                 target, strerror(errno));
 113                        return -1;
 114                }
 115
 116                if (mount(target, target, "none", MS_BIND, NULL)) {
 117                        snprintf(buff, bufflen,
 118                                 "mount --bind %s %s failed: %s",
 119                                 target, target, strerror(errno));
 120                        return -1;
 121                }
 122
 123                bind_done = true;
 124        }
 125
 126        if (mount("bpf", target, "bpf", 0, "mode=0700")) {
 127                snprintf(buff, bufflen, "mount -t bpf bpf %s failed: %s",
 128                         target, strerror(errno));
 129                return -1;
 130        }
 131
 132        return 0;
 133}
 134
 135int open_obj_pinned(char *path)
 136{
 137        int fd;
 138
 139        fd = bpf_obj_get(path);
 140        if (fd < 0) {
 141                p_err("bpf obj get (%s): %s", path,
 142                      errno == EACCES && !is_bpffs(dirname(path)) ?
 143                    "directory not in bpf file system (bpffs)" :
 144                    strerror(errno));
 145                return -1;
 146        }
 147
 148        return fd;
 149}
 150
 151int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
 152{
 153        enum bpf_obj_type type;
 154        int fd;
 155
 156        fd = open_obj_pinned(path);
 157        if (fd < 0)
 158                return -1;
 159
 160        type = get_fd_type(fd);
 161        if (type < 0) {
 162                close(fd);
 163                return type;
 164        }
 165        if (type != exp_type) {
 166                p_err("incorrect object type: %s", get_fd_type_name(type));
 167                close(fd);
 168                return -1;
 169        }
 170
 171        return fd;
 172}
 173
 174int do_pin_fd(int fd, const char *name)
 175{
 176        char err_str[ERR_MAX_LEN];
 177        char *file;
 178        char *dir;
 179        int err = 0;
 180
 181        err = bpf_obj_pin(fd, name);
 182        if (!err)
 183                goto out;
 184
 185        file = malloc(strlen(name) + 1);
 186        strcpy(file, name);
 187        dir = dirname(file);
 188
 189        if (errno != EPERM || is_bpffs(dir)) {
 190                p_err("can't pin the object (%s): %s", name, strerror(errno));
 191                goto out_free;
 192        }
 193
 194        /* Attempt to mount bpffs, then retry pinning. */
 195        err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
 196        if (!err) {
 197                err = bpf_obj_pin(fd, name);
 198                if (err)
 199                        p_err("can't pin the object (%s): %s", name,
 200                              strerror(errno));
 201        } else {
 202                err_str[ERR_MAX_LEN - 1] = '\0';
 203                p_err("can't mount BPF file system to pin the object (%s): %s",
 204                      name, err_str);
 205        }
 206
 207out_free:
 208        free(file);
 209out:
 210        return err;
 211}
 212
 213int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
 214{
 215        unsigned int id;
 216        char *endptr;
 217        int err;
 218        int fd;
 219
 220        if (argc < 3) {
 221                p_err("too few arguments, id ID and FILE path is required");
 222                return -1;
 223        } else if (argc > 3) {
 224                p_err("too many arguments");
 225                return -1;
 226        }
 227
 228        if (!is_prefix(*argv, "id")) {
 229                p_err("expected 'id' got %s", *argv);
 230                return -1;
 231        }
 232        NEXT_ARG();
 233
 234        id = strtoul(*argv, &endptr, 0);
 235        if (*endptr) {
 236                p_err("can't parse %s as ID", *argv);
 237                return -1;
 238        }
 239        NEXT_ARG();
 240
 241        fd = get_fd_by_id(id);
 242        if (fd < 0) {
 243                p_err("can't get prog by id (%u): %s", id, strerror(errno));
 244                return -1;
 245        }
 246
 247        err = do_pin_fd(fd, *argv);
 248
 249        close(fd);
 250        return err;
 251}
 252
 253const char *get_fd_type_name(enum bpf_obj_type type)
 254{
 255        static const char * const names[] = {
 256                [BPF_OBJ_UNKNOWN]       = "unknown",
 257                [BPF_OBJ_PROG]          = "prog",
 258                [BPF_OBJ_MAP]           = "map",
 259        };
 260
 261        if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
 262                return names[BPF_OBJ_UNKNOWN];
 263
 264        return names[type];
 265}
 266
 267int get_fd_type(int fd)
 268{
 269        char path[PATH_MAX];
 270        char buf[512];
 271        ssize_t n;
 272
 273        snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
 274
 275        n = readlink(path, buf, sizeof(buf));
 276        if (n < 0) {
 277                p_err("can't read link type: %s", strerror(errno));
 278                return -1;
 279        }
 280        if (n == sizeof(path)) {
 281                p_err("can't read link type: path too long!");
 282                return -1;
 283        }
 284
 285        if (strstr(buf, "bpf-map"))
 286                return BPF_OBJ_MAP;
 287        else if (strstr(buf, "bpf-prog"))
 288                return BPF_OBJ_PROG;
 289
 290        return BPF_OBJ_UNKNOWN;
 291}
 292
 293char *get_fdinfo(int fd, const char *key)
 294{
 295        char path[PATH_MAX];
 296        char *line = NULL;
 297        size_t line_n = 0;
 298        ssize_t n;
 299        FILE *fdi;
 300
 301        snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);
 302
 303        fdi = fopen(path, "r");
 304        if (!fdi) {
 305                p_err("can't open fdinfo: %s", strerror(errno));
 306                return NULL;
 307        }
 308
 309        while ((n = getline(&line, &line_n, fdi))) {
 310                char *value;
 311                int len;
 312
 313                if (!strstr(line, key))
 314                        continue;
 315
 316                fclose(fdi);
 317
 318                value = strchr(line, '\t');
 319                if (!value || !value[1]) {
 320                        p_err("malformed fdinfo!?");
 321                        free(line);
 322                        return NULL;
 323                }
 324                value++;
 325
 326                len = strlen(value);
 327                memmove(line, value, len);
 328                line[len - 1] = '\0';
 329
 330                return line;
 331        }
 332
 333        p_err("key '%s' not found in fdinfo", key);
 334        free(line);
 335        fclose(fdi);
 336        return NULL;
 337}
 338
 339void print_data_json(uint8_t *data, size_t len)
 340{
 341        unsigned int i;
 342
 343        jsonw_start_array(json_wtr);
 344        for (i = 0; i < len; i++)
 345                jsonw_printf(json_wtr, "%d", data[i]);
 346        jsonw_end_array(json_wtr);
 347}
 348
 349void print_hex_data_json(uint8_t *data, size_t len)
 350{
 351        unsigned int i;
 352
 353        jsonw_start_array(json_wtr);
 354        for (i = 0; i < len; i++)
 355                jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
 356        jsonw_end_array(json_wtr);
 357}
 358
 359int build_pinned_obj_table(struct pinned_obj_table *tab,
 360                           enum bpf_obj_type type)
 361{
 362        struct bpf_prog_info pinned_info = {};
 363        struct pinned_obj *obj_node = NULL;
 364        __u32 len = sizeof(pinned_info);
 365        struct mntent *mntent = NULL;
 366        enum bpf_obj_type objtype;
 367        FILE *mntfile = NULL;
 368        FTSENT *ftse = NULL;
 369        FTS *fts = NULL;
 370        int fd, err;
 371
 372        mntfile = setmntent("/proc/mounts", "r");
 373        if (!mntfile)
 374                return -1;
 375
 376        while ((mntent = getmntent(mntfile))) {
 377                char *path[] = { mntent->mnt_dir, NULL };
 378
 379                if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
 380                        continue;
 381
 382                fts = fts_open(path, 0, NULL);
 383                if (!fts)
 384                        continue;
 385
 386                while ((ftse = fts_read(fts))) {
 387                        if (!(ftse->fts_info & FTS_F))
 388                                continue;
 389                        fd = open_obj_pinned(ftse->fts_path);
 390                        if (fd < 0)
 391                                continue;
 392
 393                        objtype = get_fd_type(fd);
 394                        if (objtype != type) {
 395                                close(fd);
 396                                continue;
 397                        }
 398                        memset(&pinned_info, 0, sizeof(pinned_info));
 399                        err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len);
 400                        if (err) {
 401                                close(fd);
 402                                continue;
 403                        }
 404
 405                        obj_node = malloc(sizeof(*obj_node));
 406                        if (!obj_node) {
 407                                close(fd);
 408                                fts_close(fts);
 409                                fclose(mntfile);
 410                                return -1;
 411                        }
 412
 413                        memset(obj_node, 0, sizeof(*obj_node));
 414                        obj_node->id = pinned_info.id;
 415                        obj_node->path = strdup(ftse->fts_path);
 416                        hash_add(tab->table, &obj_node->hash, obj_node->id);
 417
 418                        close(fd);
 419                }
 420                fts_close(fts);
 421        }
 422        fclose(mntfile);
 423        return 0;
 424}
 425
 426void delete_pinned_obj_table(struct pinned_obj_table *tab)
 427{
 428        struct pinned_obj *obj;
 429        struct hlist_node *tmp;
 430        unsigned int bkt;
 431
 432        hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
 433                hash_del(&obj->hash);
 434                free(obj->path);
 435                free(obj);
 436        }
 437}
 438
 439unsigned int get_page_size(void)
 440{
 441        static int result;
 442
 443        if (!result)
 444                result = getpagesize();
 445        return result;
 446}
 447
 448unsigned int get_possible_cpus(void)
 449{
 450        static unsigned int result;
 451        char buf[128];
 452        long int n;
 453        char *ptr;
 454        int fd;
 455
 456        if (result)
 457                return result;
 458
 459        fd = open("/sys/devices/system/cpu/possible", O_RDONLY);
 460        if (fd < 0) {
 461                p_err("can't open sysfs possible cpus");
 462                exit(-1);
 463        }
 464
 465        n = read(fd, buf, sizeof(buf));
 466        if (n < 2) {
 467                p_err("can't read sysfs possible cpus");
 468                exit(-1);
 469        }
 470        close(fd);
 471
 472        if (n == sizeof(buf)) {
 473                p_err("read sysfs possible cpus overflow");
 474                exit(-1);
 475        }
 476
 477        ptr = buf;
 478        n = 0;
 479        while (*ptr && *ptr != '\n') {
 480                unsigned int a, b;
 481
 482                if (sscanf(ptr, "%u-%u", &a, &b) == 2) {
 483                        n += b - a + 1;
 484
 485                        ptr = strchr(ptr, '-') + 1;
 486                } else if (sscanf(ptr, "%u", &a) == 1) {
 487                        n++;
 488                } else {
 489                        assert(0);
 490                }
 491
 492                while (isdigit(*ptr))
 493                        ptr++;
 494                if (*ptr == ',')
 495                        ptr++;
 496        }
 497
 498        result = n;
 499
 500        return result;
 501}
 502
 503static char *
 504ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
 505{
 506        struct stat st;
 507        int err;
 508
 509        err = stat("/proc/self/ns/net", &st);
 510        if (err) {
 511                p_err("Can't stat /proc/self: %s", strerror(errno));
 512                return NULL;
 513        }
 514
 515        if (st.st_dev != ns_dev || st.st_ino != ns_ino)
 516                return NULL;
 517
 518        return if_indextoname(ifindex, buf);
 519}
 520
 521static int read_sysfs_hex_int(char *path)
 522{
 523        char vendor_id_buf[8];
 524        int len;
 525        int fd;
 526
 527        fd = open(path, O_RDONLY);
 528        if (fd < 0) {
 529                p_err("Can't open %s: %s", path, strerror(errno));
 530                return -1;
 531        }
 532
 533        len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
 534        close(fd);
 535        if (len < 0) {
 536                p_err("Can't read %s: %s", path, strerror(errno));
 537                return -1;
 538        }
 539        if (len >= (int)sizeof(vendor_id_buf)) {
 540                p_err("Value in %s too long", path);
 541                return -1;
 542        }
 543
 544        vendor_id_buf[len] = 0;
 545
 546        return strtol(vendor_id_buf, NULL, 0);
 547}
 548
 549static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
 550{
 551        char full_path[64];
 552
 553        snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
 554                 devname, entry_name);
 555
 556        return read_sysfs_hex_int(full_path);
 557}
 558
 559const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino)
 560{
 561        char devname[IF_NAMESIZE];
 562        int vendor_id;
 563        int device_id;
 564
 565        if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
 566                p_err("Can't get net device name for ifindex %d: %s", ifindex,
 567                      strerror(errno));
 568                return NULL;
 569        }
 570
 571        vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
 572        if (vendor_id < 0) {
 573                p_err("Can't get device vendor id for %s", devname);
 574                return NULL;
 575        }
 576
 577        switch (vendor_id) {
 578        case 0x19ee:
 579                device_id = read_sysfs_netdev_hex_int(devname, "device");
 580                if (device_id != 0x4000 &&
 581                    device_id != 0x6000 &&
 582                    device_id != 0x6003)
 583                        p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
 584                return "NFP-6xxx";
 585        default:
 586                p_err("Can't get bfd arch name for device vendor id 0x%04x",
 587                      vendor_id);
 588                return NULL;
 589        }
 590}
 591
 592void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
 593{
 594        char name[IF_NAMESIZE];
 595
 596        if (!ifindex)
 597                return;
 598
 599        printf(" dev ");
 600        if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
 601                printf("%s", name);
 602        else
 603                printf("ifindex %u ns_dev %llu ns_ino %llu",
 604                       ifindex, ns_dev, ns_inode);
 605}
 606
 607void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
 608{
 609        char name[IF_NAMESIZE];
 610
 611        if (!ifindex)
 612                return;
 613
 614        jsonw_name(json_wtr, "dev");
 615        jsonw_start_object(json_wtr);
 616        jsonw_uint_field(json_wtr, "ifindex", ifindex);
 617        jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
 618        jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
 619        if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
 620                jsonw_string_field(json_wtr, "ifname", name);
 621        jsonw_end_object(json_wtr);
 622}
 623