[dpdk-dev,v8,2/4] unci: add kernel control path kernel module

Message ID 20170621110651.75299-3-ferruh.yigit@intel.com (mailing list archive)
State Superseded, archived
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail Compilation issues

Commit Message

Ferruh Yigit June 21, 2017, 11:06 a.m. UTC
  This kernel module is based on KNI module, but this one is stripped
version of it and only for control messages, no data transfer
functionality provided.

This Linux kernel module helps userspace application create virtual
interfaces and when a control command issued into that virtual
interface, module pushes the command to the userspace and gets the
response back for the caller application.

The Linux tools like ethtool/ifconfig/ip can be used on virtual
interfaces but not ones for related data, like tcpdump.

Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
---

v8:
* rename to usci

v7:
* rebase v17.08

v6:
* rebase v17.05

v5:
* Use unsigned primitive types as possible

v4:
* Remove logging helper macros, use pr_fmt
* Seperate log msg for timeout and error

v3:
* Devices are not up by default
* Add enable/disable promisc, allmulti support
* Increase timeout to 500ms and print log when a command timedout

v2:
* Use rtnetlink to create interfaces
* Fix ethtool get/set eeprom
* Remove commented out code
---
 MAINTAINERS                                        |   4 +
 config/common_base                                 |   6 +
 config/common_linuxapp                             |   1 +
 lib/librte_eal/linuxapp/Makefile                   |   4 +-
 lib/librte_eal/linuxapp/eal/Makefile               |   1 +
 .../eal/include/exec-env/rte_unci_common.h         | 109 ++++++++
 lib/librte_eal/linuxapp/unci/Makefile              |  54 ++++
 lib/librte_eal/linuxapp/unci/unci_dev.h            |  56 ++++
 lib/librte_eal/linuxapp/unci/unci_ethtool.c        | 293 +++++++++++++++++++++
 lib/librte_eal/linuxapp/unci/unci_net.c            | 217 +++++++++++++++
 lib/librte_eal/linuxapp/unci/unci_nl.c             | 220 ++++++++++++++++
 11 files changed, 964 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/linuxapp/eal/include/exec-env/rte_unci_common.h
 create mode 100644 lib/librte_eal/linuxapp/unci/Makefile
 create mode 100644 lib/librte_eal/linuxapp/unci/unci_dev.h
 create mode 100644 lib/librte_eal/linuxapp/unci/unci_ethtool.c
 create mode 100644 lib/librte_eal/linuxapp/unci/unci_net.c
 create mode 100644 lib/librte_eal/linuxapp/unci/unci_nl.c
  

Comments

Stephen Hemminger June 21, 2017, 3:23 p.m. UTC | #1
On Wed, 21 Jun 2017 12:06:49 +0100
Ferruh Yigit <ferruh.yigit@intel.com> wrote:

> +static struct ethtool_input_buffer {
> +	int magic;
> +	void *buffer;
> +	size_t length;
> +	struct completion *msg_received;
> +	int *err;
> +	u32 in_use;
> +} ethtool_input_buffer

Naming in kernel is important. This isn't kernel ethtool, so  it shouldn't be named that.

Having a single instance per system for a control interface means it won't work
if multiple apps have control channel open or in containers.

You should also do policy validation on the netlink message.
  
Ferruh Yigit June 30, 2017, 5:02 p.m. UTC | #2
On 6/21/2017 4:23 PM, Stephen Hemminger wrote:
> On Wed, 21 Jun 2017 12:06:49 +0100
> Ferruh Yigit <ferruh.yigit@intel.com> wrote:
> 
>> +static struct ethtool_input_buffer {
>> +	int magic;
>> +	void *buffer;
>> +	size_t length;
>> +	struct completion *msg_received;
>> +	int *err;
>> +	u32 in_use;
>> +} ethtool_input_buffer
> 
> Naming in kernel is important. This isn't kernel ethtool, so  it shouldn't be named that.

I have updated naming on v9.

> 
> Having a single instance per system for a control interface means it won't work
> if multiple apps have control channel open or in containers.

This should work for multiple add and interfaces, unci_nl_exec()
serializes the requests, above struct used for output buffer indirection.

I am not sure how to handle requests without serializing them ...

> 
> You should also do policy validation on the netlink message.

I will send a new version with this.

>
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index f6095efff..8d1fb0431 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -277,6 +277,10 @@  F: test/test/test_kni.c
 F: examples/kni/
 F: doc/guides/sample_app_ug/kernel_nic_interface.rst
 
