rmnetctl: add getlink library API and CLI option
Add support for the RTM_GETLINK message in order to retrieve the configuration of an RmNet device after it has been created. Change-Id: I7de75c461ab1201a0856911348acef939d3ef172 Signed-off-by: Sean Tranchetti <stranche@codeaurora.org>
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
R M N E T C L I . C
|
R M N E T C L I . C
|
||||||
|
|
||||||
Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. All rights reserved.
|
Copyright (c) 2013-2015, 2017-2019 The Linux Foundation. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
@@ -218,6 +218,7 @@ static void rmnet_api_usage(void)
|
|||||||
printf(_2TABS" <vnd> string - vnd device_name");
|
printf(_2TABS" <vnd> string - vnd device_name");
|
||||||
printf(_2TABS" <vnd id> int - new vnd id");
|
printf(_2TABS" <vnd id> int - new vnd id");
|
||||||
printf(_2TABS" <flags> int - new flag config\n\n");
|
printf(_2TABS" <flags> int - new flag config\n\n");
|
||||||
|
printf("rmnetcli -n getlink <dev_name> Get device config\n\n");
|
||||||
printf("rmnetcli -n dellink <dev_name> Delete a vnd");
|
printf("rmnetcli -n dellink <dev_name> Delete a vnd");
|
||||||
printf(_2TABS" by inputting dev name\n\n");
|
printf(_2TABS" by inputting dev name\n\n");
|
||||||
printf("rmnetcli -n bridgelink <dev_name> Bridge a vnd and a dev");
|
printf("rmnetcli -n bridgelink <dev_name> Bridge a vnd and a dev");
|
||||||
@@ -359,6 +360,19 @@ static int rmnet_api_call(int argc, char *argv[])
|
|||||||
&error_number,
|
&error_number,
|
||||||
_STRTOI32(argv[3]),
|
_STRTOI32(argv[3]),
|
||||||
_STRTOI32(argv[4]));
|
_STRTOI32(argv[4]));
|
||||||
|
} else if (!strcmp(*argv, "getlink")) {
|
||||||
|
_RMNETCLI_CHECKNULL(argv[1]);
|
||||||
|
uint32_t flags = 0;
|
||||||
|
uint16_t mux_id = 0;
|
||||||
|
|
||||||
|
return_code = rtrmnet_ctl_getvnd(handle, argv[1],
|
||||||
|
&error_number,
|
||||||
|
&mux_id, &flags);
|
||||||
|
if (return_code == RMNETCTL_API_SUCCESS) {
|
||||||
|
printf("Configuration for device %s:\n", argv[1]);
|
||||||
|
printf("\tMux id: %d\n", mux_id);
|
||||||
|
printf("\tData format: 0x%04x\n", flags);
|
||||||
|
}
|
||||||
} else if (!strcmp(*argv, "dellink")) {
|
} else if (!strcmp(*argv, "dellink")) {
|
||||||
_RMNETCLI_CHECKNULL(argv[1]);
|
_RMNETCLI_CHECKNULL(argv[1]);
|
||||||
return_code = rtrmnet_ctl_delvnd(handle, argv[1],
|
return_code = rtrmnet_ctl_delvnd(handle, argv[1],
|
||||||
|
|||||||
@@ -591,6 +591,23 @@ int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
|
|||||||
uint16_t *error_code, uint8_t index,
|
uint16_t *error_code, uint8_t index,
|
||||||
uint32_t flagconfig);
|
uint32_t flagconfig);
|
||||||
|
|
||||||
|
/* @brief Public API to retrieve configuration of a virtual device node
|
||||||
|
* @details Message type is RTM_GETLINK
|
||||||
|
* @param hndl RmNet handle for the Netlink message
|
||||||
|
* @param vndname Name of virtual device to query
|
||||||
|
* @param error_code Status code of this operation returned from the kernel
|
||||||
|
* @param mux_id Where to store the value of the node's mux id
|
||||||
|
* @param flagconfig Where to store the value of the node's data format flags
|
||||||
|
* @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_ARF if invalid arguments were passed to the API
|
||||||
|
*/
|
||||||
|
int rtrmnet_ctl_getvnd(rmnetctl_hndl_t *hndl, char *vndname,
|
||||||
|
uint16_t *error_code, uint16_t *mux_id,
|
||||||
|
uint32_t *flagconfig);
|
||||||
|
|
||||||
/* @brief Public API to bridge a vnd and device
|
/* @brief Public API to bridge a vnd and device
|
||||||
* @details Message type is RTM_NEWLINK
|
* @details Message type is RTM_NEWLINK
|
||||||
* @param hndl RmNet handle for the Netlink message
|
* @param hndl RmNet handle for the Netlink message
|
||||||
|
|||||||
@@ -1110,6 +1110,31 @@ static void rta_nested_end(struct nlmsg *req, struct rtattr *start)
|
|||||||
start->rta_len = (char *)NLMSG_TAIL(&req->nl_addr) - (char *)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
|
/* @brief Fill a Netlink messages with the necessary common RTAs for creating a
|
||||||
* RTM_NEWLINK message for creating or changing rmnet devices.
|
* RTM_NEWLINK message for creating or changing rmnet devices.
|
||||||
* @param *req The netlink message
|
* @param *req The netlink message
|
||||||
@@ -1465,6 +1490,97 @@ int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
|
|||||||
return rmnet_get_ack(hndl, error_code);
|
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)
|
||||||
|
{
|
||||||
|
struct nlmsg req;
|
||||||
|
struct nlmsghdr *resp;
|
||||||
|
struct rtattr *attrs, *linkinfo, *datainfo;
|
||||||
|
struct rtattr *tb[IFLA_VLAN_MAX + 1];
|
||||||
|
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, IFLA_VLAN_MAX + 1, RTA_DATA(datainfo),
|
||||||
|
RTA_PAYLOAD(datainfo));
|
||||||
|
if (tb[IFLA_VLAN_ID] && mux_id)
|
||||||
|
*mux_id = *((uint16_t *)RTA_DATA(tb[IFLA_VLAN_ID]));
|
||||||
|
if (tb[IFLA_VLAN_FLAGS] && flagconfig) {
|
||||||
|
struct ifla_vlan_flags *flags;
|
||||||
|
|
||||||
|
flags = (struct ifla_vlan_flags *)
|
||||||
|
RTA_DATA(tb[IFLA_VLAN_FLAGS]);
|
||||||
|
*flagconfig = flags->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resp);
|
||||||
|
return RMNETCTL_API_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
|
int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
|
||||||
uint16_t *error_code)
|
uint16_t *error_code)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user