From: Roopa Prabhu <roprabhu(a)cisco.com>
This patch adds the following:
- functions to set and get vf configs
- Functions to replace and store vf configs (Only mac address is handled today.
But the functions can be easily extended for vlans and other vf configs)
- function to dump link dev info (This is moved from virnetdevvportprofile.c)
Signed-off-by: Roopa Prabhu <roprabhu(a)cisco.com>
---
src/util/virnetdev.c | 535 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/util/virnetdev.h | 19 ++
2 files changed, 553 insertions(+), 1 deletions(-)
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 4aa7639..9f93fda 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -1127,8 +1127,47 @@ virNetDevGetPhysicalFunction(const char *ifname, char **pfname)
return ret;
}
-#else /* !__linux__ */
+/**
+ * virNetDevGetVirtualFunctionInfo:
+ * @vfname: name of the virtual function interface
+ * @pfname: name of the physical function
+ * @vf: vf index
+ *
+ * Returns 0 on success, -errno on failure.
+ *
+ */
+int
+virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname,
+ int *vf)
+{
+ char *pf_sysfs_path = NULL, *vf_sysfs_path = NULL;
+ int ret = -1;
+
+ *pfname = NULL;
+
+ if (virNetDevGetPhysicalFunction(vfname, pfname) < 0)
+ return ret;
+
+ if (virNetDevSysfsFile(&pf_sysfs_path, *pfname, "device") < 0)
+ goto cleanup;
+
+ if (virNetDevSysfsFile(&vf_sysfs_path, vfname, "device") < 0)
+ goto cleanup;
+
+ ret = pciGetVirtualFunctionIndex(pf_sysfs_path, vf_sysfs_path, vf);
+
+cleanup:
+ if (ret < 0)
+ VIR_FREE(*pfname);
+
+ VIR_FREE(vf_sysfs_path);
+ VIR_FREE(pf_sysfs_path);
+
+ return ret;
+}
+
+#else /* !__linux__ */
int
virNetDevGetVirtualFunctions(const char *pfname ATTRIBUTE_UNUSED,
char ***vfname ATTRIBUTE_UNUSED,
@@ -1165,4 +1204,498 @@ virNetDevGetPhysicalFunction(const char *ifname ATTRIBUTE_UNUSED,
_("Unable to get physical function status on this
platform"));
return -1;
}
+
#endif /* !__linux__ */
+#if defined(__linux__) && defined(HAVE_LIBNL)
+
+static struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
+ [IFLA_VF_MAC] = { .type = NLA_UNSPEC,
+ .maxlen = sizeof(struct ifla_vf_mac) },
+ [IFLA_VF_VLAN] = { .type = NLA_UNSPEC,
+ .maxlen = sizeof(struct ifla_vf_vlan) },
+};
+
+/**
+ * virNetDevLinkDump:
+ *
+ * @ifname: The name of the interface; only use if ifindex < 0
+ * @ifindex: The interface index; may be < 0 if ifname is given
+ * @nltarget_kernel: whether to send the message to the kernel or another
+ * process
+ * @nlattr: pointer to a pointer of netlink attributes that will contain
+ * the results
+ * @recvbuf: Pointer to the buffer holding the returned netlink response
+ * message; free it, once not needed anymore
+ * @getPidFunc: Pointer to a function that will be invoked if the kernel
+ * is not the target of the netlink message but it is to be
+ * sent to another process.
+ *
+ * Get information about an interface given its name or index.
+ *
+ * Returns 0 on success, -1 on fatal error.
+ */
+int
+virNetDevLinkDump(const char *ifname, int ifindex,
+ bool nltarget_kernel, struct nlattr **tb,
+ unsigned char **recvbuf,
+ uint32_t (*getPidFunc)(void))
+{
+ int rc = 0;
+ struct nlmsghdr *resp;
+ struct nlmsgerr *err;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ unsigned int recvbuflen;
+ uint32_t pid = 0;
+ struct nl_msg *nl_msg;
+
+ *recvbuf = NULL;
+
+ if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname,
&ifindex) < 0)
+ return -1;
+
+ ifinfo.ifi_index = ifindex;
+
+ nl_msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST);
+ if (!nl_msg) {
+ virReportOOMError();
+ return -1;
+ }
+
+ if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
+ goto buffer_too_small;
+
+ if (ifname) {
+ if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
+ goto buffer_too_small;
+ }
+
+ if (!nltarget_kernel) {
+ pid = getPidFunc();
+ if (pid == 0) {
+ rc = -1;
+ goto cleanup;
+ }
+ }
+
+ if (virNetlinkCommand(nl_msg, recvbuf, &recvbuflen, pid) < 0) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error dumping %s (%d) interface"),
+ ifname, ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ rc = nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, NULL);
+ if (rc < 0)
+ goto malformed_resp;
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+cleanup:
+ nlmsg_free(nl_msg);
+
+ return rc;
+
+malformed_resp:
+ nlmsg_free(nl_msg);
+
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ nlmsg_free(nl_msg);
+
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("allocated netlink buffer is too small"));
+ return -1;
+}
+
+static int
+virNetDevSetVfConfig(const char *ifname, int ifindex, int vf,
+ bool nltarget_kernel, const unsigned char *macaddr,
+ int vlanid, uint32_t (*getPidFunc)(void))
+{
+ int rc = -1;
+ struct nlmsghdr *resp;
+ struct nlmsgerr *err;
+ unsigned char *recvbuf = NULL;
+ unsigned int recvbuflen = 0;
+ uint32_t pid = 0;
+ struct nl_msg *nl_msg;
+ struct nlattr *vfinfolist, *vfinfo;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+
+ if (!macaddr && vlanid < 0)
+ return -1;
+
+ nl_msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST);
+ if (!nl_msg) {
+ virReportOOMError();
+ return rc;
+ }
+
+ if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
+ goto buffer_too_small;
+
+ if (ifname &&
+ nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
+ goto buffer_too_small;
+
+
+ if (!(vfinfolist = nla_nest_start(nl_msg, IFLA_VFINFO_LIST)))
+ goto buffer_too_small;
+
+ if (!(vfinfo = nla_nest_start(nl_msg, IFLA_VF_INFO)))
+ goto buffer_too_small;
+
+ if (macaddr) {
+ struct ifla_vf_mac ifla_vf_mac = {
+ .vf = vf,
+ .mac = { 0, },
+ };
+
+ memcpy(ifla_vf_mac.mac, macaddr, VIR_MAC_BUFLEN);
+
+ if (nla_put(nl_msg, IFLA_VF_MAC, sizeof(ifla_vf_mac),
+ &ifla_vf_mac) < 0)
+ goto buffer_too_small;
+ }
+
+ if (vlanid >= 0) {
+ struct ifla_vf_vlan ifla_vf_vlan = {
+ .vf = vf,
+ .vlan = vlanid,
+ .qos = 0,
+ };
+
+ if (nla_put(nl_msg, IFLA_VF_VLAN, sizeof(ifla_vf_vlan),
+ &ifla_vf_vlan) < 0)
+ goto buffer_too_small;
+ }
+
+ nla_nest_end(nl_msg, vfinfo);
+ nla_nest_end(nl_msg, vfinfolist);
+
+ if (!nltarget_kernel) {
+ pid = getPidFunc();
+ if (pid == 0) {
+ rc = -1;
+ goto cleanup;
+ }
+ }
+
+ if (virNetlinkCommand(nl_msg, &recvbuf, &recvbuflen, pid) < 0)
+ goto cleanup;
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ if (err->error) {
+ virReportSystemError(-err->error,
+ _("error during set %s of ifindex %d"),
+ (macaddr ? (vlanid >= 0 ? "mac/vlan" : "mac") :
"vlanid"),
+ ifindex);
+ goto cleanup;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ rc = 0;
+
+cleanup:
+ nlmsg_free(nl_msg);
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ nlmsg_free(nl_msg);
+
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return rc;
+
+buffer_too_small:
+ nlmsg_free(nl_msg);
+
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("allocated netlink buffer is too small"));
+ return rc;
+}
+
+static int
+virNetDevParseVfConfig(struct nlattr **tb, int32_t vf, unsigned char *mac,
+ int *vlanid)
+{
+ const char *msg = NULL;
+ int rc = -1;
+
+ if (tb[IFLA_VFINFO_LIST]) {
+ struct ifla_vf_mac *vf_mac;
+ struct ifla_vf_vlan *vf_vlan;
+ struct nlattr *tb_vf_info = {NULL, };
+ struct nlattr *tb_vf[IFLA_VF_MAX+1];
+ int found = 0;
+ int rem;
+
+ nla_for_each_nested(tb_vf_info, tb[IFLA_VFINFO_LIST], rem) {
+ if (nla_type(tb_vf_info) != IFLA_VF_INFO)
+ continue;
+
+ if (nla_parse_nested(tb_vf, IFLA_VF_MAX, tb_vf_info,
+ ifla_vf_policy)) {
+ msg = _("error parsing IFLA_VF_INFO");
+ goto cleanup;
+ }
+
+ if (tb[IFLA_VF_MAC]) {
+ vf_mac = RTA_DATA(tb_vf[IFLA_VF_MAC]);
+ if (vf_mac && vf_mac->vf == vf) {
+ memcpy(mac, vf_mac->mac, VIR_MAC_BUFLEN);
+ found = 1;
+ }
+ }
+
+ if (tb[IFLA_VF_VLAN]) {
+ vf_vlan = RTA_DATA(tb_vf[IFLA_VF_VLAN]);
+ if (vf_vlan && vf_vlan->vf == vf) {
+ *vlanid = vf_vlan->vlan;
+ found = 1;
+ }
+ }
+ if (found) {
+ rc = 0;
+ break;
+ }
+ }
+ }
+
+cleanup:
+ if (msg)
+ virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ return rc;
+}
+
+static int
+virNetDevGetVfConfig(const char *ifname, int vf, unsigned char *mac,
+ int *vlanid)
+{
+ int rc = -1;
+ unsigned char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1] = {NULL, };
+ int ifindex = -1;
+
+ rc = virNetDevLinkDump(ifname, ifindex, true, tb, &recvbuf, NULL);
+ if (rc < 0)
+ return rc;
+
+ rc = virNetDevParseVfConfig(tb, vf, mac, vlanid);
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+static int
+virNetDevReplaceVfConfig(const char *pflinkdev, int vf,
+ const unsigned char *macaddress,
+ int vlanid,
+ const char *stateDir)
+{
+ unsigned char oldmac[6];
+ int oldvlanid = -1;
+ char *path = NULL;
+ char macstr[VIR_MAC_STRING_BUFLEN];
+ int ifindex = -1;
+
+ if (virNetDevGetVfConfig(pflinkdev, vf, oldmac, &oldvlanid) < 0)
+ return -1;
+
+ if (virAsprintf(&path, "%s/%s_vf%d",
+ stateDir, pflinkdev, vf) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ virMacAddrFormat(oldmac, macstr);
+ if (virFileWriteStr(path, macstr, O_CREAT|O_TRUNC|O_WRONLY) < 0) {
+ virReportSystemError(errno, _("Unable to preserve mac for pf = %s,"
+ " vf = %d"), pflinkdev, vf);
+ VIR_FREE(path);
+ return -1;
+ }
+
+ VIR_FREE(path);
+
+ return virNetDevSetVfConfig(pflinkdev, ifindex, vf, true,
+ macaddress, vlanid, NULL);
+}
+
+static int
+virNetDevRestoreVfConfig(const char *pflinkdev, int vf,
+ const char *stateDir)
+{
+ int rc = -1;
+ char *macstr = NULL;
+ char *path = NULL;
+ unsigned char oldmac[6];
+ int vlanid = -1;
+ int ifindex = -1;
+
+ if (virAsprintf(&path, "%s/%s_vf%d",
+ stateDir, pflinkdev, vf) < 0) {
+ virReportOOMError();
+ return rc;
+ }
+
+ if (virFileReadAll(path, VIR_MAC_STRING_BUFLEN, &macstr) < 0) {
+ goto cleanup;
+ }
+
+ if (virMacAddrParse(macstr, &oldmac[0]) != 0) {
+ virNetDevError(VIR_ERR_INTERNAL_ERROR,
+ _("Cannot parse MAC address from '%s'"),
+ macstr);
+ goto cleanup;
+ }
+
+ /*reset mac and remove file-ignore results*/
+ rc = virNetDevSetVfConfig(pflinkdev, ifindex, vf, true,
+ oldmac, vlanid, NULL);
+ ignore_value(unlink(path));
+
+cleanup:
+ VIR_FREE(path);
+ VIR_FREE(macstr);
+
+ return rc;
+}
+
+/**
+ * virNetDevReplaceNetConfig:
+ * @linkdev: name of the interface
+ * @vf: vf index if linkdev is a pf
+ * @macaddress: new MAC address for interface
+ * @vlanid: new vlanid
+ * @stateDir: directory to store old net config
+ *
+ * Returns 0 on success, -1 on failure
+ *
+ */
+int
+virNetDevReplaceNetConfig(char *linkdev, int vf,
+ const unsigned char *macaddress, int vlanid,
+ char *stateDir)
+{
+ if (vf == -1)
+ return virNetDevReplaceMacAddress(linkdev, macaddress, stateDir);
+ else
+ return virNetDevReplaceVfConfig(linkdev, vf, macaddress, vlanid,
+ stateDir);
+}
+
+/**
+ * virNetDevRestoreNetConfig:
+ * @linkdev: name of the interface
+ * @vf: vf index if linkdev is a pf
+ * @stateDir: directory containing old net config
+ *
+ * Returns 0 on success, -errno on failure.
+ *
+ */
+int
+virNetDevRestoreNetConfig(char *linkdev, int vf, char *stateDir)
+{
+ if (vf == -1)
+ return virNetDevRestoreMacAddress(linkdev, stateDir);
+ else
+ return virNetDevRestoreVfConfig(linkdev, vf, stateDir);
+}
+
+#else /* defined(__linux__) && defined(HAVE_LIBNL) */
+virNetDevLinkDump(const char *ifname ATTRIBUTE_UNUSED,
+ int ifindex ATTRIBUTE_UNUSED,
+ bool nltarget_kernel ATTRIBUTE_UNUSED,
+ struct nlattr **tb ATTRIBUTE_UNUSED,
+ unsigned char **recvbuf ATTRIBUTE_UNUSED,
+ uint32_t (*getPidFunc)(void) ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Unable to dump link info on this platform"));
+ return -1;
+}
+
+int
+virNetDevReplaceNetConfig(char *linkdev ATTRIBUTE_UNUSED,
+ int vf ATTRIBUTE_UNUSED,
+ const unsigned char *macaddress ATTRIBUTE_UNUSED,
+ int vlanid ATTRIBUTE_UNUSED,
+ char *stateDir ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Unable to replace net config on this platform"));
+ return -1;
+
+}
+
+int
+virNetDevRestoreNetConfig(char *linkdev ATTRIBUTE_UNUSED,
+ int vf ATTRIBUTE_UNUSED,
+ char *stateDir ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Unable to restore net config on this platform"));
+ return -1;
+}
+
+#endif /* defined(__linux__) && defined(HAVE_LIBNL) */
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 3456113..73f4c64 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -24,6 +24,7 @@
# define __VIR_NETDEV_H__
# include "virsocketaddr.h"
+# include "virnetlink.h"
int virNetDevExists(const char *brname)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
@@ -105,4 +106,22 @@ int virNetDevGetVirtualFunctions(const char *pfname,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_RETURN_CHECK;
+int virNetDevLinkDump(const char *ifname, int ifindex,
+ bool nltarget_kernel, struct nlattr **tb,
+ unsigned char **recvbuf,
+ uint32_t (*getPidFunc)(void))
+ ATTRIBUTE_RETURN_CHECK;
+
+int virNetDevReplaceNetConfig(char *linkdev, int vf,
+ const unsigned char *macaddress, int vlanid,
+ char *stateDir)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(5);
+
+int virNetDevRestoreNetConfig(char *linkdev, int vf, char *stateDir)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+
+int virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname,
+ int *vf)
+ ATTRIBUTE_NONNULL(1);
+
#endif /* __VIR_NETDEV_H__ */