+Linux Userspace Network Control Interface (UNCI)
+M: Ferruh Yigit <ferruh.yigit@intel.com>
+F: lib/librte_eal/linuxapp/unci/
+
 Linux AF_PACKET
 M: John W. Linville <linville@tuxdriver.com>
 F: drivers/net/af_packet/
diff --git a/config/common_base b/config/common_base
index c767b1090..f50ccc193 100644
--- a/config/common_base
+++ b/config/common_base
@@ -704,6 +704,12 @@  CONFIG_RTE_LIBRTE_PDUMP=y
 CONFIG_RTE_LIBRTE_ETHTOOL=n
 
 #
+# Compile Userspace Network Control Interface (UNCI) kernel module
+#
+CONFIG_RTE_UNCI_KMOD=n
+CONFIG_RTE_UNCI_KO_DEBUG=n
+
+#
 # Compile vhost user library
 #
 CONFIG_RTE_LIBRTE_VHOST=n
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 33ed11b37..4deab42c3 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -41,6 +41,7 @@  CONFIG_RTE_KNI_KMOD=y
 CONFIG_RTE_LIBRTE_KNI=y
 CONFIG_RTE_LIBRTE_PMD_KNI=y
 CONFIG_RTE_LIBRTE_ETHTOOL=y
+CONFIG_RTE_UNCI_KMOD=y
 CONFIG_RTE_LIBRTE_VHOST=y
 CONFIG_RTE_LIBRTE_PMD_VHOST=y
 CONFIG_RTE_LIBRTE_PMD_AF_PACKET=y
diff --git a/lib/librte_eal/linuxapp/Makefile b/lib/librte_eal/linuxapp/Makefile
index 4794696b6..2d293f1a6 100644
--- a/lib/librte_eal/linuxapp/Makefile
+++ b/lib/librte_eal/linuxapp/Makefile
@@ -1,6 +1,6 @@ 
 #   BSD LICENSE
 #
-#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+#   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,8 @@  DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal
 DIRS-$(CONFIG_RTE_EAL_IGB_UIO) += igb_uio
 DIRS-$(CONFIG_RTE_KNI_KMOD) += kni
 DEPDIRS-kni := eal
+DIRS-$(CONFIG_RTE_UNCI_KMOD) += unci
+DEPDIRS-unci := eal
 DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += xen_dom0
 DEPDIRS-xen_dom0 := eal
 
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 640afd088..401160bc9 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -127,6 +127,7 @@  CFLAGS_eal_thread.o += -Wno-return-type
 endif
 
 INC := rte_interrupts.h rte_kni_common.h rte_dom0_common.h
+INC += rte_unci_common.h
 
 SYMLINK-$(CONFIG_RTE_EXEC_ENV_LINUXAPP)-include/exec-env := \
 	$(addprefix include/exec-env/,$(INC))
diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_unci_common.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_unci_common.h
new file mode 100644
index 000000000..a29805734
--- /dev/null
+++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_unci_common.h
@@ -0,0 +1,109 @@ 
+/*-
+ *   This file is provided under a dual BSD/LGPLv2 license.  When using or
+ *   redistributing this file, you may do so under either license.
+ *
+ *   GNU LESSER GENERAL PUBLIC LICENSE
+ *
+ *   Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of version 2.1 of the GNU Lesser General Public License
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program;
+ *
+ *   Contact Information:
+ *   Intel Corporation
+ *
+ *
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _RTE_UNCI_COMMON_H_
+#define _RTE_UNCI_COMMON_H_
+
+#define UNCI_DEVICE "unci"
+
+#define UNCI_NL_GRP 31
+
+#define UNCI_ETHTOOL_MSG_LEN 500
+struct unci_ethtool_msg {
+	uint32_t cmd_id;
+	uint8_t port_id;
+	uint32_t flag;
+	uint8_t input_buffer[UNCI_ETHTOOL_MSG_LEN];
+	uint8_t output_buffer[UNCI_ETHTOOL_MSG_LEN];
+	size_t input_buffer_len;
+	size_t output_buffer_len;
+	int err;
+};
+
+enum unci_ethtool_msg_flag {
+	UNCI_MSG_FLAG_NONE,
+	UNCI_MSG_FLAG_REQUEST,
+	UNCI_MSG_FLAG_RESPONSE,
+};
+
+enum {
+	IFLA_UNCI_UNSPEC,
+	IFLA_UNCI_PORTID,
+	IFLA_UNCI_PID,
+	__IFLA_UNCI_MAX,
+};
+
+#define IFLA_UNCI_MAX (__IFLA_UNCI_MAX - 1)
+
+/*
+ * Request id.
+ */
+enum rte_unci_req_id {
+	RTE_UNCI_REQ_UNKNOWN = (1 << 16),
+	RTE_UNCI_REQ_CHANGE_MTU,
+	RTE_UNCI_REQ_CFG_NETWORK_IF,
+	RTE_UNCI_REQ_GET_STATS,
+	RTE_UNCI_REQ_GET_MAC,
+	RTE_UNCI_REQ_SET_MAC,
+	RTE_UNCI_REQ_START_PORT,
+	RTE_UNCI_REQ_STOP_PORT,
+	RTE_UNCI_REQ_SET_PROMISC,
+	RTE_UNCI_REQ_SET_ALLMULTI,
+	RTE_UNCI_REQ_MAX,
+};
+
+#endif /* _RTE_UNCI_COMMON_H_ */
diff --git a/lib/librte_eal/linuxapp/unci/Makefile b/lib/librte_eal/linuxapp/unci/Makefile
new file mode 100644
index 000000000..64f64b41f
--- /dev/null
+++ b/lib/librte_eal/linuxapp/unci/Makefile
@@ -0,0 +1,54 @@ 
+#   BSD LICENSE
+#
+#   Copyright(c) 2016 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# module name and path
+#
+MODULE = rte_unci
+
+#
+# CFLAGS
+#
+MODULE_CFLAGS += -I$(SRCDIR)
+MODULE_CFLAGS += -I$(RTE_OUTPUT)/include
+MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h
+MODULE_CFLAGS += -Wall -Werror
+
+#
+# all source are stored in SRCS-y
+#
+SRCS-y += unci_net.c
+SRCS-y += unci_ethtool.c
+SRCS-y += unci_nl.c
+
+include $(RTE_SDK)/mk/rte.module.mk
diff --git a/lib/librte_eal/linuxapp/unci/unci_dev.h b/lib/librte_eal/linuxapp/unci/unci_dev.h
new file mode 100644
index 000000000..d699f12d2
--- /dev/null
+++ b/lib/librte_eal/linuxapp/unci/unci_dev.h
@@ -0,0 +1,56 @@ 
+/*-
+ * GPL LICENSE SUMMARY
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of version 2 of the GNU General Public License as
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;
+ *
+ *   The full GNU General Public License is included in this distribution
+ *   in the file called LICENSE.GPL.
+ *
+ *   Contact Information:
+ *   Intel Corporation
+ */
+
+#ifndef _UNCI_DEV_H_
+#define _UNCI_DEV_H_
+
+#include <linux/netdevice.h>
+#include <exec-env/rte_unci_common.h>
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+struct unci_dev {
+	u8 port_id;
+	u32 pid;
+	struct completion msg_received;
+	u32 nb_timedout_msg;
+};
+
+void unci_nl_init(void);
+void unci_nl_release(void);
+int unci_nl_exec(u32 cmd, struct net_device *dev, void *in_data,
+		size_t in_len, void *out_data, size_t out_len);
+
+void unci_set_ethtool_ops(struct net_device *netdev);
+
+#ifdef RTE_UNCI_KO_DEBUG
+#define UNCI_DBG(args...) pr_debug(args)
+#else
+#define UNCI_DBG(args...)
+#endif
+
+#endif /* _UNCI_DEV_H_ */
diff --git a/lib/librte_eal/linuxapp/unci/unci_ethtool.c b/lib/librte_eal/linuxapp/unci/unci_ethtool.c
new file mode 100644
index 000000000..035344559
--- /dev/null
+++ b/lib/librte_eal/linuxapp/unci/unci_ethtool.c
@@ -0,0 +1,293 @@ 
+/*-
+ * GPL LICENSE SUMMARY
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of version 2 of the GNU General Public License as
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;
+ *
+ *   The full GNU General Public License is included in this distribution
+ *   in the file called LICENSE.GPL.
+ *
+ *   Contact Information:
+ *   Intel Corporation
+ */
+
+#include "unci_dev.h"
+
+#define ETHTOOL_GEEPROM_LEN 99
+#define ETHTOOL_GREGS_LEN 98
+#define ETHTOOL_GSSET_COUNT 97
+
+static int unci_check_if_running(struct net_device *dev)
+{
+	return 0;
+}
+
+static void unci_get_drvinfo(struct net_device *dev,
+		struct ethtool_drvinfo *info)
+{
+	int ret;
+
+	ret = unci_nl_exec(info->cmd, dev, NULL, 0,
+			info, sizeof(struct ethtool_drvinfo));
+	if (ret < 0)
+		memset(info, 0, sizeof(struct ethtool_drvinfo));
+}
+
+static int unci_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	return unci_nl_exec(ecmd->cmd, dev, NULL, 0,
+			ecmd, sizeof(struct ethtool_cmd));
+}
+
+static int unci_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	return unci_nl_exec(ecmd->cmd, dev, ecmd, sizeof(struct ethtool_cmd),
+			NULL, 0);
+}
+
+static void unci_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	int ret;
+
+	ret = unci_nl_exec(wol->cmd, dev, NULL, 0,
+			wol, sizeof(struct ethtool_wolinfo));
+	if (ret < 0)
+		memset(wol, 0, sizeof(struct ethtool_wolinfo));
+}
+
+static int unci_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	return unci_nl_exec(wol->cmd, dev, wol, sizeof(struct ethtool_wolinfo),
+			NULL, 0);
+}
+
+static int unci_nway_reset(struct net_device *dev)
+{
+	return unci_nl_exec(ETHTOOL_NWAY_RST, dev, NULL, 0, NULL, 0);
+}
+
+static u32 unci_get_link(struct net_device *dev)
+{
+	u32 data;
+	int ret;
+
+	ret = unci_nl_exec(ETHTOOL_GLINK, dev, NULL, 0, &data, sizeof(u32));
+	if (ret < 0)
+		return 0;
+
+	return data;
+}
+
+static int unci_get_eeprom_len(struct net_device *dev)
+{
+	int data;
+	int ret;
+
+	ret = unci_nl_exec(ETHTOOL_GEEPROM_LEN, dev, NULL, 0,
+			&data, sizeof(int));
+	if (ret < 0)
+		return 0;
+
+	return data;
+}
+
+static int unci_get_eeprom(struct net_device *dev,
+		struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct ethtool_eeprom eeprom_tmp;
+	int ret = 0;
+	int remaining;
+	u32 offset = 0;
+
+	eeprom_tmp = *eeprom;
+
+	remaining = eeprom_tmp.len;
+	while (remaining > 0 && ret == 0) {
+		eeprom_tmp.len = min(remaining, UNCI_ETHTOOL_MSG_LEN);
+
+		ret = unci_nl_exec(eeprom_tmp.cmd, dev,
+				&eeprom_tmp, sizeof(struct ethtool_eeprom),
+				data + offset, eeprom_tmp.len);
+		eeprom_tmp.offset += eeprom_tmp.len;
+		offset += eeprom_tmp.len;
+		remaining -= eeprom_tmp.len;
+	}
+
+	return ret;
+}
+
+static int unci_set_eeprom(struct net_device *dev,
+		struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct ethtool_eeprom *eeprom_tmp;
+	int ret = 0;
+	u32 remaining;
+	u32 offset = 0;
+	u32 payload;
+
+	if (sizeof(struct ethtool_eeprom) > UNCI_ETHTOOL_MSG_LEN)
+		return -1;
+
+	eeprom_tmp = kmalloc(UNCI_ETHTOOL_MSG_LEN, GFP_KERNEL);
+	payload = UNCI_ETHTOOL_MSG_LEN - sizeof(struct ethtool_eeprom);
+
+	*eeprom_tmp = *eeprom;
+	remaining = eeprom->len;
+
+	while (remaining > 0 && ret == 0) {
+		eeprom_tmp->len = min(remaining, payload);
+
+		memcpy(eeprom_tmp->data, data + offset, payload);
+
+		ret = unci_nl_exec(eeprom->cmd, dev, eeprom,
+				UNCI_ETHTOOL_MSG_LEN, NULL, 0);
+
+		eeprom_tmp->offset += eeprom_tmp->len;
+		offset += eeprom_tmp->len;
+		remaining -= eeprom_tmp->len;
+	}
+
+	kfree(eeprom_tmp);
+
+	return ret;
+}
+
+static void unci_get_ringparam(struct net_device *dev,
+		struct ethtool_ringparam *ring)
+{
+	unci_nl_exec(ring->cmd, dev, NULL, 0,
+			ring, sizeof(struct ethtool_ringparam));
+}
+
+static int unci_set_ringparam(struct net_device *dev,
+		struct ethtool_ringparam *ring)
+{
+	return unci_nl_exec(ring->cmd, dev, ring,
+			sizeof(struct ethtool_ringparam), NULL, 0);
+}
+
+static void unci_get_pauseparam(struct net_device *dev,
+		struct ethtool_pauseparam *pause)
+{
+	unci_nl_exec(pause->cmd, dev, NULL, 0,
+			pause, sizeof(struct ethtool_pauseparam));
+}
+
+static int unci_set_pauseparam(struct net_device *dev,
+		struct ethtool_pauseparam *pause)
+{
+	return unci_nl_exec(pause->cmd, dev, pause,
+			sizeof(struct ethtool_pauseparam), NULL, 0);
+}
+
+static u32 unci_get_msglevel(struct net_device *dev)
+{
+	u32 data;
+	int ret;
+
+	ret = unci_nl_exec(ETHTOOL_GMSGLVL, dev, NULL, 0, &data, sizeof(u32));
+	if (ret < 0)
+		return 0;
+
+	return data;
+}
+
+static void unci_set_msglevel(struct net_device *dev, u32 data)
+{
+	unci_nl_exec(ETHTOOL_SMSGLVL, dev, &data, sizeof(u32), NULL, 0);
+}
+
+static int unci_get_regs_len(struct net_device *dev)
+{
+	int data;
+	int ret;
+
+	ret = unci_nl_exec(ETHTOOL_GREGS_LEN, dev, NULL, 0, &data, sizeof(int));
+	if (ret < 0)
+		return 0;
+
+	return data;
+}
+
+static void unci_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+		void *p)
+{
+	struct ethtool_regs regs_tmp;
+	u32 len = regs->len;
+
+	regs_tmp = *regs;
+
+	if (len > UNCI_ETHTOOL_MSG_LEN) {
+		len = UNCI_ETHTOOL_MSG_LEN;
+		regs_tmp.len = len;
+	}
+
+	unci_nl_exec(regs->cmd, dev, &regs_tmp, sizeof(struct ethtool_regs),
+			p, len);
+}
+
+static void unci_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	unci_nl_exec(ETHTOOL_GSTRINGS, dev, &stringset, sizeof(u32), data, 0);
+}
+
+static int unci_get_sset_count(struct net_device *dev, int sset)
+{
+	int data;
+	int ret;
+
+	ret = unci_nl_exec(ETHTOOL_GSSET_COUNT, dev, &sset, sizeof(int),
+			&data, sizeof(int));
+	if (ret < 0)
+		return 0;
+
+	return data;
+}
+
+static void unci_get_ethtool_stats(struct net_device *dev,
+		struct ethtool_stats *stats, u64 *data)
+{
+	unci_nl_exec(stats->cmd, dev, stats, sizeof(struct ethtool_stats),
+			data, stats->n_stats);
+}
+
+static const struct ethtool_ops unci_ethtool_ops = {
+	.begin			= unci_check_if_running,
+	.get_drvinfo		= unci_get_drvinfo,
+	.get_settings		= unci_get_settings,
+	.set_settings		= unci_set_settings,
+	.get_regs_len		= unci_get_regs_len,
+	.get_regs		= unci_get_regs,
+	.get_wol		= unci_get_wol,
+	.set_wol		= unci_set_wol,
+	.nway_reset		= unci_nway_reset,
+	.get_link		= unci_get_link,
+	.get_eeprom_len		= unci_get_eeprom_len,
+	.get_eeprom		= unci_get_eeprom,
+	.set_eeprom		= unci_set_eeprom,
+	.get_ringparam		= unci_get_ringparam,
+	.set_ringparam		= unci_set_ringparam,
+	.get_pauseparam		= unci_get_pauseparam,
+	.set_pauseparam		= unci_set_pauseparam,
+	.get_msglevel		= unci_get_msglevel,
+	.set_msglevel		= unci_set_msglevel,
+	.get_strings		= unci_get_strings,
+	.get_sset_count		= unci_get_sset_count,
+	.get_ethtool_stats	= unci_get_ethtool_stats,
+};
+
+void unci_set_ethtool_ops(struct net_device *netdev)
+{
+	netdev->ethtool_ops = &unci_ethtool_ops;
+}
diff --git a/lib/librte_eal/linuxapp/unci/unci_net.c b/lib/librte_eal/linuxapp/unci/unci_net.c
new file mode 100644
index 000000000..59dfd99f1
--- /dev/null
+++ b/lib/librte_eal/linuxapp/unci/unci_net.c
@@ -0,0 +1,217 @@ 
+/*-
+ * GPL LICENSE SUMMARY
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of version 2 of the GNU General Public License as
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;
+ *
+ *   The full GNU General Public License is included in this distribution
+ *   in the file called LICENSE.GPL.
+ *
+ *   Contact Information:
+ *   Intel Corporation
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <net/rtnetlink.h>
+
+#include "unci_dev.h"
+
+static int unci_net_init(struct net_device *dev)
+{
+	u8 mac[ETH_ALEN] = {0};
+
+	unci_nl_exec(RTE_UNCI_REQ_GET_MAC, dev, NULL, 0, mac, ETH_ALEN);
+	memcpy(dev->dev_addr, mac, dev->addr_len);
+	return 0;
+}
+
+static int unci_net_open(struct net_device *dev)
+{
+	/* DPDK port already started, stop it first */
+	unci_nl_exec(RTE_UNCI_REQ_STOP_PORT, dev, NULL, 0, NULL, 0);
+	unci_nl_exec(RTE_UNCI_REQ_START_PORT, dev, NULL, 0, NULL, 0);
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int unci_net_close(struct net_device *dev)
+{
+	unci_nl_exec(RTE_UNCI_REQ_STOP_PORT, dev, NULL, 0, NULL, 0);
+	netif_stop_queue(dev);
+	return 0;
+}
+
+static int unci_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	dev_kfree_skb(skb);
+	return NETDEV_TX_OK;
+}
+
+static void unci_net_change_rx_flags(struct net_device *dev, int flags)
+{
+	u32 on = 1;
+	u32 off = 0;
+
+	if (flags & IFF_PROMISC)
+		unci_nl_exec(RTE_UNCI_REQ_SET_PROMISC, dev,
+				dev->flags & IFF_PROMISC ?  &on : &off,
+				sizeof(u32), NULL, 0);
+
+	if (flags & IFF_ALLMULTI)
+		unci_nl_exec(RTE_UNCI_REQ_SET_ALLMULTI, dev,
+				dev->flags & IFF_ALLMULTI ?  &on : &off,
+				sizeof(u32), NULL, 0);
+}
+
+static int unci_net_set_mac(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+	int err;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	err = unci_nl_exec(RTE_UNCI_REQ_SET_MAC, dev, addr->sa_data,
+			dev->addr_len, NULL, 0);
+	if (err < 0)
+		return -EADDRNOTAVAIL;
+
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	return 0;
+}
+
+static int unci_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	return -EOPNOTSUPP;
+}
+
+/*
+ * Configuration changes (passed on by ifconfig)
+ */
+static int unci_net_config(struct net_device *dev, struct ifmap *map)
+{
+	if (dev->flags & IFF_UP)
+		return -EBUSY;
+
+	return -EOPNOTSUPP;
+}
+
+static int unci_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+	int err = 0;
+
+	err = unci_nl_exec(RTE_UNCI_REQ_CHANGE_MTU, dev, &new_mtu, sizeof(int),
+			NULL, 0);
+
+	if (err == 0)
+		dev->mtu = new_mtu;
+
+	return err;
+}
+
+static void unci_net_stats64(struct net_device *dev,
+		struct rtnl_link_stats64 *stats)
+{
+	int err;
+
+	err = unci_nl_exec(RTE_UNCI_REQ_GET_STATS, dev, NULL, 0,
+			stats, sizeof(struct rtnl_link_stats64));
+}
+
+#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE)
+static int unci_net_change_carrier(struct net_device *dev, bool new_carrier)
+{
+	if (new_carrier)
+		netif_carrier_on(dev);
+	else
+		netif_carrier_off(dev);
+	return 0;
+}
+#endif
+
+static const struct net_device_ops unci_net_netdev_ops = {
+	.ndo_init = unci_net_init,
+	.ndo_open = unci_net_open,
+	.ndo_stop = unci_net_close,
+	.ndo_start_xmit = unci_net_xmit,
+	.ndo_change_rx_flags = unci_net_change_rx_flags,
+	.ndo_set_mac_address = unci_net_set_mac,
+	.ndo_do_ioctl = unci_net_ioctl,
+	.ndo_set_config = unci_net_config,
+	.ndo_change_mtu = unci_net_change_mtu,
+	.ndo_get_stats64 = unci_net_stats64,
+#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE)
+	.ndo_change_carrier = unci_net_change_carrier,
+#endif
+};
+
+static void unci_net_setup(struct net_device *dev)
+{
+	struct unci_dev *unci;
+
+	ether_setup(dev);
+	dev->netdev_ops = &unci_net_netdev_ops;
+
+	unci = netdev_priv(dev);
+	init_completion(&unci->msg_received);
+
+	unci_set_ethtool_ops(dev);
+}
+
+static int unci_net_newlink(struct net *net, struct net_device *dev,
+		struct nlattr *tb[], struct nlattr *data[])
+{
+	struct unci_dev *unci = netdev_priv(dev);
+
+	if (data && data[IFLA_UNCI_PORTID])
+		unci->port_id = nla_get_u8(data[IFLA_UNCI_PORTID]);
+	else
+		unci->port_id = 0;
+
+	if (data && data[IFLA_UNCI_PID])
+		unci->pid = nla_get_u32(data[IFLA_UNCI_PID]);
+	else
+		unci->pid = 0;
+
+	return register_netdevice(dev);
+}
+
+static struct rtnl_link_ops unci_link_ops __read_mostly = {
+	.kind = UNCI_DEVICE,
+	.priv_size = sizeof(struct unci_dev),
+	.setup = unci_net_setup,
+	.maxtype = IFLA_UNCI_MAX,
+	.newlink = unci_net_newlink,
+};
+
+static int __init unci_init(void)
+{
+	unci_nl_init();
+	return rtnl_link_register(&unci_link_ops);
+}
+module_init(unci_init);
+
+static void __exit unci_exit(void)
+{
+	rtnl_link_unregister(&unci_link_ops);
+	unci_nl_release();
+}
+module_exit(unci_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Kernel Module for managing unci devices");
diff --git a/lib/librte_eal/linuxapp/unci/unci_nl.c b/lib/librte_eal/linuxapp/unci/unci_nl.c
new file mode 100644
index 000000000..68a7da5fc
--- /dev/null
+++ b/lib/librte_eal/linuxapp/unci/unci_nl.c
@@ -0,0 +1,220 @@ 
+/*-
+ * GPL LICENSE SUMMARY
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of version 2 of the GNU General Public License as
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;
+ *   The full GNU General Public License is included in this distribution
+ *   in the file called LICENSE.GPL.
+ *
+ *   Contact Information:
+ *   Intel Corporation
+ */
+
+#include <net/sock.h>
+
+#include "unci_dev.h"
+
+#define UNCI_CMD_TIMEOUT 500 /* ms */
+
+static struct ethtool_input_buffer {
+	int magic;
+	void *buffer;
+	size_t length;
+	struct completion *msg_received;
+	int *err;
+	u32 in_use;
+} ethtool_input_buffer;
+
+static struct sock *nl_sock;
+static struct mutex sync_lock;
+
+static int unci_input_buffer_register(int magic, void *buffer, size_t length,
+		struct completion *msg_received, int *err)
+{
+	if (!ethtool_input_buffer.in_use) {
+		ethtool_input_buffer.magic = magic;
+		ethtool_input_buffer.buffer = buffer;
+		ethtool_input_buffer.length = length;
+		ethtool_input_buffer.msg_received = msg_received;
+		ethtool_input_buffer.err = err;
+		ethtool_input_buffer.in_use = 1;
+		return 0;
+	}
+
+	return 1;
+}
+
+static void unci_input_buffer_unregister(int magic)
+{
+	if (ethtool_input_buffer.in_use) {
+		if (magic == ethtool_input_buffer.magic) {
+			ethtool_input_buffer.magic = -1;
+			ethtool_input_buffer.buffer = NULL;
+			ethtool_input_buffer.length = 0;
+			ethtool_input_buffer.msg_received = NULL;
+			ethtool_input_buffer.err = NULL;
+			ethtool_input_buffer.in_use = 0;
+		} else {
+			pr_err("Unregister magic mismatch\n");
+		}
+	}
+}
+
+static void nl_recv_user_request(struct unci_ethtool_msg *ethtool_msg)
+{
+	UNCI_DBG("Request from userspace received\n");
+}
+
+static void nl_recv_user_response(struct unci_ethtool_msg *ethtool_msg)
+{
+	struct completion *msg_received;
+	size_t recv_len;
+	size_t expected_len;
+
+	if (ethtool_input_buffer.in_use) {
+		if (ethtool_input_buffer.buffer != NULL) {
+			recv_len = ethtool_msg->output_buffer_len;
+			expected_len = ethtool_input_buffer.length;
+
+			memcpy(ethtool_input_buffer.buffer,
+					ethtool_msg->output_buffer,
+					ethtool_input_buffer.length);
+
+			if (ethtool_msg->err == 0 && recv_len != expected_len)
+				pr_info("Expected and received len not match "
+					"%zu - %zu\n", recv_len, expected_len);
+		}
+
+		*ethtool_input_buffer.err = ethtool_msg->err;
+		msg_received = ethtool_input_buffer.msg_received;
+		unci_input_buffer_unregister(ethtool_input_buffer.magic);
+		complete(msg_received);
+	}
+}
+
+static void nl_recv(struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh;
+	struct unci_ethtool_msg ethtool_msg;
+
+	nlh = (struct nlmsghdr *)skb->data;
+
+	memcpy(&ethtool_msg, NLMSG_DATA(nlh), sizeof(struct unci_ethtool_msg));
+	UNCI_DBG("CMD: %u\n", ethtool_msg.cmd_id);
+
+	if (ethtool_msg.flag & UNCI_MSG_FLAG_REQUEST) {
+		nl_recv_user_request(&ethtool_msg);
+		return;
+	}
+
+	nl_recv_user_response(&ethtool_msg);
+}
+
+static int unci_nl_send(u32 cmd_id, u8 port_id, u32 pid, void *in_data,
+		size_t in_data_len)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	struct unci_ethtool_msg ethtool_msg;
+
+	if (pid == 0)
+		return -1;
+
+	memset(&ethtool_msg, 0, sizeof(struct unci_ethtool_msg));
+	ethtool_msg.cmd_id = cmd_id;
+	ethtool_msg.port_id = port_id;
+
+	if (in_data) {
+		if (in_data_len == 0 || in_data_len > UNCI_ETHTOOL_MSG_LEN)
+			return -EINVAL;
+		ethtool_msg.input_buffer_len = in_data_len;
+		memcpy(ethtool_msg.input_buffer, in_data, in_data_len);
+	}
+
+	skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct unci_ethtool_msg)),
+			GFP_ATOMIC);
+	nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, sizeof(struct unci_ethtool_msg),
+			0);
+
+	NETLINK_CB(skb).dst_group = 0;
+
+	memcpy(nlmsg_data(nlh), &ethtool_msg, sizeof(struct unci_ethtool_msg));
+
+	nlmsg_unicast(nl_sock, skb, pid);
+	UNCI_DBG("Sent cmd:%u port:%u pid:%u\n", cmd_id, port_id, pid);
+
+	return 0;
+}
+
+int unci_nl_exec(u32 cmd, struct net_device *dev, void *in_data,
+		size_t in_data_len, void *out_data, size_t out_data_len)
+{
+	struct unci_dev *unci = netdev_priv(dev);
+	int err = -EINVAL;
+	int ret;
+
+	if (out_data_len > UNCI_ETHTOOL_MSG_LEN) {
+		pr_err("Message is too big to receive:%zu\n", out_data_len);
+		return err;
+	}
+
+	mutex_lock(&sync_lock);
+	ret = unci_input_buffer_register(cmd, out_data, out_data_len,
+			&unci->msg_received, &err);
+	if (ret) {
+		mutex_unlock(&sync_lock);
+		return -EINVAL;
+	}
+
+	ret = unci_nl_send(cmd, unci->port_id, unci->pid, in_data, in_data_len);
+	if (ret) {
+		unci_input_buffer_unregister(ethtool_input_buffer.magic);
+		mutex_unlock(&sync_lock);
+		return ret;
+	}
+
+	ret = wait_for_completion_interruptible_timeout(&unci->msg_received,
+			 msecs_to_jiffies(UNCI_CMD_TIMEOUT));
+	if (ret == 0 || err < 0) {
+		unci_input_buffer_unregister(ethtool_input_buffer.magic);
+		mutex_unlock(&sync_lock);
+		if (ret == 0) { /* timeout */
+			unci->nb_timedout_msg++;
+			pr_info("Command timed-out for port:%u cmd:%u (%u)\n",
+				unci->port_id, cmd, unci->nb_timedout_msg);
+			return -EINVAL;
+		}
+		UNCI_DBG("Command return error for port:%d cmd:%d err:%d\n",
+				unci->port_id, cmd, err);
+		return err;
+	}
+	mutex_unlock(&sync_lock);
+
+	return 0;
+}
+
+static struct netlink_kernel_cfg cfg = {
+	.input = nl_recv,
+};
+
+void unci_nl_init(void)
+{
+	nl_sock = netlink_kernel_create(&init_net, UNCI_NL_GRP, &cfg);
+	mutex_init(&sync_lock);
+}
+
+void unci_nl_release(void)
+{
+	netlink_kernel_release(nl_sock);
+}