]> xenbits.xensource.com Git - libvirt.git/commitdiff
Run radvd for virtual networks with IPv6 addresses
authorLaine Stump <laine@laine.org>
Mon, 20 Dec 2010 06:14:11 +0000 (01:14 -0500)
committerLaine Stump <laine@laine.org>
Thu, 23 Dec 2010 20:55:05 +0000 (15:55 -0500)
Running an instance of the router advertisement daemon (radvd) allows
guests using the virtual network to automatically acquire an IPv6
address and default route. Note that acquiring an address only works
for networks with a prefix length of exactly 64 - radvd is still run
in other circumstances, and still advertises routes, but autoconf will
not work because it requires exactly 64 bits of address info from the
network prefix.

This patch avoids a race condition with the pidfile by manually
daemonizing radvd rather than allowing it to daemonize itself, then
creating our own pidfile (in addition to radvd's own file, which is
unnecessary, but there is no way to tell radvd to not create it). This
is accomplished by exec'ing it with "--debug 1" in the commandline,
and using virCommand's features to fork, create a pidfile, and detach
from the newly forked process.

configure.ac
src/conf/network_conf.h
src/network/bridge_driver.c

index c507891feb1bf515fb93ef323e077d609a1f53a7..90fe145bbfbecf2d69a90965fe6249a07708afa9 100644 (file)
@@ -135,6 +135,8 @@ dnl detect them, in which case we'll search for the program
 dnl along the $PATH at runtime and fail if it's not there.
 AC_PATH_PROG([DNSMASQ], [dnsmasq], [dnsmasq],
        [/sbin:/usr/sbin:/usr/local/sbin:$PATH])
