Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net] failover: eliminate callback hell
From: David Miller @ 2018-06-05 18:14 UTC (permalink / raw)
  To: stephen
  Cc: sridhar.samudrala, kys, haiyangz, mst, alexander.h.duyck, jiri,
	netdev, sthemmin, jesse.brandeburg
In-Reply-To: <20180605104510.611bd247@xeon-e3>

From: Stephen Hemminger <stephen@networkplumber.org>
Date: Tue, 5 Jun 2018 10:45:10 -0700

> I said it wasn't tested. Not surprising. Don't have a version of KVM
> that supports standby (and not going to build KVM from scratch for
> this).

It would definitely help me if you put "RFC" in the subject line
for patches which aren't tested :-)

Thanks.

^ permalink raw reply

* Re: [PATCH net] failover: eliminate callback hell
From: Stephen Hemminger @ 2018-06-05 17:45 UTC (permalink / raw)
  To: Samudrala, Sridhar
  Cc: kys, haiyangz, davem, mst, Alexander H, Jiri Pirko, netdev,
	Stephen Hemminger, Brandeburg, Jesse
In-Reply-To: <0d97888a-9eb9-a3a1-f96c-39367dc11a00@intel.com>

On Tue, 5 Jun 2018 10:22:13 -0700
"Samudrala, Sridhar" <sridhar.samudrala@intel.com> wrote:

> On 6/4/2018 8:42 PM, Stephen Hemminger wrote:
> > The net failover should be a simple library, not a virtual
> > object with function callbacks (see callback hell).
> > The code is simpler is smaller both for the netvsc and virtio use case.  
> 
> I quickly tried this patch and it breaks virtio-net in standby mode.
> I don't see failover netdev, unloading virtio-net causes a crash.

I said it wasn't tested. Not surprising. Don't have a version of KVM
that supports standby (and not going to build KVM from scratch for this).

> 
> With these changes, there is very minimal code that is shared between
> netvsc and virtio-net. The notifier and event handling code and the
> lookup_bymac routines are now duplicated in both the drivers. I thought
> we wanted to keep this code common between the 2 drivers and we went through
> multiple revisions to make sure that it works with both netvsc's 2 netdev
> and virtio-net's 3 netdev models.

The sharing is what needs to be shared; it turns out not much
is common really. The total code size is less with this version.
Also, since even with nested virtualization, both drivers will not
be present on same guest at same time.

> The reason for the indirect ops is to support these 2 different models and
> i am not sure if the overhead of the callbacks is that significant considering
> that they are not called in the hot path.

The callbacks invert the functionality. It makes everything bottom up.

^ permalink raw reply

* Re: Qualcomm rmnet driver and qmi_wwan
From: Subash Abhinov Kasiviswanathan @ 2018-06-05 17:38 UTC (permalink / raw)
  To: Dan Williams, Daniele Palmas; +Cc: netdev
In-Reply-To: <9786ab4bf4e425566f55a0654911ab788f3f1a38.camel@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 2834 bytes --]

On 2018-06-05 08:54, Dan Williams wrote:
> On Tue, 2018-06-05 at 11:38 +0200, Daniele Palmas wrote:
>> Hi,
>> 
>> 2018-02-21 20:47 GMT+01:00 Subash Abhinov Kasiviswanathan
>> <subashab@codeaurora.org>:
>> > On 2018-02-21 04:38, Daniele Palmas wrote:
>> > >
>> > > Hello,
>> > >
>> > > in rmnet kernel documentation I read:
>> > >
>> > > "This driver can be used to register onto any physical network
>> > > device in
>> > > IP mode. Physical transports include USB, HSIC, PCIe and IP
>> > > accelerator."
>> > >
>> > > Does this mean that it can be used in association with the
>> > > qmi_wwan
>> > > driver?
>> > >
>> > > If yes, can someone give me an hint on the steps to follow?
>> > >
>> > > If not, does anyone know if it is possible to modify qmi_wwan in
>> > > order
>> > > to take advantage of the features provided by the rmnet driver?
>> > >
>> > > In this case hint on the changes for modifying qmi_wwan are
>> > > welcome.
>> > >
>> > > Thanks in advance,
>> > > Daniele
>> >
>> >
>> > Hi
>> >
>> > I havent used qmi_wwan so the following comment is based on code
>> > inspection.
>> > qmimux_register_device() is creating qmimux devices with usb net
>> > device as
>> > real_dev. The Multiplexing and aggregation header (qmimux_hdr) is
>> > stripped
>> > off
>> > in qmimux_rx_fixup() and the packet is passed on to stack.
>> >
>> > You could instead create rmnet devices with the usb netdevice as
>> > real dev.
>> > The packets from the usb net driver can be queued to network stack
>> > directly
>> > as rmnet driver will setup a RX handler. rmnet driver will process
>> > the
>> > packets
>> > further and then queue to network stack.
>> >
>> 
>> in kernel documentation I read that rmnet user space configuration is
>> done through librmnetctl available at
>> 
>> https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource
>> /dataservices/tree/rmnetctl
>> 
>> However it seems to me that this is a bit outdated (e.g. it does not
>> properly build since it is looking for kernel header
>> linux/rmnet_data.h that, as far as I understand, is no more present).
>> 
>> Is there available a more recent version of the tool?

Hi Daniele

The attached patch should have an updated version of the tool.
Usage -

rmnetcli -n newlink wwan0 rmnet0 1 1
where wwan0 is the physical device
rmnet0 is the virtual device to be created
1 is the mux id
the other 1 is the flag to configure DL de-aggregation by default

To delete a device -

ip link delete rmnet0

> 
> I'd expect that somebody (Subash?) would add support for the
> rmnet/qmimux options to iproute2 via 'ip link' like exists for most
> other device types.

Hi Dan

Yes, I can do that and update the documentation to point to using 
iproute2.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-librmnetctl-add-initial-library-code.patch --]
[-- Type: text/x-diff; name=0001-librmnetctl-add-initial-library-code.patch, Size: 97800 bytes --]

From 94abb589a8e60c49d9cc4598a27b50c9f757b623 Mon Sep 17 00:00:00 2001
From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Date: Tue, 5 Jun 2018 11:20:56 -0600
Subject: [PATCH] librmnetctl: add initial library code

Add initial snapshot of Rmnet Control library for
upstream rmnet driver.

---
 rmnetlib/Makefile.am            |    2 +
 rmnetlib/cli/Makefile.am        |   12 +
 rmnetlib/cli/rmnetcli.c         |  502 ++++++++++++++
 rmnetlib/cli/rmnetcli.h         |   61 ++
 rmnetlib/inc/librmnetctl.h      |  604 +++++++++++++++++
 rmnetlib/inc/librmnetctl_hndl.h |   65 ++
 rmnetlib/src/Makefile.am        |   14 +
 rmnetlib/src/librmnetctl.c      | 1369 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 2629 insertions(+)
 create mode 100644 rmnetlib/Makefile.am
 create mode 100644 rmnetlib/cli/Makefile.am
 create mode 100644 rmnetlib/cli/rmnetcli.c
 create mode 100644 rmnetlib/cli/rmnetcli.h
 create mode 100644 rmnetlib/inc/librmnetctl.h
 create mode 100644 rmnetlib/inc/librmnetctl_hndl.h
 create mode 100644 rmnetlib/src/Makefile.am
 create mode 100644 rmnetlib/src/librmnetctl.c

