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 :|