]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
iproute2: add check_libbpf() and get_libbpf_version()
authorHangbin Liu <haliu@redhat.com>
Mon, 23 Nov 2020 13:11:57 +0000 (21:11 +0800)
committerDavid Ahern <dsahern@gmail.com>
Wed, 25 Nov 2020 05:14:02 +0000 (22:14 -0700)
This patch aim to add basic checking functions for later iproute2
libbpf support.

First we add check_libbpf() in configure to see if we have bpf library
support. By default the system libbpf will be used, but static linking
against a custom libbpf version can be achieved by passing libbpf DESTDIR
to variable LIBBPF_DIR for configure.

Another variable LIBBPF_FORCE is used to control whether to build iproute2
with libbpf. If set to on, then force to build with libbpf and exit if
not available. If set to off, then force to not build with libbpf.

When dynamically linking against libbpf, we can't be sure that the
version we discovered at compile time is actually the one we are
using at runtime. This can lead to hard-to-debug errors. So we add
a new file lib/bpf_glue.c and a helper function get_libbpf_version()
to get correct libbpf version at runtime.

Signed-off-by: Hangbin Liu <haliu@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
configure
include/bpf_util.h
ip/ip.c
lib/Makefile
lib/bpf_glue.c [new file with mode: 0644]
tc/tc.c

index 307912aa9185f893d80cd7097e662f759cc125ba..2c363d3bbdd634d38f7ab40e060b43be9e5d9a6e 100755 (executable)
--- a/configure
+++ b/configure
@@ -2,6 +2,11 @@
 # SPDX-License-Identifier: GPL-2.0
 # This is not an autoconf generated configure
 #
+# Influential LIBBPF environment variables:
+#   LIBBPF_FORCE={on,off}   on: require link against libbpf;
+#                           off: disable libbpf probing
+#   LIBBPF_DIR              Path to libbpf DESTDIR to use
+
 INCLUDE=${1:-"$PWD/include"}
 
 # Output file which is input to Makefile
@@ -240,6 +245,111 @@ check_elf()
     fi
 }
 
