I'm having an issue with compiling an eBPF program that I'm installing with TC. At the moment, it is only performing some basic mangling, which requires recalculating the IP checksum.
I noticed in the BPF helpers, there is a function bpf_l3_csum_replace which seems to be what I want. However, when I try to use any of the built-in functions that are mapped using the BPF_FUNC macro, I get an implicit declaration error:
... warning: implicit declaration of 'bpf_l3_csum_replace' is invalid in C99.
This is subsequently followed by an error from the verifier:
... A call to global function 'bpf_l3_csum_replace' is not supported. Only calls to predefined BPF helpers are allowed.
My compile line:
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>
I should note that I am able to compile and install a BPF object (using TC), as long as I don't use any of the "predefined" bpf helpers. The issue arises once I do.
I'm doing this outside of the kernel tree, FWIW. Not sure if that is an issue. I'm including the bpf header (linux/bpf.h), and I'm using the bpf_api.h header from the iproute2 package (didn't have much luck with the bpf_helpers.h header).
I'm still relatively new to eBPF, so I would not be surprised if I'm missing something. Any help would be appreciated.
Edit: Code
#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";
The compiler is throwing that error because bpf_api.h
defines this helper as:
static int l3_csum_replace(struct __sk_buff *skb, uint32_t off, uint32_t from, uint32_t to, uint32_t flags);
instead of bpf_l3_csum_replace
. If you remove the bpf_
prefix from the helper's name in your code it compiles as expected without manually adding a prototype.
Detail & Debugging
If you follow the prototype definition in bpf_api.h
, you'll see that it uses BPF_FUNC
which itself uses __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
According to this snippet of code, any helper definition of the form:
static return_type BPF_FUNC(name, ... args ...);
will be expanded into:
static return_type name(... args ...);
So no bpf_
prefix is ever appended.