[patch 08/12] Allow <interface type=network> with qemu

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

-- 


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]