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@xxxxxxxxxx> 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]); --