+AC_PATH_PROG([RADVD], [radvd], [radvd],
+       [/sbin:/usr/sbin:/usr/local/sbin:$PATH])
 AC_PATH_PROG([BRCTL], [brctl], [brctl],
        [/sbin:/usr/sbin:/usr/local/sbin:$PATH])
 AC_PATH_PROG([UDEVADM], [udevadm], [],
@@ -146,6 +148,8 @@ AC_PATH_PROG([MODPROBE], [modprobe], [],
 
 AC_DEFINE_UNQUOTED([DNSMASQ],["$DNSMASQ"],
         [Location or name of the dnsmasq program])
+AC_DEFINE_UNQUOTED([RADVD],["$RADVD"],
+        [Location or name of the radvd program])
 AC_DEFINE_UNQUOTED([BRCTL],["$BRCTL"],
         [Location or name of the brctl program (see bridge-utils)])
 if test -n "$UDEVADM"; then
index 6c6bbe3d0efe6ca0679bffb970aa92e4d92b7337..fd96c367b1c88bf50199cba8aa74ee3cc62e8aa6 100644 (file)
@@ -106,6 +106,7 @@ struct _virNetworkObj {
     virMutex lock;
 
     pid_t dnsmasqPid;
+    pid_t radvdPid;
     unsigned int active : 1;
     unsigned int autostart : 1;
     unsigned int persistent : 1;
index 07791f152a2fad8c8fa5f85b8ac676c389646a50..7d43ef59cec89a00efa5d2eb2a2217179cdbce81 100644 (file)
@@ -65,6 +65,7 @@
 #define NETWORK_STATE_DIR LOCALSTATEDIR "/lib/libvirt/network"
 
 #define DNSMASQ_STATE_DIR LOCALSTATEDIR "/lib/libvirt/dnsmasq"
+#define RADVD_STATE_DIR LOCALSTATEDIR "/lib/libvirt/radvd"
 
 #define VIR_FROM_THIS VIR_FROM_NETWORK
 
@@ -107,6 +108,25 @@ static void networkReloadIptablesRules(struct network_driver *driver);
 
 static struct network_driver *driverState = NULL;
 
+static char *
+networkRadvdPidfileBasename(const char *netname)
+{
+    /* this is simple but we want to be sure it's consistently done */
+    char *pidfilebase;
+
+    virAsprintf(&pidfilebase, "%s-radvd", netname);
+    return pidfilebase;
+}
+
+static char *
+networkRadvdConfigFileName(const char *netname)
+{
+    char *configfile;
+
+    virAsprintf(&configfile, RADVD_STATE_DIR "/%s-radvd.conf",
+                netname);
+    return configfile;
+}
 
 static void
 networkFindActiveConfigs(struct network_driver *driver) {
@@ -144,26 +164,43 @@ networkFindActiveConfigs(struct network_driver *driver) {
             brHasBridge(driver->brctl, obj->def->bridge) == 0) {
             obj->active = 1;
 
-            /* Finally try and read dnsmasq pid if any */
-            if (obj->def->ips && (obj->def->nips > 0) &&
-                virFileReadPid(NETWORK_PID_DIR, obj->def->name,
-                               &obj->dnsmasqPid) == 0) {
-
-                /* Check its still alive */
-                if (kill(obj->dnsmasqPid, 0) != 0)
-                    obj->dnsmasqPid = -1;
-
-#ifdef __linux__
-                char *pidpath;
+            /* Try and read dnsmasq/radvd pids if any */
+            if (obj->def->ips && (obj->def->nips > 0)) {
+                char *pidpath, *radvdpidbase;
+
+                if (virFileReadPid(NETWORK_PID_DIR, obj->def->name,
+                                   &obj->dnsmasqPid) == 0) {
+                    /* Check that it's still alive */
+                    if (kill(obj->dnsmasqPid, 0) != 0)
+                        obj->dnsmasqPid = -1;
+                    if (virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid) < 0) {
+                        virReportOOMError();
+                        goto cleanup;
+                    }
+                    if (virFileLinkPointsTo(pidpath, DNSMASQ) == 0)
+                        obj->dnsmasqPid = -1;
+                    VIR_FREE(pidpath);
+                }
 
-                if (virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid) < 0) {
+                if (!(radvdpidbase = networkRadvdPidfileBasename(obj->def->name))) {
                     virReportOOMError();
                     goto cleanup;
                 }
-                if (virFileLinkPointsTo(pidpath, DNSMASQ) == 0)
-                    obj->dnsmasqPid = -1;
-                VIR_FREE(pidpath);
-#endif
+                if (virFileReadPid(NETWORK_PID_DIR, radvdpidbase,
+                                   &obj->radvdPid) == 0) {
+                    /* Check that it's still alive */
+                    if (kill(obj->radvdPid, 0) != 0)
+                        obj->radvdPid = -1;
+                    if (virAsprintf(&pidpath, "/proc/%d/exe", obj->radvdPid) < 0) {
+                        virReportOOMError();
+                        VIR_FREE(radvdpidbase);
+                        goto cleanup;
+                    }
+                    if (virFileLinkPointsTo(pidpath, RADVD) == 0)
+                        obj->radvdPid = -1;
+                    VIR_FREE(pidpath);
+                }
+                VIR_FREE(radvdpidbase);
             }
         }
 
@@ -586,6 +623,136 @@ cleanup:
     return ret;
 }
 
+static int
+networkStartRadvd(virNetworkObjPtr network)
+{
+    char *pidfile = NULL;
+    char *radvdpidbase = NULL;
+    virBuffer configbuf = VIR_BUFFER_INITIALIZER;;
+    char *configstr = NULL;
+    char *configfile = NULL;
+    virCommandPtr cmd = NULL;
+    int ret = -1, err, ii;
+    virNetworkIpDefPtr ipdef;
+
+    network->radvdPid = -1;
+
+    if ((err = virFileMakePath(NETWORK_PID_DIR)) != 0) {
+        virReportSystemError(err,
+                             _("cannot create directory %s"),
+                             NETWORK_PID_DIR);
+        goto cleanup;
+    }
+    if ((err = virFileMakePath(RADVD_STATE_DIR)) != 0) {
+        virReportSystemError(err,
+                             _("cannot create directory %s"),
+                             RADVD_STATE_DIR);
+        goto cleanup;
+    }
+
+    /* construct pidfile name */
+    if (!(radvdpidbase = networkRadvdPidfileBasename(network->def->name))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    if (!(pidfile = virFilePid(NETWORK_PID_DIR, radvdpidbase))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* create radvd config file appropriate for this network */
+    virBufferVSprintf(&configbuf, "interface %s\n"
+                      "{\n"
+                      "  AdvSendAdvert on;\n"
+                      "  AdvManagedFlag off;\n"
+                      "  AdvOtherConfigFlag off;\n"
+                      "\n",
+                      network->def->bridge);
+    for (ii = 0;
+         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, ii));
+         ii++) {
+        int prefix;
+        char *netaddr;
+
+        prefix = virNetworkIpDefPrefix(ipdef);
+        if (prefix < 0) {
+            networkReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("bridge  '%s' has an invalid prefix"),
+                               network->def->bridge);
+            goto cleanup;
+        }
+        if (!(netaddr = virSocketFormatAddr(&ipdef->address)))
+            goto cleanup;
+        virBufferVSprintf(&configbuf,
+                          "  prefix %s/%d\n"
+                          "  {\n"
+                          "    AdvOnLink on;\n"
+                          "    AdvAutonomous on;\n"
+                          "    AdvRouterAddr off;\n"
+                          "  };\n",
+                          netaddr, prefix);
+        VIR_FREE(netaddr);
+    }
+
+    virBufferAddLit(&configbuf, "};\n");
+
+    if (virBufferError(&configbuf)) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    if (!(configstr = virBufferContentAndReset(&configbuf))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* construct the filename */
+    if (!(configfile = networkRadvdConfigFileName(network->def->name))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    /* write the file */
+    if (virFileWriteStr(configfile, configstr, 0600) < 0) {
+        virReportSystemError(errno,
+                             _("couldn't write radvd config file '%s'"),
+                             configfile);
+        goto cleanup;
+    }
+
+    /* prevent radvd from daemonizing itself with "--debug 1", and use
+     * a dummy pidfile name - virCommand will create the pidfile we
+     * want to use (this is necessary because radvd's internal
+     * daemonization and pidfile creation causes a race, and the
+     * virFileReadPid() below will fail if we use them).
+     * Unfortunately, it isn't possible to tell radvd to not create
+     * its own pidfile, so we just let it do so, with a slightly
+     * different name. Unused, but harmless.
+     */
+    cmd = virCommandNewArgList(RADVD, "--debug", "1",
+                               "--config", configfile,
+                               "--pidfile", NULL);
+    virCommandAddArgFormat(cmd, "%s-bin", pidfile);
+
+    virCommandSetPidFile(cmd, pidfile);
+    virCommandDaemonize(cmd);
+
+    if (virCommandRun(cmd, NULL) < 0)
+        goto cleanup;
+
+    if (virFileReadPid(NETWORK_PID_DIR, radvdpidbase,
+                       &network->radvdPid) < 0)
+        goto cleanup;
+
+    ret = 0;
+cleanup:
+    virCommandFree(cmd);
+    VIR_FREE(configfile);
+    VIR_FREE(configstr);
+    virBufferFreeAndReset(&configbuf);
+    VIR_FREE(radvdpidbase);
+    VIR_FREE(pidfile);
+    return ret;
+}
+
 static int
 networkAddMasqueradingIptablesRules(struct network_driver *driver,
                                     virNetworkObjPtr network,
@@ -1154,9 +1321,14 @@ networkReloadIptablesRules(struct network_driver *driver)
 
 /* Enable IP Forwarding. Return 0 for success, -1 for failure. */
 static int
-networkEnableIpForwarding(void)
+networkEnableIpForwarding(bool enableIPv4, bool enableIPv6)
 {
-    return virFileWriteStr("/proc/sys/net/ipv4/ip_forward", "1\n", 0);
+    int ret = 0;
+    if (enableIPv4)
+        ret = virFileWriteStr("/proc/sys/net/ipv4/ip_forward", "1\n", 0);
+    if (enableIPv6 && ret == 0)
+        ret = virFileWriteStr("/proc/sys/net/ipv6/conf/all/forwarding", "1\n", 0);
+    return ret;
 }
 
 #define SYSCTL_PATH "/proc/sys"
@@ -1433,7 +1605,7 @@ networkStartNetworkDaemon(struct network_driver *driver,
 
     /* If forwardType != NONE, turn on global IP forwarding */
     if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE &&
-        networkEnableIpForwarding() < 0) {
+        networkEnableIpForwarding(v4present, v6present) < 0) {
         virReportSystemError(errno, "%s",
                              _("failed to enable IP forwarding"));
         goto err3;
@@ -1444,15 +1616,28 @@ networkStartNetworkDaemon(struct network_driver *driver,
     if (v4present && networkStartDhcpDaemon(network) < 0)
         goto err3;
 
+    /* start radvd if there are any ipv6 addresses */
+    if (v6present && networkStartRadvd(network) < 0)
+        goto err4;
+
     /* Persist the live configuration now we have bridge info  */
     if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) {
-        goto err4;
+        goto err5;
     }
 
     network->active = 1;
 
     return 0;
 
+ err5:
+    if (!save_err)
+        save_err = virSaveLastError();
+
+    if (network->radvdPid > 0) {
+        kill(network->radvdPid, SIGTERM);
+        network->radvdPid = -1;
+    }
+
  err4:
     if (!save_err)
         save_err = virSaveLastError();
@@ -1511,6 +1696,19 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver,
     unlink(stateFile);
     VIR_FREE(stateFile);
 
+    if (network->radvdPid > 0) {
+        char *radvdpidbase;
+
+        kill(network->radvdPid, SIGTERM);
+        /* attempt to delete the pidfile we created */
+        if (!(radvdpidbase = networkRadvdPidfileBasename(network->def->name))) {
+            virReportOOMError();
+        } else {
+            virFileDeletePid(NETWORK_PID_DIR, radvdpidbase);
+            VIR_FREE(radvdpidbase);
+        }
+    }
+
     if (network->dnsmasqPid > 0)
         kill(network->dnsmasqPid, SIGTERM);
 
@@ -1531,8 +1729,13 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver,
     if (network->dnsmasqPid > 0 &&
         (kill(network->dnsmasqPid, 0) == 0))
         kill(network->dnsmasqPid, SIGKILL);
-
     network->dnsmasqPid = -1;
+
+    if (network->radvdPid > 0 &&
+        (kill(network->radvdPid, 0) == 0))
+        kill(network->radvdPid, SIGKILL);
+    network->radvdPid = -1;
+
     network->active = 0;
 
     if (network->newDef) {
@@ -1893,6 +2096,27 @@ static int networkUndefine(virNetworkPtr net) {
         dnsmasqContextFree(dctx);
     }
 
+    if (v6present) {
+        char *configfile = networkRadvdConfigFileName(network->def->name);
+
+        if (!configfile) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        unlink(configfile);
+        VIR_FREE(configfile);
+
+        char *radvdpidbase = networkRadvdPidfileBasename(network->def->name);
+
+        if (!(radvdpidbase)) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        virFileDeletePid(NETWORK_PID_DIR, radvdpidbase);
+        VIR_FREE(radvdpidbase);
+
+    }
+
     virNetworkRemoveInactive(&driver->networks,
                              network);
     network = NULL;