diff --git a/rmnetlib/Makefile.am b/rmnetlib/Makefile.am
new file mode 100644
index 0000000..7cafa82
--- /dev/null
+++ b/rmnetlib/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS=src cli
+
diff --git a/rmnetlib/cli/Makefile.am b/rmnetlib/cli/Makefile.am
new file mode 100644
index 0000000..499ef88
--- /dev/null
+++ b/rmnetlib/cli/Makefile.am
@@ -0,0 +1,12 @@
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_CFLAGS += -Wall -Werror -Wundef -Wstrict-prototypes -Wno-trigraphs -g
+AM_CFLAGS += -I./../inc
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
+rmnetcli_SOURCES = rmnetcli.c
+bin_PROGRAMS = rmnetcli
+requiredlibs = ../src/librmnetctl.la
+rmnetcli_LDADD = $(requiredlibs)
+LOCAL_MODULE := librmnetctl
+LOCAL_PRELINK_MODULE := false
+include $(BUILD_SHARED_LIBRARY)
diff --git a/rmnetlib/cli/rmnetcli.c b/rmnetlib/cli/rmnetcli.c
new file mode 100644
index 0000000..dc81054
--- /dev/null
+++ b/rmnetlib/cli/rmnetcli.c
@@ -0,0 +1,502 @@
+/******************************************************************************
+
+			R M N E T C L I . C
+
+Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. 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 The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+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.
+
+******************************************************************************/
+
+/******************************************************************************
+
+  @file    rmnetcli.c
+  @brief   command line interface to expose rmnet control API's
+
+  DESCRIPTION
+  File containing implementation of the command line interface to expose the
+  rmnet control configuration .
+
+******************************************************************************/
+
+/*===========================================================================
+				INCLUDE FILES
+===========================================================================*/
+
+#include <sys/socket.h>
+#include <stdint.h>
+#include <linux/netlink.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "rmnetcli.h"
+#include "librmnetctl.h"
+
+#define RMNET_MAX_STR_LEN  16
+
+#define _RMNETCLI_CHECKNULL(X)		do { if (!X) {                         \
+print_rmnet_api_status(RMNETCTL_INVALID_ARG, RMNETCTL_CFG_FAILURE_NO_COMMAND); \
+				rmnetctl_cleanup(handle);                      \
+				return RMNETCTL_INVALID_ARG;                   \
+		} } while (0);
+#define _STRTOUI32(X)           (uint32_t)strtoul(X, NULL, 0)
+#define _STRTOUI16(X)           (uint16_t)strtoul(X, NULL, 0)
+#define _STRTOUI8(X)           (uint8_t)strtoul(X, NULL, 0)
+#define _STRTOI32(X)           (int32_t)strtol(X, NULL, 0)
+
+#define _5TABS 		"\n\t\t\t\t\t"
+#define _2TABS 		"\n\t\t"
+
+/*!
+* @brief Contains a list of error message from CLI
+*/
+char rmnetcfg_error_code_text
+[RMNETCFG_TOTAL_ERR_MSGS][RMNETCTL_ERR_MSG_SIZE] = {
+	"Help option Specified",
+	"ERROR: No\\Invalid command was specified\n",
+	"ERROR: Could not allocate buffer for Egress device\n"
+};
+
+/*!
+* @brief Method to display the syntax for the commands
+* @details Displays the syntax and usage for the commands
+* @param void
+* @return void
+*/
+static void rmnet_api_usage(void)
+{
+	printf("RmNet API Usage:\n\n");
+	printf("rmnetcli help                            Displays this help\n");
+	printf("\n");
+	printf("rmnetcli assocnetdev <dev_name>          Registers the RmNet");
+	printf(_5TABS" data driver on a particular");
+	printf(_5TABS" device.dev_name cannot");
+	printf(_5TABS" be larger than 15");
+	printf(_5TABS" characters. Returns");
+	printf(_5TABS" the status code.\n\n");
+	printf("rmnetcli unassocnetdev <dev_name>        Unregisters the");
+	printf(_5TABS" RmNet data driver on a particular");
+	printf(_5TABS" device. dev_name cannot");
+	printf(_5TABS" be larger than 15");
+	printf(_5TABS" characters. Returns");
+	printf(_5TABS" the status code.\n\n");
+	printf("rmnetcli getnetdevassoc <dev_name>       Get if the RmNet");
+	printf(_5TABS" data driver is registered on");
+	printf(_5TABS" a particular device.");
+	printf(_5TABS" dev_name cannot be");
+	printf(_5TABS" larger than 15");
+	printf(_5TABS" characters. Returns 1");
+	printf(_5TABS" if is registered and");
+	printf(_5TABS" 0 if it is not");
+	printf(_5TABS" registered\n\n");
+	printf("rmnetcli setledf <egress_flags>          Sets the egress data");
+	printf(_2TABS" <agg_size>              format for a particular link.");
+	printf(_2TABS" <agg_count>             dev_name cannot be larger");
+	printf(_2TABS" <dev_name>              than 15 characters.");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli getledf <dev_name>              Gets the egress data");
+	printf(_5TABS" format for a particular link.");
+	printf(_5TABS" dev_name cannot be larger");
+	printf(_5TABS" than 15. Returns the 4");
+	printf(_5TABS" byte unsigned integer");
+	printf(_5TABS" egress_flags\n\n");
+	printf("rmnetcli setlidf <ingress_flags>         Sets the ingress");
+	printf(_2TABS" <tail_spacing>          data format for a particular");
+	printf(_2TABS" <dev_name>              link. ingress_flags is 4");
+	printf(_5TABS" byte unsigned integer.");
+	printf(_5TABS" tail_spacing is a one.");
+	printf(_5TABS" byte unsigned integer.");
+	printf(_5TABS" dev_name cannot be");
+	printf(_5TABS" larger than 15.");
+	printf(_5TABS" characters. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli getlidf <dev_name>              Gets the ingress");
+	printf(_5TABS" data format for a particular");
+	printf(_5TABS" link. dev_name cannot be");
+	printf(_5TABS" larger than 15. Returns");
+	printf(_5TABS" the 4 byte unsigned");
+	printf(_5TABS" integer ingress_flags\n\n");
+	printf("rmnetcli setlepc <logical_ep_id>         Sets the logical");
+	printf(_2TABS" <rmnet_mode>            endpoint configuration for");
+	printf(_2TABS" <dev_name>              a particular link.");
+	printf(_2TABS" <egress_dev_name>       logical_ep_id are 32bit");
+	printf(_5TABS" integers from -1 to 31.");
+	printf(_5TABS" rmnet_mode is a 1 byte");
+	printf(_5TABS" unsigned integer of");
+	printf(_5TABS" value none, vnd or");
+	printf(_5TABS" bridged. dev_name");
+	printf(_5TABS" and egress_dev_name");
+	printf(_5TABS" cannot be larger");
+	printf(_5TABS" than 15 characters");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli unsetlepc <logical_ep_id>       Un-sets the logical");
+	printf(_2TABS"  <dev_name>              endpoint configuration for");
+	printf(_5TABS" a particular link.");
+	printf(_5TABS" integers from -1 to 31.");
+	printf(_5TABS" dev_name cannot be larger");
+	printf(_5TABS" than 15 characters");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli getlepc <logical_ep_id>         Sets the logical");
+	printf(_2TABS" <dev_name>              endpoint configuration for a");
+	printf(_5TABS" particular link.");
+	printf(_5TABS" logical_ep_id are 32bit");
+	printf(_5TABS" integers from -1 to 31.");
+	printf(_5TABS" Returns the rmnet_mode");
+	printf(_5TABS" and egress_dev_name.");
+	printf(_5TABS" rmnet_mode is a 1");
+	printf(_5TABS" byte unsigned integer");
+	printf(_5TABS" of value none, vnd or");
+	printf(_5TABS" bridged. dev_name and");
+	printf(_5TABS" egress_dev_name cannot be");
+	printf(_5TABS" larger than 15 ");
+	printf(_5TABS" characters. Returns the");
+	printf(_5TABS" status code\n\n");
+	printf("rmnetcli newvnd <dev_id>                 Creates a new");
+	printf(_5TABS" virtual network device node.");
+	printf(_5TABS" dev_id is an int");
+	printf(_5TABS" less than 32. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli newvndprefix <dev_id> <name_prefix>   Creates");
+	printf(_5TABS" virtual network device node.");
+	printf(_5TABS" dev_id is an int");
+	printf(_5TABS" less than 32. Prefix");
+	printf(_5TABS" must be less than");
+	printf(_5TABS" 15 chars. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli newvndname <dev_id> <name_prefix>   Creates");
+	printf(_5TABS" virtual network device node.");
+	printf(_5TABS" dev_id is an int");
+	printf(_5TABS" less than 32. Name");
+	printf(_5TABS" must be less than");
+	printf(_5TABS" 15 chars. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli getvndname <dev_id>              Get name of");
+	printf(_5TABS" network device node from id\n\n");
+	printf("rmnetcli freevnd <dev_id>              Removes virtual");
+	printf(_5TABS" network device node. dev_name");
+	printf(_5TABS" cannot be larger than 15.");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli addvnctcflow <dev_id>            Add a modem flow");
+	printf(_2TABS" <mdm_flow_hndl>         handle - tc flow handle");
+	printf(_2TABS" <tc_flow_hndl>          mapping for a virtual network");
+	printf(_2TABS" device node\n\n");
+	printf("rmnetcli delvnctcflow <dev_id>            Delete a modem flow");
+	printf(_2TABS" <mdm_flow_hndl>         handle - tc flow handle");
+	printf(_2TABS" <tc_flow_hndl>          mapping for a virtual network");
+	printf(_2TABS" device node\n\n");
+	printf("**************************\n");
+	printf("RmNet RTM_NETLINK API Usage:\n\n");
+	printf("rmnetcli -n newlink  <dev_id>            Add a vnd w/ newlink");
+	printf(_2TABS" <vnd>                   string - vnd device_name");
+	printf(_2TABS" <vnd id>                int - new vnd id");
+	printf(_2TABS" [flags]                 int - starting flag config\n\n");
+	printf("rmnetcli -n changelink  <dev_id>         Change a vnd's flags");
+	printf(_2TABS" <vnd>                   string - vnd device_name");
+	printf(_2TABS" <vnd id>                int - new vnd id");
+	printf(_2TABS" <flags>                 int - new flag config\n\n");
+	printf("rmnetcli -n dellink <dev_name>           Delete a vnd");
+	printf(_2TABS"                         by inputting dev name\n\n");
+	printf("rmnetcli -n bridgelink  <dev_name>       Bridge a vnd and a dev");
+	printf(_2TABS" <vnd id>                by specifying dev id and vnd id\n\n");
+
+}
+
+static void print_rmnetctl_lib_errors(uint16_t error_number)
+{
+	if ((error_number > RMNETCTL_API_SUCCESS) &&
+		(error_number < RMNETCTL_API_ERR_ENUM_LENGTH)) {
+		printf("%s", rmnetctl_error_code_text[error_number]);
+	}
+	if ((error_number >= RMNETCFG_ERR_NUM_START) &&
+	(error_number < RMNETCFG_ERR_NUM_START + RMNETCFG_TOTAL_ERR_MSGS)) {
+		printf("%s", rmnetcfg_error_code_text
+			[error_number - RMNETCFG_ERR_NUM_START]);
+		if ((error_number == RMNETCTL_CFG_SUCCESS_HELP_COMMAND) ||
+			(error_number == RMNETCTL_CFG_FAILURE_NO_COMMAND))
+			rmnet_api_usage();
+	}
+}
+
+/*!
+* @brief Method to check the error numbers generated from API calls
+* @details Displays the error messages based on each error code
+* @param error_number Error number returned from the API and the CLI
+* @return void
+*/
+static void print_rmnet_api_status(int return_code, uint16_t error_number)
+{
+	if (return_code == RMNETCTL_SUCCESS)
+		printf("SUCCESS\n");
+	else if (return_code == RMNETCTL_LIB_ERR) {
+		printf("LIBRARY ");
+		print_rmnetctl_lib_errors(error_number);
+	} else if (return_code == RMNETCTL_KERNEL_ERR) {
+		if (error_number < RMNETCTL_API_ERR_ENUM_LENGTH)
+			printf("KERNEL ERROR: System or rmnet error %d\n",
+			       error_number);
+	}
+	else if (return_code == RMNETCTL_INVALID_ARG)
+		printf("INVALID_ARG\n");
+}
+
+/*!
+* @brief Method to make the API calls
+* @details Checks for each type of parameter and calls the appropriate
+* function based on the number of parameters and parameter type
+* @param argc Number of arguments which vary based on the commands
+* @param argv Value of the arguments which vary based on the commands
+* @return RMNETCTL_SUCCESS if successful. Relevant data might be printed
+* based on the message type
+* @return RMNETCTL_LIB_ERR if there was a library error. Error code will be
+* printed
+* @return RMNETCTL_KERNEL_ERR if there was a error in the kernel. Error code will be
+* printed
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+
+static int rmnet_api_call(int argc, char *argv[])
+{
+	struct rmnetctl_hndl_s *handle = NULL;
+	uint16_t error_number = RMNETCTL_CFG_FAILURE_NO_COMMAND;
+	int return_code = RMNETCTL_LIB_ERR;
+	uint32_t flags = 0;
+	uint8_t rmnet_mode;
+	char *egress_dev_name;
+	if ((!argc) || (!*argv)) {
+		print_rmnet_api_status(RMNETCTL_LIB_ERR,
+		RMNETCTL_CFG_FAILURE_NO_COMMAND);
+		return RMNETCTL_LIB_ERR;
+	}
+	if (!strcmp(*argv, "help")) {
+		print_rmnet_api_status(RMNETCTL_LIB_ERR,
+		RMNETCTL_CFG_SUCCESS_HELP_COMMAND);
+		return RMNETCTL_LIB_ERR;
+	}
+
+	if (!strcmp(*argv, "-n")) {
+		return_code = rtrmnet_ctl_init(&handle, &error_number);
+		if (return_code != RMNETCTL_SUCCESS) {
+			print_rmnet_api_status(return_code, error_number);
+			return RMNETCTL_LIB_ERR;
+		}
+		error_number = RMNETCTL_CFG_FAILURE_NO_COMMAND;
+		return_code = RMNETCTL_LIB_ERR;
+		argv++;
+		argc--;
+		if ((!argc) || (!*argv)) {
+			print_rmnet_api_status(RMNETCTL_LIB_ERR,
+			RMNETCTL_CFG_FAILURE_NO_COMMAND);
+			return RMNETCTL_LIB_ERR;
+		}
+		if (!strcmp(*argv, "newlink")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+			_RMNETCLI_CHECKNULL(argv[2]);
+			_RMNETCLI_CHECKNULL(argv[3]);
+			/* If optional flag was used pass it on*/
+			if (argv[4])
+				flags = _STRTOI32(argv[4]);
+
+			return_code = rtrmnet_ctl_newvnd(handle, argv[1],
+							 argv[2],
+							 &error_number,
+							 _STRTOI32(argv[3]),
+							 flags);
+		} else if (!strcmp(*argv, "changelink")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+			_RMNETCLI_CHECKNULL(argv[2]);
+			_RMNETCLI_CHECKNULL(argv[3]);
+			_RMNETCLI_CHECKNULL(argv[4]);
+
+			return_code = rtrmnet_ctl_changevnd(handle, argv[1],
+							    argv[2],
+							    &error_number,
+							    _STRTOI32(argv[3]),
+							    _STRTOI32(argv[4]));
+		} else if (!strcmp(*argv, "dellink")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+				return_code = rtrmnet_ctl_delvnd(handle, argv[1],
+								 &error_number);
+		} else if (!strcmp(*argv, "bridge")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+			_RMNETCLI_CHECKNULL(argv[2]);
+			return_code = rtrmnet_ctl_bridgevnd(handle, argv[1],
+							    argv[2],
+							    &error_number);
+		}
+		goto end;
+	} else {
+		return_code = rmnetctl_init(&handle, &error_number);
+		if (return_code != RMNETCTL_SUCCESS) {
+			print_rmnet_api_status(return_code, error_number);
+			return RMNETCTL_LIB_ERR;
+		}
+
+	}
+	error_number = RMNETCTL_CFG_FAILURE_NO_COMMAND;
+	return_code = RMNETCTL_LIB_ERR;
+	if (!strcmp(*argv, "assocnetdev")) {
+		return_code = rmnet_associate_network_device(handle,
+		argv[1], &error_number, RMNETCTL_DEVICE_ASSOCIATE);
+	} else if (!strcmp(*argv, "unassocnetdev")) {
+		return_code = rmnet_associate_network_device(handle,
+		argv[1], &error_number, RMNETCTL_DEVICE_UNASSOCIATE);
+	} else if (!strcmp(*argv, "getnetdevassoc")) {
+		int register_status;
+		return_code = rmnet_get_network_device_associated(handle,
+		argv[1], &register_status, &error_number);
+		if (return_code == RMNETCTL_SUCCESS)
+			printf("register_status is %d\n", register_status);
+	} else if (!strcmp(*argv, "getledf")) {
+		uint32_t egress_flags;
+		uint16_t agg_size, agg_count;
+		return_code = rmnet_get_link_egress_data_format(handle,
+		argv[1], &egress_flags, &agg_size, &agg_count, &error_number);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("egress_flags is %u\n", egress_flags);
+			printf("agg_size is %u\n", agg_size);
+			printf("agg_count is %u\n", agg_count);
+		}
+	} else if (!strcmp(*argv, "getlidf")) {
+		uint32_t ingress_flags;
+		uint8_t  tail_spacing;
+		return_code = rmnet_get_link_ingress_data_format_tailspace(
+		handle, argv[1], &ingress_flags, &tail_spacing, &error_number);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("ingress_flags is %u\n", ingress_flags);
+			printf("tail_spacing is %u\n", tail_spacing);
+		}
+	} else if (!strcmp(*argv, "newvndprefix")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		return_code = rmnet_new_vnd_prefix(handle,
+		_STRTOUI32(argv[1]), &error_number, RMNETCTL_NEW_VND, argv[2]);
+	} else if (!strcmp(*argv, "newvndname")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		return_code = rmnet_new_vnd_name(handle,
+		_STRTOUI32(argv[1]), &error_number, argv[2]);
+	} else if (!strcmp(*argv, "newvnd")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_new_vnd(handle,
+		_STRTOUI32(argv[1]), &error_number, RMNETCTL_NEW_VND);
+	} else if (!strcmp(*argv, "getvndname")) {
+		char buffer[32];
+		memset(buffer, 0, 32);
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_get_vnd_name(handle, _STRTOUI32(argv[1]),
+			           &error_number, buffer, 32);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("VND name: %s\n", buffer);
+		}
+	} else if (!strcmp(*argv, "freevnd")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_new_vnd(handle,
+		_STRTOUI32(argv[1]), &error_number, RMNETCTL_FREE_VND);
+	} else if (!strcmp(*argv, "setlidf")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_set_link_ingress_data_format_tailspace(
+		handle, _STRTOUI32(argv[1]), _STRTOUI8(argv[2]), argv[3],
+		&error_number);
+	} else if (!strcmp(*argv, "delvnctcflow")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_add_del_vnd_tc_flow(handle,
+		_STRTOUI32(argv[1]), _STRTOUI32(argv[2]), _STRTOUI32(argv[3]),
+		RMNETCTL_DEL_FLOW, &error_number);
+	} else if (!strcmp(*argv, "getlepc")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		egress_dev_name = NULL;
+		egress_dev_name = (char *)malloc(RMNET_MAX_STR_LEN
+		* sizeof(char));
+		if (!egress_dev_name) {
+			print_rmnet_api_status(RMNETCTL_LIB_ERR,
+			RMNETCTL_CFG_FAILURE_EGRESS_DEV_NAME_NULL);
+			rmnetctl_cleanup(handle);
+			return RMNETCTL_LIB_ERR;
+		}
+		return_code = rmnet_get_logical_ep_config(handle,
+		_STRTOI32(argv[1]), argv[2], &rmnet_mode,
+		&egress_dev_name, RMNET_MAX_STR_LEN, &error_number);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("rmnet_mode is %u\n", rmnet_mode);
+			printf("egress_dev_name is %s\n", egress_dev_name);
+		}
+		free(egress_dev_name);
+	} else if (!strcmp(*argv, "addvnctcflow")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_add_del_vnd_tc_flow(handle,
+		_STRTOUI32(argv[1]), _STRTOUI32(argv[2]), _STRTOUI32(argv[3]),
+		RMNETCTL_ADD_FLOW, &error_number);
+	} else if (!strcmp(*argv, "setledf")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_set_link_egress_data_format(handle,
+		_STRTOUI32(argv[1]), _STRTOUI16(argv[2]), _STRTOUI16(argv[3]),
+		argv[4], &error_number);
+	} else if (!strcmp(*argv, "setlepc")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		return_code = rmnet_set_logical_ep_config(handle,
+		_STRTOI32(argv[1]), _STRTOUI8(argv[2]), argv[3], argv[4],
+		&error_number);
+	} else if (!strcmp(*argv, "unsetlepc")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_unset_logical_ep_config(handle,
+		_STRTOI32(argv[1]), argv[2], &error_number);
+	}
+end:
+	print_rmnet_api_status(return_code, error_number);
+	(void)rtrmnet_ctl_deinit(handle);
+	return return_code;
+}
+
+/*!
+* @brief Method which serves as en entry point to the rmnetcli function
+* @details Entry point for the RmNet Netlink API. This is the command line
+* interface for the RmNet API
+* @param argc Number of arguments which vary based on the commands
+* @param argv Value of the arguments which vary based on the commands
+* @return RMNETCTL_SUCCESS if successful. Relevant data might be printed
+* based on the message type
+* @return RMNETCTL_LIB_ERR if there was a library error. Error code will be
+* printed
+* @return RMNETCTL_KERNEL_ERR if there was a error in the kernel. Error code will be
+* printed
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int main(int argc, char *argv[])
+{
+	argc--;
+	argv++;
+	return rmnet_api_call(argc, argv);
+}
diff --git a/rmnetlib/cli/rmnetcli.h b/rmnetlib/cli/rmnetcli.h
new file mode 100644
index 0000000..6375082
--- /dev/null
+++ b/rmnetlib/cli/rmnetcli.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+
+			  R M N E T C L I . H
+
+Copyright (c) 2013, 2015 The Linux Foundation. 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 The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+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.
+
+******************************************************************************/
+
+/******************************************************************************
+
+  @file	   rmnetcli.h
+  @brief   headers for the command line interface to expose rmnet control API's
+
+  DESCRIPTION
+  Header file containing definition for the command line interface to expose
+  rmnet control API's
+
+******************************************************************************/
+
+#ifndef RMNETCLI_H
+#define RMNETCLI_H
+
+/* Print the help for the commands since the help flag was used. */
+#define RMNETCTL_CFG_SUCCESS_HELP_COMMAND 100
+/* No/invalid API call was specified. So return an error. */
+#define RMNETCTL_CFG_FAILURE_NO_COMMAND 101
+/* The buffer for egress device name was NULL */
+#define RMNETCTL_CFG_FAILURE_EGRESS_DEV_NAME_NULL 102
+
+/* This should always be the value of the starting element */
+#define RMNETCFG_ERR_NUM_START 100
+
+/* This should always be the total number of error message from CLI */
+#define RMNETCFG_TOTAL_ERR_MSGS 3
+
+#endif /* not defined RMNETCLI_H */
diff --git a/rmnetlib/inc/librmnetctl.h b/rmnetlib/inc/librmnetctl.h
new file mode 100644
index 0000000..3d622bf
--- /dev/null
+++ b/rmnetlib/inc/librmnetctl.h
@@ -0,0 +1,604 @@
+/******************************************************************************
+
+			  L I B R M N E T C T L . H
+
+Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. 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 The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+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.
+
+******************************************************************************/
+
+/*!
+*  @file    librmnetctl.h
+*  @brief   rmnet control API's header file
+*/
+
+#ifndef LIBRMNETCTL_H
+#define LIBRMNETCTL_H
+
+/* RMNET API succeeded */
+#define RMNETCTL_SUCCESS 0
+/* RMNET API encountered an error while executing within the library. Check the
+* error code in this case */
+#define RMNETCTL_LIB_ERR 1
+/* RMNET API encountered an error while executing in the kernel. Check the
+* error code in this case */
+#define RMNETCTL_KERNEL_ERR 2
+/* RMNET API encountered an error because of invalid arguments*/
+#define RMNETCTL_INVALID_ARG 3
+
+/* Flag to associate a network device*/
+#define RMNETCTL_DEVICE_ASSOCIATE 1
+/* Flag to unassociate a network device*/
+#define RMNETCTL_DEVICE_UNASSOCIATE 0
+/* Flag to create a new virtual network device*/
+#define RMNETCTL_NEW_VND 1
+/* Flag to free a new virtual network device*/
+#define RMNETCTL_FREE_VND 0
+/* Flag to add a new flow*/
+#define RMNETCTL_ADD_FLOW 1
+/* Flag to delete an existing flow*/
+#define RMNETCTL_DEL_FLOW 0
+
+enum rmnetctl_error_codes_e {
+	/* API succeeded. This should always be the first element. */
+	RMNETCTL_API_SUCCESS = 0,
+
+	RMNETCTL_API_FIRST_ERR = 1,
+	/* API failed because not enough memory to create buffer to send
+	 * message */
+	RMNETCTL_API_ERR_REQUEST_INVALID = RMNETCTL_API_FIRST_ERR,
+	/* API failed because not enough memory to create buffer for the
+	 *  response message */
+	RMNETCTL_API_ERR_RESPONSE_INVALID = 2,
+	/* API failed because could not send the message to kernel */
+	RMNETCTL_API_ERR_MESSAGE_SEND = 3,
+	/* API failed because could not receive message from the kernel */
+	RMNETCTL_API_ERR_MESSAGE_RECEIVE = 4,
+
+	RMNETCTL_INIT_FIRST_ERR = 5,
+	/* Invalid process id. So return an error. */
+	RMNETCTL_INIT_ERR_PROCESS_ID = RMNETCTL_INIT_FIRST_ERR,
+	/* Invalid socket descriptor id. So return an error. */
+	RMNETCTL_INIT_ERR_NETLINK_FD = 6,
+	/* Could not bind the socket to the Netlink file descriptor */
+	RMNETCTL_INIT_ERR_BIND = 7,
+	/* Invalid user id. Only root has access to this function. (NA) */
+	RMNETCTL_INIT_ERR_INVALID_USER = 8,
+
+	RMNETCTL_API_SECOND_ERR = 9,
+	/* API failed because the RmNet handle for the transaction was NULL */
+	RMNETCTL_API_ERR_HNDL_INVALID = RMNETCTL_API_SECOND_ERR,
+	/* API failed because the request buffer for the transaction was NULL */
+	RMNETCTL_API_ERR_REQUEST_NULL = 10,
+	/* API failed because the response buffer for the transaction was NULL*/
+	RMNETCTL_API_ERR_RESPONSE_NULL = 11,
+	/* API failed because the request and response type do not match*/
+	RMNETCTL_API_ERR_MESSAGE_TYPE = 12,
+	/* API failed because the return type is invalid */
+	RMNETCTL_API_ERR_RETURN_TYPE = 13,
+	/* API failed because the string was truncated */
+	RMNETCTL_API_ERR_STRING_TRUNCATION = 14,
+
+	/* These error are 1-to-1 with rmnet_data config errors in rmnet_data.h
+	   for each conversion.
+	   please keep the enums synced.
+	*/
+	RMNETCTL_KERNEL_FIRST_ERR = 15,
+	/* No error */
+	RMNETCTL_KERNEL_ERROR_NO_ERR = RMNETCTL_KERNEL_FIRST_ERR,
+	/* Invalid / unsupported message */
+	RMNETCTL_KERNEL_ERR_UNKNOWN_MESSAGE = 16,
+	/* Internal problem in the kernel module */
+	RMNETCTL_KERNEL_ERR_INTERNAL = 17,
+	/* Kernel is temporarily out of memory */
+	RMNETCTL_KERNEL_ERR_OUT_OF_MEM = 18,
+	/* Device already exists / Still in use */
+	RMETNCTL_KERNEL_ERR_DEVICE_IN_USE = 19,
+	/* Invalid request / Unsupported scenario */
+	RMNETCTL_KERNEL_ERR_INVALID_REQUEST = 20,
+	/* Device doesn't exist */
+	RMNETCTL_KERNEL_ERR_NO_SUCH_DEVICE = 21,
+	/* One or more of the arguments is invalid */
+	RMNETCTL_KERNEL_ERR_BAD_ARGS = 22,
+	/* Egress device is invalid */
+	RMNETCTL_KERNEL_ERR_BAD_EGRESS_DEVICE = 23,
+	/* TC handle is full */
+	RMNETCTL_KERNEL_ERR_TC_HANDLE_FULL = 24,
+
+	/* This should always be the last element */
+	RMNETCTL_API_ERR_ENUM_LENGTH
+};
+
+#define RMNETCTL_ERR_MSG_SIZE 100
+
+/*!
+* @brief Contains a list of error message from API
+*/
+char rmnetctl_error_code_text
+[RMNETCTL_API_ERR_ENUM_LENGTH][RMNETCTL_ERR_MSG_SIZE] = {
+	"ERROR: API succeeded\n",
+	"ERROR: Unable to allocate the buffer to send message\n",
+	"ERROR: Unable to allocate the buffer to receive message\n",
+	"ERROR: Could not send the message to kernel\n",
+	"ERROR: Unable to receive message from the kernel\n",
+	"ERROR: Invalid process id\n",
+	"ERROR: Invalid socket descriptor id\n",
+	"ERROR: Could not bind to netlink socket\n",
+	"ERROR: Only root can access this API\n",
+	"ERROR: RmNet handle for the transaction was NULL\n",
+	"ERROR: Request buffer for the transaction was NULL\n",
+	"ERROR: Response buffer for the transaction was NULL\n",
+	"ERROR: Request and response type do not match\n",
+	"ERROR: Return type is invalid\n",
+	"ERROR: String was truncated\n",
+	/* Kernel errors */
+	"ERROR: Kernel call succeeded\n",
+	"ERROR: Invalid / Unsupported directive\n",
+	"ERROR: Internal problem in the kernel module\n",
+	"ERROR: The kernel is temporarily out of memory\n",
+	"ERROR: Device already exists / Still in use\n",
+	"ERROR: Invalid request / Unsupported scenario\n",
+	"ERROR: Device doesn't exist\n",
+	"ERROR: One or more of the arguments is invalid\n",
+	"ERROR: Egress device is invalid\n",
+	"ERROR: TC handle is full\n"
+};
+
+/*===========================================================================
+			 DEFINITIONS AND DECLARATIONS
+===========================================================================*/
+typedef struct rmnetctl_hndl_s rmnetctl_hndl_t;
+
+/*!
+* @brief Public API to initialize the RMNET control driver
+* @details Allocates memory for the RmNet handle. Creates and binds to a   and
+* netlink socket if successful
+* @param **rmnetctl_hndl_t_val RmNet handle to be initialized
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnetctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code);
+
+/*!
+* @brief Public API to clean up the RmNeT control handle
+* @details Close the socket and free the RmNet handle
+* @param *rmnetctl_hndl_t_val RmNet handle to be initialized
+* @return void
+*/
+void rmnetctl_cleanup(rmnetctl_hndl_t *hndl);
+
+/*!
+* @brief Public API to register/unregister a RMNET driver on a particular device
+* @details Message type is RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE or
+* RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE based on the flag for assoc_dev
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to register the RmNet driver
+* @param error_code Status code of this operation
+* @param assoc_dev registers the device if RMNETCTL_DEVICE_ASSOCIATE or
+* unregisters the device if RMNETCTL_DEVICE_UNASSOCIATE
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_associate_network_device(rmnetctl_hndl_t *hndl,
+				   const char *dev_name,
+				   uint16_t *error_code,
+				   uint8_t assoc_dev);
+
+/*!
+* @brief Public API to get if a RMNET driver is registered on a particular
+* device
+* @details Message type is RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to check if the RmNet driver is registered
+* @param register_status 1 if RmNet data driver is registered on a particular
+* device, 0 if not
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_network_device_associated(rmnetctl_hndl_t *hndl,
+					const char *dev_name,
+					int *register_status,
+					uint16_t *error_code);
+
+/*!
+* @brief Public API to set the egress data format for a particular link.
+* @details Message type is RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param egress_flags Egress flags to be set on the device
+* @param agg_size Max size of aggregated packets
+* @param agg_count Number of packets to be aggregated
+* @param dev_name Device on which to set the egress data format
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_set_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      uint32_t egress_flags,
+				      uint16_t agg_size,
+				      uint16_t agg_count,
+				      const char *dev_name,
+				      uint16_t *error_code);
+
+/*!
+* @brief Public API to get the egress data format for a particular link.
+* @details Message type is RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to get the egress data format
+* @param egress_flags Egress flags from the device
+* @param agg_count Number of packets to be aggregated
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      const char *dev_name,
+				      uint32_t *egress_flags,
+				      uint16_t *agg_size,
+				      uint16_t *agg_count,
+				      uint16_t *error_code);
+
+/*!
+* @brief Public API to set the ingress data format for a particular link.
+* @details Message type is RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param ingress_flags Ingress flags from the device
+* @param tail_spacing Tail spacing needed for the packet
+* @param dev_name Device on which to set the ingress data format
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_set_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						 uint32_t ingress_flags,
+						 uint8_t  tail_spacing,
+						 const char *dev_name,
+						 uint16_t *error_code);
+
+/*!
+* @brief Public API to get the ingress data format for a particular link.
+* @details Message type is RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to get the ingress data format
+* @param ingress_flags Ingress flags from the device
+* @param tail_spacing Tail spacing needed for the packet
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						const char *dev_name,
+						uint32_t *ingress_flags,
+						uint8_t  *tail_spacing,
+						uint16_t *error_code);
+
+inline int rmnet_set_link_ingress_data_format(rmnetctl_hndl_t *hndl,
+					      uint32_t ingress_flags,
+					      const char *dev_name,
+					      uint16_t *error_code)
+{
+	return rmnet_set_link_ingress_data_format_tailspace(hndl,
+							    ingress_flags,
+							    0,
+							    dev_name,
+							    error_code);
+}
+
+inline int rmnet_get_link_ingress_data_format(rmnetctl_hndl_t *hndl,
+					      const char *dev_name,
+					      uint32_t *ingress_flags,
+					      uint16_t *error_code)
+{
+	return rmnet_get_link_ingress_data_format_tailspace(hndl,
+							    dev_name,
+							    ingress_flags,
+							    0,
+							    error_code);
+}
+
+/*!
+* @brief Public API to set the logical endpoint configuration for a
+* particular link.
+* @details Message type is RMNET_NETLINK_SET_LOGICAL_EP_CONFIG.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param logical_ep_id Logical end point id on which the configuration is to be
+* set
+* @param rmnet_mode RmNet mode to be set on the device
+* @param dev_name Device on which to set the logical end point configuration
+* @param egress_dev_name Egress Device if operating in bridge mode
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_set_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				uint8_t operating_mode,
+				const char *dev_name,
+				const char *next_dev,
+				uint16_t *error_code);
+
+/*!
+* @brief Public API to un-set the logical endpoint configuration for a
+* particular link.
+* @details Message type is RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param logical_ep_id Logical end point id on which the configuration is to be
+* un-set
+* @param dev_name Device on which to un-set the logical end point configuration
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_unset_logical_ep_config(rmnetctl_hndl_t *hndl,
+				  int32_t ep_id,
+				  const char *dev_name,
+				  uint16_t *error_code);
+/*!
+* @brief Public API to get the logical endpoint configuration for a
+* particular link.
+* @details Message type is RMNET_NETLINK_GET_LOGICAL_EP_CONFIG.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param logical_ep_id Logical end point id from which to get the configuration
+* @param dev_name Device on which to get the logical end point configuration
+* @param rmnet_mode RmNet mode from the device
+* @param next_dev Egress Device name
+* @param next_dev_len Egress Device I/O string len
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				const char *dev_name,
+				uint8_t *operating_mode,
+				char **next_dev,
+				uint32_t next_dev_len,
+				uint16_t *error_code);
+
+/*!
+* @brief Public API to create a new virtual device node
+* @details Message type is RMNET_NETLINK_NEW_VND or
+* RMNETCTL_FREE_VND based on the flag for new_vnd
+* @param hndl RmNet handle for the Netlink message
+* @param id Node number to create the virtual network device node
+* @param error_code Status code of this operation returned from the kernel
+* @param new_vnd creates a new virtual network device if  RMNETCTL_NEW_VND or
+* frees the device if RMNETCTL_FREE_VND
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_new_vnd(rmnetctl_hndl_t *hndl,
+		  uint32_t id,
+		  uint16_t *error_code,
+		  uint8_t new_vnd);
+
+/*!
+ * @brief Public API to create a new virtual device node with a custom prefix
+ * @details Message type is RMNET_NETLINK_NEW_VND or
+ * RMNETCTL_FREE_VND based on the flag for new_vnd
+ * @param hndl RmNet handle for the Netlink message
+ * @param id Node number to create the virtual network device node
+ * @param error_code Status code of this operation returned from the kernel
+ * @param new_vnd creates a new virtual network device if  RMNETCTL_NEW_VND or
+ * frees the device if RMNETCTL_FREE_VND
+ * @param prefix Prefix to be used when naming the network interface
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rmnet_new_vnd_prefix(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 uint8_t new_vnd,
+			 const char *prefix);
+
+/*!
+ * @brief Public API to create a new virtual device node with a custom prefix
+ * @details Message type is RMNET_NETLINK_NEW_VND or
+ * RMNETCTL_FREE_VND based on the flag for new_vnd
+ * @param hndl RmNet handle for the Netlink message
+ * @param id Node number to create the virtual network device node
+ * @param error_code Status code of this operation returned from the kernel
+ * @param new_vnd creates a new virtual network device if  RMNETCTL_NEW_VND or
+ * frees the device if RMNETCTL_FREE_VND
+ * @param name Name to be used when naming the network interface
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rmnet_new_vnd_name(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 const char *name);
+
+/*!
+ * @brief API to get the ASCII name of a virtual network device from its ID
+ * @param hndl RmNet handle for the Netlink message
+ * @param id Node number to create the virtual network device node
+ * @param error_code Status code of this operation returned from the kernel
+ * @param buf Buffer to store ASCII representation of device name
+ * @param buflen Length of the buffer
+ * @param prefix Prefix to be used when naming the network interface
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+
+int rmnet_get_vnd_name(rmnetctl_hndl_t *hndl,
+                      uint32_t id,
+                      uint16_t *error_code,
+                      char *buf,
+                      uint32_t buflen);
+
+/*!
+* @brief Public API to set or clear a flow
+* @details Message type is RMNET_NETLINK_ADD_VND_TC_FLOW or
+* RMNET_NETLINK_DEL_VND_TC_FLOW based on the flag for set_flow
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param id Node number to set or clear the flow on the virtual network
+* device node
+* @param map_flow_id Flow handle of the modem
+* @param tc_flow_id Software flow handle
+* @param set_flow sets the flow if  RMNET_NETLINK_SET_FLOW or
+* clears the flow if RMNET_NETLINK_CLEAR_FLOW
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl,
+			      uint32_t id,
+			      uint32_t map_flow_id,
+			      uint32_t tc_flow_id,
+			      uint8_t set_flow,
+			      uint16_t *error_code);
+
+/* @brief Public API to initialize the RTM_NETLINK RMNET control driver
+ * @details Allocates memory for the RmNet handle. Creates and binds to a
+ * netlink socket if successful
+ * @param **rmnetctl_hndl_t_val RmNet handle to be initialized
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code);
+
+/* @brief Public API to clean up the RTM_NETLINK RmNeT control handle
+ * @details Close the socket and free the RmNet handle
+ * @param *rmnetctl_hndl_t_val RmNet handle to be initialized
+ * @return void
+ */
+int rtrmnet_ctl_deinit(rmnetctl_hndl_t *hndl);
+
+/* @brief Public API to create a new virtual device node
+ * @details Message type is RTM_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param dev_name Device name new node will be connected to
+ * @param vnd_name Name of virtual device to be created
+ * @param error_code Status code of this operation returned from the kernel
+ * @param index Index node will have
+ * @param flagconfig Flag configuration device will have
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_newvnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+		       uint16_t *error_code, uint8_t  index,
+		       uint32_t flagconfig);
+
+/* @brief Public API to delete a virtual device node
+ * @details Message type is RTM_DELLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param vnd_name Name of virtual device to be deleted
+ * @param error_code Status code of this operation returned from the kernel
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_delvnd(rmnetctl_hndl_t *hndl, char *vndname,
+		       uint16_t *error_code);
+
+/* @brief Public API to change flag's of a virtual device node
+ * @details Message type is RTM_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param dev_name Name of device node is connected to
+ * @param vnd_name Name of virtual device to be changed
+ * @param error_code Status code of this operation returned from the kernel
+ * @param flagconfig New flag config vnd should have
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code, uint8_t  index,
+			  uint32_t flagconfig);
+
+/* @brief Public API to bridge a vnd and device
+ * @details Message type is RTM_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param dev_name device to bridge msg will be sent to
+ * @param vnd_name vnd name of device that will be dev_name's master
+ * @param error_code Status code of this operation returned from the kernel
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code);
+
+#endif /* not defined LIBRMNETCTL_H */
+
diff --git a/rmnetlib/inc/librmnetctl_hndl.h b/rmnetlib/inc/librmnetctl_hndl.h
new file mode 100644
index 0000000..1a435ed
--- /dev/null
+++ b/rmnetlib/inc/librmnetctl_hndl.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+
+			L I B R M N E T C T L _ H N D L. H
+
+Copyright (c) 2013, 2015 The Linux Foundation. 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 The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+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.
+
+******************************************************************************/
+
+/*!
+*  @file    librmnetctl_hndl.h
+*  @brief   rmnet control API's handle file
+*/
+
+#ifndef LIBRMNETCTL_HNDL_H
+#define LIBRMNETCTL_HNDL_H
+
+/*===========================================================================
+			 DEFINITIONS AND DECLARATIONS
+===========================================================================*/
+
+/*!
+* @brief Structure for RMNET control handles. A rmnet hndl contains the caller
+* process id, the transaction id which is initialized to 0 for each new
+* initialized handle and the netlink file descriptor for this handle.
+* @var pid process id to be used for the netlink message
+* @var transaction_id message number for debugging
+* @var netlink_fd netlink file descriptor to be used
+* @var src_addr source socket address properties for this message
+* @var dest_addr destination socket address properties for this message
+*/
+
+struct rmnetctl_hndl_s {
+	 uint32_t pid;
+	 uint32_t transaction_id;
+	 int netlink_fd;
+	 struct sockaddr_nl src_addr, dest_addr;
+};
+
+#endif /* not defined LIBRMNETCTL_HNDL_H */
+
diff --git a/rmnetlib/src/Makefile.am b/rmnetlib/src/Makefile.am
new file mode 100644
index 0000000..7bd53b7
--- /dev/null
+++ b/rmnetlib/src/Makefile.am
@@ -0,0 +1,14 @@
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_CFLAGS += -Wall -Werror -Wundef -Wstrict-prototypes -Wno-trigraphs -g
+AM_CFLAGS += -I./../inc
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
+librmnetctl_la_C = @C@
+librmnetctl_la_SOURCES = librmnetctl.c
+
+librmnetctl_la_CFLAGS := $(AM_CFLAGS)
+librmnetctl_la_LDFLAGS := $(AM_LDFLAGS) -lpthread
+library_includedir = $(pkgincludedir)
+library_include_HEADERS = ./../inc/librmnetctl.h
+
+lib_LTLIBRARIES = librmnetctl.la
diff --git a/rmnetlib/src/librmnetctl.c b/rmnetlib/src/librmnetctl.c
new file mode 100644
index 0000000..4e07376
--- /dev/null
+++ b/rmnetlib/src/librmnetctl.c
@@ -0,0 +1,1369 @@
+/******************************************************************************
+
+			L I B R M N E T C T L . C
+
+Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. 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 The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+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.
+
+******************************************************************************/
+
+/*!
+* @file    librmnetctl.c
+* @brief   rmnet control API's implementation file
+*/
+
+/*===========================================================================
+			INCLUDE FILES
+===========================================================================*/
+
+#include <sys/socket.h>
+#include <stdint.h>
+#include <linux/netlink.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/rtnetlink.h>
+#include <linux/gen_stats.h>
+#include <net/if.h>
+#include <asm/types.h>
+#include "rmnet_data.h"
+#include "librmnetctl_hndl.h"
+#include "librmnetctl.h"
+
+#ifdef USE_GLIB
+#include <glib.h>
+#define strlcpy g_strlcpy
+#endif
+
+#define RMNETCTL_SOCK_FLAG 0
+#define ROOT_USER_ID 0
+#define MIN_VALID_PROCESS_ID 0
+#define MIN_VALID_SOCKET_FD 0
+#define KERNEL_PROCESS_ID 0
+#define UNICAST 0
+#define MAX_BUF_SIZE sizeof(struct nlmsghdr) + sizeof(struct rmnet_nl_msg_s)
+#define INGRESS_FLAGS_MASK   (RMNET_INGRESS_FIX_ETHERNET | \
+			      RMNET_INGRESS_FORMAT_MAP | \
+			      RMNET_INGRESS_FORMAT_DEAGGREGATION | \
+			      RMNET_INGRESS_FORMAT_DEMUXING | \
+			      RMNET_INGRESS_FORMAT_MAP_COMMANDS | \
+			      RMNET_INGRESS_FORMAT_MAP_CKSUMV3 | \
+			      RMNET_INGRESS_FORMAT_MAP_CKSUMV4)
+#define EGRESS_FLAGS_MASK    (RMNET_EGRESS_FORMAT__RESERVED__ | \
+			      RMNET_EGRESS_FORMAT_MAP | \
+			      RMNET_EGRESS_FORMAT_AGGREGATION | \
+			      RMNET_EGRESS_FORMAT_MUXING | \
+			      RMNET_EGRESS_FORMAT_MAP_CKSUMV3 | \
+			      RMNET_EGRESS_FORMAT_MAP_CKSUMV4)
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define NLMSG_TAIL(nmsg) \
+    ((struct rtattr *) (((char *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+struct nlmsg {
+	struct nlmsghdr nl_addr;
+	struct ifinfomsg ifmsg;
+	char data[500];
+};
+
+extern size_t strlcpy(char *dst, const char *src, size_t dsize);
+
+/*===========================================================================
+			LOCAL FUNCTION DEFINITIONS
+===========================================================================*/
+/*!
+* @brief Synchronous method to send and receive messages to and from the kernel
+* using  netlink sockets
+* @details Increments the transaction id for each message sent to the kernel.
+* Sends the netlink message to the kernel and receives the response from the
+* kernel.
+* @param *hndl RmNet handle for this transaction
+* @param request Message to be sent to the kernel
+* @param response Message received from the kernel
+* @return RMNETCTL_API_SUCCESS if successfully able to send and receive message
+* from the kernel
+* @return RMNETCTL_API_ERR_HNDL_INVALID if RmNet handle for the transaction was
+* NULL
+* @return RMNETCTL_API_ERR_REQUEST_NULL not enough memory to create buffer for
+* sending the message
+* @return RMNETCTL_API_ERR_MESSAGE_SEND if could not send the message to kernel
+* @return RMNETCTL_API_ERR_MESSAGE_RECEIVE if could not receive message from the
+* kernel
+* @return RMNETCTL_API_ERR_MESSAGE_TYPE if the request and response type do not
+* match
+*/
+static uint16_t rmnetctl_transact(rmnetctl_hndl_t *hndl,
+			struct rmnet_nl_msg_s *request,
+			struct rmnet_nl_msg_s *response) {
+	uint8_t *request_buf, *response_buf;
+	struct nlmsghdr *nlmsghdr_val;
+	struct rmnet_nl_msg_s *rmnet_nl_msg_s_val;
+	ssize_t bytes_read = -1;
+	uint16_t return_code = RMNETCTL_API_ERR_HNDL_INVALID;
+	struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
+	socklen_t addrlen = sizeof(struct sockaddr_nl);
+	request_buf = NULL;
+	response_buf = NULL;
+	nlmsghdr_val = NULL;
+	rmnet_nl_msg_s_val = NULL;
+	do {
+	if (!hndl){
+		break;
+	}
+	if (!request){
+		return_code = RMNETCTL_API_ERR_REQUEST_NULL;
+		break;
+	}
+	if (!response){
+		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
+		break;
+	}
+	request_buf = (uint8_t *)malloc(MAX_BUF_SIZE * sizeof(uint8_t));
+	if (!request_buf){
+		return_code = RMNETCTL_API_ERR_REQUEST_NULL;
+		break;
+	}
+
+	response_buf = (uint8_t *)malloc(MAX_BUF_SIZE * sizeof(uint8_t));
+	if (!response_buf) {
+		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
+		break;
+	}
+
+	nlmsghdr_val = (struct nlmsghdr *)request_buf;
+	rmnet_nl_msg_s_val = (struct rmnet_nl_msg_s *)NLMSG_DATA(request_buf);
+
+	memset(request_buf, 0, MAX_BUF_SIZE*sizeof(uint8_t));
+	memset(response_buf, 0, MAX_BUF_SIZE*sizeof(uint8_t));
+
+	nlmsghdr_val->nlmsg_seq = hndl->transaction_id;
+	nlmsghdr_val->nlmsg_pid = hndl->pid;
+	nlmsghdr_val->nlmsg_len = MAX_BUF_SIZE;
+
+	memcpy((void *)NLMSG_DATA(request_buf), request,
+	sizeof(struct rmnet_nl_msg_s));
+
+	rmnet_nl_msg_s_val->crd = RMNET_NETLINK_MSG_COMMAND;
+	hndl->transaction_id++;
+
+	saddr_ptr = &hndl->dest_addr;
+	if (sendto(hndl->netlink_fd,
+			request_buf,
+			MAX_BUF_SIZE,
+			RMNETCTL_SOCK_FLAG,
+			(struct sockaddr*)saddr_ptr,
+			sizeof(struct sockaddr_nl)) < 0) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		break;
+	}
+
+	saddr_ptr = &hndl->src_addr;
+	bytes_read = recvfrom(hndl->netlink_fd,
+			response_buf,
+			MAX_BUF_SIZE,
+			RMNETCTL_SOCK_FLAG,
+			(struct sockaddr*)saddr_ptr,
+			&addrlen);
+	if (bytes_read < 0) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_RECEIVE;
+		break;
+	}
+
+	memcpy(response, (void *)NLMSG_DATA(response_buf),
+	sizeof(struct rmnet_nl_msg_s));
+	if (sizeof(*response) < sizeof(struct rmnet_nl_msg_s)) {
+		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
+		break;
+	}
+
+	if (request->message_type != response->message_type) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_TYPE;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	free(request_buf);
+	free(response_buf);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the dev name
+* @details Checks if the name is not NULL and if the name is less than the
+* RMNET_MAX_STR_LEN
+* @param dev_name Name of the device
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+static inline int _rmnetctl_check_dev_name(const char *dev_name) {
+	int return_code = RMNETCTL_INVALID_ARG;
+	do {
+	if (!dev_name)
+		break;
+	if (strlen(dev_name) >= RMNET_MAX_STR_LEN)
+		break;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the string length after a copy
+* @details Checks if the string length is not lesser than zero and lesser than
+* RMNET_MAX_STR_LEN
+* @param str_len length of the string after a copy
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+*/
+static inline int _rmnetctl_check_len(size_t str_len, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if (str_len > RMNET_MAX_STR_LEN) {
+		*error_code = RMNETCTL_API_ERR_STRING_TRUNCATION;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the response type
+* @details Checks if the response type of this message was return code
+* @param crd The crd field passed
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+*/
+static inline int _rmnetctl_check_code(int crd, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if (crd != RMNET_NETLINK_MSG_RETURNCODE) {
+		*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the response type
+* @details Checks if the response type of this message was data
+* @param crd The crd field passed
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+*/
+static inline int _rmnetctl_check_data(int crd, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if (crd != RMNET_NETLINK_MSG_RETURNDATA) {
+		*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to set the return value
+* @details Checks if the error_code from the transaction is zero for a return
+* code type message and sets the message type as RMNETCTL_SUCCESS
+* @param crd The crd field passed
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+*/
+static inline int _rmnetctl_set_codes(int error_val, uint16_t *error_code) {
+	int return_code = RMNETCTL_KERNEL_ERR;
+	if (error_val == RMNET_CONFIG_OK)
+		return_code = RMNETCTL_SUCCESS;
+	else
+		*error_code = (uint16_t)error_val + RMNETCTL_KERNEL_FIRST_ERR;
+	return return_code;
+}
+
+/*===========================================================================
+				EXPOSED API
+===========================================================================*/
+
+int rmnetctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code)
+{
+	pid_t pid = 0;
+	int netlink_fd = -1, return_code = RMNETCTL_LIB_ERR;
+	struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
+	do {
+	if ((!hndl) || (!error_code)){
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	*hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t));
+	if (!*hndl) {
+		*error_code = RMNETCTL_API_ERR_HNDL_INVALID;
+		break;
+	}
+
+	memset(*hndl, 0, sizeof(rmnetctl_hndl_t));
+
+	pid = getpid();
+	if (pid  < MIN_VALID_PROCESS_ID) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_PROCESS_ID;
+		break;
+	}
+	(*hndl)->pid = (uint32_t)pid;
+	netlink_fd = socket(PF_NETLINK, SOCK_RAW, RMNET_NETLINK_PROTO);
+	if (netlink_fd < MIN_VALID_SOCKET_FD) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_NETLINK_FD;
+		break;
+	}
+
+	(*hndl)->netlink_fd = netlink_fd;
+
+	memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->src_addr.nl_family = AF_NETLINK;
+	(*hndl)->src_addr.nl_pid = (*hndl)->pid;
+
+	saddr_ptr = &(*hndl)->src_addr;
+	if (bind((*hndl)->netlink_fd,
+		(struct sockaddr*)saddr_ptr,
+		sizeof(struct sockaddr_nl)) < 0) {
+		close((*hndl)->netlink_fd);
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_BIND;
+		break;
+	}
+
+	memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->dest_addr.nl_family = AF_NETLINK;
+	(*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID;
+	(*hndl)->dest_addr.nl_groups = UNICAST;
+
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+void rmnetctl_cleanup(rmnetctl_hndl_t *hndl)
+{
+	if (!hndl)
+		return;
+	close(hndl->netlink_fd);
+	free(hndl);
+}
+
+int rmnet_associate_network_device(rmnetctl_hndl_t *hndl,
+				   const char *dev_name,
+				   uint16_t *error_code,
+				   uint8_t assoc_dev)
+{
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
+		((assoc_dev != RMNETCTL_DEVICE_ASSOCIATE) &&
+		(assoc_dev != RMNETCTL_DEVICE_UNASSOCIATE))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	if (assoc_dev == RMNETCTL_DEVICE_ASSOCIATE)
+		request.message_type = RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE;
+	else
+		request.message_type = RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data), dev_name, (size_t)RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_get_network_device_associated(rmnetctl_hndl_t *hndl,
+					const char *dev_name,
+					int *register_status,
+					uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!register_status) || (!error_code) ||
+	_rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data), dev_name, RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	*register_status = response.return_code;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_set_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      uint32_t egress_flags,
+				      uint16_t agg_size,
+				      uint16_t agg_count,
+				      const char *dev_name,
+				      uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
+	    ((~EGRESS_FLAGS_MASK) & egress_flags)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN +
+			 sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t);
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	request.data_format.flags = egress_flags;
+	request.data_format.agg_size = agg_size;
+	request.data_format.agg_count = agg_count;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_get_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      const char *dev_name,
+				      uint32_t *egress_flags,
+				      uint16_t *agg_size,
+				      uint16_t *agg_count,
+				      uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!egress_flags) || (!agg_size) || (!agg_count) ||
+	(!error_code) || _rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+	request.message_type = RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	*egress_flags = response.data_format.flags;
+	*agg_size = response.data_format.agg_size;
+	*agg_count = response.data_format.agg_count;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_set_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						 uint32_t ingress_flags,
+						 uint8_t  tail_spacing,
+						 const char *dev_name,
+						 uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
+	    ((~INGRESS_FLAGS_MASK) & ingress_flags)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN +
+	sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t);
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+	request.data_format.flags = ingress_flags;
+	request.data_format.tail_spacing = tail_spacing;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_get_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						 const char *dev_name,
+						 uint32_t *ingress_flags,
+						 uint8_t  *tail_spacing,
+						 uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) ||
+		_rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	if (ingress_flags)
+		*ingress_flags = response.data_format.flags;
+
+	if (tail_spacing)
+		*tail_spacing = response.data_format.tail_spacing;
+
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_set_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				uint8_t operating_mode,
+				const char *dev_name,
+				const char *next_dev,
+				uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || ((ep_id < -1) || (ep_id > 31)) || (!error_code) ||
+		_rmnetctl_check_dev_name(dev_name) ||
+		_rmnetctl_check_dev_name(next_dev) ||
+		operating_mode >= RMNET_EPMODE_LENGTH) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_SET_LOGICAL_EP_CONFIG;
+
+	request.arg_length = RMNET_MAX_STR_LEN +
+	RMNET_MAX_STR_LEN + sizeof(int32_t) + sizeof(uint8_t);
+	str_len = strlcpy((char *)(request.local_ep_config.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	str_len = strlcpy((char *)(request.local_ep_config.next_dev),
+			  next_dev,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+	request.local_ep_config.ep_id = ep_id;
+	request.local_ep_config.operating_mode = operating_mode;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_unset_logical_ep_config(rmnetctl_hndl_t *hndl,
+				  int32_t ep_id,
+				  const char *dev_name,
+				  uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+
+	if ((!hndl) || ((ep_id < -1) || (ep_id > 31)) || (!error_code) ||
+		_rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG;
+
+	request.arg_length = RMNET_MAX_STR_LEN + sizeof(int32_t);
+	str_len = strlcpy((char *)(request.local_ep_config.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	request.local_ep_config.ep_id = ep_id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+
+	return return_code;
+}
+
+int rmnet_get_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				const char *dev_name,
+				uint8_t *operating_mode,
+				char **next_dev,
+				uint32_t next_dev_len,
+				uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!operating_mode) || (!error_code) || ((ep_id < -1) ||
+	    (ep_id > 31)) || _rmnetctl_check_dev_name(dev_name) || (!next_dev)
+	    || (0 == next_dev_len)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_LOGICAL_EP_CONFIG;
+
+	request.arg_length = RMNET_MAX_STR_LEN + sizeof(int32_t);
+	str_len = strlcpy((char *)(request.local_ep_config.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	request.local_ep_config.ep_id = ep_id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	str_len = strlcpy(*next_dev,
+			  (char *)(response.local_ep_config.next_dev),
+			  min(RMNET_MAX_STR_LEN, next_dev_len));
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	*operating_mode = response.local_ep_config.operating_mode;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_new_vnd_prefix(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 uint8_t new_vnd,
+			 const char *prefix)
+{
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	size_t str_len = 0;
+	do {
+	if ((!hndl) || (!error_code) ||
+	((new_vnd != RMNETCTL_NEW_VND) && (new_vnd != RMNETCTL_FREE_VND))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	memset(request.vnd.vnd_name, 0, RMNET_MAX_STR_LEN);
+	if (new_vnd ==  RMNETCTL_NEW_VND) {
+		if (prefix) {
+			request.message_type =RMNET_NETLINK_NEW_VND_WITH_PREFIX;
+			str_len = strlcpy((char *)request.vnd.vnd_name,
+					  prefix, RMNET_MAX_STR_LEN);
+			if (_rmnetctl_check_len(str_len, error_code)
+						!= RMNETCTL_SUCCESS)
+				break;
+		} else {
+			request.message_type = RMNET_NETLINK_NEW_VND;
+		}
+	} else {
+		request.message_type = RMNET_NETLINK_FREE_VND;
+	}
+
+	request.arg_length = sizeof(uint32_t);
+	request.vnd.id = id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_new_vnd_name(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 const char *prefix)
+{
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	size_t str_len = 0;
+	do {
+	if ((!hndl) || (!error_code)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	memset(request.vnd.vnd_name, 0, RMNET_MAX_STR_LEN);
+		if (prefix) {
+			request.message_type =RMNET_NETLINK_NEW_VND_WITH_NAME;
+			str_len = strlcpy((char *)request.vnd.vnd_name,
+					  prefix, RMNET_MAX_STR_LEN);
+			if (_rmnetctl_check_len(str_len, error_code)
+						!= RMNETCTL_SUCCESS)
+				break;
+		} else {
+			request.message_type = RMNET_NETLINK_NEW_VND;
+		}
+
+	request.arg_length = sizeof(uint32_t);
+	request.vnd.id = id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_new_vnd(rmnetctl_hndl_t *hndl,
+		  uint32_t id,
+		  uint16_t *error_code,
+		  uint8_t new_vnd)
+{
+	return rmnet_new_vnd_prefix(hndl, id, error_code, new_vnd, 0);
+}
+
+int rmnet_get_vnd_name(rmnetctl_hndl_t *hndl,
+		       uint32_t id,
+		       uint16_t *error_code,
+		       char *buf,
+		       uint32_t buflen)
+{
+	struct rmnet_nl_msg_s request, response;
+	uint32_t str_len;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || (!buf) || (0 == buflen)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_VND_NAME;
+	request.arg_length = sizeof(uint32_t);
+	request.vnd.id = id;
+
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	str_len = (uint32_t)strlcpy(buf,
+			  (char *)(response.vnd.vnd_name),
+			  buflen);
+	if (str_len >= buflen) {
+		*error_code = RMNETCTL_API_ERR_STRING_TRUNCATION;
+		break;
+	}
+
+	return_code = RMNETCTL_SUCCESS;
+	} while (0);
+	return return_code;
+}
+
+int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl,
+			      uint32_t id,
+			      uint32_t map_flow_id,
+			      uint32_t tc_flow_id,
+			      uint8_t set_flow,
+			      uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || ((set_flow != RMNETCTL_ADD_FLOW) &&
+	    (set_flow != RMNETCTL_DEL_FLOW))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+	if (set_flow ==  RMNETCTL_ADD_FLOW)
+		request.message_type = RMNET_NETLINK_ADD_VND_TC_FLOW;
+	else
+		request.message_type = RMNET_NETLINK_DEL_VND_TC_FLOW;
+
+	request.arg_length = (sizeof(uint32_t))*3;
+	request.flow_control.id = id;
+	request.flow_control.map_flow_id = map_flow_id;
+	request.flow_control.tc_flow_id = tc_flow_id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+	!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+/*
+ *                       NEW DRIVER API
+ */
+/* @brief Synchronous method to receive messages to and from the kernel
+ * using netlink sockets
+ * @details Receives the ack response from the kernel.
+ * @param *hndl RmNet handle for this transaction
+ * @param *error_code Error code if transaction fails
+ * @return RMNETCTL_API_SUCCESS if successfully able to send and receive message
+ * from the kernel
+ * @return RMNETCTL_API_ERR_HNDL_INVALID if RmNet handle for the transaction was
+ * NULL
+ * @return RMNETCTL_API_ERR_MESSAGE_RECEIVE if could not receive message from
+ * the kernel
+ * @return RMNETCTL_API_ERR_MESSAGE_TYPE if the response type does not
+ * match
+ */
+static int rmnet_get_ack(rmnetctl_hndl_t *hndl, uint16_t *error_code)
+{
+	struct nlack {
+		struct nlmsghdr ackheader;
+		struct nlmsgerr ackdata;
+		char   data[256];
+
+	} ack;
+	int i;
+
+	if (!hndl || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	if ((i = recv(hndl->netlink_fd, &ack, sizeof(ack), 0)) < 0) {
+		*error_code = errno;
+		return RMNETCTL_API_ERR_MESSAGE_RECEIVE;
+	}
+
+	/*Ack should always be NLMSG_ERROR type*/
+	if (ack.ackheader.nlmsg_type == NLMSG_ERROR) {
+		if (ack.ackdata.error == 0) {
+			*error_code = RMNETCTL_API_SUCCESS;
+			return RMNETCTL_SUCCESS;
+		} else {
+			*error_code = -ack.ackdata.error;
+			return RMNETCTL_KERNEL_ERR;
+		}
+	}
+
+	*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
+	return RMNETCTL_API_FIRST_ERR;
+}
+
+/*
+ *                       EXPOSED NEW DRIVER API
+ */
+int rtrmnet_ctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code)
+{
+	struct sockaddr_nl __attribute__((__may_alias__)) *saddr_ptr;
+	int netlink_fd = -1;
+	pid_t pid = 0;
+
+	if (!hndl || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	*hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t));
+	if (!*hndl) {
+		*error_code = RMNETCTL_API_ERR_HNDL_INVALID;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	memset(*hndl, 0, sizeof(rmnetctl_hndl_t));
+
+	pid = getpid();
+	if (pid  < MIN_VALID_PROCESS_ID) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_PROCESS_ID;
+		return RMNETCTL_LIB_ERR;
+	}
+	(*hndl)->pid = KERNEL_PROCESS_ID;
+	netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (netlink_fd < MIN_VALID_SOCKET_FD) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_NETLINK_FD;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	(*hndl)->netlink_fd = netlink_fd;
+
+	memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->src_addr.nl_family = AF_NETLINK;
+	(*hndl)->src_addr.nl_pid = (*hndl)->pid;
+
+	saddr_ptr = &(*hndl)->src_addr;
+	if (bind((*hndl)->netlink_fd,
+		(struct sockaddr *)saddr_ptr,
+		sizeof(struct sockaddr_nl)) < 0) {
+		close((*hndl)->netlink_fd);
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_BIND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->dest_addr.nl_family = AF_NETLINK;
+	(*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID;
+	(*hndl)->dest_addr.nl_groups = UNICAST;
+
+	return RMNETCTL_SUCCESS;
+}
+
+int rtrmnet_ctl_deinit(rmnetctl_hndl_t *hndl)
+{
+	if (!hndl)
+		return RMNETCTL_SUCCESS;
+
+	close(hndl->netlink_fd);
+	free(hndl);
+
+	return RMNETCTL_SUCCESS;
+}
+
+int rtrmnet_ctl_newvnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+		       uint16_t *error_code, uint8_t  index,
+		       uint32_t flagconfig)
+{
+	struct rtattr *attrinfo, *datainfo, *linkinfo;
+	struct ifla_vlan_flags flags;
+	int devindex = 0, val = 0;
+	char *kind = "rmnet";
+	struct nlmsg req;
+	short id;
+
+	if (!hndl || !devname || !vndname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	memset(&req, 0, sizeof(req));
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL |
+				  NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of devname*/
+	devindex = if_nametoindex(devname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup link attr with devindex as data */
+	val = devindex;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type = IFLA_LINK;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+	memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+	/* Set up IFLA info kind  RMNET that has linkinfo and type */
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_IFNAME;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+	memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+	linkinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	linkinfo->rta_type = IFLA_LINKINFO;
+	linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_INFO_KIND;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+	memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+	datainfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	datainfo->rta_type =  IFLA_INFO_DATA;
+	datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+	id = index;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_VLAN_ID;
+	attrinfo->rta_len = RTA_LENGTH(sizeof(id));
+	memcpy(RTA_DATA(attrinfo), &id, sizeof(id));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(id)));
+
+	if (flagconfig != 0) {
+		flags.mask  = flagconfig;
+		flags.flags = flagconfig;
+
+		attrinfo = (struct rtattr *)(((char *)&req) +
+					     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+		attrinfo->rta_type =  IFLA_VLAN_FLAGS;
+		attrinfo->rta_len = RTA_LENGTH(sizeof(flags));
+		memcpy(RTA_DATA(attrinfo), &flags, sizeof(flags));
+		req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+					RTA_ALIGN(RTA_LENGTH(sizeof(flags)));
+	}
+
+	datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+
+	linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
+
+int rtrmnet_ctl_delvnd(rmnetctl_hndl_t *hndl, char *vndname,
+		       uint16_t *error_code)
+{
+	int devindex = 0;
+	struct nlmsg req;
+
+	if (!hndl || !vndname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	memset(&req, 0, sizeof(req));
+	req.nl_addr.nlmsg_type = RTM_DELLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of vndname*/
+	devindex = if_nametoindex(vndname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup index attribute */
+	req.ifmsg.ifi_index = devindex;
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
+
+
+int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code, uint8_t  index,
+			  uint32_t flagconfig)
+{
+	struct rtattr *attrinfo, *datainfo, *linkinfo;
+	struct ifla_vlan_flags flags;
+	char *kind = "rmnet";
+	struct nlmsg req;
+	int devindex = 0;
+	int val = 0;
+	short id;
+
+	memset(&req, 0, sizeof(req));
+
+	if (!hndl || !devname || !vndname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of devname*/
+	devindex = if_nametoindex(devname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup link attr with devindex as data */
+	val = devindex;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	attrinfo->rta_type = IFLA_LINK;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+	memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+	  /* Set up IFLA info kind  RMNET that has linkinfo and type */
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_IFNAME;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+	memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+	linkinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	linkinfo->rta_type = IFLA_LINKINFO;
+	linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	attrinfo->rta_type =  IFLA_INFO_KIND;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+	memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+	datainfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	datainfo->rta_type =  IFLA_INFO_DATA;
+	datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+	id = index;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_VLAN_ID;
+	attrinfo->rta_len = RTA_LENGTH(sizeof(id));
+	memcpy(RTA_DATA(attrinfo), &id, sizeof(id));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(id)));
+
+	if (flagconfig != 0) {
+		flags.mask  = flagconfig;
+		flags.flags = flagconfig;
+
+		attrinfo = (struct rtattr *)(((char *)&req) +
+					     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+		attrinfo->rta_type =  IFLA_VLAN_FLAGS;
+		attrinfo->rta_len = RTA_LENGTH(sizeof(flags));
+		memcpy(RTA_DATA(attrinfo), &flags, sizeof(flags));
+		req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+					RTA_ALIGN(RTA_LENGTH(sizeof(flags)));
+	}
+
+	datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+
+	linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
+
+int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code)
+{
+	int devindex = 0, vndindex = 0;
+	struct rtattr *masterinfo;
+	struct nlmsg req;
+
+	if (!hndl || !vndname || !devname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	memset(&req, 0, sizeof(req));
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of vndname*/
+	devindex = if_nametoindex(devname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	vndindex = if_nametoindex(vndname);
+	if (vndindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup index attribute */
+	req.ifmsg.ifi_index = devindex;
+	masterinfo = (struct rtattr *)(((char *)&req) +
+				       NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	masterinfo->rta_type =  IFLA_MASTER;
+	masterinfo->rta_len = RTA_LENGTH(sizeof(vndindex));
+	memcpy(RTA_DATA(masterinfo), &vndindex, sizeof(vndindex));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(vndindex)));
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
-- 
1.9.1


^ permalink raw reply related

* Re: [PATCH] r8169: Reinstate ALDPS and ASPM support
From: Bjorn Helgaas @ 2018-06-05 17:28 UTC (permalink / raw)
  To: Ryankao
  Cc: Kai Heng Feng, jrg.otte@gmail.com, David Miller, Hayes Wang,
	hkallweit1@gmail.com, romieu@fr.zoreil.com, Linux Netdev List,
	Linux Kernel Mailing List, Hau
In-Reply-To: <B01FA80E1422A647BEB597328C544223D30B8F90@RTITMBSV05.realtek.com.tw>

On Tue, Jun 05, 2018 at 06:34:09AM +0000, Ryankao wrote:
> Add realtek folk Hau
> 
> -----Original Message-----
> From: Kai Heng Feng [mailto:kai.heng.feng@canonical.com] 
> Sent: Tuesday, June 05, 2018 1:02 PM
> To: jrg.otte@gmail.com
> Cc: David Miller <davem@davemloft.net>; Hayes Wang <hayeswang@realtek.com>; hkallweit1@gmail.com; romieu@fr.zoreil.com; Linux Netdev List <netdev@vger.kernel.org>; Linux Kernel Mailing List <linux-kernel@vger.kernel.org>; Ryankao <ryankao@realtek.com>
> Subject: Re: [PATCH] r8169: Reinstate ALDPS and ASPM support
> 
> Hi Jörg Otte,
> 
> Can you give this patch a try?
> 
> Since you are the only one that reported ALDPS/ASPM regression,
> 
> And I think this patch should solve the issue you had [1].
> 
> Hopefully we don't need to go down the rabbit hole of blacklist/whitelist...
> 
> Kai-Heng
> 
> [1] https://lkml.org/lkml/2013/1/5/36

I have no idea what ALDPS is.  It's not mentioned in the PCIe spec, so
presumably it's some Realtek-specific thing.  ASPM is a generic PCIe
thing.  Changes to these two things should be in separate patches so
they don't get tangled up.

> > On Jun 5, 2018, at 12:58 PM, Kai-Heng Feng 
> > <kai.heng.feng@canonical.com>
> > wrote:
> >
> > This patch reinstate ALDPS and ASPM support on r8169.
> >
> > On some Intel platforms, ASPM support on r8169 is the key factor to 
> > let Package C-State achieve PC8. Without ASPM support, the deepest 
> > Package C-State can hit is PC3. PC8 can save additional ~3W in 
> > comparison with PC3.
> >
> > This patch is from Realtek.
> >
> > Fixes: e0c075577965 ("r8169: enable ALDPS for power saving")
> > Fixes: d64ec841517a ("r8169: enable internal ASPM and clock request
> > settings")

> > +3507,15 @@ static void rtl8168e_1_hw_phy_config(struct 
> > rtl8169_private *tp)
> >  	rtl_writephy(tp, 0x0d, 0x4007);
> >  	rtl_writephy(tp, 0x0e, 0x0000);
> >  	rtl_writephy(tp, 0x0d, 0x0000);
> > +
> > +	/* Check ALDPS bit, disable it if enabled */
> > +	rtl_writephy(tp, 0x1f, 0x0000);
> > +	if (enable_aldps)
> > +		rtl_w0w1_phy(tp, 0x15, 0x1000, 0x0000);
> > +	else if (rtl_readphy(tp, 0x15) & 0x1000)
> > +		rtl_w0w1_phy(tp, 0x15, 0x0000, 0x1000);

There's a lot of repetition of this code with minor variations.  You
could probably factor it out and make it more concise and more
readable.

> > +static void rtl8169_check_link_status(struct net_device *dev,
> > +				      struct rtl8169_private *tp) {
> > +	struct device *d = tp_to_dev(tp);
> > +
> > +	if (tp->link_ok(tp)) {
> > +		rtl_link_chg_patch(tp);
> > +		/* This is to cancel a scheduled suspend if there's one. */
> > +		if (pm_request_resume(d))
> > +			_rtl_reset_work(tp);
> > +		netif_carrier_on(dev);
> > +		if (net_ratelimit())
> > +			netif_info(tp, ifup, dev, "link up\n");
> > +	} else {
> > +		netif_carrier_off(dev);
> > +		netif_info(tp, ifdown, dev, "link down\n");
> > +		pm_runtime_idle(d);
> > +	}
> > +}

This function apparently just got moved around without changing
anything.  That's fine, but the move should be in a separate patch to
make the real changes easier to review.

> > @@ -7649,8 +7757,12 @@ static int rtl_init_one(struct pci_dev *pdev, 
> > const struct pci_device_id *ent)
> >
> >  	/* disable ASPM completely as that cause random device stop working
> >  	 * problems as well as full system hangs for some PCIe devices users */
> > -	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
> > -				     PCIE_LINK_STATE_CLKPM);
> > +	if (!enable_aspm) {
> > +		pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
> > +					     PCIE_LINK_STATE_L1 |
> > +					     PCIE_LINK_STATE_CLKPM);
> > +		netif_info(tp, probe, dev, "ASPM disabled\n");
> > +	}

ASPM is a generic PCIe feature that should be configured by the PCI
core without any help from the device driver.

If code in the driver is needed, that means either the PCI core is
doing it wrong and we should fix it there, or the device is broken and
the driver is working around the erratum.

If this is an erratum, you should include details about exactly what's
broken and (ideally) a URL to the published erratum.  Otherwise this
is just unmaintainable black magic and likely to be broken by future
ASPM changes in the PCI core.

ASPM configuration is done by the PCI core before drivers are bound to
the device.  If you need device-specific workarounds, they should
probably be in quirks so they're done before the core does that ASPM
configuration.

> >  	/* enable device (incl. PCI PM wakeup and hotplug setup) */
> >  	rc = pcim_enable_device(pdev);
> > --
> > 2.17.0
> 
> ------Please consider the environment before printing this e-mail.

^ permalink raw reply

* Re: [PATCH net] failover: eliminate callback hell
From: Samudrala, Sridhar @ 2018-06-05 17:22 UTC (permalink / raw)
  To: Stephen Hemminger, kys, haiyangz, davem, mst, Alexander H,
	Jiri Pirko
  Cc: netdev, Stephen Hemminger, Brandeburg, Jesse
In-Reply-To: <20180605034231.31610-1-sthemmin@microsoft.com>

On 6/4/2018 8:42 PM, Stephen Hemminger wrote:
> The net failover should be a simple library, not a virtual
> object with function callbacks (see callback hell).
> The code is simpler is smaller both for the netvsc and virtio use case.

I quickly tried this patch and it breaks virtio-net in standby mode.
I don't see failover netdev, unloading virtio-net causes a crash.

With these changes, there is very minimal code that is shared between
netvsc and virtio-net. The notifier and event handling code and the
lookup_bymac routines are now duplicated in both the drivers. I thought
we wanted to keep this code common between the 2 drivers and we went through
multiple revisions to make sure that it works with both netvsc's 2 netdev
and virtio-net's 3 netdev models.

The reason for the indirect ops is to support these 2 different models and
i am not sure if the overhead of the callbacks is that significant considering
that they are not called in the hot path.




>
> The code is restructured in many ways. I should have given these
> as review comments to net_failover during review
> but did not want to overwhelm the original submitter.
> Therefore it was merged prematurely.
>
> Some of the many items changed are:
>
>    * The support routines should just be selected as needed in
>      kernel config, no need for them to be visible config items.
>
>    * Both netvsc and net_failover should keep their list of their
>      own devices. Not a common list.
>
> 	  * The matching of secondary device to primary device policy
>      is up to the network device. Both net_failover and netvsc
>      will use MAC for now but can change separately.
>
>    * The match policy is only used during initial discovery; after
>      that the secondary device knows what the upper device is because
>      of the parent/child relationship; no searching is required.
>
>    * Now, netvsc and net_failover use the same delayed work type
>      mechanism for setup. Previously, net_failover code was triggering off
>      name change but a similar policy was rejected for netvsc.
>      "what is good for the goose is good for the gander"
>
>    * The net_failover private device info 'struct net_failover_info'
>      should have been private to the driver file, not a visible
>      API.
>
>    * The net_failover device should use SET_NETDEV_DEV
>      that is intended only for physical devices not virtual devices.
>
>    * No point in having DocBook style comments on a driver file.
>      They only make sense on an external exposed API.
>
>    * net_failover only supports Ethernet, so use ether_addr_copy.
>
>    * Set permanent and current address of net_failover device
>      to match the primary.
>
>    * Carrier should be marked off before registering device
>      the net_failover device.
>
>    * Use netdev_XXX for log messages, in net_failover (not dev_xxx)
>
>    * Since failover infrastructure is about linking devices just
>      use RTNL no need for other locking in init and teardown.
>
>    * Don't bother with ERR_PTR() style return if only possible
>      return is success or no memory.
>
>    * As much as possible, the terms master and slave should be avoided
>      because of their cultural connotations.
>
> Note; this code has been tested on Hyper-V
> but is compile tested only on virtio.
>
> Fixes: 30c8bd5aa8b2 ("net: Introduce generic failover module")
> Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
> ---
>
> Although this patch needs to go into 4.18 (linux-net),
> this version is based against net-next because net-next
> hasn't been merged into linux-net yet.
>
>
>   drivers/net/hyperv/hyperv_net.h |   3 +-
>   drivers/net/hyperv/netvsc_drv.c | 173 +++++++++++------
>   drivers/net/net_failover.c      | 312 ++++++++++++++++++++-----------
>   drivers/net/virtio_net.c        |   9 +-
>   include/net/failover.h          |  31 +---
>   include/net/net_failover.h      |  32 +---
>   net/Kconfig                     |  13 +-
>   net/core/failover.c             | 316 ++++----------------------------
>   8 files changed, 373 insertions(+), 516 deletions(-)
>
> diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
> index 99d8e7398a5b..c7d25d10765e 100644
> --- a/drivers/net/hyperv/hyperv_net.h
> +++ b/drivers/net/hyperv/hyperv_net.h
> @@ -902,6 +902,8 @@ struct net_device_context {
>   	struct hv_device *device_ctx;
>   	/* netvsc_device */
>   	struct netvsc_device __rcu *nvdev;
> +	/* list of netvsc net_devices */
> +	struct list_head list;
>   	/* reconfigure work */
>   	struct delayed_work dwork;
>   	/* last reconfig time */
> @@ -933,7 +935,6 @@ struct net_device_context {
>   	/* Serial number of the VF to team with */
>   	u32 vf_serial;
>   
> -	struct failover *failover;
>   };
>   
>   /* Per channel data */
> diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
> index bef4d55a108c..074e6b8578df 100644
> --- a/drivers/net/hyperv/netvsc_drv.c
> +++ b/drivers/net/hyperv/netvsc_drv.c
> @@ -70,6 +70,8 @@ static int debug = -1;
>   module_param(debug, int, 0444);
>   MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
>   
> +static LIST_HEAD(netvsc_dev_list);
> +
>   static void netvsc_change_rx_flags(struct net_device *net, int change)
>   {
>   	struct net_device_context *ndev_ctx = netdev_priv(net);
> @@ -1846,101 +1848,120 @@ static void netvsc_vf_setup(struct work_struct *w)
>   	}
>   
>   	vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
> -	if (vf_netdev)
> +	if (vf_netdev) {
>   		__netvsc_vf_setup(ndev, vf_netdev);
> -
> +		dev_put(vf_netdev);
> +	}
>   	rtnl_unlock();
>   }
>   
> -static int netvsc_pre_register_vf(struct net_device *vf_netdev,
> -				  struct net_device *ndev)
> +static struct net_device *get_netvsc_bymac(const u8 *mac)
>   {
> -	struct net_device_context *net_device_ctx;
> -	struct netvsc_device *netvsc_dev;
> +	struct net_device_context *ndev_ctx;
>   
> -	net_device_ctx = netdev_priv(ndev);
> -	netvsc_dev = rtnl_dereference(net_device_ctx->nvdev);
> -	if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev))
> -		return -ENODEV;
> +	ASSERT_RTNL();
>   
> -	return 0;
> +	list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) {
> +		struct net_device *dev = hv_get_drvdata(ndev_ctx->device_ctx);
> +
> +		if (ether_addr_equal(mac, dev->perm_addr))
> +			return dev;
> +	}
> +
> +	return NULL;
>   }
>   
> -static int netvsc_register_vf(struct net_device *vf_netdev,
> -			      struct net_device *ndev)
> +static int netvsc_register_vf(struct net_device *vf_netdev)
>   {
> -	struct net_device_context *ndev_ctx = netdev_priv(ndev);
> +	struct net_device *ndev;
> +	struct net_device_context *ndev_ctx;
> +
> +	/* Must use Ethernet addresses */
> +	if (vf_netdev->addr_len != ETH_ALEN)
> +		return NOTIFY_DONE;
> +
> +	/* VF must be a physical device not VLAN, etc */
> +	if (!vf_netdev->dev.parent)
> +		return NOTIFY_DONE;
> +
> +	/* Use the MAC address to locate the synthetic interface to
> +	 * associate with the VF interface.
> +	 */
> +	ndev = get_netvsc_bymac(vf_netdev->perm_addr);
> +	if (!ndev)
> +		return NOTIFY_DONE;
> +
> +	/* If network device is being removed, don't do anything */
> +	ndev_ctx = netdev_priv(ndev);
> +	if (!rtnl_dereference(ndev_ctx->nvdev))
> +		return NOTIFY_DONE;
> +
> +	if (netdev_failover_join(vf_netdev, ndev, netvsc_vf_handle_frame)) {
> +		netdev_err(vf_netdev, "could not join: %s", ndev->name);
> +		return NOTIFY_DONE;
> +	}
>   
>   	/* set slave flag before open to prevent IPv6 addrconf */
>   	vf_netdev->flags |= IFF_SLAVE;
>   
> +	dev_hold(vf_netdev);
> +
>   	schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT);
>   
>   	call_netdevice_notifiers(NETDEV_JOIN, vf_netdev);
>   
>   	netdev_info(vf_netdev, "joined to %s\n", ndev->name);
>   
> -	dev_hold(vf_netdev);
>   	rcu_assign_pointer(ndev_ctx->vf_netdev, vf_netdev);
>   
> -	return 0;
> +	return NOTIFY_OK;
>   }
>   
>   /* VF up/down change detected, schedule to change data path */
> -static int netvsc_vf_changed(struct net_device *vf_netdev,
> -			     struct net_device *ndev)
> +static int netvsc_vf_changed(struct net_device *vf_netdev)
>   {
>   	struct net_device_context *net_device_ctx;
>   	struct netvsc_device *netvsc_dev;
> +	struct net_device *ndev;
>   	bool vf_is_up = netif_running(vf_netdev);
>   
> +	ndev = netdev_failover_upper_get(vf_netdev);
> +	if (!ndev)
> +		return NOTIFY_DONE;
> +
>   	net_device_ctx = netdev_priv(ndev);
>   	netvsc_dev = rtnl_dereference(net_device_ctx->nvdev);
>   	if (!netvsc_dev)
> -		return -ENODEV;
> +		return NOTIFY_DONE;
>   
>   	netvsc_switch_datapath(ndev, vf_is_up);
>   	netdev_info(ndev, "Data path switched %s VF: %s\n",
>   		    vf_is_up ? "to" : "from", vf_netdev->name);
>   
> -	return 0;
> +	return NOTIFY_OK;
>   }
>   
> -static int netvsc_pre_unregister_vf(struct net_device *vf_netdev,
> -				    struct net_device *ndev)
> +static int netvsc_unregister_vf(struct net_device *vf_netdev)
>   {
>   	struct net_device_context *net_device_ctx;
> +	struct net_device *ndev;
>   
> -	net_device_ctx = netdev_priv(ndev);
> -	cancel_delayed_work_sync(&net_device_ctx->vf_takeover);
> -
> -	return 0;
> -}
> -
> -static int netvsc_unregister_vf(struct net_device *vf_netdev,
> -				struct net_device *ndev)
> -{
> -	struct net_device_context *net_device_ctx;
> +	ndev = netdev_failover_upper_get(vf_netdev);
> +	if (!ndev)
> +		return NOTIFY_DONE;
>   
>   	net_device_ctx = netdev_priv(ndev);
> +	if (cancel_delayed_work_sync(&net_device_ctx->vf_takeover))
> +		dev_put(vf_netdev);
>   
>   	netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name);
>   
> +	netdev_failover_unjoin(vf_netdev, ndev);
>   	RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL);
> -	dev_put(vf_netdev);
>   
> -	return 0;
> +	return NOTIFY_OK;
>   }
>   
> -static struct failover_ops netvsc_failover_ops = {
> -	.slave_pre_register	= netvsc_pre_register_vf,
> -	.slave_register		= netvsc_register_vf,
> -	.slave_pre_unregister	= netvsc_pre_unregister_vf,
> -	.slave_unregister	= netvsc_unregister_vf,
> -	.slave_link_change	= netvsc_vf_changed,
> -	.slave_handle_frame	= netvsc_vf_handle_frame,
> -};
> -
>   static int netvsc_probe(struct hv_device *dev,
>   			const struct hv_vmbus_device_id *dev_id)
>   {
> @@ -2009,6 +2030,8 @@ static int netvsc_probe(struct hv_device *dev,
>   
>   	memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
>   
> +	net->priv_flags |= IFF_FAILOVER;
> +
>   	/* hw_features computed in rndis_netdev_set_hwcaps() */
>   	net->features = net->hw_features |
>   		NETIF_F_HIGHDMA | NETIF_F_SG |
> @@ -2024,23 +2047,19 @@ static int netvsc_probe(struct hv_device *dev,
>   	else
>   		net->max_mtu = ETH_DATA_LEN;
>   
> -	ret = register_netdev(net);
> +	rtnl_lock();
> +	ret = register_netdevice(net);
>   	if (ret != 0) {
>   		pr_err("Unable to register netdev.\n");
>   		goto register_failed;
>   	}
>   
> -	net_device_ctx->failover = failover_register(net, &netvsc_failover_ops);
> -	if (IS_ERR(net_device_ctx->failover)) {
> -		ret = PTR_ERR(net_device_ctx->failover);
> -		goto err_failover;
> -	}
> -
> -	return ret;
> +	list_add(&net_device_ctx->list, &netvsc_dev_list);
> +	rtnl_unlock();
> +	return 0;
>   
> -err_failover:
> -	unregister_netdev(net);
>   register_failed:
> +	rtnl_unlock();
>   	rndis_filter_device_remove(dev, nvdev);
>   rndis_failed:
>   	free_percpu(net_device_ctx->vf_stats);
> @@ -2079,15 +2098,17 @@ static int netvsc_remove(struct hv_device *dev)
>   	 */
>   	rtnl_lock();
>   	vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
> -	if (vf_netdev)
> -		failover_slave_unregister(vf_netdev);
> +	if (vf_netdev) {
> +		netdev_failover_unjoin(vf_netdev, net);
> +		dev_put(vf_netdev);
> +	}
>   
>   	if (nvdev)
>   		rndis_filter_device_remove(dev, nvdev);
>   
>   	unregister_netdevice(net);
>   
> -	failover_unregister(ndev_ctx->failover);
> +	list_del(&ndev_ctx->list);
>   
>   	rtnl_unlock();
>   	rcu_read_unlock();
> @@ -2115,8 +2136,47 @@ static struct  hv_driver netvsc_drv = {
>   	.remove = netvsc_remove,
>   };
>   
> +/* On Hyper-V, every VF interface is matched with a corresponding
> + * synthetic interface. The synthetic interface is presented first
> + * to the guest. When the corresponding VF instance is registered,
> + * we will take care of switching the data path.
> + */
> +static int netvsc_netdev_event(struct notifier_block *this,
> +			       unsigned long event, void *ptr)
> +{
> +	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
> +
> +	/* Skip parent events */
> +	if (netif_is_failover(event_dev))
> +		return NOTIFY_DONE;
> +
> +	/* Avoid non-Ethernet type devices */
> +	if (event_dev->type != ARPHRD_ETHER)
> +		return NOTIFY_DONE;
> +
> +	switch (event) {
> +	case NETDEV_REGISTER:
> +		return netvsc_register_vf(event_dev);
> +
> +	case NETDEV_UNREGISTER:
> +		return netvsc_unregister_vf(event_dev);
> +
> +	case NETDEV_UP:
> +	case NETDEV_DOWN:
> +		return netvsc_vf_changed(event_dev);
> +
> +	default:
> +		return NOTIFY_DONE;
> +	}
> +}
> +
> +static struct notifier_block netvsc_netdev_notifier = {
> +	.notifier_call = netvsc_netdev_event,
> +};
> +
>   static void __exit netvsc_drv_exit(void)
>   {
> +	unregister_netdevice_notifier(&netvsc_netdev_notifier);
>   	vmbus_driver_unregister(&netvsc_drv);
>   }
>   
> @@ -2136,6 +2196,7 @@ static int __init netvsc_drv_init(void)
>   	if (ret)
>   		return ret;
>   
> +	register_netdevice_notifier(&netvsc_netdev_notifier);
>   	return 0;
>   }
>   
> diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
> index 83f7420ddea5..e0d30527f748 100644
> --- a/drivers/net/net_failover.c
> +++ b/drivers/net/net_failover.c
> @@ -28,6 +28,46 @@
>   #include <uapi/linux/if_arp.h>
>   #include <net/net_failover.h>
>   
> +static LIST_HEAD(net_failover_list);
> +
> +/* failover state */
> +struct net_failover_info {
> +	struct net_device *failover_dev;
> +
> +	/* list of failover virtual devices */
> +	struct list_head list;
> +
> +	/* primary netdev with same MAC */
> +	struct net_device __rcu *primary_dev;
> +
> +	/* standby netdev */
> +	struct net_device __rcu *standby_dev;
> +
> +	/* primary netdev stats */
> +	struct rtnl_link_stats64 primary_stats;
> +
> +	/* standby netdev stats */
> +	struct rtnl_link_stats64 standby_stats;
> +
> +	/* aggregated stats */
> +	struct rtnl_link_stats64 failover_stats;
> +
> +	/* spinlock while updating stats */
> +	spinlock_t stats_lock;
> +
> +	/* delayed setup of slave */
> +	struct delayed_work standby_init;
> +};
> +
> +#define FAILOVER_VLAN_FEATURES	(NETIF_F_HW_CSUM | NETIF_F_SG | \
> +				 NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
> +				 NETIF_F_HIGHDMA | NETIF_F_LRO)
> +
> +#define FAILOVER_ENC_FEATURES	(NETIF_F_HW_CSUM | NETIF_F_SG | \
> +				 NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
> +
> +#define FAILOVER_SETUP_INTERVAL	(HZ / 10)
> +
>   static bool net_failover_xmit_ready(struct net_device *dev)
>   {
>   	return netif_running(dev) && netif_carrier_ok(dev);
> @@ -460,22 +500,42 @@ static void net_failover_lower_state_changed(struct net_device *slave_dev,
>   	netdev_lower_state_changed(slave_dev, &info);
>   }
>   
> -static int net_failover_slave_pre_register(struct net_device *slave_dev,
> -					   struct net_device *failover_dev)
> +static struct net_device *get_net_failover_bymac(const u8 *mac)
>   {
> -	struct net_device *standby_dev, *primary_dev;
> +	struct net_failover_info *nfo_info;
> +
> +	ASSERT_RTNL();
> +
> +	list_for_each_entry(nfo_info, &net_failover_list, list) {
> +		struct net_device *failover_dev = nfo_info->failover_dev;
> +
> +		if (ether_addr_equal(mac, failover_dev->perm_addr))
> +			return failover_dev;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int net_failover_register_event(struct net_device *slave_dev)
> +{
> +	struct net_device *failover_dev, *standby_dev, *primary_dev;
>   	struct net_failover_info *nfo_info;
>   	bool slave_is_standby;
>   
> +	failover_dev = get_net_failover_bymac(slave_dev->perm_addr);
> +	if (!failover_dev)
> +		return NOTIFY_DONE;
> +
>   	nfo_info = netdev_priv(failover_dev);
>   	standby_dev = rtnl_dereference(nfo_info->standby_dev);
>   	primary_dev = rtnl_dereference(nfo_info->primary_dev);
>   	slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent;
>   	if (slave_is_standby ? standby_dev : primary_dev) {
> -		netdev_err(failover_dev, "%s attempting to register as slave dev when %s already present\n",
> +		netdev_err(failover_dev,
> +			   "%s attempting to register as slave dev when %s already present\n",
>   			   slave_dev->name,
>   			   slave_is_standby ? "standby" : "primary");
> -		return -EINVAL;
> +		return NOTIFY_DONE;
>   	}
>   
>   	/* We want to allow only a direct attached VF device as a primary
> @@ -484,23 +544,33 @@ static int net_failover_slave_pre_register(struct net_device *slave_dev,
>   	 */
>   	if (!slave_is_standby && (!slave_dev->dev.parent ||
>   				  !dev_is_pci(slave_dev->dev.parent)))
> -		return -EINVAL;
> +		return NOTIFY_DONE;
>   
>   	if (failover_dev->features & NETIF_F_VLAN_CHALLENGED &&
>   	    vlan_uses_dev(failover_dev)) {
> -		netdev_err(failover_dev, "Device %s is VLAN challenged and failover device has VLAN set up\n",
> +		netdev_err(failover_dev,
> +			   "Device %s is VLAN challenged and failover device has VLAN set up\n",
>   			   failover_dev->name);
> -		return -EINVAL;
> +		return NOTIFY_DONE;
>   	}
>   
> -	return 0;
> +	if (netdev_failover_join(slave_dev, failover_dev,
> +				 net_failover_handle_frame)) {
> +		netdev_err(failover_dev, "could not join: %s", slave_dev->name);
> +		return NOTIFY_DONE;
> +	}
> +
> +	/* Trigger rest of setup in process context */
> +	schedule_delayed_work(&nfo_info->standby_init, FAILOVER_SETUP_INTERVAL);
> +
> +	return NOTIFY_OK;
>   }
>   
> -static int net_failover_slave_register(struct net_device *slave_dev,
> -				       struct net_device *failover_dev)
> +static void __net_failover_setup(struct net_device *failover_dev)
>   {
> +	struct net_failover_info *nfo_info = netdev_priv(failover_dev);
> +	struct net_device *slave_dev = rtnl_dereference(nfo_info->standby_dev);
>   	struct net_device *standby_dev, *primary_dev;
> -	struct net_failover_info *nfo_info;
>   	bool slave_is_standby;
>   	u32 orig_mtu;
>   	int err;
> @@ -509,13 +579,12 @@ static int net_failover_slave_register(struct net_device *slave_dev,
>   	orig_mtu = slave_dev->mtu;
>   	err = dev_set_mtu(slave_dev, failover_dev->mtu);
>   	if (err) {
> -		netdev_err(failover_dev, "unable to change mtu of %s to %u register failed\n",
> +		netdev_err(failover_dev,
> +			   "unable to change mtu of %s to %u register failed\n",
>   			   slave_dev->name, failover_dev->mtu);
>   		goto done;
>   	}
>   
> -	dev_hold(slave_dev);
> -
>   	if (netif_running(failover_dev)) {
>   		err = dev_open(slave_dev);
>   		if (err && (err != -EBUSY)) {
> @@ -537,7 +606,6 @@ static int net_failover_slave_register(struct net_device *slave_dev,
>   		goto err_vlan_add;
>   	}
>   
> -	nfo_info = netdev_priv(failover_dev);
>   	standby_dev = rtnl_dereference(nfo_info->standby_dev);
>   	primary_dev = rtnl_dereference(nfo_info->primary_dev);
>   	slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent;
> @@ -562,52 +630,56 @@ static int net_failover_slave_register(struct net_device *slave_dev,
>   	netdev_info(failover_dev, "failover %s slave:%s registered\n",
>   		    slave_is_standby ? "standby" : "primary", slave_dev->name);
>   
> -	return 0;
> +	return;
>   
>   err_vlan_add:
>   	dev_uc_unsync(slave_dev, failover_dev);
>   	dev_mc_unsync(slave_dev, failover_dev);
>   	dev_close(slave_dev);
>   err_dev_open:
> -	dev_put(slave_dev);
>   	dev_set_mtu(slave_dev, orig_mtu);
>   done:
> -	return err;
> +	return;
>   }
>   
> -static int net_failover_slave_pre_unregister(struct net_device *slave_dev,
> -					     struct net_device *failover_dev)
> +static void net_failover_setup(struct work_struct *w)
>   {
> -	struct net_device *standby_dev, *primary_dev;
> -	struct net_failover_info *nfo_info;
> +	struct net_failover_info *nfo_info
> +		= container_of(w, struct net_failover_info, standby_init.work);
> +	struct net_device *failover_dev = nfo_info->failover_dev;
>   
> -	nfo_info = netdev_priv(failover_dev);
> -	primary_dev = rtnl_dereference(nfo_info->primary_dev);
> -	standby_dev = rtnl_dereference(nfo_info->standby_dev);
> -
> -	if (slave_dev != primary_dev && slave_dev != standby_dev)
> -		return -ENODEV;
> +	/* handle race with cancel delayed work on removal */
> +	if (!rtnl_trylock()) {
> +		schedule_delayed_work(&nfo_info->standby_init, 0);
> +		return;
> +	}
>   
> -	return 0;
> +	__net_failover_setup(failover_dev);
> +	rtnl_unlock();
>   }
>   
> -static int net_failover_slave_unregister(struct net_device *slave_dev,
> -					 struct net_device *failover_dev)
> +static int net_failover_unregister_event(struct net_device *slave_dev)
>   {
> -	struct net_device *standby_dev, *primary_dev;
> +	struct net_device *failover_dev, *primary_dev, *standby_dev;
>   	struct net_failover_info *nfo_info;
>   	bool slave_is_standby;
>   
> +	failover_dev = netdev_failover_upper_get(slave_dev);
> +	if (!failover_dev)
> +		return NOTIFY_DONE;
> +
>   	nfo_info = netdev_priv(failover_dev);
>   	primary_dev = rtnl_dereference(nfo_info->primary_dev);
>   	standby_dev = rtnl_dereference(nfo_info->standby_dev);
>   
> +	if (slave_dev != primary_dev && slave_dev != standby_dev)
> +		return NOTIFY_DONE;
> +
>   	vlan_vids_del_by_dev(slave_dev, failover_dev);
>   	dev_uc_unsync(slave_dev, failover_dev);
>   	dev_mc_unsync(slave_dev, failover_dev);
>   	dev_close(slave_dev);
>   
> -	nfo_info = netdev_priv(failover_dev);
>   	dev_get_stats(failover_dev, &nfo_info->failover_stats);
>   
>   	slave_is_standby = slave_dev->dev.parent == failover_dev->dev.parent;
> @@ -628,22 +700,25 @@ static int net_failover_slave_unregister(struct net_device *slave_dev,
>   	netdev_info(failover_dev, "failover %s slave:%s unregistered\n",
>   		    slave_is_standby ? "standby" : "primary", slave_dev->name);
>   
> -	return 0;
> +	return NOTIFY_OK;
>   }
>   
> -static int net_failover_slave_link_change(struct net_device *slave_dev,
> -					  struct net_device *failover_dev)
> +static int net_failover_link_event(struct net_device *slave_dev)
> +
>   {
> -	struct net_device *primary_dev, *standby_dev;
> +	struct net_device *failover_dev, *primary_dev, *standby_dev;
>   	struct net_failover_info *nfo_info;
>   
> -	nfo_info = netdev_priv(failover_dev);
> +	failover_dev = netdev_failover_upper_get(slave_dev);
> +	if (!failover_dev)
> +		return NOTIFY_DONE;
>   
> +	nfo_info = netdev_priv(failover_dev);
>   	primary_dev = rtnl_dereference(nfo_info->primary_dev);
>   	standby_dev = rtnl_dereference(nfo_info->standby_dev);
>   
>   	if (slave_dev != primary_dev && slave_dev != standby_dev)
> -		return -ENODEV;
> +		return NOTIFY_DONE;
>   
>   	if ((primary_dev && net_failover_xmit_ready(primary_dev)) ||
>   	    (standby_dev && net_failover_xmit_ready(standby_dev))) {
> @@ -657,43 +732,11 @@ static int net_failover_slave_link_change(struct net_device *slave_dev,
>   
>   	net_failover_lower_state_changed(slave_dev, primary_dev, standby_dev);
>   
> -	return 0;
> +	return NOTIFY_DONE;
>   }
>   
> -static int net_failover_slave_name_change(struct net_device *slave_dev,
> -					  struct net_device *failover_dev)
> -{
> -	struct net_device *primary_dev, *standby_dev;
> -	struct net_failover_info *nfo_info;
> -
> -	nfo_info = netdev_priv(failover_dev);
> -
> -	primary_dev = rtnl_dereference(nfo_info->primary_dev);
> -	standby_dev = rtnl_dereference(nfo_info->standby_dev);
> -
> -	if (slave_dev != primary_dev && slave_dev != standby_dev)
> -		return -ENODEV;
> -
> -	/* We need to bring up the slave after the rename by udev in case
> -	 * open failed with EBUSY when it was registered.
> -	 */
> -	dev_open(slave_dev);
> -
> -	return 0;
> -}
> -
> -static struct failover_ops net_failover_ops = {
> -	.slave_pre_register	= net_failover_slave_pre_register,
> -	.slave_register		= net_failover_slave_register,
> -	.slave_pre_unregister	= net_failover_slave_pre_unregister,
> -	.slave_unregister	= net_failover_slave_unregister,
> -	.slave_link_change	= net_failover_slave_link_change,
> -	.slave_name_change	= net_failover_slave_name_change,
> -	.slave_handle_frame	= net_failover_handle_frame,
> -};
> -
>   /**
> - * net_failover_create - Create and register a failover instance
> + * net_failover_create - Create and register a failover device
>    *
>    * @dev: standby netdev
>    *
> @@ -703,13 +746,12 @@ static struct failover_ops net_failover_ops = {
>    * the original standby netdev and a VF netdev with the same MAC gets
>    * registered as primary netdev.
>    *
> - * Return: pointer to failover instance
> + * Return: pointer to failover network device
>    */
> -struct failover *net_failover_create(struct net_device *standby_dev)
> +struct net_device *net_failover_create(struct net_device *standby_dev)
>   {
> -	struct device *dev = standby_dev->dev.parent;
> +	struct net_failover_info *nfo_info;
>   	struct net_device *failover_dev;
> -	struct failover *failover;
>   	int err;
>   
>   	/* Alloc at least 2 queues, for now we are going with 16 assuming
> @@ -717,18 +759,22 @@ struct failover *net_failover_create(struct net_device *standby_dev)
>   	 */
>   	failover_dev = alloc_etherdev_mq(sizeof(struct net_failover_info), 16);
>   	if (!failover_dev) {
> -		dev_err(dev, "Unable to allocate failover_netdev!\n");
> -		return ERR_PTR(-ENOMEM);
> +		netdev_err(standby_dev, "Unable to allocate failover_netdev!\n");
> +		return NULL;
>   	}
>   
> +	nfo_info = netdev_priv(failover_dev);
>   	dev_net_set(failover_dev, dev_net(standby_dev));
> -	SET_NETDEV_DEV(failover_dev, dev);
> +	nfo_info->failover_dev = failover_dev;
> +	INIT_DELAYED_WORK(&nfo_info->standby_init, net_failover_setup);
>   
>   	failover_dev->netdev_ops = &failover_dev_ops;
>   	failover_dev->ethtool_ops = &failover_ethtool_ops;
>   
>   	/* Initialize the device options */
> -	failover_dev->priv_flags |= IFF_UNICAST_FLT | IFF_NO_QUEUE;
> +	failover_dev->priv_flags |= IFF_UNICAST_FLT |
> +				    IFF_NO_QUEUE |
> +				    IFF_FAILOVER;
>   	failover_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE |
>   				       IFF_TX_SKB_SHARING);
>   
> @@ -746,29 +792,38 @@ struct failover *net_failover_create(struct net_device *standby_dev)
>   	failover_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
>   	failover_dev->features |= failover_dev->hw_features;
>   
> -	memcpy(failover_dev->dev_addr, standby_dev->dev_addr,
> -	       failover_dev->addr_len);
> +	ether_addr_copy(failover_dev->dev_addr, standby_dev->dev_addr);
> +	ether_addr_copy(failover_dev->perm_addr, standby_dev->perm_addr);
>   
>   	failover_dev->min_mtu = standby_dev->min_mtu;
>   	failover_dev->max_mtu = standby_dev->max_mtu;
>   
> -	err = register_netdev(failover_dev);
> +	netif_carrier_off(failover_dev);
> +
> +	rtnl_lock();
> +	err = register_netdevice(failover_dev);
>   	if (err) {
> -		dev_err(dev, "Unable to register failover_dev!\n");
> +		netdev_err(standby_dev, "Unable to register failover_dev!\n");
>   		goto err_register_netdev;
>   	}
>   
> -	netif_carrier_off(failover_dev);
> +	err = netdev_failover_join(standby_dev, failover_dev,
> +				   net_failover_handle_frame);
> +	if (err) {
> +		netdev_err(failover_dev, "Unable to join with %s\n",
> +			   standby_dev->name);
> +		goto err_failover_join;
> +	}
>   
> -	failover = failover_register(failover_dev, &net_failover_ops);
> -	if (IS_ERR(failover))
> -		goto err_failover_register;
> +	list_add(&nfo_info->list, &net_failover_list);
> +	rtnl_unlock();
>   
> -	return failover;
> +	return failover_dev;
>   
> -err_failover_register:
> -	unregister_netdev(failover_dev);
> +err_failover_join:
> +	unregister_netdevice(failover_dev);
>   err_register_netdev:
> +	rtnl_unlock();
>   	free_netdev(failover_dev);
>   
>   	return ERR_PTR(err);
> @@ -786,31 +841,27 @@ EXPORT_SYMBOL_GPL(net_failover_create);
>    * netdev. Used by paravirtual drivers that use 3-netdev model.
>    *
>    */
> -void net_failover_destroy(struct failover *failover)
> +void net_failover_destroy(struct net_device *failover_dev)
>   {
> -	struct net_failover_info *nfo_info;
> -	struct net_device *failover_dev;
> +	struct net_failover_info *nfo_info = netdev_priv(failover_dev);
>   	struct net_device *slave_dev;
>   
> -	if (!failover)
> -		return;
> -
> -	failover_dev = rcu_dereference(failover->failover_dev);
> -	nfo_info = netdev_priv(failover_dev);
> -
>   	netif_device_detach(failover_dev);
>   
>   	rtnl_lock();
> -
>   	slave_dev = rtnl_dereference(nfo_info->primary_dev);
> -	if (slave_dev)
> -		failover_slave_unregister(slave_dev);
> +	if (slave_dev) {
> +		netdev_failover_unjoin(slave_dev, failover_dev);
> +		dev_put(slave_dev);
> +	}
>   
>   	slave_dev = rtnl_dereference(nfo_info->standby_dev);
> -	if (slave_dev)
> -		failover_slave_unregister(slave_dev);
> +	if (slave_dev) {
> +		netdev_failover_unjoin(slave_dev, failover_dev);
> +		dev_put(slave_dev);
> +	}
>   
> -	failover_unregister(failover);
> +	list_del(&nfo_info->list);
>   
>   	unregister_netdevice(failover_dev);
>   
> @@ -820,9 +871,53 @@ void net_failover_destroy(struct failover *failover)
>   }
>   EXPORT_SYMBOL_GPL(net_failover_destroy);
>   
> +static int net_failover_event(struct notifier_block *this,
> +			      unsigned long event, void *ptr)
> +{
> +	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
> +
> +	/* Skip parent events */
> +	if (netif_is_failover(event_dev))
> +		return NOTIFY_DONE;
> +
> +	/* Avoid non-Ethernet type devices */
> +	if (event_dev->type != ARPHRD_ETHER)
> +		return NOTIFY_DONE;
> +
> +	/* Avoid Vlan dev with same MAC registering as VF */
> +	if (is_vlan_dev(event_dev))
> +		return NOTIFY_DONE;
> +
> +	/* Avoid Bonding master dev with same MAC registering as VF */
> +	if ((event_dev->priv_flags & IFF_BONDING) &&
> +	    (event_dev->flags & IFF_MASTER))
> +		return NOTIFY_DONE;
> +
> +	switch (event) {
> +	case NETDEV_REGISTER:
> +		return net_failover_register_event(event_dev);
> +
> +	case NETDEV_UNREGISTER:
> +		return net_failover_unregister_event(event_dev);
> +
> +	case NETDEV_UP:
> +	case NETDEV_DOWN:
> +	case NETDEV_CHANGE:
> +		return net_failover_link_event(event_dev);
> +
> +	default:
> +		return NOTIFY_DONE;
> +	}
> +}
> +
> +static struct notifier_block net_failover_notifier = {
> +	.notifier_call = net_failover_event,
> +};
> +
>   static __init int
>   net_failover_init(void)
>   {
> +	register_netdevice_notifier(&net_failover_notifier);
>   	return 0;
>   }
>   module_init(net_failover_init);
> @@ -830,6 +925,7 @@ module_init(net_failover_init);
>   static __exit
>   void net_failover_exit(void)
>   {
> +	unregister_netdevice_notifier(&net_failover_notifier);
>   }
>   module_exit(net_failover_exit);
>   
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 6d710b8b41c5..b40ae28dac93 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -215,7 +215,7 @@ struct virtnet_info {
>   	unsigned long guest_offloads;
>   
>   	/* failover when STANDBY feature enabled */
> -	struct failover *failover;
> +	struct net_device *failover;
>   };
>   
>   struct padded_vnet_hdr {
> @@ -2930,11 +2930,10 @@ static int virtnet_probe(struct virtio_device *vdev)
>   	virtnet_init_settings(dev);
>   
>   	if (virtio_has_feature(vdev, VIRTIO_NET_F_STANDBY)) {
> -		vi->failover = net_failover_create(vi->dev);
> -		if (IS_ERR(vi->failover)) {
> -			err = PTR_ERR(vi->failover);
> +		err = -ENOMEM;
> +		vi->failover = net_failover_create(dev);
> +		if (!vi->failover)
>   			goto free_vqs;
> -		}
>   	}
>   
>   	err = register_netdev(dev);
> diff --git a/include/net/failover.h b/include/net/failover.h
> index bb15438f39c7..22d6c1369101 100644
> --- a/include/net/failover.h
> +++ b/include/net/failover.h
> @@ -6,31 +6,10 @@
>   
>   #include <linux/netdevice.h>
>   
> -struct failover_ops {
> -	int (*slave_pre_register)(struct net_device *slave_dev,
> -				  struct net_device *failover_dev);
> -	int (*slave_register)(struct net_device *slave_dev,
> -			      struct net_device *failover_dev);
> -	int (*slave_pre_unregister)(struct net_device *slave_dev,
> -				    struct net_device *failover_dev);
> -	int (*slave_unregister)(struct net_device *slave_dev,
> -				struct net_device *failover_dev);
> -	int (*slave_link_change)(struct net_device *slave_dev,
> -				 struct net_device *failover_dev);
> -	int (*slave_name_change)(struct net_device *slave_dev,
> -				 struct net_device *failover_dev);
> -	rx_handler_result_t (*slave_handle_frame)(struct sk_buff **pskb);
> -};
> -
> -struct failover {
> -	struct list_head list;
> -	struct net_device __rcu *failover_dev;
> -	struct failover_ops __rcu *ops;
> -};
> -
> -struct failover *failover_register(struct net_device *dev,
> -				   struct failover_ops *ops);
> -void failover_unregister(struct failover *failover);
> -int failover_slave_unregister(struct net_device *slave_dev);
> +int netdev_failover_join(struct net_device *lower, struct net_device *upper,
> +			 rx_handler_func_t *rx_handler);
> +struct net_device *netdev_failover_upper_get(struct net_device *lower);
> +void netdev_failover_unjoin(struct net_device *lower,
> +			    struct net_device *upper);
>   
>   #endif /* _FAILOVER_H */
> diff --git a/include/net/net_failover.h b/include/net/net_failover.h
> index b12a1c469d1c..a99b3b00b4e3 100644
> --- a/include/net/net_failover.h
> +++ b/include/net/net_failover.h
> @@ -6,35 +6,7 @@
>   
>   #include <net/failover.h>
>   
> -/* failover state */
> -struct net_failover_info {
> -	/* primary netdev with same MAC */
> -	struct net_device __rcu *primary_dev;
> -
> -	/* standby netdev */
> -	struct net_device __rcu *standby_dev;
> -
> -	/* primary netdev stats */
> -	struct rtnl_link_stats64 primary_stats;
> -
> -	/* standby netdev stats */
> -	struct rtnl_link_stats64 standby_stats;
> -
> -	/* aggregated stats */
> -	struct rtnl_link_stats64 failover_stats;
> -
> -	/* spinlock while updating stats */
> -	spinlock_t stats_lock;
> -};
> -
> -struct failover *net_failover_create(struct net_device *standby_dev);
> -void net_failover_destroy(struct failover *failover);
> -
> -#define FAILOVER_VLAN_FEATURES	(NETIF_F_HW_CSUM | NETIF_F_SG | \
> -				 NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
> -				 NETIF_F_HIGHDMA | NETIF_F_LRO)
> -
> -#define FAILOVER_ENC_FEATURES	(NETIF_F_HW_CSUM | NETIF_F_SG | \
> -				 NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
> +struct net_device *net_failover_create(struct net_device *standby_dev);
> +void net_failover_destroy(struct net_device *failover_dev);
>   
>   #endif /* _NET_FAILOVER_H */
> diff --git a/net/Kconfig b/net/Kconfig
> index f738a6f27665..697d84202695 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -433,17 +433,8 @@ config PAGE_POOL
>          bool
>   
>   config FAILOVER
> -	tristate "Generic failover module"
> -	help
> -	  The failover module provides a generic interface for paravirtual
> -	  drivers to register a netdev and a set of ops with a failover
> -	  instance. The ops are used as event handlers that get called to
> -	  handle netdev register/unregister/link change/name change events
> -	  on slave pci ethernet devices with the same mac address as the
> -	  failover netdev. This enables paravirtual drivers to use a
> -	  VF as an accelerated low latency datapath. It also allows live
> -	  migration of VMs with direct attached VFs by failing over to the
> -	  paravirtual datapath when the VF is unplugged.
> +	bool
> +	default n
>   
>   endif   # if NET
>   
> diff --git a/net/core/failover.c b/net/core/failover.c
> index 4a92a98ccce9..499f0fd7e4d3 100644
> --- a/net/core/failover.c
> +++ b/net/core/failover.c
> @@ -1,10 +1,8 @@
>   // SPDX-License-Identifier: GPL-2.0
>   /* Copyright (c) 2018, Intel Corporation. */
>   
> -/* A common module to handle registrations and notifications for paravirtual
> +/* A library for managing chained upper/oower devices such as
>    * drivers to enable accelerated datapath and support VF live migration.
> - *
> - * The notifier and event handling code is based on netvsc driver.
>    */
>   
>   #include <linux/module.h>
> @@ -14,302 +12,62 @@
>   #include <linux/if_vlan.h>
>   #include <net/failover.h>
>   
> -static LIST_HEAD(failover_list);
> -static DEFINE_SPINLOCK(failover_lock);
> -
> -static struct net_device *failover_get_bymac(u8 *mac, struct failover_ops **ops)
> -{
> -	struct net_device *failover_dev;
> -	struct failover *failover;
> -
> -	spin_lock(&failover_lock);
> -	list_for_each_entry(failover, &failover_list, list) {
> -		failover_dev = rtnl_dereference(failover->failover_dev);
> -		if (ether_addr_equal(failover_dev->perm_addr, mac)) {
> -			*ops = rtnl_dereference(failover->ops);
> -			spin_unlock(&failover_lock);
> -			return failover_dev;
> -		}
> -	}
> -	spin_unlock(&failover_lock);
> -	return NULL;
> -}
> -
> -/**
> - * failover_slave_register - Register a slave netdev
> - *
> - * @slave_dev: slave netdev that is being registered
> - *
> - * Registers a slave device to a failover instance. Only ethernet devices
> - * are supported.
> - */
> -static int failover_slave_register(struct net_device *slave_dev)
> +/* failover_join - Join an lower netdev with an upper device. */
> +int netdev_failover_join(struct net_device *lower_dev,
> +			 struct net_device *upper_dev,
> +			 rx_handler_func_t *rx_handler)
>   {
> -	struct netdev_lag_upper_info lag_upper_info;
> -	struct net_device *failover_dev;
> -	struct failover_ops *fops;
>   	int err;
>   
> -	if (slave_dev->type != ARPHRD_ETHER)
> -		goto done;
> -
>   	ASSERT_RTNL();
>   
> -	failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
> -	if (!failover_dev)
> -		goto done;
> +	/* Don't allow joining devices of different protocols */
> +	if (upper_dev->type != lower_dev->type)
> +		return -EINVAL;
>   
> -	if (fops && fops->slave_pre_register &&
> -	    fops->slave_pre_register(slave_dev, failover_dev))
> -		goto done;
> -
> -	err = netdev_rx_handler_register(slave_dev, fops->slave_handle_frame,
> -					 failover_dev);
> +	err = netdev_rx_handler_register(lower_dev, rx_handler, upper_dev);
>   	if (err) {
> -		netdev_err(slave_dev, "can not register failover rx handler (err = %d)\n",
> +		netdev_err(lower_dev,
> +			   "can not register failover rx handler (err = %d)\n",
>   			   err);
> -		goto done;
> +		return err;
>   	}
>   
> -	lag_upper_info.tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP;
> -	err = netdev_master_upper_dev_link(slave_dev, failover_dev, NULL,
> -					   &lag_upper_info, NULL);
> +	err = netdev_master_upper_dev_link(lower_dev, upper_dev, NULL,
> +					   NULL, NULL);
>   	if (err) {
> -		netdev_err(slave_dev, "can not set failover device %s (err = %d)\n",
> -			   failover_dev->name, err);
> -		goto err_upper_link;
> +		netdev_err(lower_dev,
> +			   "can not set failover device %s (err = %d)\n",
> +			   upper_dev->name, err);
> +		netdev_rx_handler_unregister(lower_dev);
> +		return err;
>   	}
>   
> -	slave_dev->priv_flags |= IFF_FAILOVER_SLAVE;
> -
> -	if (fops && fops->slave_register &&
> -	    !fops->slave_register(slave_dev, failover_dev))
> -		return NOTIFY_OK;
> -
> -	netdev_upper_dev_unlink(slave_dev, failover_dev);
> -	slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE;
> -err_upper_link:
> -	netdev_rx_handler_unregister(slave_dev);
> -done:
> -	return NOTIFY_DONE;
> -}
> -
> -/**
> - * failover_slave_unregister - Unregister a slave netdev
> - *
> - * @slave_dev: slave netdev that is being unregistered
> - *
> - * Unregisters a slave device from a failover instance.
> - */
> -int failover_slave_unregister(struct net_device *slave_dev)
> -{
> -	struct net_device *failover_dev;
> -	struct failover_ops *fops;
> -
> -	if (!netif_is_failover_slave(slave_dev))
> -		goto done;
> -
> -	ASSERT_RTNL();
> -
> -	failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
> -	if (!failover_dev)
> -		goto done;
> -
> -	if (fops && fops->slave_pre_unregister &&
> -	    fops->slave_pre_unregister(slave_dev, failover_dev))
> -		goto done;
> -
> -	netdev_rx_handler_unregister(slave_dev);
> -	netdev_upper_dev_unlink(slave_dev, failover_dev);
> -	slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE;
> -
> -	if (fops && fops->slave_unregister &&
> -	    !fops->slave_unregister(slave_dev, failover_dev))
> -		return NOTIFY_OK;
> -
> -done:
> -	return NOTIFY_DONE;
> +	dev_hold(lower_dev);
> +	lower_dev->priv_flags |= IFF_FAILOVER_SLAVE;
> +	return 0;
>   }
> -EXPORT_SYMBOL_GPL(failover_slave_unregister);
> +EXPORT_SYMBOL_GPL(netdev_failover_join);
>   
> -static int failover_slave_link_change(struct net_device *slave_dev)
> +/* Find upper network device for failover slave device */
> +struct net_device *netdev_failover_upper_get(struct net_device *lower_dev)
>   {
> -	struct net_device *failover_dev;
> -	struct failover_ops *fops;
> -
> -	if (!netif_is_failover_slave(slave_dev))
> -		goto done;
> -
> -	ASSERT_RTNL();
> -
> -	failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
> -	if (!failover_dev)
> -		goto done;
> -
> -	if (!netif_running(failover_dev))
> -		goto done;
> +	if (!netif_is_failover_slave(lower_dev))
> +		return NULL;
>   
> -	if (fops && fops->slave_link_change &&
> -	    !fops->slave_link_change(slave_dev, failover_dev))
> -		return NOTIFY_OK;
> -
> -done:
> -	return NOTIFY_DONE;
> +	return netdev_master_upper_dev_get(lower_dev);
>   }
> +EXPORT_SYMBOL_GPL(netdev_failover_upper_get);
>   
> -static int failover_slave_name_change(struct net_device *slave_dev)
> +/* failover_unjoin - Break connection between lower and upper device. */
> +void netdev_failover_unjoin(struct net_device *lower_dev,
> +			    struct net_device *upper_dev)
>   {
> -	struct net_device *failover_dev;
> -	struct failover_ops *fops;
> -
> -	if (!netif_is_failover_slave(slave_dev))
> -		goto done;
> -
>   	ASSERT_RTNL();
>   
> -	failover_dev = failover_get_bymac(slave_dev->perm_addr, &fops);
> -	if (!failover_dev)
> -		goto done;
> -
> -	if (!netif_running(failover_dev))
> -		goto done;
> -
> -	if (fops && fops->slave_name_change &&
> -	    !fops->slave_name_change(slave_dev, failover_dev))
> -		return NOTIFY_OK;
> -
> -done:
> -	return NOTIFY_DONE;
> -}
> -
> -static int
> -failover_event(struct notifier_block *this, unsigned long event, void *ptr)
> -{
> -	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
> -
> -	/* Skip parent events */
> -	if (netif_is_failover(event_dev))
> -		return NOTIFY_DONE;
> -
> -	switch (event) {
> -	case NETDEV_REGISTER:
> -		return failover_slave_register(event_dev);
> -	case NETDEV_UNREGISTER:
> -		return failover_slave_unregister(event_dev);
> -	case NETDEV_UP:
> -	case NETDEV_DOWN:
> -	case NETDEV_CHANGE:
> -		return failover_slave_link_change(event_dev);
> -	case NETDEV_CHANGENAME:
> -		return failover_slave_name_change(event_dev);
> -	default:
> -		return NOTIFY_DONE;
> -	}
> -}
> -
> -static struct notifier_block failover_notifier = {
> -	.notifier_call = failover_event,
> -};
> -
> -static void
> -failover_existing_slave_register(struct net_device *failover_dev)
> -{
> -	struct net *net = dev_net(failover_dev);
> -	struct net_device *dev;
> -
> -	rtnl_lock();
> -	for_each_netdev(net, dev) {
> -		if (netif_is_failover(dev))
> -			continue;
> -		if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr))
> -			failover_slave_register(dev);
> -	}
> -	rtnl_unlock();
> -}
> -
> -/**
> - * failover_register - Register a failover instance
> - *
> - * @dev: failover netdev
> - * @ops: failover ops
> - *
> - * Allocate and register a failover instance for a failover netdev. ops
> - * provides handlers for slave device register/unregister/link change/
> - * name change events.
> - *
> - * Return: pointer to failover instance
> - */
> -struct failover *failover_register(struct net_device *dev,
> -				   struct failover_ops *ops)
> -{
> -	struct failover *failover;
> -
> -	if (dev->type != ARPHRD_ETHER)
> -		return ERR_PTR(-EINVAL);
> -
> -	failover = kzalloc(sizeof(*failover), GFP_KERNEL);
> -	if (!failover)
> -		return ERR_PTR(-ENOMEM);
> -
> -	rcu_assign_pointer(failover->ops, ops);
> -	dev_hold(dev);
> -	dev->priv_flags |= IFF_FAILOVER;
> -	rcu_assign_pointer(failover->failover_dev, dev);
> -
> -	spin_lock(&failover_lock);
> -	list_add_tail(&failover->list, &failover_list);
> -	spin_unlock(&failover_lock);
> -
> -	netdev_info(dev, "failover master:%s registered\n", dev->name);
> -
> -	failover_existing_slave_register(dev);
> -
> -	return failover;
> -}
> -EXPORT_SYMBOL_GPL(failover_register);
> -
> -/**
> - * failover_unregister - Unregister a failover instance
> - *
> - * @failover: pointer to failover instance
> - *
> - * Unregisters and frees a failover instance.
> - */
> -void failover_unregister(struct failover *failover)
> -{
> -	struct net_device *failover_dev;
> -
> -	failover_dev = rcu_dereference(failover->failover_dev);
> -
> -	netdev_info(failover_dev, "failover master:%s unregistered\n",
> -		    failover_dev->name);
> -
> -	failover_dev->priv_flags &= ~IFF_FAILOVER;
> -	dev_put(failover_dev);
> -
> -	spin_lock(&failover_lock);
> -	list_del(&failover->list);
> -	spin_unlock(&failover_lock);
> -
> -	kfree(failover);
> +	netdev_rx_handler_unregister(lower_dev);
> +	netdev_upper_dev_unlink(lower_dev, upper_dev);
> +	dev_put(lower_dev);
> +	lower_dev->priv_flags &= ~IFF_FAILOVER_SLAVE;
>   }
> -EXPORT_SYMBOL_GPL(failover_unregister);
> -
> -static __init int
> -failover_init(void)
> -{
> -	register_netdevice_notifier(&failover_notifier);
> -
> -	return 0;
> -}
> -module_init(failover_init);
> -
> -static __exit
> -void failover_exit(void)
> -{
> -	unregister_netdevice_notifier(&failover_notifier);
> -}
> -module_exit(failover_exit);
> -
> -MODULE_DESCRIPTION("Generic failover infrastructure/interface");
> -MODULE_LICENSE("GPL v2");
> +EXPORT_SYMBOL_GPL(netdev_failover_unjoin);

^ permalink raw reply

* Re: [PATCH v5 1/3] drivers core: refactor device_shutdown
From: Pavel Tatashin @ 2018-06-05 17:14 UTC (permalink / raw)
  To: andy.shevchenko
  Cc: Steven Sistare, Daniel Jordan, LKML, jeffrey.t.kirsher,
	intel-wired-lan, netdev, gregkh, Alexander Duyck, tobin
In-Reply-To: <CAHp75Vduh4FG=K56-8bhoDPic_BXcV3UfV8Mxp_UZOeqeJ6F8w@mail.gmail.com>

> FWIW,
> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
>

Thank you Andy.

Pavel

^ permalink raw reply

* Re: [PATCH iproute2 v2 1/2] ip: display netns name instead of nsid
From: Stephen Hemminger @ 2018-06-05 16:52 UTC (permalink / raw)
  To: Nicolas Dichtel; +Cc: netdev
In-Reply-To: <20180605130831.8175-2-nicolas.dichtel@6wind.com>

On Tue,  5 Jun 2018 15:08:30 +0200
Nicolas Dichtel <nicolas.dichtel@6wind.com> wrote:

>  
> +char *get_name_from_nsid(int nsid)
> +{
> +	struct nsid_cache *c;
> +
> +	netns_nsid_socket_init();
> +	netns_map_init();
> +
> +	c = netns_map_get_by_nsid(nsid);
> +	if (c)
> +		return c->name;
> +
> +	return NULL;
> +}
> +

This is better, but now there is a different problem.
When doing multiple interfaces, won't the initialization code be called twice?

^ permalink raw reply

* [Patch net v2] netdev-FAQ: clarify DaveM's position for stable backports
From: Cong Wang @ 2018-06-05 16:48 UTC (permalink / raw)
  To: netdev; +Cc: Cong Wang, stable, Greg Kroah-Hartman

Per discussion with David at netconf 2018, let's clarify
DaveM's position of handling stable backports in netdev-FAQ.

This is important for people relying on upstream -stable
releases.

Cc: stable@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
---
 Documentation/networking/netdev-FAQ.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/networking/netdev-FAQ.txt b/Documentation/networking/netdev-FAQ.txt
index 2a3278d5cf35..fa951b820b25 100644
--- a/Documentation/networking/netdev-FAQ.txt
+++ b/Documentation/networking/netdev-FAQ.txt
@@ -179,6 +179,15 @@ A: No.  See above answer.  In short, if you think it really belongs in
    dash marker line as described in Documentation/process/submitting-patches.rst to
    temporarily embed that information into the patch that you send.
 
+Q: Are all networking bug fixes backported to all stable releases?
+
+A: Due to capacity, Dave could only take care of the backports for the last
+   2 stable releases. For earlier stable releases, each stable branch maintainer
+   is supposed to take care of them. If you find any patch is missing from an
+   earlier stable branch, please notify stable@vger.kernel.org with either a
+   commit ID or a formal patch backported, and CC Dave and other relevant
+   networking developers.
+
 Q: Someone said that the comment style and coding convention is different
    for the networking content.  Is this true?
 
-- 
2.13.0

^ permalink raw reply related

* Re: [PATCH] r8169: Reinstate ALDPS and ASPM support
From: Florian Fainelli @ 2018-06-05 16:47 UTC (permalink / raw)
  To: David Miller, andrew
  Cc: kai.heng.feng, hayeswang, hkallweit1, romieu, netdev,
	linux-kernel, ryankao, jiri
In-Reply-To: <20180605.101532.899235999013307302.davem@davemloft.net>

On 06/05/2018 07:15 AM, David Miller wrote:
> From: Andrew Lunn <andrew@lunn.ch>
> Date: Tue, 5 Jun 2018 16:11:14 +0200
> 
>> No module parameter please. Just turn it on by default. Assuming
>> testing shows works.
> 
> Agreed.

devlink would be a good candidate to add such configuration attributes,
since you would be operating on the PCI function itself, thus allowing
this to be on a per-device instance basis as opposed to global, which is
what a module parameter is.
-- 
Florian

^ permalink raw reply

* Re: [PATCH net-next] rtnetlink: validate attributes in do_setlink()
From: David Miller @ 2018-06-05 16:46 UTC (permalink / raw)
  To: edumazet; +Cc: netdev, eric.dumazet, dvyukov
In-Reply-To: <20180605162519.230428-1-edumazet@google.com>

From: Eric Dumazet <edumazet@google.com>
Date: Tue,  5 Jun 2018 09:25:19 -0700

> It seems that rtnl_group_changelink() can call do_setlink
> while a prior call to validate_linkmsg(dev = NULL, ...) could
> not validate IFLA_ADDRESS / IFLA_BROADCAST
> 
> Make sure do_setlink() calls validate_linkmsg() instead
> of letting its callers having this responsibility.
> 
> With help from Dmitry Vyukov, thanks a lot !
 ...
> Fixes: e7ed828f10bd ("netlink: support setting devgroup parameters")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: syzbot <syzkaller@googlegroups.com>

Applied and queued up for -stable.

^ permalink raw reply

* Re: [PATCH net-next] rtnetlink: validate attributes in do_setlink()
From: David Miller @ 2018-06-05 16:45 UTC (permalink / raw)
  To: eric.dumazet; +Cc: edumazet, netdev, dvyukov
In-Reply-To: <66acac36-a304-b743-8c6e-ef9cc87366d3@gmail.com>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Tue, 5 Jun 2018 09:42:54 -0700

> On 06/05/2018 09:41 AM, David Miller wrote:
>> From: Eric Dumazet <edumazet@google.com>
>> Date: Tue,  5 Jun 2018 09:25:19 -0700
>> 
>>> It seems that rtnl_group_changelink() can call do_setlink
>>> while a prior call to validate_linkmsg(dev = NULL, ...) could
>>> not validate IFLA_ADDRESS / IFLA_BROADCAST
>>>
>>> Make sure do_setlink() calls validate_linkmsg() instead
>>> of letting its callers having this responsibility.
>> 
>> But now rtnl_newlink() will validate_linkmsg() twice....
>> 
> 
> Yes, is it a problem ? That is hardly fast path :)

Not a problem, just making sure you were aware.

^ permalink raw reply

* Re: [PATCH v5 1/3] drivers core: refactor device_shutdown
From: Andy Shevchenko @ 2018-06-05 16:44 UTC (permalink / raw)
  To: Pavel Tatashin
  Cc: Steven Sistare, Daniel Jordan, Linux Kernel Mailing List,
	Kirsher, Jeffrey T, intel-wired-lan, netdev, Greg Kroah-Hartman,
	Alexander Duyck, tobin
In-Reply-To: <20180516024004.28977-2-pasha.tatashin@oracle.com>

On Wed, May 16, 2018 at 5:40 AM, Pavel Tatashin
<pasha.tatashin@oracle.com> wrote:
> device_shutdown() traverses through the list of devices, and calls
> dev->{bug/driver}->shutdown() for each entry in the list.
>
> Refactor the function by keeping device_shutdown() to do the logic of
> traversing the list of devices, and device_shutdown_one() to perform the
> actual shutdown operation on one device.
>

FWIW,
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

> Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com>
> ---
>  drivers/base/core.c | 50 +++++++++++++++++++++++++++------------------
>  1 file changed, 30 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index b610816eb887..ed189f6d1a2f 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -2765,6 +2765,35 @@ int device_move(struct device *dev, struct device *new_parent,
>  }
>  EXPORT_SYMBOL_GPL(device_move);
>
> +/*
> + * device_shutdown_one - call ->shutdown() for the device passed as
> + * argument.
> + */
> +static void device_shutdown_one(struct device *dev)
> +{
> +       /* Don't allow any more runtime suspends */
> +       pm_runtime_get_noresume(dev);
> +       pm_runtime_barrier(dev);
> +
> +       if (dev->class && dev->class->shutdown_pre) {
> +               if (initcall_debug)
> +                       dev_info(dev, "shutdown_pre\n");
> +               dev->class->shutdown_pre(dev);
> +       }
> +       if (dev->bus && dev->bus->shutdown) {
> +               if (initcall_debug)
> +                       dev_info(dev, "shutdown\n");
> +               dev->bus->shutdown(dev);
> +       } else if (dev->driver && dev->driver->shutdown) {
> +               if (initcall_debug)
> +                       dev_info(dev, "shutdown\n");
> +               dev->driver->shutdown(dev);
> +       }
> +
> +       /* decrement the reference counter */
> +       put_device(dev);
> +}
> +
>  /**
>   * device_shutdown - call ->shutdown() on each device to shutdown.
>   */
> @@ -2801,30 +2830,11 @@ void device_shutdown(void)
>                         device_lock(parent);
>                 device_lock(dev);
>
> -               /* Don't allow any more runtime suspends */
> -               pm_runtime_get_noresume(dev);
> -               pm_runtime_barrier(dev);
> -
> -               if (dev->class && dev->class->shutdown_pre) {
> -                       if (initcall_debug)
> -                               dev_info(dev, "shutdown_pre\n");
> -                       dev->class->shutdown_pre(dev);
> -               }
> -               if (dev->bus && dev->bus->shutdown) {
> -                       if (initcall_debug)
> -                               dev_info(dev, "shutdown\n");
> -                       dev->bus->shutdown(dev);
> -               } else if (dev->driver && dev->driver->shutdown) {
> -                       if (initcall_debug)
> -                               dev_info(dev, "shutdown\n");
> -                       dev->driver->shutdown(dev);
> -               }
> -
> +               device_shutdown_one(dev);
>                 device_unlock(dev);
>                 if (parent)
>                         device_unlock(parent);
>
> -               put_device(dev);
>                 put_device(parent);
>
>                 spin_lock(&devices_kset->list_lock);
> --
> 2.17.0
>



-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [Patch net-next] netdev-FAQ: clarify DaveM's position for stable backports
From: Cong Wang @ 2018-06-05 16:44 UTC (permalink / raw)
  To: David Miller; +Cc: Linux Kernel Network Developers, stable, Greg KH
In-Reply-To: <20180605.094347.1260769683352764390.davem@davemloft.net>

On Tue, Jun 5, 2018 at 6:43 AM, David Miller <davem@davemloft.net> wrote:
> From: Cong Wang <xiyou.wangcong@gmail.com>
> Date: Mon,  4 Jun 2018 11:07:19 -0700
>
>> +Q: Are all networking bug fixes backported to all stable releases?
>> +
>> +A: Due to capacity, Dave could only take care of the backports for the last
>> +   3 stable releases.
>
> As Greg stated, I only do 2 not 3.

Sure, will send v2.
We just need a number here. :)

Thanks!

^ permalink raw reply

* Re: [PATCH net-next] rtnetlink: validate attributes in do_setlink()
From: Eric Dumazet @ 2018-06-05 16:42 UTC (permalink / raw)
  To: David Miller, edumazet; +Cc: netdev, eric.dumazet, dvyukov
In-Reply-To: <20180605.124103.1922429680259846762.davem@davemloft.net>



On 06/05/2018 09:41 AM, David Miller wrote:
> From: Eric Dumazet <edumazet@google.com>
> Date: Tue,  5 Jun 2018 09:25:19 -0700
> 
>> It seems that rtnl_group_changelink() can call do_setlink
>> while a prior call to validate_linkmsg(dev = NULL, ...) could
>> not validate IFLA_ADDRESS / IFLA_BROADCAST
>>
>> Make sure do_setlink() calls validate_linkmsg() instead
>> of letting its callers having this responsibility.
> 
> But now rtnl_newlink() will validate_linkmsg() twice....
> 

Yes, is it a problem ? That is hardly fast path :)

^ permalink raw reply

* Re: [PATCH net-next] rtnetlink: validate attributes in do_setlink()
From: David Miller @ 2018-06-05 16:41 UTC (permalink / raw)
  To: edumazet; +Cc: netdev, eric.dumazet, dvyukov
In-Reply-To: <20180605162519.230428-1-edumazet@google.com>

From: Eric Dumazet <edumazet@google.com>
Date: Tue,  5 Jun 2018 09:25:19 -0700

> It seems that rtnl_group_changelink() can call do_setlink
> while a prior call to validate_linkmsg(dev = NULL, ...) could
> not validate IFLA_ADDRESS / IFLA_BROADCAST
> 
> Make sure do_setlink() calls validate_linkmsg() instead
> of letting its callers having this responsibility.

But now rtnl_newlink() will validate_linkmsg() twice....

^ permalink raw reply

* pull-request: bpf-next 2018-06-05
From: Daniel Borkmann @ 2018-06-05 16:39 UTC (permalink / raw)
  To: davem; +Cc: daniel, ast, netdev

Hi David,

The following pull-request contains BPF updates for your *net-next* tree.

The main changes are:

1) Add a new BPF hook for sendmsg similar to existing hooks for bind and
   connect: "This allows to override source IP (including the case when it's
   set via cmsg(3)) and destination IP:port for unconnected UDP (slow path).
   TCP and connected UDP (fast path) are not affected. This makes UDP support
   complete, that is, connected UDP is handled by connect hooks, unconnected
   by sendmsg ones.", from Andrey.

2) Rework of the AF_XDP API to allow extending it in future for type writer
   model if necessary. In this mode a memory window is passed to hardware
   and multiple frames might be filled into that window instead of just one
   that is the case in the current fixed frame-size model. With the new
   changes made this can be supported without having to add a new descriptor
   format. Also, core bits for the zero-copy support for AF_XDP have been
   merged as agreed upon, where i40e bits will be routed via Jeff later on.
   Various improvements to documentation and sample programs included as
   well, all from Björn and Magnus.

3) Given BPF's flexibility, a new program type has been added to implement
   infrared decoders. Quote: "The kernel IR decoders support the most
   widely used IR protocols, but there are many protocols which are not
   supported. [...] There is a 'long tail' of unsupported IR protocols,
   for which lircd is need to decode the IR. IR encoding is done in such
   a way that some simple circuit can decode it; therefore, BPF is ideal.
   [...] user-space can define a decoder in BPF, attach it to the rc
   device through the lirc chardev.", from Sean.

4) Several improvements and fixes to BPF core, among others, dumping map
   and prog IDs into fdinfo which is a straight forward way to correlate
   BPF objects used by applications, removing an indirect call and therefore
   retpoline in all map lookup/update/delete calls by invoking the callback
   directly for 64 bit archs, adding a new bpf_skb_cgroup_id() BPF helper
   for tc BPF programs to have an efficient way of looking up cgroup v2 id
   for policy or other use cases. Fixes to make sure we zero tunnel/xfrm
   state that hasn't been filled, to allow context access wrt pt_regs in
   32 bit archs for tracing, and last but not least various test cases
   for fixes that landed in bpf earlier, from Daniel.

5) Get rid of the ndo_xdp_flush API and extend the ndo_xdp_xmit with
   a XDP_XMIT_FLUSH flag instead which allows to avoid one indirect
   call as flushing is now merged directly into ndo_xdp_xmit(), from Jesper.

6) Add a new bpf_get_current_cgroup_id() helper that can be used in
   tracing to retrieve the cgroup id from the current process in order
   to allow for e.g. aggregation of container-level events, from Yonghong.

7) Two follow-up fixes for BTF to reject invalid input values and
   related to that also two test cases for BPF kselftests, from Martin.

8) Various API improvements to the bpf_fib_lookup() helper, that is,
   dropping MPLS bits which are not fully hashed out yet, rejecting
   invalid helper flags, returning error for unsupported address
   families as well as renaming flowlabel to flowinfo, from David.

9) Various fixes and improvements to sockmap BPF kselftests in particular
   in proper error detection and data verification, from Prashant.

10) Two arm32 BPF JIT improvements. One is to fix imm range check with
    regards to whether immediate fits into 24 bits, and a naming cleanup
    to get functions related to rsh handling consistent to those handling
    lsh, from Wang.

11) Two compile warning fixes in BPF, one for BTF and a false positive
    to silent gcc in stack_map_get_build_id_offset(), from Arnd.

12) Add missing seg6.h header into tools include infrastructure in order
    to fix compilation of BPF kselftests, from Mathieu.

13) Several formatting cleanups in the BPF UAPI helper description that
    also fix an error during rst2man compilation, from Quentin.

14) Hide an unused variable in sk_msg_convert_ctx_access() when IPv6 is
    not built into the kernel, from Yue.

15) Remove a useless double assignment in dev_map_enqueue(), from Colin.

Please consider pulling these changes from:

  git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git

Thanks a lot!

----------------------------------------------------------------

The following changes since commit 5b79c2af667c0e2684f2a6dbf6439074b78f490c:

  Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net (2018-05-26 19:46:15 -0400)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git 

for you to fetch changes up to 9fa06104a235f64d6a2bf3012cc9966e8e4be5eb:

  Merge branch 'bpf-af-xdp-zc-api' (2018-06-05 15:58:07 +0200)

----------------------------------------------------------------
Alexei Starovoitov (4):
      Merge branch 'btf-fixes'
      Merge branch 'misc-BPF-improvements'
      Merge branch 'ndo_xdp_xmit-cleanup'
      Merge branch 'bpf_get_current_cgroup_id'

Andrey Ignatov (8):
      libbpf: Install btf.h with libbpf
      bpf: Define cgroup_bpf_enabled for CONFIG_CGROUP_BPF=n
      bpf: Hooks for sys_sendmsg
      bpf: Sync bpf.h to tools/
      libbpf: Support guessing sendmsg{4,6} progs
      selftests/bpf: Prepare test_sock_addr for extension
      selftests/bpf: Selftest for sys_sendmsg hooks
      bpftool: Support sendmsg{4,6} attach types

Arnd Bergmann (2):
      bpf: btf: avoid -Wreturn-type warning
      bpf: avoid -Wmaybe-uninitialized warning

Björn Töpel (10):
      xsk: proper fill queue descriptor validation
      xsk: proper Rx drop statistics update
      xsk: new descriptor addressing scheme
      samples/bpf: adapted to new uapi
      xsk: moved struct xdp_umem definition
      xsk: introduce xdp_umem_page
      net: xdp: added bpf_netdev_command XDP_{QUERY, SETUP}_XSK_UMEM
      xdp: add MEM_TYPE_ZERO_COPY
      xsk: add zero-copy support for Rx
      samples/bpf: xdpsock: use skb Tx path for XDP_SKB

Colin Ian King (1):
      bpf: devmap: remove redundant assignment of dev = dev

Daniel Borkmann (17):
      Merge branch 'bpf-sendmsg-hook'
      Merge branch 'bpf-ir-decoder'
      Merge branch 'bpf-sockmap-test-fixes'
      bpf: test case for map pointer poison with calls/branches
      bpf: add also cbpf long jump test cases with heavy expansion
      bpf: fixup error message from gpl helpers on license mismatch
      bpf: show prog and map id in fdinfo
      bpf: avoid retpoline for lookup/update/delete calls on maps
      bpf: add bpf_skb_cgroup_id helper
      bpf: make sure to clear unused fields in tunnel/xfrm state fetch
      bpf: fix cbpf parser bug for octal numbers
      bpf: fix context access in tracing progs on 32 bit archs
      bpf: sync bpf uapi header with tools
      bpf, doc: add missing patchwork url and libbpf to maintainers
      Merge branch 'bpf-af-xdp-fixes'
      Merge branch 'bpf-xdp-remove-xdp-flush'
      Merge branch 'bpf-af-xdp-zc-api'

David Ahern (4):
      bpf: Drop mpls from bpf_fib_lookup
      bpf: Verify flags in bpf_fib_lookup
      bpf: Change bpf_fib_lookup to return -EAFNOSUPPORT for unsupported address families
      bpf: flowlabel in bpf_fib_lookup should be flowinfo

Jesper Dangaard Brouer (13):
      xdp: add flags argument to ndo_xdp_xmit API
      i40e: implement flush flag for ndo_xdp_xmit
      ixgbe: implement flush flag for ndo_xdp_xmit
      tun: implement flush flag for ndo_xdp_xmit
      virtio_net: implement flush flag for ndo_xdp_xmit
      xdp: done implementing ndo_xdp_xmit flush flag for all drivers
      bpf/xdp: non-map redirect can avoid calling ndo_xdp_flush
      bpf/xdp: devmap can avoid calling ndo_xdp_flush
      i40e: remove ndo_xdp_flush call i40e_xdp_flush
      ixgbe: remove ndo_xdp_flush call ixgbe_xdp_flush
      virtio_net: remove ndo_xdp_flush call virtnet_xdp_flush
      tun: remove ndo_xdp_flush call tun_xdp_flush
      net: remove net_device operation ndo_xdp_flush

Magnus Karlsson (3):
      samples/bpf: minor *_nb_free performance fix
      net: added netdevice operation for Tx
      xsk: wire upp Tx zero-copy functions

Martin KaFai Lau (2):
      bpf: btf: Check array t->size
      bpf: btf: Ensure t->type == 0 for BTF_KIND_FWD

Mathieu Xhonneux (1):
      selftests/bpf: missing headers test_lwt_seg6local

Prashant Bhole (5):
      selftests/bpf: test_sockmap, check test failure
      selftests/bpf: test_sockmap, join cgroup in selftest mode
      selftests/bpf: test_sockmap, timing improvements
      selftests/bpf: test_sockmap, fix data verification
      selftests/bpf: test_sockmap, print additional test options

Quentin Monnet (1):
      bpf: clean up eBPF helpers documentation

Sean Young (3):
      bpf: bpf_prog_array_copy() should return -ENOENT if exclude_prog not found
      media: rc: introduce BPF_PROG_LIRC_MODE2
      bpf: add selftest for lirc_mode2 type program

Wang YanQing (2):
      bpf, arm32: correct check_imm24
      bpf, arm32: fix inconsistent naming about emit_a32_lsr_{r64,i64}

Yonghong Song (4):
      bpf: implement bpf_get_current_cgroup_id() helper
      tools/bpf: sync uapi bpf.h for bpf_get_current_cgroup_id() helper
      tools/bpf: add a selftest for bpf_get_current_cgroup_id() helper
      bpf: guard bpf_get_current_cgroup_id() with CONFIG_CGROUPS

YueHaibing (1):
      bpf: hide the unused 'off' variable

 Documentation/networking/af_xdp.rst                |  101 +-
 MAINTAINERS                                        |    2 +
 arch/arm/net/bpf_jit_32.c                          |   16 +-
 drivers/media/rc/Kconfig                           |   13 +
 drivers/media/rc/Makefile                          |    1 +
 drivers/media/rc/bpf-lirc.c                        |  313 ++++++
 drivers/media/rc/lirc_dev.c                        |   30 +
 drivers/media/rc/rc-core-priv.h                    |   21 +
 drivers/media/rc/rc-ir-raw.c                       |   12 +-
 drivers/net/ethernet/intel/i40e/i40e_main.c        |    1 -
 drivers/net/ethernet/intel/i40e/i40e_txrx.c        |   33 +-
 drivers/net/ethernet/intel/i40e/i40e_txrx.h        |    4 +-
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c      |   42 +-
 drivers/net/tun.c                                  |   44 +-
 drivers/net/virtio_net.c                           |   22 +-
 include/linux/bpf-cgroup.h                         |   24 +-
 include/linux/bpf.h                                |    1 +
 include/linux/bpf_lirc.h                           |   29 +
 include/linux/bpf_types.h                          |    3 +
 include/linux/filter.h                             |   44 +-
 include/linux/netdevice.h                          |   21 +-
 include/net/xdp.h                                  |   14 +
 include/net/xdp_sock.h                             |   44 +-
 include/uapi/linux/bpf.h                           |  136 ++-
 include/uapi/linux/if_xdp.h                        |   16 +-
 kernel/bpf/btf.c                                   |   28 +-
 kernel/bpf/cgroup.c                                |   11 +-
 kernel/bpf/core.c                                  |   12 +-
 kernel/bpf/devmap.c                                |   21 +-
 kernel/bpf/hashtab.c                               |   12 +-
 kernel/bpf/helpers.c                               |   15 +
 kernel/bpf/stackmap.c                              |    7 +-
 kernel/bpf/syscall.c                               |   27 +-
 kernel/bpf/verifier.c                              |   73 +-
 kernel/trace/bpf_trace.c                           |   16 +-
 lib/test_bpf.c                                     |   63 ++
 net/core/filter.c                                  |   91 +-
 net/core/xdp.c                                     |   19 +-
 net/ipv4/udp.c                                     |   20 +-
 net/ipv6/udp.c                                     |   24 +
 net/xdp/xdp_umem.c                                 |  151 ++-
 net/xdp/xdp_umem.h                                 |   45 +-
 net/xdp/xdp_umem_props.h                           |    4 +-
 net/xdp/xsk.c                                      |  199 +++-
 net/xdp/xsk_queue.c                                |    2 +-
 net/xdp/xsk_queue.h                                |   98 +-
 samples/bpf/xdp_fwd_kern.c                         |    2 +-
 samples/bpf/xdpsock_user.c                         |   97 +-
 tools/bpf/bpf_exp.l                                |    2 +-
 tools/bpf/bpftool/Documentation/bpftool-cgroup.rst |    9 +-
 tools/bpf/bpftool/bash-completion/bpftool          |    5 +-
 tools/bpf/bpftool/cgroup.c                         |    4 +-
 tools/bpf/bpftool/prog.c                           |    1 +
 tools/include/linux/filter.h                       |   10 +
 tools/include/uapi/linux/bpf.h                     |  134 ++-
 tools/include/uapi/linux/lirc.h                    |  217 ++++
 tools/include/uapi/linux/seg6.h                    |   55 +
 tools/include/uapi/linux/seg6_local.h              |   80 ++
 tools/lib/bpf/Makefile                             |    1 +
 tools/lib/bpf/libbpf.c                             |    3 +
 tools/testing/selftests/bpf/.gitignore             |    2 +
 tools/testing/selftests/bpf/Makefile               |    9 +-
 tools/testing/selftests/bpf/bpf_helpers.h          |    7 +
 tools/testing/selftests/bpf/cgroup_helpers.c       |   57 +
 tools/testing/selftests/bpf/cgroup_helpers.h       |    1 +
 tools/testing/selftests/bpf/get_cgroup_id_kern.c   |   28 +
 tools/testing/selftests/bpf/get_cgroup_id_user.c   |  141 +++
 tools/testing/selftests/bpf/sendmsg4_prog.c        |   49 +
 tools/testing/selftests/bpf/sendmsg6_prog.c        |   60 +
 tools/testing/selftests/bpf/test_btf.c             |   45 +
 tools/testing/selftests/bpf/test_lirc_mode2.sh     |   28 +
 tools/testing/selftests/bpf/test_lirc_mode2_kern.c |   23 +
 tools/testing/selftests/bpf/test_lirc_mode2_user.c |  149 +++
 tools/testing/selftests/bpf/test_sock_addr.c       | 1155 ++++++++++++++++----
 tools/testing/selftests/bpf/test_sockmap.c         |   87 +-
 tools/testing/selftests/bpf/test_verifier.c        |  185 +++-
 76 files changed, 3841 insertions(+), 730 deletions(-)
 create mode 100644 drivers/media/rc/bpf-lirc.c
 create mode 100644 include/linux/bpf_lirc.h
 create mode 100644 tools/include/uapi/linux/lirc.h
 create mode 100644 tools/include/uapi/linux/seg6.h
 create mode 100644 tools/include/uapi/linux/seg6_local.h
 create mode 100644 tools/testing/selftests/bpf/get_cgroup_id_kern.c
 create mode 100644 tools/testing/selftests/bpf/get_cgroup_id_user.c
 create mode 100644 tools/testing/selftests/bpf/sendmsg4_prog.c
 create mode 100644 tools/testing/selftests/bpf/sendmsg6_prog.c
 create mode 100755 tools/testing/selftests/bpf/test_lirc_mode2.sh
 create mode 100644 tools/testing/selftests/bpf/test_lirc_mode2_kern.c
 create mode 100644 tools/testing/selftests/bpf/test_lirc_mode2_user.c

^ permalink raw reply

* Re: [PATCH net] l2tp: fix refcount leakage on PPPoL2TP sockets
From: Guillaume Nault @ 2018-06-05 16:37 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, jchapman
In-Reply-To: <20180605.094124.1023096251110931871.davem@davemloft.net>

On Tue, Jun 05, 2018 at 09:41:24AM -0400, David Miller wrote:
> From: Guillaume Nault <g.nault@alphalink.fr>
> Date: Mon, 4 Jun 2018 18:52:19 +0200
> 
> > Commit d02ba2a6110c ("l2tp: fix race in pppol2tp_release with session
> > object destroy") tried to fix a race condition where a PPPoL2TP socket
> > would disappear while the L2TP session was still using it. However, it
> > missed the root issue which is that an L2TP session may accept to be
> > reconnected if its associated socket has entered the release process.
> > 
> > The tentative fix makes the session hold the socket it is connected to.
> > That saves the kernel from crashing, but introduces refcount leakage,
> > preventing the socket from completing the release process. Once stalled,
> > everything the socket depends on can't be released anymore, including
> > the L2TP session and the l2tp_ppp module.
>  ...
> > So it all boils down to pppol2tp_connect() failing to realise that the
> > session has already been connected. This patch drops the unneeded extra
> > reference counting (mostly reverting d02ba2a6110c) and checks that
> > neither ->sk nor ->__sk is set before allowing a session to be
> > connected.
> > 
> > Fixes: d02ba2a6110c ("l2tp: fix race in pppol2tp_release with session object destroy")
> > Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
> 
> So much fidgeting around in this area over the past year or two :-)
> 
Putting L2TP into production without adding custom workarounds has been
such a long journey, but we're almost there :-)

> Applied and queued up for -stable, thanks for fixing this.
> 
I still have a handful of issues to fix, though.

^ permalink raw reply

* Re: [net-next PATCH v3 0/5] Symmetric queue selection using XPS for Rx queues
From: David Miller @ 2018-06-05 16:33 UTC (permalink / raw)
  To: amritha.nambiar
  Cc: netdev, alexander.h.duyck, willemdebruijn.kernel,
	sridhar.samudrala, edumazet, hannes, tom
In-Reply-To: <152818727065.20862.10108275498797168689.stgit@anamdev.jf.intel.com>

From: Amritha Nambiar <amritha.nambiar@intel.com>
Date: Tue, 05 Jun 2018 01:37:45 -0700

> This patch series implements support for Tx queue selection based on
> Rx queue(s) map. This is done by configuring Rx queue(s) map per Tx-queue
> using sysfs attribute. If the user configuration for Rx queues does
> not apply, then the Tx queue selection falls back to XPS using CPUs and
> finally to hashing.
> 
> XPS is refactored to support Tx queue selection based on either the
> CPUs map or the Rx-queues map. The config option CONFIG_XPS needs to be
> enabled. By default no receive queues are configured for the Tx queue.
> 
> - /sys/class/net/<dev>/queues/tx-*/xps_rxqs
> 
> A set of receive queues can be mapped to a set of transmit queues (many:many),
> although the common use case is a 1:1 mapping. This will enable sending
> packets on the same Tx-Rx queue pair as this is useful for busy polling
> multi-threaded workloads where it is not possible to pin the threads to
> a CPU. This is a rework of Sridhar's patch for symmetric queueing via
> socket option:
> https://www.spinics.net/lists/netdev/msg453106.html
 ...

Thanks for doing this work.

I think this needs more time to sit and get reviews, and therefore needs
to be deferred to the next merge window.

^ permalink raw reply

* Re: [PATCH v2 net-next 0/3] devlink: Add extack messages for reload and port split/unsplit
From: David Miller @ 2018-06-05 16:32 UTC (permalink / raw)
  To: dsahern; +Cc: netdev, idosch, jiri, jakub.kicinski, dsahern
In-Reply-To: <20180605151411.20310-1-dsahern@kernel.org>

From: dsahern@kernel.org
Date: Tue,  5 Jun 2018 08:14:08 -0700

> From: David Ahern <dsahern@gmail.com>
> 
> Patch 1 adds extack arg to reload, port_split and port_unsplit devlink
> operations.
> 
> Patch 2 adds extack messages for reload operation in netdevsim.
> 
> Patch 3 adds extack messages to port split/unsplit in mlxsw driver.
> 
> v2
> - make the extack messages align with existing dev_err

Series applied, thanks David.

^ permalink raw reply

* Re: [PATCH v2 net-next] net: metrics: add proper netlink validation
From: David Miller @ 2018-06-05 16:30 UTC (permalink / raw)
  To: edumazet; +Cc: netdev, eric.dumazet, dsahern
In-Reply-To: <20180605130619.150153-1-edumazet@google.com>

From: Eric Dumazet <edumazet@google.com>
Date: Tue,  5 Jun 2018 06:06:19 -0700

> Before using nla_get_u32(), better make sure the attribute
> is of the proper size.
> 
> Code recently was changed, but bug has been there from beginning
> of git.
 ...
> Fixes: a919525ad832 ("net: Move fib_convert_metrics to metrics file")
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: syzbot <syzkaller@googlegroups.com>
> Cc: David Ahern <dsahern@gmail.com>
> ---
> v2: fixed a typo.

Applied and queued up for -stable, thanks Eric.

^ permalink raw reply

* Re: [PATCH net v2 2/2] ipmr: fix error path when ipmr_new_table fails
From: David Miller @ 2018-06-05 16:31 UTC (permalink / raw)
  To: sd; +Cc: netdev, edumazet, nikolay, yuvalm, ivecera
In-Reply-To: <572e1baf89c76fafb45a97a724c3e838e5dd4abf.1528194845.git.sd@queasysnail.net>

From: Sabrina Dubroca <sd@queasysnail.net>
Date: Tue,  5 Jun 2018 15:02:00 +0200

> commit 0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table")
> refactored ipmr_new_table, so that it now returns NULL when
> mr_table_alloc fails. Unfortunately, all callers of ipmr_new_table
> expect an ERR_PTR.
> 
> This can result in NULL deref, for example when ipmr_rules_exit calls
> ipmr_free_table with NULL net->ipv4.mrt in the
> !CONFIG_IP_MROUTE_MULTIPLE_TABLES version.
> 
> This patch makes mr_table_alloc return errors, and changes
> ip6mr_new_table and its callers to return/expect error pointers as
> well. It also removes the version of mr_table_alloc defined under
> !CONFIG_IP_MROUTE_COMMON, since it is never used.
> 
> Fixes: 0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table")
> Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
> ---
> v2: - fixed brainfart that shadowed mrt variable in ip6_mroute_setsockopt
>     - rebased on top of ip6_mroute_setsockopt fix

Applied and queued up for -stable.

^ permalink raw reply

* Re: [PATCH net v2 1/2] ip6mr: only set ip6mr_table from setsockopt when ip6mr_new_table succeeds
From: David Miller @ 2018-06-05 16:30 UTC (permalink / raw)
  To: sd; +Cc: netdev, edumazet, nikolay, yuvalm, ivecera
In-Reply-To: <604985fb55d51eef9130bff0640a62d5015f25bd.1528194845.git.sd@queasysnail.net>

From: Sabrina Dubroca <sd@queasysnail.net>
Date: Tue,  5 Jun 2018 15:01:59 +0200

> Currently, raw6_sk(sk)->ip6mr_table is set unconditionally during
> ip6_mroute_setsockopt(MRT6_TABLE). A subsequent attempt at the same
> setsockopt will fail with -ENOENT, since we haven't actually created
> that table.
> 
> A similar fix for ipv4 was included in commit 5e1859fbcc3c ("ipv4: ipmr:
> various fixes and cleanups").
> 
> Fixes: d1db275dd3f6 ("ipv6: ip6mr: support multiple tables")
> Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>

Applied and queued up for -stable.

^ permalink raw reply

* [PATCH net-next] rtnetlink: validate attributes in do_setlink()
From: Eric Dumazet @ 2018-06-05 16:25 UTC (permalink / raw)
  To: David S . Miller; +Cc: netdev, Eric Dumazet, Eric Dumazet, Dmitry Vyukov

It seems that rtnl_group_changelink() can call do_setlink
while a prior call to validate_linkmsg(dev = NULL, ...) could
not validate IFLA_ADDRESS / IFLA_BROADCAST

Make sure do_setlink() calls validate_linkmsg() instead
of letting its callers having this responsibility.

With help from Dmitry Vyukov, thanks a lot !

BUG: KMSAN: uninit-value in is_valid_ether_addr include/linux/etherdevice.h:199 [inline]
BUG: KMSAN: uninit-value in eth_prepare_mac_addr_change net/ethernet/eth.c:275 [inline]
BUG: KMSAN: uninit-value in eth_mac_addr+0x203/0x2b0 net/ethernet/eth.c:308
CPU: 1 PID: 8695 Comm: syz-executor3 Not tainted 4.17.0-rc5+ #103
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
 __dump_stack lib/dump_stack.c:77 [inline]
 dump_stack+0x185/0x1d0 lib/dump_stack.c:113
 kmsan_report+0x149/0x260 mm/kmsan/kmsan.c:1084
 __msan_warning_32+0x6e/0xc0 mm/kmsan/kmsan_instr.c:686
 is_valid_ether_addr include/linux/etherdevice.h:199 [inline]
 eth_prepare_mac_addr_change net/ethernet/eth.c:275 [inline]
 eth_mac_addr+0x203/0x2b0 net/ethernet/eth.c:308
 dev_set_mac_address+0x261/0x530 net/core/dev.c:7157
 do_setlink+0xbc3/0x5fc0 net/core/rtnetlink.c:2317
 rtnl_group_changelink net/core/rtnetlink.c:2824 [inline]
 rtnl_newlink+0x1fe9/0x37a0 net/core/rtnetlink.c:2976
 rtnetlink_rcv_msg+0xa32/0x1560 net/core/rtnetlink.c:4646
 netlink_rcv_skb+0x378/0x600 net/netlink/af_netlink.c:2448
 rtnetlink_rcv+0x50/0x60 net/core/rtnetlink.c:4664
 netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline]
 netlink_unicast+0x1678/0x1750 net/netlink/af_netlink.c:1336
 netlink_sendmsg+0x104f/0x1350 net/netlink/af_netlink.c:1901
 sock_sendmsg_nosec net/socket.c:629 [inline]
 sock_sendmsg net/socket.c:639 [inline]
 ___sys_sendmsg+0xec0/0x1310 net/socket.c:2117
 __sys_sendmsg net/socket.c:2155 [inline]
 __do_sys_sendmsg net/socket.c:2164 [inline]
 __se_sys_sendmsg net/socket.c:2162 [inline]
 __x64_sys_sendmsg+0x331/0x460 net/socket.c:2162
 do_syscall_64+0x152/0x230 arch/x86/entry/common.c:287
 entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x455a09
RSP: 002b:00007fc07480ec68 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007fc07480f6d4 RCX: 0000000000455a09
RDX: 0000000000000000 RSI: 00000000200003c0 RDI: 0000000000000014
RBP: 000000000072bea0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000ffffffff
R13: 00000000000005d0 R14: 00000000006fdc20 R15: 0000000000000000

Uninit was stored to memory at:
 kmsan_save_stack_with_flags mm/kmsan/kmsan.c:279 [inline]
 kmsan_save_stack mm/kmsan/kmsan.c:294 [inline]
 kmsan_internal_chain_origin+0x12b/0x210 mm/kmsan/kmsan.c:685
 kmsan_memcpy_origins+0x11d/0x170 mm/kmsan/kmsan.c:527
 __msan_memcpy+0x109/0x160 mm/kmsan/kmsan_instr.c:478
 do_setlink+0xb84/0x5fc0 net/core/rtnetlink.c:2315
 rtnl_group_changelink net/core/rtnetlink.c:2824 [inline]
 rtnl_newlink+0x1fe9/0x37a0 net/core/rtnetlink.c:2976
 rtnetlink_rcv_msg+0xa32/0x1560 net/core/rtnetlink.c:4646
 netlink_rcv_skb+0x378/0x600 net/netlink/af_netlink.c:2448
 rtnetlink_rcv+0x50/0x60 net/core/rtnetlink.c:4664
 netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline]
 netlink_unicast+0x1678/0x1750 net/netlink/af_netlink.c:1336
 netlink_sendmsg+0x104f/0x1350 net/netlink/af_netlink.c:1901
 sock_sendmsg_nosec net/socket.c:629 [inline]
 sock_sendmsg net/socket.c:639 [inline]
 ___sys_sendmsg+0xec0/0x1310 net/socket.c:2117
 __sys_sendmsg net/socket.c:2155 [inline]
 __do_sys_sendmsg net/socket.c:2164 [inline]
 __se_sys_sendmsg net/socket.c:2162 [inline]
 __x64_sys_sendmsg+0x331/0x460 net/socket.c:2162
 do_syscall_64+0x152/0x230 arch/x86/entry/common.c:287
 entry_SYSCALL_64_after_hwframe+0x44/0xa9
Uninit was created at:
 kmsan_save_stack_with_flags mm/kmsan/kmsan.c:279 [inline]
 kmsan_internal_poison_shadow+0xb8/0x1b0 mm/kmsan/kmsan.c:189
 kmsan_kmalloc+0x94/0x100 mm/kmsan/kmsan.c:315
 kmsan_slab_alloc+0x10/0x20 mm/kmsan/kmsan.c:322
 slab_post_alloc_hook mm/slab.h:446 [inline]
 slab_alloc_node mm/slub.c:2753 [inline]
 __kmalloc_node_track_caller+0xb32/0x11b0 mm/slub.c:4395
 __kmalloc_reserve net/core/skbuff.c:138 [inline]
 __alloc_skb+0x2cb/0x9e0 net/core/skbuff.c:206
 alloc_skb include/linux/skbuff.h:988 [inline]
 netlink_alloc_large_skb net/netlink/af_netlink.c:1182 [inline]
 netlink_sendmsg+0x76e/0x1350 net/netlink/af_netlink.c:1876
 sock_sendmsg_nosec net/socket.c:629 [inline]
 sock_sendmsg net/socket.c:639 [inline]
 ___sys_sendmsg+0xec0/0x1310 net/socket.c:2117
 __sys_sendmsg net/socket.c:2155 [inline]
 __do_sys_sendmsg net/socket.c:2164 [inline]
 __se_sys_sendmsg net/socket.c:2162 [inline]
 __x64_sys_sendmsg+0x331/0x460 net/socket.c:2162
 do_syscall_64+0x152/0x230 arch/x86/entry/common.c:287
 entry_SYSCALL_64_after_hwframe+0x44/0xa9

Fixes: e7ed828f10bd ("netlink: support setting devgroup parameters")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
---
 net/core/rtnetlink.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 9a1ba2015ad8901680cfc58f95cf6ec525413566..5ef61222fdef1f305909eeca6ac278bcac88e1b0 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2266,6 +2266,10 @@ static int do_setlink(const struct sk_buff *skb,
 	const struct net_device_ops *ops = dev->netdev_ops;
 	int err;
 
+	err = validate_linkmsg(dev, tb);
+	if (err < 0)
+		return err;
+
 	if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD] || tb[IFLA_IF_NETNSID]) {
 		struct net *net = rtnl_link_get_net_capable(skb, dev_net(dev),
 							    tb, CAP_NET_ADMIN);
@@ -2629,10 +2633,6 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 		goto errout;
 	}
 
-	err = validate_linkmsg(dev, tb);
-	if (err < 0)
-		goto errout;
-
 	err = do_setlink(skb, dev, ifm, extack, tb, ifname, 0);
 errout:
 	return err;
-- 
2.17.1.1185.g55be947832-goog

^ permalink raw reply related

* Re: suspicius csum initialization in vmxnet3_rx_csum
From: Paolo Abeni @ 2018-06-05 16:08 UTC (permalink / raw)
  To: Ronak Doshi, Neil Horman; +Cc: Guolin Yang, Boon Ang, Louis Luo, netdev
In-Reply-To: <alpine.OSX.2.21.1806011100140.27872@doshir-m01.vmware.com>

Hi,

I'm sorry for the long delay in my answer, I've been travelling.

On Fri, 2018-06-01 at 11:10 -0700, Ronak Doshi wrote:
> On Thu, 31 May 2018, Neil Horman wrote:
> > What packet types will rcd.csum be set for?
> > Neil 
> 
> I looked thorugh the emulation code and found that rcd.csum is not set. 
> For valid v4/v6, TCP/UDP packets the code block above the mentioend "if" 
> block will be executed or else it will go through checksum none.
> 
> That's why I wanted to know (in previous emails) which ESX build is being 
> used while this was tested. The code block under "if (gdesc->rcd.csum)" 
> block might seem incorrect but it shouldn't be hit as rcd.csum is not set. 

I'm unsure if I read the above correctly. Do you mean that the relevant
code-path is never hit? If so, can we simply drop it, as we agreed that
such code is uncorrect? Elsewhere, could you plese specify under which
circumstances gdesc->rcd.csum is filled by the hypervisor?

> Hence, I asked did the fix provided by Paolo worked for the icmp test?

Unfortunatelly so far I've not been able to reproduce the issue outside
a production environment and I can't run test kernel there.

Thanks,

Paolo

^ permalink raw reply

* Re: [RFC PATCH] kcm: hold rx mux lock when updating the receive queue.
From: Paolo Abeni @ 2018-06-05 16:06 UTC (permalink / raw)
  To: Tom Herbert, David Miller
  Cc: Linux Kernel Network Developers, Tom Herbert, ktkhai
In-Reply-To: <CALx6S353uk_W8b4ic1NYNBS--z41PT6brkwzPvZZj6J2-yEieg@mail.gmail.com>

Hi,

On Tue, 2018-06-05 at 08:35 -0700, Tom Herbert wrote:
> On Tue, Jun 5, 2018 at 7:53 AM, David Miller <davem@davemloft.net> wrote:
> > From: Paolo Abeni <pabeni@redhat.com>
> > Date: Tue,  5 Jun 2018 12:32:33 +0200
> >
> >> @@ -1157,7 +1158,9 @@ static int kcm_recvmsg(struct socket *sock, struct msghdr *msg,
> >>                       /* Finished with message */
> >>                       msg->msg_flags |= MSG_EOR;
> >>                       KCM_STATS_INCR(kcm->stats.rx_msgs);
> >> +                     spin_lock_bh(&kcm->mux->rx_lock);
> >>                       skb_unlink(skb, &sk->sk_receive_queue);
> >> +                     spin_unlock_bh(&kcm->mux->rx_lock);
> >
> > Hmmm, maybe I don't understand the corruption.
> >
> > But, skb_unlink() takes the sk->sk_receive_queue.lock which should
> > prevent SKB list corruption.
> 
> It looks like there is a case where the list is being manipulated
> without the queue lock. That is in requeue_rx_msgs where
> __skb_dequeue is being called instead of skb_dequeue which is in
> requeue_rx_msgs. requeue_rx_msgs holds the mux rx_lock which would
> explain why the suggested patch avoids the issue.

Yep, I belive this is the correct explanation. Sorry for the noise with
the previous patch, I underlooked the skb_queue lock already in place.

> Paolo, thanks for looking into this! Can you try replacing
> __skb_dequeue in requeue_rx_msgs with skb_dequeue to see if that is
> the fix.

Sure, I'll retrigger the test, and report the result here (or directly
a new patch, should the test be succesful)

Thanks,

Paolo

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox