iproute2: add libbpf support

Started by Hangbin Liu on Oct 23, 2020 · 167 messages from 15 authors · Last reply Dec 1, 2020

Series

5 patches, 16 files

+795 −19

Subsystems (2)

Maintainers (2)

STALE2167d SUPERSEDED

Status: Superseded by v6 (posted 2020-11-23). [go to v6]

Revision v1 (6 revisions archived).

Revisions (6)
  1. v1 current
  2. v2 [diff against previous]
  3. v3 [diff against previous]
  4. v4 [diff against previous]
  5. v5 [diff against previous]
  6. v6 [diff against previous]

page 1 of 3 · open the first message on its own page

  1. [PATCH iproute2-next 1/5] configure: add check_libbpf() for later libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1 current
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  2. [PATCH iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c
    Revisions and related patches
    Revisions (6)
    1. v1 current
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  3. [PATCH iproute2-next 3/5] lib: add libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1 current
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  4. [PATCH iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder
    Revisions and related patches
    Revisions (6)
    1. v1 current
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  5. [PATCH iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps
    Revisions and related patches
    Revisions (6)
    1. v1 current
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  6. [PATCHv2 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 current [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  7. [PATCHv2 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 current [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  8. [PATCHv2 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 current [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  9. [PATCHv2 iproute2-next 3/5] lib: add libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 current [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  10. [PATCHv2 iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 current [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  11. [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 current [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  12. [PATCHv3 iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 current [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  13. [PATCHv3 iproute2-next 3/5] lib: add libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 current [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  14. [PATCHv3 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 current [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  15. [PATCHv3 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 current [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  16. [PATCHv4 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 current [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  17. [PATCHv4 iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 current [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  18. [PATCHv4 iproute2-next 3/5] lib: add libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 current [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  19. [PATCHv4 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 current [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  20. [PATCHv4 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 current [diff against previous]
    5. v5 [diff against previous]
    6. v6 [diff against previous]
  21. [PATCHv5 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 current [diff against previous]
    6. v6 [diff against previous]
  22. [PATCHv5 iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 current [diff against previous]
    6. v6 [diff against previous]
  23. [PATCHv5 iproute2-next 3/5] lib: add libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 current [diff against previous]
    6. v6 [diff against previous]
  24. [PATCHv5 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 current [diff against previous]
    6. v6 [diff against previous]
  25. [PATCHv5 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 current [diff against previous]
    6. v6 [diff against previous]
  26. [PATCHv6 iproute2-next 1/5] iproute2: add check_libbpf() and get_libbpf_version()
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 current [diff against previous]
  27. [PATCHv6 iproute2-next 2/5] lib: make ipvrf able to use libbpf and fix function name conflicts
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 current [diff against previous]
  28. [PATCHv6 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 current [diff against previous]
  29. [PATCHv6 iproute2-next 3/5] lib: add libbpf support
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 current [diff against previous]
  30. [PATCHv6 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder
    Revisions and related patches
    Revisions (6)
    1. v1
    2. v2 [diff against previous]
    3. v3 [diff against previous]
    4. v4 [diff against previous]
    5. v5 [diff against previous]
    6. v6 current [diff against previous]
Message details and related content
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.

Here are the test results with patched iproute2:

== setup env
# clang -O2 -Wall -g -target bpf -c bpf_graft.c -o btf_graft.o
# clang -O2 -Wall -g -target bpf -c bpf_map_in_map.c -o btf_map_in_map.o
# clang -O2 -Wall -g -target bpf -c bpf_shared.c -o btf_shared.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_cyclic.c -o bpf_cyclic.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_graft.c -o bpf_graft.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_map_in_map.c -o bpf_map_in_map.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_shared.c -o bpf_shared.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_tailcall.c -o bpf_tailcall.o
# rm -rf /sys/fs/bpf/xdp/globals
# /root/iproute2/ip/ip link add type veth
# /root/iproute2/ip/ip link set veth0 up
# /root/iproute2/ip/ip link set veth1 up

416 more lines

[PATCH iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 configure | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
diff --git a/configure b/configure
index 307912aa..77f475d9 100755
--- a/configure
+++ b/configure
@@ -240,6 +240,51 @@ check_elf()
     fi
 }
 
+check_libbpf()
+{
+    if ${PKG_CONFIG} libbpf --exists || [ -n "$LIBBPF_DIR" ] ; then
+
+        if [ -n "$LIBBPF_DIR" ]; then
+            LIBBPF_CFLAGS="-I${LIBBPF_DIR}/include -L${LIBBPF_DIR}/lib64"
+            LIBBPF_LDLIBS="${LIBBPF_DIR}/lib64/libbpf.a -lz -lelf"
+        else
+            LIBBPF_CFLAGS=$(${PKG_CONFIG} libbpf --cflags)
+            LIBBPF_LDLIBS=$(${PKG_CONFIG} libbpf --libs)
+        fi
+
+        cat >$TMPDIR/libbpftest.c <<EOF
+#include <bpf/libbpf.h>
+int main(int argc, char **argv) {
+    void *ptr;
+    DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, .relaxed_maps = true, .pin_root_path = "/path");
+    (void) bpf_object__open_file("file", &opts);
+    (void) bpf_map__name(ptr);
+    (void) bpf_map__ifindex(ptr);
+    (void) bpf_map__reuse_fd(ptr, 0);
+    (void) bpf_map__pin(ptr, "/path");
+    return 0;
+}
+EOF
+
+        if $CC -o $TMPDIR/libbpftest $TMPDIR/libbpftest.c $LIBBPF_CFLAGS -lbpf 2>&1; then
+            echo "HAVE_LIBBPF:=y" >>$CONFIG
+            echo 'CFLAGS += -DHAVE_LIBBPF ' $LIBBPF_CFLAGS >> $CONFIG
+            echo 'LDLIBS += ' $LIBBPF_LDLIBS >>$CONFIG
+            echo "yes"
+            return 0
+        fi
+    fi
+
+    echo "no"
+
+    # if set FORCE_LIBBPF but no libbpf support, just exist the config
+    # process to make sure we don't build without libbpf.
+    if [ -n "$FORCE_LIBBPF" ]; then
+	    echo "FORCE_LIBBPF set, but couldn't find a usable libbpf"
+	    exit 1
+    fi
+}
+
 check_selinux()
 # SELinux is a compile time option in the ss utility
 {
@@ -385,6 +430,9 @@ check_setns
 echo -n "SELinux support: "
 check_selinux
 
+echo -n "libbpf support: "
+check_libbpf
+
 echo -n "ELF support: "
 check_elf
 
-- 
2.25.4

[PATCH iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c

Message details and related content
This is a preparation for later libbpf support in iproute2. Function
bpf_prog_load() is also renamed to bpf_prog_load_buf() as there is a
conflict with libbpf.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 include/bpf_util.h          | 6 +++---
 ip/ipvrf.c                  | 4 ++--
 lib/Makefile                | 2 +-
 lib/{bpf.c => bpf_legacy.c} | 6 +++---
 4 files changed, 9 insertions(+), 9 deletions(-)
 rename lib/{bpf.c => bpf_legacy.c} (99%)
diff --git a/include/bpf_util.h b/include/bpf_util.h
index 63db07ca..72d3a32c 100644
--- a/include/bpf_util.h
+++ b/include/bpf_util.h
@@ -274,9 +274,9 @@ int bpf_trace_pipe(void);
 
 void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
 
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log);
+int bpf_prog_load_buf(enum bpf_prog_type type, const struct bpf_insn *insns,
+		      size_t size_insns, const char *license, char *log,
+		      size_t size_log);
 
 int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
 int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 28dd8e25..33150ac2 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -256,8 +256,8 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
-	return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
-			     "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
diff --git a/lib/Makefile b/lib/Makefile
index 7cba1857..a326fb9f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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_legacy.o exec.o fs.o cg_map.o
 
 NLOBJ=libgenl.o libnetlink.o
 
diff --git a/lib/bpf.c b/lib/bpf_legacy.c
similarity index 99%
rename from lib/bpf.c
rename to lib/bpf_legacy.c
index c7d45077..2e6e0602 100644
--- a/lib/bpf.c
+++ b/lib/bpf_legacy.c
@@ -1109,9 +1109,9 @@ static int bpf_prog_load_dev(enum bpf_prog_type type,
 	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
 }
 
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log)
+int bpf_prog_load_buf(enum bpf_prog_type type, const struct bpf_insn *insns,
+		      size_t size_insns, const char *license, char *log,
+		      size_t size_log)
 {
 	return bpf_prog_load_dev(type, insns, size_insns, license, 0,
 				 log, size_log);
-- 
2.25.4

[PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
This patch converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available, which is started by Toke's
implementation[1]. With libbpf iproute2 could correctly process BTF
information and support the new-style BTF-defined maps, while keeping
compatibility with the old internal map definition syntax.

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code in bpf_legacy.c ensures that
iproute2 will still understand the old map definition format, including
populating map-in-map and tail call maps before load.

In bpf_libbpf.c, we init iproute2 ctx and elf info first to check the
legacy bytes. When handling the legacy maps, for map-in-maps, we create
them manually and re-use the fd as they are associated with id/inner_id.
For pin maps, we only set the pin path and let libbp load to handle it.
For tail calls, we find it first and update the element after prog load.

Other maps/progs will be loaded by libbpf directly.

Note: ip/ipvrf.c is not convert to use libbpf as it only encodes a few
instructions and load directly.

[1] https://lore.kernel.org/bpf/20190820114706.18546-1-toke@redhat.com/

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 include/bpf_util.h |  11 ++
 lib/Makefile       |   4 +
 lib/bpf_legacy.c   | 178 ++++++++++++++++++++++++
 lib/bpf_libbpf.c   | 338 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 531 insertions(+)
 create mode 100644 lib/bpf_libbpf.c
diff --git a/include/bpf_util.h b/include/bpf_util.h
index 72d3a32c..e200c107 100644
--- a/include/bpf_util.h
+++ b/include/bpf_util.h
@@ -300,4 +300,15 @@ static inline int bpf_recv_map_fds(const char *path, int *fds,
 	return -1;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg);
+int iproute2_bpf_fetch_ancillary(void);
+int iproute2_get_root_path(char *root_path, size_t len);
+bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname);
+bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
+			    struct bpf_elf_map *omap, char *omap_name);
+int iproute2_find_map_name_by_id(unsigned int map_id, char *name);
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg);
+#endif /* HAVE_LIBBPF */
 #endif /* __BPF_UTIL__ */
diff --git a/lib/Makefile b/lib/Makefile
index a326fb9f..82d6e465 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -7,6 +7,10 @@ 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_legacy.o exec.o fs.o cg_map.o
 
+ifeq ($(HAVE_LIBBPF),y)
+UTILOBJ += bpf_libbpf.o
+endif
+
 NLOBJ=libgenl.o libnetlink.o
 
 all: libnetlink.a libutil.a
diff --git a/lib/bpf_legacy.c b/lib/bpf_legacy.c
index 2e6e0602..c5ff3e32 100644
--- a/lib/bpf_legacy.c
+++ b/lib/bpf_legacy.c
@@ -940,6 +940,9 @@ static int bpf_do_parse(struct bpf_cfg_in *cfg, const bool *opt_tbl)
 static int bpf_do_load(struct bpf_cfg_in *cfg)
 {
 	if (cfg->mode == EBPF_OBJECT) {
+#ifdef HAVE_LIBBPF
+		return iproute2_load_libbpf(cfg);
+#endif
 		cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
 					    cfg->section, cfg->ifindex,
 					    cfg->verbose);
@@ -3165,3 +3168,178 @@ int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
 	return ret;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+/* The following functions are wrapper functions for libbpf code to be
+ * compatible with the legacy format. So all the functions have prefix
+ * with iproute2_
+ */
+int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+
+	return bpf_elf_ctx_init(ctx, cfg->object, cfg->type, cfg->ifindex, cfg->verbose);
+}
+
+int iproute2_bpf_fetch_ancillary(void)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	struct bpf_elf_sec_data data;
+	int i, ret = 0;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		ret = bpf_fill_section_data(ctx, i, &data);
+		if (ret < 0)
+			continue;
+
+		if (data.sec_hdr.sh_type == SHT_PROGBITS &&
+		    !strcmp(data.sec_name, ELF_SECTION_MAPS))
+			ret = bpf_fetch_maps_begin(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
+			 !strcmp(data.sec_name, ".symtab"))
+			ret = bpf_fetch_symtab(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_STRTAB &&
+			 !strcmp(data.sec_name, ".strtab"))
+			ret = bpf_fetch_strtab(ctx, i, &data);
+		if (ret < 0) {
+			fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
+				i);
+			return ret;
+		}
+	}
+
+	if (bpf_has_map_data(ctx)) {
+		ret = bpf_fetch_maps_end(ctx);
+		if (ret < 0) {
+			fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+int iproute2_get_root_path(char *root_path, size_t len)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	int ret = 0;
+
+	snprintf(root_path, len, "%s/%s",
+		 bpf_get_work_dir(ctx->type), BPF_DIR_GLOBALS);
+
+	ret = mkdir(root_path, S_IRWXU);
+	if (ret && errno != EEXIST) {
+		fprintf(stderr, "mkdir %s failed: %s\n", root_path, strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *map_name, *tmp;
+	unsigned int pinning;
+	int i, ret = 0;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		if (ctx->maps[i].pinning == PIN_OBJECT_NS &&
+		    ctx->noafalg) {
+			fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n");
+			return false;
+		}
+
+		map_name = bpf_map_fetch_name(ctx, i);
+		if (!map_name) {
+			return false;
+		}
+
+		if (strcmp(libbpf_map_name, map_name))
+			continue;
+
+		pinning = ctx->maps[i].pinning;
+
+		if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
+			return false;
+
+		if (pinning == PIN_OBJECT_NS)
+			ret = bpf_make_obj_path(ctx);
+		else if ((tmp = bpf_custom_pinning(ctx, pinning)))
+			ret = bpf_make_custom_path(ctx, tmp);
+		if (ret < 0)
+			return false;
+
+		bpf_make_pathname(pathname, PATH_MAX, map_name, ctx, pinning);
+
+		return true;
+	}
+
+	return false;
+}
+
+bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
+			    struct bpf_elf_map *omap, char *omap_name)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *inner_map_name, *outer_map_name;
+	int i, j;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		inner_map_name = bpf_map_fetch_name(ctx, i);
+		if (!inner_map_name) {
+			return false;
+		}
+
+		if (strcmp(libbpf_map_name, inner_map_name))
+			continue;
+
+		if (!ctx->maps[i].id ||
+		    ctx->maps[i].inner_id ||
+		    ctx->maps[i].inner_idx == -1)
+			continue;
+
+		*imap = ctx->maps[i];
+
+		for (j = 0; j < ctx->map_num; j++) {
+			if (!bpf_is_map_in_map_type(&ctx->maps[j]))
+				continue;
+			if (ctx->maps[j].inner_id != ctx->maps[i].id)
+				continue;
+
+			*omap = ctx->maps[j];
+			outer_map_name = bpf_map_fetch_name(ctx, j);
+			memcpy(omap_name, outer_map_name, strlen(outer_map_name) + 1);
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+int iproute2_find_map_name_by_id(unsigned int map_id, char *name)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *map_name;
+	int i, idx = -1;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		if (ctx->maps[i].id == map_id &&
+		    ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) {
+			idx = i;
+			break;
+		}
+	}
+
+	if (idx < 0)
+		return -1;
+
+	map_name = bpf_map_fetch_name(ctx, idx);
+	if (!map_name)
+		return -1;
+
+	memcpy(name, map_name, strlen(map_name) + 1);
+	return 0;
+}
+#endif /* HAVE_LIBBPF */
diff --git a/lib/bpf_libbpf.c b/lib/bpf_libbpf.c
new file mode 100644
index 00000000..9e3b9787
--- /dev/null
+++ b/lib/bpf_libbpf.c
@@ -0,0 +1,338 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+
+#include "bpf_util.h"
+
+#define MAX_ERRNO       4095
+#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline bool IS_ERR_OR_NULL(const void *ptr)
+{
+	return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
+}
+
+static int verbose_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+static int silent_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+	if (level > LIBBPF_WARN)
+		return 0;
+
+	/* Skip warning from bpf_object__init_user_maps() for legacy maps */
+	if (strstr(format, "has unrecognized, non-zero options"))
+		return 0;
+
+	return vfprintf(stderr, format, args);
+}
+
+static int create_map(const char *name, struct bpf_elf_map *map,
+		      __u32 ifindex, int inner_fd)
+{
+	struct bpf_create_map_attr map_attr = {};
+
+	map_attr.name = name;
+	map_attr.map_type = map->type;
+	map_attr.map_flags = map->flags;
+	map_attr.key_size = map->size_key;
+	map_attr.value_size = map->size_value;
+	map_attr.max_entries = map->max_elem;
+	map_attr.map_ifindex = ifindex;
+	map_attr.inner_map_fd = inner_fd;
+
+	return bpf_create_map_xattr(&map_attr);
+}
+
+static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
+			     struct bpf_elf_map *elf_map, int inner_fd,
+			     bool *reuse_pin_map)
+{
+	char pathname[PATH_MAX];
+	const char *map_name;
+	bool pin_map = false;
+	int map_fd, ret = 0;
+
+	map_name = bpf_map__name(map);
+
+	if (iproute2_is_pin_map(map_name, pathname)) {
+		pin_map = true;
+
+		/* Check if there already has a pinned map */
+		map_fd = bpf_obj_get(pathname);
+		if (map_fd > 0) {
+			if (reuse_pin_map)
+				*reuse_pin_map = true;
+			close(map_fd);
+			return bpf_map__set_pin_path(map, pathname);
+		}
+	}
+
+	map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
+	if (map_fd < 0) {
+		fprintf(stderr, "create map %s failed\n", map_name);
+		return map_fd;
+	}
+
+	ret = bpf_map__reuse_fd(map, map_fd);
+	if (ret < 0) {
+		fprintf(stderr, "map %s reuse fd failed\n", map_name);
+		goto err_out;
+	}
+
+	if (pin_map) {
+		ret = bpf_map__set_pin_path(map, pathname);
+		if (ret < 0)
+			goto err_out;
+	}
+
+	return 0;
+err_out:
+	close(map_fd);
+	return ret;
+}
+
+static int
+handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
+			 const char *inner_map_name)
+{
+	int inner_fd, outer_fd, inner_idx, ret = 0;
+	struct bpf_elf_map imap, omap;
+	struct bpf_map *outer_map;
+	/* What's the size limit of map name? */
+	char outer_map_name[128];
+	bool reuse_pin_map = false;
+
+	/* Deal with map-in-map */
+	if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
+		ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
+		if (ret < 0)
+			return ret;
+
+		inner_fd = bpf_map__fd(inner_map);
+		outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
+		ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
+		if (ret < 0)
+			return ret;
+
+		if (!reuse_pin_map) {
+			inner_idx = imap.inner_idx;
+			outer_fd = bpf_map__fd(outer_map);
+			ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
+			if (ret < 0)
+				fprintf(stderr, "Cannot update inner_idx into outer_map\n");
+		}
+	}
+
+	return ret;
+}
+
+static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj)
+{
+	unsigned int map_id, key_id;
+	const char *sec_name;
+	struct bpf_map *map;
+	char map_name[128];
+	int ret;
+
+	/* Handle iproute2 tail call */
+	sec_name = bpf_program__section_name(prog);
+	ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+	if (ret != 2)
+		return -1;
+
+	ret = iproute2_find_map_name_by_id(map_id, map_name);
+	if (ret < 0) {
+		fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
+		return ret;
+	}
+
+	map = bpf_object__find_map_by_name(obj, map_name);
+	if (!map)
+		return -1;
+
+	/* Save the map here for later updating */
+	bpf_program__set_priv(prog, map, NULL);
+
+	return 0;
+}
+
+static int update_legacy_tail_call_maps(struct bpf_object *obj)
+{
+	int prog_fd, map_fd, ret = 0;
+	unsigned int map_id, key_id;
+	struct bpf_program *prog;
+	const char *sec_name;
+	struct bpf_map *map;
+
+	bpf_object__for_each_program(prog, obj) {
+		map = bpf_program__priv(prog);
+		if (!map)
+			continue;
+
+		prog_fd = bpf_program__fd(prog);
+		if (prog_fd < 0)
+			continue;
+
+		sec_name = bpf_program__section_name(prog);
+		ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+		if (ret != 2)
+			continue;
+
+		map_fd = bpf_map__fd(map);
+		ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
+		if (ret < 0) {
+			fprintf(stderr, "Cannot update map key for tail call!\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int handle_legacy_maps(struct bpf_object *obj)
+{
+	char pathname[PATH_MAX];
+	struct bpf_map *map;
+	const char *map_name;
+	int map_fd, ret = 0;
+
+	bpf_object__for_each_map(map, obj) {
+		map_name = bpf_map__name(map);
+
+		ret = handle_legacy_map_in_map(obj, map, map_name);
+		if (ret)
+			return ret;
+
+		/* If it is a iproute2 legacy pin maps, just set pin path
+		 * and let bpf_object__load() to deal with the map creation.
+		 * We need to ignore map-in-maps which have pinned maps manually
+		 */
+		map_fd = bpf_map__fd(map);
+		if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
+			ret = bpf_map__set_pin_path(map, pathname);
+			if (ret) {
+				fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
+				break;
+			}
+		}
+
+	}
+
+	return ret;
+}
+
+static int load_bpf_object(struct bpf_cfg_in *cfg)
+{
+	struct bpf_program *p, *prog = NULL;
+	struct bpf_object *obj;
+	char root_path[PATH_MAX];
+	struct bpf_map *map;
+	int prog_fd, ret = 0;
+
+	ret = iproute2_get_root_path(root_path, PATH_MAX);
+	if (ret)
+		return ret;
+
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
+			.relaxed_maps = true,
+			.pin_root_path = root_path,
+	);
+
+	obj = bpf_object__open_file(cfg->object, &open_opts);
+	if (IS_ERR_OR_NULL(obj))
+		return -ENOENT;
+
+	bpf_object__for_each_program(p, obj) {
+		/* Only load the programs that will either be subsequently
+		 * attached or inserted into a tail call map */
+		if (find_legacy_tail_calls(p, obj) < 0 && cfg->section &&
+		    strcmp(bpf_program__section_name(p), cfg->section)) {
+			ret = bpf_program__set_autoload(p, false);
+			if (ret)
+				return -EINVAL;
+			continue;
+		}
+
+		bpf_program__set_type(p, cfg->type);
+		bpf_program__set_ifindex(p, cfg->ifindex);
+		if (!prog)
+			prog = p;
+	}
+
+	bpf_object__for_each_map(map, obj) {
+		if (!bpf_map__is_offload_neutral(map))
+			bpf_map__set_ifindex(map, cfg->ifindex);
+	}
+
+	if (!prog) {
+		fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
+		return -ENOENT;
+	}
+
+	/* Handle iproute2 legacy pin maps and map-in-maps */
+	ret = handle_legacy_maps(obj);
+	if (ret)
+		goto unload_obj;
+
+	ret = bpf_object__load(obj);
+	if (ret)
+		goto unload_obj;
+
+	ret = update_legacy_tail_call_maps(obj);
+	if (ret)
+		goto unload_obj;
+
+	prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
+	if (prog_fd < 0)
+		ret = -errno;
+	else
+		cfg->prog_fd = prog_fd;
+
+unload_obj:
+	/* Close obj as we don't need it */
+	bpf_object__close(obj);
+	return ret;
+}
+
+/* Load ebpf and return prog fd */
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
+{
+	int ret = 0;
+
+	if (cfg->verbose)
+		libbpf_set_print(verbose_print);
+	else
+		libbpf_set_print(silent_print);
+
+	ret = iproute2_bpf_elf_ctx_init(cfg);
+	if (ret < 0) {
+		fprintf(stderr, "Cannot initialize ELF context!\n");
+		return ret;
+	}
+
+	ret = iproute2_bpf_fetch_ancillary();
+	if (ret < 0) {
+		fprintf(stderr, "Error fetching ELF ancillary data!\n");
+		return ret;
+	}
+
+	ret = load_bpf_object(cfg);
+	if (ret)
+		return ret;
+
+	return cfg->prog_fd;
+}
-- 
2.25.4

[PATCH iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder

Message details and related content
Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 examples/bpf/README                        | 14 +++++++++-----
 examples/bpf/{ => legacy}/bpf_cyclic.c     |  2 +-
 examples/bpf/{ => legacy}/bpf_graft.c      |  2 +-
 examples/bpf/{ => legacy}/bpf_map_in_map.c |  2 +-
 examples/bpf/{ => legacy}/bpf_shared.c     |  2 +-
 examples/bpf/{ => legacy}/bpf_tailcall.c   |  2 +-
 6 files changed, 14 insertions(+), 10 deletions(-)
 rename examples/bpf/{ => legacy}/bpf_cyclic.c (95%)
 rename examples/bpf/{ => legacy}/bpf_graft.c (97%)
 rename examples/bpf/{ => legacy}/bpf_map_in_map.c (96%)
 rename examples/bpf/{ => legacy}/bpf_shared.c (97%)
 rename examples/bpf/{ => legacy}/bpf_tailcall.c (98%)
diff --git a/examples/bpf/README b/examples/bpf/README
index 1bbdda3f..732bcc83 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,8 +1,12 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
- - bpf_shared.c		-> Ingress/egress map sharing example
- - bpf_tailcall.c	-> Using tail call chains
- - bpf_cyclic.c		-> Simple cycle as tail calls
- - bpf_graft.c		-> Demo on altering runtime behaviour
- - bpf_map_in_map.c     -> Using map in map example
+ - legacy/bpf_shared.c		-> Ingress/egress map sharing example
+ - legacy/bpf_tailcall.c	-> Using tail call chains
+ - legacy/bpf_cyclic.c		-> Simple cycle as tail calls
+ - legacy/bpf_graft.c		-> Demo on altering runtime behaviour
+ - legacy/bpf_map_in_map.c	-> Using map in map example
+
+Note: Users should use new BTF way to defined the maps, the examples
+in legacy folder which is using struct bpf_elf_map defined maps is not
+recommanded.
diff --git a/examples/bpf/bpf_cyclic.c b/examples/bpf/legacy/bpf_cyclic.c
similarity index 95%
rename from examples/bpf/bpf_cyclic.c
rename to examples/bpf/legacy/bpf_cyclic.c
index 11d1c061..33590730 100644
--- a/examples/bpf/bpf_cyclic.c
+++ b/examples/bpf/legacy/bpf_cyclic.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* Cyclic dependency example to test the kernel's runtime upper
  * bound on loops. Also demonstrates on how to use direct-actions,
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/legacy/bpf_graft.c
similarity index 97%
rename from examples/bpf/bpf_graft.c
rename to examples/bpf/legacy/bpf_graft.c
index 07113d4a..f4c920cc 100644
--- a/examples/bpf/bpf_graft.c
+++ b/examples/bpf/legacy/bpf_graft.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* This example demonstrates how classifier run-time behaviour
  * can be altered with tail calls. We start out with an empty
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/legacy/bpf_map_in_map.c
similarity index 96%
rename from examples/bpf/bpf_map_in_map.c
rename to examples/bpf/legacy/bpf_map_in_map.c
index ff0e623a..575f8812 100644
--- a/examples/bpf/bpf_map_in_map.c
+++ b/examples/bpf/legacy/bpf_map_in_map.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 #define MAP_INNER_ID	42
 
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/legacy/bpf_shared.c
similarity index 97%
rename from examples/bpf/bpf_shared.c
rename to examples/bpf/legacy/bpf_shared.c
index 21fe6f1e..05b2b9ef 100644
--- a/examples/bpf/bpf_shared.c
+++ b/examples/bpf/legacy/bpf_shared.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* Minimal, stand-alone toy map pinning example:
  *
diff --git a/examples/bpf/bpf_tailcall.c b/examples/bpf/legacy/bpf_tailcall.c
similarity index 98%
rename from examples/bpf/bpf_tailcall.c
rename to examples/bpf/legacy/bpf_tailcall.c
index 161eb606..8ebc554c 100644
--- a/examples/bpf/bpf_tailcall.c
+++ b/examples/bpf/legacy/bpf_tailcall.c
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 #define ENTRY_INIT	3
 #define ENTRY_0		0
-- 
2.25.4

[PATCH iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps

Message details and related content
Users should try use the new BTF defined maps instead of struct
bpf_elf_map defined maps. The tail call examples are not added yet
as libbpf doesn't currently support declaratively populating tail call
maps.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 examples/bpf/README           |  6 ++++
 examples/bpf/bpf_graft.c      | 66 +++++++++++++++++++++++++++++++++++
 examples/bpf/bpf_map_in_map.c | 55 +++++++++++++++++++++++++++++
 examples/bpf/bpf_shared.c     | 53 ++++++++++++++++++++++++++++
 include/bpf_api.h             | 13 +++++++
 5 files changed, 193 insertions(+)
 create mode 100644 examples/bpf/bpf_graft.c
 create mode 100644 examples/bpf/bpf_map_in_map.c
 create mode 100644 examples/bpf/bpf_shared.c
diff --git a/examples/bpf/README b/examples/bpf/README
index 732bcc83..b7261191 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,6 +1,12 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
+- BTF defined map examples
+ - bpf_graft.c		-> Demo on altering runtime behaviour
+ - bpf_shared.c 	-> Ingress/egress map sharing example
+ - bpf_map_in_map.c	-> Using map in map example
+
+- legacy struct bpf_elf_map defined map examples
  - legacy/bpf_shared.c		-> Ingress/egress map sharing example
  - legacy/bpf_tailcall.c	-> Using tail call chains
  - legacy/bpf_cyclic.c		-> Simple cycle as tail calls
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/bpf_graft.c
new file mode 100644
index 00000000..8066dcce
--- /dev/null
+++ b/examples/bpf/bpf_graft.c
@@ -0,0 +1,66 @@
+#include "../../include/bpf_api.h"
+
+/* This example demonstrates how classifier run-time behaviour
+ * can be altered with tail calls. We start out with an empty
+ * jmp_tc array, then add section aaa to the array slot 0, and
+ * later on atomically replace it with section bbb. Note that
+ * as shown in other examples, the tc loader can prepopulate
+ * tail called sections, here we start out with an empty one
+ * on purpose to show it can also be done this way.
+ *
+ * tc filter add dev foo parent ffff: bpf obj graft.o
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-20229 [001] ..s. 138993.003923: : fallthrough
+ *   <idle>-0            [001] ..s. 138993.202265: : fallthrough
+ *   Socket Thread-20229 [001] ..s. 138994.004149: : fallthrough
+ *   [...]
+ *
+ * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec aaa
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-19818 [002] ..s. 139012.053587: : aaa
+ *   <idle>-0            [002] ..s. 139012.172359: : aaa
+ *   Socket Thread-19818 [001] ..s. 139012.173556: : aaa
+ *   [...]
+ *
+ * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec bbb
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-19818 [002] ..s. 139022.102967: : bbb
+ *   <idle>-0            [002] ..s. 139022.155640: : bbb
+ *   Socket Thread-19818 [001] ..s. 139022.156730: : bbb
+ *   [...]
+ */
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);
+} jmp_tc __section(".maps");
+
+__section("aaa")
+int cls_aaa(struct __sk_buff *skb)
+{
+	printt("aaa\n");
+	return TC_H_MAKE(1, 42);
+}
+
+__section("bbb")
+int cls_bbb(struct __sk_buff *skb)
+{
+	printt("bbb\n");
+	return TC_H_MAKE(1, 43);
+}
+
+__section_cls_entry
+int cls_entry(struct __sk_buff *skb)
+{
+	tail_call(skb, &jmp_tc, 0);
+	printt("fallthrough\n");
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/bpf_map_in_map.c
new file mode 100644
index 00000000..39c86268
--- /dev/null
+++ b/examples/bpf/bpf_map_in_map.c
@@ -0,0 +1,55 @@
+#include "../../include/bpf_api.h"
+
+struct inner_map {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+} map_inner __section(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);
+	__array(values, struct inner_map);
+} map_outer __section(".maps") = {
+	.values = {
+		[0] = &map_inner,
+	},
+};
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			lock_xadd(val, 1);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			printt("map val: %d\n", *val);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/bpf_shared.c
new file mode 100644
index 00000000..99a332f4
--- /dev/null
+++ b/examples/bpf/bpf_shared.c
@@ -0,0 +1,53 @@
+#include "../../include/bpf_api.h"
+
+/* Minimal, stand-alone toy map pinning example:
+ *
+ * clang -target bpf -O2 [...] -o bpf_shared.o -c bpf_shared.c
+ * tc filter add dev foo parent 1: bpf obj bpf_shared.o sec egress
+ * tc filter add dev foo parent ffff: bpf obj bpf_shared.o sec ingress
+ *
+ * Both classifier will share the very same map instance in this example,
+ * so map content can be accessed from ingress *and* egress side!
+ *
+ * This example has a pinning of PIN_OBJECT_NS, so it's private and
+ * thus shared among various program sections within the object.
+ *
+ * A setting of PIN_GLOBAL_NS would place it into a global namespace,
+ * so that it can be shared among different object files. A setting
+ * of PIN_NONE (= 0) means no sharing, so each tc invocation a new map
+ * instance is being created.
+ */
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);	/* or LIBBPF_PIN_NONE */
+} map_sh __section(".maps");
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	int key = 0, *val;
+
+	val = map_lookup_elem(&map_sh, &key);
+	if (val)
+		lock_xadd(val, 1);
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	int key = 0, *val;
+
+	val = map_lookup_elem(&map_sh, &key);
+	if (val)
+		printt("map val: %d\n", *val);
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/include/bpf_api.h b/include/bpf_api.h
index 89d3488d..82c47089 100644
--- a/include/bpf_api.h
+++ b/include/bpf_api.h
@@ -19,6 +19,19 @@
 
 #include "bpf_elf.h"
 
+/** libbpf pin type. */
+enum libbpf_pin_type {
+	LIBBPF_PIN_NONE,
+	/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
+	LIBBPF_PIN_BY_NAME,
+};
+
+/** Type helper macros. */
+
+#define __uint(name, val) int (*name)[val]
+#define __type(name, val) typeof(val) *name
+#define __array(name, val) typeof(val) *name[]
+
 /** Misc macros. */
 
 #ifndef __stringify
-- 
2.25.4

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
On Thu, Oct 22, 2020 at 8:39 PM Hangbin Liu [off-list ref] wrote:
This patch converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available, which is started by Toke's
implementation[1]. With libbpf iproute2 could correctly process BTF
information and support the new-style BTF-defined maps, while keeping
compatibility with the old internal map definition syntax.

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code in bpf_legacy.c ensures that
iproute2 will still understand the old map definition format, including
populating map-in-map and tail call maps before load.

In bpf_libbpf.c, we init iproute2 ctx and elf info first to check the
legacy bytes. When handling the legacy maps, for map-in-maps, we create
them manually and re-use the fd as they are associated with id/inner_id.
For pin maps, we only set the pin path and let libbp load to handle it.
For tail calls, we find it first and update the element after prog load.
I never implemented tail call map initialization using the same
approach as declarative map-in-map support in libbpf, because no one
asked and/or showed a use case. But all the pieces are there, and if
there's interest, we should probably support that in libbpf as well.
Other maps/progs will be loaded by libbpf directly.

Note: ip/ipvrf.c is not convert to use libbpf as it only encodes a few
instructions and load directly.

[1] https://lore.kernel.org/bpf/20190820114706.18546-1-toke@redhat.com/

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 include/bpf_util.h |  11 ++
 lib/Makefile       |   4 +
 lib/bpf_legacy.c   | 178 ++++++++++++++++++++++++
 lib/bpf_libbpf.c   | 338 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 531 insertions(+)
 create mode 100644 lib/bpf_libbpf.c
[...]
quoted hunk
+
+static int load_bpf_object(struct bpf_cfg_in *cfg)
+{
+       struct bpf_program *p, *prog = NULL;
+       struct bpf_object *obj;
+       char root_path[PATH_MAX];
+       struct bpf_map *map;
+       int prog_fd, ret = 0;
+
+       ret = iproute2_get_root_path(root_path, PATH_MAX);
+       if (ret)
+               return ret;
+
+       DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
+                       .relaxed_maps = true,
+                       .pin_root_path = root_path,
+       );
+
+       obj = bpf_object__open_file(cfg->object, &open_opts);
+       if (IS_ERR_OR_NULL(obj))
libbpf defines libbpf_get_error() to check that the returned pointer
is not encoding error, you shouldn't need to define your IS_ERR
macros.
+               return -ENOENT;
+
[...]

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
Andrii Nakryiko [off-list ref] writes:
On Thu, Oct 22, 2020 at 8:39 PM Hangbin Liu [off-list ref] wrote:
quoted
This patch converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available, which is started by Toke's
implementation[1]. With libbpf iproute2 could correctly process BTF
information and support the new-style BTF-defined maps, while keeping
compatibility with the old internal map definition syntax.

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code in bpf_legacy.c ensures that
iproute2 will still understand the old map definition format, including
populating map-in-map and tail call maps before load.

In bpf_libbpf.c, we init iproute2 ctx and elf info first to check the
legacy bytes. When handling the legacy maps, for map-in-maps, we create
them manually and re-use the fd as they are associated with id/inner_id.
For pin maps, we only set the pin path and let libbp load to handle it.
For tail calls, we find it first and update the element after prog load.
I never implemented tail call map initialization using the same
approach as declarative map-in-map support in libbpf, because no one
asked and/or showed a use case. But all the pieces are there, and if
there's interest, we should probably support that in libbpf as well.
Yeah, that's what we figured; and since this series maintains
compatibility with the old map definition format for declarative
tail-calls, this doesn't have to hold up the conversion: iproute2 will
just magically gain this when/if it lands in libbpf :)

-Toke

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
David Ahern [off-list ref] writes:
On 10/22/20 9:38 PM, Hangbin Liu wrote:
quoted
Note: ip/ipvrf.c is not convert to use libbpf as it only encodes a few
instructions and load directly.
for completeness, libbpf should be able to load a program from a buffer
as well.
It can, but the particular use in ipvrf is just loading half a dozen
instructions defined inline in C - there's no object files, BTF or
anything. So why bother with going through libbpf in this case? The
actual attachment is using the existing code anyway...

-Toke

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
On 10/25/20 9:13 AM, Toke Høiland-Jørgensen wrote:
David Ahern [off-list ref] writes:
quoted
On 10/22/20 9:38 PM, Hangbin Liu wrote:
quoted
Note: ip/ipvrf.c is not convert to use libbpf as it only encodes a few
instructions and load directly.
for completeness, libbpf should be able to load a program from a buffer
as well.
It can, but the particular use in ipvrf is just loading half a dozen
instructions defined inline in C - there's no object files, BTF or
anything. So why bother with going through libbpf in this case? The
actual attachment is using the existing code anyway...
actually, it already does: bpf_load_program

I recalled figuring out how to do it, just did not remember if it was
local changes to libbpf. Does not look like any changes were needed:

https://github.com/dsahern/bpf-progs/blob/master/src/cgroup_sock.c

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
On Fri, Oct 23, 2020 at 05:21:20PM -0700, Andrii Nakryiko wrote:
quoted
+       obj = bpf_object__open_file(cfg->object, &open_opts);
+       if (IS_ERR_OR_NULL(obj))
libbpf defines libbpf_get_error() to check that the returned pointer
is not encoding error, you shouldn't need to define your IS_ERR
macros.
Thanks for this tip, I will fix it in next version.

Hangbin

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
Hi David,

On Sun, Oct 25, 2020 at 04:12:34PM -0600, David Ahern wrote:
On 10/25/20 9:13 AM, Toke Høiland-Jørgensen wrote:
quoted
David Ahern [off-list ref] writes:
quoted
On 10/22/20 9:38 PM, Hangbin Liu wrote:
quoted
Note: ip/ipvrf.c is not convert to use libbpf as it only encodes a few
instructions and load directly.
for completeness, libbpf should be able to load a program from a buffer
as well.
It can, but the particular use in ipvrf is just loading half a dozen
instructions defined inline in C - there's no object files, BTF or
anything. So why bother with going through libbpf in this case? The
actual attachment is using the existing code anyway...
actually, it already does: bpf_load_program
Thanks for this info. Do you want to convert ipvrf.c to:
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;
 	}
 
+#ifdef HAVE_LIBBPF
+	if (bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0)) {
+#else
 	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
+#endif
 		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
 			strerror(errno));
 		goto out;
Thanks
Hangbin

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
On 10/26/20 2:56 AM, Hangbin Liu wrote:
quoted hunk
Hi David,

On Sun, Oct 25, 2020 at 04:12:34PM -0600, David Ahern wrote:
quoted
On 10/25/20 9:13 AM, Toke Høiland-Jørgensen wrote:
quoted
David Ahern [off-list ref] writes:
quoted
On 10/22/20 9:38 PM, Hangbin Liu wrote:
quoted
Note: ip/ipvrf.c is not convert to use libbpf as it only encodes a few
instructions and load directly.
for completeness, libbpf should be able to load a program from a buffer
as well.
It can, but the particular use in ipvrf is just loading half a dozen
instructions defined inline in C - there's no object files, BTF or
anything. So why bother with going through libbpf in this case? The
actual attachment is using the existing code anyway...
actually, it already does: bpf_load_program
Thanks for this info. Do you want to convert ipvrf.c to:
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;

11 more quoted lines

works for me. The rename in patch 2 can be dropped as well correct?

Re: [PATCH iproute2-next 3/5] lib: add libbpf support

Message details and related content
On Mon, Oct 26, 2020 at 09:15:00AM -0600, David Ahern wrote:
quoted
quoted
actually, it already does: bpf_load_program
Thanks for this info. Do you want to convert ipvrf.c to:
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;
 	}
 
+#ifdef HAVE_LIBBPF
+	if (bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0)) {
+#else
 	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
+#endif
 		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
 			strerror(errno));
 		goto out;
works for me. The rename in patch 2 can be dropped as well correct?
No, the BPF_MOV64_* micros are not defined in uapi, so we still need include
"bpf_util.h", which will got bpf_prog_load() conflicts.

Thanks
Hangbin

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 09:25:24PM +0800, Hangbin Liu wrote:
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).
Awesome to see this work continue! Thank you.

[PATCHv2 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder

Message details and related content
Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 examples/bpf/README                        | 14 +++++++++-----
 examples/bpf/{ => legacy}/bpf_cyclic.c     |  2 +-
 examples/bpf/{ => legacy}/bpf_graft.c      |  2 +-
 examples/bpf/{ => legacy}/bpf_map_in_map.c |  2 +-
 examples/bpf/{ => legacy}/bpf_shared.c     |  2 +-
 examples/bpf/{ => legacy}/bpf_tailcall.c   |  2 +-
 6 files changed, 14 insertions(+), 10 deletions(-)
 rename examples/bpf/{ => legacy}/bpf_cyclic.c (95%)
 rename examples/bpf/{ => legacy}/bpf_graft.c (97%)
 rename examples/bpf/{ => legacy}/bpf_map_in_map.c (96%)
 rename examples/bpf/{ => legacy}/bpf_shared.c (97%)
 rename examples/bpf/{ => legacy}/bpf_tailcall.c (98%)
diff --git a/examples/bpf/README b/examples/bpf/README
index 1bbdda3f..732bcc83 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,8 +1,12 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
- - bpf_shared.c		-> Ingress/egress map sharing example
- - bpf_tailcall.c	-> Using tail call chains
- - bpf_cyclic.c		-> Simple cycle as tail calls
- - bpf_graft.c		-> Demo on altering runtime behaviour
- - bpf_map_in_map.c     -> Using map in map example
+ - legacy/bpf_shared.c		-> Ingress/egress map sharing example
+ - legacy/bpf_tailcall.c	-> Using tail call chains
+ - legacy/bpf_cyclic.c		-> Simple cycle as tail calls
+ - legacy/bpf_graft.c		-> Demo on altering runtime behaviour
+ - legacy/bpf_map_in_map.c	-> Using map in map example
+
+Note: Users should use new BTF way to defined the maps, the examples
+in legacy folder which is using struct bpf_elf_map defined maps is not
+recommanded.
diff --git a/examples/bpf/bpf_cyclic.c b/examples/bpf/legacy/bpf_cyclic.c
similarity index 95%
rename from examples/bpf/bpf_cyclic.c
rename to examples/bpf/legacy/bpf_cyclic.c
index 11d1c061..33590730 100644
--- a/examples/bpf/bpf_cyclic.c
+++ b/examples/bpf/legacy/bpf_cyclic.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* Cyclic dependency example to test the kernel's runtime upper
  * bound on loops. Also demonstrates on how to use direct-actions,
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/legacy/bpf_graft.c
similarity index 97%
rename from examples/bpf/bpf_graft.c
rename to examples/bpf/legacy/bpf_graft.c
index 07113d4a..f4c920cc 100644
--- a/examples/bpf/bpf_graft.c
+++ b/examples/bpf/legacy/bpf_graft.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* This example demonstrates how classifier run-time behaviour
  * can be altered with tail calls. We start out with an empty
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/legacy/bpf_map_in_map.c
similarity index 96%
rename from examples/bpf/bpf_map_in_map.c
rename to examples/bpf/legacy/bpf_map_in_map.c
index ff0e623a..575f8812 100644
--- a/examples/bpf/bpf_map_in_map.c
+++ b/examples/bpf/legacy/bpf_map_in_map.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 #define MAP_INNER_ID	42
 
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/legacy/bpf_shared.c
similarity index 97%
rename from examples/bpf/bpf_shared.c
rename to examples/bpf/legacy/bpf_shared.c
index 21fe6f1e..05b2b9ef 100644
--- a/examples/bpf/bpf_shared.c
+++ b/examples/bpf/legacy/bpf_shared.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* Minimal, stand-alone toy map pinning example:
  *
diff --git a/examples/bpf/bpf_tailcall.c b/examples/bpf/legacy/bpf_tailcall.c
similarity index 98%
rename from examples/bpf/bpf_tailcall.c
rename to examples/bpf/legacy/bpf_tailcall.c
index 161eb606..8ebc554c 100644
--- a/examples/bpf/bpf_tailcall.c
+++ b/examples/bpf/legacy/bpf_tailcall.c
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 #define ENTRY_INIT	3
 #define ENTRY_0		0
-- 
2.25.4

[PATCHv2 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 configure | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
diff --git a/configure b/configure
index 307912aa..77f475d9 100755
--- a/configure
+++ b/configure
@@ -240,6 +240,51 @@ check_elf()
     fi
 }
 
+check_libbpf()
+{
+    if ${PKG_CONFIG} libbpf --exists || [ -n "$LIBBPF_DIR" ] ; then
+
+        if [ -n "$LIBBPF_DIR" ]; then
+            LIBBPF_CFLAGS="-I${LIBBPF_DIR}/include -L${LIBBPF_DIR}/lib64"
+            LIBBPF_LDLIBS="${LIBBPF_DIR}/lib64/libbpf.a -lz -lelf"
+        else
+            LIBBPF_CFLAGS=$(${PKG_CONFIG} libbpf --cflags)
+            LIBBPF_LDLIBS=$(${PKG_CONFIG} libbpf --libs)
+        fi
+
+        cat >$TMPDIR/libbpftest.c <<EOF
+#include <bpf/libbpf.h>
+int main(int argc, char **argv) {
+    void *ptr;
+    DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, .relaxed_maps = true, .pin_root_path = "/path");
+    (void) bpf_object__open_file("file", &opts);
+    (void) bpf_map__name(ptr);
+    (void) bpf_map__ifindex(ptr);
+    (void) bpf_map__reuse_fd(ptr, 0);
+    (void) bpf_map__pin(ptr, "/path");
+    return 0;
+}
+EOF
+
+        if $CC -o $TMPDIR/libbpftest $TMPDIR/libbpftest.c $LIBBPF_CFLAGS -lbpf 2>&1; then
+            echo "HAVE_LIBBPF:=y" >>$CONFIG
+            echo 'CFLAGS += -DHAVE_LIBBPF ' $LIBBPF_CFLAGS >> $CONFIG
+            echo 'LDLIBS += ' $LIBBPF_LDLIBS >>$CONFIG
+            echo "yes"
+            return 0
+        fi
+    fi
+
+    echo "no"
+
+    # if set FORCE_LIBBPF but no libbpf support, just exist the config
+    # process to make sure we don't build without libbpf.
+    if [ -n "$FORCE_LIBBPF" ]; then
+	    echo "FORCE_LIBBPF set, but couldn't find a usable libbpf"
+	    exit 1
+    fi
+}
+
 check_selinux()
 # SELinux is a compile time option in the ss utility
 {
@@ -385,6 +430,9 @@ check_setns
 echo -n "SELinux support: "
 check_selinux
 
+echo -n "libbpf support: "
+check_libbpf
+
 echo -n "ELF support: "
 check_elf
 
-- 
2.25.4

[PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Revisions (6)
  1. v1
  2. v2 current [diff against previous]
  3. v3 [diff against previous]
  4. v4 [diff against previous]
  5. v5 [diff against previous]
  6. v6 [diff against previous]
Message details and related content
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.


v2:
a) Remove self defined IS_ERR_OR_NULL and use libbpf_get_error() instead.
b) Add ipvrf with libbpf support.


Here are the test results with patched iproute2:

== setup env
# clang -O2 -Wall -g -target bpf -c bpf_graft.c -o btf_graft.o
# clang -O2 -Wall -g -target bpf -c bpf_map_in_map.c -o btf_map_in_map.o
# clang -O2 -Wall -g -target bpf -c bpf_shared.c -o btf_shared.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_cyclic.c -o bpf_cyclic.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_graft.c -o bpf_graft.o
# clang -O2 -Wall -g -target bpf -c legacy/bpf_map_in_map.c -o bpf_map_in_map.o

421 more lines

[PATCHv2 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps

Message details and related content
Users should try use the new BTF defined maps instead of struct
bpf_elf_map defined maps. The tail call examples are not added yet
as libbpf doesn't currently support declaratively populating tail call
maps.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 examples/bpf/README           |  6 ++++
 examples/bpf/bpf_graft.c      | 66 +++++++++++++++++++++++++++++++++++
 examples/bpf/bpf_map_in_map.c | 55 +++++++++++++++++++++++++++++
 examples/bpf/bpf_shared.c     | 53 ++++++++++++++++++++++++++++
 include/bpf_api.h             | 13 +++++++
 5 files changed, 193 insertions(+)
 create mode 100644 examples/bpf/bpf_graft.c
 create mode 100644 examples/bpf/bpf_map_in_map.c
 create mode 100644 examples/bpf/bpf_shared.c
diff --git a/examples/bpf/README b/examples/bpf/README
index 732bcc83..b7261191 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,6 +1,12 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
+- BTF defined map examples
+ - bpf_graft.c		-> Demo on altering runtime behaviour
+ - bpf_shared.c 	-> Ingress/egress map sharing example
+ - bpf_map_in_map.c	-> Using map in map example
+
+- legacy struct bpf_elf_map defined map examples
  - legacy/bpf_shared.c		-> Ingress/egress map sharing example
  - legacy/bpf_tailcall.c	-> Using tail call chains
  - legacy/bpf_cyclic.c		-> Simple cycle as tail calls
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/bpf_graft.c
new file mode 100644
index 00000000..8066dcce
--- /dev/null
+++ b/examples/bpf/bpf_graft.c
@@ -0,0 +1,66 @@
+#include "../../include/bpf_api.h"
+
+/* This example demonstrates how classifier run-time behaviour
+ * can be altered with tail calls. We start out with an empty
+ * jmp_tc array, then add section aaa to the array slot 0, and
+ * later on atomically replace it with section bbb. Note that
+ * as shown in other examples, the tc loader can prepopulate
+ * tail called sections, here we start out with an empty one
+ * on purpose to show it can also be done this way.
+ *
+ * tc filter add dev foo parent ffff: bpf obj graft.o
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-20229 [001] ..s. 138993.003923: : fallthrough
+ *   <idle>-0            [001] ..s. 138993.202265: : fallthrough
+ *   Socket Thread-20229 [001] ..s. 138994.004149: : fallthrough
+ *   [...]
+ *
+ * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec aaa
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-19818 [002] ..s. 139012.053587: : aaa
+ *   <idle>-0            [002] ..s. 139012.172359: : aaa
+ *   Socket Thread-19818 [001] ..s. 139012.173556: : aaa
+ *   [...]
+ *
+ * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec bbb
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-19818 [002] ..s. 139022.102967: : bbb
+ *   <idle>-0            [002] ..s. 139022.155640: : bbb
+ *   Socket Thread-19818 [001] ..s. 139022.156730: : bbb
+ *   [...]
+ */
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);
+} jmp_tc __section(".maps");
+
+__section("aaa")
+int cls_aaa(struct __sk_buff *skb)
+{
+	printt("aaa\n");
+	return TC_H_MAKE(1, 42);
+}
+
+__section("bbb")
+int cls_bbb(struct __sk_buff *skb)
+{
+	printt("bbb\n");
+	return TC_H_MAKE(1, 43);
+}
+
+__section_cls_entry
+int cls_entry(struct __sk_buff *skb)
+{
+	tail_call(skb, &jmp_tc, 0);
+	printt("fallthrough\n");
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/bpf_map_in_map.c
new file mode 100644
index 00000000..39c86268
--- /dev/null
+++ b/examples/bpf/bpf_map_in_map.c
@@ -0,0 +1,55 @@
+#include "../../include/bpf_api.h"
+
+struct inner_map {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+} map_inner __section(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);
+	__array(values, struct inner_map);
+} map_outer __section(".maps") = {
+	.values = {
+		[0] = &map_inner,
+	},
+};
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			lock_xadd(val, 1);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			printt("map val: %d\n", *val);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/bpf_shared.c
new file mode 100644
index 00000000..99a332f4
--- /dev/null
+++ b/examples/bpf/bpf_shared.c
@@ -0,0 +1,53 @@
+#include "../../include/bpf_api.h"
+
+/* Minimal, stand-alone toy map pinning example:
+ *
+ * clang -target bpf -O2 [...] -o bpf_shared.o -c bpf_shared.c
+ * tc filter add dev foo parent 1: bpf obj bpf_shared.o sec egress
+ * tc filter add dev foo parent ffff: bpf obj bpf_shared.o sec ingress
+ *
+ * Both classifier will share the very same map instance in this example,
+ * so map content can be accessed from ingress *and* egress side!
+ *
+ * This example has a pinning of PIN_OBJECT_NS, so it's private and
+ * thus shared among various program sections within the object.
+ *
+ * A setting of PIN_GLOBAL_NS would place it into a global namespace,
+ * so that it can be shared among different object files. A setting
+ * of PIN_NONE (= 0) means no sharing, so each tc invocation a new map
+ * instance is being created.
+ */
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);	/* or LIBBPF_PIN_NONE */
+} map_sh __section(".maps");
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	int key = 0, *val;
+
+	val = map_lookup_elem(&map_sh, &key);
+	if (val)
+		lock_xadd(val, 1);
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	int key = 0, *val;
+
+	val = map_lookup_elem(&map_sh, &key);
+	if (val)
+		printt("map val: %d\n", *val);
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/include/bpf_api.h b/include/bpf_api.h
index 89d3488d..82c47089 100644
--- a/include/bpf_api.h
+++ b/include/bpf_api.h
@@ -19,6 +19,19 @@
 
 #include "bpf_elf.h"
 
+/** libbpf pin type. */
+enum libbpf_pin_type {
+	LIBBPF_PIN_NONE,
+	/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
+	LIBBPF_PIN_BY_NAME,
+};
+
+/** Type helper macros. */
+
+#define __uint(name, val) int (*name)[val]
+#define __type(name, val) typeof(val) *name
+#define __array(name, val) typeof(val) *name[]
+
 /** Misc macros. */
 
 #ifndef __stringify
-- 
2.25.4

[PATCHv2 iproute2-next 3/5] lib: add libbpf support

Message details and related content
This patch converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available, which is started by Toke's
implementation[1]. With libbpf iproute2 could correctly process BTF
information and support the new-style BTF-defined maps, while keeping
compatibility with the old internal map definition syntax.

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code in bpf_legacy.c ensures that
iproute2 will still understand the old map definition format, including
populating map-in-map and tail call maps before load.

In bpf_libbpf.c, we init iproute2 ctx and elf info first to check the
legacy bytes. When handling the legacy maps, for map-in-maps, we create
them manually and re-use the fd as they are associated with id/inner_id.
For pin maps, we only set the pin path and let libbp load to handle it.
For tail calls, we find it first and update the element after prog load.

Other maps/progs will be loaded by libbpf directly.

[1] https://lore.kernel.org/bpf/20190820114706.18546-1-toke@redhat.com/

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---

v2:
Remove self defined IS_ERR_OR_NULL and use libbpf_get_error() instead.
Add ipvrf with libbpf support.
---
 include/bpf_util.h |  11 ++
 ip/ipvrf.c         |  15 ++
 lib/Makefile       |   4 +
 lib/bpf_legacy.c   | 178 ++++++++++++++++++++++++
 lib/bpf_libbpf.c   | 332 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 540 insertions(+)
 create mode 100644 lib/bpf_libbpf.c
diff --git a/include/bpf_util.h b/include/bpf_util.h
index 72d3a32c..e200c107 100644
--- a/include/bpf_util.h
+++ b/include/bpf_util.h
@@ -300,4 +300,15 @@ static inline int bpf_recv_map_fds(const char *path, int *fds,
 	return -1;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg);
+int iproute2_bpf_fetch_ancillary(void);
+int iproute2_get_root_path(char *root_path, size_t len);
+bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname);
+bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
+			    struct bpf_elf_map *omap, char *omap_name);
+int iproute2_find_map_name_by_id(unsigned int map_id, char *name);
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg);
+#endif /* HAVE_LIBBPF */
 #endif /* __BPF_UTIL__ */
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 33150ac2..afaf1de7 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -28,8 +28,14 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+
 #include "bpf_util.h"
 
+#ifdef HAVE_LIBBPF
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#endif
+
 #define CGRP_PROC_FILE  "/cgroup.procs"
 
 static struct link_filter vrf_filter;
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;
 	}
 
+#ifdef HAVE_LIBBPF
+	if (bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0)) {
+#else
 	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
+#endif
 		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
 			strerror(errno));
 		goto out;
diff --git a/lib/Makefile b/lib/Makefile
index a326fb9f..82d6e465 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -7,6 +7,10 @@ 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_legacy.o exec.o fs.o cg_map.o
 
+ifeq ($(HAVE_LIBBPF),y)
+UTILOBJ += bpf_libbpf.o
+endif
+
 NLOBJ=libgenl.o libnetlink.o
 
 all: libnetlink.a libutil.a
diff --git a/lib/bpf_legacy.c b/lib/bpf_legacy.c
index 2e6e0602..c5ff3e32 100644
--- a/lib/bpf_legacy.c
+++ b/lib/bpf_legacy.c
@@ -940,6 +940,9 @@ static int bpf_do_parse(struct bpf_cfg_in *cfg, const bool *opt_tbl)
 static int bpf_do_load(struct bpf_cfg_in *cfg)
 {
 	if (cfg->mode == EBPF_OBJECT) {
+#ifdef HAVE_LIBBPF
+		return iproute2_load_libbpf(cfg);
+#endif
 		cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
 					    cfg->section, cfg->ifindex,
 					    cfg->verbose);
@@ -3165,3 +3168,178 @@ int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
 	return ret;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+/* The following functions are wrapper functions for libbpf code to be
+ * compatible with the legacy format. So all the functions have prefix
+ * with iproute2_
+ */
+int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+
+	return bpf_elf_ctx_init(ctx, cfg->object, cfg->type, cfg->ifindex, cfg->verbose);
+}
+
+int iproute2_bpf_fetch_ancillary(void)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	struct bpf_elf_sec_data data;
+	int i, ret = 0;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		ret = bpf_fill_section_data(ctx, i, &data);
+		if (ret < 0)
+			continue;
+
+		if (data.sec_hdr.sh_type == SHT_PROGBITS &&
+		    !strcmp(data.sec_name, ELF_SECTION_MAPS))
+			ret = bpf_fetch_maps_begin(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
+			 !strcmp(data.sec_name, ".symtab"))
+			ret = bpf_fetch_symtab(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_STRTAB &&
+			 !strcmp(data.sec_name, ".strtab"))
+			ret = bpf_fetch_strtab(ctx, i, &data);
+		if (ret < 0) {
+			fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
+				i);
+			return ret;
+		}
+	}
+
+	if (bpf_has_map_data(ctx)) {
+		ret = bpf_fetch_maps_end(ctx);
+		if (ret < 0) {
+			fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+int iproute2_get_root_path(char *root_path, size_t len)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	int ret = 0;
+
+	snprintf(root_path, len, "%s/%s",
+		 bpf_get_work_dir(ctx->type), BPF_DIR_GLOBALS);
+
+	ret = mkdir(root_path, S_IRWXU);
+	if (ret && errno != EEXIST) {
+		fprintf(stderr, "mkdir %s failed: %s\n", root_path, strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *map_name, *tmp;
+	unsigned int pinning;
+	int i, ret = 0;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		if (ctx->maps[i].pinning == PIN_OBJECT_NS &&
+		    ctx->noafalg) {
+			fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n");
+			return false;
+		}
+
+		map_name = bpf_map_fetch_name(ctx, i);
+		if (!map_name) {
+			return false;
+		}
+
+		if (strcmp(libbpf_map_name, map_name))
+			continue;
+
+		pinning = ctx->maps[i].pinning;
+
+		if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
+			return false;
+
+		if (pinning == PIN_OBJECT_NS)
+			ret = bpf_make_obj_path(ctx);
+		else if ((tmp = bpf_custom_pinning(ctx, pinning)))
+			ret = bpf_make_custom_path(ctx, tmp);
+		if (ret < 0)
+			return false;
+
+		bpf_make_pathname(pathname, PATH_MAX, map_name, ctx, pinning);
+
+		return true;
+	}
+
+	return false;
+}
+
+bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
+			    struct bpf_elf_map *omap, char *omap_name)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *inner_map_name, *outer_map_name;
+	int i, j;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		inner_map_name = bpf_map_fetch_name(ctx, i);
+		if (!inner_map_name) {
+			return false;
+		}
+
+		if (strcmp(libbpf_map_name, inner_map_name))
+			continue;
+
+		if (!ctx->maps[i].id ||
+		    ctx->maps[i].inner_id ||
+		    ctx->maps[i].inner_idx == -1)
+			continue;
+
+		*imap = ctx->maps[i];
+
+		for (j = 0; j < ctx->map_num; j++) {
+			if (!bpf_is_map_in_map_type(&ctx->maps[j]))
+				continue;
+			if (ctx->maps[j].inner_id != ctx->maps[i].id)
+				continue;
+
+			*omap = ctx->maps[j];
+			outer_map_name = bpf_map_fetch_name(ctx, j);
+			memcpy(omap_name, outer_map_name, strlen(outer_map_name) + 1);
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+int iproute2_find_map_name_by_id(unsigned int map_id, char *name)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *map_name;
+	int i, idx = -1;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		if (ctx->maps[i].id == map_id &&
+		    ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) {
+			idx = i;
+			break;
+		}
+	}
+
+	if (idx < 0)
+		return -1;
+
+	map_name = bpf_map_fetch_name(ctx, idx);
+	if (!map_name)
+		return -1;
+
+	memcpy(name, map_name, strlen(map_name) + 1);
+	return 0;
+}
+#endif /* HAVE_LIBBPF */
diff --git a/lib/bpf_libbpf.c b/lib/bpf_libbpf.c
new file mode 100644
index 00000000..9c29abc1
--- /dev/null
+++ b/lib/bpf_libbpf.c
@@ -0,0 +1,332 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+
+#include "bpf_util.h"
+
+static int verbose_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+static int silent_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+	if (level > LIBBPF_WARN)
+		return 0;
+
+	/* Skip warning from bpf_object__init_user_maps() for legacy maps */
+	if (strstr(format, "has unrecognized, non-zero options"))
+		return 0;
+
+	return vfprintf(stderr, format, args);
+}
+
+static int create_map(const char *name, struct bpf_elf_map *map,
+		      __u32 ifindex, int inner_fd)
+{
+	struct bpf_create_map_attr map_attr = {};
+
+	map_attr.name = name;
+	map_attr.map_type = map->type;
+	map_attr.map_flags = map->flags;
+	map_attr.key_size = map->size_key;
+	map_attr.value_size = map->size_value;
+	map_attr.max_entries = map->max_elem;
+	map_attr.map_ifindex = ifindex;
+	map_attr.inner_map_fd = inner_fd;
+
+	return bpf_create_map_xattr(&map_attr);
+}
+
+static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
+			     struct bpf_elf_map *elf_map, int inner_fd,
+			     bool *reuse_pin_map)
+{
+	char pathname[PATH_MAX];
+	const char *map_name;
+	bool pin_map = false;
+	int map_fd, ret = 0;
+
+	map_name = bpf_map__name(map);
+
+	if (iproute2_is_pin_map(map_name, pathname)) {
+		pin_map = true;
+
+		/* Check if there already has a pinned map */
+		map_fd = bpf_obj_get(pathname);
+		if (map_fd > 0) {
+			if (reuse_pin_map)
+				*reuse_pin_map = true;
+			close(map_fd);
+			return bpf_map__set_pin_path(map, pathname);
+		}
+	}
+
+	map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
+	if (map_fd < 0) {
+		fprintf(stderr, "create map %s failed\n", map_name);
+		return map_fd;
+	}
+
+	ret = bpf_map__reuse_fd(map, map_fd);
+	if (ret < 0) {
+		fprintf(stderr, "map %s reuse fd failed\n", map_name);
+		goto err_out;
+	}
+
+	if (pin_map) {
+		ret = bpf_map__set_pin_path(map, pathname);
+		if (ret < 0)
+			goto err_out;
+	}
+
+	return 0;
+err_out:
+	close(map_fd);
+	return ret;
+}
+
+static int
+handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
+			 const char *inner_map_name)
+{
+	int inner_fd, outer_fd, inner_idx, ret = 0;
+	struct bpf_elf_map imap, omap;
+	struct bpf_map *outer_map;
+	/* What's the size limit of map name? */
+	char outer_map_name[128];
+	bool reuse_pin_map = false;
+
+	/* Deal with map-in-map */
+	if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
+		ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
+		if (ret < 0)
+			return ret;
+
+		inner_fd = bpf_map__fd(inner_map);
+		outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
+		ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
+		if (ret < 0)
+			return ret;
+
+		if (!reuse_pin_map) {
+			inner_idx = imap.inner_idx;
+			outer_fd = bpf_map__fd(outer_map);
+			ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
+			if (ret < 0)
+				fprintf(stderr, "Cannot update inner_idx into outer_map\n");
+		}
+	}
+
+	return ret;
+}
+
+static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj)
+{
+	unsigned int map_id, key_id;
+	const char *sec_name;
+	struct bpf_map *map;
+	char map_name[128];
+	int ret;
+
+	/* Handle iproute2 tail call */
+	sec_name = bpf_program__section_name(prog);
+	ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+	if (ret != 2)
+		return -1;
+
+	ret = iproute2_find_map_name_by_id(map_id, map_name);
+	if (ret < 0) {
+		fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
+		return ret;
+	}
+
+	map = bpf_object__find_map_by_name(obj, map_name);
+	if (!map)
+		return -1;
+
+	/* Save the map here for later updating */
+	bpf_program__set_priv(prog, map, NULL);
+
+	return 0;
+}
+
+static int update_legacy_tail_call_maps(struct bpf_object *obj)
+{
+	int prog_fd, map_fd, ret = 0;
+	unsigned int map_id, key_id;
+	struct bpf_program *prog;
+	const char *sec_name;
+	struct bpf_map *map;
+
+	bpf_object__for_each_program(prog, obj) {
+		map = bpf_program__priv(prog);
+		if (!map)
+			continue;
+
+		prog_fd = bpf_program__fd(prog);
+		if (prog_fd < 0)
+			continue;
+
+		sec_name = bpf_program__section_name(prog);
+		ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+		if (ret != 2)
+			continue;
+
+		map_fd = bpf_map__fd(map);
+		ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
+		if (ret < 0) {
+			fprintf(stderr, "Cannot update map key for tail call!\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int handle_legacy_maps(struct bpf_object *obj)
+{
+	char pathname[PATH_MAX];
+	struct bpf_map *map;
+	const char *map_name;
+	int map_fd, ret = 0;
+
+	bpf_object__for_each_map(map, obj) {
+		map_name = bpf_map__name(map);
+
+		ret = handle_legacy_map_in_map(obj, map, map_name);
+		if (ret)
+			return ret;
+
+		/* If it is a iproute2 legacy pin maps, just set pin path
+		 * and let bpf_object__load() to deal with the map creation.
+		 * We need to ignore map-in-maps which have pinned maps manually
+		 */
+		map_fd = bpf_map__fd(map);
+		if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
+			ret = bpf_map__set_pin_path(map, pathname);
+			if (ret) {
+				fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
+				break;
+			}
+		}
+
+	}
+
+	return ret;
+}
+
+static int load_bpf_object(struct bpf_cfg_in *cfg)
+{
+	struct bpf_program *p, *prog = NULL;
+	struct bpf_object *obj;
+	char root_path[PATH_MAX];
+	struct bpf_map *map;
+	int prog_fd, ret = 0;
+
+	ret = iproute2_get_root_path(root_path, PATH_MAX);
+	if (ret)
+		return ret;
+
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
+			.relaxed_maps = true,
+			.pin_root_path = root_path,
+	);
+
+	obj = bpf_object__open_file(cfg->object, &open_opts);
+	if (libbpf_get_error(obj)) {
+		fprintf(stderr, "ERROR: opening BPF object file failed\n");
+		return -ENOENT;
+	}
+
+	bpf_object__for_each_program(p, obj) {
+		/* Only load the programs that will either be subsequently
+		 * attached or inserted into a tail call map */
+		if (find_legacy_tail_calls(p, obj) < 0 && cfg->section &&
+		    strcmp(bpf_program__section_name(p), cfg->section)) {
+			ret = bpf_program__set_autoload(p, false);
+			if (ret)
+				return -EINVAL;
+			continue;
+		}
+
+		bpf_program__set_type(p, cfg->type);
+		bpf_program__set_ifindex(p, cfg->ifindex);
+		if (!prog)
+			prog = p;
+	}
+
+	bpf_object__for_each_map(map, obj) {
+		if (!bpf_map__is_offload_neutral(map))
+			bpf_map__set_ifindex(map, cfg->ifindex);
+	}
+
+	if (!prog) {
+		fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
+		return -ENOENT;
+	}
+
+	/* Handle iproute2 legacy pin maps and map-in-maps */
+	ret = handle_legacy_maps(obj);
+	if (ret)
+		goto unload_obj;
+
+	ret = bpf_object__load(obj);
+	if (ret)
+		goto unload_obj;
+
+	ret = update_legacy_tail_call_maps(obj);
+	if (ret)
+		goto unload_obj;
+
+	prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
+	if (prog_fd < 0)
+		ret = -errno;
+	else
+		cfg->prog_fd = prog_fd;
+
+unload_obj:
+	/* Close obj as we don't need it */
+	bpf_object__close(obj);
+	return ret;
+}
+
+/* Load ebpf and return prog fd */
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
+{
+	int ret = 0;
+
+	if (cfg->verbose)
+		libbpf_set_print(verbose_print);
+	else
+		libbpf_set_print(silent_print);
+
+	ret = iproute2_bpf_elf_ctx_init(cfg);
+	if (ret < 0) {
+		fprintf(stderr, "Cannot initialize ELF context!\n");
+		return ret;
+	}
+
+	ret = iproute2_bpf_fetch_ancillary();
+	if (ret < 0) {
+		fprintf(stderr, "Error fetching ELF ancillary data!\n");
+		return ret;
+	}
+
+	ret = load_bpf_object(cfg);
+	if (ret)
+		return ret;
+
+	return cfg->prog_fd;
+}
-- 
2.25.4

[PATCHv2 iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c

Message details and related content
This is a preparation for later libbpf support in iproute2. Function
bpf_prog_load() is also renamed to bpf_prog_load_buf() as there is a
conflict with libbpf.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 include/bpf_util.h          | 6 +++---
 ip/ipvrf.c                  | 4 ++--
 lib/Makefile                | 2 +-
 lib/{bpf.c => bpf_legacy.c} | 6 +++---
 4 files changed, 9 insertions(+), 9 deletions(-)
 rename lib/{bpf.c => bpf_legacy.c} (99%)
diff --git a/include/bpf_util.h b/include/bpf_util.h
index 63db07ca..72d3a32c 100644
--- a/include/bpf_util.h
+++ b/include/bpf_util.h
@@ -274,9 +274,9 @@ int bpf_trace_pipe(void);
 
 void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
 
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log);
+int bpf_prog_load_buf(enum bpf_prog_type type, const struct bpf_insn *insns,
+		      size_t size_insns, const char *license, char *log,
+		      size_t size_log);
 
 int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
 int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 28dd8e25..33150ac2 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -256,8 +256,8 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
-	return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
-			     "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
diff --git a/lib/Makefile b/lib/Makefile
index 7cba1857..a326fb9f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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_legacy.o exec.o fs.o cg_map.o
 
 NLOBJ=libgenl.o libnetlink.o
 
diff --git a/lib/bpf.c b/lib/bpf_legacy.c
similarity index 99%
rename from lib/bpf.c
rename to lib/bpf_legacy.c
index c7d45077..2e6e0602 100644
--- a/lib/bpf.c
+++ b/lib/bpf_legacy.c
@@ -1109,9 +1109,9 @@ static int bpf_prog_load_dev(enum bpf_prog_type type,
 	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
 }
 
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log)
+int bpf_prog_load_buf(enum bpf_prog_type type, const struct bpf_insn *insns,
+		      size_t size_insns, const char *license, char *log,
+		      size_t size_log)
 {
 	return bpf_prog_load_dev(type, insns, size_insns, license, 0,
 				 log, size_log);
-- 
2.25.4

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 10/28/20 7:25 AM, Hangbin Liu wrote:
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.


v2:
a) Remove self defined IS_ERR_OR_NULL and use libbpf_get_error() instead.
b) Add ipvrf with libbpf support.
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM	yes
 IPT	using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM	yes
 IPT	using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.

But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.

Thanks
Hangbin

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM  yes
 IPT  using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.
Why not using libbpf from submodule?
But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.

Thanks
Hangbin

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 10/28/20 8:06 PM, Hangbin Liu wrote:
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM	yes
 IPT	using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.
nope. you need to be able to handle this. Ubuntu 20.10 was just
released, and it has a version of libbpf. If you are going to integrate
libbpf into other packages like iproute2, it needs to just work with
that version.
But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.
This is going to be an on-going problem. iproute2 should work with
whatever version of libbpf is installed on that system.

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Thu, 29 Oct 2020 10:06:37 +0800
Hangbin Liu [off-list ref] wrote:
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM	yes
 IPT	using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2  
You need to update libbpf to latest version.

But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.

3 more quoted lines

Then configure needs to check for this or every distro is going to get real mad...

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 10/28/20 8:27 PM, Andrii Nakryiko wrote:
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM  yes
 IPT  using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.
Why not using libbpf from submodule?

1 more quoted lines

no. iproute2 does not bring in libmnl, libc, ... a submodules. libbpf is
not special. OS versions provide it and it needs to co-exist with packages.

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, 28 Oct 2020 19:27:20 -0700
Andrii Nakryiko [off-list ref] wrote:
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:  
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM  yes
 IPT  using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2  
You need to update libbpf to latest version.  
Why not using libbpf from submodule?
Because it makes it harder for people downloading tarballs and distributions.
Iproute2 has worked well by being standalone.

Want to merge libbpf into iproute2?? 
 

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 7:33 PM David Ahern [off-list ref] wrote:
On 10/28/20 8:27 PM, Andrii Nakryiko wrote:
quoted
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM  yes
 IPT  using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.

6 more quoted lines

Not saying libbpf is special, but libbpf is a fast moving target right
now, so it's pragmatic to have it as submodule, because if you'd like
to use some latest functionality, you won't have to add all the
conditional compilation shenanigans to detect every single new API
you'd like to use from libbpf. And libbpf is pretty small to not worry
about saving memory through a shared library.

But it's up to you guys, of course.

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 10/28/20 8:45 PM, Hangbin Liu wrote:
On Wed, Oct 28, 2020 at 08:20:55PM -0600, David Ahern wrote:
quoted
quoted
quoted
root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.
nope. you need to be able to handle this. Ubuntu 20.10 was just
released, and it has a version of libbpf. If you are going to integrate
libbpf into other packages like iproute2, it needs to just work with
that version.
OK, I can replace bpf_program__section_name by bpf_program__title().
I believe this one can be handled through a compatability check. Looks
the rename / deprecation is fairly recent (78cdb58bdf15f from Sept 2020).

quoted
quoted
But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.
This is going to be an on-going problem. iproute2 should work with
whatever version of libbpf is installed on that system.
I will make it works on Ubuntu 20.10, but with whatever version of libbpf?
That looks hard, especially with old libbpf.
I meant what comes with the OS. I believe I read that Fedora 33 was just
released as well. Does it have a version of libbpf? If so, please verify
it compiles and works with that version too. Before committing I will
also verify it compiles and links against a local version of libbpf (top
of tree) just to get a range of versions.

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 7:34 PM Stephen Hemminger
[off-list ref] wrote:
On Wed, 28 Oct 2020 19:27:20 -0700
Andrii Nakryiko [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:

root@u2010-sfo3:~/iproute2.git# ./configure
TC schedulers
 ATM  yes
 IPT  using xtables
 IPSET  yes

iptables modules directory: /usr/lib/x86_64-linux-gnu/xtables
libc has setns: yes
SELinux support: yes
libbpf support: yes
ELF support: yes
libmnl support: yes
Berkeley DB: no
need for strlcpy: yes
libcap support: yes

root@u2010-sfo3:~/iproute2.git# make clean

root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2

6 more quoted lines

Genuinely curious, making harder how exactly? When packaging sources
as a tarball you'd check out submodules before packaging, right?
Iproute2 has worked well by being standalone.
Again, maybe I'm missing something, but what makes it not a
standalone, if it is using a submodule? Pahole, for instance, is using
libbpf through submodule and just bypasses all the problems with
detection of features and library availability. I haven't heard anyone
complaining about it made working with pahole harder in any way.
Want to merge libbpf into iproute2??
No... How did you come to this conclusion?..

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 08:20:55PM -0600, David Ahern wrote:
quoted
quoted
root@u2010-sfo3:~/iproute2.git# make -j 4
...
/usr/bin/ld: ../lib/libutil.a(bpf_libbpf.o): in function `load_bpf_object':
bpf_libbpf.c:(.text+0x3cb): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x438): undefined reference to
`bpf_program__section_name'
/usr/bin/ld: bpf_libbpf.c:(.text+0x716): undefined reference to
`bpf_program__section_name'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:27: ip] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:64: all] Error 2
You need to update libbpf to latest version.
nope. you need to be able to handle this. Ubuntu 20.10 was just
released, and it has a version of libbpf. If you are going to integrate
libbpf into other packages like iproute2, it needs to just work with
that version.
OK, I can replace bpf_program__section_name by bpf_program__title().
quoted
But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.
This is going to be an on-going problem. iproute2 should work with
whatever version of libbpf is installed on that system.
I will make it works on Ubuntu 20.10, but with whatever version of libbpf?
That looks hard, especially with old libbpf.

Thanks
Hangbin

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 09:00:41PM -0600, David Ahern wrote:
quoted
quoted
quoted
You need to update libbpf to latest version.
nope. you need to be able to handle this. Ubuntu 20.10 was just
released, and it has a version of libbpf. If you are going to integrate
libbpf into other packages like iproute2, it needs to just work with
that version.
OK, I can replace bpf_program__section_name by bpf_program__title().
I believe this one can be handled through a compatability check. Looks
Do you mean add a check like

#ifdef has_section_name_support
	use bpf_program__section_name;
#else
	use bpf_program__title;
#endif
the rename / deprecation is fairly recent (78cdb58bdf15f from Sept 2020).
Yeah... As Andrii said, libbpf is in fast moving..
quoted
quoted
quoted
But this also remind me that I need to add bpf_program__section_name() to
configure checking. I will see if I missed other functions' checking.
This is going to be an on-going problem. iproute2 should work with
whatever version of libbpf is installed on that system.
I will make it works on Ubuntu 20.10, but with whatever version of libbpf?
That looks hard, especially with old libbpf.
I meant what comes with the OS. I believe I read that Fedora 33 was just
released as well. Does it have a version of libbpf? If so, please verify
it compiles and works with that version too. Before committing I will
also verify it compiles and links against a local version of libbpf (top
of tree) just to get a range of versions.
Yes, it makes sense. I will also check the libbpf on Fedora 33.

Thanks
Hangbin

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Oct 28, 2020 at 09:00:41PM -0600, David Ahern wrote:
quoted
quoted
nope. you need to be able to handle this. Ubuntu 20.10 was just
released, and it has a version of libbpf. If you are going to integrate
libbpf into other packages like iproute2, it needs to just work with
that version.
OK, I can replace bpf_program__section_name by bpf_program__title().
I believe this one can be handled through a compatability check. Looks
the rename / deprecation is fairly recent (78cdb58bdf15f from Sept 2020).
Hi David,

I just come up with another way. In configure, build a temp program and update
the function checking every time is not graceful. How about just check the
libbpf version, since libbpf has exported all functions in src/libbpf.map.

Currently, only bpf_program__section_name() is added in 0.2.0, all other
needed functions are supported in 0.1.0.

So in configure, the new check would like:

check_force_libbpf()
{
    # if set FORCE_LIBBPF but no libbpf support, just exist the config
    # process to make sure we don't build without libbpf.
    if [ -n "$FORCE_LIBBPF" ]; then
        echo "FORCE_LIBBPF set, but couldn't find a usable libbpf"
        exit 1
    fi
}

check_libbpf()
{
    if ! ${PKG_CONFIG} libbpf --exists && [ -z "$LIBBPF_DIR" ] ; then
        echo "no"
        check_force_libbpf
        return
    fi

    if [ $(uname -m) == x86_64 ]; then
        local LIBSUBDIR=lib64
    else
        local LIBSUBDIR=lib
    fi

    if [ -n "$LIBBPF_DIR" ]; then
        LIBBPF_CFLAGS="-I${LIBBPF_DIR}/include -L${LIBBPF_DIR}/${LIBSUBDIR}"
        LIBBPF_LDLIBS="${LIBBPF_DIR}/${LIBSUBDIR}/libbpf.a -lz -lelf"
    else

40 more lines

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
Hangbin Liu [off-list ref] writes:
On Wed, Oct 28, 2020 at 09:00:41PM -0600, David Ahern wrote:
quoted
quoted
quoted
nope. you need to be able to handle this. Ubuntu 20.10 was just
released, and it has a version of libbpf. If you are going to integrate
libbpf into other packages like iproute2, it needs to just work with
that version.
OK, I can replace bpf_program__section_name by bpf_program__title().
I believe this one can be handled through a compatability check. Looks
the rename / deprecation is fairly recent (78cdb58bdf15f from Sept 2020).
Hi David,

I just come up with another way. In configure, build a temp program and update
the function checking every time is not graceful. How about just check the
libbpf version, since libbpf has exported all functions in src/libbpf.map.

Currently, only bpf_program__section_name() is added in 0.2.0, all other
needed functions are supported in 0.1.0.

So in configure, the new check would like:
Why is this easier than just checking for the function you need? In
xdp-tools configure we have a test like this:

check_perf_consume()
{
    cat >$TMPDIR/libbpftest.c <<EOF
#include <bpf/libbpf.h>
int main(int argc, char **argv) {
    perf_buffer__consume(NULL);
    return 0;
}
EOF
    libbpf_err=$($CC -o $TMPDIR/libbpftest $TMPDIR/libbpftest.c $LIBBPF_CFLAGS $LIBBPF_LDLIBS 2>&1)
    if [ "$?" -eq "0" ]; then
        echo "HAVE_LIBBPF_PERF_BUFFER__CONSUME:=y" >>"$CONFIG"
        echo "yes"
    else
        echo "HAVE_LIBBPF_PERF_BUFFER__CONSUME:=n" >>"$CONFIG"
        echo "no"
    fi
}

Just do that for __section_name(), and you'll also be able to work with
custom libbpf versions using LIBBPF_DIR.
static const char *get_bpf_program__section_name(const struct bpf_program *prog)
{
#ifdef HAVE_LIBBPF_SECTION_NAME
	return bpf_program__section_name(prog);
#else
	return bpf_program__title(prog, false);
#endif
}
This bit is fine :)

-Toke

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, 28 Oct 2020 19:50:51 -0700
Andrii Nakryiko [off-list ref] wrote:
On Wed, Oct 28, 2020 at 7:34 PM Stephen Hemminger
[off-list ref] wrote:
quoted
On Wed, 28 Oct 2020 19:27:20 -0700
Andrii Nakryiko [off-list ref] wrote:
 
quoted
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:  
quoted
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:  
quoted
fails to compile on Ubuntu 20.10:
[...]
quoted
quoted
quoted
You need to update libbpf to latest version.  
Why not using libbpf from submodule?  
Because it makes it harder for people downloading tarballs and distributions.  
Genuinely curious, making harder how exactly? When packaging sources
as a tarball you'd check out submodules before packaging, right?
quoted
Iproute2 has worked well by being standalone.  
Again, maybe I'm missing something, but what makes it not a
standalone, if it is using a submodule? Pahole, for instance, is using
libbpf through submodule and just bypasses all the problems with
detection of features and library availability. I haven't heard anyone
complaining about it made working with pahole harder in any way.
I do believe you are missing something.  I guess I can be the relay for
complains, so you will officially hear about this.  Red Hat and Fedora
security is complaining that we are packaging a library (libbpf)
directly into the individual packages.  They complain because in case
of a security issue, they have to figure out to rebuild all the software
packages that are statically compiled with this library.

Maybe you say I don't care that Distro security teams have to do more
work and update more packages.  Then security team says, we expect
customers will use this library right, and if we ship it as a dynamic
loadable (.so) file, then we can update and fix security issues in
library without asking customers to recompile. (Notice the same story
goes if we can update the base-image used by a container).


-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer

[PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.

Signed-off-by: Hangbin Liu <redacted>
---
v3:
Check function bpf_program__section_name() separately and only use it
on higher libbpf version.

v2:
No update
---
 configure | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)
diff --git a/configure b/configure
index 307912aa..58a7176e 100755
--- a/configure
+++ b/configure
@@ -240,6 +240,97 @@ 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()
+{
+    # if set FORCE_LIBBPF but no libbpf support, just exist the config
+    # process to make sure we don't build without libbpf.
+    if [ -n "$FORCE_LIBBPF" ]; then
+        echo "FORCE_LIBBPF set, but couldn't find a usable libbpf"
+        exit 1
+    fi
+}
+
+check_libbpf()
+{
+    if ! ${PKG_CONFIG} libbpf --exists && [ -z "$LIBBPF_DIR" ] ; then
+        echo "no"
+        check_force_libbpf
+        return
+    fi
+
+    if [ $(uname -m) == x86_64 ]; then
+        local LIBSUBDIR=lib64
+    else
+        local LIBSUBDIR=lib
+    fi
+
+    if [ -n "$LIBBPF_DIR" ]; then
+        LIBBPF_CFLAGS="-I${LIBBPF_DIR}/include -L${LIBBPF_DIR}/${LIBSUBDIR}"
+        LIBBPF_LDLIBS="${LIBBPF_DIR}/${LIBSUBDIR}/libbpf.a -lz -lelf"
+    else
+        LIBBPF_CFLAGS=$(${PKG_CONFIG} libbpf --cflags)
+        LIBBPF_LDLIBS=$(${PKG_CONFIG} libbpf --libs)
+    fi
+
+    if ! have_libbpf_basic; then
+        echo "no"
+        echo "	libbpf version is too low, please update it to at least 0.1.0"
+        check_force_libbpf
+        return
+    else
+        echo "HAVE_LIBBPF:=y" >>$CONFIG
+        echo 'CFLAGS += -DHAVE_LIBBPF ' $LIBBPF_CFLAGS >> $CONFIG
+        echo 'LDLIBS += ' $LIBBPF_LDLIBS >>$CONFIG
+    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 ' $LIBBPF_CFLAGS >> $CONFIG
+    fi
+
+    echo "yes"
+}
+
 check_selinux()
 # SELinux is a compile time option in the ss utility
 {
@@ -385,6 +476,9 @@ check_setns
 echo -n "SELinux support: "
 check_selinux
 
+echo -n "libbpf support: "
+check_libbpf
+
 echo -n "ELF support: "
 check_elf
 
-- 
2.25.4

[PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Revisions (6)
  1. v1
  2. v2 [diff against previous]
  3. v3 current [diff against previous]
  4. v4 [diff against previous]
  5. v5 [diff against previous]
  6. v6 [diff against previous]
Message details and related content
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.

v3:
a) Update configure to Check function bpf_program__section_name() separately
b) Add a new function get_bpf_program__section_name() to choose whether to
use bpf_program__title() or not.
c) Test build the patch on Fedora 33 with libbpf-0.1.0-1.fc33 and
   libbpf-devel-0.1.0-1.fc33

v2:
a) Remove self defined IS_ERR_OR_NULL and use libbpf_get_error() instead.
b) Add ipvrf with libbpf support.


Here are the test results with patched iproute2:

== setup env

427 more lines

[PATCHv3 iproute2-next 2/5] lib: rename bpf.c to bpf_legacy.c

Message details and related content
This is a preparation for later libbpf support in iproute2. Function
bpf_prog_load() is also renamed to bpf_prog_load_buf() as there is a
conflict with libbpf.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 include/bpf_util.h          | 6 +++---
 ip/ipvrf.c                  | 4 ++--
 lib/Makefile                | 2 +-
 lib/{bpf.c => bpf_legacy.c} | 6 +++---
 4 files changed, 9 insertions(+), 9 deletions(-)
 rename lib/{bpf.c => bpf_legacy.c} (99%)
diff --git a/include/bpf_util.h b/include/bpf_util.h
index 63db07ca..72d3a32c 100644
--- a/include/bpf_util.h
+++ b/include/bpf_util.h
@@ -274,9 +274,9 @@ int bpf_trace_pipe(void);
 
 void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
 
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log);
+int bpf_prog_load_buf(enum bpf_prog_type type, const struct bpf_insn *insns,
+		      size_t size_insns, const char *license, char *log,
+		      size_t size_log);
 
 int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
 int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 28dd8e25..33150ac2 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -256,8 +256,8 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
-	return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
-			     "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
diff --git a/lib/Makefile b/lib/Makefile
index 7cba1857..a326fb9f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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_legacy.o exec.o fs.o cg_map.o
 
 NLOBJ=libgenl.o libnetlink.o
 
diff --git a/lib/bpf.c b/lib/bpf_legacy.c
similarity index 99%
rename from lib/bpf.c
rename to lib/bpf_legacy.c
index c7d45077..2e6e0602 100644
--- a/lib/bpf.c
+++ b/lib/bpf_legacy.c
@@ -1109,9 +1109,9 @@ static int bpf_prog_load_dev(enum bpf_prog_type type,
 	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
 }
 
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log)
+int bpf_prog_load_buf(enum bpf_prog_type type, const struct bpf_insn *insns,
+		      size_t size_insns, const char *license, char *log,
+		      size_t size_log)
 {
 	return bpf_prog_load_dev(type, insns, size_insns, license, 0,
 				 log, size_log);
-- 
2.25.4

[PATCHv3 iproute2-next 3/5] lib: add libbpf support

Message details and related content
This patch converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available, which is started by Toke's
implementation[1]. With libbpf iproute2 could correctly process BTF
information and support the new-style BTF-defined maps, while keeping
compatibility with the old internal map definition syntax.

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code in bpf_legacy.c ensures that
iproute2 will still understand the old map definition format, including
populating map-in-map and tail call maps before load.

In bpf_libbpf.c, we init iproute2 ctx and elf info first to check the
legacy bytes. When handling the legacy maps, for map-in-maps, we create
them manually and re-use the fd as they are associated with id/inner_id.
For pin maps, we only set the pin path and let libbp load to handle it.
For tail calls, we find it first and update the element after prog load.

Other maps/progs will be loaded by libbpf directly.

[1] https://lore.kernel.org/bpf/20190820114706.18546-1-toke@redhat.com/

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>

---
v3:
Add a new function get_bpf_program__section_name() to choose whether
use bpf_program__title() or not.

v2:
Remove self defined IS_ERR_OR_NULL and use libbpf_get_error() instead.
Add ipvrf with libbpf support.
---
 include/bpf_util.h |  11 ++
 ip/ipvrf.c         |  15 ++
 lib/Makefile       |   4 +
 lib/bpf_legacy.c   | 178 +++++++++++++++++++++++
 lib/bpf_libbpf.c   | 341 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 549 insertions(+)
 create mode 100644 lib/bpf_libbpf.c

1 more lines

diff --git a/include/bpf_util.h b/include/bpf_util.h
index 72d3a32c..e200c107 100644
--- a/include/bpf_util.h
+++ b/include/bpf_util.h
@@ -300,4 +300,15 @@ static inline int bpf_recv_map_fds(const char *path, int *fds,
 	return -1;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg);
+int iproute2_bpf_fetch_ancillary(void);
+int iproute2_get_root_path(char *root_path, size_t len);
+bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname);
+bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
+			    struct bpf_elf_map *omap, char *omap_name);
+int iproute2_find_map_name_by_id(unsigned int map_id, char *name);
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg);
+#endif /* HAVE_LIBBPF */
 #endif /* __BPF_UTIL__ */
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 33150ac2..afaf1de7 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -28,8 +28,14 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+
 #include "bpf_util.h"
 
+#ifdef HAVE_LIBBPF
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#endif
+
 #define CGRP_PROC_FILE  "/cgroup.procs"
 
 static struct link_filter vrf_filter;
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;
 	}
 
+#ifdef HAVE_LIBBPF
+	if (bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0)) {
+#else
 	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
+#endif
 		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
 			strerror(errno));
 		goto out;
diff --git a/lib/Makefile b/lib/Makefile
index a326fb9f..82d6e465 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -7,6 +7,10 @@ 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_legacy.o exec.o fs.o cg_map.o
 
+ifeq ($(HAVE_LIBBPF),y)
+UTILOBJ += bpf_libbpf.o
+endif
+
 NLOBJ=libgenl.o libnetlink.o
 
 all: libnetlink.a libutil.a
diff --git a/lib/bpf_legacy.c b/lib/bpf_legacy.c
index 2e6e0602..c5ff3e32 100644
--- a/lib/bpf_legacy.c
+++ b/lib/bpf_legacy.c
@@ -940,6 +940,9 @@ static int bpf_do_parse(struct bpf_cfg_in *cfg, const bool *opt_tbl)
 static int bpf_do_load(struct bpf_cfg_in *cfg)
 {
 	if (cfg->mode == EBPF_OBJECT) {
+#ifdef HAVE_LIBBPF
+		return iproute2_load_libbpf(cfg);
+#endif
 		cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
 					    cfg->section, cfg->ifindex,
 					    cfg->verbose);
@@ -3165,3 +3168,178 @@ int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
 	return ret;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+/* The following functions are wrapper functions for libbpf code to be
+ * compatible with the legacy format. So all the functions have prefix
+ * with iproute2_
+ */
+int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+
+	return bpf_elf_ctx_init(ctx, cfg->object, cfg->type, cfg->ifindex, cfg->verbose);
+}
+
+int iproute2_bpf_fetch_ancillary(void)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	struct bpf_elf_sec_data data;
+	int i, ret = 0;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		ret = bpf_fill_section_data(ctx, i, &data);
+		if (ret < 0)
+			continue;
+
+		if (data.sec_hdr.sh_type == SHT_PROGBITS &&
+		    !strcmp(data.sec_name, ELF_SECTION_MAPS))
+			ret = bpf_fetch_maps_begin(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
+			 !strcmp(data.sec_name, ".symtab"))
+			ret = bpf_fetch_symtab(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_STRTAB &&
+			 !strcmp(data.sec_name, ".strtab"))
+			ret = bpf_fetch_strtab(ctx, i, &data);
+		if (ret < 0) {
+			fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
+				i);
+			return ret;
+		}
+	}
+
+	if (bpf_has_map_data(ctx)) {
+		ret = bpf_fetch_maps_end(ctx);
+		if (ret < 0) {
+			fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+int iproute2_get_root_path(char *root_path, size_t len)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	int ret = 0;
+
+	snprintf(root_path, len, "%s/%s",
+		 bpf_get_work_dir(ctx->type), BPF_DIR_GLOBALS);
+
+	ret = mkdir(root_path, S_IRWXU);
+	if (ret && errno != EEXIST) {
+		fprintf(stderr, "mkdir %s failed: %s\n", root_path, strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *map_name, *tmp;
+	unsigned int pinning;
+	int i, ret = 0;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		if (ctx->maps[i].pinning == PIN_OBJECT_NS &&
+		    ctx->noafalg) {
+			fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n");
+			return false;
+		}
+
+		map_name = bpf_map_fetch_name(ctx, i);
+		if (!map_name) {
+			return false;
+		}
+
+		if (strcmp(libbpf_map_name, map_name))
+			continue;
+
+		pinning = ctx->maps[i].pinning;
+
+		if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
+			return false;
+
+		if (pinning == PIN_OBJECT_NS)
+			ret = bpf_make_obj_path(ctx);
+		else if ((tmp = bpf_custom_pinning(ctx, pinning)))
+			ret = bpf_make_custom_path(ctx, tmp);
+		if (ret < 0)
+			return false;
+
+		bpf_make_pathname(pathname, PATH_MAX, map_name, ctx, pinning);
+
+		return true;
+	}
+
+	return false;
+}
+
+bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
+			    struct bpf_elf_map *omap, char *omap_name)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *inner_map_name, *outer_map_name;
+	int i, j;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		inner_map_name = bpf_map_fetch_name(ctx, i);
+		if (!inner_map_name) {
+			return false;
+		}
+
+		if (strcmp(libbpf_map_name, inner_map_name))
+			continue;
+
+		if (!ctx->maps[i].id ||
+		    ctx->maps[i].inner_id ||
+		    ctx->maps[i].inner_idx == -1)
+			continue;
+
+		*imap = ctx->maps[i];
+
+		for (j = 0; j < ctx->map_num; j++) {
+			if (!bpf_is_map_in_map_type(&ctx->maps[j]))
+				continue;
+			if (ctx->maps[j].inner_id != ctx->maps[i].id)
+				continue;
+
+			*omap = ctx->maps[j];
+			outer_map_name = bpf_map_fetch_name(ctx, j);
+			memcpy(omap_name, outer_map_name, strlen(outer_map_name) + 1);
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+int iproute2_find_map_name_by_id(unsigned int map_id, char *name)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	const char *map_name;
+	int i, idx = -1;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		if (ctx->maps[i].id == map_id &&
+		    ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) {
+			idx = i;
+			break;
+		}
+	}
+
+	if (idx < 0)
+		return -1;
+
+	map_name = bpf_map_fetch_name(ctx, idx);
+	if (!map_name)
+		return -1;
+
+	memcpy(name, map_name, strlen(map_name) + 1);
+	return 0;
+}
+#endif /* HAVE_LIBBPF */
diff --git a/lib/bpf_libbpf.c b/lib/bpf_libbpf.c
new file mode 100644
index 00000000..4fe0bc4b
--- /dev/null
+++ b/lib/bpf_libbpf.c
@@ -0,0 +1,341 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+
+#include "bpf_util.h"
+
+static int verbose_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+static int silent_print(enum libbpf_print_level level, const char *format, va_list args)
+{
+	if (level > LIBBPF_WARN)
+		return 0;
+
+	/* Skip warning from bpf_object__init_user_maps() for legacy maps */
+	if (strstr(format, "has unrecognized, non-zero options"))
+		return 0;
+
+	return vfprintf(stderr, format, args);
+}
+
+static const char *get_bpf_program__section_name(const struct bpf_program *prog)
+{
+#ifdef HAVE_LIBBPF_SECTION_NAME
+	return bpf_program__section_name(prog);
+#else
+	return bpf_program__title(prog, false);
+#endif
+}
+
+static int create_map(const char *name, struct bpf_elf_map *map,
+		      __u32 ifindex, int inner_fd)
+{
+	struct bpf_create_map_attr map_attr = {};
+
+	map_attr.name = name;
+	map_attr.map_type = map->type;
+	map_attr.map_flags = map->flags;
+	map_attr.key_size = map->size_key;
+	map_attr.value_size = map->size_value;
+	map_attr.max_entries = map->max_elem;
+	map_attr.map_ifindex = ifindex;
+	map_attr.inner_map_fd = inner_fd;
+
+	return bpf_create_map_xattr(&map_attr);
+}
+
+static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
+			     struct bpf_elf_map *elf_map, int inner_fd,
+			     bool *reuse_pin_map)
+{
+	char pathname[PATH_MAX];
+	const char *map_name;
+	bool pin_map = false;
+	int map_fd, ret = 0;
+
+	map_name = bpf_map__name(map);
+
+	if (iproute2_is_pin_map(map_name, pathname)) {
+		pin_map = true;
+
+		/* Check if there already has a pinned map */
+		map_fd = bpf_obj_get(pathname);
+		if (map_fd > 0) {
+			if (reuse_pin_map)
+				*reuse_pin_map = true;
+			close(map_fd);
+			return bpf_map__set_pin_path(map, pathname);
+		}
+	}
+
+	map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
+	if (map_fd < 0) {
+		fprintf(stderr, "create map %s failed\n", map_name);
+		return map_fd;
+	}
+
+	ret = bpf_map__reuse_fd(map, map_fd);
+	if (ret < 0) {
+		fprintf(stderr, "map %s reuse fd failed\n", map_name);
+		goto err_out;
+	}
+
+	if (pin_map) {
+		ret = bpf_map__set_pin_path(map, pathname);
+		if (ret < 0)
+			goto err_out;
+	}
+
+	return 0;
+err_out:
+	close(map_fd);
+	return ret;
+}
+
+static int
+handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
+			 const char *inner_map_name)
+{
+	int inner_fd, outer_fd, inner_idx, ret = 0;
+	struct bpf_elf_map imap, omap;
+	struct bpf_map *outer_map;
+	/* What's the size limit of map name? */
+	char outer_map_name[128];
+	bool reuse_pin_map = false;
+
+	/* Deal with map-in-map */
+	if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
+		ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
+		if (ret < 0)
+			return ret;
+
+		inner_fd = bpf_map__fd(inner_map);
+		outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
+		ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
+		if (ret < 0)
+			return ret;
+
+		if (!reuse_pin_map) {
+			inner_idx = imap.inner_idx;
+			outer_fd = bpf_map__fd(outer_map);
+			ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
+			if (ret < 0)
+				fprintf(stderr, "Cannot update inner_idx into outer_map\n");
+		}
+	}
+
+	return ret;
+}
+
+static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj)
+{
+	unsigned int map_id, key_id;
+	const char *sec_name;
+	struct bpf_map *map;
+	char map_name[128];
+	int ret;
+
+	/* Handle iproute2 tail call */
+	sec_name = get_bpf_program__section_name(prog);
+	ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+	if (ret != 2)
+		return -1;
+
+	ret = iproute2_find_map_name_by_id(map_id, map_name);
+	if (ret < 0) {
+		fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
+		return ret;
+	}
+
+	map = bpf_object__find_map_by_name(obj, map_name);
+	if (!map)
+		return -1;
+
+	/* Save the map here for later updating */
+	bpf_program__set_priv(prog, map, NULL);
+
+	return 0;
+}
+
+static int update_legacy_tail_call_maps(struct bpf_object *obj)
+{
+	int prog_fd, map_fd, ret = 0;
+	unsigned int map_id, key_id;
+	struct bpf_program *prog;
+	const char *sec_name;
+	struct bpf_map *map;
+
+	bpf_object__for_each_program(prog, obj) {
+		map = bpf_program__priv(prog);
+		if (!map)
+			continue;
+
+		prog_fd = bpf_program__fd(prog);
+		if (prog_fd < 0)
+			continue;
+
+		sec_name = get_bpf_program__section_name(prog);
+		ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
+		if (ret != 2)
+			continue;
+
+		map_fd = bpf_map__fd(map);
+		ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
+		if (ret < 0) {
+			fprintf(stderr, "Cannot update map key for tail call!\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int handle_legacy_maps(struct bpf_object *obj)
+{
+	char pathname[PATH_MAX];
+	struct bpf_map *map;
+	const char *map_name;
+	int map_fd, ret = 0;
+
+	bpf_object__for_each_map(map, obj) {
+		map_name = bpf_map__name(map);
+
+		ret = handle_legacy_map_in_map(obj, map, map_name);
+		if (ret)
+			return ret;
+
+		/* If it is a iproute2 legacy pin maps, just set pin path
+		 * and let bpf_object__load() to deal with the map creation.
+		 * We need to ignore map-in-maps which have pinned maps manually
+		 */
+		map_fd = bpf_map__fd(map);
+		if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
+			ret = bpf_map__set_pin_path(map, pathname);
+			if (ret) {
+				fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
+				break;
+			}
+		}
+
+	}
+
+	return ret;
+}
+
+static int load_bpf_object(struct bpf_cfg_in *cfg)
+{
+	struct bpf_program *p, *prog = NULL;
+	struct bpf_object *obj;
+	char root_path[PATH_MAX];
+	struct bpf_map *map;
+	int prog_fd, ret = 0;
+
+	ret = iproute2_get_root_path(root_path, PATH_MAX);
+	if (ret)
+		return ret;
+
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
+			.relaxed_maps = true,
+			.pin_root_path = root_path,
+	);
+
+	obj = bpf_object__open_file(cfg->object, &open_opts);
+	if (libbpf_get_error(obj)) {
+		fprintf(stderr, "ERROR: opening BPF object file failed\n");
+		return -ENOENT;
+	}
+
+	bpf_object__for_each_program(p, obj) {
+		/* Only load the programs that will either be subsequently
+		 * attached or inserted into a tail call map */
+		if (find_legacy_tail_calls(p, obj) < 0 && cfg->section &&
+		    strcmp(get_bpf_program__section_name(p), cfg->section)) {
+			ret = bpf_program__set_autoload(p, false);
+			if (ret)
+				return -EINVAL;
+			continue;
+		}
+
+		bpf_program__set_type(p, cfg->type);
+		bpf_program__set_ifindex(p, cfg->ifindex);
+		if (!prog)
+			prog = p;
+	}
+
+	bpf_object__for_each_map(map, obj) {
+		if (!bpf_map__is_offload_neutral(map))
+			bpf_map__set_ifindex(map, cfg->ifindex);
+	}
+
+	if (!prog) {
+		fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
+		return -ENOENT;
+	}
+
+	/* Handle iproute2 legacy pin maps and map-in-maps */
+	ret = handle_legacy_maps(obj);
+	if (ret)
+		goto unload_obj;
+
+	ret = bpf_object__load(obj);
+	if (ret)
+		goto unload_obj;
+
+	ret = update_legacy_tail_call_maps(obj);
+	if (ret)
+		goto unload_obj;
+
+	prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
+	if (prog_fd < 0)
+		ret = -errno;
+	else
+		cfg->prog_fd = prog_fd;
+
+unload_obj:
+	/* Close obj as we don't need it */
+	bpf_object__close(obj);
+	return ret;
+}
+
+/* Load ebpf and return prog fd */
+int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
+{
+	int ret = 0;
+
+	if (cfg->verbose)
+		libbpf_set_print(verbose_print);
+	else
+		libbpf_set_print(silent_print);
+
+	ret = iproute2_bpf_elf_ctx_init(cfg);
+	if (ret < 0) {
+		fprintf(stderr, "Cannot initialize ELF context!\n");
+		return ret;
+	}
+
+	ret = iproute2_bpf_fetch_ancillary();
+	if (ret < 0) {
+		fprintf(stderr, "Error fetching ELF ancillary data!\n");
+		return ret;
+	}
+
+	ret = load_bpf_object(cfg);
+	if (ret)
+		return ret;
+
+	return cfg->prog_fd;
+}
-- 
2.25.4

[PATCHv3 iproute2-next 4/5] examples/bpf: move struct bpf_elf_map defined maps to legacy folder

Message details and related content
Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 examples/bpf/README                        | 14 +++++++++-----
 examples/bpf/{ => legacy}/bpf_cyclic.c     |  2 +-
 examples/bpf/{ => legacy}/bpf_graft.c      |  2 +-
 examples/bpf/{ => legacy}/bpf_map_in_map.c |  2 +-
 examples/bpf/{ => legacy}/bpf_shared.c     |  2 +-
 examples/bpf/{ => legacy}/bpf_tailcall.c   |  2 +-
 6 files changed, 14 insertions(+), 10 deletions(-)
 rename examples/bpf/{ => legacy}/bpf_cyclic.c (95%)
 rename examples/bpf/{ => legacy}/bpf_graft.c (97%)
 rename examples/bpf/{ => legacy}/bpf_map_in_map.c (96%)
 rename examples/bpf/{ => legacy}/bpf_shared.c (97%)
 rename examples/bpf/{ => legacy}/bpf_tailcall.c (98%)
diff --git a/examples/bpf/README b/examples/bpf/README
index 1bbdda3f..732bcc83 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,8 +1,12 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
- - bpf_shared.c		-> Ingress/egress map sharing example
- - bpf_tailcall.c	-> Using tail call chains
- - bpf_cyclic.c		-> Simple cycle as tail calls
- - bpf_graft.c		-> Demo on altering runtime behaviour
- - bpf_map_in_map.c     -> Using map in map example
+ - legacy/bpf_shared.c		-> Ingress/egress map sharing example
+ - legacy/bpf_tailcall.c	-> Using tail call chains
+ - legacy/bpf_cyclic.c		-> Simple cycle as tail calls
+ - legacy/bpf_graft.c		-> Demo on altering runtime behaviour
+ - legacy/bpf_map_in_map.c	-> Using map in map example
+
+Note: Users should use new BTF way to defined the maps, the examples
+in legacy folder which is using struct bpf_elf_map defined maps is not
+recommanded.
diff --git a/examples/bpf/bpf_cyclic.c b/examples/bpf/legacy/bpf_cyclic.c
similarity index 95%
rename from examples/bpf/bpf_cyclic.c
rename to examples/bpf/legacy/bpf_cyclic.c
index 11d1c061..33590730 100644
--- a/examples/bpf/bpf_cyclic.c
+++ b/examples/bpf/legacy/bpf_cyclic.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* Cyclic dependency example to test the kernel's runtime upper
  * bound on loops. Also demonstrates on how to use direct-actions,
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/legacy/bpf_graft.c
similarity index 97%
rename from examples/bpf/bpf_graft.c
rename to examples/bpf/legacy/bpf_graft.c
index 07113d4a..f4c920cc 100644
--- a/examples/bpf/bpf_graft.c
+++ b/examples/bpf/legacy/bpf_graft.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* This example demonstrates how classifier run-time behaviour
  * can be altered with tail calls. We start out with an empty
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/legacy/bpf_map_in_map.c
similarity index 96%
rename from examples/bpf/bpf_map_in_map.c
rename to examples/bpf/legacy/bpf_map_in_map.c
index ff0e623a..575f8812 100644
--- a/examples/bpf/bpf_map_in_map.c
+++ b/examples/bpf/legacy/bpf_map_in_map.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 #define MAP_INNER_ID	42
 
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/legacy/bpf_shared.c
similarity index 97%
rename from examples/bpf/bpf_shared.c
rename to examples/bpf/legacy/bpf_shared.c
index 21fe6f1e..05b2b9ef 100644
--- a/examples/bpf/bpf_shared.c
+++ b/examples/bpf/legacy/bpf_shared.c
@@ -1,4 +1,4 @@
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 /* Minimal, stand-alone toy map pinning example:
  *
diff --git a/examples/bpf/bpf_tailcall.c b/examples/bpf/legacy/bpf_tailcall.c
similarity index 98%
rename from examples/bpf/bpf_tailcall.c
rename to examples/bpf/legacy/bpf_tailcall.c
index 161eb606..8ebc554c 100644
--- a/examples/bpf/bpf_tailcall.c
+++ b/examples/bpf/legacy/bpf_tailcall.c
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#include "../../include/bpf_api.h"
+#include "../../../include/bpf_api.h"
 
 #define ENTRY_INIT	3
 #define ENTRY_0		0
-- 
2.25.4

[PATCHv3 iproute2-next 5/5] examples/bpf: add bpf examples with BTF defined maps

Message details and related content
Users should try use the new BTF defined maps instead of struct
bpf_elf_map defined maps. The tail call examples are not added yet
as libbpf doesn't currently support declaratively populating tail call
maps.

Reviewed-by: Toke Høiland-Jørgensen <redacted>
Signed-off-by: Hangbin Liu <redacted>
---
 examples/bpf/README           |  6 ++++
 examples/bpf/bpf_graft.c      | 66 +++++++++++++++++++++++++++++++++++
 examples/bpf/bpf_map_in_map.c | 55 +++++++++++++++++++++++++++++
 examples/bpf/bpf_shared.c     | 53 ++++++++++++++++++++++++++++
 include/bpf_api.h             | 13 +++++++
 5 files changed, 193 insertions(+)
 create mode 100644 examples/bpf/bpf_graft.c
 create mode 100644 examples/bpf/bpf_map_in_map.c
 create mode 100644 examples/bpf/bpf_shared.c
diff --git a/examples/bpf/README b/examples/bpf/README
index 732bcc83..b7261191 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,6 +1,12 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
+- BTF defined map examples
+ - bpf_graft.c		-> Demo on altering runtime behaviour
+ - bpf_shared.c 	-> Ingress/egress map sharing example
+ - bpf_map_in_map.c	-> Using map in map example
+
+- legacy struct bpf_elf_map defined map examples
  - legacy/bpf_shared.c		-> Ingress/egress map sharing example
  - legacy/bpf_tailcall.c	-> Using tail call chains
  - legacy/bpf_cyclic.c		-> Simple cycle as tail calls
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/bpf_graft.c
new file mode 100644
index 00000000..8066dcce
--- /dev/null
+++ b/examples/bpf/bpf_graft.c
@@ -0,0 +1,66 @@
+#include "../../include/bpf_api.h"
+
+/* This example demonstrates how classifier run-time behaviour
+ * can be altered with tail calls. We start out with an empty
+ * jmp_tc array, then add section aaa to the array slot 0, and
+ * later on atomically replace it with section bbb. Note that
+ * as shown in other examples, the tc loader can prepopulate
+ * tail called sections, here we start out with an empty one
+ * on purpose to show it can also be done this way.
+ *
+ * tc filter add dev foo parent ffff: bpf obj graft.o
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-20229 [001] ..s. 138993.003923: : fallthrough
+ *   <idle>-0            [001] ..s. 138993.202265: : fallthrough
+ *   Socket Thread-20229 [001] ..s. 138994.004149: : fallthrough
+ *   [...]
+ *
+ * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec aaa
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-19818 [002] ..s. 139012.053587: : aaa
+ *   <idle>-0            [002] ..s. 139012.172359: : aaa
+ *   Socket Thread-19818 [001] ..s. 139012.173556: : aaa
+ *   [...]
+ *
+ * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec bbb
+ * tc exec bpf dbg
+ *   [...]
+ *   Socket Thread-19818 [002] ..s. 139022.102967: : bbb
+ *   <idle>-0            [002] ..s. 139022.155640: : bbb
+ *   Socket Thread-19818 [001] ..s. 139022.156730: : bbb
+ *   [...]
+ */
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);
+} jmp_tc __section(".maps");
+
+__section("aaa")
+int cls_aaa(struct __sk_buff *skb)
+{
+	printt("aaa\n");
+	return TC_H_MAKE(1, 42);
+}
+
+__section("bbb")
+int cls_bbb(struct __sk_buff *skb)
+{
+	printt("bbb\n");
+	return TC_H_MAKE(1, 43);
+}
+
+__section_cls_entry
+int cls_entry(struct __sk_buff *skb)
+{
+	tail_call(skb, &jmp_tc, 0);
+	printt("fallthrough\n");
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/bpf_map_in_map.c
new file mode 100644
index 00000000..39c86268
--- /dev/null
+++ b/examples/bpf/bpf_map_in_map.c
@@ -0,0 +1,55 @@
+#include "../../include/bpf_api.h"
+
+struct inner_map {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+} map_inner __section(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);
+	__array(values, struct inner_map);
+} map_outer __section(".maps") = {
+	.values = {
+		[0] = &map_inner,
+	},
+};
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			lock_xadd(val, 1);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			printt("map val: %d\n", *val);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/bpf_shared.c
new file mode 100644
index 00000000..99a332f4
--- /dev/null
+++ b/examples/bpf/bpf_shared.c
@@ -0,0 +1,53 @@
+#include "../../include/bpf_api.h"
+
+/* Minimal, stand-alone toy map pinning example:
+ *
+ * clang -target bpf -O2 [...] -o bpf_shared.o -c bpf_shared.c
+ * tc filter add dev foo parent 1: bpf obj bpf_shared.o sec egress
+ * tc filter add dev foo parent ffff: bpf obj bpf_shared.o sec ingress
+ *
+ * Both classifier will share the very same map instance in this example,
+ * so map content can be accessed from ingress *and* egress side!
+ *
+ * This example has a pinning of PIN_OBJECT_NS, so it's private and
+ * thus shared among various program sections within the object.
+ *
+ * A setting of PIN_GLOBAL_NS would place it into a global namespace,
+ * so that it can be shared among different object files. A setting
+ * of PIN_NONE (= 0) means no sharing, so each tc invocation a new map
+ * instance is being created.
+ */
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(key_size, sizeof(uint32_t));
+	__uint(value_size, sizeof(uint32_t));
+	__uint(max_entries, 1);
+	__uint(pinning, LIBBPF_PIN_BY_NAME);	/* or LIBBPF_PIN_NONE */
+} map_sh __section(".maps");
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	int key = 0, *val;
+
+	val = map_lookup_elem(&map_sh, &key);
+	if (val)
+		lock_xadd(val, 1);
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	int key = 0, *val;
+
+	val = map_lookup_elem(&map_sh, &key);
+	if (val)
+		printt("map val: %d\n", *val);
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
diff --git a/include/bpf_api.h b/include/bpf_api.h
index 89d3488d..82c47089 100644
--- a/include/bpf_api.h
+++ b/include/bpf_api.h
@@ -19,6 +19,19 @@
 
 #include "bpf_elf.h"
 
+/** libbpf pin type. */
+enum libbpf_pin_type {
+	LIBBPF_PIN_NONE,
+	/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
+	LIBBPF_PIN_BY_NAME,
+};
+
+/** Type helper macros. */
+
+#define __uint(name, val) int (*name)[val]
+#define __type(name, val) typeof(val) *name
+#define __array(name, val) typeof(val) *name[]
+
 /** Misc macros. */
 
 #ifndef __stringify
-- 
2.25.4

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
Hangbin Liu [off-list ref] writes:
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.

Signed-off-by: Hangbin Liu <redacted>
With one nit below, feel free to add back my:

Reviewed-by: Toke Høiland-Jørgensen <redacted>
quoted hunk
---
v3:
Check function bpf_program__section_name() separately and only use it
on higher libbpf version.

v2:
No update
---
 configure | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)
diff --git a/configure b/configure
index 307912aa..58a7176e 100755
--- a/configure
+++ b/configure
@@ -240,6 +240,97 @@ 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()

65 more quoted lines

You already added $LIBBPF_CFLAGS above, so with this it ends up being
duplicated, doesn't it?

-Toke

Re: [PATCHv2 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Thu, Oct 29, 2020 at 4:38 AM Jesper Dangaard Brouer
[off-list ref] wrote:
On Wed, 28 Oct 2020 19:50:51 -0700
Andrii Nakryiko [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 7:34 PM Stephen Hemminger
[off-list ref] wrote:
quoted
On Wed, 28 Oct 2020 19:27:20 -0700
Andrii Nakryiko [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 7:06 PM Hangbin Liu [off-list ref] wrote:
quoted
On Wed, Oct 28, 2020 at 05:02:34PM -0600, David Ahern wrote:
quoted
fails to compile on Ubuntu 20.10:
[...]
quoted
quoted
quoted
quoted
You need to update libbpf to latest version.
Why not using libbpf from submodule?
Because it makes it harder for people downloading tarballs and distributions.
Genuinely curious, making harder how exactly? When packaging sources
as a tarball you'd check out submodules before packaging, right?
quoted
Iproute2 has worked well by being standalone.
Again, maybe I'm missing something, but what makes it not a
standalone, if it is using a submodule? Pahole, for instance, is using
libbpf through submodule and just bypasses all the problems with
detection of features and library availability. I haven't heard anyone
complaining about it made working with pahole harder in any way.
I do believe you are missing something.
I don't think I got an answer how submodules make it harder for people
downloading tarballs and distributions, and the standalone-ness issue.
Your security angle is a very different aspect.
 I guess I can be the relay for
complains, so you will officially hear about this.  Red Hat and Fedora
security is complaining that we are packaging a library (libbpf)
directly into the individual packages.  They complain because in case
of a security issue, they have to figure out to rebuild all the software
packages that are statically compiled with this library.
They must be having nightmares already about BCC, bpftool, pahole, as
well as perf built with libbpf statically (perf on my server is, at
least). I also wonder how many other projects do use either submodules
or static linking with libraries as well.
Maybe you say I don't care that Distro security teams have to do more
work and update more packages.  Then security team says, we expect
customers will use this library right, and if we ship it as a dynamic
loadable (.so) file, then we can update and fix security issues in
library without asking customers to recompile. (Notice the same story
goes if we can update the base-image used by a container).
It's a trade off, and everyone decides for themselves where they want
to stand on this.

On the one hand, there are security folks obsessing about hypothetical
security vulnerabilities in libbpf so bad that they will need to
update libbpf overnight.

On the other hand, extra complexity for multiple users of libbpf to do
feature detection and working around the lack of some of the APIs in
libbpf due to older versions in the system. That extra complexity
might lead to more problems, bugs, vulnerabilities in the long run.

I understand the concerns and how dynamic libraries make it easier. We
can't really know for sure which of those two aspects would lead to
more pain and problems overall. I personally choose simplicity,
though.

But as I said, it's up to iproute2 folks to decide. Was just curious
about some of the claims I cited.


--
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
On 10/29/20 9:11 AM, Hangbin Liu wrote:
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.
Add an option to force libbpf off and use of the legacy code. i.e, yes
it is installed, but don't use it.

configure script really needs a usage to dump options like disabling libbpf.

Re: [PATCHv3 iproute2-next 3/5] lib: add libbpf support

Message details and related content
On 10/29/20 9:11 AM, Hangbin Liu wrote:
quoted hunk
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 33150ac2..afaf1de7 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -28,8 +28,14 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+
 #include "bpf_util.h"
 
+#ifdef HAVE_LIBBPF
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#endif
+
 #define CGRP_PROC_FILE  "/cgroup.procs"
 
 static struct link_filter vrf_filter;
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;
 	}
 
+#ifdef HAVE_LIBBPF
+	if (bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0)) {
+#else

5 more quoted lines

I would prefer to have these #ifdef .. #endif checks consolidated in the
lib code. Create a bpf_compat file for these. e.g.,

int bpf_program_load(enum bpf_prog_type type, const struct bpf_insn *insns,
                     size_t size_insns, const char *license, char *log,
                     size_t size_log)
{
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
}

Similarly for bpf_program_attach.


I think even the includes can be done once in bpf_util.h with a single
+#ifdef HAVE_LIBBPF
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#endif
+

The iproute2_* functions added later in this patch can be in the compat
file as well.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 10/29/20 9:11 AM, Hangbin Liu wrote:
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.
In regards to comments from v2 of the series:

iproute2 is a stable, production package that requires minimal support
from external libraries. The external packages it does require are also
stable with few to no relevant changes.

bpf and libbpf on the other hand are under active development and
rapidly changing month over month. The git submodule approach has its
conveniences for rapid development but is inappropriate for a package
like iproute2 and will not be considered.

To explicitly state what I think should be obvious to any experienced
Linux user, iproute2 code should always compile and work *without
functionality loss* on LTS versions N and N-1 of well known OS’es with
LTS releases (e.g., Debian, Ubuntu, RHEL). Meaning iproute2 will compile
and work with the external dependencies as they exist in that OS version.

I believe there are more than enough established compatibility and
library version checks to find the middle ground to integrate new
features requiring new versions of libbpf while maintaining stability
and compatibility with older releases. The biannual releases of Ubuntu
and Fedora serve as testing grounds for integrating new features
requiring a newer version of libbpf while continuing to work with
released versions of libbpf. It appears Debian Bullseye will also fall
into this category.

Finally, bpf-based features in iproute2 will only be committed once
relevant support exists in a released version of libbpf (ie., the github
version, not just commits to the in-kernel tree version). Patches can
and should be sent for review based on testing with the in-kernel tree
version of libbpf, but I will not commit them until the library has been
released.

Thanks for working on this, Hangbin. It is right direction in the long term.

Re: [PATCHv3 iproute2-next 3/5] lib: add libbpf support

Message details and related content
On Mon, Nov 02, 2020 at 08:41:09AM -0700, David Ahern wrote:
I would prefer to have these #ifdef .. #endif checks consolidated in the
lib code. Create a bpf_compat file for these. e.g.,

int bpf_program_load(enum bpf_prog_type type, const struct bpf_insn *insns,
                     size_t size_insns, const char *license, char *log,
                     size_t size_log)
{
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
}

Similarly for bpf_program_attach.
I think even the includes can be done once in bpf_util.h with a single
+#ifdef HAVE_LIBBPF
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#endif
+

The iproute2_* functions added later in this patch can be in the compat
file as well.
The iproute2_* functions need access static struct bpf_elf_ctx __ctx;
We need move the struct bpf_elf_ctx to another header file if add the
iproute2_* functions to compat file. Do you still want this?

Thanks
Hangbin

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
On Mon, Nov 02, 2020 at 08:37:37AM -0700, David Ahern wrote:
On 10/29/20 9:11 AM, Hangbin Liu wrote:
quoted
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.
Add an option to force libbpf off and use of the legacy code. i.e, yes
it is installed, but don't use it.

configure script really needs a usage to dump options like disabling libbpf.
Shouldn't we use libbpf by default if system support? The same like libmnl.
There is no options to force libnml off.

Thanks
Hangbin

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Mon, Nov 2, 2020 at 7:47 AM David Ahern [off-list ref] wrote:
On 10/29/20 9:11 AM, Hangbin Liu wrote:
quoted
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.
In regards to comments from v2 of the series:

iproute2 is a stable, production package that requires minimal support
from external libraries. The external packages it does require are also
stable with few to no relevant changes.

bpf and libbpf on the other hand are under active development and
rapidly changing month over month. The git submodule approach has its
conveniences for rapid development but is inappropriate for a package
like iproute2 and will not be considered.
It's ok to not consider that, really. I'm trying to understand what's
so bad about the submodule approach, not convince you (anymore) to use
libbpf through submodule. And the submodule is not for rapid
development, it's mainly for guaranteed libbpf features and version,
and simplicity of iproute2 code when using libbpf.

But I don't think I got a real answer as to what's the exact reason
against the submodule. Like what "inappropriate" even means in this
case? Jesper's security argument so far was the only objective
criteria, as far as I can tell.
To explicitly state what I think should be obvious to any experienced
Linux user, iproute2 code should always compile and work *without
functionality loss* on LTS versions N and N-1 of well known OS’es with
LTS releases (e.g., Debian, Ubuntu, RHEL). Meaning iproute2 will compile
and work with the external dependencies as they exist in that OS version.
I love the appeal to obviousness and "experienced Linux user" ;)

But I also see that using libbpf through submodule gives iproute2
exact control over which version of libbpf is being used. And that
does not depend at all on any specific Linux distribution, its
version, LTS vs non-LTS, etc. iproute2 will just work the same across
all of them. So matches your stated goals very directly and
explicitly.
I believe there are more than enough established compatibility and
library version checks to find the middle ground to integrate new
features requiring new versions of libbpf while maintaining stability
and compatibility with older releases. The biannual releases of Ubuntu
and Fedora serve as testing grounds for integrating new features
requiring a newer version of libbpf while continuing to work with
released versions of libbpf. It appears Debian Bullseye will also fall
into this category.
Beyond just more unnecessary complexity in iproute2 library to
accommodate older libbpf versions, users basically will need to pay
closer attention not just to which version of iproute2 they have, but
also which version of libbpf is installed on their system. Which is
ok, but an unnecessary burden, IMO. By controlling the libbpf version
through the submodule, it would be simple to say: "iproute2 vX uses
libbpf vY with features Z1, Z2, Z3". Then the user would just know
what to expect from iproute2 and its BPF support. And iproute2 code
base won't have to do as much feature detection and condition
compilation tricks.

That's what I don't understand, why settle for the lowest common
denominator of libbpf versions across a wide range of systems, when
you can take control and develop against a well-known version of
libbpf. I get security upgrades angle (even if I don't rank it higher
than simplicity). But I don't get the ideal behind a blanket statement
"libbpf through submodule is inappropriate".
Finally, bpf-based features in iproute2 will only be committed once
relevant support exists in a released version of libbpf (ie., the github
version, not just commits to the in-kernel tree version). Patches can
and should be sent for review based on testing with the in-kernel tree
version of libbpf, but I will not commit them until the library has been
released.
Makes sense. And the submodule approach gives you a great deal of
control and flexibility in this case. For testing, it's easy to use
either Github or even in-kernel sources (with a bit of symlinking,
though). But for upstreaming the submodule should only reference a
released tag from Github repo. Again, everything seems to work out,
no?
Thanks for working on this, Hangbin. It is right direction in the long term.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Mon, 2 Nov 2020 22:58:06 -0800, Andrii Nakryiko wrote:
But I don't think I got a real answer as to what's the exact reason
against the submodule. Like what "inappropriate" even means in this
case? Jesper's security argument so far was the only objective
criteria, as far as I can tell.
It's the fundamental objection. Distributions in general have the "no
bundled libraries" policy. It is sometimes annoying but it helps to
understand that the policy is not a whim of distros, it's coming from
years of experience with package maintenance for security and stability.
But I also see that using libbpf through submodule gives iproute2
exact control over which version of libbpf is being used. And that
does not depend at all on any specific Linux distribution, its
version, LTS vs non-LTS, etc. iproute2 will just work the same across
all of them. So matches your stated goals very directly and
explicitly.
If you take this route, the end result would be all dependencies for
all projects being included as submodules and bundled. At the first
sight, this sounds easier for the developers. Why bother with dynamic
linking at all? Everything can be linked statically.

The result would be nightmare for both distros and users. No timely
security updates possible, critical bugs not being fixed in some
programs, etc. There is enough experience with this kind of setup to
conclude it is not the right way to go.

Yes, dynamic linking is initially more work for developers of both apps
and libraries. However, it pays off over time - there's no need to keep
track of security and other important fixes in the dependencies, it
comes for free from the distro work.

Btw, taking the bundling to the extreme, every app could bundle its own
well tested and compatible kernel version and be run in a VM. This
might sound far fetched but there were actual attempts to do that. It
didn't take off; I think part of the reason was that the Linux kernel
is very good in keeping its APIs stable.

And I'm convinced this is the way to go for libraries, too: put an
emphasis on API stability. Make it easy to get consumed and updated
under the hood. Everybody wins this way.

 Jiri

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 11/3/20 7:58 AM, Andrii Nakryiko wrote:
On Mon, Nov 2, 2020 at 7:47 AM David Ahern [off-list ref] wrote:
quoted
On 10/29/20 9:11 AM, Hangbin Liu wrote:
quoted
This series converts iproute2 to use libbpf for loading and attaching
BPF programs when it is available. This means that iproute2 will
correctly process BTF information and support the new-style BTF-defined
maps, while keeping compatibility with the old internal map definition
syntax.

This is achieved by checking for libbpf at './configure' time, and using
it if available. By default the system libbpf will be used, but static
linking against a custom libbpf version can be achieved by passing
LIBBPF_DIR to configure. FORCE_LIBBPF can be set to force configure to
abort if no suitable libbpf is found (useful for automatic packaging
that wants to enforce the dependency).

The old iproute2 bpf code is kept and will be used if no suitable libbpf
is available. When using libbpf, wrapper code ensures that iproute2 will
still understand the old map definition format, including populating
map-in-map and tail call maps before load.

The examples in bpf/examples are kept, and a separate set of examples
are added with BTF-based map definitions for those examples where this
is possible (libbpf doesn't currently support declaratively populating
tail call maps).

At last, Thanks a lot for Toke's help on this patch set.
In regards to comments from v2 of the series:

iproute2 is a stable, production package that requires minimal support
from external libraries. The external packages it does require are also
stable with few to no relevant changes.

bpf and libbpf on the other hand are under active development and
rapidly changing month over month. The git submodule approach has its
conveniences for rapid development but is inappropriate for a package
like iproute2 and will not be considered.
I thought last time this discussion came up there was consensus that the
submodule could be an explicit opt in for the configure script at least?

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
On 11/2/20 10:54 PM, Hangbin Liu wrote:
On Mon, Nov 02, 2020 at 08:37:37AM -0700, David Ahern wrote:
quoted
On 10/29/20 9:11 AM, Hangbin Liu wrote:
quoted
This patch adds a check to see if we support libbpf. By default the
system libbpf will be used, but static linking against a custom libbpf
version can be achieved by passing LIBBPF_DIR to configure. FORCE_LIBBPF
can be set to force configure to abort if no suitable libbpf is found,
which is useful for automatic packaging that wants to enforce the
dependency.
Add an option to force libbpf off and use of the legacy code. i.e, yes
it is installed, but don't use it.

configure script really needs a usage to dump options like disabling libbpf.
Shouldn't we use libbpf by default if system support? The same like libmnl.
There is no options to force libnml off.
configure scripts usually allow you to control options directly,
overriding the autoprobe.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 11/3/20 1:42 AM, Jiri Benc wrote:
And I'm convinced this is the way to go for libraries, too: put an
emphasis on API stability. Make it easy to get consumed and updated
under the hood. Everybody wins this way.
exactly. Libraries should export well thought out, easy to use, stable
APIs. Maintainers do not need to be concerned about how the code is
consumed by projects.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, Nov 3, 2020 at 9:36 AM David Ahern [off-list ref] wrote:
On 11/3/20 1:46 AM, Daniel Borkmann wrote:
quoted
I thought last time this discussion came up there was consensus that the
submodule could be an explicit opt in for the configure script at least?
I do not recall Stephen agreeing to that, and I certainly did not.
Daniel,

since David is deaf to technical arguments,
how about we fork iproute2 and maintain it separately?

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, 3 Nov 2020 09:47:00 -0800
Alexei Starovoitov [off-list ref] wrote:
On Tue, Nov 3, 2020 at 9:36 AM David Ahern [off-list ref] wrote:
quoted
On 11/3/20 1:46 AM, Daniel Borkmann wrote:  
quoted
I thought last time this discussion came up there was consensus that the
submodule could be an explicit opt in for the configure script at least?  
I do not recall Stephen agreeing to that, and I certainly did not.  
Daniel,

since David is deaf to technical arguments,
how about we fork iproute2 and maintain it separately?
A submodule is not a practical viable option.

Please come back when you are ready to use distro libbpf packages.
This seems a microcosm of the Linux packaging problem that was discussed
around Kubernetes and "vendorizaton"

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 11/3/20 10:47 AM, Alexei Starovoitov wrote:
since David is deaf to technical arguments,
It is not that I am "deaf to technical arguments"; you do not like my
response.

The scope of bpf in iproute2 is tiny - a few tc modules (and VRF but it
does not need libbpf) which is a small subset of the functionality and
commands within the package.

The configure script will allow you to use any libbpf version you wish.
Standard operating procedure for configuring a dependency within a package.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, Nov 03, 2020 at 03:32:55PM -0700, David Ahern wrote:
On 11/3/20 10:47 AM, Alexei Starovoitov wrote:
quoted
since David is deaf to technical arguments,
It is not that I am "deaf to technical arguments"; you do not like my
response.

The scope of bpf in iproute2 is tiny - a few tc modules (and VRF but it
does not need libbpf) which is a small subset of the functionality and
commands within the package.
When Hangbin sent this patch set I got excited that finally tc command
will start working with the latest bpf elf files.
Currently "tc" supports 4 year old files which caused plenty of pain to bpf users.
I got excited, but now I've realized that this patch set will make it worse.
The bpf support in "tc" command instead of being obviously old and obsolete
will be sort-of working with unpredictable delay between released kernel
and released iproute2 version. The iproute2 release that suppose to match kernel
release will be meaningless.
More so, the upgrade of shared libbpf.so can make older iproute2/tc to do 
something new and unpredictable.
The user experience will be awful. Not only the users won't know
what to expect out of 'tc' command they won't have a way to debug it.
All of it because iproute2 build will take system libbpf and link it
as shared library by default.
So I think iproute2 must not use libbpf. If I could remove bpf support
from iproute2 I would do so as well.
The current state of iproute2 is hurting bpf ecosystem and proposed
libbpf+iproute2 integration will make it worse.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 11/3/20 3:55 PM, Alexei Starovoitov wrote:
The bpf support in "tc" command instead of being obviously old and obsolete
will be sort-of working with unpredictable delay between released kernel
and released iproute2 version. The iproute2 release that suppose to match kernel
release will be meaningless.
iproute2, like all userspace commands, is written to an API and for well
written APIs the commands should be backward and forward compatible
across kernel versions. Kernel upgrades do not force an update of the
entire ecosystem. New userspace on old kernels should again just work.
New functionality in the new userpsace will not, but detection of that
is a different problem and relies on kernel APIs doing proper data
validation.

More so, the upgrade of shared libbpf.so can make older iproute2/tc to do 
something new and unpredictable.
How so? If libbpf is written against kernel APIs and properly versioned,
it should just work. A new version of libbpf changes the .so version, so
old commands will not load it.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, Nov 03, 2020 at 02:55:54PM -0800, Alexei Starovoitov wrote:
quoted
The scope of bpf in iproute2 is tiny - a few tc modules (and VRF but it
does not need libbpf) which is a small subset of the functionality and
commands within the package.
When Hangbin sent this patch set I got excited that finally tc command
will start working with the latest bpf elf files.
Currently "tc" supports 4 year old files which caused plenty of pain to bpf users.
I got excited, but now I've realized that this patch set will make it worse.
The bpf support in "tc" command instead of being obviously old and obsolete
will be sort-of working with unpredictable delay between released kernel
and released iproute2 version. The iproute2 release that suppose to match kernel
release will be meaningless.
More so, the upgrade of shared libbpf.so can make older iproute2/tc to do 
something new and unpredictable.
The user experience will be awful. Not only the users won't know
what to expect out of 'tc' command they won't have a way to debug it.
All of it because iproute2 build will take system libbpf and link it
as shared library by default.
So I think iproute2 must not use libbpf. If I could remove bpf support
from iproute2 I would do so as well.
The current state of iproute2 is hurting bpf ecosystem and proposed
libbpf+iproute2 integration will make it worse.
Hi Guys,

Please take it easy. IMHO, it always very hard to make a perfect solution.
From development side, it's easier and could get latest features by using
libbpf as submodule. But we need to take care of users, backward
compatibility, distros policy etc.

I like using iproute2 to load bpf objs. But it's not standardized and too old
to load the new BTF defined objs. I think all of us like to improve it by
using libbpf. But users and distros are slowly. Some user are still using
`ifconfig`. Distros have policies to link the shared .so, etc. We have to
compromise on something.

Our purpose is to push the user to use new features. As this patchset
does, push users to try libbpf instead of legacy code. But this need time.

Sorry if my word make you feel confused. I'm not a native speaker, but I hope
we could find a solution that all(we, users, distros) could accept instead of
break/give up.

Thanks
Hangbin

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, Nov 03, 2020 at 06:40:44PM -0700, David Ahern wrote:
On 11/3/20 3:55 PM, Alexei Starovoitov wrote:
quoted
The bpf support in "tc" command instead of being obviously old and obsolete
will be sort-of working with unpredictable delay between released kernel
and released iproute2 version. The iproute2 release that suppose to match kernel
release will be meaningless.
iproute2, like all userspace commands, is written to an API and for well
written APIs the commands should be backward and forward compatible
across kernel versions. Kernel upgrades do not force an update of the
entire ecosystem. New userspace on old kernels should again just work.
New functionality in the new userpsace will not, but detection of that
is a different problem and relies on kernel APIs doing proper data
validation.
commands ?!
libbpf is not a library that translates user input into kernel syscalls.
It's not libmnl that is a wrapper for netlink.
It's not libelf either.
libbpf probes kernel features and does different things depending on what it found.
libbpf is the only library I know that is backward and forward compatible.
All other libraries are backwards compatible only.
iproute2 itself is backward compatible only as well.
New devlink feature in iproute2 won't do anything on the kernel that doesn't
have the support.
libbpf, on the other side, has to work on older kernels. New libbpf features
have to gradually degrade when possible.
The users can upgrade and downgrade libbpf version at any time.
They can upgrade and downgrade kernel while keeping libbpf version the same.
The users can upgrade llvm as well and libbpf has to expect unexpected
and deal with all combinations.
quoted
More so, the upgrade of shared libbpf.so can make older iproute2/tc to do 
something new and unpredictable.
How so? If libbpf is written against kernel APIs and properly versioned,
it should just work. A new version of libbpf changes the .so version, so
old commands will not load it.
Please point out where do you see this happening in the patch set.
See tools/lib/bpf/README.rst to understand the versioning.

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Wed, Nov 04, 2020 at 10:17:30AM +0800, Hangbin Liu wrote:
On Tue, Nov 03, 2020 at 02:55:54PM -0800, Alexei Starovoitov wrote:
quoted
quoted
The scope of bpf in iproute2 is tiny - a few tc modules (and VRF but it
does not need libbpf) which is a small subset of the functionality and
commands within the package.
When Hangbin sent this patch set I got excited that finally tc command
will start working with the latest bpf elf files.
Currently "tc" supports 4 year old files which caused plenty of pain to bpf users.
I got excited, but now I've realized that this patch set will make it worse.
The bpf support in "tc" command instead of being obviously old and obsolete
will be sort-of working with unpredictable delay between released kernel
and released iproute2 version. The iproute2 release that suppose to match kernel
release will be meaningless.
More so, the upgrade of shared libbpf.so can make older iproute2/tc to do 
something new and unpredictable.
The user experience will be awful. Not only the users won't know
what to expect out of 'tc' command they won't have a way to debug it.
All of it because iproute2 build will take system libbpf and link it
as shared library by default.
So I think iproute2 must not use libbpf. If I could remove bpf support
from iproute2 I would do so as well.
The current state of iproute2 is hurting bpf ecosystem and proposed
libbpf+iproute2 integration will make it worse.
Hi Guys,

Please take it easy. IMHO, it always very hard to make a perfect solution.
From development side, it's easier and could get latest features by using
libbpf as submodule. But we need to take care of users, backward
compatibility, distros policy etc.

I like using iproute2 to load bpf objs. But it's not standardized and too old
to load the new BTF defined objs. I think all of us like to improve it by
using libbpf. But users and distros are slowly. Some user are still using
`ifconfig`. Distros have policies to link the shared .so, etc. We have to
compromise on something.

Our purpose is to push the user to use new features. As this patchset
does, push users to try libbpf instead of legacy code. But this need time.
My problem with iproute2 picking random libbpf is unpredictability.
Such roll of dice gives no confidence to users on what is expected to work.
bpf_hello_world.o will load, but that's it.
What is going to work with this or that version of "tc" command? No one knows.
The user will do 'tc -V'. Does version mean anything from bpf loading pov?
It's not. The user will do "ldd `which tc`" and then what?
Such bpf support in "tc" is worse than the current one.
At least the current one is predictably old.

There are alternatives though.
Forking the whole iproute2 because of "tc" is pointless, of course.
My 'proposal' was a fire starter because people are too stubborn to
realize that their long term believes could be incorrect until the fire is burning.
"bpftool prog load" can load any kind of elf. It cannot operate on qdiscs
and shouldn't do qdisc manipulations, but may be we can combine them into pipe
of some sort. Like "bpftool prog load file.o | tc filter ... bpf pipe"
I think that would be better long term. It will be predictable.

When we release new version of libbpf it goes through rigorous testing.
bpftool gets a lot of test coverage as well.
iproute2 with shared libbpf will get nothing. It's the same random roll of dice.
New libbpf may or may not break iproute2. That's awful user experience.
So iproute2 has to use git submodule with particular libbpf sha.
Then libbpf release process can incorporate proper testing of libbpf
and iproute2 combination.
Or iproute2 should stay as-is with obsolete bpf support.

Few years from now the situation could be different and shared libbpf would
be the most appropriate choice. But that day is not today.

Re: [PATCHv3 iproute2-next 3/5] lib: add libbpf support

Message details and related content
On Mon, Nov 02, 2020 at 08:41:09AM -0700, David Ahern wrote:
On 10/29/20 9:11 AM, Hangbin Liu wrote:
quoted
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
index 33150ac2..afaf1de7 100644
--- a/ip/ipvrf.c
+++ b/ip/ipvrf.c
@@ -28,8 +28,14 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+
 #include "bpf_util.h"
 
+#ifdef HAVE_LIBBPF
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#endif
+
 #define CGRP_PROC_FILE  "/cgroup.procs"
 
 static struct link_filter vrf_filter;
@@ -256,8 +262,13 @@ static int prog_load(int idx)
 		BPF_EXIT_INSN(),
 	};
 
+#ifdef HAVE_LIBBPF
+	return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
+				"GPL", 0, bpf_log_buf, sizeof(bpf_log_buf));
+#else
 	return bpf_prog_load_buf(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
 			         "GPL", bpf_log_buf, sizeof(bpf_log_buf));
+#endif
 }
 
 static int vrf_configure_cgroup(const char *path, int ifindex)
@@ -288,7 +299,11 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
 		goto out;
 	}
 
+#ifdef HAVE_LIBBPF
+	if (bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0)) {

32 more quoted lines

Oh, I just found why I didn't include libbpf.h in bpf_legacy.c.
The reason is there are more function conflicts. e.g.
bpf_obj_get, bpf_obj_pin, bpf_prog_attach.

If we move this #ifdef HAVE_LIBBPF to bpf_legacy.c, we need to rename
them all. With current patch, we limit all the legacy functions in bpf_legacy
and doesn't mix them with libbpf.h. What do you think?

Thanks
Hangbin

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
On Tue, Nov 03, 2020 at 10:32:37AM -0700, David Ahern wrote:
configure scripts usually allow you to control options directly,
overriding the autoprobe.
What do you think of the follow update? It's a little rough and only controls
libbpf.

$ git diff
diff --git a/configure b/configure
index 711bb69c..be35c024 100755
--- a/configure
+++ b/configure
@@ -442,6 +442,35 @@ endif
 EOF
 }

+usage()
+{
+       cat <<EOF
+Usage: $0 [OPTIONS]
+  -h | --help                  Show this usage info
+  --no-libbpf                  build the package without libbpf
+  --libbpf-dir=DIR             build the package with self defined libbpf dir
+EOF
+       exit $1
+}
+
+while true; do
+       case "$1" in
+               --libbpf-dir)
+                       LIBBPF_DIR="$2"
+                       shift 2 ;;
+               --no-libbpf)
+                       NO_LIBBPF_CHECK=1
+                       shift ;;
+               -h | --help)
+                       usage 0 ;;
+               "")
+                       break ;;
+               *)
+                       usage 1 ;;
+       esac
+done
+
+
 echo "# Generated config based on" $INCLUDE >$CONFIG
 quiet_config >> $CONFIG
@@ -476,8 +505,10 @@ check_setns
 echo -n "SELinux support: "
 check_selinux

-echo -n "libbpf support: "
-check_libbpf
+if [ -z $NO_LIBBPF_CHECK ]; then
+       echo -n "libbpf support: "
+       check_libbpf
+fi

 echo -n "ELF support: "
 check_elf

$ ./configure -h
Usage: ./configure [OPTIONS]
  -h | --help                   Show this usage info
  --no-libbpf                   build the package without libbpf
  --libbpf-dir=DIR              build the package with self defined libbpf dir

Thanks
Hangbin

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, 3 Nov 2020 18:45:59 -0800, Alexei Starovoitov wrote:
libbpf is the only library I know that is backward and forward compatible.
This is great to hear. It means there will be no problem with iproute2
using the system libbpf. As libbpf is both backward and forward
compatible, iproute2 will just work with whatever version it is used
with.
All other libraries are backwards compatible only.
Backward compatibility would be enough for iproute2 but forward
compatibility does not hurt, of course.
The users can upgrade and downgrade libbpf version at any time.
They can upgrade and downgrade kernel while keeping libbpf version the same.
The users can upgrade llvm as well and libbpf has to expect unexpected
and deal with all combinations.
This actually goes beyond what would be needed for iproute2 dynamically
linked against system libbpf.
quoted
How so? If libbpf is written against kernel APIs and properly versioned,
it should just work. A new version of libbpf changes the .so version, so
old commands will not load it.  
Please point out where do you see this happening in the patch set.
See tools/lib/bpf/README.rst to understand the versioning.
If the iproute2 binaries are linked against a symbol of a newer version than
is available in the system libbpf (which should not really happen
unless the system is broken), the dynamic linker will refuse to load
it. If the binary is linked against an old version of a particular
symbol, that old version will be used, if it's still provided by the
library. Otherwise, it will not load. I don't see a problem here?

The only problem would be if a particular function changed its
semantics while retaining ABI. But since libbpf is backward and forward
compatible, this should not happen.

 Jiri

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On Tue, 3 Nov 2020 19:11:45 -0800, Alexei Starovoitov wrote:
When we release new version of libbpf it goes through rigorous testing.
bpftool gets a lot of test coverage as well.
iproute2 with shared libbpf will get nothing. It's the same random roll of dice.
"Random roll of dice" would be true only if libbpf did incredibly bad
job in keeping backward compatibility. In my experience it is not the
case. Sure, a bug in retaining the compatibility may occasionally
appear; after all, any software tends to contain bugs in various
places. You are right that such bug may not be caught by your testing.

I also believe that if there is a bug in backward compatibility
reported by someone, it will be fixed (if possible). So this is really
just a matter of testing, not a fundamental problem of ABI
compatibility.

Let the distros worry about the testing. Upstream may test (and
even recommend!) certain combinations of iproute2 + libbpf, such as the
latest of both at the time of testing. If distros want to use a
different combination, they can and should do their own testing. If
their testing reveals a bug in backward compatibility and a patch to
fix it is accepted, everything will work smoothly for the distro users.

Non-distro users (or small distros) may just rely on the upstream
tested combination of iproute2 + libbpf.
Few years from now the situation could be different and shared libbpf would
be the most appropriate choice. But that day is not today.
Interestingly, the major compatibility problems we had were with llvm
updates. After llvm update while keeping the same kernel version, llvm
started to emit code that the verifier did not accept. Meaning a bpf
program that was previously accepted by the kernel was rejected after
recompilation. This was solved by adding a translation code to libbpf
(which nicely demonstrates that indeed libbpf cares about backward
compatibility).

Now, with dynamically linked libbpf, a single package update was able
to solve the problem for everything on the system, including users' own
programs. All that was needed was making the llvm package force update
the libbpf package (which rpm can do easily with its Conflicts
dependency).

So, at least for us, there was so far no disadvantage (and no problem)
with dynamic linking and a quite substantial advantage.

 Jiri

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 11/4/20 4:11 AM, Alexei Starovoitov wrote:
On Wed, Nov 04, 2020 at 10:17:30AM +0800, Hangbin Liu wrote:
quoted
On Tue, Nov 03, 2020 at 02:55:54PM -0800, Alexei Starovoitov wrote:
quoted
quoted
The scope of bpf in iproute2 is tiny - a few tc modules (and VRF but it
does not need libbpf) which is a small subset of the functionality and
commands within the package.
When Hangbin sent this patch set I got excited that finally tc command
will start working with the latest bpf elf files.
Currently "tc" supports 4 year old files which caused plenty of pain to bpf users.
I got excited, but now I've realized that this patch set will make it worse.
The bpf support in "tc" command instead of being obviously old and obsolete
will be sort-of working with unpredictable delay between released kernel
and released iproute2 version. The iproute2 release that suppose to match kernel
release will be meaningless.
More so, the upgrade of shared libbpf.so can make older iproute2/tc to do
something new and unpredictable.
The user experience will be awful. Not only the users won't know
what to expect out of 'tc' command they won't have a way to debug it.
All of it because iproute2 build will take system libbpf and link it
as shared library by default.
So I think iproute2 must not use libbpf. If I could remove bpf support
from iproute2 I would do so as well.
The current state of iproute2 is hurting bpf ecosystem and proposed
libbpf+iproute2 integration will make it worse.
Please take it easy. IMHO, it always very hard to make a perfect solution.
From development side, it's easier and could get latest features by using
libbpf as submodule. But we need to take care of users, backward
compatibility, distros policy etc.

I like using iproute2 to load bpf objs. But it's not standardized and too old
to load the new BTF defined objs. I think all of us like to improve it by
using libbpf. But users and distros are slowly. Some user are still using
`ifconfig`. Distros have policies to link the shared .so, etc. We have to
compromise on something.

Our purpose is to push the user to use new features. As this patchset
does, push users to try libbpf instead of legacy code. But this need time.
My problem with iproute2 picking random libbpf is unpredictability.

7 more quoted lines

User experience will be crappy and predictability worse, agree on that. For libbpf
it's the same as with rest of iproute2 code in that features are developed along
with the kernel. Distros so far are more or less used to upgrade iproute2 along
with new kernel releases though it's not the first time that some major ones have
been shipping old iproute2 for several releases until we pinged them to finally
get their act together to upgrade. With libbpf dynamically linked it's one more
moving target and it's not clear whether distros will upgrade with same cadence
as iproute2 (or even add libbpf as dependency to their packaging). Only option
users might have if they were to rely on iproute2 and to have predictability is
to ship their stuff via container with current libbpf approach which is probably
not the goal of this set.
There are alternatives though.
Forking the whole iproute2 because of "tc" is pointless, of course.
My 'proposal' was a fire starter because people are too stubborn to
realize that their long term believes could be incorrect until the fire is burning.
"bpftool prog load" can load any kind of elf. It cannot operate on qdiscs
and shouldn't do qdisc manipulations, but may be we can combine them into pipe
of some sort. Like "bpftool prog load file.o | tc filter ... bpf pipe"
I think that would be better long term. It will be predictable.
We've been thinking about 'bpftool prog load' as well given we build it right of
the kernel tree and bpftool + libbpf are predictable since they are both built out
of the same git tree and with latest features. I don't think it needs to pipe, it
would be enough to just specify where the loaded progs should be pinned in bpf fs
and tc/ip(xdp) already has the option to pick fd of the entry point from pinned
file. But should be doable as well to just pass fd via pipe to avoid later potential
cleanup. Either way I think it makes sense to do 'bpftool prog load' regardless
since it's generic and useful also for other (non-networking) prog types that can
be attached elsewhere in the system.
When we release new version of libbpf it goes through rigorous testing.
bpftool gets a lot of test coverage as well.
iproute2 with shared libbpf will get nothing. It's the same random roll of dice.
New libbpf may or may not break iproute2. That's awful user experience.
So iproute2 has to use git submodule with particular libbpf sha.
Alternatively, you have an uapi sync script already in order to not rely on the
distro system headers installed by the distro and to copy latest uapi ones from
kernel tree. Could as well be extended to have similar fixed built-in situation as
with the current lib/bpf.c in iproute2. Back in the days when developing lib/bpf.c,
it was explicitly done as built-in for iproute2 so that it doesn't take years for
users to actually get to the point where they can realistically make use of it. If
we were to extend the internal lib/bpf.c to similar feature state as libbpf today,
how is that different in the bigger picture compared to sync or submodule... so far
noone complained about lib/bpf.c.
Then libbpf release process can incorporate proper testing of libbpf
and iproute2 combination.
Or iproute2 should stay as-is with obsolete bpf support.

Few years from now the situation could be different and shared libbpf would
be the most appropriate choice. But that day is not today.
Yep, for libbpf to be in same situation as libelf or libmnl basically feature
development would have to pretty much come to a stop so that even minor or exotic
distros get to a point where they ship same libbpf version as major distros where
then users can start to rely on the base feature set for developing programs
against it.

Thanks,
Daniel

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
Hangbin Liu [off-list ref] writes:
quoted hunk
On Tue, Nov 03, 2020 at 10:32:37AM -0700, David Ahern wrote:
quoted
configure scripts usually allow you to control options directly,
overriding the autoprobe.
What do you think of the follow update? It's a little rough and only controls
libbpf.

$ git diff
diff --git a/configure b/configure
index 711bb69c..be35c024 100755
--- a/configure
+++ b/configure
@@ -442,6 +442,35 @@ endif
 EOF
 }

+usage()
+{
+       cat <<EOF
+Usage: $0 [OPTIONS]
+  -h | --help                  Show this usage info
+  --no-libbpf                  build the package without libbpf
+  --libbpf-dir=DIR             build the package with self defined libbpf dir
+EOF
+       exit $1
+}
This would be the only command line arg that configure takes; all other
options are passed via the environment. I think we should be consistent
here; and since converting the whole configure script is probably out of
scope for this patch, why not just use the existing FORCE_LIBBPF
variable?

I.e., FORCE_LIBBPF=on will fail if not libbpf is present,
FORCE_LIBBPF=off will disable libbpf entirely, and if the variable is
unset, libbpf will be used if found?

Alternatively, keep them as two separate variables (FORCE_LIBBPF and
DISABLE_LIBBPF?). I don't have any strong preference as to which of
those is best, but I think they'd both be more consistent with the
existing configure script logic...

-Toke

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
Daniel Borkmann [off-list ref] writes:
Back in the days when developing lib/bpf.c, it was explicitly done as
built-in for iproute2 so that it doesn't take years for users to
actually get to the point where they can realistically make use of it.
If we were to extend the internal lib/bpf.c to similar feature state
as libbpf today, how is that different in the bigger picture compared
to sync or submodule... so far noone complained about lib/bpf.c.
Except that this whole effort started because lib/bpf.c is slowly
bitrotting into oblivion? If all the tools are dynamically linked
against libbpf, that's only one package the distros have to keep
up-to-date instead of a whole list of tools. How does that make things
*worse*?

-Toke

Re: [PATCHv3 iproute2-next 1/5] configure: add check_libbpf() for later libbpf support

Message details and related content
On Wed, Nov 04, 2020 at 12:09:15PM +0100, Toke Høiland-Jørgensen wrote:
quoted
+usage()
+{
+       cat <<EOF
+Usage: $0 [OPTIONS]
+  -h | --help                  Show this usage info
+  --no-libbpf                  build the package without libbpf
+  --libbpf-dir=DIR             build the package with self defined libbpf dir
+EOF
+       exit $1
+}
This would be the only command line arg that configure takes; all other
options are passed via the environment. I think we should be consistent
here; and since converting the whole configure script is probably out of
scope for this patch, why not just use the existing FORCE_LIBBPF
variable?
Yes, converting the whole configure script should be split as another patch
work.
I.e., FORCE_LIBBPF=on will fail if not libbpf is present,
FORCE_LIBBPF=off will disable libbpf entirely, and if the variable is
unset, libbpf will be used if found?
I like this one, with only one variable. I will check how to re-organize the
script.
Alternatively, keep them as two separate variables (FORCE_LIBBPF and
DISABLE_LIBBPF?). I don't have any strong preference as to which of
those is best, but I think they'd both be more consistent with the
existing configure script logic...
Please tell me if others have any other ideas.

Thanks
Hnagbin

Re: [PATCHv3 iproute2-next 0/5] iproute2: add libbpf support

Message details and related content
On 11/4/20 12:20 PM, Toke Høiland-Jørgensen wrote:
Daniel Borkmann [off-list ref] writes:
quoted
Back in the days when developing lib/bpf.c, it was explicitly done as
built-in for iproute2 so that it doesn't take years for users to
actually get to the point where they can realistically make use of it.
If we were to extend the internal lib/bpf.c to similar feature state
as libbpf today, how is that different in the bigger picture compared
to sync or submodule... so far noone complained about lib/bpf.c.
Except that this whole effort started because lib/bpf.c is slowly
bitrotting into oblivion? If all the tools are dynamically linked
against libbpf, that's only one package the distros have to keep
up-to-date instead of a whole list of tools. How does that make things
*worse*?
It sounds good in theory if that would all work out as expected, but reality
differs unfortunately. Today on vast majority of distros you are able to use
iproute2's BPF loader via lib/bpf.c given it's a fixed built-in, even if
it's bitrotting for a while now in terms of features^BTF, but the base functionality
that is in there can be used, and it is used in the wild today. If libbpf is
dynamically linked to iproute2, then I - as a user - am left with continuing
to assume that the current lib/bpf.c is the /only/ base that is really /guaranteed/
to be available as a loader across distros, but iproute2 + libbpf may not be
(it may be the case for RHEL but potentially not others). So from user PoV
I might be sticking to the current lib/bpf.c that iproute2 ships instead of
converting code over until even major distros catch up in maybe 2 years from now
(that is in fact how long it took Canonical to get bpftool included, not kidding).
If we would have done lib/bpf.c as a dynamic library back then, we wouldn't be
where we are today since users might be able to start consuming BPF functionality
just now, don't you agree? This was an explicit design choice back then for exactly
this reason. If we extend lib/bpf.c or import libbpf one way or another then there
is consistency across distros and users would be able to consume it in a predictable
way starting from next major releases. And you could start making this assumption
on all major distros in say, 3 months from now. The discussion is somehow focused
on the PoV of /a/ distro which is all nice and good, but the ones consuming the
loader shipping software /across/ distros are users writing BPF progs, all I'm
trying to say is that the _user experience_ should be the focus of this discussion
and right now we're trying hard making it rather painful for them to consume it.

Cheers,
Daniel
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help