diff options
author | Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> | 2017-12-20 19:11:40 -0700 |
---|---|---|
committer | Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> | 2018-01-25 07:26:52 -0700 |
commit | 5b903ddace71fe4fee98056e7a9f507029c04bd9 (patch) | |
tree | 576788e8d45b8c2027053ca99d03bd1932f93e90 | |
parent | dba447c1eed52628c756fc2614de119d44da10f3 (diff) |
librmnetctl: Add support for rmnet driver
Add support for newlink, dellink, chagelink and bridge netlink
operations for the netlink type "rmnet" to communicate with the
linux rmnet driver.
CRs-Fixed: 2184454
Change-Id: Ib088fd919a77c926c5e4c3a55062a59ab72fd5d0
-rw-r--r-- | rmnetctl/cli/rmnetcli.c | 90 | ||||
-rw-r--r-- | rmnetctl/inc/librmnetctl.h | 85 | ||||
-rw-r--r-- | rmnetctl/src/librmnetctl.c | 435 |
3 files changed, 601 insertions, 9 deletions
diff --git a/rmnetctl/cli/rmnetcli.c b/rmnetctl/cli/rmnetcli.c index a483c93..c02850b 100644 --- a/rmnetctl/cli/rmnetcli.c +++ b/rmnetctl/cli/rmnetcli.c @@ -2,7 +2,7 @@ R M N E T C L I . C -Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. +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 @@ -208,6 +208,21 @@ static void rmnet_api_usage(void) printf(_2TABS" <mdm_flow_hndl> handle - tc flow handle"); printf(_2TABS" <tc_flow_hndl> mapping for a virtual network"); printf(_2TABS" device node\n\n"); + printf("**************************\n"); + printf("RmNet RTM_NETLINK API Usage:\n\n"); + printf("rmnetcli -n newlink <dev_id> Add a vnd w/ newlink"); + printf(_2TABS" <vnd> string - vnd device_name"); + printf(_2TABS" <vnd id> int - new vnd id"); + printf(_2TABS" [flags] int - starting flag config\n\n"); + printf("rmnetcli -n changelink <dev_id> Change a vnd's flags"); + printf(_2TABS" <vnd> string - vnd device_name"); + printf(_2TABS" <vnd id> int - new vnd id"); + printf(_2TABS" <flags> int - new flag config\n\n"); + printf("rmnetcli -n dellink <dev_name> Delete a vnd"); + printf(_2TABS" by inputting dev name\n\n"); + printf("rmnetcli -n bridgelink <dev_name> Bridge a vnd and a dev"); + printf(_2TABS" <vnd id> by specifying dev id and vnd id\n\n"); + } static void print_rmnetctl_lib_errors(uint16_t error_number) @@ -239,8 +254,11 @@ static void print_rmnet_api_status(int return_code, uint16_t error_number) else if (return_code == RMNETCTL_LIB_ERR) { printf("LIBRARY "); print_rmnetctl_lib_errors(error_number); - } else if (return_code == RMNETCTL_KERNEL_ERR) - printf("KERNEL %s", rmnetctl_error_code_text[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"); } @@ -275,10 +293,66 @@ static int rmnet_api_call(int argc, char *argv[]) RMNETCTL_CFG_SUCCESS_HELP_COMMAND); return RMNETCTL_LIB_ERR; } - return_code = rmnetctl_init(&handle, &error_number); - if (return_code!= RMNETCTL_SUCCESS) { - print_rmnet_api_status(return_code, error_number); - 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]); + uint32_t flags = 0; + /* 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; @@ -400,8 +474,10 @@ static int rmnet_api_call(int argc, char *argv[]) return_code = rmnet_unset_logical_ep_config(handle, _STRTOI32(argv[1]), argv[2], &error_number); } +end: print_rmnet_api_status(return_code, error_number); rmnetctl_cleanup(handle); + rtrmnet_ctl_deinit(handle); return return_code; } diff --git a/rmnetctl/inc/librmnetctl.h b/rmnetctl/inc/librmnetctl.h index 8913cc8..3d622bf 100644 --- a/rmnetctl/inc/librmnetctl.h +++ b/rmnetctl/inc/librmnetctl.h @@ -2,7 +2,7 @@ L I B R M N E T C T L . H -Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. +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 @@ -517,5 +517,88 @@ int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl, 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/rmnetctl/src/librmnetctl.c b/rmnetctl/src/librmnetctl.c index 1cb9930..c9c74a8 100644 --- a/rmnetctl/src/librmnetctl.c +++ b/rmnetctl/src/librmnetctl.c @@ -2,7 +2,7 @@ L I B R M N E T C T L . C -Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved. +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 @@ -47,6 +47,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdio.h> #include <unistd.h> #include <stdlib.h> +#include <errno.h> +#include <linux/rtnetlink.h> +#include <linux/gen_stats.h> +#include <net/if.h> +#include <asm/types.h> #include <linux/rmnet_data.h> #include "librmnetctl_hndl.h" #include "librmnetctl.h" @@ -78,6 +83,17 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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]; +}; + + + /*=========================================================================== LOCAL FUNCTION DEFINITIONS ===========================================================================*/ @@ -934,3 +950,420 @@ int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl, 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); +} |