From 1a2abef95b2915ff316e7720236c5c3ee513fb03 Mon Sep 17 00:00:00 2001 From: Sean Tranchetti Date: Thu, 3 Jan 2019 14:33:16 -0700 Subject: [PATCH] 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 --- rmnetctl/cli/rmnetcli.c | 16 ++++- rmnetctl/inc/librmnetctl.h | 17 ++++++ rmnetctl/src/librmnetctl.c | 116 +++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) diff --git a/rmnetctl/cli/rmnetcli.c b/rmnetctl/cli/rmnetcli.c index a40be0a..1b1c938 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-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 modification, are permitted provided that the following conditions are @@ -218,6 +218,7 @@ static void rmnet_api_usage(void) printf(_2TABS" string - vnd device_name"); printf(_2TABS" int - new vnd id"); printf(_2TABS" int - new flag config\n\n"); + printf("rmnetcli -n getlink Get device config\n\n"); printf("rmnetcli -n dellink Delete a vnd"); printf(_2TABS" by inputting dev name\n\n"); printf("rmnetcli -n bridgelink Bridge a vnd and a dev"); @@ -359,6 +360,19 @@ static int rmnet_api_call(int argc, char *argv[]) &error_number, _STRTOI32(argv[3]), _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")) { _RMNETCLI_CHECKNULL(argv[1]); return_code = rtrmnet_ctl_delvnd(handle, argv[1], diff --git a/rmnetctl/inc/librmnetctl.h b/rmnetctl/inc/librmnetctl.h index ef85c2b..4651fda 100644 --- a/rmnetctl/inc/librmnetctl.h +++ b/rmnetctl/inc/librmnetctl.h @@ -591,6 +591,23 @@ 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 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 * @details Message type is RTM_NEWLINK * @param hndl RmNet handle for the Netlink message diff --git a/rmnetctl/src/librmnetctl.c b/rmnetctl/src/librmnetctl.c index bb86027..abfa704 100644 --- a/rmnetctl/src/librmnetctl.c +++ b/rmnetctl/src/librmnetctl.c @@ -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; } +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 @@ -1465,6 +1490,97 @@ int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname, 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, uint16_t *error_code) {