iproute2: Support loading XDP programs with libbpf

This switches over loading of XDP programs to the using libbpf, if it is
available. It uses the automatic pinning features added to libbpf to
construct the same pinning paths as the libelf-based loader.

Since map-in-map support has not yet been added to libbpf, this means that
map-in-map definitions will not work with this patch.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
diff --git a/lib/bpf.c b/lib/bpf.c
index c6e3bd0..de1a655 100644
--- a/lib/bpf.c
+++ b/lib/bpf.c
@@ -938,9 +938,17 @@
 	return ret;
 }
 
+#ifdef HAVE_LIBBPF
+static int bpf_do_load_libbpf(struct bpf_cfg_in *cfg);
+#endif
+
 static int bpf_do_load(struct bpf_cfg_in *cfg)
 {
 	if (cfg->mode == EBPF_OBJECT) {
+#ifdef HAVE_LIBBPF
+		if(cfg->type == BPF_PROG_TYPE_XDP)
+			return bpf_do_load_libbpf(cfg);
+#endif
 		cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
 					    cfg->section, cfg->ifindex,
 					    cfg->verbose);
@@ -1407,25 +1415,22 @@
 	}
 }
 
-static void bpf_make_pathname(char *pathname, size_t len, const char *name,
+static int bpf_make_pathname(char *pathname, size_t len, const char *name,
 			      const struct bpf_elf_ctx *ctx, uint32_t pinning)
 {
 	switch (pinning) {
 	case PIN_OBJECT_NS:
-		snprintf(pathname, len, "%s/%s/%s",
-			 bpf_get_work_dir(ctx->type),
-			 ctx->obj_uid, name);
-		break;
+		return snprintf(pathname, len, "%s/%s/%s",
+				bpf_get_work_dir(ctx->type),
+				ctx->obj_uid, name);
 	case PIN_GLOBAL_NS:
-		snprintf(pathname, len, "%s/%s/%s",
-			 bpf_get_work_dir(ctx->type),
-			 BPF_DIR_GLOBALS, name);
-		break;
+		return snprintf(pathname, len, "%s/%s/%s",
+				bpf_get_work_dir(ctx->type),
+				BPF_DIR_GLOBALS, name);
 	default:
-		snprintf(pathname, len, "%s/../%s/%s",
-			 bpf_get_work_dir(ctx->type),
-			 bpf_custom_pinning(ctx, pinning), name);
-		break;
+		return snprintf(pathname, len, "%s/../%s/%s",
+				bpf_get_work_dir(ctx->type),
+				bpf_custom_pinning(ctx, pinning), name);
 	}
 }
 
@@ -3160,3 +3165,87 @@
 	return ret;
 }
 #endif /* HAVE_ELF */
+
+#ifdef HAVE_LIBBPF
+static int bpf_gen_pin_name(void *priv, char *buf, int buf_len,
+			    const char *name, unsigned int pinning)
+{
+	struct bpf_elf_ctx *ctx = priv;
+	const char *tmp;
+	int ret = 0;
+
+	if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
+		return 0;
+
+	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 ret;
+
+	return bpf_make_pathname(buf, buf_len, name, ctx, pinning);
+}
+
+static int bpf_elf_ctx_init_stub(struct bpf_elf_ctx *ctx, const char *pathname,
+				 enum bpf_prog_type type, int verbose)
+{
+	uint8_t tmp[20];
+	int ret;
+
+	memset(ctx, 0, sizeof(*ctx));
+	ret = bpf_obj_hash(pathname, tmp, sizeof(tmp));
+	if (ret)
+		ctx->noafalg = true;
+	else
+		hexstring_n2a(tmp, sizeof(tmp), ctx->obj_uid,
+			      sizeof(ctx->obj_uid));
+
+	ctx->verbose = verbose;
+	ctx->type    = type;
+	bpf_hash_init(ctx, CONFDIR "/bpf_pinning");
+
+	return 0;
+}
+
+static int verbose_print(enum libbpf_print_level level, const char *format,
+			 va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+static int bpf_do_load_libbpf(struct bpf_cfg_in *cfg)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	struct bpf_program *prog;
+	struct bpf_object *obj;
+	int err, prog_fd = -1;
+
+	struct bpf_prog_load_attr attr = {
+		.file = cfg->object,
+		.prog_type = cfg->type,
+		.ifindex = cfg->ifindex,
+		.log_level = cfg->verbose,
+		.auto_pin_cb = bpf_gen_pin_name,
+		.auto_pin_ctx = ctx,
+	};
+
+	if (cfg->verbose)
+		libbpf_set_print(verbose_print);
+
+	bpf_elf_ctx_init_stub(ctx, cfg->object, cfg->type, cfg->verbose);
+
+	err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
+	if (err)
+		return err;
+
+	if (cfg->section) {
+		prog = bpf_object__find_program_by_title(obj,
+							 cfg->section);
+		prog_fd = bpf_program__fd(prog);
+	}
+
+	cfg->prog_fd = prog_fd;
+	return cfg->prog_fd;
+}
+#endif