From 94abb589a8e60c49d9cc4598a27b50c9f757b623 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan 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 +#include +#include +#include +#include +#include +#include +#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 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 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 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 Sets the egress data"); + printf(_2TABS" format for a particular link."); + printf(_2TABS" dev_name cannot be larger"); + printf(_2TABS" than 15 characters."); + printf(_5TABS" Returns the status code\n\n"); + printf("rmnetcli getledf 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 Sets the ingress"); + printf(_2TABS" data format for a particular"); + printf(_2TABS" 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 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 Sets the logical"); + printf(_2TABS" endpoint configuration for"); + printf(_2TABS" a particular link."); + printf(_2TABS" 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 Un-sets the logical"); + printf(_2TABS" 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 Sets the logical"); + printf(_2TABS" 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 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 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 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 Get name of"); + printf(_5TABS" network device node from id\n\n"); + printf("rmnetcli freevnd 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 Add a modem flow"); + printf(_2TABS" handle - tc flow handle"); + printf(_2TABS" mapping for a virtual network"); + printf(_2TABS" device node\n\n"); + printf("rmnetcli delvnctcflow Delete a modem flow"); + printf(_2TABS" handle - tc flow handle"); + printf(_2TABS" 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 Add a vnd w/ newlink"); + printf(_2TABS" string - vnd device_name"); + printf(_2TABS" int - new vnd id"); + printf(_2TABS" [flags] int - starting flag config\n\n"); + printf("rmnetcli -n changelink Change a vnd's flags"); + printf(_2TABS" string - vnd device_name"); + printf(_2TABS" int - new vnd id"); + printf(_2TABS" int - new flag config\n\n"); + printf("rmnetcli -n dellink Delete a vnd"); + printf(_2TABS" by inputting dev name\n\n"); + printf("rmnetcli -n bridgelink Bridge a vnd and a dev"); + printf(_2TABS" 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], ®ister_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmnet_data.h" +#include "librmnetctl_hndl.h" +#include "librmnetctl.h" + +#ifdef USE_GLIB +#include +#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