rmnetcfg: Initial commit

RmNet configuration library and command line utility for configuring
the RmNet Data kernel module.

CRs-Fixed: 525675
Acked-by: Subash Abhinov Kasiviswanathan <subashab@qti.qualcomm.com>
Change-Id: I4c15e2f0e32082077c189cf316f845699aef5f13
diff --git a/rmnetctl/src/Android.mk b/rmnetctl/src/Android.mk
new file mode 100644
index 0000000..86c70ef
--- /dev/null
+++ b/rmnetctl/src/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_COPY_HEADERS_TO   := dataservices/rmnetctl
+LOCAL_COPY_HEADERS      := ../inc/librmnetctl.h
+
+LOCAL_SRC_FILES := librmnetctl.c
+LOCAL_CFLAGS := -Wall -Werror
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../inc
+LOCAL_C_INCLUDES += $(LOCAL_PATH)
+LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+
+
+LOCAL_MODULE := librmnetctl
+LOCAL_MODULE_TAGS := optional
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/rmnetctl/src/librmnetctl.c b/rmnetctl/src/librmnetctl.c
new file mode 100644
index 0000000..91d7711
--- /dev/null
+++ b/rmnetctl/src/librmnetctl.c
@@ -0,0 +1,687 @@
+/******************************************************************************
+
+			L I B R M N E T C T L . C
+
+Copyright (c) 2013, 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 implentations 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 <linux/rmnet_data.h>
+#include "librmnetctl_hndl.h"
+#include "librmnetctl.h"
+
+#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)
+
+/*===========================================================================
+			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 int 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;
+	int bytes_read = -1, return_code = RMNETCTL_API_ERR_HNDL_INVALID;
+	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) {
+		free(request_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++;
+
+	socklen_t addrlen = sizeof(struct sockaddr_nl);
+	if (sendto(hndl->netlink_fd,
+			request_buf,
+			MAX_BUF_SIZE,
+			RMNETCTL_SOCK_FLAG,
+			(struct sockaddr *) &hndl->dest_addr,
+			sizeof(struct sockaddr_nl)) < 0) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		free(request_buf);
+		free(response_buf);
+		break;
+	}
+
+	bytes_read = recvfrom(hndl->netlink_fd,
+			response_buf,
+			MAX_BUF_SIZE,
+			RMNETCTL_SOCK_FLAG,
+			(struct sockaddr *) &hndl->src_addr,
+			&addrlen);
+	if (bytes_read < 0) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_RECEIVE;
+		free(request_buf);
+		free(response_buf);
+		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;
+		free(request_buf);
+		free(response_buf);
+		break;
+	}
+
+	if (request->message_type != response->message_type) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_TYPE;
+		free(request_buf);
+		free(response_buf);
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	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(int str_len, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((str_len < 0) || (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;
+	*error_code = error_val;
+	if (*error_code == RMNETCTL_SUCCESS)
+		return_code = RMNETCTL_SUCCESS;
+	return return_code;
+}
+
+/*===========================================================================
+				EXPOSED API
+===========================================================================*/
+
+int rmnetctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code)
+{
+	int pid = -1, netlink_fd = -1, return_code = RMNETCTL_LIB_ERR;
+	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 = 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;
+
+	if (bind((*hndl)->netlink_fd,
+		(struct sockaddr *)&(*hndl)->src_addr,
+		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;
+	int str_len = -1, 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, 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;
+	int str_len = -1, 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)
+		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;
+	int str_len = -1, 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_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;
+	int str_len = -1, 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)
+		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(rmnetctl_hndl_t *hndl,
+				       uint32_t ingress_flags,
+				       const char *dev_name,
+				       uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	int str_len = -1, 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_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;
+
+	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(rmnetctl_hndl_t *hndl,
+				       const char *dev_name,
+				       uint32_t *ingress_flags,
+				       uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	int str_len = -1, return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!ingress_flags) || (!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)
+		break;
+
+	*ingress_flags = response.data_format.flags;
+	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;
+	int str_len = -1, 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)) {
+		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_get_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				const char *dev_name,
+				uint8_t *operating_mode,
+				char **next_dev,
+				uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	int str_len = -1, 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)) {
+		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)
+		break;
+	printf("%s\n", (char *)(response.local_ep_config.next_dev));
+	str_len = strlcpy(*next_dev,
+			  (char *)(response.local_ep_config.next_dev),
+			  RMNET_MAX_STR_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(rmnetctl_hndl_t *hndl,
+		  uint32_t id,
+		  uint16_t *error_code,
+		  uint8_t new_vnd) {
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) ||
+	((new_vnd != RMNETCTL_NEW_VND) && (new_vnd != RMNETCTL_FREE_VND))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+	if (new_vnd ==  RMNETCTL_NEW_VND)
+		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;
+}