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:
Sean Tranchetti
2019-01-03 14:33:16 -07:00
parent 4fb7294ca2
commit 1a2abef95b
3 changed files with 148 additions and 1 deletions

View File

@@ -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],

View File

@@ -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

View File

@@ -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)
{ {