LCOV - code coverage report
Current view: top level - bpfilter/cgen/prog - link.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 12.5 % 128 16
Test Date: 2025-09-14 19:43:39 Functions: 18.2 % 11 2

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0-only
       2              : /*
       3              :  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
       4              :  */
       5              : 
       6              : #include "cgen/prog/link.h"
       7              : 
       8              : #include <linux/bpf.h>
       9              : #include <linux/if_link.h>
      10              : 
      11              : #include <errno.h>
      12              : #include <fcntl.h>
      13              : #include <stdio.h>
      14              : #include <stdlib.h>
      15              : #include <string.h>
      16              : #include <unistd.h>
      17              : 
      18              : #include <bpfilter/bpf.h>
      19              : #include <bpfilter/dump.h>
      20              : #include <bpfilter/flavor.h>
      21              : #include <bpfilter/helper.h>
      22              : #include <bpfilter/hook.h>
      23              : #include <bpfilter/logger.h>
      24              : #include <bpfilter/pack.h>
      25              : 
      26            3 : int bf_link_new(struct bf_link **link, const char *name)
      27              : {
      28            3 :     _free_bf_link_ struct bf_link *_link = NULL;
      29              : 
      30            3 :     bf_assert(link && name);
      31            3 :     bf_assert(name[0] != '\0');
      32              : 
      33            3 :     _link = malloc(sizeof(*_link));
      34            3 :     if (!_link)
      35              :         return -ENOMEM;
      36              : 
      37            3 :     bf_strncpy(_link->name, BPF_OBJ_NAME_LEN, name);
      38              : 
      39            3 :     _link->hookopts = NULL;
      40            3 :     _link->fd = -1;
      41              : 
      42            3 :     *link = TAKE_PTR(_link);
      43              : 
      44            3 :     return 0;
      45              : }
      46              : 
      47            0 : int bf_link_new_from_pack(struct bf_link **link, int dir_fd,
      48              :                           bf_rpack_node_t node)
      49              : {
      50            0 :     _free_bf_link_ struct bf_link *_link = NULL;
      51            0 :     _cleanup_free_ char *name = NULL;
      52              :     bf_rpack_node_t child;
      53              :     int r;
      54              : 
      55            0 :     bf_assert(link);
      56              : 
      57            0 :     r = bf_rpack_kv_str(node, "name", &name);
      58            0 :     if (r)
      59            0 :         return bf_rpack_key_err(r, "bf_link.name");
      60              : 
      61            0 :     r = bf_link_new(&_link, name);
      62            0 :     if (r)
      63            0 :         return bf_err_r(r, "failed to create bf_link from pack");
      64              : 
      65            0 :     r = bf_rpack_kv_node(node, "hookopts", &child);
      66            0 :     if (r)
      67            0 :         return bf_rpack_key_err(r, "bf_link.hookopts");
      68            0 :     if (!bf_rpack_is_nil(child)) {
      69            0 :         r = bf_hookopts_new_from_pack(&_link->hookopts, child);
      70            0 :         if (r)
      71              :             return r;
      72              :     }
      73              : 
      74            0 :     if (dir_fd != -1) {
      75            0 :         r = bf_bpf_obj_get(_link->name, dir_fd, &_link->fd);
      76            0 :         if (r) {
      77            0 :             return bf_err_r(r, "failed to open pinned BPF link '%s'",
      78              :                             _link->name);
      79              :         }
      80              :     }
      81              : 
      82            0 :     *link = TAKE_PTR(_link);
      83              : 
      84            0 :     return 0;
      85              : }
      86              : 
      87            6 : void bf_link_free(struct bf_link **link)
      88              : {
      89            6 :     bf_assert(link);
      90              : 
      91            6 :     if (!*link)
      92              :         return;
      93              : 
      94            3 :     bf_hookopts_free(&(*link)->hookopts);
      95            3 :     closep(&(*link)->fd);
      96              :     freep((void *)link);
      97              : }
      98              : 
      99            0 : int bf_link_pack(const struct bf_link *link, bf_wpack_t *pack)
     100              : {
     101            0 :     bf_assert(link);
     102            0 :     bf_assert(pack);
     103              : 
     104            0 :     bf_wpack_kv_str(pack, "name", link->name);
     105              : 
     106            0 :     if (link->hookopts) {
     107            0 :         bf_wpack_open_object(pack, "hookopts");
     108            0 :         bf_hookopts_pack(link->hookopts, pack);
     109            0 :         bf_wpack_close_object(pack);
     110              :     } else {
     111              :         bf_wpack_kv_nil(pack, "hookopts");
     112              :     }
     113              : 
     114            0 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     115              : }
     116              : 
     117            0 : void bf_link_dump(const struct bf_link *link, prefix_t *prefix)
     118              : {
     119            0 :     bf_assert(link && prefix);
     120              : 
     121            0 :     DUMP(prefix, "struct bf_link at %p", link);
     122              : 
     123            0 :     bf_dump_prefix_push(prefix);
     124            0 :     DUMP(prefix, "name: %s", link->name);
     125              : 
     126            0 :     if (link->hookopts) {
     127            0 :         DUMP(prefix, "hookopts: struct bf_hookopts *");
     128            0 :         bf_dump_prefix_push(prefix);
     129            0 :         bf_hookopts_dump(link->hookopts, prefix);
     130            0 :         bf_dump_prefix_pop(prefix);
     131              :     } else {
     132            0 :         DUMP(prefix, "hookopts: struct bf_hookopts * (NULL)");
     133              :     }
     134              : 
     135            0 :     DUMP(bf_dump_prefix_last(prefix), "fd: %d", link->fd);
     136            0 :     bf_dump_prefix_pop(prefix);
     137            0 : }
     138              : 
     139            0 : int bf_link_attach(struct bf_link *link, enum bf_hook hook,
     140              :                    struct bf_hookopts **hookopts, int prog_fd)
     141              : {
     142            0 :     bf_assert(link && hookopts);
     143              : 
     144            0 :     _cleanup_close_ int cgroup_fd = -1;
     145            0 :     struct bf_hookopts *_hookopts = *hookopts;
     146              :     int r;
     147              : 
     148            0 :     switch (bf_hook_to_flavor(hook)) {
     149            0 :     case BF_FLAVOR_XDP:
     150            0 :         r = bf_bpf_link_create(prog_fd, _hookopts->ifindex, hook, _hookopts,
     151              :                                XDP_FLAGS_SKB_MODE);
     152            0 :         break;
     153            0 :     case BF_FLAVOR_TC:
     154            0 :         r = bf_bpf_link_create(prog_fd, _hookopts->ifindex, hook, _hookopts, 0);
     155            0 :         break;
     156            0 :     case BF_FLAVOR_CGROUP:
     157            0 :         cgroup_fd = open(_hookopts->cgpath, O_DIRECTORY | O_RDONLY);
     158            0 :         if (cgroup_fd < 0) {
     159            0 :             return bf_err_r(errno, "failed to open cgroup '%s'",
     160              :                             _hookopts->cgpath);
     161              :         }
     162              : 
     163            0 :         r = bf_bpf_link_create(prog_fd, cgroup_fd, hook, _hookopts, 0);
     164            0 :         break;
     165            0 :     case BF_FLAVOR_NF:
     166            0 :         r = bf_bpf_link_create(prog_fd, 0, hook, _hookopts, 0);
     167            0 :         break;
     168              :     default:
     169              :         return -ENOTSUP;
     170              :     }
     171              : 
     172            0 :     if (r < 0)
     173              :         return r;
     174              : 
     175            0 :     link->fd = r;
     176            0 :     link->hookopts = TAKE_PTR(*hookopts);
     177              : 
     178            0 :     return 0;
     179              : }
     180              : 
     181            0 : static int _bf_link_update_nf(struct bf_link *link, enum bf_hook hook,
     182              :                               int prog_fd)
     183              : {
     184            0 :     bf_assert(link);
     185              : 
     186            0 :     _cleanup_close_ int new_link_fd = -1;
     187            0 :     struct bf_hookopts opts = *link->hookopts;
     188              : 
     189            0 :     opts.priorities[0] = link->hookopts->priorities[1];
     190            0 :     opts.priorities[1] = link->hookopts->priorities[0];
     191              : 
     192            0 :     new_link_fd = bf_bpf_link_create(prog_fd, 0, hook, &opts, 0);
     193            0 :     if (new_link_fd < 0)
     194              :         return new_link_fd;
     195              : 
     196              :     // Swap priorities, so priorities[0] is the one currently used
     197            0 :     link->hookopts->priorities[0] = opts.priorities[0];
     198            0 :     link->hookopts->priorities[1] = opts.priorities[1];
     199              : 
     200            0 :     closep(&link->fd);
     201            0 :     link->fd = TAKE_FD(new_link_fd);
     202              : 
     203            0 :     return 0;
     204              : }
     205              : 
     206            0 : int bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
     207              : {
     208            0 :     bf_assert(link);
     209              : 
     210              :     int r;
     211              : 
     212            0 :     switch (bf_hook_to_flavor(hook)) {
     213            0 :     case BF_FLAVOR_XDP:
     214              :     case BF_FLAVOR_TC:
     215              :     case BF_FLAVOR_CGROUP:
     216            0 :         r = bf_bpf_link_update(link->fd, prog_fd);
     217            0 :         break;
     218            0 :     case BF_FLAVOR_NF:
     219            0 :         r = _bf_link_update_nf(link, hook, prog_fd);
     220            0 :         break;
     221              :     default:
     222              :         return -ENOTSUP;
     223              :     }
     224              : 
     225              :     return r;
     226              : }
     227              : 
     228            0 : void bf_link_detach(struct bf_link *link)
     229              : {
     230            0 :     bf_assert(link);
     231              : 
     232              :     int r;
     233              : 
     234            0 :     r = bf_bpf_link_detach(link->fd);
     235            0 :     if (r) {
     236            0 :         bf_warn_r(
     237              :             r,
     238              :             "call to BPF_LINK_DETACH failed, closing the file descriptor and assuming the link is destroyed");
     239              :     }
     240              : 
     241            0 :     bf_hookopts_free(&link->hookopts);
     242            0 :     closep(&link->fd);
     243            0 : }
     244              : 
     245            0 : int bf_link_pin(struct bf_link *link, int dir_fd)
     246              : {
     247              :     int r;
     248              : 
     249            0 :     bf_assert(link);
     250            0 :     bf_assert(dir_fd > 0);
     251              : 
     252            0 :     r = bf_bpf_obj_pin(link->name, link->fd, dir_fd);
     253            0 :     if (r)
     254            0 :         return bf_err_r(r, "failed to pin BPF link '%s'", link->name);
     255              : 
     256              :     return 0;
     257              : }
     258              : 
     259            0 : void bf_link_unpin(struct bf_link *link, int dir_fd)
     260              : {
     261              :     int r;
     262              : 
     263            0 :     bf_assert(link);
     264            0 :     bf_assert(dir_fd > 0);
     265              : 
     266            0 :     r = unlinkat(dir_fd, link->name, 0);
     267            0 :     if (r < 0 && errno != ENOENT) {
     268              :         // Do not warn on ENOENT, we want the file to be gone!
     269            0 :         bf_warn_r(
     270              :             errno,
     271              :             "failed to unlink BPF link '%s', assuming the link is not pinned",
     272              :             link->name);
     273              :     }
     274            0 : }
        

Generated by: LCOV version 2.0-1