[RFC PATCH 3/4] xdp: Support for kParser as bpf helper function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Aravind Kumar Buduri <aravind.buduri@xxxxxxxxx>

	This patch adds an XDP helper to parse packets
	using the kParser. The metadata produced by the
	kParser is returned in the buf pointer whose
	size is len. conf provides a key that
	identifies the kParser to invoke and conf_len
	is the length of the key.
	The sample xdp program is loaded into xdp hook
	calls xdp helper function to get the metadata.

	bpf helper function prototype for kParser.
	long bpf_xdp_kparser(struct xdp_buff *xdp_md,
			     void *conf, u32 conf_len,
			     void *buf, u32 len);
            xdp_md          - xdp buffer pointer
            conf            - kParser parser identifier
			      (hash key,name)
            conf_len        - size of conf in bytes
            buf             - metadata buffer to pass
			      to kparser
            len             - metadata size in bytes

	This helper parses a packet using the kParser.
	The metadata produced by the parser is returned
	in the buf pointer whose size is len. conf provides
	a key that identifies the parser to invoke and
	conf_len is the length of the key

	metadata is mapped to bpf map.So that user space bpf
        program or bpftool can read the map(metadata is
        displayed in BTF format).

        funchooks are callbacks for packet parsing functions
	of the kParser module and will be populated after
	the kParser module is loaded.

        bpf xdp helper function is defined for kernel
	parser(kParser).
        kParser is configured via userspace ip command.
        kParser data path API's as mentioned in kparser.h are
	invoked using registered callback hooks.
        xdp frame is passed on to kParser via the xdp helper function
        and metadata is populated in the user specified buffer.

        xdp user space component, which loads xdp kernel component
        into xdp hook and displays number of packets
        transmitted/received per second.