+have_libbpf_basic()
+{
+    cat >$TMPDIR/libbpf_test.c <<EOF
+#include <bpf/libbpf.h>
+int main(int argc, char **argv) {
+    bpf_program__set_autoload(NULL, false);
+    bpf_map__ifindex(NULL);
+    bpf_map__set_pin_path(NULL, NULL);
+    bpf_object__open_file(NULL, NULL);
+    return 0;
+}
+EOF
+
+    $CC -o $TMPDIR/libbpf_test $TMPDIR/libbpf_test.c $LIBBPF_CFLAGS $LIBBPF_LDLIBS >/dev/null 2>&1
+    local ret=$?
+
+    rm -f $TMPDIR/libbpf_test.c $TMPDIR/libbpf_test
+    return $ret
+}
+
+have_libbpf_sec_name()
+{
+    cat >$TMPDIR/libbpf_sec_test.c <<EOF
+#include <bpf/libbpf.h>
+int main(int argc, char **argv) {
+    void *ptr;
+    bpf_program__section_name(NULL);
+    return 0;
+}
+EOF
+
+    $CC -o $TMPDIR/libbpf_sec_test $TMPDIR/libbpf_sec_test.c $LIBBPF_CFLAGS $LIBBPF_LDLIBS >/dev/null 2>&1
+    local ret=$?
+
+    rm -f $TMPDIR/libbpf_sec_test.c $TMPDIR/libbpf_sec_test
+    return $ret
+}
+
+check_force_libbpf_on()
+{
+    # if set LIBBPF_FORCE=on but no libbpf support, just exist the config
+    # process to make sure we don't build without libbpf.
+    if [ "$LIBBPF_FORCE" = on ]; then
+        echo " LIBBPF_FORCE=on set, but couldn't find a usable libbpf"
+        exit 1
+    fi
+}
+
+check_libbpf()
+{
+    # if set LIBBPF_FORCE=off, disable libbpf entirely
+    if [ "$LIBBPF_FORCE" = off ]; then
+        echo "no"
+        return
+    fi
+
+    if ! ${PKG_CONFIG} libbpf --exists && [ -z "$LIBBPF_DIR" ] ; then
+        echo "no"
+        check_force_libbpf_on
+        return
+    fi
+
+    if [ $(uname -m) = x86_64 ]; then
+        local LIBBPF_LIBDIR="${LIBBPF_DIR}/usr/lib64"
+    else
+        local LIBBPF_LIBDIR="${LIBBPF_DIR}/usr/lib"
+    fi
+
+    if [ -n "$LIBBPF_DIR" ]; then
+        LIBBPF_CFLAGS="-I${LIBBPF_DIR}/usr/include"
+        LIBBPF_LDLIBS="${LIBBPF_LIBDIR}/libbpf.a -lz -lelf"
+        LIBBPF_VERSION=$(PKG_CONFIG_LIBDIR=${LIBBPF_LIBDIR}/pkgconfig ${PKG_CONFIG} libbpf --modversion)
+    else
+        LIBBPF_CFLAGS=$(${PKG_CONFIG} libbpf --cflags)
+        LIBBPF_LDLIBS=$(${PKG_CONFIG} libbpf --libs)
+        LIBBPF_VERSION=$(${PKG_CONFIG} libbpf --modversion)
+    fi
+
+    if ! have_libbpf_basic; then
+        echo "no"
+        echo " libbpf version $LIBBPF_VERSION is too low, please update it to at least 0.1.0"
+        check_force_libbpf_on
+        return
+    else
+        echo "HAVE_LIBBPF:=y" >> $CONFIG
+        echo 'CFLAGS += -DHAVE_LIBBPF ' $LIBBPF_CFLAGS >> $CONFIG
+        echo "CFLAGS += -DLIBBPF_VERSION=\\\"$LIBBPF_VERSION\\\"" >> $CONFIG
+        echo 'LDLIBS += ' $LIBBPF_LDLIBS >> $CONFIG
+
+        if [ -z "$LIBBPF_DIR" ]; then
+            echo "CFLAGS += -DLIBBPF_DYNAMIC" >> $CONFIG
+        fi
+    fi
+
+    # bpf_program__title() is deprecated since libbpf 0.2.0, use
+    # bpf_program__section_name() instead if we support
+    if have_libbpf_sec_name; then
+        echo "HAVE_LIBBPF_SECTION_NAME:=y" >> $CONFIG
+        echo 'CFLAGS += -DHAVE_LIBBPF_SECTION_NAME ' >> $CONFIG
+    fi
+
+    echo "yes"
+    echo "     libbpf version $LIBBPF_VERSION"
+}
+
 check_selinux()
 # SELinux is a compile time option in the ss utility
 {
@@ -385,6 +495,9 @@ check_setns
 echo -n "SELinux support: "
 check_selinux
 
+echo -n "libbpf support: "
+check_libbpf
+
 echo -n "ELF support: "
 check_elf
 
index 63db07ca49aebe220742869ca6769de8e3531733..dee5bb02019d594c708d2bb5981e4754dcbfdbd2 100644 (file)
@@ -300,4 +300,7 @@ static inline int bpf_recv_map_fds(const char *path, int *fds,
        return -1;
 }
 #endif /* HAVE_ELF */
+
+const char *get_libbpf_version(void);
+
 #endif /* __BPF_UTIL__ */
diff --git a/ip/ip.c b/ip/ip.c
index 5e31957f2420abacc279e0da0a08a32577620ffe..466dbb5263d644aa735541e736d07b6c93ad89a7 100644 (file)
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -24,6 +24,7 @@
 #include "namespace.h"
 #include "color.h"
 #include "rt_names.h"
+#include "bpf_util.h"
 
 int preferred_family = AF_UNSPEC;
 int human_readable;
@@ -147,8 +148,9 @@ static int batch(const char *name)
 
 int main(int argc, char **argv)
 {
-       char *basename;
+       const char *libbpf_version;
        char *batch_file = NULL;
+       char *basename;
        int color = 0;
 
        /* to run vrf exec without root, capabilities might be set, drop them
@@ -229,7 +231,11 @@ int main(int argc, char **argv)
                        ++timestamp;
                        ++timestamp_short;
                } else if (matches(opt, "-Version") == 0) {
-                       printf("ip utility, iproute2-%s\n", version);
+                       printf("ip utility, iproute2-%s", version);
+                       libbpf_version = get_libbpf_version();
+                       if (libbpf_version)
+                               printf(", libbpf %s", libbpf_version);
+                       printf("\n");
                        exit(0);
                } else if (matches(opt, "-force") == 0) {
                        ++force;
index 13f4ee15373b78fbbe9fd387b36c301c2e92dfe0..a02775a581e109dddbf90e1e735f8cd6b817729c 100644 (file)
@@ -5,7 +5,7 @@ CFLAGS += -fPIC
 
 UTILOBJ = utils.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \
        inet_proto.o namespace.o json_writer.o json_print.o \
-       names.o color.o bpf.o exec.o fs.o cg_map.o
+       names.o color.o bpf.o bpf_glue.o exec.o fs.o cg_map.o
 
 NLOBJ=libgenl.o libnetlink.o mnl_utils.o
 
diff --git a/lib/bpf_glue.c b/lib/bpf_glue.c
new file mode 100644 (file)
index 0000000..67c41c2
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * bpf_glue.c: BPF code to call both legacy and libbpf code
+ * Authors:    Hangbin Liu <haliu@redhat.com>
+ *
+ */
+#include "bpf_util.h"
+
+#ifdef HAVE_LIBBPF
+static const char *_libbpf_compile_version = LIBBPF_VERSION;
+static char _libbpf_version[10] = {};
+
+const char *get_libbpf_version(void)
+{
+       /* Start by copying compile-time version into buffer so we have a
+        * fallback value in case we are dynamically linked, or can't find a
+        * version in /proc/self/maps below.
+        */
+       strncpy(_libbpf_version, _libbpf_compile_version,
+               sizeof(_libbpf_version)-1);
+#ifdef LIBBPF_DYNAMIC
+       char buf[PATH_MAX], *s;
+       bool found = false;
+       FILE *fp;
+
+       /* When dynamically linking against libbpf, we can't be sure that the
+        * version we discovered at compile time is actually the one we are
+        * using at runtime. This can lead to hard-to-debug errors, so we try to
+        * discover the correct version at runtime.
+        *
+        * The simple solution to this would be if libbpf itself exported a
+        * version in its API. But since it doesn't, we work around this by
+        * parsing the mappings of the binary at runtime, looking for the full
+        * filename of libbpf.so and using that.
+        */
+       fp = fopen("/proc/self/maps", "r");
+       if (fp == NULL)
+               goto out;
+
+       while ((s = fgets(buf, sizeof(buf), fp)) != NULL) {
+               if ((s = strstr(buf, "libbpf.so.")) != NULL) {
+                       strncpy(_libbpf_version, s+10, sizeof(_libbpf_version)-1);
+                       strtok(_libbpf_version, "\n");
+                       found = true;
+                       break;
+               }
+       }
+
+       fclose(fp);
+out:
+       if (!found)
+               fprintf(stderr, "Couldn't find runtime libbpf version - falling back to compile-time value!\n");
+#endif /* LIBBPF_DYNAMIC */
+
+       _libbpf_version[sizeof(_libbpf_version)-1] = '\0';
+       return _libbpf_version;
+}
+#else
+const char *get_libbpf_version(void)
+{
+       return NULL;
+}
+#endif /* HAVE_LIBBPF */
diff --git a/tc/tc.c b/tc/tc.c
index af9b21dae1fcd0bc2d8c28c889b51b74581d27ce..7557b9778111f3e3724e524beee0b5ee81ea11d5 100644 (file)
--- a/tc/tc.c
+++ b/tc/tc.c
@@ -30,6 +30,7 @@
 #include "tc_common.h"
 #include "namespace.h"
 #include "rt_names.h"
+#include "bpf_util.h"
 
 int show_stats;
 int show_details;
@@ -259,8 +260,9 @@ static int batch(const char *name)
 
 int main(int argc, char **argv)
 {
-       int ret;
+       const char *libbpf_version;
        char *batch_file = NULL;
+       int ret;
 
        while (argc > 1) {
                if (argv[1][0] != '-')
@@ -277,7 +279,11 @@ int main(int argc, char **argv)
                } else if (matches(argv[1], "-graph") == 0) {
                        show_graph = 1;
                } else if (matches(argv[1], "-Version") == 0) {
-                       printf("tc utility, iproute2-%s\n", version);
+                       printf("tc utility, iproute2-%s", version);
+                       libbpf_version = get_libbpf_version();
+                       if (libbpf_version)
+                               printf(", libbpf %s", libbpf_version);
+                       printf("\n");
                        return 0;
                } else if (matches(argv[1], "-iec") == 0) {
                        ++use_iec;