lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1424287559-25700-4-git-send-email-simon.horman@netronome.com>
Date:	Wed, 18 Feb 2015 14:25:59 -0500
From:	Simon Horman <simon.horman@...ronome.com>
To:	netdev@...r.kernel.org
Cc:	Simon Horman <simon.horman@...ronome.com>
Subject: [PATCH/RFC 3/3] net: unft: Add Userspace hairpin network flow table device

*** 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@...ronome.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@...ronome.com>
+ *
+ * Based on: flow_table.c
+ * Copyright (c) 2014 John Fastabend <john.r.fastabend@...el.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@...ronome.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@...ronome.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

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists