bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH net-next]net: Add new kParser KMOD in net, integrate kParser with XDP
@ 2022-10-26  7:50 Pratyush Kumar Khan
  2022-10-26  7:50 ` [RFC PATCH 3/4] xdp: Support for kParser as bpf helper function Pratyush Kumar Khan
  2022-10-26  7:50 ` [RFC PATCH 4/4] xdp: Support for flow_dissector " Pratyush Kumar Khan
  0 siblings, 2 replies; 3+ messages in thread
From: Pratyush Kumar Khan @ 2022-10-26  7:50 UTC (permalink / raw)
  To: netdev, bpf; +Cc: tom, abuduri, chethan, Pratyush Kumar Khan

kParser, or the "Kernel Parser" is a highly programmable, high
performance protocol parser in the Linux network stack. An instance
of kParser is programmed by "ip parser ..." commands in iproute2.

The kParser is dynamically programmable and script-able. For instance,
to add new protocol to parse in an existing instance of kparser, CLI
commands are executed -- there is no need to write user code or perform
a recompile of the parser.

A parser is programmed through the "ip parser" CLI, and common netlink
interfaces are used to instantiate a parser in the kernel. A parser is
defined by a parse graph which is implemented by kparser as a set of
parse node definitions and protocol tables that describe the linkages
between the nodes. Per its program, a kparser instance will report
metadata about the packet and various protocols layers of the packet.
Metadata is any information about the packet including header offsets
and value of fields of interest to the programmer; the later is
analogous to the flow information extracted by flow dissector (the
primary difference being that kParser can extract arbitrary protocol
fields and report on fields in multiple layers of encapsulation).

kParser is called in the kernel by the kparser_parse and
__kparser_parse functions. The output returned is a set metadata about
the parsed packet. Metadata is any information that the parser is
programmed to report about a packet (e.g. offsets of various headers,
extracted IP addresses and ports, etc.). A pointer to a metadata buffer
is input to kparser_parse, kparser will fill in the metadata as per
the programming of the parser. Note that the structure of the metadata
buffer is determined by the programmer, when the buffer is returned
the caller can cast the buffer to the data structure for that parser.

The iproute2 CLI for the kParser works in tandem with the KMOD kParser
to configure any number of parser instances. If kParser KMOD is not
statically compiled with Linux kernel, it needs to be
additionally enabled, compiled and loaded to use the iproute2 CLI for
kParser. Please note that the kParser CLI is scriptable meaning the
parser configuration in the kernel can by dynamically updated without
need to recompile any code or reload kernel module.

Building blocks of kParser are various objects from different
namespaces/object types. For example, parser, node, table etc. are all
different types of objects, also known as namespaces. All the namespaces
are described in the next section.

Each object is identified by a maximum 128 bytes long '\0' terminated
(128 bytes including the '\0' character) human readable ASCII name (only
character '/' is not allowed in the name, and names can not start with
'-'). Alternatively an unsigned 16 bit ID or both ID and name can be
used to identify objects.
NOTE: During CLI create operations of these objects, it is must to
specify either the name or ID. Both can also be specified.
Whichever is not specified during create will be auto generated by the
KMOD kParser and CLI will convey the identifiers to user for later use.
User should save these identifiers.
NOTE: During CLI create operations, unique name or ID must always be
specified. Those name/ID can later be used to identify the associated
object in further CLI operations.

Various objects are:
* parser:
	A parser represents a parse tree. It defines the user
	metadata and metametadata structure size, number of parsing node
	and encapsulation limits, root node for the parse tree, success
	and failure case exit nodes.

* node:
	A node (a.k.a parse node) represents a specific protocol header.
	Defining protocol handler involves multiple work, i.e.configure
	the parser about the associated protocol's packet header, e.g.
	minimum header length, where to look for the next protocol field
	in the packet, etc.
	Along with that, it also defines the rules/handlers to parse and
	store the required metadata by associating a metalist.
	The table to find the next protocol node is attached to node.
	node can be 3 types:
		* PLAIN:
			PLAIN nodes are the basic protocol headers.
		* TLVS:
			TLVS nodes are the Type-Length-Value protocol
			headers, such as TCP.
			They also binds a tlvtable to a node.
		* FLAGFIELDS:
			FLAGFIELDS are indexed flag and associated flag
			fields protocol headers, such as GRE headers.
			It also binds a flagstable with a node.

* table:
	A table is a protocol table, which associated a protocol
	number with a node. e.g. Ethernet protocol type 0x8000 in
	network order means the next node after Ethernet header is IPv4.

	NOTE: table has key, key must be unique. Usually this key is
	protocol number, such as Ethernet type, or IPv4 protocol number
	etc.

* metadata-rule:
	Defines the metadata structures that will be passed to
	the kParser datapath parser API by the user. This basically
	defines a specific metadata extraction rule. This must match
	with the user passed metadata structure in the datapath API.

* metadata-ruleset:
	A list of metadata(s) to associate it with packet
	parsing action handlers, i.e. parse node.

* tlvnode:
	A tlvnode defines a specific TLV parsing rule, e.g. to
	parse TCP option MSS, a new tlvnode needs to be defined.
	Each tlvnode can also associate a metalist with the TLV parsing
	rule, i.e. tlvnode

* tlvtable:
	This is a table of multiple tlvnode(s) where the key are
	types of TLVs (e.g. tlvnode defined for TCP MSS should have the
	type/kind value set to 2.

* flags:
	It describes parsing rules to extract certain protocol's flags
	in bitfields, such as flag value, mask and size.

* flagfields:
	It defines flagfields in packet associated with flags in
	bitfields of the same packet.
	e.g. GRE flagfields such as checksum, key, sequence number etc.

* flagstable:
	This defines a table of flagfields and associate them
	with their respective flag values via their indexes. Here the
	keys are usually indexes, because in typical flag based protocol
	header, such as GRE, the flagfields appear in protocol packet in
	the same order as the set flag bits. The flag is defined by the
	flag value, mask, size and associated metalist.

* condexprs:
	"Conditional expressions" used to define and configure
	various complex conditional expressions in kParser.
	They are used to validate certain conditions for
	protocol packet field values.

* condexprslist:
	"List of Conditional expressions" used to create
	more complex and composite expressions involving more than one
	conditional expression(s).

* condexprstable:
	"A table of Conditional expressions" used to
	associate one or more than one list of Conditional expressions
	with a packet parsing action handlers, i.e. parse node.

* counter:
	It is used to create and configure counter objects which can
	be used for a wide range of usages such as count how many VLAN
	headers were parsed, how many TCP options are encountered etc.

* countertable:
	kParser has a global table of counters, which supports various
	and unique counter configurations upto seven entries. Multiple
	kParser parser instances can share this countertable.

Global header file include/net/kparser.h exports the kParser datapath
KMOD APIs. The APIs are:

/* kparser_parse(): Function to parse a skb using a parser instance key.
 *
 * skb: input packet skb
 * kparser_key: key of the associated kParser parser object which must
 *     be already created via CLI.
 * _metadata: User provided metadata buffer. It must be same as
 *
 * configured metadata objects in CLI.
 * metadata_len: Total length of the user provided metadata buffer.
 *
 * return: kParser error code as defined in include/uapi/linux/kparser.h
 */
        int kparser_parse(struct sk_buff *skb,
                          const struct kparser_hkey *kparser_key,
                          void *_metadata, size_t metadata_len);

/* __kparser_parse(): Function to parse a void * packet buffer using a
 * parser instance key.
 *
 * parser: Non NULL kparser_get_parser() returned and cached opaque
 *     pointer referencing a valid parser instance.
 * _hdr: input packet buffer
 * parse_len: length of input packet buffer
 * _metadata: User provided metadata buffer. It must be same as
 *     configured metadata objects in CLI.
 * metadata_len: Total length of the user provided metadata buffer.
 *
 * return: kParser error code as defined in include/uapi/linux/kparser.h
 */
        int __kparser_parse(const void *parser, void *_hdr,
                            size_t parse_len, void *_metadata,
                            size_t metadata_len);

/* kparser_get_parser(): Function to get an opaque reference of a parser
 * instance and mark it immutable so that while actively using, it can
 * not be deleted. The parser is identified by a key. It marks  the
 * associated parser and whole parse tree immutable so that when it is
 * locked, it can not be deleted.
 *
 * kparser_key: key of the associated kParser parser object which must
 *     be already created via CLI.
 *
 * return: NULL if key not found, else an opaque parser instance pointer
 *     which can be used in the following APIs 3 and 4.
 *
 * NOTE: This call makes the whole parser tree immutable. If caller
 * calls this more than once, later caller will need to release the same
 * parser exactly that many times using the API kparser_put_parser().
 */
        const void *kparser_get_parser(const struct kparser_hkey
                                       *kparser_key);

/* kparser_put_parser(): Function to return and undo the read only
 * operation done previously by kparser_get_parser(). The parser
 * instance is identified by using a previously obtained opaque parser
 * pointer via API kparser_get_parser(). This undo the immutable
 * change so that any component of the whole parse tree can be deleted
 * again.
 *
 * parser: void *, Non NULL opaque pointer which was previously returned
 *     by kparser_get_parser(). Caller can use cached opaque pointer as
 *     long as system does not restart and kparser.ko is not reloaded.
 *
 * return: boolean, true if put operation is success, else false.
 *
 * NOTE: This call makes the whole parser tree deletable for the very
 * last call.
 */
        bool kparser_put_parser(const void *parser);

Now we can refer to an example kParser configuration which can parse
simple IPv4 five tuples, i.e. IPv4 header offset, offset of IPv4
addresses, IPv4 protocol number, L4 header offset (i.e. TCP/UDP) and
L4 port numbers. The sample ip commands are:

ip parser create md-rule name md.iphdr_offset type offset md-off 0

ip parser create md-rule name md.ipaddrs src-hdr-off 12 length 8 \
	md-off 4

ip parser create md-rule name md.l4_hdr.offset type offset md-off 2

ip parser create md-rule name md.ports src-hdr-off 0 length 4 \
	md-off 12 isendianneeded true

ip parser create node name node.ports hdr.minlen 4 \
	md-rule md.l4_hdr.offset md-rule md.ports

ip parser create node name node.ipv4 hdr.minlen 20 \
	hdr.len-field-off 0 hdr.len-field-len 1 \
	hdr.len-field-mask 0x0f hdr.len-field-multiplier 4 \
	nxt.field-off 9 nxt.field-len 1 \
	nxt.table-ent 6:node.ports nxt.table-ent 17:node.ports \
	md-rule md.iphdr_offset \
	md-rule md.ipaddrs

ip parser create node name node.ether hdr.minlen 14 nxt.offset 12 \
	nxt.length 2 nxt.table-ent 0x800:node.ipv4

ip parser create parser name tuple_parser rootnode node.ether \
	base-metametadata-size 14

This sample parser will parse Ethernet/IPv4 to UDP and TCP, report the
offsets of the innermost IP and TCP or UDP header, extract IPv4
addresses and UDP or TCP ports (into a frame).

About the XDP kParser integration changes:

xdp: Support for kParser as bpf helper function

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 include/net/kparser.h
       are called using registered callback hooks.    
       xdp frame buffer 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 . when xdp packet is received by the xdp prog
       in kernel calls bpf helper function to pass kparser configuration
       and buffer to collect the metadata mapped to bpf map.
       so that bpftool can display the metadata in BTF format.
       
       kParser user space component displays number of packets
       transmitted/received per second.
           
       Following are the steps to test/run the kParser with xdp,

- load the kParser module
- configure the kparser via  ip command
 
The ip command can be used to load the xdp kernel
component of xdp kParser.
ip link set dev <interface-name> xdp obj  \
xdp_kparser_kern.o verbose
                
  below is the command to run kParser on network interface
 ./xdp_kparser -S <interface-name>
         
xdp: Support for flow dissector  as bpf helper function

xdp program which is loaded into the xdp hook calls the xdp
helper function to get the metadata.

bpf xdp helper function is defined for flow dissector.
    xdp frame is passed on to Flow dissector call and with
    keys(either basic keys or big parser keys) .
    bpf helper function calls __skb_flow_dissector() with xdp
    buffer, keys and user specified buffer for metadata.
    xdp frame is passed via 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.

below is the command to run flow dissector on network
interface
 ./xdp_flow_dissector -S <interface-name>
 
The ip command can be used to load the xdp kernel
component of flow_dissector.
ip link set dev <interface-name> xdp obj     \
xdp_flow_dissector_kern.o verbose

---

Aravind Kumar Buduri (2):
  xdp: Support for kParser as bpf helper function
  xdp: Support for flow_dissector as bpf helper function

Pratyush (1):
  kParser: Add new kParser KMOD

Pratyush Kumar Khan (1):
  docs: networking: add doc entry for kParser

 Documentation/networking/kParser.rst          |  302 ++
 .../networking/parse_graph_example.svg        | 2039 ++++++++++
 include/net/kparser.h                         |   90 +
 include/uapi/linux/bpf.h                      |   20 +
 include/uapi/linux/kparser.h                  |  678 +++
 net/Kconfig                                   |    9 +
 net/Makefile                                  |    1 +
 net/core/filter.c                             |  244 ++
 net/kparser/Makefile                          |   10 +
 net/kparser/kparser.h                         |  391 ++
 net/kparser/kparser_cmds.c                    |  898 ++++
 net/kparser/kparser_cmds_dump_ops.c           |  532 +++
 net/kparser/kparser_cmds_ops.c                | 3621 +++++++++++++++++
 net/kparser/kparser_condexpr.h                |   52 +
 net/kparser/kparser_datapath.c                | 1094 +++++
 net/kparser/kparser_main.c                    |  325 ++
 net/kparser/kparser_metaextract.h             |  896 ++++
 net/kparser/kparser_types.h                   |  586 +++
 samples/bpf/Makefile                          |    6 +
 samples/bpf/metadata_def.h                    |   21 +
 samples/bpf/xdp_flow_dissector_kern.c         |   91 +
 samples/bpf/xdp_flow_dissector_user.c         |  170 +
 samples/bpf/xdp_kparser_kern.c                |   94 +
 samples/bpf/xdp_kparser_user.c                |  171 +
 tools/include/uapi/linux/bpf.h                |   20 +
 25 files changed, 12361 insertions(+)
 create mode 100644 Documentation/networking/kParser.rst
 create mode 100644 Documentation/networking/parse_graph_example.svg
 create mode 100644 include/net/kparser.h
 create mode 100644 include/uapi/linux/kparser.h
 create mode 100644 net/kparser/Makefile
 create mode 100644 net/kparser/kparser.h
 create mode 100644 net/kparser/kparser_cmds.c
 create mode 100644 net/kparser/kparser_cmds_dump_ops.c
 create mode 100644 net/kparser/kparser_cmds_ops.c
 create mode 100644 net/kparser/kparser_condexpr.h
 create mode 100644 net/kparser/kparser_datapath.c
 create mode 100644 net/kparser/kparser_main.c
 create mode 100644 net/kparser/kparser_metaextract.h
 create mode 100644 net/kparser/kparser_types.h
 create mode 100644 samples/bpf/metadata_def.h
 create mode 100644 samples/bpf/xdp_flow_dissector_kern.c
 create mode 100644 samples/bpf/xdp_flow_dissector_user.c
 create mode 100644 samples/bpf/xdp_kparser_kern.c
 create mode 100644 samples/bpf/xdp_kparser_user.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [RFC PATCH 3/4] xdp: Support for kParser as bpf helper function
  2022-10-26  7:50 [RFC PATCH net-next]net: Add new kParser KMOD in net, integrate kParser with XDP Pratyush Kumar Khan
@ 2022-10-26  7:50 ` Pratyush Kumar Khan
  2022-10-26  7:50 ` [RFC PATCH 4/4] xdp: Support for flow_dissector " Pratyush Kumar Khan
  1 sibling, 0 replies; 3+ messages in thread
From: Pratyush Kumar Khan @ 2022-10-26  7:50 UTC (permalink / raw)
  To: netdev, bpf; +Cc: tom, abuduri, chethan, Aravind Kumar Buduri

From: Aravind Kumar Buduri <aravind.buduri@gmail.com>

	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@gmail.com>
---
 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@gmail.com>
+ * 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@gmail.com>
+ * 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@gmail.com>
+ *
+ * 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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [RFC PATCH 4/4] xdp: Support for flow_dissector as bpf helper function
  2022-10-26  7:50 [RFC PATCH net-next]net: Add new kParser KMOD in net, integrate kParser with XDP Pratyush Kumar Khan
  2022-10-26  7:50 ` [RFC PATCH 3/4] xdp: Support for kParser as bpf helper function Pratyush Kumar Khan
@ 2022-10-26  7:50 ` Pratyush Kumar Khan
  1 sibling, 0 replies; 3+ messages in thread
From: Pratyush Kumar Khan @ 2022-10-26  7:50 UTC (permalink / raw)
  To: netdev, bpf; +Cc: tom, abuduri, chethan, Aravind Kumar Buduri

From: Aravind Kumar Buduri <aravind.buduri@gmail.com>

        xdp program which is loaded into the xdp hook
	calls the xdp helper function to get the metadata.

        bpf helper function prototype for flow dissector:
        long bpf_xdp_flow_dissector(struct xdp_buff *xdp_md,
                                    u32 flowd_sel,
				    void *buf, u32 len);
        xdp_md          - xdp buffer pointer
        flowd_sel       - key selection (either basic
			  keys(0) or big parser keys(1))
	buf             - received metadata buffer
        len             - size of the metadata buffer

        This helper parses a packet using the Flow dissector
	API.
        The metadata produced by the Flow dissecor is returned
	in the buf pointer whose size is len. The parameter
	flowd_sel will specify which type of keys(either basic
	keys or big parser keys) used for parsing.
   
        Below are the steps implemented,
        bpf xdp helper function is defined for the flow
	dissector.xdp frame is passed on to Flow dissector
	call and with keys(either basic keys or big parser
	keys).bpf helper function calls __skb_flow_dissector()
	with xdp buffer,keys and user specified buffer
	for metadata.
        xdp frame is passed via 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@gmail.com>
---
 include/uapi/linux/bpf.h              |  10 ++
 net/core/filter.c                     | 147 ++++++++++++++++++++++
 samples/bpf/Makefile                  |   3 +
 samples/bpf/xdp_flow_dissector_kern.c |  91 ++++++++++++++
 samples/bpf/xdp_flow_dissector_user.c | 170 ++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h        |  10 ++
 6 files changed, 431 insertions(+)
 create mode 100644 samples/bpf/xdp_flow_dissector_kern.c
 create mode 100644 samples/bpf/xdp_flow_dissector_user.c

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b88d0aa689a9..24f48268268c 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5444,6 +5444,15 @@ union bpf_attr {
  *              in *buf* of *len* bytes.
  *      Return
  *              0 on success, or a negative error in case of failure.
+ *
+ * long bpf_xdp_flow_dissector(struct xdp_buff *xdp_md, u32 flowd_sel, void *buf, u32 len)
+ *      Description
+ *              This helper is provided as an easy way to parse the metadata
+ *              and test the functionality. The frame associated to *xdp_md*,
+ *              choosing flowd *flowd_sel* 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)				\
@@ -5657,6 +5666,7 @@ union bpf_attr {
 	FN(ktime_get_tai_ns, 208, ##ctx)		\
 	FN(user_ringbuf_drain, 209, ##ctx)		\
 	FN(xdp_kparser, 210, ##ctx)			\
+	FN(xdp_flow_dissector, 211, ##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 98b884123814..ffd290914de8 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3984,6 +3984,149 @@ static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
 	.arg4_type	= ARG_CONST_SIZE,
 };
 
+#if IS_ENABLED(CONFIG_KPARSER)
+static int xdp_flow_dissector(struct xdp_buff *xdp, u32 flowd_sel, void *buf, u32 len)
+{
+	void *data = xdp->data;
+	struct ethhdr *eth = (struct ethhdr *)data;
+	ktime_t start_time, stop_time, elapsed_time;
+	struct iphdr *ip = data + sizeof(*eth);
+	struct flow_keys_basic keys;
+	struct vlan_hdr *vlan_hdr;
+	unsigned short  proto;
+	struct flow_keys flow;
+	struct flow_keys *flowptr = &flow;
+	struct ipv6hdr *ip6h;
+	struct arphdr  *arph;
+	int nh_off;
+	int hlen;
+	bool ret;
+
+	proto =  eth->h_proto;
+
+	if (!buf || flowd_sel > 0xffff || len > 0xffff) {
+		pr_err("\n%s %d len =\n ", __func__, __LINE__);
+		return -EINVAL;
+	}
+	if (flowd_sel == 0) {
+		if (len < sizeof(struct flow_keys_basic)) {
+			pr_err("\n%s %d len = %d size flow_keys_basic= %d len error\n",
+			       __func__, __LINE__, sizeof(struct flow_keys_basic));
+			return -EINVAL;
+		}
+	} else if (flowd_sel == 1) {
+		if (len < sizeof(struct flow_keys)) {
+			pr_err("\n%s %d len = %d size flow_keys= %d len error\n",
+			       __func__, __LINE__, sizeof(struct flow_keys));
+			return -EINVAL;
+		}
+	}
+	nh_off = sizeof(*eth);
+#if KPARSER_DEBUG
+	pr_debug("\n%s nh_off = %d hlen= %d\n",	__func__, nh_off, hlen);
+	pr_debug("%s proto = %d htons(ETH_P_IP)= %d\n",
+		 __func__, proto, htons(ETH_P_IP));
+	pr_debug("%s eth->h_proto = %d ETH_P_IP= %d\n",
+		 __func__, eth->h_proto, ETH_P_IP);
+#endif
+	/* Extract L3 protocol */
+	switch (proto) {
+	case htons(ETH_P_8021Q):
+		hlen = sizeof(*eth) + sizeof(*vlan_hdr);
+		break;
+	case htons(ETH_P_IP):
+		hlen = sizeof(*eth) + sizeof(*ip);
+		break;
+	case htons(ETH_P_IPV6):
+		hlen = sizeof(*eth) + sizeof(*ip6h);
+		break;
+	case htons(ETH_P_ARP):
+		hlen = sizeof(*eth) + sizeof(*arph);
+		break;
+	default:
+		break;
+	}
+
+	if (flowd_sel == 0) {
+		memset(&keys, 0, sizeof(keys));
+		start_time = ktime_get();
+		ret = __skb_flow_dissect(NULL, NULL, &flow_keys_basic_dissector,
+					 &keys, data, proto, nh_off, hlen, 0);
+		stop_time = ktime_get();
+		elapsed_time = ktime_sub(stop_time, start_time);
+		pr_err("elapsed Time : %lld\n",  ktime_to_ns(elapsed_time));
+		if (ret == true) {
+#if KPARSER_DEBUG
+			pr_debug("\n%d %s keys.control.thoff= %d\n",
+				 __LINE__, __func__, keys.control.thoff);
+			pr_debug("%d %s keys.control.addr_type= %d\n",
+				 __LINE__, __func__, keys.control.addr_type);
+			pr_debug("%d %s keys.control.flags= %d\n",
+				 __LINE__, __func__, keys.control.flags);
+			pr_debug("%d %s keys.basic.n_proto= 0x0x\n",
+				 __LINE__, __func__, ntohs(keys.basic.n_proto));
+			pr_debug("%d %s keys.basic.ip_proto= %d\n",
+				 __LINE__, __func__, keys.basic.ip_proto);
+#endif
+		}
+		memcpy((char *)buf, &keys, sizeof(keys));
+		return 0;
+	} else if (flowd_sel == 1) {
+		memset(&flow, 0, sizeof(flow));
+#if KPARSER_DEBUG
+		start_time = ktime_get();
+#endif
+		ret = __skb_flow_dissect(NULL, NULL, &flow_keys_dissector,
+					 &flow, data, proto, nh_off, hlen, 0);
+#if KPARSER_DEBUG
+		stop_time = ktime_get();
+		elapsed_time = ktime_sub(stop_time, start_time);
+		pr_debug("elapsed Time : %lld\n",  ktime_to_ns(elapsed_time));
+#endif
+		if (ret == true) {
+#if KPARSER_DEBUG
+			pr_debug("%d %s control.thoff= %d\n",
+				 __LINE__, __func__, flowptr->control.thoff);
+			pr_debug("%d %s keys.control.addr_type= %d\n",
+				 __LINE__, __func__, flowptr->control.addr_type);
+			pr_debug("%d %s keys.control.flags= %d\n",
+				 __LINE__, __func__, flowptr->control.flags);
+			pr_debug("%d %s flowptr->basic.n_proto= 0x0%x\n",
+				 __LINE__, __func__, ntohs(flowptr->basic.n_proto));
+			pr_debug("%d %s flowptr->basic.ip_proto= %d\n",
+				 __LINE__, __func__, flowptr->basic.ip_proto);
+			pr_debug("%d %s flowptr->addrs.v4addrs.src = %pI4\n",
+				 __LINE__, __func__, &flowptr->addrs.v4addrs.src);
+			pr_debug("%d %s flowptr->addrs.v4addrs.dst = %pI4\n",
+				 __LINE__, __func__, &flowptr->addrs.v4addrs.dst);
+#endif
+		}
+		memcpy((char *)buf, &flow, sizeof(flow));
+		return 0;
+	}
+	return 0;
+}
+
+BPF_CALL_4(bpf_xdp_flow_dissector, struct xdp_buff *, xdp, u32, flowd_sel,
+	   void *, buf, u32, len)
+{
+	int ret;
+
+	ret = xdp_flow_dissector(xdp, flowd_sel, buf, len);
+	return ret;
+}
+
+const struct bpf_func_proto bpf_xdp_flow_dissector_proto = {
+	.func           = bpf_xdp_flow_dissector,
+	.gpl_only       = false,
+	.ret_type       = RET_INTEGER,
+	.arg1_type      = ARG_PTR_TO_CTX,
+	.arg2_type      = ARG_ANYTHING,
+	.arg3_type      = ARG_PTR_TO_UNINIT_MEM,
+	.arg4_type      = ARG_CONST_SIZE,
+};
+#endif
+
 #if IS_ENABLED(CONFIG_KPARSER)
 struct get_kparser_funchooks kparser_funchooks = {
 	.kparser_get_parser_hook = NULL,
@@ -8076,6 +8219,10 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 #if IS_ENABLED(CONFIG_KPARSER)
 	case BPF_FUNC_xdp_kparser:
 		return &bpf_xdp_kparser_proto;
+#endif
+#if IS_ENABLED(CONFIG_KPARSER)
+	case BPF_FUNC_xdp_flow_dissector:
+		return &bpf_xdp_flow_dissector_proto;
 #endif
 	default:
 		return bpf_sk_base_func_proto(func_id);
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 0777447b7c88..108e29d097dc 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -47,6 +47,7 @@ tprogs-y += cpustat
 tprogs-y += xdp_adjust_tail
 tprogs-y += xdp_fwd
 tprogs-y += xdp_kparser
+tprogs-y += xdp_flow_dissector
 tprogs-y += task_fd_query
 tprogs-y += xdp_sample_pkts
 tprogs-y += ibumad
@@ -108,6 +109,7 @@ 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_flow_dissector-objs := xdp_flow_dissector_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
@@ -171,6 +173,7 @@ 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 += xdp_flow_dissector_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/xdp_flow_dissector_kern.c b/samples/bpf/xdp_flow_dissector_kern.c
new file mode 100644
index 000000000000..778e65c3b039
--- /dev/null
+++ b/samples/bpf/xdp_flow_dissector_kern.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022-23 Aravind Kumar Buduri <aravind.buduri@gmail.com>
+ * 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 <bpf/bpf_helpers.h>
+#include <net/flow_dissector.h>
+
+#define FLOWD_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;
+	}
+}
+
+void dump_flowd_user_buf(void *buf, int len)
+{
+	struct flow_keys *flowptr = (struct flow_keys *)buf;
+
+	if (!buf || len < sizeof(*flowptr)) {
+		bpf_printk(" Insufficient buffer\n");
+		return;
+	}
+#if FLOWD_DEBUG
+	bpf_printk("pkts received = %lu\n ", pkts);
+	bpf_printk("control.thoff= %d\n ",
+			flowptr->control.thoff);
+	bpf_printk("keys.control.addr_type= %d\n ",
+			flowptr->control.addr_type);
+	bpf_printk("keys.control.flags= %d\n ",
+			flowptr->control.flags);
+	bpf_printk("flowptr->basic.n_proto= 0x0%x\n ",
+			ntohs(flowptr->basic.n_proto));
+	bpf_printk("flowptr->basic.ip_proto= %d\n ",
+			flowptr->basic.ip_proto);
+	bpf_printk("flowptr->addrs.v4addrs.src = %pi4\n ",
+			&flowptr->addrs.v4addrs.src);
+	bpf_printk("flowptr->addrs.v4addrs.dst = %pi4\n ",
+			&flowptr->addrs.v4addrs.dst);
+#endif
+
+}
+
+char arr1[512] = {0};
+
+SEC("prog")
+int xdp_flowd_prog(struct xdp_md *ctx)
+{
+	/*
+	 * code for flow dissector
+	 * 2nd parameter differenciate flow dissector selection
+	 * 0 - basic key flow dissector
+	 * 1 - big key flow dissector
+	 */
+	bpf_xdp_flow_dissector(ctx, 1, arr1, 512);
+	count_pkts();
+	return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/xdp_flow_dissector_user.c b/samples/bpf/xdp_flow_dissector_user.c
new file mode 100644
index 000000000000..7fc1be3f6f14
--- /dev/null
+++ b/samples/bpf/xdp_flow_dissector_user.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022-23 Aravind Kumar Buduri <aravind.buduri@gmail.com>
+ *
+ * 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 b88d0aa689a9..24f48268268c 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5444,6 +5444,15 @@ union bpf_attr {
  *              in *buf* of *len* bytes.
  *      Return
  *              0 on success, or a negative error in case of failure.
+ *
+ * long bpf_xdp_flow_dissector(struct xdp_buff *xdp_md, u32 flowd_sel, void *buf, u32 len)
+ *      Description
+ *              This helper is provided as an easy way to parse the metadata
+ *              and test the functionality. The frame associated to *xdp_md*,
+ *              choosing flowd *flowd_sel* 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)				\
@@ -5657,6 +5666,7 @@ union bpf_attr {
 	FN(ktime_get_tai_ns, 208, ##ctx)		\
 	FN(user_ringbuf_drain, 209, ##ctx)		\
 	FN(xdp_kparser, 210, ##ctx)			\
+	FN(xdp_flow_dissector, 211, ##ctx)		\
 	/* */
 
 /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-10-26  7:51 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-26  7:50 [RFC PATCH net-next]net: Add new kParser KMOD in net, integrate kParser with XDP Pratyush Kumar Khan
2022-10-26  7:50 ` [RFC PATCH 3/4] xdp: Support for kParser as bpf helper function Pratyush Kumar Khan
2022-10-26  7:50 ` [RFC PATCH 4/4] xdp: Support for flow_dissector " Pratyush Kumar Khan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).