[PATCH v2 5/5] ch: Enable ETHERNET Network mode support

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

 



enable VIR_DOMAIN_NET_TYPE_ETHERNET network support for ch guests.

Tested with following interface config:

    <interface type='ethernet'>
      <target dev='chtap0' managed="yes"/>
      <model type='virtio'/>
      <driver queues='2'/>
    <interface>

Signed-off-by: Praveen K Paladugu <prapal@xxxxxxxxxxxxxxxxxxx>
---
 po/POTFILES           |   1 +
 src/ch/ch_conf.h      |   4 +
 src/ch/ch_domain.c    |  41 ++++++++
 src/ch/ch_domain.h    |   3 +
 src/ch/ch_interface.c | 100 ++++++++++++++++++++
 src/ch/ch_interface.h |  35 +++++++
 src/ch/ch_monitor.c   | 213 ++++++++++++++++--------------------------
 src/ch/ch_monitor.h   |   7 +-
 src/ch/ch_process.c   | 166 +++++++++++++++++++++++++++++++-
 src/ch/meson.build    |   2 +
 10 files changed, 430 insertions(+), 142 deletions(-)
 create mode 100644 src/ch/ch_interface.c
 create mode 100644 src/ch/ch_interface.h

diff --git a/po/POTFILES b/po/POTFILES
index b594a8dd39..e48b9023e2 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -21,6 +21,7 @@ src/bhyve/bhyve_process.c
 src/ch/ch_conf.c
 src/ch/ch_domain.c
 src/ch/ch_driver.c
+src/ch/ch_interface.c
 src/ch/ch_monitor.c
 src/ch/ch_process.c
 src/conf/backup_conf.c
diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h
index 5b9b42540d..579eca894e 100644
--- a/src/ch/ch_conf.h
+++ b/src/ch/ch_conf.h
@@ -23,6 +23,7 @@
 #include "virdomainobjlist.h"
 #include "virthread.h"
 #include "ch_capabilities.h"
+#include "virebtables.h"
 
 #define CH_DRIVER_NAME "CH"
 #define CH_CMD "cloud-hypervisor"
@@ -75,6 +76,9 @@ struct _virCHDriver
 
     /* pid file FD, ensures two copies of the driver can't use the same root */
     int lockFD;
+
+    /* Immutable pointer, lockless APIs. Pointless abstraction */
+    ebtablesContext *ebtables;
 };
 
 virCaps *virCHDriverCapsInit(void);
diff --git a/src/ch/ch_domain.c b/src/ch/ch_domain.c
index c0d6c75b1d..25610a9459 100644
--- a/src/ch/ch_domain.c
+++ b/src/ch/ch_domain.c
@@ -22,6 +22,7 @@
 
 #include "ch_domain.h"
 #include "domain_driver.h"
+#include "domain_validate.h"
 #include "virchrdev.h"
 #include "virlog.h"
 #include "virtime.h"
@@ -355,3 +356,43 @@ virCHDomainObjFromDomain(virDomainPtr domain)
 
     return vm;
 }
+
+int
+virCHDomainValidateActualNetDef(virDomainNetDef *net)
+{
+    virDomainNetType actualType = virDomainNetGetActualType(net);
+
+    /* hypervisor-agnostic validation */
+    if (virDomainActualNetDefValidate(net) < 0)
+        return -1;
+
+    /* CH specific validation */
+    switch (actualType) {
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+        if (net->guestIP.nips > 1) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("ethernet type supports a single guest ip"));
+            return -1;
+        }
+        break;
+    case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+    case VIR_DOMAIN_NET_TYPE_DIRECT:
+    case VIR_DOMAIN_NET_TYPE_USER:
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+    case VIR_DOMAIN_NET_TYPE_INTERNAL:
+    case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+    case VIR_DOMAIN_NET_TYPE_UDP:
+    case VIR_DOMAIN_NET_TYPE_VDPA:
+    case VIR_DOMAIN_NET_TYPE_NULL:
+    case VIR_DOMAIN_NET_TYPE_VDS:
+    case VIR_DOMAIN_NET_TYPE_LAST:
+    default:
+        break;
+    }
+
+    return 0;
+}
diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h
index 4990914e9f..8dea2b2123 100644
--- a/src/ch/ch_domain.h
+++ b/src/ch/ch_domain.h
@@ -75,3 +75,6 @@ virCHDomainGetMachineName(virDomainObj *vm);
 
 virDomainObj *
 virCHDomainObjFromDomain(virDomainPtr domain);
