Subject: Re: Qualcomm rmnet driver and qmi_wwan

From: Subash Abhinov Kasiviswanathan <[email-masked]>
Date: Tue, 05 Jun 2018 11:38:51 -0600

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
>> <[email-masked]>:
>> > 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

From 94abb589a8e60c49d9cc4598a27b50c9f757b623 Mon Sep 17 00:00:00 2001
From: Subash Abhinov Kasiviswanathan <[email-masked]>
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