All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device
@ 2015-02-18 19:25 Simon Horman
  2015-02-18 19:25 ` [PATCH/RFC 1/3] net: flow: export net_flow_{put_rule,get_{field,action}} Simon Horman
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Simon Horman @ 2015-02-18 19:25 UTC (permalink / raw)
  To: netdev; +Cc: Simon Horman

*** Not for Upstream Merge
*** For informational purposes only

As discussed at netconf we have been working on hairpinning Flow API
messages back to user-space as a mechanism for exercising that API.

And as promised at netconf I am releasing our code.

What this can do:
* Allow the implementation of the NDO's proposed by John Fastabend's API
  to be implemented in user-space. This is done using netlink messages.

What this cannot do:
* Anything else

Limitations:
* Both the design and the implementation are slow

I have also written user-space code. There are two portions:

1. flow-table

   This may be used to send and receive messages from the Flow API.
   It a command-line utility which may be used to exercise the flow API.
   And a library to help achieve this. An interesting portion
   of the library is a small framework for converting between
   netlink and JSON.

   It is available here: https://github.com/horms/flow-table
   The licence is GPLv2

   It overlaps to some extent with user-space code by John Fastabend.
   I was not aware of that work which he was doing concurrently.

2. flow-table-hairpin

   This is a daemon that listens for messages hairpined back
   to user-space and responds accordingly. That is, the user-space
   backing of the NDOs of the Flow API.

   It includes a simple flow table backend (ftbe) abstraction
   and a dummy implementation that stores flows in a local list
   ** and does nothing else with them ***

   It is available here: https://github.com/horms/flow-table-hairpin
   The licence is GPLv2


Usage example:

# Create unft netdev
ip link add type unft

# Start haripind. The tables, headers, etc... are provided as JSON
flow-table-hairpind \
        --tables tables.json \
        --headers headers.json \
        --actions actions.json \
        --header-graph header-graph.json \
        --table-graph table-graph.json &

# Get the tables of unft using the Flow API
flow-table-ctl get-tables unft0


Base:

These patches are based on v2 of the Flow API.
"[net-next PATCH v2 00/12] Flow API"
http://www.spinics.net/lists/netdev/msg311961.html


Simon Horman (3):
  net: flow: export net_flow_{put_rule,get_{field,action}}
  net: flow: Introduce flow table hairpin API
  net: unft: Add Userspace hairpin network flow table device

 drivers/net/Kconfig                  |    9 +
 drivers/net/Makefile                 |    1 +
 drivers/net/unft.c                   | 1520 ++++++++++++++++++++++++++++++++++
 include/linux/if_flow.h              |    6 +
 include/linux/if_flow_hairpin.h      |    6 +
 include/uapi/linux/if_flow_hairpin.h |  159 ++++
 net/core/flow_table.c                |   10 +-
 7 files changed, 1707 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/unft.c
 create mode 100644 include/linux/if_flow_hairpin.h
 create mode 100644 include/uapi/linux/if_flow_hairpin.h

-- 
2.1.4

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

* [PATCH/RFC 1/3] net: flow: export net_flow_{put_rule,get_{field,action}}
  2015-02-18 19:25 [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
@ 2015-02-18 19:25 ` Simon Horman
  2015-02-18 19:25 ` [PATCH/RFC 2/3] net: flow: Introduce flow table hairpin API Simon Horman
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Simon Horman @ 2015-02-18 19:25 UTC (permalink / raw)
  To: netdev; +Cc: Simon Horman

*** Not for Upstream Merge
*** For informational purposes only

This is to allow these functions to be used by a new driver, unft,
which will be proposed in a subsequent patch.

Signed-off-by: Simon Horman <simon.horman@netronome.com>
---
 include/linux/if_flow.h |  6 ++++++
 net/core/flow_table.c   | 10 ++++++----
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/include/linux/if_flow.h b/include/linux/if_flow.h
index dc70e3e..eb19b6c8 100644
--- a/include/linux/if_flow.h
+++ b/include/linux/if_flow.h
@@ -225,4 +225,10 @@ net_flow_destroy_cache(struct net_flow_tbl *table)
 	return;
 }
 #endif /* CONFIG_NET_FLOW_TABLES */
+
+struct nlattr;
+
+int net_flow_put_rule(struct sk_buff *skb, struct net_flow_rule *rule);
+int net_flow_get_field(struct net_flow_field_ref *field, struct nlattr *nla);
+int net_flow_get_action(struct net_flow_action *action, struct nlattr *nla);
 #endif /* _IF_FLOW_H_ */
diff --git a/net/core/flow_table.c b/net/core/flow_table.c
index a938929..8c26019 100644
--- a/net/core/flow_table.c
+++ b/net/core/flow_table.c
@@ -975,7 +975,7 @@ done:
 	return 0;
 }
 
-static int net_flow_put_rule(struct sk_buff *skb, struct net_flow_rule *rule)
+int net_flow_put_rule(struct sk_buff *skb, struct net_flow_rule *rule)
 {
 	struct nlattr *flows, *actions, *matches;
 	int j, i = 0;
@@ -1039,6 +1039,7 @@ flows_put_failure:
 put_failure:
 	return err;
 }
+EXPORT_SYMBOL(net_flow_put_rule);
 
 static int net_flow_get_rule_cache(struct sk_buff *skb,
 				   struct net_flow_tbl *table,
@@ -1233,8 +1234,7 @@ const struct nla_policy net_flow_field_policy[NFL_FIELD_REF_MAX + 1] = {
 				      .len = sizeof(u64)},
 };
 
-static int net_flow_get_field(struct net_flow_field_ref *field,
-			      struct nlattr *nla)
+int net_flow_get_field(struct net_flow_field_ref *field, struct nlattr *nla)
 {
 	struct nlattr *ref[NFL_FIELD_REF_MAX+1];
 	int err;
@@ -1332,6 +1332,7 @@ static int net_flow_get_field(struct net_flow_field_ref *field,
 
 	return err;
 }
+EXPORT_SYMBOL(net_flow_get_field);
 
 static void net_flow_free_actions(struct net_flow_action *actions)
 {
@@ -1420,7 +1421,7 @@ static int net_flow_get_actarg(struct net_flow_action_arg *arg,
 	return 0;
 }
 
-static int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
+int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
 {
 	struct nlattr *act[NFL_ACTION_ATTR_MAX+1];
 	struct nlattr *args;
@@ -1470,6 +1471,7 @@ static int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
 	}
 	return 0;
 }
+EXPORT_SYMBOL(net_flow_get_action);
 
 static const
 struct nla_policy net_flow_rule_policy[NFL_ATTR_MAX + 1] = {
-- 
2.1.4

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

* [PATCH/RFC 2/3] net: flow: Introduce flow table hairpin API
  2015-02-18 19:25 [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
  2015-02-18 19:25 ` [PATCH/RFC 1/3] net: flow: export net_flow_{put_rule,get_{field,action}} Simon Horman
@ 2015-02-18 19:25 ` Simon Horman
  2015-02-18 19:25 ` [PATCH/RFC 3/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
  2015-02-20 21:29 ` [PATCH/RFC 0/3] " David Miller
  3 siblings, 0 replies; 5+ messages in thread
From: Simon Horman @ 2015-02-18 19:25 UTC (permalink / raw)
  To: netdev; +Cc: Simon Horman

*** Not for Upstream Merge
*** For informational purposes only

This introduces an netlink API to allow the Flow API to be hairpined
back to user-space thus allowing the NDOs of the Flow API to be backed there.

This will be used by a follow-up patch.

Signed-off-by: Simon Horman <simon.horman@netronome.com>
---
 include/linux/if_flow_hairpin.h      |   6 ++
 include/uapi/linux/if_flow_hairpin.h | 159 +++++++++++++++++++++++++++++++++++
 2 files changed, 165 insertions(+)
 create mode 100644 include/linux/if_flow_hairpin.h
 create mode 100644 include/uapi/linux/if_flow_hairpin.h

diff --git a/include/linux/if_flow_hairpin.h b/include/linux/if_flow_hairpin.h
new file mode 100644
index 0000000..d958f8d
--- /dev/null
+++ b/include/linux/if_flow_hairpin.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_IF_FLOW_HAIRPIN_H
+#define _LINUX_IF_FLOW_HAIRPIN_H
+
+#include <uapi/linux/if_flow_hairpin.h>
+
+#endif
diff --git a/include/uapi/linux/if_flow_hairpin.h b/include/uapi/linux/if_flow_hairpin.h
new file mode 100644
index 0000000..eb4cbd8
--- /dev/null
+++ b/include/uapi/linux/if_flow_hairpin.h
@@ -0,0 +1,159 @@
+/*
+ * include/uapi/linux/if_flow_hairpin.h -
+ * Hairpin to allow the messages of the Flow table interface for
+ * Swtich devices to be forwarded to user-space
+ * Copyright (c) 2014 Simon Horman <simon.horman@netronome.com>
+ *
+ * Based on: Flow table interface for Switch devices
+ * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: Simon Horman <simon.horman@netronome.com>
+ */
+
+/* Netlink description:
+ *
+ * The NFL_* attributes contained in NFLH_ENCAP_ATTR
+ * should also include their nested attributes, as described in
+ * include/uapi/linux/if_flow.h.
+
+ * Set Listener <Request>,
+ * Get Listener <Request> and
+ * Get Listener <Reply> description.
+ *
+ * Set Listener registers netlink port ids to receive flow deletion
+ * notifications if the NFLH_LISTENER_ATTR_PIDS attribute is
+ * present. Otherwise it unregisters port ids if they were previously
+ * registered by a Set Listener with the
+ * NFLH_LISTENER_ATTR_PIDS attribute present.
+ *
+ * Get Listener reports the port ids if they were previously registered by
+ * a Set Listener with the NFLH_LISTENER_ATTR_PIDS.  If no ids
+ * are registered then the NFLH_LISTENER_ATTR_PIDS attribute of
+ * the reply should be omitted.
+ *
+ * The NFLH_LISTENER_ATTR_PIDS attribute is an array of u32
+ * values. If the attribute is present then it must contain at least one
+ * element. The implementation may choose to ignore some elements.
+ * Currently the implementation ignores all elements other than the first
+ * one.
+ *
+ *
+ * [NFLH_LISTENER]
+ *   [NFLH_LISTENER_ATTR_TYPE]
+ *   [NFLH_LISTENER_ATTR_PIDS]
+ *
+ * Message used to encapsulate both a NFL message forwardeded from
+ * the kernel to user-space as a notification, and the reply of processing
+ * that message sent from user-space to the kernel. In the case of the
+ * former NFLH_ENCAP_STATUS whould be omitted. In the case
+ * of the latter it should be included and its value should be 0 on success
+ * otherwise a negative error code.
+ *
+ * [NFLH_ENCAP]
+ *   [NFLH_ENCAP_CMD_TYPE]
+ *   [NFLH_ENCAP_CMD]
+ *   [NFLH_ENCAP_SEQ]
+ *   [NFLH_ENCAP_STATUS]
+ *   [NFLH_ENCAP_ATTR]
+ *     [NFL_TABLES]
+ *     [NFL_HEADERS]
+ *     [NFL_ACTIONS]
+ *     [NFL_HEADER_GRAPH]
+ *     [NFL_TABLE_GRAPH]
+ *     [NFL_FLOWS]
+ *     [NFL_FLOWS_ERROR]
+ */
+
+#ifndef _UAPI_LINUX_IF_FLOW_HAIRPIN
+#define _UAPI_LINUX_IF_FLOW_HAIRPIN
+
+#include <linux/types.h>
+
+/**
+ * @struct net_flow_hairpin_encap_header
+ * @brief defines the header of an encapsulated message
+ *
+ * @cmd_type: type of inner command
+ * @cmd: identifier of inner command
+ * @status: status of command execution
+ * @seq: sequence number of request
+ */
+struct net_flow_hairpin_encap_header {
+	__u32 cmd_type;
+	__u32 cmd;
+	__u64 seq;
+	__s32 status;
+};
+
+enum {
+	NFLH_LISTENER_ATTR_TYPE_ENCAP,
+};
+
+enum {
+	NFLH_LISTENER_ATTR_UNSPEC,
+	NFLH_LISTENER_ATTR_TYPE,
+	NFLH_LISTENER_ATTR_PIDS,
+	__NFLH_LISTENER_ATTR_MAX,
+};
+#define NFLH_LISTENER_ATTR_MAX (__NFLH_LISTENER_ATTR_MAX - 1)
+
+enum {
+	/* A Net Flow Table Command is used */
+	NFLH_ENCAP_CMD_NFL_CMD,
+};
+
+enum {
+	/* A Net Flow Table Command is used */
+	NFLH_ENCAP_STATUS_OK,
+	NFLH_ENCAP_STATUS_EINVAL,
+	NFLH_ENCAP_STATUS_EOPNOTSUPP,
+};
+
+enum {
+	NFLH_ENCAP_UNSPEC,
+	NFLH_ENCAP_CMD_TYPE,
+	NFLH_ENCAP_CMD,
+	NFLH_ENCAP_SEQ,
+	NFLH_ENCAP_STATUS,
+	NFLH_ENCAP_ATTR,
+	__NFLH_ENCAP_MAX,
+};
+#define NFLH_ENCAP_MAX (__NFLH_ENCAP_MAX - 1)
+
+enum {
+	NFLH_UNSPEC,
+
+	NFLH_ENCAP,
+	NFLH_LISTENER,
+
+	__NFLH_MAX,
+};
+#define NFLH_MAX (__NFLH_MAX - 1)
+
+enum {
+	/* Userspace commands. */
+	NFLH_CMD_SET_LISTENER,
+	NFLH_CMD_GET_LISTENER,
+
+	/* Both userspace commands and Kernel-to-user notifications. */
+	NFLH_CMD_ENCAP,
+
+	__NFLH_CMD_MAX,
+	NFLH_CMD_MAX = (__NFLH_CMD_MAX - 1),
+};
+
+#define NFLH_GENL_NAME "net_flow_hp"
+#define NFLH_GENL_VERSION 0x1
+#endif /* _UAPI_LINUX_IF_FLOW_HAIRPIN */
-- 
2.1.4

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

* [PATCH/RFC 3/3] net: unft: Add Userspace hairpin network flow table device
  2015-02-18 19:25 [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
  2015-02-18 19:25 ` [PATCH/RFC 1/3] net: flow: export net_flow_{put_rule,get_{field,action}} Simon Horman
  2015-02-18 19:25 ` [PATCH/RFC 2/3] net: flow: Introduce flow table hairpin API Simon Horman
@ 2015-02-18 19:25 ` Simon Horman
  2015-02-20 21:29 ` [PATCH/RFC 0/3] " David Miller
  3 siblings, 0 replies; 5+ messages in thread
From: Simon Horman @ 2015-02-18 19:25 UTC (permalink / raw)
  To: netdev; +Cc: Simon Horman

*** Not for Upstream Merge
*** For informational purposes only

Allows the implementation of the NDO's proposed by John Fastabend's API
to be implemented in user-space. This is done using netlink messages.

Limitations:
* Both the design and the implementation are slow

I have also written user-space code. There are two portions:

1. flow-table

   This may be used to send and receive messages from the Flow API.
   It a command-line utility which may be used to exercise the flow API.
   And a library to help achieve this. An interesting portion
   of the library is a small framework for converting between
   netlink and JSON.

   It is available here: https://github.com/horms/flow-table
   The licence is GPLv2

   It overlaps to some extent with user-space code by John Fastabend.
   I was not aware of that work which he was doing concurrently.

2. flow-table-hairpin

   This is a daemon that listens for messages hairpined back
   to user-space and responds accordingly. That is, the user-space
   backing of the NDOs of the Flow API.

   It includes a simple flow table backend (ftbe) abstraction
   and a dummy implementation that stores flows in a local list
   ** and does nothing else with them ***

   It is available here: https://github.com/horms/flow-table-hairpin
   The licence is GPLv2

Simple usage example:

ip link add type unft

flow-table-hairpind \
        --tables tables.json \
        --headers headers.json \
        --actions actions.json \
        --header-graph header-graph.json \
        --table-graph table-graph.json &

flow-table-ctl get-tables unft0

Signed-off-by: Simon Horman <simon.horman@netronome.com>
---
 drivers/net/Kconfig  |    9 +
 drivers/net/Makefile |    1 +
 drivers/net/unft.c   | 1520 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1530 insertions(+)
 create mode 100644 drivers/net/unft.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index d6607ee..9a4ddb1 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -268,6 +268,15 @@ config NLMON
 	  diagnostics, etc. This is mostly intended for developers or support
 	  to debug netlink issues. If unsure, say N.
 
+config UNFT
+	tristate "User-Space hairpin network flow table device"
+	depends on NET_FLOW_TABLES
+	---help---
+	  This option enables a hairpin network flow table device. The
+	  purpose of this is to reflect network flow table API calls,
+	  made via netlink messages, to user-space to allow prototyping
+	  of implementations there. If unsure, say N.
+
 endif # NET_CORE
 
 config SUNGEM_PHY
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e25fdd7..88ca294 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
 obj-$(CONFIG_VXLAN) += vxlan.o
 obj-$(CONFIG_NLMON) += nlmon.o
+obj-$(CONFIG_UNFT) += unft.o
 
 #
 # Networking Drivers
diff --git a/drivers/net/unft.c b/drivers/net/unft.c
new file mode 100644
index 0000000..483dc8d
--- /dev/null
+++ b/drivers/net/unft.c
@@ -0,0 +1,1520 @@
+/* Based on nlmon.c by Daniel Borkmann, Mathieu Geli et al. */
+/* Based on flow_table.c by John Fastabend */
+/*
+ * include/uapi/linux/if_flow_hairpin.h -
+ * Hairpin to allow the messages of the Flow table interface for
+ * Swtich devices to be forwarded to user-space
+ * Copyright (c) 2014 Simon Horman <simon.horman@netronome.com>
+ *
+ * Based on: flow_table.c
+ * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
+ *
+ * Based on nlmon.c by Daniel Borkmann, Mathieu Geli et al.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: Simon Horman <simon.horman@netronome.com>
+ */
+
+#include <linux/if_arp.h>
+#include <linux/if_flow_common.h>
+#include <linux/if_flow_hairpin.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/genetlink.h>
+#include <net/sock.h>
+#include <net/rtnetlink.h>
+
+static struct genl_family net_flow_hairpin_nl_family = {
+	.id = GENL_ID_GENERATE,
+	.name = NFLH_GENL_NAME,
+	.version = NFLH_GENL_VERSION,
+	.maxattr = NFLH_MAX,
+	.netnsok = true,
+};
+
+/* Protected by genl_lock */
+static u32 net_flow_hairpin_listener_pid;
+static bool net_flow_hairpin_listener_set;
+static struct net_flow_tbl **unft_table_list;
+static struct net_flow_hdr **unft_header_list;
+static struct net_flow_action **unft_action_list;
+static struct net_flow_hdr_node **unft_header_nodes;
+static struct net_flow_tbl_node **unft_table_nodes;
+
+#ifdef CONFIG_NET_NS
+/* Protected by genl_lock */
+static struct net *net_flow_hairpin_listener_net;
+#endif
+
+/* In flight encap request details.
+ * Protected by genl_lock.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(unft_msg_wq);
+static int unft_msg_state;
+static int unft_msg_status;
+
+enum {
+	UNFT_MSG_S_NONE,
+	UNFT_MSG_S_REQUEST,
+	UNFT_MSG_S_REPLY,
+};
+
+/* This is 64-bits to allow plenty of space
+ * for example to partition the sequence number space on a per-CPU basis.
+ */
+static u64 unft_msg_seq;
+
+static int unft_flow_encap_request(struct net_device *dev, u32 cmd,
+				   int (*cb)(struct sk_buff *msg, void *priv),
+				   void *priv)
+{
+	int err = -ENOBUFS;
+	struct genl_info info = {
+		.dst_sk = read_pnet(&net_flow_hairpin_listener_net)->genl_sock,
+		.snd_portid = net_flow_hairpin_listener_pid,
+	};
+	struct genlmsghdr *hdr;
+	struct nlattr *encap, *encap_attr;
+	struct sk_buff *msg;
+
+	/* At this time only one message is allowed at a time */
+	if (unft_msg_state != UNFT_MSG_S_NONE)
+		return -EBUSY;
+
+	msg = genlmsg_new_unicast(NLMSG_DEFAULT_SIZE, &info, GFP_KERNEL);
+	if (!msg)
+		return -ENOBUFS;
+
+	hdr = genlmsg_put(msg, 0, 0, &net_flow_hairpin_nl_family, 0,
+			  NFLH_CMD_ENCAP);
+	if (!hdr)
+		goto err_msg;
+
+	encap = nla_nest_start(msg, NFLH_ENCAP);
+	if (!encap) {
+		err = -EMSGSIZE;
+		goto err_msg;
+	}
+
+	unft_msg_state = UNFT_MSG_S_REQUEST;
+	unft_msg_seq++;
+
+	if (nla_put_u32(msg, NFLH_ENCAP_CMD_TYPE,
+			NFLH_ENCAP_CMD_NFL_CMD) ||
+	    nla_put_u32(msg, NFLH_ENCAP_CMD, cmd) ||
+	    nla_put_u64(msg, NFLH_ENCAP_SEQ, unft_msg_seq)) {
+		err = -ENOBUFS;
+		goto err_encap;
+	}
+
+	encap_attr = nla_nest_start(msg, NFLH_ENCAP_ATTR);
+	if (!encap) {
+		err = -EMSGSIZE;
+		goto err_encap;
+	}
+
+	if (nla_put_u32(msg, NFL_IDENTIFIER_TYPE,
+			NFL_IDENTIFIER_IFINDEX) ||
+	    nla_put_u32(msg, NFL_IDENTIFIER, dev->ifindex)) {
+		err = -ENOBUFS;
+		goto err_encap_attr;
+	}
+
+	if (cb) {
+		err = cb(msg, priv);
+		if (err)
+			goto err_encap_attr;
+	}
+
+	nla_nest_end(msg, encap_attr);
+	nla_nest_end(msg, encap);
+
+	err = genlmsg_end(msg, hdr);
+	if (err < 0)
+		goto err_msg;
+
+	err = genlmsg_unicast(read_pnet(&net_flow_hairpin_listener_net),
+			      msg, net_flow_hairpin_listener_pid);
+	if (err)
+		return err;
+
+	genl_unlock();
+	err = wait_event_interruptible_timeout(unft_msg_wq,
+					       unft_msg_state == UNFT_MSG_S_REPLY,
+					       msecs_to_jiffies(5000));
+	genl_lock();
+	if (err < 0)
+		goto out;
+	if (unft_msg_state != UNFT_MSG_S_REPLY) {
+		err = -ETIMEDOUT;
+		goto out;
+	}
+
+	err = unft_msg_status;
+	goto out;
+
+err_encap_attr:
+	nla_nest_cancel(msg, encap_attr);
+err_encap:
+	nla_nest_cancel(msg, encap);
+err_msg:
+	nlmsg_free(msg);
+out:
+	unft_msg_state = UNFT_MSG_S_NONE;
+
+	return err;
+}
+
+static int unft_set_del_rule_cb(struct sk_buff *msg, void *priv)
+{
+	int err;
+	struct net_flow_rule *rule = priv;
+	struct nlattr *start;
+
+	start = nla_nest_start(msg, NFL_FLOWS);
+	if (!start)
+		return -EMSGSIZE;
+
+	err = net_flow_put_rule(msg, rule);
+	if (err) {
+		nla_nest_cancel(msg, start);
+		return -ENOBUFS;
+	}
+
+	nla_nest_end(msg, start);
+
+	return 0;
+}
+
+static int unft_flow_table_set_rule(struct net_device *dev,
+				    struct net_flow_rule *rule)
+{
+	return unft_flow_encap_request(dev, NFL_TABLE_CMD_SET_FLOWS,
+				       unft_set_del_rule_cb, rule);
+}
+
+static int unft_flow_table_del_rule(struct net_device *dev,
+				     struct net_flow_rule *rule)
+{
+	return unft_flow_encap_request(dev, NFL_TABLE_CMD_DEL_FLOWS,
+				       unft_set_del_rule_cb, rule);
+}
+
+static const
+struct nla_policy net_flow_hairpin_listener_policy[NFLH_LISTENER_ATTR_MAX + 1] = {
+	[NFLH_LISTENER_ATTR_TYPE] = { .type = NLA_U32,},
+	[NFLH_LISTENER_ATTR_PIDS] = { .type = NLA_U32,},
+};
+
+static int net_flow_table_hairpin_cmd_set_listener(struct sk_buff *skb,
+						   struct genl_info *info)
+{
+	int err;
+	struct nlattr *tb[NFLH_LISTENER_ATTR_MAX + 1];
+	u32 pid, type;
+
+	if (!info->attrs[NFLH_LISTENER])
+		return -EINVAL;
+
+	err = nla_parse_nested(tb, NFLH_LISTENER_ATTR_MAX,
+			       info->attrs[NFLH_LISTENER],
+			       net_flow_hairpin_listener_policy);
+	if (err)
+		return err;
+
+	if (!tb[NFLH_LISTENER_ATTR_TYPE] ||
+	    !tb[NFLH_LISTENER_ATTR_PIDS])
+		return -EINVAL;
+	type = nla_get_u32(tb[NFLH_LISTENER_ATTR_TYPE]);
+	if (type != NFLH_LISTENER_ATTR_TYPE_ENCAP)
+		return -EOPNOTSUPP;
+
+	if (tb[NFLH_LISTENER_ATTR_PIDS]) {
+		/* Only the first pid is used at this time */
+		pid = nla_get_u32(tb[NFLH_LISTENER_ATTR_PIDS]);
+		net_flow_hairpin_listener_pid = pid;
+		write_pnet(&net_flow_hairpin_listener_net,
+			   hold_net(sock_net(skb->sk)));
+		net_flow_hairpin_listener_set = true;
+	} else {
+		net_flow_hairpin_listener_set = false;
+	}
+
+	return 0;
+}
+
+static int net_flow_table_hairpin_cmd_get_listener(struct sk_buff *skb,
+						   struct genl_info *info)
+{
+	int err;
+	struct genlmsghdr *hdr;
+	struct nlattr *start;
+	struct nlattr *tb[NFLH_LISTENER_ATTR_MAX + 1];
+	struct sk_buff *msg = NULL;
+	u32 type;
+
+	if (!info->attrs[NFLH_LISTENER]) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	err = nla_parse_nested(tb, NFLH_LISTENER_ATTR_MAX,
+			       info->attrs[NFLH_LISTENER],
+			       net_flow_hairpin_listener_policy);
+	if (err)
+		goto err;
+
+	if (!tb[NFLH_LISTENER_ATTR_TYPE]) {
+		err = -EINVAL;
+		goto err;
+	}
+	type = nla_get_u32(tb[NFLH_LISTENER_ATTR_TYPE]);
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg) {
+		err = -ENOBUFS;
+		goto err;
+	}
+
+	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+			  &net_flow_hairpin_nl_family, 0,
+			  NFLH_CMD_GET_LISTENER);
+	if (!hdr) {
+		err = -ENOBUFS;
+		goto err;
+	}
+
+	start = nla_nest_start(msg, NFLH_LISTENER);
+	if (!start)
+		return -EMSGSIZE;
+
+	if (nla_put_u32(msg, NFLH_LISTENER_ATTR_TYPE,
+			NFLH_LISTENER_ATTR_TYPE_ENCAP))
+		return -ENOBUFS;
+
+	if (net_flow_hairpin_listener_set &&
+	    nla_put_u32(msg, NFLH_LISTENER_ATTR_PIDS,
+			net_flow_hairpin_listener_pid))
+		return -ENOBUFS;
+
+	nla_nest_end(msg, start);
+
+	err = genlmsg_end(msg, hdr);
+	if (err < 0)
+		goto err;
+
+	return genlmsg_reply(msg, info);
+
+err:
+	nlmsg_free(msg);
+	return err;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static struct net_flow_field_ref *
+unft_encap_get_field_refs(struct nlattr *attr)
+{
+	int count, err, rem;
+	struct net_flow_field_ref *refs;
+	struct nlattr *a;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_FIELD_REF)
+			count++;
+
+	refs = kcalloc(count + 1, sizeof *refs, GFP_KERNEL);
+	if (!refs)
+		return ERR_PTR(-ENOMEM);
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		if (nla_type(a) != NFL_FIELD_REF)
+			continue;
+		err = net_flow_get_field(&refs[count++], a);
+		if (err) {
+			kfree(refs);
+			return ERR_PTR(err);
+		}
+	}
+
+	return refs;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int *
+unft_encap_get_action_descs(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+	int *actions;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_ACTION_ATTR_UID)
+			count++;
+
+	actions = kcalloc(count + 1, sizeof *actions, GFP_KERNEL);
+	if (!actions)
+		return ERR_PTR(-ENOMEM);
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		u32 x;
+
+		if (nla_type(a) != NFL_ACTION_ATTR_UID)
+			continue;
+		x = nla_get_u32(a);
+		if (!x || x > INT_MAX) {
+			kfree(actions);
+			return ERR_PTR(-EINVAL);
+		}
+		actions[count] = x;
+	}
+
+	return actions;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static char *unft_encap_get_name(struct nlattr *attr)
+{
+	int max;
+	char *name;
+
+	max = nla_len(attr);
+	if (max > NFL_MAX_NAME)
+		max = NFL_MAX_NAME;
+	name = kzalloc(max, GFP_KERNEL);
+	if (!name)
+		return NULL;
+	nla_strlcpy(name, attr, max);
+
+	return name;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static const
+struct nla_policy flow_table_table_attr_policy[NFL_TABLE_ATTR_MAX + 1] =
+{
+	[NFL_TABLE_ATTR_NAME]		= { .type = NLA_STRING },
+	[NFL_TABLE_ATTR_UID]		= { .type = NLA_U32 },
+	[NFL_TABLE_ATTR_SOURCE]		= { .type = NLA_U32 },
+	[NFL_TABLE_ATTR_APPLY]		= { .type = NLA_U32 },
+	[NFL_TABLE_ATTR_SIZE]		= { .type = NLA_U32 },
+	[NFL_TABLE_ATTR_MATCHES]	= { .type = NLA_NESTED },
+	[NFL_TABLE_ATTR_ACTIONS]	= { .type = NLA_NESTED },
+};
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_table(struct net_flow_tbl *table)
+{
+	kfree(table->name);
+	kfree(table->matches);
+	kfree(table->actions);
+	kfree(table);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure */
+struct net_flow_tbl *unft_encap_get_table(struct nlattr *attr)
+{
+	int err = -EINVAL;
+	struct net_flow_tbl *table;
+	struct nlattr *attrs[NFL_TABLE_ATTR_MAX + 1];
+
+	table = kzalloc(sizeof *table, GFP_KERNEL);
+	if (!table)
+		return ERR_PTR(-ENOMEM);
+
+	err = nla_parse_nested(attrs, NFL_TABLE_ATTR_MAX,
+			       attr, flow_table_table_attr_policy);
+	if (err)
+		goto err;
+
+	if (!attrs[NFL_TABLE_ATTR_NAME] || !attrs[NFL_TABLE_ATTR_UID] ||
+	    !attrs[NFL_TABLE_ATTR_SOURCE] || !attrs[NFL_TABLE_ATTR_APPLY] ||
+	    !attrs[NFL_TABLE_ATTR_SIZE] || !attrs[NFL_TABLE_ATTR_UID] ||
+	    !attrs[NFL_TABLE_ATTR_MATCHES] || !attrs[NFL_TABLE_ATTR_ACTIONS])
+		goto err;
+
+	table->name = unft_encap_get_name(attrs[NFL_TABLE_ATTR_NAME]);
+	if (!table->name) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	table->uid = nla_get_u32(attrs[NFL_TABLE_ATTR_UID]);
+	table->source = nla_get_u32(attrs[NFL_TABLE_ATTR_SOURCE]);
+	table->apply_action = nla_get_u32(attrs[NFL_TABLE_ATTR_APPLY]);
+	table->size = nla_get_u32(attrs[NFL_TABLE_ATTR_SIZE]);
+
+	table->matches = unft_encap_get_field_refs(attrs[NFL_TABLE_ATTR_MATCHES]);
+	if (IS_ERR(table->matches)) {
+		err = PTR_ERR(table->matches);
+		goto err;
+	}
+
+	table->actions = unft_encap_get_action_descs(attrs[NFL_TABLE_ATTR_ACTIONS]);
+	if (IS_ERR(table->actions)) {
+		err = PTR_ERR(table->actions);
+		goto err;
+	}
+
+	return table;
+err:
+	unft_encap_free_table(table);
+	return ERR_PTR(err);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_tables(struct net_flow_tbl **tables)
+{
+	int i;
+
+	if (!tables)
+		return;
+
+	for (i = 0; !IS_ERR_OR_NULL(tables[i]); i++)
+		unft_encap_free_table(tables[i]);
+
+	kfree(tables);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int unft_encap_get_tables(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+
+	if (!attr || unft_table_list)
+		return -EINVAL;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_TABLE)
+			count++;
+
+	unft_table_list = kcalloc(count + 1, sizeof *unft_table_list,
+				  GFP_KERNEL);
+	if (!unft_table_list)
+		return -ENOMEM;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		if (nla_type(a) != NFL_TABLE)
+			continue;
+
+		unft_table_list[count] = unft_encap_get_table(a);
+		if (IS_ERR(unft_table_list[count])) {
+			int err = PTR_ERR(unft_table_list[count]);
+
+			unft_encap_free_tables(unft_table_list);
+			unft_table_list = NULL;
+			return err;
+		}
+
+		count++;
+	}
+
+	return 0;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static const
+struct nla_policy flow_table_field_attr_policy[NFL_FIELD_ATTR_MAX + 1] =
+{
+	[NFL_FIELD_ATTR_NAME]		= { .type = NLA_STRING },
+	[NFL_FIELD_ATTR_UID]		= { .type = NLA_U32 },
+	[NFL_FIELD_ATTR_BITWIDTH]	= { .type = NLA_U32 },
+};
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure */
+int unft_encap_get_field(struct nlattr *attr, struct net_flow_field *field)
+{
+	int err;
+	struct nlattr *attrs[NFL_FIELD_ATTR_MAX + 1];
+
+	err = nla_parse_nested(attrs, NFL_FIELD_ATTR_MAX, attr,
+			       flow_table_field_attr_policy);
+	if (err)
+		return err;
+
+	if (!attrs[NFL_FIELD_ATTR_NAME] || !attrs[NFL_FIELD_ATTR_UID] ||
+	    !attrs[NFL_FIELD_ATTR_BITWIDTH])
+		return -EINVAL;
+
+	field->name = unft_encap_get_name(attrs[NFL_FIELD_ATTR_NAME]);
+	if (!field->name)
+		return -ENOMEM;
+
+	field->uid = nla_get_u32(attrs[NFL_FIELD_ATTR_UID]);
+	field->bitwidth = nla_get_u32(attrs[NFL_FIELD_ATTR_BITWIDTH]);
+
+	return 0;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_fields(struct net_flow_field *fields, int count)
+{
+	int i;
+
+	if (!fields)
+		return;
+
+	for (i = 0; i < count; i++)
+		kfree(fields[i].name);
+
+	kfree(fields);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int unft_encap_get_header_fields(struct nlattr *attr,
+					struct net_flow_hdr *header)
+{
+	int count, rem;
+	struct nlattr *a;
+
+	if (!attr)
+		return -EINVAL;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_FIELD)
+			count++;
+
+	header->field_sz = count;
+	header->fields = kcalloc(count, sizeof *header->fields, GFP_KERNEL);
+	if (!header->fields)
+		return -ENOMEM;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		int err;
+
+		if (nla_type(a) != NFL_FIELD)
+			continue;
+
+		err = unft_encap_get_field(a, &header->fields[count]);
+		if (err) {
+			unft_encap_free_fields(header->fields, count);
+			return err;
+		}
+
+		count++;
+	}
+
+	return 0;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_header(struct net_flow_hdr *header)
+{
+	unft_encap_free_fields(header->fields, header->field_sz);
+	kfree(header->name);
+	kfree(header);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static const
+struct nla_policy flow_table_header_attr_policy[NFL_HEADER_ATTR_MAX + 1] =
+{
+	[NFL_HEADER_ATTR_NAME]		= { .type = NLA_STRING },
+	[NFL_HEADER_ATTR_UID]		= { .type = NLA_U32 },
+	[NFL_HEADER_ATTR_FIELDS]	= { .type = NLA_NESTED },
+};
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure */
+struct net_flow_hdr *unft_encap_get_header(struct nlattr *attr)
+{
+	int err = -EINVAL;
+	struct net_flow_hdr *header;
+	struct nlattr *attrs[NFL_HEADER_ATTR_MAX + 1];
+
+	header = kzalloc(sizeof *header, GFP_KERNEL);
+	if (!header)
+		return ERR_PTR(-ENOMEM);
+
+	err = nla_parse_nested(attrs, NFL_HEADER_ATTR_MAX, attr,
+			       flow_table_header_attr_policy);
+	if (err)
+		goto err;
+
+	if (!attrs[NFL_HEADER_ATTR_NAME] || !attrs[NFL_HEADER_ATTR_UID] ||
+	    !attrs[NFL_HEADER_ATTR_FIELDS])
+		goto err;
+
+	header->name = unft_encap_get_name(attrs[NFL_HEADER_ATTR_NAME]);
+	if (!header->name) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	header->uid = nla_get_u32(attrs[NFL_HEADER_ATTR_UID]);
+
+	err = unft_encap_get_header_fields(attrs[NFL_HEADER_ATTR_FIELDS],
+					   header);
+	if (err)
+		goto err;
+
+	return header;
+err:
+	unft_encap_free_header(header);
+	return ERR_PTR(err);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_headers(struct net_flow_hdr **headers)
+{
+	int i;
+
+	if (!headers)
+		return;
+
+	for (i = 0; !IS_ERR_OR_NULL(headers[i]); i++)
+		unft_encap_free_header(headers[i]);
+
+	kfree(headers);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int unft_encap_get_headers(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+
+	if (!attr || unft_header_list)
+		return -EINVAL;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_HEADER)
+			count++;
+
+	unft_header_list = kcalloc(count + 1, sizeof *unft_header_list,
+				  GFP_KERNEL);
+	if (!unft_header_list)
+		return -ENOMEM;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		if (nla_type(a) != NFL_HEADER)
+			continue;
+
+		unft_header_list[count] = unft_encap_get_header(a);
+		if (IS_ERR(unft_header_list[count])) {
+			int err = PTR_ERR(unft_header_list[count]);
+
+			unft_encap_free_headers(unft_header_list);
+			unft_header_list = NULL;
+			return err;
+		}
+
+		count++;
+	}
+
+	return 0;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_actions(struct net_flow_action **actions)
+{
+	int i;
+
+	if (!actions)
+		return;
+
+	for (i = 0; actions[i]; i++) {
+		if (actions[i]->args) {
+			kfree(actions[i]->args->name);
+			kfree(actions[i]->args);
+		}
+		kfree(actions[i]);
+	}
+
+	kfree(actions);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int unft_encap_get_actions(struct nlattr *attr)
+{
+	int count, rem;
+	int err = 0;
+	struct nlattr *a;
+
+	if (!attr || unft_action_list)
+		return -EINVAL;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_HEADER)
+			count++;
+
+	unft_action_list = kcalloc(count + 1, sizeof *unft_action_list,
+				  GFP_KERNEL);
+	if (!unft_action_list)
+		return -ENOMEM;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		int err;
+
+		if (nla_type(a) != NFL_HEADER)
+			continue;
+
+		unft_action_list[count] = kzalloc(sizeof *unft_action_list[count],
+						  GFP_KERNEL);
+		if (!unft_action_list[count]) {
+			err = -ENOMEM;
+			goto err;
+		}
+
+		err = net_flow_get_action(unft_action_list[count], a);
+		if (err)
+			goto err;
+
+		count++;
+	}
+
+	return 0;
+
+err:
+	unft_encap_free_actions(unft_action_list);
+	unft_action_list = NULL;
+	return err;
+}
+
+/* Copied from flow_table.c */
+static const
+struct nla_policy net_flow_field_policy[NFL_FIELD_REF_MAX + 1] = {
+        [NFL_FIELD_REF_NEXT_NODE] = { .type = NLA_U32,},
+        [NFL_FIELD_REF_INSTANCE]  = { .type = NLA_U32,},
+        [NFL_FIELD_REF_HEADER]    = { .type = NLA_U32,},
+        [NFL_FIELD_REF_FIELD]     = { .type = NLA_U32,},
+        [NFL_FIELD_REF_MASK_TYPE] = { .type = NLA_U32,},
+        [NFL_FIELD_REF_TYPE]      = { .type = NLA_U32,},
+        [NFL_FIELD_REF_VALUE]     = { .type = NLA_BINARY,
+                                      .len = sizeof(u64)},
+        [NFL_FIELD_REF_MASK]      = { .type = NLA_BINARY,
+                                      .len = sizeof(u64)},
+};
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure */
+int unft_encap_get_jump_table(struct net_flow_jump_table *table,
+			      struct nlattr *attr)
+{
+	int err;
+	struct nlattr *attrs[NFL_FIELD_REF_MAX + 1];
+
+	err = net_flow_get_field(&table->field, attr);
+	if (err)
+		return err;
+
+	/* net_flow_get_field() does not parse NFL_FIELD_REF_NEXT_NODE
+	 * which has no corresponding field in struct net_flow_field_ref
+	 */
+
+	err = nla_parse_nested(attrs, NFL_FIELD_REF_MAX,
+			       attr, net_flow_field_policy);
+	if (err)
+		return err;
+
+	if (!attrs[NFL_FIELD_REF_NEXT_NODE])
+		return -EINVAL;
+
+	table->node =  nla_get_u32(attrs[NFL_FIELD_REF_NEXT_NODE]);
+
+	return 0;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static struct net_flow_jump_table *unft_encap_get_jump_tables(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+	struct net_flow_jump_table *tables;
+
+	count = 0;
+	if (attr)
+		nla_for_each_nested(a, attr, rem)
+			if (nla_type(a) == NFL_HEADER_NODE_HDRS_VALUE)
+				count++;
+
+	tables = kcalloc(count + 1, sizeof *tables, GFP_KERNEL);
+	if (!tables)
+		return ERR_PTR(-ENOMEM);
+
+	if (!attr)
+		return tables;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		int err;
+
+		if (nla_type(a) != NFL_HEADER_NODE_HDRS_VALUE)
+			continue;
+
+		err = unft_encap_get_jump_table(&tables[count], a);
+		if (err) {
+			kfree(tables);
+			return ERR_PTR(err);
+		}
+
+		count++;
+	}
+
+	return tables;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int *unft_encap_get_header_node_hdrs(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+	int *hdrs;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_HEADER_NODE_HDRS_VALUE)
+			count++;
+
+	hdrs = kcalloc(count + 1, sizeof *hdrs, GFP_KERNEL);
+	if (!hdrs)
+		return ERR_PTR(-ENOMEM);
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		u32 value;
+
+		if (nla_type(a) != NFL_HEADER_NODE_HDRS_VALUE)
+			continue;
+
+		value = nla_get_u32(a);
+		if (value > INT_MAX)
+			return ERR_PTR(-EINVAL);
+		hdrs[count++] = value;
+	}
+
+	return hdrs;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_header_node(struct net_flow_hdr_node *node)
+{
+	kfree(node->name);
+	kfree(node->hdrs);
+	kfree(node->jump);
+	kfree(node);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static const
+struct nla_policy flow_table_header_node_policy[NFL_HEADER_NODE_MAX + 1] =
+{
+        [NFL_HEADER_NODE_NAME]          = { .type = NLA_STRING },
+        [NFL_HEADER_NODE_UID]           = { .type = NLA_U32 },
+        [NFL_HEADER_NODE_HDRS]          = { .type = NLA_NESTED },
+        [NFL_HEADER_NODE_JUMP]          = { .type = NLA_NESTED },
+};
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure */
+struct net_flow_hdr_node *unft_encap_get_header_node(struct nlattr *attr)
+{
+	int err;
+	struct net_flow_hdr_node *node;
+	struct nlattr *attrs[NFL_HEADER_NODE_MAX + 1];
+
+	node = kzalloc(sizeof *node, GFP_KERNEL);
+	if (!node)
+		return ERR_PTR(-ENOMEM);
+
+	err = nla_parse_nested(attrs, NFL_HEADER_NODE_MAX,
+			       attr, flow_table_header_node_policy);
+	if (err)
+		goto err;
+
+	if (!attrs[NFL_HEADER_NODE_NAME] || !attrs[NFL_HEADER_NODE_UID] ||
+	    !attrs[NFL_HEADER_NODE_HDRS]) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	node->name = unft_encap_get_name(attrs[NFL_HEADER_NODE_NAME]);
+	if (!node->name) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	node->uid = nla_get_u32(attrs[NFL_HEADER_NODE_UID]);
+
+	node->hdrs = unft_encap_get_header_node_hdrs(attrs[NFL_HEADER_NODE_HDRS]);
+	if (IS_ERR(node->hdrs)) {
+		err = PTR_ERR(node->hdrs);
+		node->hdrs = NULL;
+		goto err;
+	}
+
+	node->jump = unft_encap_get_jump_tables(attrs[NFL_HEADER_NODE_JUMP]);
+	if (IS_ERR(node->jump)) {
+		err = PTR_ERR(node->jump);
+		node->jump = NULL;
+		goto err;
+	}
+
+	return node;
+err:
+	unft_encap_free_header_node(node);
+	return ERR_PTR(err);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_header_nodes(struct net_flow_hdr_node **nodes)
+{
+	int i;
+
+	if (!nodes)
+		return;
+
+	for (i = 0; !IS_ERR_OR_NULL(nodes[i]); i++)
+		unft_encap_free_header_node(nodes[i]);
+
+	kfree(nodes);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int unft_encap_get_header_graph(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+
+	if (!attr || unft_header_nodes)
+		return -EINVAL;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_HEADER_GRAPH_NODE)
+			count++;
+
+	unft_header_nodes = kcalloc(count + 1, sizeof *unft_header_nodes,
+				  GFP_KERNEL);
+	if (!unft_header_nodes)
+		return -ENOMEM;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		if (nla_type(a) != NFL_HEADER_GRAPH_NODE)
+			continue;
+
+		unft_header_nodes[count] = unft_encap_get_header_node(a);
+		if (IS_ERR(unft_header_nodes[count])) {
+			int err = PTR_ERR(unft_header_nodes[count]);
+
+			unft_encap_free_header_nodes(unft_header_nodes);
+			unft_header_nodes = NULL;
+			return err;
+		}
+
+		count++;
+	}
+
+	return 0;
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_table_node(struct net_flow_tbl_node *node)
+{
+	kfree(node->jump);
+	kfree(node);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static const
+struct nla_policy flow_table_table_node_policy[NFL_TABLE_GRAPH_NODE_MAX + 1] =
+{
+        [NFL_TABLE_GRAPH_NODE_UID]	= { .type = NLA_U32 },
+        [NFL_TABLE_GRAPH_NODE_FLAGS]	= { .type = NLA_U32 },
+        [NFL_TABLE_GRAPH_NODE_JUMP]	= { .type = NLA_NESTED },
+};
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure */
+struct net_flow_tbl_node *unft_encap_get_table_node(struct nlattr *attr)
+{
+	int err;
+	struct net_flow_tbl_node *node;
+	struct nlattr *attrs[NFL_TABLE_GRAPH_NODE_MAX + 1];
+
+	node = kzalloc(sizeof *node, GFP_KERNEL);
+	if (!node)
+		return ERR_PTR(-ENOMEM);
+
+	err = nla_parse_nested(attrs, NFL_TABLE_GRAPH_NODE_MAX,
+			       attr, flow_table_table_node_policy);
+	if (err)
+		goto err;
+
+	if (!attrs[NFL_TABLE_GRAPH_NODE_UID] ||
+	    !attrs[NFL_TABLE_GRAPH_NODE_FLAGS]) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	node->uid = nla_get_u32(attrs[NFL_TABLE_GRAPH_NODE_UID]);
+	node->flags = nla_get_u32(attrs[NFL_TABLE_GRAPH_NODE_FLAGS]);
+
+	node->jump = unft_encap_get_jump_tables(attrs[NFL_TABLE_GRAPH_NODE_JUMP]);
+	if (IS_ERR(node->jump)) {
+		err = PTR_ERR(node->jump);
+		node->jump = NULL;
+		goto err;
+	}
+
+	return node;
+err:
+	unft_encap_free_table_node(node);
+	return ERR_PTR(err);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static void unft_encap_free_table_nodes(struct net_flow_tbl_node **nodes)
+{
+	int i;
+
+	if (!nodes)
+		return;
+
+	for (i = 0; !IS_ERR_OR_NULL(nodes[i]); i++)
+		unft_encap_free_table_node(nodes[i]);
+
+	kfree(nodes);
+}
+
+/* This only deals with encoding NFT attibutes and could
+ * be part of flow table infrastructure.
+ */
+static int unft_encap_get_table_graph(struct nlattr *attr)
+{
+	int count, rem;
+	struct nlattr *a;
+
+	if (!attr || unft_table_nodes)
+		return -EINVAL;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem)
+		if (nla_type(a) == NFL_TABLE_GRAPH_NODE)
+			count++;
+
+	unft_table_nodes = kcalloc(count + 1, sizeof *unft_table_nodes,
+				   GFP_KERNEL);
+	if (!unft_table_nodes)
+		return -ENOMEM;
+
+	count = 0;
+	nla_for_each_nested(a, attr, rem) {
+		if (nla_type(a) != NFL_TABLE_GRAPH_NODE)
+			continue;
+
+		unft_table_nodes[count] = unft_encap_get_table_node(a);
+		if (IS_ERR(unft_table_nodes[count])) {
+			int err = PTR_ERR(unft_table_nodes[count]);
+
+			unft_encap_free_table_nodes(unft_table_nodes);
+			unft_table_nodes = NULL;
+			return err;
+		}
+
+		count++;
+	}
+
+	return 0;
+}
+
+static const
+struct nla_policy unft_net_flow_policy[NFL_MAX + 1] = {
+	[NFL_IDENTIFIER_TYPE]	= { .type = NLA_U32,},
+	[NFL_IDENTIFIER]	= { .type = NLA_U32,},
+	[NFL_TABLES]		= { .type = NLA_NESTED,},
+	[NFL_HEADERS]		= { .type = NLA_NESTED,},
+	[NFL_ACTIONS]		= { .type = NLA_NESTED,},
+	[NFL_HEADER_GRAPH]	= { .type = NLA_NESTED,},
+	[NFL_TABLE_GRAPH]	= { .type = NLA_NESTED,},
+	[NFL_FLOWS]		= { .type = NLA_NESTED,},
+	[NFL_FLOWS_ERROR]	= { .type = NLA_NESTED,},
+};
+
+static int unft_encap_net_flow_cmd(u32 cmd, struct nlattr *attr)
+{
+	int err;
+	struct nlattr *tb[NFL_MAX + 1];
+	u32 ifindex, type;
+
+	if (!attr)
+		return -EINVAL;
+
+	err = nla_parse_nested(tb, NFL_MAX, attr, unft_net_flow_policy);
+	if (err)
+		return err;
+
+	if (!tb[NFL_IDENTIFIER_TYPE] || !tb[NFL_IDENTIFIER])
+		return -EINVAL;
+	type = nla_get_u32(tb[NFL_IDENTIFIER_TYPE]);
+	if (type != NFL_IDENTIFIER_IFINDEX)
+		return -EOPNOTSUPP;
+	ifindex = nla_get_u32(tb[NFL_IDENTIFIER]);
+
+	pr_debug("%s type: %u ifindex: %u cmd: %u\n", __func__, type,
+		 ifindex, cmd);
+
+	switch (cmd) {
+	case NFL_TABLE_CMD_GET_TABLES:
+		return unft_encap_get_tables(tb[NFL_TABLES]);
+
+	case NFL_TABLE_CMD_GET_HEADERS:
+		return unft_encap_get_headers(tb[NFL_HEADERS]);
+
+	case NFL_TABLE_CMD_GET_ACTIONS:
+		return unft_encap_get_actions(tb[NFL_ACTIONS]);
+
+	case NFL_TABLE_CMD_GET_HDR_GRAPH:
+		return unft_encap_get_header_graph(tb[NFL_HEADER_GRAPH]);
+
+	case NFL_TABLE_CMD_GET_TABLE_GRAPH:
+		return unft_encap_get_table_graph(tb[NFL_TABLE_GRAPH]);
+
+	case NFL_TABLE_CMD_SET_FLOWS:
+	case NFL_TABLE_CMD_DEL_FLOWS:
+		/* Noting more to decode for these commands */
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static const
+struct nla_policy net_flow_hairpin_encap_policy[NFLH_ENCAP_MAX + 1] = {
+	[NFLH_ENCAP_CMD_TYPE]	= { .type = NLA_U32,},
+	[NFLH_ENCAP_CMD]	= { .type = NLA_U32,},
+	[NFLH_ENCAP_SEQ]	= { .type = NLA_U64,},
+	[NFLH_ENCAP_STATUS]	= { .type = NLA_U32,},
+	[NFLH_ENCAP_ATTR]	= { .type = NLA_NESTED,},
+};
+
+static int net_flow_table_hairpin_cmd_encap(struct sk_buff *skb,
+					    struct genl_info *info)
+{
+	int err = -EINVAL;
+	struct nlattr *tb[NFLH_ENCAP_MAX + 1];
+	u32 cmd, status, type;
+	u64 seq;
+
+	if (unft_msg_state != UNFT_MSG_S_REQUEST)
+		goto out;
+
+	if (!info->attrs[NFLH_ENCAP])
+		goto out;
+
+	err = nla_parse_nested(tb, NFLH_ENCAP_MAX,
+			       info->attrs[NFLH_ENCAP],
+			       net_flow_hairpin_encap_policy);
+	if (err)
+		goto out;
+
+	if (!tb[NFLH_ENCAP_CMD_TYPE] ||
+	    !tb[NFLH_ENCAP_CMD] ||
+	    !tb[NFLH_ENCAP_SEQ] ||
+	    !tb[NFLH_ENCAP_STATUS]) {
+		err = -EINVAL;
+		goto out;
+	}
+	type = nla_get_u32(tb[NFLH_ENCAP_CMD_TYPE]);
+	if (type != NFLH_ENCAP_CMD_NFL_CMD) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+	cmd = nla_get_u32(tb[NFLH_ENCAP_CMD]);
+	seq = nla_get_u64(tb[NFLH_ENCAP_SEQ]);
+	status = nla_get_u32(tb[NFLH_ENCAP_STATUS]);
+
+	if (unft_msg_seq != seq) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	pr_debug("%s cmd: %u seq: %llu status: %u\n", __func__,
+		 cmd, seq, status);
+
+	switch (status) {
+	case NFLH_ENCAP_STATUS_OK:
+		err = unft_encap_net_flow_cmd(cmd, tb[NFLH_ENCAP_ATTR]);
+		if (err)
+			goto out;
+		unft_msg_status = 0;
+		break;
+
+	case NFLH_ENCAP_STATUS_EINVAL:
+		unft_msg_status = -EINVAL;
+		break;
+
+	case NFLH_ENCAP_STATUS_EOPNOTSUPP:
+		unft_msg_status = -EOPNOTSUPP;
+		break;
+
+	default:
+		err = -EINVAL;
+		goto out;
+	}
+
+out:
+	unft_msg_state = UNFT_MSG_S_REPLY;
+	wake_up_interruptible(&unft_msg_wq);
+	return err;
+}
+
+static const struct genl_ops net_flow_table_hairpin_nl_ops[] = {
+	{
+		.cmd = NFLH_CMD_SET_LISTENER,
+		.doit = net_flow_table_hairpin_cmd_set_listener,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NFLH_CMD_GET_LISTENER,
+		.doit = net_flow_table_hairpin_cmd_get_listener,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NFLH_CMD_ENCAP,
+		.doit = net_flow_table_hairpin_cmd_encap,
+		.flags = GENL_ADMIN_PERM,
+	},
+};
+
+static netdev_tx_t unft_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	dev_kfree_skb(skb);
+
+	return NETDEV_TX_OK;
+}
+
+static int
+unft_flow_table_get_tables__(struct net_device *dev)
+{
+	int err, i;
+
+	err = unft_flow_encap_request(dev, NFL_TABLE_CMD_GET_TABLES,
+				      NULL, NULL);
+	if (err)
+		return err;
+
+	for (i = 0; unft_table_list[i]; i++) {
+		err = net_flow_init_cache(unft_table_list[i]);
+		if (err)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	while (i-- > 1)
+		net_flow_destroy_cache(unft_table_list[i - 1]);
+	return err;
+}
+
+static struct net_flow_tbl **unft_flow_table_get_tables(struct net_device *dev)
+{
+	if (!unft_table_list && unft_flow_table_get_tables__(dev))
+		return NULL;
+
+	return unft_table_list;
+}
+
+static struct net_flow_hdr **unft_flow_table_get_headers(struct net_device *dev)
+{
+	if (!unft_header_list &&
+	    unft_flow_encap_request(dev, NFL_TABLE_CMD_GET_HEADERS, NULL, NULL))
+		return NULL;
+
+	return unft_header_list;
+}
+
+static struct net_flow_action **unft_flow_table_get_actions(struct net_device *dev)
+{
+	if (!unft_action_list &&
+	    unft_flow_encap_request(dev, NFL_TABLE_CMD_GET_ACTIONS, NULL, NULL))
+		return NULL;
+
+	return unft_action_list;
+}
+
+static struct net_flow_hdr_node **unft_flow_table_get_hgraph(struct net_device *dev)
+{
+	if (!unft_header_nodes &&
+	    unft_flow_encap_request(dev, NFL_TABLE_CMD_GET_HDR_GRAPH,
+				    NULL, NULL))
+		return NULL;
+
+	return unft_header_nodes;
+}
+
+static struct net_flow_tbl_node **unft_flow_table_get_tgraph(struct net_device *dev)
+{
+	if (!unft_table_nodes &&
+	    unft_flow_encap_request(dev, NFL_TABLE_CMD_GET_TABLE_GRAPH,
+				    NULL, NULL))
+		return NULL;
+
+	return unft_table_nodes;
+}
+
+static const struct net_device_ops unft_ops = {
+	.ndo_start_xmit = unft_xmit, /* Required */
+	.ndo_flow_set_rule = unft_flow_table_set_rule,
+	.ndo_flow_del_rule = unft_flow_table_del_rule,
+	.ndo_flow_get_tbls = unft_flow_table_get_tables,
+	.ndo_flow_get_hdrs = unft_flow_table_get_headers,
+	.ndo_flow_get_actions = unft_flow_table_get_actions,
+	.ndo_flow_get_hdr_graph = unft_flow_table_get_hgraph,
+	.ndo_flow_get_tbl_graph = unft_flow_table_get_tgraph,
+};
+
+static void unft_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_NETLINK;
+	dev->tx_queue_len = 0;
+
+	dev->netdev_ops	= &unft_ops;
+	dev->destructor	= free_netdev;
+
+	dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
+			NETIF_F_HIGHDMA | NETIF_F_LLTX;
+	dev->flags = IFF_NOARP;
+
+	/* That's rather a softlimit here, which, of course,
+	 * can be altered. Not a real MTU, but what is to be
+	 * expected in most cases.
+	 */
+	dev->mtu = NLMSG_GOODSIZE;
+}
+
+static int unft_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+	if (tb[IFLA_ADDRESS])
+		return -EINVAL;
+	return 0;
+}
+
+static struct rtnl_link_ops unft_link_ops __read_mostly = {
+	.kind			= "unft",
+	.setup			= unft_setup,
+	.validate		= unft_validate,
+};
+static __init int unft_register(void)
+{
+	int err;
+
+	err = genl_register_family_with_ops(&net_flow_hairpin_nl_family,
+					    net_flow_table_hairpin_nl_ops);
+	if (err)
+		return err;
+
+	err = rtnl_link_register(&unft_link_ops);
+	if (err)
+		goto err;
+
+	return 0;
+
+err:
+	genl_unregister_family(&net_flow_hairpin_nl_family);
+	return err;
+}
+
+static __exit void unft_unregister(void)
+{
+	int i;
+
+	genl_unregister_family(&net_flow_hairpin_nl_family);
+	rtnl_link_unregister(&unft_link_ops);
+
+	for (i = 0; unft_table_list[i]; i++)
+		net_flow_destroy_cache(unft_table_list[i]);
+	unft_encap_free_tables(unft_table_list);
+	unft_encap_free_headers(unft_header_list);
+	unft_encap_free_actions(unft_action_list);
+	unft_encap_free_header_nodes(unft_header_nodes);
+	unft_encap_free_table_nodes(unft_table_nodes);
+}
+
+module_init(unft_register);
+module_exit(unft_unregister);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Simon Horman <simon.horman@netronome.com>");
+MODULE_DESCRIPTION("User-Space Hairpin Network Flow Table Device");
+MODULE_ALIAS_RTNL_LINK("unft");
+MODULE_ALIAS_GENL_FAMILY(NFLH_GENL_NAME);
-- 
2.1.4

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

* Re: [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device
  2015-02-18 19:25 [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
                   ` (2 preceding siblings ...)
  2015-02-18 19:25 ` [PATCH/RFC 3/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
@ 2015-02-20 21:29 ` David Miller
  3 siblings, 0 replies; 5+ messages in thread
From: David Miller @ 2015-02-20 21:29 UTC (permalink / raw)
  To: simon.horman; +Cc: netdev

From: Simon Horman <simon.horman@netronome.com>
Date: Wed, 18 Feb 2015 14:25:56 -0500

> *** Not for Upstream Merge
> *** For informational purposes only
> 
> As discussed at netconf we have been working on hairpinning Flow API
> messages back to user-space as a mechanism for exercising that API.
> 
> And as promised at netconf I am releasing our code.

Thanks for posting this series Simon.

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

end of thread, other threads:[~2015-02-20 21:29 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-18 19:25 [PATCH/RFC 0/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
2015-02-18 19:25 ` [PATCH/RFC 1/3] net: flow: export net_flow_{put_rule,get_{field,action}} Simon Horman
2015-02-18 19:25 ` [PATCH/RFC 2/3] net: flow: Introduce flow table hairpin API Simon Horman
2015-02-18 19:25 ` [PATCH/RFC 3/3] net: unft: Add Userspace hairpin network flow table device Simon Horman
2015-02-20 21:29 ` [PATCH/RFC 0/3] " David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.