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

Re: [Libvir] Virtual networking (not the rathole thread :-)



On Wed, 2007-01-17 at 18:38 +0000, Mark McLoughlin wrote:
> On Tue, 2007-01-16 at 22:28 +0000, Daniel P. Berrange wrote:
> > On Mon, Jan 15, 2007 at 08:06:18PM +0000, Mark McLoughlin wrote:

> > > Virtual Networks will be implemented in libvirt. First, there will be an
> > > XML description of Virtual Networks e.g.:
> > > 
> > >   <network id="0">
> > >     <name>Foo</name>
> > >     <uuid>596a5d2171f48fb2e068e2386a5c413e</uuid>
> > >     <listen address="172.31.0.5" port="1234" />
> > >     <connections>
> > >       <connection address="172.31.0.6" port="4321" />
> > >     </conections>
> > >     <dhcp enabled="true">
> > >       <ip address="10.0.0.1" 
> > >           netmask="255.255.255.0" 
> > >           start="10.0.0.128"
> > >           end="10.0.0.254" />
> > >     </dhcp>
> > >     <forwarding enabled="true">
> > >       <incoming default="deny">
> > >         <allow port="123" domain="foobar" destport="321" />
> > >       </incoming>
> > >       <outgoing default="allow">
> > >         <deny port="25" />
> > >       </outgoing>
> > >     </forwarding>
> > >   <network>
> > 
> > Got to also think how we connect guest domains to the virtual network.
> 
> 	Right, further on in the mail I said:
> 
>       * Where is the connection between domains and networks in either
>         the API or the XML format? How is a domain associated with a
>         network? You put a bridge name in the <network> definition
>         and use that in the domains <interface> definition? Or you put
>         the network name in the interface definition and have libvirt
>         look up the bridge name when creating the guest? 
> 
> > Currently we just have something really simple like
> > 
> >   <interface type="bridge">
> >     <source bridge='xenbr0'/>
> >     <mac address='00:11:22:33:44:55'/>
> >   </interface>
> > 
> > I guess we've probably want to refer to the UUID of the network to map
> > it into the guest.
> 
> 	Well, the UUID isn't much good if you can't map it. So, it would
> probably be the name and libvirt URI, right?

	Related to the last patch, how about we just put the network name in
the interface definition?

	Attached are patches implementing this for both QEMU and Xen guests.

Cheers,
Mark.
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.

Index: libvirt/qemud/config.c
===================================================================
--- libvirt.orig/qemud/config.c
+++ libvirt/qemud/config.c
@@ -364,6 +364,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");
@@ -384,6 +386,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;
@@ -400,6 +404,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;
@@ -418,7 +430,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;
 }
 
 
@@ -761,6 +813,61 @@ 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];
+    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, NULL, -1, &tapfd))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "Failed to add tap interface '%s' to bridge '%s' : %s",
+                         tapifname, network->bridge, strerror(err));
+        goto error;
+    }
+
+    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;
+
+    snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=", tapfd);
+
+    return strdup(tapfdstr);
+
+ no_memory:
+    qemudReportError(server, VIR_ERR_NO_MEMORY, "tapfds");
+ error:
+    if (tapfd != -1)
+        close(tapfd);
+    return NULL;
+}
+
 /*
  * Constructs a argv suitable for launching qemu with config defined
  * for a given virtual machine.
@@ -902,9 +1009,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;
         }
@@ -929,12 +1042,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;
 }
 
@@ -1653,6 +1774,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/qemud/internal.h
===================================================================
--- libvirt.orig/qemud/internal.h
+++ libvirt/qemud/internal.h
@@ -90,6 +90,7 @@ enum qemud_vm_net_type {
     QEMUD_NET_SERVER,
     QEMUD_NET_CLIENT,
     QEMUD_NET_MCAST,
+    QEMUD_NET_NETWORK,
     /*  QEMUD_NET_VDE*/
 };
 
@@ -118,6 +119,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;
@@ -183,6 +188,9 @@ struct qemud_vm {
     int monitor;
     int pid;
 
+    int *tapfds;
+    int ntapfds;
+
     char configFile[PATH_MAX];
 
     struct qemud_vm_def def;
Index: libvirt/qemud/qemud.c
===================================================================
--- libvirt.orig/qemud/qemud.c
+++ libvirt/qemud/qemud.c
@@ -571,8 +571,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};
@@ -630,7 +645,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);
@@ -667,10 +683,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]);
@@ -1028,7 +1054,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]);
When create Xen guests with <interface type='network'>, lookup
the bridge associated with the network and configure the guest
to connect to that bridge.

Index: libvirt/src/xml.c
===================================================================
--- libvirt.orig/src/xml.c
+++ libvirt/src/xml.c
@@ -1199,6 +1199,7 @@ virDomainParseXMLIfDesc(virConnectPtr co
     xmlChar *script = NULL;
     xmlChar *ip = NULL;
     int typ = 0;
+    int ret = -1;
 
     type = xmlGetProp(node, BAD_CAST "type");
     if (type != NULL) {
@@ -1206,6 +1207,8 @@ virDomainParseXMLIfDesc(virConnectPtr co
             typ = 0;
         else if (xmlStrEqual(type, BAD_CAST "ethernet"))
             typ = 1;
+        else if (xmlStrEqual(type, BAD_CAST "network"))
+            typ = 2;
         xmlFree(type);
     }
     cur = node->children;
@@ -1215,8 +1218,10 @@ virDomainParseXMLIfDesc(virConnectPtr co
                 (xmlStrEqual(cur->name, BAD_CAST "source"))) {
                 if (typ == 0)
                     source = xmlGetProp(cur, BAD_CAST "bridge");
-                else
+                else if (typ == 1)
                     source = xmlGetProp(cur, BAD_CAST "dev");
+                else
+                    source = xmlGetProp(cur, BAD_CAST "network");
             } else if ((mac == NULL) &&
                        (xmlStrEqual(cur->name, BAD_CAST "mac"))) {
                 mac = xmlGetProp(cur, BAD_CAST "address");
@@ -1241,8 +1246,18 @@ virDomainParseXMLIfDesc(virConnectPtr co
     if (source != NULL) {
         if (typ == 0)
             virBufferVSprintf(buf, "(bridge '%s')", (const char *) source);
-        else                    /* TODO does that work like that ? */
+        else if (typ == 1)      /* TODO does that work like that ? */
             virBufferVSprintf(buf, "(dev '%s')", (const char *) source);
+        else {
+            virNetworkPtr network = virNetworkLookupByName(conn, (const char *) source);
+            char *bridge;
+            if (!network || !(bridge = virNetworkGetBridgeName(network))) {
+                virXMLError(conn, VIR_ERR_NO_SOURCE, (const char *) source, 0);
+                goto error;
+            }
+            virBufferVSprintf(buf, "(bridge '%s')", bridge);
+            free(bridge);
+        }
     }
     if (script != NULL)
         virBufferVSprintf(buf, "(script '%s')", script);
@@ -1252,6 +1267,8 @@ virDomainParseXMLIfDesc(virConnectPtr co
         virBufferAdd(buf, "(type ioemu)", 12);
 
     virBufferAdd(buf, ")", 1);
+    ret = 0;
+ error:
     if (mac != NULL)
         xmlFree(mac);
     if (source != NULL)
@@ -1260,7 +1277,7 @@ virDomainParseXMLIfDesc(virConnectPtr co
         xmlFree(script);
     if (ip != NULL)
         xmlFree(ip);
-    return (0);
+    return (ret);
 }
 
 /**

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