blob: d5e55e3891d3cc92c08a564773383279dac7a36a [file] [log] [blame]
/******************************************************************************
L I B R M N E T C T L . C
Copyright (c) 2013-2015, 2017-2019, 2021 The Linux Foundation.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
/*!
* @file librmnetctl.c
* @brief rmnet control API's implementation file
*/
/*===========================================================================
INCLUDE FILES
===========================================================================*/
#include <sys/socket.h>
#include <stdint.h>
#include <linux/netlink.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/rtnetlink.h>
#include <linux/gen_stats.h>
#include <net/if.h>
#include <asm/types.h>
#include <linux/rmnet_data.h>
#include "librmnetctl_hndl.h"
#include "librmnetctl.h"
#ifdef USE_GLIB
#include <glib.h>
#define strlcpy g_strlcpy
#endif
#define RMNETCTL_SOCK_FLAG 0
#define ROOT_USER_ID 0
#define MIN_VALID_PROCESS_ID 0
#define MIN_VALID_SOCKET_FD 0
#define KERNEL_PROCESS_ID 0
#define UNICAST 0
#define MAX_BUF_SIZE sizeof(struct nlmsghdr) + sizeof(struct rmnet_nl_msg_s)
#define INGRESS_FLAGS_MASK (RMNET_INGRESS_FIX_ETHERNET | \
RMNET_INGRESS_FORMAT_MAP | \
RMNET_INGRESS_FORMAT_DEAGGREGATION | \
RMNET_INGRESS_FORMAT_DEMUXING | \
RMNET_INGRESS_FORMAT_MAP_COMMANDS | \
RMNET_INGRESS_FORMAT_MAP_CKSUMV3 | \
RMNET_INGRESS_FORMAT_MAP_CKSUMV4)
#define EGRESS_FLAGS_MASK (RMNET_EGRESS_FORMAT__RESERVED__ | \
RMNET_EGRESS_FORMAT_MAP | \
RMNET_EGRESS_FORMAT_AGGREGATION | \
RMNET_EGRESS_FORMAT_MUXING | \
RMNET_EGRESS_FORMAT_MAP_CKSUMV3 | \
RMNET_EGRESS_FORMAT_MAP_CKSUMV4)
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((char *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
#define NLMSG_DATA_SIZE 500
#define CHECK_MEMSCPY(x) ({if (x < 0 ){return RMNETCTL_LIB_ERR;}})
struct nlmsg {
struct nlmsghdr nl_addr;
struct ifinfomsg ifmsg;
char data[NLMSG_DATA_SIZE];
};
struct rmnetctl_uplink_params {
uint16_t byte_count;
uint8_t packet_count;
uint8_t features;
uint32_t time_limit;
};
/* Uplink Aggregation contexts for the RT RmNet driver */
enum {
RMNETCTL_DEFAULT_UL_AGG_STATE,
RMNETCTL_LL_UL_AGG_STATE,
RMNETCTL_MAX_UL_AGG_STATE,
};
/* IFLA Attributes for the RT RmNet driver */
enum {
RMNETCTL_IFLA_UNSPEC,
RMNETCTL_IFLA_MUX_ID,
RMNETCTL_IFLA_FLAGS,
RMNETCTL_IFLA_DFC_QOS,
RMNETCTL_IFLA_UPLINK_PARAMS,
RMNETCTL_IFLA_UPLINK_STATE_ID,
__RMNETCTL_IFLA_MAX,
};
/* Flow message types sent to DFC driver */
enum {
/* Activate flow */
RMNET_FLOW_MSG_ACTIVATE = 1,
/* Delete flow */
RMNET_FLOW_MSG_DEACTIVATE = 2,
/* Legacy flow control */
RMNET_FLOW_MSG_CONTROL = 3,
/* Flow up */
RMNET_FLOW_MSG_UP = 4,
/* Flow down */
RMNET_FLOW_MSG_DOWN = 5,
/* Change ACK scaling */
RMNET_FLOW_MSG_QMI_SCALE = 6,
/* Change powersave workqueue polling freq */
RMNET_FLOW_MSG_WDA_FREQ = 7,
/* Change underlying transport channel */
RMNET_FLOW_MSG_CHANNEL_SWITCH = 8,
};
/* 0 reserved, 1-15 for data, 16-30 for acks */
#define RMNETCTL_NUM_TX_QUEUES 31
/* This needs to be hardcoded here because some legacy linux systems
* do not have this definition
*/
#define RMNET_IFLA_NUM_TX_QUEUES 31
/*===========================================================================
LOCAL FUNCTION DEFINITIONS
===========================================================================*/
/* Helper functions
* @brief helper function to implement a secure memcpy
* @details take source and destination buffer size into
* considerations before copying
* @param dst destination buffer
* @param dst_size size of destination buffer
* @param src source buffer
* @param src_size size of source buffer
* @return size of the smallest of two buffer
*/
static inline size_t memscpy(void *dst, size_t dst_size, const void *src,
size_t src_size) {
size_t min_size = dst_size < src_size ? dst_size : src_size;
memcpy(dst, src, min_size);
return min_size;
}
/*
* @brief helper function to implement a secure memcpy
* for a concatenating buffer where offset is kept
* track of
* @details take source and destination buffer size into
* considerations before copying
* @param dst destination buffer
* @param dst_size pointer used to decrement
* @param src source buffer
* @param src_size size of source buffer
* @return size of the remaining buffer
*/
static inline int memscpy_repeat(void* dst, size_t *dst_size,
const void* src, size_t src_size)
{
if( !dst_size || *dst_size <= src_size || !dst || !src)
return RMNETCTL_LIB_ERR;
else {
*dst_size -= memscpy(dst, *dst_size, src, src_size);
}
return *dst_size;
}
/*!
* @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, buffsize = MAX_BUF_SIZE - sizeof(struct nlmsghdr);
uint16_t return_code = RMNETCTL_API_ERR_HNDL_INVALID;
struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
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;
memscpy((void *)NLMSG_DATA(request_buf), buffsize, 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;
socklen_t addrlen = sizeof(struct sockaddr_nl);
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;
}
buffsize = MAX_BUF_SIZE - sizeof(struct nlmsghdr);
memscpy(response, buffsize, (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 | SOCK_CLOEXEC, 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 Add a Routing Attribute to a Netlink message
* @param *req The Netlink message we're adding to
* @param *reqsize The remaining space within the Netlink message
* @param type The type of the RTA to add
* @param len The length of the RTA data to add
* @param *data A pointer to the RTA data to add
* @return RMNETCTL_LIB_ERR if there is not enough space to add the RTA
* @return RMNETCTL_SUCCESS if we added the RTA successfully
*/
static int rta_put(struct nlmsg *req, size_t *reqsize, int type, int len,
void *data)
{
struct rtattr *attr = NLMSG_TAIL(&req->nl_addr);
attr->rta_type = type;
attr->rta_len = RTA_LENGTH(len);
CHECK_MEMSCPY(memscpy_repeat(RTA_DATA(attr), reqsize, data, len));
req->nl_addr.nlmsg_len = NLMSG_ALIGN(req->nl_addr.nlmsg_len) +
RTA_ALIGN(attr->rta_len);
return RMNETCTL_SUCCESS;
}
/* @brief Add an RTA to the Netlink message with a u8 data
* @param *req The Netlink message
* @param *reqsize The remainins space within the Netlink message
* @param type The type of the RTA to add
* @param data The data of the RTA
* @rteturn RMNETCTL_LIB_ERR if there is not enough space to add the RTA
* @return RMNETCTL_SUCCESS if we added the RTA successfully
*/
static int rta_put_u8(struct nlmsg *req, size_t *reqsize, int type,
uint8_t data)
{
return rta_put(req, reqsize, type, sizeof(data), &data);
}
/* @brief Add an RTA to the Netlink message with a u16 data
* @param *req The Netlink message
* @param *reqsize The remainins space within the Netlink message
* @param type The type of the RTA to add
* @param data The data of the RTA
* @rteturn RMNETCTL_LIB_ERR if there is not enough space to add the RTA
* @return RMNETCTL_SUCCESS if we added the RTA successfully
*/
static int rta_put_u16(struct nlmsg *req, size_t *reqsize, int type,
uint16_t data)
{
return rta_put(req, reqsize, type, sizeof(data), &data);
}
/* @brief Add an RTA to the Netlink message with a u32 data
* @param *req The Netlink message
* @param *reqsize The remainins space within the Netlink message
* @param type The type of the RTA to add
* @param data The data of the RTA
* @rteturn RMNETCTL_LIB_ERR if there is not enough space to add the RTA
* @return RMNETCTL_SUCCESS if we added the RTA successfully
*/
static int rta_put_u32(struct nlmsg *req, size_t *reqsize, int type,
uint32_t data)
{
return rta_put(req, reqsize, type, sizeof(data), &data);
}
/* @brief Add an RTA to the Netlink message with string data
* @param *req The Netlink message
* @param *reqsize The remainins space within the Netlink message
* @param type The type of the RTA to add
* @param *data The data of the RTA
* @rteturn RMNETCTL_LIB_ERR if there is not enough space to add the RTA
* @return RMNETCTL_SUCCESS if we added the RTA successfully
*/
static int rta_put_string(struct nlmsg *req, size_t *reqsize, int type,
char *data)
{
return rta_put(req, reqsize, type, strlen(data) + 1, data);
}
/* @brief Start a nested RTA within the Netlink message
* @param *req The Netlink message
* @param *reqsize The remainins space within the Netlink message
* @param type The type of the RTA to add
* @param **start A pointer where we store the start of the ensted attribute
* @rteturn RMNETCTL_LIB_ERR if there is not enough space to add the RTA
* @return RMNETCTL_SUCCESS if we added the RTA successfully
*/
static int rta_nested_start(struct nlmsg *req, size_t *reqsize, int type,
struct rtattr **start)
{
*start = NLMSG_TAIL(&req->nl_addr);
return rta_put(req, reqsize, type, 0, NULL);
}
/* @brief End a nested RTA previously started with rta_nested_start
* @param *req The Netlink message
* @param *start The start of the nested RTA, as provided by rta_nested_start
*/
static void rta_nested_end(struct nlmsg *req, struct rtattr *start)
{
start->rta_len = (char *)NLMSG_TAIL(&req->nl_addr) - (char *)start;
}
static void rta_parse(struct rtattr **tb, int maxtype, struct rtattr *head,
int len)
{
struct rtattr *rta;
memset(tb, 0, sizeof(struct rtattr *) * maxtype);
for (rta = head; RTA_OK(rta, len);
rta = RTA_NEXT(rta, len)) {
__u16 type = rta->rta_type & NLA_TYPE_MASK;
if (type > 0 && type < maxtype)
tb[type] = rta;
}
}
static struct rtattr *rta_find(struct rtattr *rta, int attrlen, uint16_t type)
{
for (; RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) {
if (rta->rta_type == (type & NLA_TYPE_MASK))
return rta;
}
return NULL;
}
/* @brief Fill a Netlink messages with the necessary common RTAs for creating a
* RTM_NEWLINK message for creating or changing rmnet devices.
* @param *req The netlink message
* @param *reqsize The remaining space within the Netlink message
* @param devindex The ifindex of the physical device
* @param *vndname The name of the rmnet device
* @param index The MUX ID of the rmnet device
* @param flagconfig The dataformat flags for the rmnet device
* @return RMNETCTL_LIB_ERR if there is not enough space to add all RTAs
* @return RMNETCTL_SUCCESS if everything was added successfully
*/
static int rmnet_fill_newlink_msg(struct nlmsg *req, size_t *reqsize,
unsigned int devindex, char *vndname,
uint8_t index, uint32_t flagconfig)
{
struct rtattr *linkinfo, *datainfo;
struct ifla_vlan_flags flags;
int rc;
/* Set up link attr with devindex as data */
rc = rta_put_u32(req, reqsize, IFLA_LINK, devindex);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_string(req, reqsize, IFLA_IFNAME, vndname);
if (rc != RMNETCTL_SUCCESS)
return rc;
/* Set up info kind RMNET that has linkinfo and type */
rc = rta_nested_start(req, reqsize, IFLA_LINKINFO, &linkinfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_string(req, reqsize, IFLA_INFO_KIND, "rmnet");
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_nested_start(req, reqsize, IFLA_INFO_DATA, &datainfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_u16(req, reqsize, RMNETCTL_IFLA_MUX_ID, index);
if (rc != RMNETCTL_SUCCESS)
return rc;
if (flagconfig != 0) {
flags.mask = flagconfig;
flags.flags = flagconfig;
rc = rta_put(req, reqsize, RMNETCTL_IFLA_FLAGS, sizeof(flags),
&flags);
if (rc != RMNETCTL_SUCCESS)
return rc;
}
rta_nested_end(req, datainfo);
rta_nested_end(req, linkinfo);
return RMNETCTL_SUCCESS;
}
/* @brief Fill a Netlink messages with the necessary common RTAs for creating a
* RTM_NEWLINK message that configures the uplink aggregation parameters
* @param *req The netlink message
* @param *reqsize The remaining space within the Netlink message
* @param devindex The ifindex of the physical device
* @param *vndname The name of the rmnet device
* @param packet_count The max packet count
* @param byte_count The max byte count
* @param time_limit The max time limit
* @param features The enabled aggregatin features
* @param state_id The aggregation state to configure
* @param flagconfig The dataformat flags for the rmnet device
* @return RMNETCTL_LIB_ERR if there is not enough space to add all RTAs
* @return RMNETCTL_SUCCESS if everything was added successfully
*/
static int rmnet_fill_ul_agg_msg(struct nlmsg *req, size_t *reqsize,
unsigned int devindex, char *vndname,
uint8_t packet_count, uint16_t byte_count,
uint32_t time_limit, uint8_t features,
uint8_t state_id)
{
struct rmnetctl_uplink_params uplink_params;
struct rtattr *linkinfo, *datainfo;
int rc;
memset(&uplink_params, 0, sizeof(uplink_params));
/* Set up link attr with devindex as data */
rc = rta_put_u32(req, reqsize, IFLA_LINK, devindex);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_string(req, reqsize, IFLA_IFNAME, vndname);
if (rc != RMNETCTL_SUCCESS)
return rc;
/* Set up IFLA info kind RMNET that has linkinfo and type */
rc = rta_nested_start(req, reqsize, IFLA_LINKINFO, &linkinfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_string(req, reqsize, IFLA_INFO_KIND, "rmnet");
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_nested_start(req, reqsize, IFLA_INFO_DATA, &datainfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
uplink_params.byte_count = byte_count;
uplink_params.packet_count = packet_count;
uplink_params.features = features;
uplink_params.time_limit = time_limit;
rc = rta_put(req, reqsize, RMNETCTL_IFLA_UPLINK_PARAMS,
sizeof(uplink_params), &uplink_params);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_u8(req, reqsize, RMNETCTL_IFLA_UPLINK_STATE_ID, state_id);
if (rc != RMNETCTL_SUCCESS)
return rc;
rta_nested_end(req, datainfo);
rta_nested_end(req, linkinfo);
return RMNETCTL_SUCCESS;
}
/* @brief Add all necessary RTA elements to a Netlink message suitable for
* sending to the DFC driver
* @param *req The Netlink message
* @param *reqsize The remaining space in the Netlink message
* @param devindex The ifindex of the real physical device
* @param *vndname The name of the VND we're modifying
* @param *flowinfo The parameters sent to the DFC driver
* @param flowlen The length of the flowinfo parameter in bytes
* @return RMENTCTL_LIB_ERR if there is not enough space to add the RTAs
* @return RMNETCTL_SUCCESS if everything was added successfully
*/
static int rmnet_fill_flow_msg(struct nlmsg *req, size_t *reqsize,
unsigned int devindex, char *vndname,
char *flowinfo, size_t flowlen)
{
struct rtattr *linkinfo, *datainfo;
int rc;
/* Set up link attr with devindex as data */
rc = rta_put_u32(req, reqsize, IFLA_LINK, devindex);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_string(req, reqsize, IFLA_IFNAME, vndname);
if (rc != RMNETCTL_SUCCESS)
return rc;
/* Set up IFLA info kind RMNET that has linkinfo and type */
rc = rta_nested_start(req, reqsize, IFLA_LINKINFO, &linkinfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put_string(req, reqsize, IFLA_INFO_KIND, "rmnet");
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_nested_start(req, reqsize, IFLA_INFO_DATA, &datainfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rta_put(req, reqsize, RMNETCTL_IFLA_DFC_QOS, flowlen,
flowinfo);
if (rc != RMNETCTL_SUCCESS)
return rc;
rta_nested_end(req, datainfo);
rta_nested_end(req, linkinfo);
return RMNETCTL_SUCCESS;
}
/* @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;
socklen_t addr_len = sizeof(struct sockaddr_nl);
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));
netlink_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, 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;
(*hndl)->src_addr.nl_family = AF_NETLINK;
(*hndl)->src_addr.nl_pid = 0; /* auto assign */
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;
}
/* Get assigned port_id */
if (!getsockname(netlink_fd, (struct sockaddr *)saddr_ptr, &addr_len))
(*hndl)->pid = (*hndl)->src_addr.nl_pid;
(*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;
if (hndl->llc_hndl)
rtrmnet_ctl_deinit(hndl->llc_hndl);
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)
{
unsigned int devindex = 0;
struct nlmsg req;
size_t reqsize;
int rc;
if (!hndl || !devname || !vndname || !error_code ||
_rmnetctl_check_dev_name(vndname) || _rmnetctl_check_dev_name(devname))
return RMNETCTL_INVALID_ARG;
memset(&req, 0, sizeof(req));
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
rc = rta_put_u32(&req, &reqsize, RMNET_IFLA_NUM_TX_QUEUES,
RMNETCTL_NUM_TX_QUEUES);
if (rc != RMNETCTL_SUCCESS)
return rc;
rc = rmnet_fill_newlink_msg(&req, &reqsize, devindex, vndname, index,
flagconfig);
if (rc != RMNETCTL_SUCCESS)
return rc;
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)
{
unsigned 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 nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
if (!hndl || !devname || !vndname || !error_code ||
_rmnetctl_check_dev_name(vndname) || _rmnetctl_check_dev_name(devname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
rc = rmnet_fill_newlink_msg(&req, &reqsize, devindex, vndname, index,
flagconfig);
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_getvnd(rmnetctl_hndl_t *hndl, char *vndname,
uint16_t *error_code, uint16_t *mux_id,
uint32_t *flagconfig, uint8_t *agg_count,
uint16_t *agg_size, uint32_t *agg_time,
uint8_t *features)
{
struct nlmsg req;
struct nlmsghdr *resp;
struct rtattr *attrs, *linkinfo, *datainfo;
struct rtattr *tb[__RMNETCTL_IFLA_MAX];
unsigned int devindex = 0;
int resp_len;
memset(&req, 0, sizeof(req));
if (!hndl || !vndname || !error_code || !(mux_id || flagconfig) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
req.nl_addr.nlmsg_type = RTM_GETLINK;
req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nl_addr.nlmsg_flags = NLM_F_REQUEST;
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;
}
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;
}
resp_len = recv(hndl->netlink_fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
if (resp_len < 0) {
*error_code = errno;
return RMNETCTL_API_ERR_MESSAGE_RECEIVE;
}
resp = malloc((size_t)resp_len);
if (!resp) {
*error_code = errno;
return RMNETCTL_LIB_ERR;
}
resp_len = recv(hndl->netlink_fd, (char *)resp, (size_t)resp_len, 0);
if (resp_len < 0) {
*error_code = errno;
free(resp);
return RMNETCTL_API_ERR_MESSAGE_RECEIVE;
}
/* Parse out the RT attributes */
attrs = (struct rtattr *)((char *)NLMSG_DATA(resp) +
NLMSG_ALIGN(sizeof(req.ifmsg)));
linkinfo = rta_find(attrs, NLMSG_PAYLOAD(resp, sizeof(req.ifmsg)),
IFLA_LINKINFO);
if (!linkinfo) {
free(resp);
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return RMNETCTL_KERNEL_ERR;
}
datainfo = rta_find(RTA_DATA(linkinfo), RTA_PAYLOAD(linkinfo),
IFLA_INFO_DATA);
if (!datainfo) {
free(resp);
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return RMNETCTL_KERNEL_ERR;
}
/* Parse all the rmnet-specific information from the kernel */
rta_parse(tb, __RMNETCTL_IFLA_MAX, RTA_DATA(datainfo),
RTA_PAYLOAD(datainfo));
if (tb[RMNETCTL_IFLA_MUX_ID] && mux_id)
*mux_id = *((uint16_t *)RTA_DATA(tb[RMNETCTL_IFLA_MUX_ID]));
if (tb[RMNETCTL_IFLA_FLAGS] && flagconfig) {
struct ifla_vlan_flags *flags;
flags = (struct ifla_vlan_flags *)
RTA_DATA(tb[RMNETCTL_IFLA_FLAGS]);
*flagconfig = flags->flags;
}
if (tb[RMNETCTL_IFLA_UPLINK_PARAMS]) {
struct rmnetctl_uplink_params *ul_agg;
ul_agg = (struct rmnetctl_uplink_params *)
RTA_DATA(tb[RMNETCTL_IFLA_UPLINK_PARAMS]);
if (agg_size)
*agg_size = ul_agg->byte_count;
if (agg_count)
*agg_count = ul_agg->packet_count;
if (features)
*features = ul_agg->features;
if (agg_time)
*agg_time = ul_agg->time_limit;
}
free(resp);
return RMNETCTL_API_SUCCESS;
}
int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
uint16_t *error_code)
{
unsigned int devindex = 0, vndindex = 0;
struct nlmsg req;
size_t reqsize;
int rc;
if (!hndl || !vndname || !devname || !error_code || _rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
memset(&req, 0, sizeof(req));
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
rc = rta_put_u32(&req, &reqsize, IFLA_MASTER, vndindex);
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_set_uplink_aggregation_params(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint8_t packet_count,
uint16_t byte_count,
uint32_t time_limit,
uint8_t features,
uint16_t *error_code)
{
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
rc = rmnet_fill_ul_agg_msg(&req, &reqsize, devindex, vndname,
packet_count, byte_count, time_limit,
features, RMNETCTL_DEFAULT_UL_AGG_STATE);
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_set_ll_uplink_aggregation_params(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint8_t packet_count,
uint16_t byte_count,
uint32_t time_limit,
uint8_t features,
uint16_t *error_code)
{
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
rc = rmnet_fill_ul_agg_msg(&req, &reqsize, devindex, vndname,
packet_count, byte_count, time_limit,
features, RMNETCTL_LL_UL_AGG_STATE);
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_activate_flow(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint8_t bearer_id,
uint32_t flow_id,
int ip_type,
uint32_t tcm_handle,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize =0;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_handle = tcm_handle;
flowinfo.tcm_family = RMNET_FLOW_MSG_ACTIVATE;
flowinfo.tcm__pad1 = bearer_id;
flowinfo.tcm_ifindex = ip_type;
flowinfo.tcm_parent = flow_id;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_delete_flow(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint8_t bearer_id,
uint32_t flow_id,
int ip_type,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_family = RMNET_FLOW_MSG_DEACTIVATE;
flowinfo.tcm_ifindex = ip_type;
flowinfo.tcm__pad1 = bearer_id;
flowinfo.tcm_parent = flow_id;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo,sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_control_flow(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint8_t bearer_id,
uint16_t sequence,
uint32_t grantsize,
uint8_t ack,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_family = RMNET_FLOW_MSG_CONTROL;
flowinfo.tcm__pad1 = bearer_id;
flowinfo.tcm__pad2 = sequence;
flowinfo.tcm_parent = ack;
flowinfo.tcm_info = grantsize;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_flow_state_up(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint32_t instance,
uint32_t ep_type,
uint32_t ifaceid,
int flags,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_handle = instance;
flowinfo.tcm_family = RMNET_FLOW_MSG_UP;
flowinfo.tcm_ifindex = flags;
flowinfo.tcm_parent = ifaceid;
flowinfo.tcm_info = ep_type;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_flow_state_down(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint32_t instance,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_handle = instance;
flowinfo.tcm_family = RMNET_FLOW_MSG_DOWN;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_set_qmi_scale(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint32_t scale,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname) || !scale)
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_ifindex = scale;
flowinfo.tcm_family = RMNET_FLOW_MSG_QMI_SCALE;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_set_wda_freq(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint32_t freq,
uint16_t *error_code)
{
struct tcmsg flowinfo;
struct nlmsg req;
unsigned int devindex = 0;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname))
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
flowinfo.tcm_ifindex = freq;
flowinfo.tcm_family = RMNET_FLOW_MSG_WDA_FREQ;
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
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_change_bearer_channel(rmnetctl_hndl_t *hndl,
char *devname,
char *vndname,
uint8_t switch_type,
uint32_t flags,
uint8_t num_bearers,
uint8_t *bearers,
uint16_t *error_code)
{
struct nlmsg req;
struct {
struct tcmsg tcm;
uint8_t data[16]; /* Max number of bearers */
} flowinfo;
unsigned int devindex;
size_t reqsize;
int rc;
memset(&req, 0, sizeof(req));
memset(&flowinfo, 0, sizeof(flowinfo));
if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
_rmnetctl_check_dev_name(vndname) || num_bearers > 16 || !bearers)
return RMNETCTL_INVALID_ARG;
reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
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;
}
/* Create 2nd hndl to receive LLC status */
if ((flags & RMNETCTL_LL_MASK_ACK) && !hndl->llc_hndl) {
rc = rtrmnet_ctl_init(&hndl->llc_hndl, error_code);
if (rc != RMNETCTL_SUCCESS) {
hndl->llc_hndl = NULL;
return rc;
}
}
/* Fill the flow information */
flowinfo.tcm.tcm_family = RMNET_FLOW_MSG_CHANNEL_SWITCH;
flowinfo.tcm.tcm__pad1 = switch_type;
flowinfo.tcm.tcm__pad2 = num_bearers;
flowinfo.tcm.tcm_info = flags;
memscpy(flowinfo.data, 16, bearers, num_bearers);
/* DFC needs this to send the ACK back to LLC socket specifically */
if (hndl->llc_hndl) {
flowinfo.tcm.tcm_ifindex = hndl->llc_hndl->netlink_fd;
flowinfo.tcm.tcm_handle = hndl->llc_hndl->pid;
flowinfo.tcm.tcm_parent = hndl->transaction_id--;
}
rc = rmnet_fill_flow_msg(&req, &reqsize, devindex, vndname,
(char *)&flowinfo, sizeof(flowinfo));
if (rc != RMNETCTL_SUCCESS) {
*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
return rc;
}
/* Fire away */
if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
return RMNETCTL_LIB_ERR;
}
/* Initial ACK: The kernel got the message */
return rmnet_get_ack(hndl, error_code);
}
int rtrmnet_get_ll_ack(rmnetctl_hndl_t *hndl,
struct rmnetctl_ll_ack *ll_ack,
uint16_t *error_code)
{
struct nlack {
struct nlmsghdr ackheader;
struct nlmsgerr ackdata;
} ack;
int i;
if (!hndl || !hndl->llc_hndl || !ll_ack || !error_code)
return RMNETCTL_INVALID_ARG;
if ((i = recv(hndl->llc_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) {
ll_ack->bearer_id =
(uint8_t)ack.ackdata.msg.nlmsg_type;
ll_ack->status_code =
(uint8_t)ack.ackdata.msg.nlmsg_flags;
ll_ack->current_ch =
(uint8_t)ack.ackdata.msg.nlmsg_seq;
*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;
}
static const char *rmnetctl_ll_status_text[] = {
"Error",
"Success",
"Switched to Default",
"Switched to LL",
"Temporary Failure",
"Permanent Failure"
};
const char *rtrmnet_ll_status_to_text(uint8_t status)
{
if (status == LL_STATUS_TIMEOUT)
return "Time out";
if (status == LL_STATUS_NO_EFFECT)
return "No Effect";
if (status < sizeof(rmnetctl_ll_status_text) /
sizeof(rmnetctl_ll_status_text[0]))
return rmnetctl_ll_status_text[status];
return "Unknown";
}