iproute2/lib/bpf_libbpf.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2/*
   3 * bpf_libbpf.c         BPF code relay on libbpf
   4 * Authors:             Hangbin Liu <haliu@redhat.com>
   5 *
   6 */
   7
   8#include <stdio.h>
   9#include <stdlib.h>
  10#include <unistd.h>
  11#include <string.h>
  12#include <stdbool.h>
  13#include <stdint.h>
  14#include <errno.h>
  15#include <fcntl.h>
  16#include <limits.h>
  17
  18#include <libelf.h>
  19#include <gelf.h>
  20
  21#include <bpf/libbpf.h>
  22#include <bpf/bpf.h>
  23
  24#include "bpf_util.h"
  25
  26static int verbose_print(enum libbpf_print_level level, const char *format, va_list args)
  27{
  28        return vfprintf(stderr, format, args);
  29}
  30
  31static int silent_print(enum libbpf_print_level level, const char *format, va_list args)
  32{
  33        if (level > LIBBPF_WARN)
  34                return 0;
  35
  36        /* Skip warning from bpf_object__init_user_maps() for legacy maps */
  37        if (strstr(format, "has unrecognized, non-zero options"))
  38                return 0;
  39
  40        return vfprintf(stderr, format, args);
  41}
  42
  43static const char *get_bpf_program__section_name(const struct bpf_program *prog)
  44{
  45#ifdef HAVE_LIBBPF_SECTION_NAME
  46        return bpf_program__section_name(prog);
  47#else
  48        return bpf_program__title(prog, false);
  49#endif
  50}
  51
  52static int create_map(const char *name, struct bpf_elf_map *map,
  53                      __u32 ifindex, int inner_fd)
  54{
  55        struct bpf_create_map_attr map_attr = {};
  56
  57        map_attr.name = name;
  58        map_attr.map_type = map->type;
  59        map_attr.map_flags = map->flags;
  60        map_attr.key_size = map->size_key;
  61        map_attr.value_size = map->size_value;
  62        map_attr.max_entries = map->max_elem;
  63        map_attr.map_ifindex = ifindex;
  64        map_attr.inner_map_fd = inner_fd;
  65
  66        return bpf_create_map_xattr(&map_attr);
  67}
  68
  69static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
  70                             struct bpf_elf_map *elf_map, int inner_fd,
  71                             bool *reuse_pin_map)
  72{
  73        char pathname[PATH_MAX];
  74        const char *map_name;
  75        bool pin_map = false;
  76        int map_fd, ret = 0;
  77
  78        map_name = bpf_map__name(map);
  79
  80        if (iproute2_is_pin_map(map_name, pathname)) {
  81                pin_map = true;
  82
  83                /* Check if there already has a pinned map */
  84                map_fd = bpf_obj_get(pathname);
  85                if (map_fd > 0) {
  86                        if (reuse_pin_map)
  87                                *reuse_pin_map = true;
  88                        close(map_fd);
  89                        return bpf_map__set_pin_path(map, pathname);
  90                }
  91        }
  92
  93        map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
  94        if (map_fd < 0) {
  95                fprintf(stderr, "create map %s failed\n", map_name);
  96                return map_fd;
  97        }
  98
  99        ret = bpf_map__reuse_fd(map, map_fd);
 100        if (ret < 0) {
 101                fprintf(stderr, "map %s reuse fd failed\n", map_name);
 102                goto err_out;
 103        }
 104
 105        if (pin_map) {
 106                ret = bpf_map__set_pin_path(map, pathname);
 107                if (ret < 0)
 108                        goto err_out;
 109        }
 110
 111        return 0;
 112err_out:
 113        close(map_fd);
 114        return ret;
 115}
 116
 117static int
 118handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
 119                         const char *inner_map_name)
 120{
 121        int inner_fd, outer_fd, inner_idx, ret = 0;
 122        struct bpf_elf_map imap, omap;
 123        struct bpf_map *outer_map;
 124        /* What's the size limit of map name? */
 125        char outer_map_name[128];
 126        bool reuse_pin_map = false;
 127
 128        /* Deal with map-in-map */
 129        if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
 130                ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
 131                if (ret < 0)
 132                        return ret;
 133
 134                inner_fd = bpf_map__fd(inner_map);
 135                outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
 136                ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
 137                if (ret < 0)
 138                        return ret;
 139
 140                if (!reuse_pin_map) {
 141                        inner_idx = imap.inner_idx;
 142                        outer_fd = bpf_map__fd(outer_map);
 143                        ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
 144                        if (ret < 0)
 145                                fprintf(stderr, "Cannot update inner_idx into outer_map\n");
 146                }
 147        }
 148
 149        return ret;
 150}
 151
 152static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj)
 153{
 154        unsigned int map_id, key_id;
 155        const char *sec_name;
 156        struct bpf_map *map;
 157        char map_name[128];
 158        int ret;
 159
 160        /* Handle iproute2 tail call */
 161        sec_name = get_bpf_program__section_name(prog);
 162        ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
 163        if (ret != 2)
 164                return -1;
 165
 166        ret = iproute2_find_map_name_by_id(map_id, map_name);
 167        if (ret < 0) {
 168                fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
 169                return ret;
 170        }
 171
 172        map = bpf_object__find_map_by_name(obj, map_name);
 173        if (!map)
 174                return -1;
 175
 176        /* Save the map here for later updating */
 177        bpf_program__set_priv(prog, map, NULL);
 178
 179        return 0;
 180}
 181
 182static int update_legacy_tail_call_maps(struct bpf_object *obj)
 183{
 184        int prog_fd, map_fd, ret = 0;
 185        unsigned int map_id, key_id;
 186        struct bpf_program *prog;
 187        const char *sec_name;
 188        struct bpf_map *map;
 189
 190        bpf_object__for_each_program(prog, obj) {
 191                map = bpf_program__priv(prog);
 192                if (!map)
 193                        continue;
 194
 195                prog_fd = bpf_program__fd(prog);
 196                if (prog_fd < 0)
 197                        continue;
 198
 199                sec_name = get_bpf_program__section_name(prog);
 200                ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
 201                if (ret != 2)
 202                        continue;
 203
 204                map_fd = bpf_map__fd(map);
 205                ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
 206                if (ret < 0) {
 207                        fprintf(stderr, "Cannot update map key for tail call!\n");
 208                        return ret;
 209                }
 210        }
 211
 212        return 0;
 213}
 214
 215static int handle_legacy_maps(struct bpf_object *obj)
 216{
 217        char pathname[PATH_MAX];
 218        struct bpf_map *map;
 219        const char *map_name;
 220        int map_fd, ret = 0;
 221
 222        bpf_object__for_each_map(map, obj) {
 223                map_name = bpf_map__name(map);
 224
 225                ret = handle_legacy_map_in_map(obj, map, map_name);
 226                if (ret)
 227                        return ret;
 228
 229                /* If it is a iproute2 legacy pin maps, just set pin path
 230                 * and let bpf_object__load() to deal with the map creation.
 231                 * We need to ignore map-in-maps which have pinned maps manually
 232                 */
 233                map_fd = bpf_map__fd(map);
 234                if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
 235                        ret = bpf_map__set_pin_path(map, pathname);
 236                        if (ret) {
 237                                fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
 238                                break;
 239                        }
 240                }
 241
 242        }
 243
 244        return ret;
 245}
 246
 247static int load_bpf_object(struct bpf_cfg_in *cfg)
 248{
 249        struct bpf_program *p, *prog = NULL;
 250        struct bpf_object *obj;
 251        char root_path[PATH_MAX];
 252        struct bpf_map *map;
 253        int prog_fd, ret = 0;
 254
 255        ret = iproute2_get_root_path(root_path, PATH_MAX);
 256        if (ret)
 257                return ret;
 258
 259        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
 260                        .relaxed_maps = true,
 261                        .pin_root_path = root_path,
 262        );
 263
 264        obj = bpf_object__open_file(cfg->object, &open_opts);
 265        if (libbpf_get_error(obj)) {
 266                fprintf(stderr, "ERROR: opening BPF object file failed\n");
 267                return -ENOENT;
 268        }
 269
 270        bpf_object__for_each_program(p, obj) {
 271                /* Only load the programs that will either be subsequently
 272                 * attached or inserted into a tail call map */
 273                if (find_legacy_tail_calls(p, obj) < 0 && cfg->section &&
 274                    strcmp(get_bpf_program__section_name(p), cfg->section)) {
 275                        ret = bpf_program__set_autoload(p, false);
 276                        if (ret)
 277                                return -EINVAL;
 278                        continue;
 279                }
 280
 281                bpf_program__set_type(p, cfg->type);
 282                bpf_program__set_ifindex(p, cfg->ifindex);
 283                if (!prog)
 284                        prog = p;
 285        }
 286
 287        bpf_object__for_each_map(map, obj) {
 288                if (!bpf_map__is_offload_neutral(map))
 289                        bpf_map__set_ifindex(map, cfg->ifindex);
 290        }
 291
 292        if (!prog) {
 293                fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
 294                return -ENOENT;
 295        }
 296
 297        /* Handle iproute2 legacy pin maps and map-in-maps */
 298        ret = handle_legacy_maps(obj);
 299        if (ret)
 300                goto unload_obj;
 301
 302        ret = bpf_object__load(obj);
 303        if (ret)
 304                goto unload_obj;
 305
 306        ret = update_legacy_tail_call_maps(obj);
 307        if (ret)
 308                goto unload_obj;
 309
 310        prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
 311        if (prog_fd < 0)
 312                ret = -errno;
 313        else
 314                cfg->prog_fd = prog_fd;
 315
 316unload_obj:
 317        /* Close obj as we don't need it */
 318        bpf_object__close(obj);
 319        return ret;
 320}
 321
 322/* Load ebpf and return prog fd */
 323int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
 324{
 325        int ret = 0;
 326
 327        if (cfg->verbose)
 328                libbpf_set_print(verbose_print);
 329        else
 330                libbpf_set_print(silent_print);
 331
 332        ret = iproute2_bpf_elf_ctx_init(cfg);
 333        if (ret < 0) {
 334                fprintf(stderr, "Cannot initialize ELF context!\n");
 335                return ret;
 336        }
 337
 338        ret = iproute2_bpf_fetch_ancillary();
 339        if (ret < 0) {
 340                fprintf(stderr, "Error fetching ELF ancillary data!\n");
 341                return ret;
 342        }
 343
 344        ret = load_bpf_object(cfg);
 345        if (ret)
 346                return ret;
 347
 348        return cfg->prog_fd;
 349}
 350