The build happened to work on arm64 because the glibc arm64 headers don't support multilib. On x86_64, glibc headers assume that BPF is a 32-bit platform (because __x86_64__ isn't defined) and fail to find the 32-bit headers. This is not a glibc bug. Rather, BPF programs should not be including glibc headers. Most Linux headers are not trivial to include in BPF programs. The version of the headers meant for userspace use do include glibc headers, and that isn't supported in BPF. The version meant for building kernel modules does not, but using it requires much more complicated build system. Solve this problem by only including headers intended for use in BPF programs. These headers include declarations explicitly intended for use in BPF programs, so if they do pull in libc headers that is a bug. Nix's wrapped clang would pull in libc headers automatically and is not suitable when a target is specified explicitly. Therefore, use an unwrapped clang. Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com> --- To check that the program does not include libc headers, one can add #include <stdio.h> (or any other libc header) and check that one gets an appropriate error. Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com> --- tools/default.nix | 11 +++---- tools/meson.options | 4 +++ tools/xdp-forwarder/meson.build | 11 +++++-- tools/xdp-forwarder/parsing_helpers.h | 57 ----------------------------------- 4 files changed, 18 insertions(+), 65 deletions(-) diff --git a/tools/default.nix b/tools/default.nix index 2c6846c80073e7b64fb7a19488103f6cf97a4420..f4febc9aba394fad85f20a51bb33e6b3b9224bab 100644 --- a/tools/default.nix +++ b/tools/default.nix @@ -6,7 +6,7 @@ import ../lib/call-package.nix ( { src, lib, stdenv, fetchCrate, fetchurl, runCommand, buildPackages , meson, ninja, pkg-config, rustc , clang-tools, clippy, jq -, dbus +, dbus, linuxHeaders # clang 19 (current nixpkgs default) is too old to support -fwrapv-pointer , clang_21, libbpf , buildSupport ? false @@ -87,8 +87,8 @@ stdenv.mkDerivation (finalAttrs: { nativeBuildInputs = [ meson ninja ] ++ lib.optionals (appSupport || driverSupport) [ pkg-config ] ++ lib.optionals hostSupport [ rustc ] - ++ lib.optionals driverSupport [ clang_21 ]; - buildInputs = lib.optionals appSupport [ dbus ] ++ lib.optionals driverSupport [ libbpf ]; + ++ lib.optionals driverSupport [ clang_21.cc ]; + buildInputs = lib.optionals appSupport [ dbus ] ++ lib.optionals driverSupport [ libbpf linuxHeaders ]; postPatch = lib.optionals hostSupport (lib.concatMapStringsSep "\n" (crate: '' mkdir -p subprojects/packagecache @@ -104,11 +104,10 @@ stdenv.mkDerivation (finalAttrs: { "-Dtests=false" "-Dunwind=false" "-Dwerror=true" + ] ++ lib.optionals driverSupport [ + "-Dlinux-headers=${linuxHeaders}" ]; - # Not supported for target bpf - hardeningDisable = lib.optionals driverSupport [ "zerocallusedregs" ]; - passthru.tests = { clang-tidy = finalAttrs.finalPackage.overrideAttrs ( { name, src, nativeBuildInputs ? [], ... }: diff --git a/tools/meson.options b/tools/meson.options index 301efb9f677fdec57c8491fd6a6868f2d35cb076..2077cdeb33d6b962107b46733f855172ecfc499d 100644 --- a/tools/meson.options +++ b/tools/meson.options @@ -13,6 +13,10 @@ option('driver', type : 'boolean', value : false, option('hostfsrootdir', type : 'string', value : '/run/host', description : 'Path where the virtio-fs provided by the host will be mounted') +option('linux-headers', + type : 'string', + description : 'Path to Linux kernel package') + option('tests', type : 'boolean', description : 'Build tests') diff --git a/tools/xdp-forwarder/meson.build b/tools/xdp-forwarder/meson.build index b73130eb27b8000a102b0a8847ecb06b93a955d2..501540af4d3e786774cedc1bb092c9887a2f37d8 100644 --- a/tools/xdp-forwarder/meson.build +++ b/tools/xdp-forwarder/meson.build @@ -11,8 +11,13 @@ executable('set-router-iface', 'set_router_iface.c', clang = find_program('clang', native : true) +linux_headers_path = get_option('linux-headers') +if linux_headers_path == '' + error('Linux header path must be provided to build XDP forwarder') +endif + bpf_o_cmd = [ - clang.full_path(), + clang, '-fno-stack-protector', '-fno-strict-aliasing', '-fwrapv', '-fwrapv-pointer', @@ -22,10 +27,12 @@ bpf_o_cmd = [ '-Wno-sign-compare', '-O2', '-target', 'bpf', - '-I', meson.current_source_dir() + '/include', '-g', '-c', + '-std=gnu23', '-o', '@OUTPUT@', + '-I', libbpf.get_variable('includedir'), + '-I', linux_headers_path + '/include', '-MD', '-MQ', '@OUTPUT', '-MF', '@DEPFILE@', diff --git a/tools/xdp-forwarder/parsing_helpers.h b/tools/xdp-forwarder/parsing_helpers.h index da099346008bd58485af8308feb4d3391ceef8f5..1ea822100fdb9a75c2d28d34d93e6bb2b5d3ae26 100644 --- a/tools/xdp-forwarder/parsing_helpers.h +++ b/tools/xdp-forwarder/parsing_helpers.h @@ -26,8 +26,6 @@ #include <linux/if_packet.h> #include <linux/ip.h> #include <linux/ipv6.h> -#include <linux/icmp.h> -#include <linux/icmpv6.h> #include <linux/udp.h> #include <linux/tcp.h> @@ -46,16 +44,6 @@ struct vlan_hdr { __be16 h_vlan_encapsulated_proto; }; -/* - * Struct icmphdr_common represents the common part of the icmphdr and icmp6hdr - * structures. - */ -struct icmphdr_common { - __u8 type; - __u8 code; - __sum16 cksum; -}; - /* Allow users of header file to redefine VLAN max depth */ #ifndef VLAN_MAX_DEPTH #define VLAN_MAX_DEPTH 2 @@ -175,51 +163,6 @@ static __always_inline int parse_iphdr(struct hdr_cursor *nh, return iph->protocol; } -static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, - void *data_end, - struct icmp6hdr **icmp6hdr) -{ - struct icmp6hdr *icmp6h = nh->pos; - - if (icmp6h + 1 > data_end) - return -1; - - nh->pos = icmp6h + 1; - *icmp6hdr = icmp6h; - - return icmp6h->icmp6_type; -} - -static __always_inline int parse_icmphdr(struct hdr_cursor *nh, - void *data_end, - struct icmphdr **icmphdr) -{ - struct icmphdr *icmph = nh->pos; - - if (icmph + 1 > data_end) - return -1; - - nh->pos = icmph + 1; - *icmphdr = icmph; - - return icmph->type; -} - -static __always_inline int parse_icmphdr_common(struct hdr_cursor *nh, - void *data_end, - struct icmphdr_common **icmphdr) -{ - struct icmphdr_common *h = nh->pos; - - if (h + 1 > data_end) - return -1; - - nh->pos = h + 1; - *icmphdr = h; - - return h->type; -} - /* * parse_udphdr: parse the udp header and return the length of the udp payload */ -- 2.51.1