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 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list