diff --git a/configure.ac b/configure.ac index c507891feb..90fe145bbf 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 6c6bbe3d0e..fd96c367b1 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -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; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 07791f152a..7d43ef59ce 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -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) { + /* Try and read dnsmasq/radvd pids if any */ + if (obj->def->ips && (obj->def->nips > 0)) { + char *pidpath, *radvdpidbase; - /* Check its still alive */ - if (kill(obj->dnsmasqPid, 0) != 0) - obj->dnsmasqPid = -1; + 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); + } -#ifdef __linux__ - char *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;