Add support for a "network" type of network interface e.g.
<interface type='network'>
<source network="Foo" />
</interface>
When starting a QEMU instance with one of these interfaces,
create a TAP device, bring it up, enslave it to the appropriate
bridge and pass the tap file descriptor to qemu.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: libvirt-foo/qemud/conf.c
===================================================================
--- libvirt-foo.orig/qemud/conf.c 2007-02-14 16:03:29.000000000 +0000
+++ libvirt-foo.orig/qemud/conf.c 2007-02-14 16:03:29.000000000 +0000
@@ -366,6 +366,8 @@ static struct qemud_vm_net_def *qemudPar
xmlNodePtr cur;
xmlChar *macaddr = NULL;
xmlChar *type = NULL;
+ xmlChar *network = NULL;
+ xmlChar *tapifname = NULL;
if (!net) {
qemudReportError(server, VIR_ERR_NO_MEMORY, "net");
@@ -386,6 +388,8 @@ static struct qemud_vm_net_def *qemudPar
net->type = QEMUD_NET_CLIENT;
else if (xmlStrEqual(type, BAD_CAST "mcast"))
net->type = QEMUD_NET_MCAST;
+ else if (xmlStrEqual(type, BAD_CAST "network"))
+ net->type = QEMUD_NET_NETWORK;
/*
else if (xmlStrEqual(type, BAD_CAST "vde"))
typ = QEMUD_NET_VDE;
@@ -402,6 +406,14 @@ static struct qemud_vm_net_def *qemudPar
if ((macaddr == NULL) &&
(xmlStrEqual(cur->name, BAD_CAST "mac"))) {
macaddr = xmlGetProp(cur, BAD_CAST "address");
+ } else if ((network == NULL) &&
+ (net->type == QEMUD_NET_NETWORK) &&
+ (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+ network = xmlGetProp(cur, BAD_CAST "network");
+ } else if ((tapifname == NULL) &&
+ (net->type == QEMUD_NET_NETWORK) &&
+ xmlStrEqual(cur->name, BAD_CAST "tap")) {
+ tapifname = xmlGetProp(cur, BAD_CAST "ifname");
}
}
cur = cur->next;
@@ -421,7 +433,47 @@ static struct qemud_vm_net_def *qemudPar
xmlFree(macaddr);
}
+ if (net->type == QEMUD_NET_NETWORK) {
+ int len;
+
+ if (network == NULL) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "No <source> 'network' attribute
specified with <interface type='network'/>");
+ goto error;
+ } else if ((len = xmlStrlen(network)) >= QEMUD_MAX_NAME_LEN) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Network name '%s' too long", network);
+ goto error;
+ } else {
+ strncpy(net->dst.network.name, (char *)network, len);
+ net->dst.network.name[len] = '\0';
+ }
+
+ if (network)
+ xmlFree(network);
+
+ if (tapifname != NULL) {
+ if ((len == xmlStrlen(tapifname)) >= BR_IFNAME_MAXLEN) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "TAP interface name '%s' is too long",
tapifname);
+ goto error;
+ } else {
+ strncpy(net->dst.network.tapifname, (char *)tapifname, len);
+ net->dst.network.tapifname[len] = '\0';
+ }
+ xmlFree(tapifname);
+ }
+ }
+
return net;
+
+ error:
+ if (network)
+ xmlFree(network);
+ if (tapifname)
+ xmlFree(tapifname);
+ free(net);
+ return NULL;
}
@@ -770,6 +822,68 @@ static int qemudParseXML(struct qemud_se
}
+static char *
+qemudNetworkIfaceConnect(struct qemud_server *server,
+ struct qemud_vm *vm,
+ struct qemud_vm_net_def *net)
+{
+ struct qemud_network *network;
+ const char *tapifname;
+ char tapfdstr[4+3+32+7];
+ char *retval = NULL;
+ int err;
+ int tapfd = -1;
+ int *tapfds;
+
+ if (!(network = qemudFindNetworkByName(server, net->dst.network.name))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Network '%s' not found",
net->dst.network.name);
+ goto error;
+ } else if (network->bridge[0] == '\0') {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Network '%s' not active",
net->dst.network.name);
+ goto error;
+ }
+
+ if (net->dst.network.tapifname[0] == '\0' ||
+ strchr(net->dst.network.tapifname, '%')) {
+ tapifname = "vnet%d";
+ } else {
+ tapifname = net->dst.network.tapifname;
+ }
+
+ if ((err = brAddTap(server->brctl, network->bridge, tapifname,
+ &net->dst.network.tapifname[0], BR_IFNAME_MAXLEN,
&tapfd))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failed to add tap interface '%s' to bridge
'%s' : %s",
+ tapifname, network->bridge, strerror(err));
+ goto error;
+ }
+
+ snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=", tapfd);
+
+ if (!(retval = strdup(tapfdstr)))
+ goto no_memory;
+
+ if (!(tapfds = realloc(vm->tapfds, sizeof(int) * (vm->ntapfds+2))))
+ goto no_memory;
+
+ vm->tapfds = tapfds;
+ vm->tapfds[vm->ntapfds++] = tapfd;
+ vm->tapfds[vm->ntapfds] = -1;
+
+ return retval;
+
+ no_memory:
+ qemudReportError(server, VIR_ERR_NO_MEMORY, "tapfds");
+ error:
+ if (retval)
+ free(retval);
+ if (tapfd != -1)
+ close(tapfd);
+ return NULL;
+}
+
/*
* Constructs a argv suitable for launching qemu with config defined
* for a given virtual machine.
@@ -921,9 +1035,15 @@ int qemudBuildCommandLine(struct qemud_s
goto no_memory;
if (!((*argv)[++n] = strdup("-net")))
goto no_memory;
- /* XXX don't hardcode user */
- if (!((*argv)[++n] = strdup("user")))
- goto no_memory;
+
+ if (net->type != QEMUD_NET_NETWORK) {
+ /* XXX don't hardcode user */
+ if (!((*argv)[++n] = strdup("user")))
+ goto no_memory;
+ } else {
+ if (!((*argv)[++n] = qemudNetworkIfaceConnect(server, vm, net)))
+ goto error;
+ }
net = net->next;
}
@@ -948,12 +1068,20 @@ int qemudBuildCommandLine(struct qemud_s
return 0;
no_memory:
+ qemudReportError(server, VIR_ERR_NO_MEMORY, "argv");
+ error:
+ if (vm->tapfds) {
+ for (i = 0; vm->tapfds[i] != -1; i++)
+ close(vm->tapfds[i]);
+ free(vm->tapfds);
+ vm->tapfds = NULL;
+ vm->ntapfds = 0;
+ }
if (argv) {
for (i = 0 ; i < n ; i++)
free((*argv)[i]);
free(*argv);
}
- qemudReportError(server, VIR_ERR_NO_MEMORY, "argv");
return -1;
}
@@ -1716,6 +1844,18 @@ char *qemudGenerateXML(struct qemud_serv
net->mac[3], net->mac[4], net->mac[5]) < 0)
goto no_memory;
+ if (net->type == QEMUD_NET_NETWORK) {
+ if (qemudBufferPrintf(&buf, " <network name='%s",
net->dst.network.name) < 0)
+ goto no_memory;
+
+ if (net->dst.network.tapifname[0] != '\0' &&
+ qemudBufferPrintf(&buf, " tapifname='%s'",
net->dst.network.tapifname) < 0)
+ goto no_memory;
+
+ if (qemudBufferPrintf(&buf, "/>\n") < 0)
+ goto no_memory;
+ }
+
if (qemudBufferPrintf(&buf, " </interface>\n") < 0)
goto no_memory;
Index: libvirt-foo/qemud/internal.h
===================================================================
--- libvirt-foo.orig/qemud/internal.h 2007-02-14 16:03:29.000000000 +0000
+++ libvirt-foo.orig/qemud/internal.h 2007-02-14 16:03:29.000000000 +0000
@@ -95,6 +95,7 @@ enum qemud_vm_net_type {
QEMUD_NET_SERVER,
QEMUD_NET_CLIENT,
QEMUD_NET_MCAST,
+ QEMUD_NET_NETWORK,
/* QEMUD_NET_VDE*/
};
@@ -123,6 +124,10 @@ struct qemud_vm_net_def {
struct {
char vlan[PATH_MAX];
} vde;
+ struct {
+ char name[QEMUD_MAX_NAME_LEN];
+ char tapifname[BR_IFNAME_MAXLEN];
+ } network;
} dst;
struct qemud_vm_net_def *next;
@@ -193,6 +198,9 @@ struct qemud_vm {
int monitor;
int pid;
+ int *tapfds;
+ int ntapfds;
+
char configFile[PATH_MAX];
struct qemud_vm_def def;
Index: libvirt-foo/qemud/qemud.c
===================================================================
--- libvirt-foo.orig/qemud/qemud.c 2007-02-14 16:03:29.000000000 +0000
+++ libvirt-foo.orig/qemud/qemud.c 2007-02-14 16:03:29.000000000 +0000
@@ -333,8 +333,23 @@ static int qemudDispatchServer(struct qe
static int
+qemudLeaveFdOpen(int *openfds, int fd)
+{
+ int i;
+
+ if (!openfds)
+ return 0;
+
+ for (i = 0; openfds[i] != -1; i++)
+ if (fd == openfds[i])
+ return 1;
+
+ return 0;
+}
+
+static int
qemudExec(struct qemud_server *server, char **argv,
- int *retpid, int *outfd, int *errfd) {
+ int *retpid, int *outfd, int *errfd, int *openfds) {
int pid, null;
int pipeout[2] = {-1,-1};
int pipeerr[2] = {-1,-1};
@@ -392,7 +407,8 @@ qemudExec(struct qemud_server *server, c
for (i = 0; i < open_max; i++)
if (i != STDOUT_FILENO &&
i != STDERR_FILENO &&
- i != STDIN_FILENO)
+ i != STDIN_FILENO &&
+ !qemudLeaveFdOpen(openfds, i))
close(i);
execvp(argv[0], argv);
@@ -429,10 +445,20 @@ int qemudStartVMDaemon(struct qemud_serv
if (qemudBuildCommandLine(server, vm, &argv) < 0)
return -1;
- if (qemudExec(server, argv, &vm->pid, &vm->stdout, &vm->stderr)
== 0) {
+ if (qemudExec(server, argv, &vm->pid, &vm->stdout, &vm->stderr,
vm->tapfds) == 0) {
vm->def.id = server->nextvmid++;
ret = 0;
}
+
+ if (vm->tapfds) {
+ for (i = 0; vm->tapfds[i] != -1; i++) {
+ close(vm->tapfds[i]);
+ vm->tapfds[i] = -1;
+ }
+ free(vm->tapfds);
+ vm->tapfds = NULL;
+ vm->ntapfds = 0;
+ }
for (i = 0 ; argv[i] ; i++)
free(argv[i]);
@@ -632,8 +658,17 @@ static int qemudVMData(struct qemud_serv
}
}
+static void
+qemudNetworkIfaceDisconnect(struct qemud_server *server ATTRIBUTE_UNUSED,
+ struct qemud_vm *vm ATTRIBUTE_UNUSED,
+ struct qemud_vm_net_def *net) {
+ /* FIXME: will be needed to remove iptables rules */
+ net = NULL;
+}
+
int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
struct qemud_vm *prev = NULL, *curr = server->activevms;
+ struct qemud_vm_net_def *net;
/* Already cleaned-up */
if (vm->pid < 0)
@@ -676,6 +711,13 @@ int qemudShutdownVMDaemon(struct qemud_s
curr->monitor = -1;
server->nvmfds -= 2;
+ net = vm->def.nets;
+ while (net) {
+ if (net->type == QEMUD_NET_NETWORK)
+ qemudNetworkIfaceDisconnect(server, vm, net);
+ net = net->next;
+ }
+
if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) {
kill(vm->pid, SIGKILL);
if (waitpid(vm->pid, NULL, 0) != vm->pid) {
@@ -794,7 +836,7 @@ dhcpStartDhcpDaemon(struct qemud_server
if (qemudBuildDnsmasqArgv(server, network, &argv) < 0)
return -1;
- ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL);
+ ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL, NULL);
for (i = 0; argv[i]; i++)
free(argv[i]);
--