[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 2/4 v3] virtnetdev: Add support functions for mac and portprofile associations on a hostdev



From: Roopa Prabhu <roprabhu 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 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__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]