LCOV - code coverage report
Current view: top level - bpfilter/cgen/prog - link.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 8.8 % 181 16
Test Date: 2025-05-09 22:39:08 Functions: 12.5 % 16 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 "bpfilter/cgen/prog/link.h"
       7              : 
       8              : #include <linux/bpf.h>
       9              : 
      10              : #include <errno.h>
      11              : #include <fcntl.h>
      12              : #include <stdio.h>
      13              : #include <stdlib.h>
      14              : #include <string.h>
      15              : #include <unistd.h>
      16              : 
      17              : #include "core/bpf.h"
      18              : #include "core/dump.h"
      19              : #include "core/flavor.h"
      20              : #include "core/helper.h"
      21              : #include "core/hook.h"
      22              : #include "core/logger.h"
      23              : #include "core/marsh.h"
      24              : 
      25            4 : int bf_link_new(struct bf_link **link, const char *name)
      26              : {
      27            4 :     _free_bf_link_ struct bf_link *_link = NULL;
      28              : 
      29            4 :     bf_assert(link && name);
      30            4 :     bf_assert(name[0] != '\0');
      31              : 
      32            4 :     _link = malloc(sizeof(*_link));
      33            4 :     if (!_link)
      34              :         return -ENOMEM;
      35              : 
      36            4 :     bf_strncpy(_link->name, BPF_OBJ_NAME_LEN, name);
      37              : 
      38            4 :     _link->hookopts = NULL;
      39            4 :     _link->fd = -1;
      40              : 
      41            4 :     *link = TAKE_PTR(_link);
      42              : 
      43            4 :     return 0;
      44              : }
      45              : 
      46            0 : int bf_link_new_from_marsh(struct bf_link **link, int dir_fd,
      47              :                            const struct bf_marsh *marsh)
      48              : {
      49            0 :     _free_bf_link_ struct bf_link *_link = NULL;
      50            0 :     _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
      51              :     struct bf_marsh *child = NULL;
      52              :     int r;
      53              : 
      54            0 :     bf_assert(link && marsh);
      55              : 
      56            0 :     _link = malloc(sizeof(*_link));
      57            0 :     if (!_link)
      58              :         return -ENOMEM;
      59              : 
      60            0 :     _link->hookopts = NULL;
      61            0 :     _link->fd = -1;
      62              : 
      63            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
      64              :         return -EINVAL;
      65            0 :     memcpy(&_link->name, child->data, BPF_OBJ_NAME_LEN);
      66              : 
      67            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
      68              :         return -EINVAL;
      69            0 :     if (!bf_marsh_is_empty(child)) {
      70            0 :         r = bf_hookopts_new_from_marsh(&_link->hookopts, child);
      71            0 :         if (r)
      72              :             return r;
      73              :     }
      74              : 
      75            0 :     if (dir_fd != -1) {
      76            0 :         r = bf_bpf_obj_get(_link->name, dir_fd, &_link->fd);
      77            0 :         if (r) {
      78            0 :             return bf_err_r(r, "failed to open pinned BPF link '%s'",
      79              :                             _link->name);
      80              :         }
      81              :     }
      82              : 
      83            0 :     *link = TAKE_PTR(_link);
      84              : 
      85            0 :     return 0;
      86              : }
      87              : 
      88            8 : void bf_link_free(struct bf_link **link)
      89              : {
      90            8 :     bf_assert(link);
      91              : 
      92            8 :     if (!*link)
      93              :         return;
      94              : 
      95            4 :     bf_hookopts_free(&(*link)->hookopts);
      96            4 :     closep(&(*link)->fd);
      97              :     freep((void *)link);
      98              : }
      99              : 
     100            0 : int bf_link_marsh(const struct bf_link *link, struct bf_marsh **marsh)
     101              : {
     102            0 :     _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
     103              :     int r;
     104              : 
     105            0 :     bf_assert(link && marsh);
     106              : 
     107            0 :     r = bf_marsh_new(&_marsh, NULL, 0);
     108            0 :     if (r)
     109              :         return r;
     110              : 
     111            0 :     r = bf_marsh_add_child_raw(&_marsh, &link->name, BPF_OBJ_NAME_LEN);
     112            0 :     if (r)
     113              :         return r;
     114              : 
     115              :     // Serialize link.hookopts
     116            0 :     if (link->hookopts) {
     117            0 :         _cleanup_bf_marsh_ struct bf_marsh *hookopts_elem = NULL;
     118              : 
     119            0 :         r = bf_hookopts_marsh(link->hookopts, &hookopts_elem);
     120            0 :         if (r < 0)
     121              :             return r;
     122              : 
     123            0 :         r = bf_marsh_add_child_obj(&_marsh, hookopts_elem);
     124            0 :         if (r < 0)
     125              :             return r;
     126              :     } else {
     127            0 :         r = bf_marsh_add_child_raw(&_marsh, NULL, 0);
     128            0 :         if (r)
     129              :             return r;
     130              :     }
     131              : 
     132            0 :     *marsh = TAKE_PTR(_marsh);
     133              : 
     134            0 :     return 0;
     135              : }
     136              : 
     137            0 : void bf_link_dump(const struct bf_link *link, prefix_t *prefix)
     138              : {
     139            0 :     bf_assert(link && prefix);
     140              : 
     141            0 :     DUMP(prefix, "struct bf_link at %p", link);
     142              : 
     143            0 :     bf_dump_prefix_push(prefix);
     144            0 :     DUMP(prefix, "name: %s", link->name);
     145              : 
     146            0 :     if (link->hookopts) {
     147            0 :         DUMP(prefix, "hookopts: struct bf_hookopts *");
     148            0 :         bf_dump_prefix_push(prefix);
     149            0 :         bf_hookopts_dump(link->hookopts, prefix);
     150            0 :         bf_dump_prefix_pop(prefix);
     151              :     } else {
     152            0 :         DUMP(prefix, "hookopts: struct bf_hookopts * (NULL)");
     153              :     }
     154              : 
     155            0 :     DUMP(bf_dump_prefix_last(prefix), "fd: %d", link->fd);
     156            0 :     bf_dump_prefix_pop(prefix);
     157            0 : }
     158              : 
     159            0 : static int _bf_link_attach_xdp(struct bf_link *link, enum bf_hook hook,
     160              :                                const struct bf_hookopts *hookopts, int prog_fd)
     161              : {
     162              :     union bpf_attr attr;
     163              :     int r;
     164              : 
     165              :     UNUSED(hook);
     166              : 
     167            0 :     memset(&attr, 0, sizeof(attr));
     168              : 
     169            0 :     attr.link_create.prog_fd = prog_fd;
     170            0 :     attr.link_create.target_fd = hookopts->ifindex;
     171            0 :     attr.link_create.attach_type = BPF_XDP;
     172            0 :     attr.link_create.flags = BF_XDP_MODE_SKB;
     173              : 
     174            0 :     r = bf_bpf(BPF_LINK_CREATE, &attr);
     175            0 :     if (r < 0)
     176              :         return r;
     177              : 
     178            0 :     link->fd = r;
     179              : 
     180            0 :     return 0;
     181              : }
     182              : 
     183            0 : static int _bf_link_attach_tc(struct bf_link *link, enum bf_hook hook,
     184              :                               const struct bf_hookopts *hookopts, int prog_fd)
     185              : {
     186              :     union bpf_attr attr;
     187              :     int r;
     188              : 
     189              :     UNUSED(hook);
     190              : 
     191            0 :     memset(&attr, 0, sizeof(attr));
     192              : 
     193            0 :     attr.link_create.prog_fd = prog_fd;
     194            0 :     attr.link_create.target_fd = hookopts->ifindex;
     195            0 :     attr.link_create.attach_type = bf_hook_to_bpf_attach_type(hook);
     196              : 
     197            0 :     r = bf_bpf(BPF_LINK_CREATE, &attr);
     198            0 :     if (r < 0)
     199              :         return r;
     200              : 
     201            0 :     link->fd = r;
     202              : 
     203            0 :     return 0;
     204              : }
     205              : 
     206            0 : static int _bf_link_attach_nf(struct bf_link *link, enum bf_hook hook,
     207              :                               const struct bf_hookopts *hookopts, int prog_fd)
     208              : {
     209              :     union bpf_attr attr;
     210              :     int r;
     211              : 
     212            0 :     memset(&attr, 0, sizeof(attr));
     213              : 
     214            0 :     attr.link_create.prog_fd = prog_fd;
     215            0 :     attr.link_create.attach_type = BPF_NETFILTER;
     216            0 :     attr.link_create.netfilter.pf = hookopts->family;
     217            0 :     attr.link_create.netfilter.hooknum = bf_hook_to_nf_hook(hook);
     218            0 :     attr.link_create.netfilter.priority = hookopts->priorities[0];
     219              : 
     220            0 :     r = bf_bpf(BPF_LINK_CREATE, &attr);
     221            0 :     if (r < 0)
     222              :         return r;
     223              : 
     224            0 :     link->fd = r;
     225              : 
     226            0 :     return 0;
     227              : }
     228              : 
     229            0 : static int _bf_link_attach_cgroup(struct bf_link *link, enum bf_hook hook,
     230              :                                   const struct bf_hookopts *hookopts,
     231              :                                   int prog_fd)
     232              : {
     233            0 :     _cleanup_close_ int cgroup_fd = -1;
     234              :     union bpf_attr attr;
     235              :     int r;
     236              : 
     237            0 :     memset(&attr, 0, sizeof(attr));
     238              : 
     239            0 :     attr.link_create.prog_fd = prog_fd;
     240            0 :     attr.link_create.attach_type = bf_hook_to_bpf_attach_type(hook);
     241              : 
     242            0 :     cgroup_fd = open(hookopts->cgpath, O_DIRECTORY | O_RDONLY);
     243            0 :     if (cgroup_fd < 0)
     244            0 :         return bf_err_r(errno, "failed to open cgroup '%s'", hookopts->cgpath);
     245              : 
     246            0 :     attr.link_create.target_fd = cgroup_fd;
     247              : 
     248            0 :     r = bf_bpf(BPF_LINK_CREATE, &attr);
     249            0 :     if (r < 0)
     250              :         return r;
     251              : 
     252            0 :     link->fd = r;
     253              : 
     254            0 :     return 0;
     255              : }
     256              : 
     257            0 : int bf_link_attach(struct bf_link *link, enum bf_hook hook,
     258              :                    struct bf_hookopts **hookopts, int prog_fd)
     259              : {
     260              :     int r;
     261              : 
     262            0 :     bf_assert(link && hookopts);
     263              : 
     264            0 :     switch (bf_hook_to_flavor(hook)) {
     265            0 :     case BF_FLAVOR_XDP:
     266            0 :         r = _bf_link_attach_xdp(link, hook, *hookopts, prog_fd);
     267            0 :         break;
     268            0 :     case BF_FLAVOR_TC:
     269            0 :         r = _bf_link_attach_tc(link, hook, *hookopts, prog_fd);
     270            0 :         break;
     271            0 :     case BF_FLAVOR_CGROUP:
     272            0 :         r = _bf_link_attach_cgroup(link, hook, *hookopts, prog_fd);
     273            0 :         break;
     274            0 :     case BF_FLAVOR_NF:
     275            0 :         r = _bf_link_attach_nf(link, hook, *hookopts, prog_fd);
     276            0 :         break;
     277              :     default:
     278              :         return -ENOTSUP;
     279              :     }
     280              : 
     281            0 :     if (r)
     282              :         return r;
     283              : 
     284            0 :     link->hookopts = TAKE_PTR(*hookopts);
     285              : 
     286            0 :     return 0;
     287              : }
     288              : 
     289            0 : static int _bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
     290              : {
     291              :     union bpf_attr attr;
     292              : 
     293              :     UNUSED(hook);
     294              : 
     295            0 :     bf_assert(link);
     296              : 
     297            0 :     memset(&attr, 0, sizeof(attr));
     298              : 
     299            0 :     attr.link_update.link_fd = link->fd;
     300            0 :     attr.link_update.new_prog_fd = prog_fd;
     301              : 
     302            0 :     return bf_bpf(BPF_LINK_UPDATE, &attr);
     303              : }
     304              : 
     305            0 : static int _bf_link_update_nf(struct bf_link *link, enum bf_hook hook,
     306              :                               int prog_fd)
     307              : {
     308            0 :     _cleanup_close_ int new_link_fd = -1;
     309              :     union bpf_attr attr;
     310              :     int priorities[2];
     311              : 
     312            0 :     bf_assert(link);
     313              : 
     314            0 :     memset(&attr, 0, sizeof(attr));
     315              : 
     316            0 :     attr.link_create.prog_fd = prog_fd;
     317            0 :     attr.link_create.attach_type = BPF_NETFILTER;
     318              : 
     319            0 :     memcpy(priorities, link->hookopts->priorities, sizeof(priorities));
     320              : 
     321            0 :     attr.link_create.netfilter.pf = link->hookopts->family;
     322            0 :     attr.link_create.netfilter.hooknum = bf_hook_to_nf_hook(hook);
     323            0 :     attr.link_create.netfilter.priority = priorities[1];
     324              : 
     325            0 :     new_link_fd = bf_bpf(BPF_LINK_CREATE, &attr);
     326            0 :     if (new_link_fd < 0)
     327              :         return new_link_fd;
     328              : 
     329              :     // Swap priorities, so priorities[0] is the one currently used
     330            0 :     link->hookopts->priorities[0] = priorities[1];
     331            0 :     link->hookopts->priorities[1] = priorities[0];
     332              : 
     333            0 :     return 0;
     334              : }
     335              : 
     336            0 : int bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
     337              : {
     338              :     int r;
     339              : 
     340            0 :     bf_assert(link);
     341              : 
     342            0 :     switch (bf_hook_to_flavor(hook)) {
     343            0 :     case BF_FLAVOR_XDP:
     344              :     case BF_FLAVOR_TC:
     345              :     case BF_FLAVOR_CGROUP:
     346            0 :         r = _bf_link_update(link, hook, prog_fd);
     347            0 :         break;
     348            0 :     case BF_FLAVOR_NF:
     349            0 :         r = _bf_link_update_nf(link, hook, prog_fd);
     350            0 :         break;
     351              :     default:
     352              :         return -ENOTSUP;
     353              :     }
     354              : 
     355              :     return r;
     356              : }
     357              : 
     358            0 : void bf_link_detach(struct bf_link *link)
     359              : {
     360              :     union bpf_attr attr;
     361              :     int r;
     362              : 
     363            0 :     bf_assert(link);
     364              : 
     365            0 :     memset(&attr, 0, sizeof(attr));
     366              : 
     367            0 :     attr.link_detach.link_fd = link->fd;
     368              : 
     369            0 :     r = bf_bpf(BPF_LINK_DETACH, &attr);
     370            0 :     if (r) {
     371            0 :         bf_warn_r(
     372              :             r,
     373              :             "call to BPF_LINK_DETACH failed, closing the file descriptor and assuming the link is destroyed");
     374              :     }
     375              : 
     376            0 :     bf_hookopts_free(&link->hookopts);
     377            0 :     closep(&link->fd);
     378            0 : }
     379              : 
     380            0 : int bf_link_pin(struct bf_link *link, int dir_fd)
     381              : {
     382              :     int r;
     383              : 
     384            0 :     bf_assert(link);
     385            0 :     bf_assert(dir_fd > 0);
     386              : 
     387            0 :     r = bf_bpf_obj_pin(link->name, link->fd, dir_fd);
     388            0 :     if (r)
     389            0 :         return bf_err_r(r, "failed to pin BPF link '%s'", link->name);
     390              : 
     391              :     return 0;
     392              : }
     393              : 
     394            0 : void bf_link_unpin(struct bf_link *link, int dir_fd)
     395              : {
     396              :     int r;
     397              : 
     398            0 :     bf_assert(link);
     399            0 :     bf_assert(dir_fd > 0);
     400              : 
     401            0 :     r = unlinkat(dir_fd, link->name, 0);
     402            0 :     if (r < 0 && errno != ENOENT) {
     403              :         // Do not warn on ENOENT, we want the file to be gone!
     404            0 :         bf_warn_r(
     405              :             errno,
     406              :             "failed to unlink BPF link '%s', assuming the link is not pinned",
     407              :             link->name);
     408              :     }
     409            0 : }
        

Generated by: LCOV version 2.0-1