Неявное объявление eBPF помощника BPF

У меня проблема с компиляцией программы eBPF, которую я устанавливаю с помощью TC. На данный момент он выполняет только некоторые базовые изменения, которые требуют пересчета контрольной суммы IP.

Я заметил в хелперах BPF, что есть функция bpf_l3_csum_replace, которая, кажется, то, что я хочу. Однако, когда я пытаюсь использовать любую из встроенных функций, отображаемых с помощью макроса BPF_FUNC, я получаю неявную ошибку объявления:

... предупреждение: неявное объявление 'bpf_l3_csum_replace' недопустимо в C99.

Впоследствии за этим следует ошибка от верификатора:

... Вызов глобальной функции 'bpf_l3_csum_replace' не поддерживается. Разрешены только вызовы предопределенных помощников BPF.

Моя строка компиляции:

clang -target bpf -nostdinc -I/usr/include -I/usr/lib64/clang/5.0.2/include -O2 -emit-llvm -c <file> -o - | llc -march=bpf -filetype=obj -o <output>

Я должен отметить, что я могу скомпилировать и установить объект BPF (используя TC), если я не использую какой-либо из «предопределенных» помощников bpf. Проблема возникает, как только я это делаю.

Я делаю это вне дерева ядра, FWIW. Не уверен, что это проблема. Я включаю заголовок bpf (linux/bpf.h) и использую заголовок bpf_api.h из пакета iproute2 (мне не повезло с заголовком bpf_helpers.h).

Я все еще относительно новичок в eBPF, поэтому не удивлюсь, если что-то упустил. Любая помощь будет оценена по достоинству.

Изменить: код

#define KBUILD_NAME "testbpf"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/filter.h>
#include <linux/pkt_cls.h>
#include "bpf_api.h"


#define _htonl __builtin_bswap32

struct eth_hdr {
    unsigned char h_dest[ETH_ALEN];
    unsigned char h_source[ETH_ALEN];
    unsigned short h_proto;
};

#define IP_CSUM_OFFSET (ETH_HLEN + offsetof(struct iphdr, check))
#define TOS_OFF (ETH_HLEN + offsetof(struct iphdr, tos))
#define PROTO_OFF (ETH_HLEN + offsetof(struct iphdr, protocol))

__section("ingress") int bpf_prog(struct __sk_buff *skb)
{
    void *data = (void *) (long)skb->data;
    struct eth_hdr *eth = data;
    void *data_end = (void*) (long) skb->data_end;

    if (data+sizeof(*eth) > data_end)
        return BPF_H_DEFAULT;

    if (eth->h_proto == htons(ETH_P_ARP))
        return BPF_H_DEFAULT;

    // ipv4
    if (eth->h_proto == htons(ETH_P_IP))
    {
        struct iphdr *ip = data+sizeof(*eth);
        if (data+sizeof(*ip)+sizeof(*eth) > data_end)
            return BPF_H_DEFAULT;

        __u8 proto = ip->protocol;
        __u8 old_tos = ip->tos;
        // mangle our tos; not meant to achieve anything
        ip->tos = 0x04;
        if (proto == IPPROTO_ICMP)
            ip->tos = 0x00;
        if (proto == IPPROTO_TCP)
            ip->tos = 0x04;
        if (proto == IPPROTO_UDP)
            ip->tos = 0x08;
        // update our csum and return
        bpf_l3_csum_replace(skb, IP_CSUM_OFFSET, old_tos, ip->tos, 2); // ISSUE here
        return BPF_H_DEFAULT;
    }

    return BPF_H_DEFAULT;
}

char __license[] __section("license") = "GPL";

person gsm    schedule 03.09.2018    source источник
comment
bpf_api.h должно быть достаточно. Не могли бы вы опубликовать программу или MCVE?   -  person pchaigno    schedule 03.09.2018
comment
@pchaigno Итак, я скомпилировал его, вручную добавив прототип функции в заголовок bpf_api.h, хотя я не уверен, зачем это нужно.   -  person gsm    schedule 04.09.2018
comment
Попробуем разобраться. Я предполагаю, что вы скопировали bpf_api.h в тот же каталог, что и исходный файл? Откуда именно? Какая версия iproute2?   -  person pchaigno    schedule 04.09.2018
comment
Да, это правильно. Это из последней версии iproute2 (взято с git.kernel.org)   -  person gsm    schedule 04.09.2018


Ответы (1)


Компилятор выдает эту ошибку, потому что bpf_api.h определяет этот помощник как:

static int l3_csum_replace(struct __sk_buff *skb, uint32_t off, uint32_t from, uint32_t to, uint32_t flags);

вместо bpf_l3_csum_replace. Если вы удалите префикс bpf_ из имени помощника в своем коде, он скомпилируется, как и ожидалось, без добавления прототипа вручную.


Подробности и отладка

Если вы будете следовать определению прототипа в bpf_api.h, вы увидите, что он использует BPF_FUNC, который сам использует __BPF_FUNC:

#ifndef __BPF_FUNC
# define __BPF_FUNC(NAME, ...)                      \
    (* NAME)(__VA_ARGS__) __maybe_unused
#endif

#ifndef BPF_FUNC
# define BPF_FUNC(NAME, ...)                        \
    __BPF_FUNC(NAME, __VA_ARGS__) = (void *) BPF_FUNC_##NAME
#else
#endif

Согласно этому фрагменту кода любое вспомогательное определение формы:

static return_type BPF_FUNC(name, ... args ...);

будет расширен на:

static return_type name(... args ...);

Таким образом, префикс bpf_ никогда не добавляется.

person pchaigno    schedule 04.09.2018
comment
Я следил за этими макросами, но не был уверен, что верификатор (?) будет жаловаться на «глобальную функцию», которая не является частью API. Меня сейчас нет рядом с компьютером, чтобы проверить, но сделаю это утром! Спасибо за помощь до сих пор - person gsm; 04.09.2018
comment
О, это компилятор выдает ошибку, а не верификатор ядра (см. коммит LLVM, который добавил это). - person pchaigno; 04.09.2018
comment
Это сработало; Спасибо за помощь! Да, я не думал, что это верификатор, но не был уверен, что это было - person gsm; 05.09.2018
comment
Рад, что смог помочь! - person pchaigno; 05.09.2018