+
+int
+virCHDomainValidateActualNetDef(virDomainNetDef *net);
diff --git a/src/ch/ch_interface.c b/src/ch/ch_interface.c
new file mode 100644
index 0000000000..ba90103c4e
--- /dev/null
+++ b/src/ch/ch_interface.c
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright Microsoft Corp. 2023
+ *
+ * ch_interface.c: methods to connect guest interfaces to appropriate host
+ * backends
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "domain_conf.h"
+#include "domain_interface.h"
+#include "virebtables.h"
+#include "viralloc.h"
+#include "ch_interface.h"
+#include "virjson.h"
+#include "virlog.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_CH
+
+VIR_LOG_INIT("ch.ch_interface");
+
+/**
+ * virCHConnetNetworkInterfaces:
+ * @driver: pointer to ch driver object
+ * @vm: pointer to domain definition
+ * @net: pointer to a guest net
+ * @nicindexes: returned array of FDs of guest interfaces
+ * @nnicindexes: returned number of guest interfaces
+ *
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+virCHConnetNetworkInterfaces(virCHDriver *driver,
+                             virDomainDef *vm,
+                             virDomainNetDef *net,
+                             int *tapfds, int **nicindexes, size_t *nnicindexes)
+{
+    virDomainNetType actualType = virDomainNetGetActualType(net);
+
+
+    switch (actualType) {
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+
+        if (virDomainInterfaceEthernetConnect(vm, net,
+                                              driver->ebtables, false,
+                                              driver->privileged, tapfds,
+                                              net->driver.virtio.queues) < 0)
+            return -1;
+
+        G_GNUC_FALLTHROUGH;
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+    case VIR_DOMAIN_NET_TYPE_DIRECT:
+        if (nicindexes && nnicindexes && net->ifname) {
+            int nicindex = 0;
+
+            if (virNetDevGetIndex(net->ifname, &nicindex) < 0)
+                return -1;
+
+            VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex);
+        }
+
+        break;
+    case VIR_DOMAIN_NET_TYPE_USER:
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+    case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+    case VIR_DOMAIN_NET_TYPE_INTERNAL:
+    case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+    case VIR_DOMAIN_NET_TYPE_UDP:
+    case VIR_DOMAIN_NET_TYPE_VDPA:
+    case VIR_DOMAIN_NET_TYPE_NULL:
+    case VIR_DOMAIN_NET_TYPE_VDS:
+    case VIR_DOMAIN_NET_TYPE_LAST:
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Unsupported Network type %1$d"), actualType);
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/src/ch/ch_interface.h b/src/ch/ch_interface.h
new file mode 100644
index 0000000000..df87d4511f
--- /dev/null
+++ b/src/ch/ch_interface.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright Microsoft Corp. 2023
+ *
+ * ch_interface.c: methods to connect guest interfaces to appropriate host
+ * backends
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+
+#include "ch_conf.h"
+#include "virconftypes.h"
+
+
+int
+virCHConnetNetworkInterfaces(virCHDriver *driver,
+                             virDomainDef *vmdef,
+                             virDomainNetDef *netdef,
+                             int *tapfds,
+                             int **nicindexes,
+                             size_t *nnicindexes);
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
index 6f960c3a51..12f2468cc7 100644
--- a/src/ch/ch_monitor.c
+++ b/src/ch/ch_monitor.c
@@ -24,7 +24,11 @@
 #include <unistd.h>
 #include <curl/curl.h>
 
+#include "datatypes.h"
+#include "ch_conf.h"
+#include "ch_interface.h"
 #include "ch_monitor.h"
+#include "domain_interface.h"
 #include "viralloc.h"
 #include "vircommand.h"
 #include "virerror.h"
@@ -258,148 +262,98 @@ virCHMonitorBuildDisksJson(virJSONValue *content, virDomainDef *vmdef)
     return 0;
 }
 
-static int
-virCHMonitorBuildNetJson(virJSONValue *nets,
-                         virDomainNetDef *netdef,
-                         size_t *nnicindexes,
-                         int **nicindexes)
+/**
+ * virCHMonitorBuildNetJson:
+ * @net: pointer to a guest network definition
+ * @jsonstr: returned network json
+ *
+ * Build net json to send to CH
+ * Returns 0 on success or -1 in case of error
+ */
+int
+virCHMonitorBuildNetJson(virDomainNetDef *net, char **jsonstr)
 {
-    virDomainNetType netType = virDomainNetGetActualType(netdef);
     char macaddr[VIR_MAC_STRING_BUFLEN];
-    g_autoptr(virJSONValue) net = NULL;
-
-    // check net type at first
-    net = virJSONValueNewObject();
-
-    switch (netType) {
-        case VIR_DOMAIN_NET_TYPE_ETHERNET:
-            if (netdef->guestIP.nips == 1) {
-                const virNetDevIPAddr *ip = netdef->guestIP.ips[0];
-                g_autofree char *addr = NULL;
-                virSocketAddr netmask;
-                g_autofree char *netmaskStr = NULL;
-
-                if (!(addr = virSocketAddrFormat(&ip->address)))
-                    return -1;
-                if (virJSONValueObjectAppendString(net, "ip", addr) < 0)
-                    return -1;
-
-                if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
-                    virReportError(VIR_ERR_INTERNAL_ERROR,
-                                   _("Failed to translate net prefix %1$d to netmask"),
-                                   ip->prefix);
-                    return -1;
-                }
-                if (!(netmaskStr = virSocketAddrFormat(&netmask)))
-                    return -1;
-                if (virJSONValueObjectAppendString(net, "mask", netmaskStr) < 0)
-                    return -1;
-            } else if (netdef->guestIP.nips > 1) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("ethernet type supports a single guest ip"));
-            }
+    g_autoptr(virJSONValue) net_json = virJSONValueNewObject();
+    virDomainNetType actualType = virDomainNetGetActualType(net);
 
-            /* network and bridge use a tap device, and direct uses a
-             * macvtap device
-             */
-            if (nicindexes && nnicindexes && netdef->ifname) {
-                int nicindex = 0;
 
-                if (virNetDevGetIndex(netdef->ifname, &nicindex) < 0)
-                    return -1;
 
-                VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex);
-            }
-            break;
-        case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
-            if ((virDomainChrType)netdef->data.vhostuser->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("vhost_user type support UNIX socket in this CH"));
-                return -1;
-            } else {
-                if (virJSONValueObjectAppendString(net, "vhost_socket", netdef->data.vhostuser->data.nix.path) < 0)
-                    return -1;
-                if (virJSONValueObjectAppendBoolean(net, "vhost_user", true) < 0)
-                    return -1;
-            }
-            break;
-        case VIR_DOMAIN_NET_TYPE_BRIDGE:
-        case VIR_DOMAIN_NET_TYPE_NETWORK:
-        case VIR_DOMAIN_NET_TYPE_DIRECT:
-        case VIR_DOMAIN_NET_TYPE_USER:
-        case VIR_DOMAIN_NET_TYPE_SERVER:
-        case VIR_DOMAIN_NET_TYPE_CLIENT:
-        case VIR_DOMAIN_NET_TYPE_MCAST:
-        case VIR_DOMAIN_NET_TYPE_INTERNAL:
-        case VIR_DOMAIN_NET_TYPE_HOSTDEV:
-        case VIR_DOMAIN_NET_TYPE_UDP:
-        case VIR_DOMAIN_NET_TYPE_VDPA:
-        case VIR_DOMAIN_NET_TYPE_NULL:
-        case VIR_DOMAIN_NET_TYPE_VDS:
-        case VIR_DOMAIN_NET_TYPE_LAST:
-        default:
-            virReportEnumRangeError(virDomainNetType, netType);
-            return -1;
-    }
-
-    if (netdef->ifname != NULL) {
-        if (virJSONValueObjectAppendString(net, "tap", netdef->ifname) < 0)
-            return -1;
-    }
-    if (virJSONValueObjectAppendString(net, "mac", virMacAddrFormat(&netdef->mac, macaddr)) < 0)
-        return -1;
+    if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET) {
+        const virNetDevIPAddr *ip;
+        g_autofree char *addr = NULL;
+        virSocketAddr netmask;
+        g_autofree char *netmaskStr = NULL;
+
+        /* nips should be 1, this is already validated in
+        virCHDomainValidateActualNetDef */
+        if (net->guestIP.nips != 1)
+            return -1;
 
+        ip = net->guestIP.ips[0];
 
-    if (netdef->virtio != NULL) {
-        if (netdef->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
-            if (virJSONValueObjectAppendBoolean(net, "iommu", true) < 0)
-                return -1;
-        }
-    }
-    if (netdef->driver.virtio.queues) {
-        if (virJSONValueObjectAppendNumberInt(net, "num_queues", netdef->driver.virtio.queues) < 0)
+
+        if (!(addr = virSocketAddrFormat(&ip->address)))
             return -1;
-    }
 
-    if (netdef->driver.virtio.rx_queue_size || netdef->driver.virtio.tx_queue_size) {
-        if (netdef->driver.virtio.rx_queue_size != netdef->driver.virtio.tx_queue_size) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-               _("virtio rx_queue_size option %1$d is not same with tx_queue_size %2$d"),
-               netdef->driver.virtio.rx_queue_size,
-               netdef->driver.virtio.tx_queue_size);
+        if (virJSONValueObjectAppendString(net_json, "ip", addr) < 0)
+            return -1;
+
+        if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to translate net prefix %1$d to netmask"),
+                           ip->prefix);
             return -1;
         }
-        if (virJSONValueObjectAppendNumberInt(net, "queue_size", netdef->driver.virtio.rx_queue_size) < 0)
+
+        if (!(netmaskStr = virSocketAddrFormat(&netmask)))
+            return -1;
+        if (virJSONValueObjectAppendString(net_json, "mask", netmaskStr) < 0)
             return -1;
     }
 
-    if (virJSONValueArrayAppend(nets, &net) < 0)
+    if (virJSONValueObjectAppendString(net_json, "mac",
+                                       virMacAddrFormat(&net->mac, macaddr)) < 0)
         return -1;
 
-    return 0;
-}
-
-static int
-virCHMonitorBuildNetsJson(virJSONValue *content,
-                          virDomainDef *vmdef,
-                          size_t *nnicindexes,
-                          int **nicindexes)
-{
-    g_autoptr(virJSONValue) nets = NULL;
-    size_t i;
+    if (net->virtio != NULL) {
+        if (net->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
+            if (virJSONValueObjectAppendBoolean(net_json, "iommu", true) < 0)
+                return -1;
+        }
+    }
 
-    if (vmdef->nnets > 0) {
-        nets = virJSONValueNewArray();
+    /* Cloud-Hypervisor expects number of queues. 1 for rx and 1 for tx.
+     * Multiply queue pairs by 2 to provide total number of queues to CH
+     */
+    if (net->driver.virtio.queues) {
+        if (virJSONValueObjectAppendNumberInt(net_json, "num_queues",
+                                              2 * net->driver.virtio.queues) < 0)
+            return -1;
+    }
 
-        for (i = 0; i < vmdef->nnets; i++) {
-            if (virCHMonitorBuildNetJson(nets, vmdef->nets[i],
-                                         nnicindexes, nicindexes) < 0)
-                return -1;
+    if (net->driver.virtio.rx_queue_size || net->driver.virtio.tx_queue_size) {
+        if (net->driver.virtio.rx_queue_size !=
+                                            net->driver.virtio.tx_queue_size) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("virtio rx_queue_size option %1$d is not same with tx_queue_size %2$d"),
+                           net->driver.virtio.rx_queue_size,
+                           net->driver.virtio.tx_queue_size);
+            return -1;
         }
-        if (virJSONValueObjectAppend(content, "net", &nets) < 0)
+        if (virJSONValueObjectAppendNumberInt(net_json, "queue_size",
+                                              net->driver.virtio.rx_queue_size) < 0)
+            return -1;
+    }
+
+    if (net->mtu) {
+        if (virJSONValueObjectAppendNumberInt(net_json, "mtu", net->mtu) < 0)
             return -1;
     }
 
+    if (!(*jsonstr = virJSONValueToString(net_json, false)))
+        return -1;
+
     return 0;
 }
 
@@ -456,11 +410,8 @@ virCHMonitorBuildDevicesJson(virJSONValue *content,
 }
 
 static int
-virCHMonitorBuildVMJson(virCHDriver *driver,
-                        virDomainDef *vmdef,
-                        char **jsonstr,
-                        size_t *nnicindexes,
-                        int **nicindexes)
+virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef,
+                        char **jsonstr)
 {
     g_autoptr(virJSONValue) content = virJSONValueNewObject();
 
@@ -490,10 +441,6 @@ virCHMonitorBuildVMJson(virCHDriver *driver,
     if (virCHMonitorBuildDisksJson(content, vmdef) < 0)
         return -1;
 
-
-    if (virCHMonitorBuildNetsJson(content, vmdef, nnicindexes, nicindexes) < 0)
-        return -1;
-
     if (virCHMonitorBuildDevicesJson(content, vmdef) < 0)
         return -1;
 
@@ -877,10 +824,7 @@ virCHMonitorShutdownVMM(virCHMonitor *mon)
 }
 
 int
-virCHMonitorCreateVM(virCHDriver *driver,
-                     virCHMonitor *mon,
-                     size_t *nnicindexes,
-                     int **nicindexes)
+virCHMonitorCreateVM(virCHDriver *driver, virCHMonitor *mon)
 {
     g_autofree char *url = NULL;
     int responseCode = 0;
@@ -892,8 +836,7 @@ virCHMonitorCreateVM(virCHDriver *driver,
     headers = curl_slist_append(headers, "Accept: application/json");
     headers = curl_slist_append(headers, "Content-Type: application/json");
 
-    if (virCHMonitorBuildVMJson(driver, mon->vm->def, &payload,
-                                nnicindexes, nicindexes) != 0)
+    if (virCHMonitorBuildVMJson(driver, mon->vm->def, &payload) != 0)
         return -1;
 
     VIR_WITH_OBJECT_LOCK_GUARD(mon) {
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
index bbfa77cdff..47b4e7abbd 100644
--- a/src/ch/ch_monitor.h
+++ b/src/ch/ch_monitor.h
@@ -104,10 +104,7 @@ void virCHMonitorClose(virCHMonitor *mon);
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCHMonitor, virCHMonitorClose);
 
 
-int virCHMonitorCreateVM(virCHDriver *driver,
-                         virCHMonitor *mon,
-                         size_t *nnicindexes,
-                         int **nicindexes);
+int virCHMonitorCreateVM(virCHDriver *driver, virCHMonitor *mon);
 int virCHMonitorBootVM(virCHMonitor *mon);
 int virCHMonitorShutdownVM(virCHMonitor *mon);
 int virCHMonitorRebootVM(virCHMonitor *mon);
@@ -123,3 +120,5 @@ size_t virCHMonitorGetThreadInfo(virCHMonitor *mon, bool refresh,
                                  virCHMonitorThreadInfo **threads);
 int virCHMonitorGetIOThreads(virCHMonitor *mon,
                              virDomainIOThreadInfo ***iothreads);
+int
+virCHMonitorBuildNetJson(virDomainNetDef *netdef, char **jsonstr);
diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c
index f3bb4a7280..5bf24932d0 100644
--- a/src/ch/ch_process.c
+++ b/src/ch/ch_process.c
@@ -27,9 +27,13 @@
 #include "ch_monitor.h"
 #include "ch_process.h"
 #include "domain_cgroup.h"
+#include "domain_interface.h"
 #include "virerror.h"
+#include "virfile.h"
 #include "virjson.h"
 #include "virlog.h"
+#include "virstring.h"
+#include "ch_interface.h"
 
 #define VIR_FROM_THIS VIR_FROM_CH
 
@@ -448,13 +452,148 @@ virCHProcessSetupVcpus(virDomainObj *vm)
     return 0;
 }
 
+/**
+ * chProcessAddNetworkDevices:
+ * @driver: pointer to ch driver object
+ * @mon: pointer to the monitor object
+ * @vmdef: pointer to domain definition
+ * @nicindexes: returned array of FDs of guest interfaces
+ * @nnicindexes: returned number of network indexes
+ *
+ * Send tap fds to CH process via AddNet api. Capture the network indexes of
+ * guest interfaces in nicindexes.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int
+chProcessAddNetworkDevices(virCHDriver *driver,
+                           virCHMonitor *mon,
+                           virDomainDef *vmdef,
+                           int **nicindexes,
+                           size_t *nnicindexes)
+{
+    size_t i, j, tapfd_len;
+    int mon_sockfd, http_res, ret;
+    g_autofree int *tapfds = NULL;
+    g_autoptr(virJSONValue) net = NULL;
+    struct sockaddr_un server_addr;
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    g_auto(virBuffer) http_headers = VIR_BUFFER_INITIALIZER;
+
+    if (!virBitmapIsBitSet(driver->chCaps, CH_MULTIFD_IN_ADDNET)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Guest networking is not supported by this version of ch"));
+        return -1;
+    }
+
+    mon_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (mon_sockfd < 0) {
+        virReportSystemError(errno, "%s", _("Failed to open a UNIX socket"));
+        return -1;
+    }
+
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sun_family = AF_UNIX;
+    if (virStrcpyStatic(server_addr.sun_path, mon->socketpath) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("UNIX socket path '%1$s' too long"), mon->socketpath);
+        goto err;
+    }
+
+    if (connect(mon_sockfd, (struct sockaddr *)&server_addr,
+                sizeof(server_addr)) == -1) {
+        virReportSystemError(errno, "%s", _("Failed to connect to mon socket"));
+        goto err;
+    }
+
+    virBufferAddLit(&http_headers, "PUT /api/v1/vm.add-net HTTP/1.1\r\n");
+    virBufferAddLit(&http_headers, "Host: localhost\r\n");
+    virBufferAddLit(&http_headers, "Content-Type: application/json\r\n");
+
+    for (i = 0; i < vmdef->nnets; i++) {
+        g_autofree char *payload = NULL;
+        g_autofree char *response = NULL;
+
+        if (vmdef->nets[i]->driver.virtio.queues == 0) {
+            /* "queues" here refers to queue pairs. When 0, initialize
+             * queue pairs to 1.
+             */
+            vmdef->nets[i]->driver.virtio.queues = 1;
+        }
+        tapfd_len = vmdef->nets[i]->driver.virtio.queues;
+
+        if (virCHDomainValidateActualNetDef(vmdef->nets[i]) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("net definition failed validation"));
+            goto err;
+        }
+
+        tapfds = g_new0(int, tapfd_len);
+        memset(tapfds, -1, (tapfd_len) * sizeof(int));
+
+        /* Connect Guest interfaces */
+        if (virCHConnetNetworkInterfaces(driver, vmdef, vmdef->nets[i], tapfds,
+                                         nicindexes, nnicindexes) < 0)
+            goto err;
+
+        if (virCHMonitorBuildNetJson(vmdef->nets[i], &payload) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Failed to build net json"));
+            goto err;
+        }
+
+        VIR_DEBUG("payload sent with net-add request to CH = %s", payload);
+
+        virBufferAsprintf(&buf, "%s", virBufferCurrentContent(&http_headers));
+        virBufferAsprintf(&buf, "Content-Length: %ld\r\n\r\n", strlen(payload));
+        virBufferAsprintf(&buf, "%s", payload);
+        payload = virBufferContentAndReset(&buf);
+
+        if (virSocketSendMsgWithFDs(mon_sockfd, payload, tapfds, tapfd_len) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Failed to send net-add request to CH"));
+            goto err;
+        }
+
+        /* Close sent tap fds in Libvirt, as they have been dup()ed in CH */
+        for (j = 0; j < tapfd_len; j++) {
+            VIR_FORCE_CLOSE(tapfds[j]);
+        }
+
+        /* Process the response from CH */
+        response = virSocketRecv(mon_sockfd);
+        if (response == NULL) {
+            VIR_ERROR(_("Failed while receiving response from CH"));
+            goto err;
+        }
+
+        /* Parse the HTTP response code */
+        ret = sscanf(response, "HTTP/1.%*d %d", &http_res);
+        if (ret != 1) {
+            VIR_ERROR(_("Failed to parse HTTP response code"));
+            goto err;
+        }
+        if (http_res != 204 && http_res != 200) {
+            VIR_ERROR(_("Unexpected response from CH:  %1$d"), http_res);
+            goto err;
+        }
+    }
+
+    VIR_FORCE_CLOSE(mon_sockfd);
+    return 0;
+
+ err:
+    VIR_FORCE_CLOSE(mon_sockfd);
+    return -1;
+}
+
 /**
  * virCHProcessStart:
  * @driver: pointer to driver structure
  * @vm: pointer to virtual machine structure
  * @reason: reason for switching vm to running state
  *
- * Starts Cloud-Hypervisor listen on a local socket
+ * Starts Cloud-Hypervisor listening on a local socket
  *
  * Returns 0 on success or -1 in case of error
  */
