diff --git a/ChangeLog b/ChangeLog
index d8d8c1a026..5263b4479e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Wed Jun  3 12:03:52 BST 2009 Daniel P. Berrange <berrange@redhat.com>
+
+	Support networking in UML driver
+	* src/bridge.c: Add new brDeleteTap function. Allow brAddTap
+	to create a persistent tap devices.
+	* src/bridge.h, src/libvirt_bridge.syms: Add brDeleteTap
+	* src/domain_conf.c: Fix missing 'break' in network XML formatter
+	* src/uml_conf.c, src/uml_conf.h, src/uml_driver.c: Add support
+	for bridge, network, mcast and user mode network interfaces
+
 Wed Jun  3 11:53:52 BST 2009 Daniel P. Berrange <berrange@redhat.com>
 
 	Misc User Mode Linux startup/shutdown bugs
diff --git a/src/bridge.c b/src/bridge.c
index 8425158868..0509afd0c7 100644
--- 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;
         }
 
@@ -536,6 +543,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:
  * @ctl: bridge control pointer
diff --git a/src/bridge.h b/src/bridge.h
index e06ff417fe..2d9bec16b2 100644
--- a/src/bridge.h
+++ b/src/bridge.h
@@ -60,12 +60,20 @@ int     brDeleteInterface       (brControl *ctl,
                                  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,
                                  int up);
diff --git a/src/domain_conf.c b/src/domain_conf.c
index 623eba3596..c0c4df9fba 100644
--- 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
index 1f0a63f3a0..2658291049 100644
--- a/src/libvirt_bridge.syms
+++ b/src/libvirt_bridge.syms
@@ -8,6 +8,7 @@
 brAddBridge;
 brAddInterface;
 brAddTap;
+brDeleteTap;
 brDeleteBridge;
 brHasBridge;
 brInit;
diff --git a/src/uml_conf.c b/src/uml_conf.c
index c0d086ee50..3f9023050e 100644
--- 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
 
@@ -91,6 +92,172 @@ virCapsPtr umlCapsInit(void) {
 }
 
 
+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,
                        virDomainChrDefPtr def,
@@ -166,9 +333,8 @@ int umlBuildCommandLine(virConnectPtr conn,
                         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 conn,
         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 conn,
  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
index f7846e056d..7e398850a7 100644
--- a/src/uml_conf.h
+++ b/src/uml_conf.h
@@ -70,8 +70,6 @@ int         umlBuildCommandLine       (virConnectPtr conn,
                                        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
index be03567ab7..6311542b6d 100644
--- a/src/uml_driver.c
+++ b/src/uml_driver.c
@@ -722,6 +722,35 @@ error:
 }
 
 
+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,
                             virDomainObjPtr vm) {
@@ -732,8 +761,6 @@ static int umlStartVMDaemon(virConnectPtr conn,
     char *logfile;
     int logfd = -1;
     struct stat sb;
-    int *tapfds = NULL;
-    int ntapfds = 0;
     fd_set keepfd;
     char ebuf[1024];
 
@@ -792,9 +819,9 @@ static int umlStartVMDaemon(virConnectPtr conn,
     }
 
     if (umlBuildCommandLine(conn, driver, vm,
-                            &argv, &progenv,
-                            &tapfds, &ntapfds) < 0) {
+                            &argv, &progenv) < 0) {
         close(logfd);
+        umlCleanupTapDevices(conn, vm);
         return -1;
     }
 
@@ -824,9 +851,6 @@ static int umlStartVMDaemon(virConnectPtr conn,
 
     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);
@@ -840,15 +864,14 @@ static int umlStartVMDaemon(virConnectPtr conn,
         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;
 }
@@ -879,6 +902,8 @@ static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
     VIR_FREE(vm->vcpupids);
     vm->nvcpupids = 0;
 
+    umlCleanupTapDevices(conn, vm);
+
     if (vm->newDef) {
         virDomainDefFree(vm->def);
         vm->def = vm->newDef;