mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-26 18:41:17 +00:00
Support networking in UML driver
This commit is contained in:
parent
7cce4768ae
commit
c0d74ed43b
10
ChangeLog
10
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
|
||||
|
52
src/bridge.c
52
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
|
||||
|
10
src/bridge.h
10
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);
|
||||
|
@ -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",
|
||||
|
@ -8,6 +8,7 @@
|
||||
brAddBridge;
|
||||
brAddInterface;
|
||||
brAddTap;
|
||||
brDeleteTap;
|
||||
brDeleteBridge;
|
||||
brHasBridge;
|
||||
brInit;
|
||||
|
187
src/uml_conf.c
187
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]);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user