[libvirt] PATCH: Support networking in User Mode Linux driver

Daniel P. Berrange berrange at redhat.com
Tue Jun 2 09:52:59 UTC 2009


This patch adds support for bridge, virtual network, multicast and user-mode
networking  in the User Mode Linux driver. You can't pass an open TAP device
file descriptor to UML, so we also extend the bridge.c file to supporting
creation & deletion of persistent TAP devices.

Daniel

diff --git a/src/bridge.c b/src/bridge.c
--- a/src/bridge.c
+++ b/src/bridge.c
@@ -451,8 +451,11 @@ brProbeVnetHdr(int tapfd)
  *
  * This function creates a new tap device on a bridge. @ifname can be either
  * a fixed name or a name template with '%d' for dynamic name allocation.
- * in either case the final name for the bridge will be stored in @ifname
- * and the associated file descriptor in @tapfd.
+ * in either case the final name for the bridge will be stored in @ifname.
+ * If the @tapfd parameter is supplied, the open tap device file
+ * descriptor will be returned, otherwise the TAP device will be made
+ * persistent and closed. The caller must use brDeleteTap to remove
+ * a persistent TAP devices when it is no longer needed.
  *
  * Returns 0 in case of success or an errno code in case of failure.
  */
@@ -465,7 +468,7 @@ brAddTap(brControl *ctl,
 {
     int id, subst, fd;
 
-    if (!ctl || !ctl->fd || !bridge || !ifname || !tapfd)
+    if (!ctl || !ctl->fd || !bridge || !ifname)
         return EINVAL;
 
     subst = id = 0;
@@ -520,10 +523,14 @@ brAddTap(brControl *ctl,
                 goto error;
             if ((errno = brSetInterfaceUp(ctl, try.ifr_name, 1)))
                 goto error;
+            if (!tapfd &&
+                (errno = ioctl(fd, TUNSETPERSIST, 1)))
+                goto error;
             VIR_FREE(*ifname);
             if (!(*ifname = strdup(try.ifr_name)))
                 goto error;
-            *tapfd = fd;
+            if (tapfd)
+                *tapfd = fd;
             return 0;
         }
 
@@ -535,6 +542,43 @@ brAddTap(brControl *ctl,
 
     return errno;
 }
+
+int brDeleteTap(brControl *ctl,
+                const char *ifname)
+{
+    struct ifreq try;
+    int len;
+    int fd;
+
+    if (!ctl || !ctl->fd || !ifname)
+        return EINVAL;
+
+    if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
+        return errno;
+
+    memset(&try, 0, sizeof(struct ifreq));
+    try.ifr_flags = IFF_TAP|IFF_NO_PI;
+
+    len = strlen(ifname);
+    if (len >= BR_IFNAME_MAXLEN - 1) {
+        errno = EINVAL;
+        goto error;
+    }
+
+    strncpy(try.ifr_name, ifname, len);
+    try.ifr_name[len] = '\0';
+
+    if (ioctl(fd, TUNSETIFF, &try) == 0) {
+        if ((errno = ioctl(fd, TUNSETPERSIST, 0)))
+            goto error;
+    }
+
+ error:
+    close(fd);
+
+    return errno;
+}
+
 
 /**
  * brSetInterfaceUp:
diff --git a/src/bridge.h b/src/bridge.h
--- a/src/bridge.h
+++ b/src/bridge.h
@@ -60,11 +60,19 @@ int     brDeleteInterface       (brContr
                                  const char *bridge,
                                  const char *iface);
 
+enum {
+    BR_TAP_VNET_HDR = (1 << 0),
+    BR_TAP_PERSIST =  (1 << 1),
+};
+
 int     brAddTap                (brControl *ctl,
                                  const char *bridge,
                                  char **ifname,
-                                 int vnet_hdr,
+                                 int features,
                                  int *tapfd);
+
+int     brDeleteTap             (brControl *ctl,
+                                 const char *ifname);
 
 int     brSetInterfaceUp        (brControl *ctl,
                                  const char *ifname,
diff --git a/src/domain_conf.c b/src/domain_conf.c
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -3146,6 +3146,7 @@ virDomainNetDefFormat(virConnectPtr conn
         else
             virBufferVSprintf(buf, "      <source port='%d'/>\n",
                               def->data.socket.port);
+	break;
 
     case VIR_DOMAIN_NET_TYPE_INTERNAL:
         virBufferEscapeString(buf, "      <source name='%s'/>\n",
diff --git a/src/libvirt_bridge.syms b/src/libvirt_bridge.syms
--- a/src/libvirt_bridge.syms
+++ b/src/libvirt_bridge.syms
@@ -8,6 +8,7 @@ brAddBridge;
 brAddBridge;
 brAddInterface;
 brAddTap;
+brDeleteTap;
 brDeleteBridge;
 brHasBridge;
 brInit;
diff --git a/src/uml_conf.c b/src/uml_conf.c
--- a/src/uml_conf.c
+++ b/src/uml_conf.c
@@ -44,6 +44,7 @@
 #include "memory.h"
 #include "nodeinfo.h"
 #include "verify.h"
+#include "bridge.h"
 
 #define VIR_FROM_THIS VIR_FROM_UML
 
@@ -90,6 +91,172 @@ virCapsPtr umlCapsInit(void) {
     return NULL;
 }
 
+
+static int
+umlConnectTapDevice(virConnectPtr conn,
+                    virDomainNetDefPtr net,
+                    const char *bridge)
+{
+    int tapfd = -1;
+    int err;
+    brControl *brctl = NULL;
+
+    if (!net->ifname ||
+        STRPREFIX(net->ifname, "vnet") ||
+        strchr(net->ifname, '%')) {
+        VIR_FREE(net->ifname);
+        if (!(net->ifname = strdup("vnet%d")))
+            goto no_memory;
+    }
+
+    if ((err = brInit(&brctl))) {
+        char ebuf[1024];
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot initialize bridge support: %s"),
+                       virStrerror(err, ebuf, sizeof ebuf));
+        goto error;
+    }
+
+    if ((err = brAddTap(brctl, bridge,
+                        &net->ifname, BR_TAP_PERSIST, &tapfd))) {
+        if (errno == ENOTSUP) {
+            /* In this particular case, give a better diagnostic. */
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to add tap interface to bridge. "
+                             "%s is not a bridge device"), bridge);
+        } else {
+            char ebuf[1024];
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to add tap interface '%s' "
+                             "to bridge '%s' : %s"),
+                           net->ifname, bridge, virStrerror(err, ebuf, sizeof ebuf));
+        }
+        goto error;
+    }
+    close(tapfd);
+
+    brShutdown(brctl);
+
+    return 0;
+
+no_memory:
+    virReportOOMError(conn);
+error:
+    brShutdown(brctl);
+    return -1;
+}
+
+static char *
+umlBuildCommandLineNet(virConnectPtr conn,
+                       virDomainNetDefPtr def,
+                       int idx)
+{
+    char *ret;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    /* General format:  ethNN=type,options */
+
+    virBufferVSprintf(&buf, "eth%d=", idx);
+
+    switch (def->type) {
+    case VIR_DOMAIN_NET_TYPE_USER:
+        /* ethNNN=slirp,macaddr */
+        virBufferAddLit(&buf, "slirp");
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+        /* ethNNN=tuntap,tapname,macaddr,gateway */
+        virBufferAddLit(&buf, "tuntap");
+        if (def->data.ethernet.ipaddr) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("IP address not supported for ethernet inteface"));
+            goto error;
+        }
+        if (def->data.ethernet.script) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("script execution not supported for ethernet inteface"));
+            goto error;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("TCP server networking type not supported"));
+        goto error;
+
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("TCP client networking type not supported"));
+        goto error;
+
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+        /* ethNNN=tuntap,macaddr,ipaddr,port */
+        virBufferAddLit(&buf, "mcast");
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+    {
+        char *bridge;
+        virNetworkPtr network = virNetworkLookupByName(conn,
+                                                       def->data.network.name);
+        if (!network) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                           _("Network '%s' not found"),
+                           def->data.network.name);
+            goto error;
+        }
+        bridge = virNetworkGetBridgeName(network);
+        virNetworkFree(network);
+        if (bridge == NULL) {
+            goto error;
+        }
+
+        if (umlConnectTapDevice(conn, def, bridge) < 0) {
+            VIR_FREE(bridge);
+            goto error;
+        }
+
+        /* ethNNN=tuntap,tapname,macaddr,gateway */
+        virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
+        break;
+    }
+
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        if (umlConnectTapDevice(conn, def, def->data.bridge.brname) < 0)
+            goto error;
+
+        /* ethNNN=tuntap,tapname,macaddr,gateway */
+        virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_INTERNAL:
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("internal networking type not supported"));
+        goto error;
+    }
+
+    virBufferVSprintf(&buf, ",%02x:%02x:%02x:%02x:%02x:%02x",
+                      def->mac[0], def->mac[1], def->mac[2],
+                      def->mac[3], def->mac[4], def->mac[5]);
+
+    if (def->type == VIR_DOMAIN_NET_TYPE_MCAST) {
+        virBufferVSprintf(&buf, ",%s,%d",
+                          def->data.socket.address,
+                          def->data.socket.port);
+    }
+
+    if (virBufferError(&buf)) {
+        virReportOOMError(conn);
+        return NULL;
+    }
+
+    return virBufferContentAndReset(&buf);
+
+error:
+    ret = virBufferContentAndReset(&buf);
+    VIR_FREE(ret);
+    return NULL;
+}
 
 static char *
 umlBuildCommandLineChr(virConnectPtr conn,
@@ -166,9 +333,8 @@ int umlBuildCommandLine(virConnectPtr co
                         struct uml_driver *driver ATTRIBUTE_UNUSED,
                         virDomainObjPtr vm,
                         const char ***retargv,
-                        const char ***retenv,
-                        int **tapfds,
-                        int *ntapfds) {
+                        const char ***retenv)
+{
     int i, j;
     char memory[50];
     struct utsname ut;
@@ -277,6 +443,13 @@ int umlBuildCommandLine(virConnectPtr co
         ADD_ARG_PAIR(disk->dst, disk->src);
     }
 
+    for (i = 0 ; i < vm->def->nnets ; i++) {
+        char *ret = umlBuildCommandLineNet(conn, vm->def->nets[i], i);
+        if (!ret)
+            goto error;
+        ADD_ARG(ret);
+    }
+
     for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
         char *ret;
         if (i == 0 && vm->def->console)
@@ -311,13 +484,7 @@ int umlBuildCommandLine(virConnectPtr co
  no_memory:
     virReportOOMError(conn);
  error:
-    if (tapfds &&
-        *tapfds) {
-        for (i = 0; i < *ntapfds; i++)
-            close((*tapfds)[i]);
-        VIR_FREE(*tapfds);
-        *ntapfds = 0;
-    }
+
     if (qargv) {
         for (i = 0 ; i < qargc ; i++)
             VIR_FREE((qargv)[i]);
diff --git a/src/uml_conf.h b/src/uml_conf.h
--- a/src/uml_conf.h
+++ b/src/uml_conf.h
@@ -70,8 +70,6 @@ int         umlBuildCommandLine       (v
                                        struct uml_driver *driver,
                                        virDomainObjPtr dom,
                                        const char ***retargv,
-                                       const char ***retenv,
-                                       int **tapfds,
-                                       int *ntapfds);
+                                       const char ***retenv);
 
 #endif /* __UML_CONF_H */
diff --git a/src/uml_driver.c b/src/uml_driver.c
--- a/src/uml_driver.c
+++ b/src/uml_driver.c
@@ -715,6 +721,35 @@ error:
     return -1;
 }
 
+
+static int umlCleanupTapDevices(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                virDomainObjPtr vm) {
+    int i;
+    int err;
+    int ret = 0;
+    brControl *brctl = NULL;
+    VIR_ERROR0("Cleanup tap");
+    if (brInit(&brctl) < 0)
+        return -1;
+
+    for (i = 0 ; i < vm->def->nnets ; i++) {
+        virDomainNetDefPtr def = vm->def->nets[i];
+
+        if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
+            def->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+            continue;
+
+        VIR_ERROR("Cleanup '%s'", def->ifname);
+        err = brDeleteTap(brctl, def->ifname);
+        if (err) {
+            VIR_ERROR("Cleanup failed %d", err);
+            ret = -1;
+        }
+    }
+    VIR_ERROR0("Cleanup tap done");
+    brShutdown(brctl);
+    return ret;
+}
 
 static int umlStartVMDaemon(virConnectPtr conn,
                             struct uml_driver *driver,
@@ -726,8 +761,6 @@ static int umlStartVMDaemon(virConnectPt
     char *logfile;
     int logfd = -1;
     struct stat sb;
-    int *tapfds = NULL;
-    int ntapfds = 0;
     fd_set keepfd;
     char ebuf[1024];
 
@@ -786,9 +819,9 @@ static int umlStartVMDaemon(virConnectPt
     }
 
     if (umlBuildCommandLine(conn, driver, vm,
-                            &argv, &progenv,
-                            &tapfds, &ntapfds) < 0) {
+                            &argv, &progenv) < 0) {
         close(logfd);
+        umlCleanupTapDevices(conn, vm);
         return -1;
     }
 
@@ -818,9 +851,6 @@ static int umlStartVMDaemon(virConnectPt
 
     vm->monitor = -1;
 
-    for (i = 0 ; i < ntapfds ; i++)
-        FD_SET(tapfds[i], &keepfd);
-
     ret = virExecDaemonize(conn, argv, progenv, &keepfd, &pid,
                            -1, &logfd, &logfd,
                            0, NULL, NULL, NULL);
@@ -834,15 +864,14 @@ static int umlStartVMDaemon(virConnectPt
         VIR_FREE(progenv[i]);
     VIR_FREE(progenv);
 
-    if (tapfds) {
-        for (i = 0 ; i < ntapfds ; i++) {
-            close(tapfds[i]);
-        }
-        VIR_FREE(tapfds);
-    }
+    if (ret < 0)
+        umlCleanupTapDevices(conn, vm);
 
     /* NB we don't mark it running here - we do that async
        with inotify */
+    /* XXX what if someone else tries to start it again
+       before we get the inotification ? Sounds like
+       trouble.... */
 
     return ret;
 }
@@ -874,6 +901,8 @@ static void umlShutdownVMDaemon(virConne
     vm->state = VIR_DOMAIN_SHUTOFF;
     VIR_FREE(vm->vcpupids);
     vm->nvcpupids = 0;
+
+    umlCleanupTapDevices(conn, vm);
 
     if (vm->newDef) {
         virDomainDefFree(vm->def);



-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list