From c0d74ed43b3d60e1f8f0b3a3a6aaf15601d6d42c Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 3 Jun 2009 11:13:33 +0000 Subject: [PATCH] Support networking in UML driver --- ChangeLog | 10 +++ src/bridge.c | 52 ++++++++++- src/bridge.h | 10 ++- src/domain_conf.c | 1 + src/libvirt_bridge.syms | 1 + src/uml_conf.c | 187 +++++++++++++++++++++++++++++++++++++--- src/uml_conf.h | 4 +- src/uml_driver.c | 51 ++++++++--- 8 files changed, 285 insertions(+), 31 deletions(-) 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 + + 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 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, " \n", def->data.socket.port); + break; case VIR_DOMAIN_NET_TYPE_INTERNAL: virBufferEscapeString(buf, " \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; -- GitLab