@@ -483,8 +622,7 @@ virCHProcessStart(virCHDriver *driver,
             goto cleanup;
         }
 
-        if (virCHMonitorCreateVM(driver, priv->monitor,
-                                 &nnicindexes, &nicindexes) < 0) {
+        if (virCHMonitorCreateVM(driver, priv->monitor) < 0) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("failed to create guest VM"));
             goto cleanup;
@@ -495,6 +633,13 @@ virCHProcessStart(virCHDriver *driver,
     vm->def->id = vm->pid;
     priv->machineName = virCHDomainGetMachineName(vm);
 
+    if (chProcessAddNetworkDevices(driver, priv->monitor, vm->def,
+                                   &nicindexes, &nnicindexes) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed while adding guest interfaces"));
+        goto cleanup;
+    }
+
     if (virDomainCgroupSetupCgroup("ch", vm,
                                    nnicindexes, nicindexes,
                                    &priv->cgroup,
@@ -507,6 +652,10 @@ virCHProcessStart(virCHDriver *driver,
     if (virCHProcessInitCpuAffinity(vm) < 0)
         goto cleanup;
 
+    /* Bring up netdevs before starting CPUs */
+    if (virDomainInterfaceStartDevices(vm->def) < 0)
+        return -1;
+
     if (virCHMonitorBootVM(priv->monitor) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("failed to boot guest VM"));
@@ -552,6 +701,9 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
     int ret;
     int retries = 0;
     virCHDomainObjPrivate *priv = vm->privateData;
+    virCHDriverConfig *cfg = virCHDriverGetConfig(driver);
+    virDomainDef *def = vm->def;
+    size_t i;
 
     VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d",
               vm->def->name, (int)vm->pid, (int)reason);
@@ -560,6 +712,14 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
         g_clear_pointer(&priv->monitor, virCHMonitorClose);
     }
 
+    /* de-activate netdevs after stopping vm */
+    ignore_value(virDomainInterfaceStopDevices(vm->def));
+
+    for (i = 0; i < def->nnets; i++) {
+        virDomainNetDef *net = def->nets[i];
+        virDomainInterfaceDeleteDevice(def, net, false, cfg->stateDir);
+    }
+
  retry:
     if ((ret = virDomainCgroupRemoveCgroup(vm,
                                            priv->cgroup,
diff --git a/src/ch/meson.build b/src/ch/meson.build
index 6c8bf9c43f..633966aac7 100644
--- a/src/ch/meson.build
+++ b/src/ch/meson.build
@@ -7,6 +7,8 @@ ch_driver_sources = [
   'ch_domain.h',
   'ch_driver.c',
   'ch_driver.h',
+  'ch_interface.c',
+  'ch_interface.h',
   'ch_monitor.c',
   'ch_monitor.h',
   'ch_process.c',
-- 
2.43.0
_______________________________________________
Devel mailing list -- devel@xxxxxxxxxxxxxxxxx
To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxx




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux