Home | History | Annotate | Download | only in src
      1 /******************************************************************************
      2 
      3 			L I B R M N E T C T L . C
      4 
      5 Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
      6 
      7 Redistribution and use in source and binary forms, with or without
      8 modification, are permitted provided that the following conditions are
      9 met:
     10 	* Redistributions of source code must retain the above copyright
     11 	  notice, this list of conditions and the following disclaimer.
     12 	* Redistributions in binary form must reproduce the above
     13 	  copyright notice, this list of conditions and the following
     14 	  disclaimer in the documentation and/or other materials provided
     15 	  with the distribution.
     16 	* Neither the name of The Linux Foundation nor the names of its
     17 	  contributors may be used to endorse or promote products derived
     18 	  from this software without specific prior written permission.
     19 
     20 THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
     21 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
     23 ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
     24 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     27 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     28 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     29 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     30 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 
     32 ******************************************************************************/
     33 
     34 /*!
     35 * @file    librmnetctl.c
     36 * @brief   rmnet control API's implentations file
     37 */
     38 
     39 /*===========================================================================
     40 			INCLUDE FILES
     41 ===========================================================================*/
     42 
     43 #include <sys/socket.h>
     44 #include <stdint.h>
     45 #include <linux/netlink.h>
     46 #include <string.h>
     47 #include <stdio.h>
     48 #include <unistd.h>
     49 #include <stdlib.h>
     50 #include <linux/rmnet_data.h>
     51 #include "librmnetctl_hndl.h"
     52 #include "librmnetctl.h"
     53 
     54 #ifdef USE_GLIB
     55 #include <glib.h>
     56 #define strlcpy g_strlcpy
     57 #endif
     58 
     59 #define RMNETCTL_SOCK_FLAG 0
     60 #define ROOT_USER_ID 0
     61 #define MIN_VALID_PROCESS_ID 0
     62 #define MIN_VALID_SOCKET_FD 0
     63 #define KERNEL_PROCESS_ID 0
     64 #define UNICAST 0
     65 #define MAX_BUF_SIZE sizeof(struct nlmsghdr) + sizeof(struct rmnet_nl_msg_s)
     66 #define INGRESS_FLAGS_MASK   (RMNET_INGRESS_FIX_ETHERNET | \
     67 			      RMNET_INGRESS_FORMAT_MAP | \
     68 			      RMNET_INGRESS_FORMAT_DEAGGREGATION | \
     69 			      RMNET_INGRESS_FORMAT_DEMUXING | \
     70 			      RMNET_INGRESS_FORMAT_MAP_COMMANDS | \
     71 			      RMNET_INGRESS_FORMAT_MAP_CKSUMV3)
     72 #define EGRESS_FLAGS_MASK    (RMNET_EGRESS_FORMAT__RESERVED__ | \
     73 			      RMNET_EGRESS_FORMAT_MAP | \
     74 			      RMNET_EGRESS_FORMAT_AGGREGATION | \
     75 			      RMNET_EGRESS_FORMAT_MUXING)
     76 
     77 #define min(a, b) (((a) < (b)) ? (a) : (b))
     78 /*===========================================================================
     79 			LOCAL FUNCTION DEFINITIONS
     80 ===========================================================================*/
     81 /*!
     82 * @brief Synchronous method to send and receive messages to and from the kernel
     83 * using  netlink sockets
     84 * @details Increments the transaction id for each message sent to the kernel.
     85 * Sends the netlink message to the kernel and receives the response from the
     86 * kernel.
     87 * @param *hndl RmNet handle for this transaction
     88 * @param request Message to be sent to the kernel
     89 * @param response Message received from the kernel
     90 * @return RMNETCTL_API_SUCCESS if successfully able to send and receive message
     91 * from the kernel
     92 * @return RMNETCTL_API_ERR_HNDL_INVALID if RmNet handle for the transaction was
     93 * NULL
     94 * @return RMNETCTL_API_ERR_REQUEST_NULL not enough memory to create buffer for
     95 * sending the message
     96 * @return RMNETCTL_API_ERR_MESSAGE_SEND if could not send the message to kernel
     97 * @return RMNETCTL_API_ERR_MESSAGE_RECEIVE if could not receive message from the
     98 * kernel
     99 * @return RMNETCTL_API_ERR_MESSAGE_TYPE if the request and response type do not
    100 * match
    101 */
    102 static uint16_t rmnetctl_transact(rmnetctl_hndl_t *hndl,
    103 			struct rmnet_nl_msg_s *request,
    104 			struct rmnet_nl_msg_s *response) {
    105 	uint8_t *request_buf, *response_buf;
    106 	struct nlmsghdr *nlmsghdr_val;
    107 	struct rmnet_nl_msg_s *rmnet_nl_msg_s_val;
    108 	ssize_t bytes_read = -1;
    109 	uint16_t return_code = RMNETCTL_API_ERR_HNDL_INVALID;
    110 	struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
    111 	request_buf = NULL;
    112 	response_buf = NULL;
    113 	nlmsghdr_val = NULL;
    114 	rmnet_nl_msg_s_val = NULL;
    115 	do {
    116 	if (!hndl){
    117 		break;
    118 	}
    119 	if (!request){
    120 		return_code = RMNETCTL_API_ERR_REQUEST_NULL;
    121 		break;
    122 	}
    123 	if (!response){
    124 		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
    125 		break;
    126 	}
    127 	request_buf = (uint8_t *)malloc(MAX_BUF_SIZE * sizeof(uint8_t));
    128 	if (!request_buf){
    129 		return_code = RMNETCTL_API_ERR_REQUEST_NULL;
    130 		break;
    131 	}
    132 
    133 	response_buf = (uint8_t *)malloc(MAX_BUF_SIZE * sizeof(uint8_t));
    134 	if (!response_buf) {
    135 		free(request_buf);
    136 		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
    137 		break;
    138 	}
    139 
    140 	nlmsghdr_val = (struct nlmsghdr *)request_buf;
    141 	rmnet_nl_msg_s_val = (struct rmnet_nl_msg_s *)NLMSG_DATA(request_buf);
    142 
    143 	memset(request_buf, 0, MAX_BUF_SIZE*sizeof(uint8_t));
    144 	memset(response_buf, 0, MAX_BUF_SIZE*sizeof(uint8_t));
    145 
    146 	nlmsghdr_val->nlmsg_seq = hndl->transaction_id;
    147 	nlmsghdr_val->nlmsg_pid = hndl->pid;
    148 	nlmsghdr_val->nlmsg_len = MAX_BUF_SIZE;
    149 
    150 	memcpy((void *)NLMSG_DATA(request_buf), request,
    151 	sizeof(struct rmnet_nl_msg_s));
    152 
    153 	rmnet_nl_msg_s_val->crd = RMNET_NETLINK_MSG_COMMAND;
    154 	hndl->transaction_id++;
    155 
    156 	saddr_ptr = &hndl->dest_addr;
    157 	socklen_t addrlen = sizeof(struct sockaddr_nl);
    158 	if (sendto(hndl->netlink_fd,
    159 			request_buf,
    160 			MAX_BUF_SIZE,
    161 			RMNETCTL_SOCK_FLAG,
    162 			(struct sockaddr*)saddr_ptr,
    163 			sizeof(struct sockaddr_nl)) < 0) {
    164 		return_code = RMNETCTL_API_ERR_MESSAGE_SEND;
    165 		free(request_buf);
    166 		free(response_buf);
    167 		break;
    168 	}
    169 
    170 	saddr_ptr = &hndl->src_addr;
    171 	bytes_read = recvfrom(hndl->netlink_fd,
    172 			response_buf,
    173 			MAX_BUF_SIZE,
    174 			RMNETCTL_SOCK_FLAG,
    175 			(struct sockaddr*)saddr_ptr,
    176 			&addrlen);
    177 	if (bytes_read < 0) {
    178 		return_code = RMNETCTL_API_ERR_MESSAGE_RECEIVE;
    179 		free(request_buf);
    180 		free(response_buf);
    181 		break;
    182 	}
    183 
    184 	memcpy(response, (void *)NLMSG_DATA(response_buf),
    185 	sizeof(struct rmnet_nl_msg_s));
    186 	if (sizeof(*response) < sizeof(struct rmnet_nl_msg_s)) {
    187 		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
    188 		free(request_buf);
    189 		free(response_buf);
    190 		break;
    191 	}
    192 
    193 	if (request->message_type != response->message_type) {
    194 		return_code = RMNETCTL_API_ERR_MESSAGE_TYPE;
    195 		free(request_buf);
    196 		free(response_buf);
    197 		break;
    198 	}
    199 	return_code = RMNETCTL_SUCCESS;
    200 	} while(0);
    201 	free(request_buf);
    202 	free(response_buf);
    203 	return return_code;
    204 }
    205 
    206 /*!
    207 * @brief Static function to check the dev name
    208 * @details Checks if the name is not NULL and if the name is less than the
    209 * RMNET_MAX_STR_LEN
    210 * @param dev_name Name of the device
    211 * @return RMNETCTL_SUCCESS if successful
    212 * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
    213 */
    214 static inline int _rmnetctl_check_dev_name(const char *dev_name) {
    215 	int return_code = RMNETCTL_INVALID_ARG;
    216 	do {
    217 	if (!dev_name)
    218 		break;
    219 	if (strlen(dev_name) >= RMNET_MAX_STR_LEN)
    220 		break;
    221 	return_code = RMNETCTL_SUCCESS;
    222 	} while(0);
    223 	return return_code;
    224 }
    225 
    226 /*!
    227 * @brief Static function to check the string length after a copy
    228 * @details Checks if the string length is not lesser than zero and lesser than
    229 * RMNET_MAX_STR_LEN
    230 * @param str_len length of the string after a copy
    231 * @param error_code Status code of this operation
    232 * @return RMNETCTL_SUCCESS if successful
    233 * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
    234 */
    235 static inline int _rmnetctl_check_len(size_t str_len, uint16_t *error_code) {
    236 	int return_code = RMNETCTL_LIB_ERR;
    237 	do {
    238 	if (str_len > RMNET_MAX_STR_LEN) {
    239 		*error_code = RMNETCTL_API_ERR_STRING_TRUNCATION;
    240 		break;
    241 	}
    242 	return_code = RMNETCTL_SUCCESS;
    243 	} while(0);
    244 	return return_code;
    245 }
    246 
    247 /*!
    248 * @brief Static function to check the response type
    249 * @details Checks if the response type of this message was return code
    250 * @param crd The crd field passed
    251 * @param error_code Status code of this operation
    252 * @return RMNETCTL_SUCCESS if successful
    253 * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
    254 */
    255 static inline int _rmnetctl_check_code(int crd, uint16_t *error_code) {
    256 	int return_code = RMNETCTL_LIB_ERR;
    257 	do {
    258 	if (crd != RMNET_NETLINK_MSG_RETURNCODE) {
    259 		*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
    260 		break;
    261 	}
    262 	return_code = RMNETCTL_SUCCESS;
    263 	} while(0);
    264 	return return_code;
    265 }
    266 
    267 /*!
    268 * @brief Static function to check the response type
    269 * @details Checks if the response type of this message was data
    270 * @param crd The crd field passed
    271 * @param error_code Status code of this operation
    272 * @return RMNETCTL_SUCCESS if successful
    273 * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
    274 */
    275 static inline int _rmnetctl_check_data(int crd, uint16_t *error_code) {
    276 	int return_code = RMNETCTL_LIB_ERR;
    277 	do {
    278 	if (crd != RMNET_NETLINK_MSG_RETURNDATA) {
    279 		*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
    280 		break;
    281 	}
    282 	return_code = RMNETCTL_SUCCESS;
    283 	} while(0);
    284 	return return_code;
    285 }
    286 
    287 /*!
    288 * @brief Static function to set the return value
    289 * @details Checks if the error_code from the transaction is zero for a return
    290 * code type message and sets the message type as RMNETCTL_SUCCESS
    291 * @param crd The crd field passed
    292 * @param error_code Status code of this operation
    293 * @return RMNETCTL_SUCCESS if successful
    294 * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
    295 * Check error_code
    296 */
    297 static inline int _rmnetctl_set_codes(int error_val, uint16_t *error_code) {
    298 	int return_code = RMNETCTL_KERNEL_ERR;
    299 	if (error_val == RMNET_CONFIG_OK)
    300 		return_code = RMNETCTL_SUCCESS;
    301 	else
    302 		*error_code = (uint16_t)error_val + RMNETCTL_KERNEL_FIRST_ERR;
    303 	return return_code;
    304 }
    305 
    306 /*===========================================================================
    307 				EXPOSED API
    308 ===========================================================================*/
    309 
    310 int rmnetctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code)
    311 {
    312 	pid_t pid = 0;
    313 	int netlink_fd = -1, return_code = RMNETCTL_LIB_ERR;
    314 	struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
    315 	do {
    316 	if ((!hndl) || (!error_code)){
    317 		return_code = RMNETCTL_INVALID_ARG;
    318 		break;
    319 	}
    320 
    321 	*hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t));
    322 	if (!*hndl) {
    323 		*error_code = RMNETCTL_API_ERR_HNDL_INVALID;
    324 		break;
    325 	}
    326 
    327 	memset(*hndl, 0, sizeof(rmnetctl_hndl_t));
    328 
    329 	pid = getpid();
    330 	if (pid  < MIN_VALID_PROCESS_ID) {
    331 		free(*hndl);
    332 		*error_code = RMNETCTL_INIT_ERR_PROCESS_ID;
    333 		break;
    334 	}
    335 	(*hndl)->pid = (uint32_t)pid;
    336 	netlink_fd = socket(PF_NETLINK, SOCK_RAW, RMNET_NETLINK_PROTO);
    337 	if (netlink_fd < MIN_VALID_SOCKET_FD) {
    338 		free(*hndl);
    339 		*error_code = RMNETCTL_INIT_ERR_NETLINK_FD;
    340 		break;
    341 	}
    342 
    343 	(*hndl)->netlink_fd = netlink_fd;
    344 
    345 	memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl));
    346 
    347 	(*hndl)->src_addr.nl_family = AF_NETLINK;
    348 	(*hndl)->src_addr.nl_pid = (*hndl)->pid;
    349 
    350 	saddr_ptr = &(*hndl)->src_addr;
    351 	if (bind((*hndl)->netlink_fd,
    352 		(struct sockaddr*)saddr_ptr,
    353 		sizeof(struct sockaddr_nl)) < 0) {
    354 		close((*hndl)->netlink_fd);
    355 		free(*hndl);
    356 		*error_code = RMNETCTL_INIT_ERR_BIND;
    357 		break;
    358 	}
    359 
    360 	memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl));
    361 
    362 	(*hndl)->dest_addr.nl_family = AF_NETLINK;
    363 	(*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID;
    364 	(*hndl)->dest_addr.nl_groups = UNICAST;
    365 
    366 	return_code = RMNETCTL_SUCCESS;
    367 	} while(0);
    368 	return return_code;
    369 }
    370 
    371 void rmnetctl_cleanup(rmnetctl_hndl_t *hndl)
    372 {
    373 	if (!hndl)
    374 		return;
    375 	close(hndl->netlink_fd);
    376 	free(hndl);
    377 }
    378 
    379 int rmnet_associate_network_device(rmnetctl_hndl_t *hndl,
    380 				   const char *dev_name,
    381 				   uint16_t *error_code,
    382 				   uint8_t assoc_dev)
    383 {
    384 	struct rmnet_nl_msg_s request, response;
    385 	size_t str_len = 0;
    386 	int return_code = RMNETCTL_LIB_ERR;
    387 	do {
    388 	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
    389 		((assoc_dev != RMNETCTL_DEVICE_ASSOCIATE) &&
    390 		(assoc_dev != RMNETCTL_DEVICE_UNASSOCIATE))) {
    391 		return_code = RMNETCTL_INVALID_ARG;
    392 		break;
    393 	}
    394 
    395 	if (assoc_dev == RMNETCTL_DEVICE_ASSOCIATE)
    396 		request.message_type = RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE;
    397 	else
    398 		request.message_type = RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE;
    399 
    400 	request.arg_length = RMNET_MAX_STR_LEN;
    401 	str_len = strlcpy((char *)(request.data), dev_name, (size_t)RMNET_MAX_STR_LEN);
    402 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    403 		break;
    404 
    405 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    406 		!= RMNETCTL_SUCCESS)
    407 		break;
    408 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    409 		break;
    410 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    411 	} while(0);
    412 	return return_code;
    413 }
    414 
    415 int rmnet_get_network_device_associated(rmnetctl_hndl_t *hndl,
    416 					const char *dev_name,
    417 					int *register_status,
    418 					uint16_t *error_code) {
    419 	struct rmnet_nl_msg_s request, response;
    420 	size_t str_len = 0;
    421 	int  return_code = RMNETCTL_LIB_ERR;
    422 	do {
    423 	if ((!hndl) || (!register_status) || (!error_code) ||
    424 	_rmnetctl_check_dev_name(dev_name)) {
    425 		return_code = RMNETCTL_INVALID_ARG;
    426 		break;
    427 	}
    428 
    429 	request.message_type = RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED;
    430 
    431 	request.arg_length = RMNET_MAX_STR_LEN;
    432 	str_len = strlcpy((char *)(request.data), dev_name, RMNET_MAX_STR_LEN);
    433 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    434 		break;
    435 
    436 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    437 		!= RMNETCTL_SUCCESS)
    438 		break;
    439 
    440 	if (_rmnetctl_check_data(response.crd, error_code) != RMNETCTL_SUCCESS)
    441 		break;
    442 
    443 	*register_status = response.return_code;
    444 	return_code = RMNETCTL_SUCCESS;
    445 	} while(0);
    446 	return return_code;
    447 }
    448 
    449 int rmnet_set_link_egress_data_format(rmnetctl_hndl_t *hndl,
    450 				      uint32_t egress_flags,
    451 				      uint16_t agg_size,
    452 				      uint16_t agg_count,
    453 				      const char *dev_name,
    454 				      uint16_t *error_code) {
    455 	struct rmnet_nl_msg_s request, response;
    456 	size_t str_len = 0;
    457 	int  return_code = RMNETCTL_LIB_ERR;
    458 	do {
    459 	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
    460 	    ((~EGRESS_FLAGS_MASK) & egress_flags)) {
    461 		return_code = RMNETCTL_INVALID_ARG;
    462 		break;
    463 	}
    464 
    465 	request.message_type = RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT;
    466 
    467 	request.arg_length = RMNET_MAX_STR_LEN +
    468 			 sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t);
    469 	str_len = strlcpy((char *)(request.data_format.dev),
    470 			  dev_name,
    471 			  RMNET_MAX_STR_LEN);
    472 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    473 		break;
    474 
    475 	request.data_format.flags = egress_flags;
    476 	request.data_format.agg_size = agg_size;
    477 	request.data_format.agg_count = agg_count;
    478 
    479 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    480 		!= RMNETCTL_SUCCESS)
    481 		break;
    482 
    483 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    484 		break;
    485 
    486 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    487 	} while(0);
    488 	return return_code;
    489 }
    490 
    491 int rmnet_get_link_egress_data_format(rmnetctl_hndl_t *hndl,
    492 				      const char *dev_name,
    493 				      uint32_t *egress_flags,
    494 				      uint16_t *agg_size,
    495 				      uint16_t *agg_count,
    496 				      uint16_t *error_code) {
    497 	struct rmnet_nl_msg_s request, response;
    498 	size_t str_len = 0;
    499 	int  return_code = RMNETCTL_LIB_ERR;
    500 	do {
    501 	if ((!hndl) || (!egress_flags) || (!agg_size) || (!agg_count) ||
    502 	(!error_code) || _rmnetctl_check_dev_name(dev_name)) {
    503 		return_code = RMNETCTL_INVALID_ARG;
    504 		break;
    505 	}
    506 	request.message_type = RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT;
    507 
    508 	request.arg_length = RMNET_MAX_STR_LEN;
    509 	str_len = strlcpy((char *)(request.data_format.dev),
    510 			  dev_name,
    511 			  RMNET_MAX_STR_LEN);
    512 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    513 		break;
    514 
    515 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    516 		!= RMNETCTL_SUCCESS)
    517 		break;
    518 
    519 	if (_rmnetctl_check_data(response.crd, error_code) != RMNETCTL_SUCCESS)
    520 		break;
    521 
    522 	*egress_flags = response.data_format.flags;
    523 	*agg_size = response.data_format.agg_size;
    524 	*agg_count = response.data_format.agg_count;
    525 	return_code = RMNETCTL_SUCCESS;
    526 	} while(0);
    527 	return return_code;
    528 }
    529 
    530 int rmnet_set_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
    531 						 uint32_t ingress_flags,
    532 						 uint8_t  tail_spacing,
    533 						 const char *dev_name,
    534 						 uint16_t *error_code) {
    535 	struct rmnet_nl_msg_s request, response;
    536 	size_t str_len = 0;
    537 	int  return_code = RMNETCTL_LIB_ERR;
    538 	do {
    539 	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
    540 	    ((~INGRESS_FLAGS_MASK) & ingress_flags)) {
    541 		return_code = RMNETCTL_INVALID_ARG;
    542 		break;
    543 	}
    544 
    545 	request.message_type = RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT;
    546 
    547 	request.arg_length = RMNET_MAX_STR_LEN +
    548 	sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t);
    549 	str_len = strlcpy((char *)(request.data_format.dev),
    550 			  dev_name,
    551 			  RMNET_MAX_STR_LEN);
    552 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    553 		break;
    554 	request.data_format.flags = ingress_flags;
    555 	request.data_format.tail_spacing = tail_spacing;
    556 
    557 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    558 		!= RMNETCTL_SUCCESS)
    559 		break;
    560 
    561 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    562 		break;
    563 
    564 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    565 	} while(0);
    566 	return return_code;
    567 }
    568 
    569 int rmnet_get_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
    570 						 const char *dev_name,
    571 						 uint32_t *ingress_flags,
    572 						 uint8_t  *tail_spacing,
    573 						 uint16_t *error_code) {
    574 	struct rmnet_nl_msg_s request, response;
    575 	size_t str_len = 0;
    576 	int  return_code = RMNETCTL_LIB_ERR;
    577 	do {
    578 	if ((!hndl) || (!error_code) ||
    579 		_rmnetctl_check_dev_name(dev_name)) {
    580 		return_code = RMNETCTL_INVALID_ARG;
    581 		break;
    582 	}
    583 
    584 	request.message_type = RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT;
    585 
    586 	request.arg_length = RMNET_MAX_STR_LEN;
    587 	str_len = strlcpy((char *)(request.data_format.dev),
    588 			  dev_name,
    589 			  RMNET_MAX_STR_LEN);
    590 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    591 		break;
    592 
    593 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    594 		!= RMNETCTL_SUCCESS)
    595 		break;
    596 
    597 	if (_rmnetctl_check_data(response.crd, error_code) != RMNETCTL_SUCCESS)
    598 		break;
    599 
    600 	if (ingress_flags)
    601 		*ingress_flags = response.data_format.flags;
    602 
    603 	if (tail_spacing)
    604 		*tail_spacing = response.data_format.tail_spacing;
    605 
    606 	return_code = RMNETCTL_SUCCESS;
    607 	} while(0);
    608 	return return_code;
    609 }
    610 
    611 int rmnet_set_logical_ep_config(rmnetctl_hndl_t *hndl,
    612 				int32_t ep_id,
    613 				uint8_t operating_mode,
    614 				const char *dev_name,
    615 				const char *next_dev,
    616 				uint16_t *error_code) {
    617 	struct rmnet_nl_msg_s request, response;
    618 	size_t str_len = 0;
    619 	int return_code = RMNETCTL_LIB_ERR;
    620 	do {
    621 	if ((!hndl) || ((ep_id < -1) || (ep_id > 31)) || (!error_code) ||
    622 		_rmnetctl_check_dev_name(dev_name) ||
    623 		_rmnetctl_check_dev_name(next_dev) ||
    624 		operating_mode >= RMNET_EPMODE_LENGTH) {
    625 		return_code = RMNETCTL_INVALID_ARG;
    626 		break;
    627 	}
    628 
    629 	request.message_type = RMNET_NETLINK_SET_LOGICAL_EP_CONFIG;
    630 
    631 	request.arg_length = RMNET_MAX_STR_LEN +
    632 	RMNET_MAX_STR_LEN + sizeof(int32_t) + sizeof(uint8_t);
    633 	str_len = strlcpy((char *)(request.local_ep_config.dev),
    634 			  dev_name,
    635 			  RMNET_MAX_STR_LEN);
    636 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    637 		break;
    638 
    639 	str_len = strlcpy((char *)(request.local_ep_config.next_dev),
    640 			  next_dev,
    641 			  RMNET_MAX_STR_LEN);
    642 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    643 		break;
    644 	request.local_ep_config.ep_id = ep_id;
    645 	request.local_ep_config.operating_mode = operating_mode;
    646 
    647 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    648 		!= RMNETCTL_SUCCESS)
    649 		break;
    650 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    651 		break;
    652 
    653 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    654 	} while(0);
    655 	return return_code;
    656 }
    657 
    658 int rmnet_unset_logical_ep_config(rmnetctl_hndl_t *hndl,
    659 				  int32_t ep_id,
    660 				  const char *dev_name,
    661 				  uint16_t *error_code) {
    662 	struct rmnet_nl_msg_s request, response;
    663 	size_t str_len = 0;
    664 	int return_code = RMNETCTL_LIB_ERR;
    665 	do {
    666 
    667 	if ((!hndl) || ((ep_id < -1) || (ep_id > 31)) || (!error_code) ||
    668 		_rmnetctl_check_dev_name(dev_name)) {
    669 		return_code = RMNETCTL_INVALID_ARG;
    670 		break;
    671 	}
    672 
    673 	request.message_type = RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG;
    674 
    675 	request.arg_length = RMNET_MAX_STR_LEN + sizeof(int32_t);
    676 	str_len = strlcpy((char *)(request.local_ep_config.dev),
    677 			  dev_name,
    678 			  RMNET_MAX_STR_LEN);
    679 
    680 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    681 		break;
    682 
    683 	request.local_ep_config.ep_id = ep_id;
    684 
    685 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    686 		!= RMNETCTL_SUCCESS)
    687 		break;
    688 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    689 		break;
    690 
    691 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    692 	} while(0);
    693 
    694 	return return_code;
    695 }
    696 
    697 int rmnet_get_logical_ep_config(rmnetctl_hndl_t *hndl,
    698 				int32_t ep_id,
    699 				const char *dev_name,
    700 				uint8_t *operating_mode,
    701 				char **next_dev,
    702 				uint32_t next_dev_len,
    703 				uint16_t *error_code) {
    704 	struct rmnet_nl_msg_s request, response;
    705 	size_t str_len = 0;
    706 	int return_code = RMNETCTL_LIB_ERR;
    707 	do {
    708 	if ((!hndl) || (!operating_mode) || (!error_code) || ((ep_id < -1) ||
    709 	    (ep_id > 31)) || _rmnetctl_check_dev_name(dev_name) || (!next_dev)
    710 	    || (0 == next_dev_len)) {
    711 		return_code = RMNETCTL_INVALID_ARG;
    712 		break;
    713 	}
    714 
    715 	request.message_type = RMNET_NETLINK_GET_LOGICAL_EP_CONFIG;
    716 
    717 	request.arg_length = RMNET_MAX_STR_LEN + sizeof(int32_t);
    718 	str_len = strlcpy((char *)(request.local_ep_config.dev),
    719 			  dev_name,
    720 			  RMNET_MAX_STR_LEN);
    721 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    722 		break;
    723 
    724 	request.local_ep_config.ep_id = ep_id;
    725 
    726 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    727 		!= RMNETCTL_SUCCESS)
    728 		break;
    729 	if (_rmnetctl_check_data(response.crd, error_code) != RMNETCTL_SUCCESS)
    730 		break;
    731 
    732 	str_len = strlcpy(*next_dev,
    733 			  (char *)(response.local_ep_config.next_dev),
    734 			  min(RMNET_MAX_STR_LEN, next_dev_len));
    735 	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
    736 		break;
    737 
    738 	*operating_mode = response.local_ep_config.operating_mode;
    739 	return_code = RMNETCTL_SUCCESS;
    740 	} while(0);
    741 	return return_code;
    742 }
    743 
    744 int rmnet_new_vnd_prefix(rmnetctl_hndl_t *hndl,
    745 			 uint32_t id,
    746 			 uint16_t *error_code,
    747 			 uint8_t new_vnd,
    748 			 const char *prefix)
    749 {
    750 	struct rmnet_nl_msg_s request, response;
    751 	int return_code = RMNETCTL_LIB_ERR;
    752 	size_t str_len = 0;
    753 	do {
    754 	if ((!hndl) || (!error_code) ||
    755 	((new_vnd != RMNETCTL_NEW_VND) && (new_vnd != RMNETCTL_FREE_VND))) {
    756 		return_code = RMNETCTL_INVALID_ARG;
    757 		break;
    758 	}
    759 
    760 	memset(request.vnd.vnd_name, 0, RMNET_MAX_STR_LEN);
    761 	if (new_vnd ==  RMNETCTL_NEW_VND) {
    762 		if (prefix) {
    763 			request.message_type =RMNET_NETLINK_NEW_VND_WITH_PREFIX;
    764 			str_len = strlcpy((char *)request.vnd.vnd_name,
    765 					  prefix, RMNET_MAX_STR_LEN);
    766 			if (_rmnetctl_check_len(str_len, error_code)
    767 						!= RMNETCTL_SUCCESS)
    768 				break;
    769 		} else {
    770 			request.message_type = RMNET_NETLINK_NEW_VND;
    771 		}
    772 	} else {
    773 		request.message_type = RMNET_NETLINK_FREE_VND;
    774 	}
    775 
    776 	request.arg_length = sizeof(uint32_t);
    777 	request.vnd.id = id;
    778 
    779 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    780 		!= RMNETCTL_SUCCESS)
    781 		break;
    782 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    783 		break;
    784 
    785 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    786 	} while(0);
    787 	return return_code;
    788 }
    789 
    790 int rmnet_new_vnd(rmnetctl_hndl_t *hndl,
    791 		  uint32_t id,
    792 		  uint16_t *error_code,
    793 		  uint8_t new_vnd)
    794 {
    795 	return rmnet_new_vnd_prefix(hndl, id, error_code, new_vnd, 0);
    796 }
    797 
    798 int rmnet_get_vnd_name(rmnetctl_hndl_t *hndl,
    799 		       uint32_t id,
    800 		       uint16_t *error_code,
    801 		       char *buf,
    802 		       uint32_t buflen)
    803 {
    804 	struct rmnet_nl_msg_s request, response;
    805 	uint32_t str_len;
    806 	int return_code = RMNETCTL_LIB_ERR;
    807 	do {
    808 	if ((!hndl) || (!error_code) || (!buf) || (0 == buflen)) {
    809 		return_code = RMNETCTL_INVALID_ARG;
    810 		break;
    811 	}
    812 
    813 	request.message_type = RMNET_NETLINK_GET_VND_NAME;
    814 	request.arg_length = sizeof(uint32_t);
    815 	request.vnd.id = id;
    816 
    817 
    818 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    819 		!= RMNETCTL_SUCCESS)
    820 		break;
    821 	if (_rmnetctl_check_data(response.crd, error_code) != RMNETCTL_SUCCESS)
    822 		break;
    823 
    824 	str_len = (uint32_t)strlcpy(buf,
    825 			  (char *)(response.vnd.vnd_name),
    826 			  buflen);
    827 	if (str_len >= buflen) {
    828 		*error_code = RMNETCTL_API_ERR_STRING_TRUNCATION;
    829 		break;
    830 	}
    831 
    832 	return_code = RMNETCTL_SUCCESS;
    833 	} while (0);
    834 	return return_code;
    835 }
    836 
    837 int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl,
    838 			      uint32_t id,
    839 			      uint32_t map_flow_id,
    840 			      uint32_t tc_flow_id,
    841 			      uint8_t set_flow,
    842 			      uint16_t *error_code) {
    843 	struct rmnet_nl_msg_s request, response;
    844 	int return_code = RMNETCTL_LIB_ERR;
    845 	do {
    846 	if ((!hndl) || (!error_code) || ((set_flow != RMNETCTL_ADD_FLOW) &&
    847 	    (set_flow != RMNETCTL_DEL_FLOW))) {
    848 		return_code = RMNETCTL_INVALID_ARG;
    849 		break;
    850 	}
    851 	if (set_flow ==  RMNETCTL_ADD_FLOW)
    852 		request.message_type = RMNET_NETLINK_ADD_VND_TC_FLOW;
    853 	else
    854 		request.message_type = RMNET_NETLINK_DEL_VND_TC_FLOW;
    855 
    856 	request.arg_length = (sizeof(uint32_t))*3;
    857 	request.flow_control.id = id;
    858 	request.flow_control.map_flow_id = map_flow_id;
    859 	request.flow_control.tc_flow_id = tc_flow_id;
    860 
    861 	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
    862 	!= RMNETCTL_SUCCESS)
    863 		break;
    864 	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
    865 		break;
    866 	return_code = _rmnetctl_set_codes(response.return_code, error_code);
    867 	} while(0);
    868 	return return_code;
    869 }
    870 
    871