Signed-off-by: Aravind Kumar Buduri <aravind.buduri@xxxxxxxxx>
---
 include/uapi/linux/bpf.h       |  10 ++
 net/core/filter.c              |  97 +++++++++++++++++++
 samples/bpf/Makefile           |   3 +
 samples/bpf/metadata_def.h     |  21 ++++
 samples/bpf/xdp_kparser_kern.c |  94 ++++++++++++++++++
 samples/bpf/xdp_kparser_user.c | 171 +++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h |  10 ++
 7 files changed, 406 insertions(+)
 create mode 100644 samples/bpf/metadata_def.h
 create mode 100644 samples/bpf/xdp_kparser_kern.c
 create mode 100644 samples/bpf/xdp_kparser_user.c

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 17f61338f8f8..b88d0aa689a9 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5435,6 +5435,15 @@ union bpf_attr {
  *		**-E2BIG** if user-space has tried to publish a sample which is
  *		larger than the size of the ring buffer, or which cannot fit
  *		within a struct bpf_dynptr.
+ *
+ * long bpf_xdp_kparser(struct xdp_buff *xdp_md, void *conf, u32 conf_len, void *buf, u32 len)
+ *      Description
+ *              This helper is provided as an easy way to parse the metadata in
+ *              xdp buffer .The frame associated to *xdp_md*,configuration *conf*
+ *              config len of *conf_len* and metadata is stored
+ *              in *buf* of *len* bytes.
+ *      Return
+ *              0 on success, or a negative error in case of failure.
  */
 #define ___BPF_FUNC_MAPPER(FN, ctx...)			\
 	FN(unspec, 0, ##ctx)				\
@@ -5647,6 +5656,7 @@ union bpf_attr {
 	FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx)	\
 	FN(ktime_get_tai_ns, 208, ##ctx)		\
 	FN(user_ringbuf_drain, 209, ##ctx)		\
+	FN(xdp_kparser, 210, ##ctx)			\
 	/* */
 
 /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't
diff --git a/net/core/filter.c b/net/core/filter.c
index bb0136e7a8e4..98b884123814 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -81,6 +81,13 @@
 #include <net/xdp.h>
 #include <net/mptcp.h>
 
+#if IS_ENABLED(CONFIG_KPARSER)
+#include <linux/kparser.h>
+#include <net/kparser.h>
+#include <linux/rhashtable.h>
+#include <linux/ktime.h>
+#define KPARSER_DEBUG 1
+#endif
 static const struct bpf_func_proto *
 bpf_sk_base_func_proto(enum bpf_func_id func_id);
 
@@ -3977,6 +3984,92 @@ static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
 	.arg4_type	= ARG_CONST_SIZE,
 };
 
+#if IS_ENABLED(CONFIG_KPARSER)
+struct get_kparser_funchooks kparser_funchooks = {
+	.kparser_get_parser_hook = NULL,
+	.__kparser_parse_hook = NULL,
+	.kparser_put_parser_hook = NULL,
+};
+EXPORT_SYMBOL_GPL(kparser_funchooks);
+
+int kparser_xdp_parse(struct xdp_buff *xdp, void *conf, size_t conf_len,
+		      void *_metadata, size_t metadata_len)
+{
+	struct kparser_hkey *keyptr = (struct kparser_hkey *)conf;
+	ktime_t start_time, stop_time, elapsed_time;
+	struct kparser_hkey key;
+	const void *parser;
+	void *data;
+	int pktlen;
+	int rc = 0;
+
+	key.id = keyptr->id;
+	strcpy(key.name, keyptr->name);
+	pktlen = xdp_get_buff_len(xdp);
+	data = (void *)(long)xdp->data;
+	if (!kparser_funchooks.kparser_get_parser_hook) {
+		pr_err("\n kparser module not loaded\n");
+		return -EINVAL;
+	} else {
+		parser = kparser_funchooks.kparser_get_parser_hook(&key);
+		if (!parser) {
+			pr_err("kparser_get_parser() failed, key:{%s:%u}\n",
+			       key.name, key.id);
+			return -EINVAL;
+		}
+	}
+
+	if (!kparser_funchooks.__kparser_parse_hook) {
+		pr_err("\n kparser module not loaded\n");
+		return -EINVAL;
+	} else {
+#if KPARSER_DEBUG
+		start_time = ktime_get();
+#endif
+		rc = kparser_funchooks.__kparser_parse_hook(parser, data, pktlen,
+							    _metadata, metadata_len);
+#if KPARSER_DEBUG
+		stop_time = ktime_get();
+		elapsed_time = ktime_sub(stop_time, start_time);
+		pr_err("elapsedTime : %lld\n",  ktime_to_ns(elapsed_time));
+#endif
+		if (!kparser_funchooks.kparser_put_parser_hook) {
+			pr_err("\n kparser module not loaded\n");
+			return -EINVAL;
+		} else {
+			if (kparser_funchooks.kparser_put_parser_hook(parser) != true)
+				pr_err("kparser_put_parser() failed\n");
+		}
+#if KPARSER_DEBUG
+		pr_debug("%s:rc:{%d:%s}\n", __func__, rc, kparser_code_to_text(rc));
+#endif
+	}
+	return rc;
+}
+
+BPF_CALL_5(bpf_xdp_kparser, struct xdp_buff *, xdp, void *, conf,
+	   u32, conf_len, void *, buf, u32, len)
+{
+	int len1;
+	int err;
+
+	len1 = xdp_get_buff_len(xdp);
+	err = kparser_xdp_parse(xdp, conf, conf_len, buf, len);
+	return 0;
+}
+
+const struct bpf_func_proto bpf_xdp_kparser_proto = {
+	.func           = bpf_xdp_kparser,
+	.gpl_only       = false,
+	.ret_type       = RET_INTEGER,
+	.arg1_type      = ARG_PTR_TO_CTX,
+	.arg2_type      = ARG_PTR_TO_MEM | MEM_RDONLY,
+	.arg3_type      = ARG_CONST_SIZE,
+	.arg4_type      = ARG_PTR_TO_UNINIT_MEM,
+	.arg5_type      = ARG_CONST_SIZE,
+};
+#endif
+
 static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset)
 {
 	struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
@@ -7979,6 +8072,10 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 	case BPF_FUNC_tcp_raw_check_syncookie_ipv6:
 		return &bpf_tcp_raw_check_syncookie_ipv6_proto;
 #endif
+#endif
+#if IS_ENABLED(CONFIG_KPARSER)
+	case BPF_FUNC_xdp_kparser:
+		return &bpf_xdp_kparser_proto;
 #endif
 	default:
 		return bpf_sk_base_func_proto(func_id);
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 727da3c5879b..0777447b7c88 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -46,6 +46,7 @@ tprogs-y += syscall_tp
 tprogs-y += cpustat
 tprogs-y += xdp_adjust_tail
 tprogs-y += xdp_fwd
+tprogs-y += xdp_kparser
 tprogs-y += task_fd_query
 tprogs-y += xdp_sample_pkts
 tprogs-y += ibumad
@@ -106,6 +107,7 @@ xdp_rxq_info-objs := xdp_rxq_info_user.o
 syscall_tp-objs := syscall_tp_user.o
 cpustat-objs := cpustat_user.o
 xdp_adjust_tail-objs := xdp_adjust_tail_user.o
+xdp_kparser-objs := xdp_kparser_user.o
 xdp_fwd-objs := xdp_fwd_user.o
 task_fd_query-objs := task_fd_query_user.o $(TRACE_HELPERS)
 xdp_sample_pkts-objs := xdp_sample_pkts_user.o
@@ -168,6 +170,7 @@ always-y += syscall_tp_kern.o
 always-y += cpustat_kern.o
 always-y += xdp_adjust_tail_kern.o
 always-y += xdp_fwd_kern.o
+always-y += xdp_kparser_kern.o
 always-y += task_fd_query_kern.o
 always-y += xdp_sample_pkts_kern.o
 always-y += ibumad_kern.o
diff --git a/samples/bpf/metadata_def.h b/samples/bpf/metadata_def.h
new file mode 100644
index 000000000000..114849d04e12
--- /dev/null
+++ b/samples/bpf/metadata_def.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2022-23 Aravind Kumar Buduri <aravind.buduri@xxxxxxxxx>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+struct user_frame {
+	unsigned short ip_offset;
+	unsigned short l4_offset;
+	unsigned int ipv4_addrs[2];
+	unsigned short ports[2];
+} __packed;
+
+struct user_metadata {
+	struct user_frame frames;
+} __packed;
diff --git a/samples/bpf/xdp_kparser_kern.c b/samples/bpf/xdp_kparser_kern.c
new file mode 100644
index 000000000000..bc4146bc3a94
--- /dev/null
+++ b/samples/bpf/xdp_kparser_kern.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022-23 Aravind Kumar Buduri <aravind.buduri@xxxxxxxxx>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#define KBUILD_MODNAME "foo"
+#include <uapi/linux/bpf.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/kparser.h>
+#include <bpf/bpf_helpers.h>
+#include "metadata_def.h"
+#define IPV6_FLOWINFO_MASK              cpu_to_be32(0x0FFFFFFF)
+#define DEBUG 0
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, __u64);
+	__uint(max_entries, 1);
+} counter_map SEC(".maps");
+
+u64 *counter;
+u64 pkts;
+
+void count_pkts(void)
+{
+	u32 key = 0;
+
+	counter = bpf_map_lookup_elem(&counter_map, &key);
+	if (counter) {
+		*counter += 1;
+		pkts = *counter;
+	}
+}
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__uint(max_entries, 2);
+	__type(key, __u32);
+	__type(value, struct user_metadata);
+} ctx_map SEC(".maps");
+
+static __always_inline void xdp_update_ctx(const void *buffer, size_t len)
+{
+	const struct user_metadata *buf = buffer;
+	__u32 key = 1;
+
+	if (!buf || len < sizeof(*buf)) {
+		bpf_printk("Insufficient buffer error\n");
+		return;
+	}
+	bpf_map_update_elem(&ctx_map, &key, buf, BPF_ANY);
+}
+
+static struct user_metadata user_metadata_buffer;
+static struct kparser_hkey key;
+
+SEC("prog")
+int xdp_parser_prog(struct xdp_md *ctx)
+{
+	/* prepare a parser key which is already created and configured via the ip cli */
+	key.id = 0xffff;
+	strcpy(key.name, "tuple_parser");
+
+	/* set all bits to 1 in user metadata buffer to easily determine later which
+	 * fields were set/updated by kParser KMOD
+	 */
+	memset(&user_metadata_buffer, 0xff, sizeof(user_metadata_buffer));
+
+	bpf_xdp_kparser(ctx, &key, sizeof(key), &user_metadata_buffer,
+			sizeof(user_metadata_buffer));
+
+	/* now dump the metadata to be displayed by bpftool */
+	xdp_update_ctx(&user_metadata_buffer, sizeof(user_metadata_buffer));
+
+	/* count how many packets were processed in this interval */
+	count_pkts();
+
+	return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/xdp_kparser_user.c b/samples/bpf/xdp_kparser_user.c
new file mode 100644
index 000000000000..0ebcadce0431
--- /dev/null
+++ b/samples/bpf/xdp_kparser_user.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022-23 Aravind Kumar Buduri <aravind.buduri@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/bpf.h>
+#include <linux/if_link.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <net/if.h>
+#include <locale.h>
+#include "bpf_util.h"
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+static int ifindex;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static __u32 prog_id;
+
+static void poll_stats(int map_fd, int interval)
+{
+	unsigned int nr_cpus = bpf_num_possible_cpus();
+	__u64 values[nr_cpus], prev[UINT8_MAX] = { 0 };
+	int i;
+	__u32 key = UINT32_MAX;
+
+	while (1) {
+		sleep(interval);
+		if (bpf_map_get_next_key(map_fd, &key, &key) != -1) {
+			__u64 sum = 0;
+
+			assert(bpf_map_lookup_elem(map_fd, &key, values) == 0);
+			for (i = 0; i < nr_cpus; i++)
+				sum += values[i];
+			if (sum > prev[key])
+				printf(" packet rate =%10llu pkt/s\n",
+						(sum - prev[key]) / interval);
+			prev[key] = sum;
+		}
+	}
+}
+
+static void int_exit(int sig)
+{
+	__u32 curr_prog_id = 0;
+
+	if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) {
+		printf("bpf_xdp_query_id failed\n");
+		exit(1);
+	}
+	if (prog_id == curr_prog_id)
+		bpf_xdp_detach(ifindex, xdp_flags, NULL);
+	else if (!curr_prog_id)
+		printf("couldn't find a prog id on a given interface\n");
+	else
+		printf("program on interface changed, not removing\n");
+	exit(0);
+}
+
+static void usage(const char *prog)
+{
+	fprintf(stderr,
+		"usage: %s [OPTS] IFACE\n\n"
+		"OPTS:\n"
+		"    -S    use skb-mode\n"
+		"    -N    enforce native mode\n"
+		"    -F    force loading prog\n",
+		prog);
+}
+
+int main(int argc, char **argv)
+{
+	struct bpf_prog_info info = {};
+	__u32 info_len = sizeof(info);
+	const char *optstr = "FSN";
+	int prog_fd, map_fd, opt;
+	struct bpf_program *prog;
+	struct bpf_object *obj;
+	struct bpf_map *map;
+	char filename[256];
+	int err;
+
+	while ((opt = getopt(argc, argv, optstr)) != -1) {
+		switch (opt) {
+		case 'S':
+			xdp_flags |= XDP_FLAGS_SKB_MODE;
+			break;
+		case 'N':
+			/* default, set below */
+			break;
+		case 'F':
+			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+			break;
+		default:
+			usage(basename(argv[0]));
+			return 1;
+		}
+	}
+
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+		xdp_flags |= XDP_FLAGS_DRV_MODE;
+
+	if (optind == argc) {
+		usage(basename(argv[0]));
+		return 1;
+	}
+
+	ifindex = if_nametoindex(argv[optind]);
+	if (!ifindex) {
+		perror("if_nametoindex");
+		return 1;
+	}
+
+	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+	obj = bpf_object__open_file(filename, NULL);
+	if (libbpf_get_error(obj))
+		return 1;
+
+	prog = bpf_object__next_program(obj, NULL);
+	bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
+
+	err = bpf_object__load(obj);
+	if (err)
+		return 1;
+
+	prog_fd = bpf_program__fd(prog);
+
+	map = bpf_object__next_map(obj, NULL);
+	if (!map) {
+		printf("finding a map in obj file failed\n");
+		return 1;
+	}
+	map_fd = bpf_map__fd(map);
+
+	if (!prog_fd) {
+		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
+		return 1;
+	}
+
+	signal(SIGINT, int_exit);
+	signal(SIGTERM, int_exit);
+
+	if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) {
+		printf("link set xdp fd failed\n");
+		return 1;
+	}
+
+	err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+	if (err) {
+		printf("can't get prog info - %s\n", strerror(errno));
+		return err;
+	}
+	prog_id = info.id;
+	poll_stats(map_fd, 1);
+
+	return 0;
+}
+
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 17f61338f8f8..b88d0aa689a9 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5435,6 +5435,15 @@ union bpf_attr {
  *		**-E2BIG** if user-space has tried to publish a sample which is
  *		larger than the size of the ring buffer, or which cannot fit
  *		within a struct bpf_dynptr.
+ *
+ * long bpf_xdp_kparser(struct xdp_buff *xdp_md, void *conf, u32 conf_len, void *buf, u32 len)
+ *      Description
+ *              This helper is provided as an easy way to parse the metadata in
+ *              xdp buffer .The frame associated to *xdp_md*,configuration *conf*
+ *              config len of *conf_len* and metadata is stored
+ *              in *buf* of *len* bytes.
+ *      Return
+ *              0 on success, or a negative error in case of failure.
  */
 #define ___BPF_FUNC_MAPPER(FN, ctx...)			\
 	FN(unspec, 0, ##ctx)				\
@@ -5647,6 +5656,7 @@ union bpf_attr {
 	FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx)	\
 	FN(ktime_get_tai_ns, 208, ##ctx)		\
 	FN(user_ringbuf_drain, 209, ##ctx)		\
+	FN(xdp_kparser, 210, ##ctx)			\
 	/* */
 
 /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't
-- 
2.34.1




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux