[libvirt] [PATCH 0/3] Add dnsmasq module

The following series of patches is a prototype implementation of dnsmasq module. It implements an idea originally suggested by Daniel-san and can address the problem that too many --dhcp-host args hitting ARG_MAX limit I reported last year [1]. [1] https://www.redhat.com/archives/libvir-list/2009-October/msg00216.html Thanks, Satoru SATOH

This patch adds the files implements dnsmasq (hostsfile) module. Signed-off-by: Satoru SATOH <satoru.satoh@gmail.com> --- src/util/dnsmasq.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/dnsmasq.h | 54 ++++++++++ 2 files changed, 350 insertions(+), 0 deletions(-) create mode 100644 src/util/dnsmasq.c create mode 100644 src/util/dnsmasq.h diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c new file mode 100644 index 0000000..8760c7c --- /dev/null +++ b/src/util/dnsmasq.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2010 Satoru SATOH <satoru.satoh@gmail.com> + * Copyright (C) 2007-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based on iptables.c + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> + +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#include "internal.h" +#include "dnsmasq.h" +#include "util.h" +#include "memory.h" +#include "virterror_internal.h" +#include "logging.h" + +static void +hostsfileFree(dnsmasqHostfile *hostsfile) +{ + int i; + + if (hostsfile->entries) { + for (i = 0; i < hostsfile->nentries; i++) + VIR_FREE(hostsfile->entries[i].entry); + + VIR_FREE(hostsfile->entries); + + hostsfile->nentries = 0; + } + + hostsfile->path[0] = '\0'; + + VIR_FREE(hostsfile); +} + +static dnsmasqHostfile * +hostsfileNew(const char *name, + const char *config_dir) +{ + int err; + char ebuf[1024]; + + dnsmasqHostfile *hostsfile; + + if (VIR_ALLOC(hostsfile) < 0) + return NULL; + + hostsfile->entries = NULL; + hostsfile->nentries = 0; + + if ((err = virFileMakePath(config_dir))) { + VIR_WARN(_("Failed to create directory %s : %s"), config_dir, + virStrerror(err, ebuf, sizeof ebuf)); + goto error; + } + + if (virFileBuildPath(config_dir, name, ".hostsfile", hostsfile->path, + sizeof(hostsfile->path)) < 0) + goto error; + + return hostsfile; + + error: + hostsfileFree(hostsfile); + return NULL; +} + +static int +hostsfileAdd(dnsmasqHostfile *hostsfile, + const char *mac, + const char *ip, + const char *name) +{ + if (VIR_REALLOC_N(hostsfile->entries, hostsfile->nentries+1) < 0) + return ENOMEM; + + if (virAsprintf(&hostsfile->entries[hostsfile->nentries].entry, + "%s,%s,%s", mac, name, ip) < 0) { + return ENOMEM; + } + + hostsfile->nentries++; + + return 0; +} + +static int +hostsfileWrite(const char *path, + const dhcpHostEntry *entries, + int nentries) +{ + char tmp[PATH_MAX]; + FILE *f; + int istmp; + int i; + + if (nentries == 0 && unlink(path) == 0) + return 0; + + if (snprintf(tmp, PATH_MAX, "%s.new", path) >= PATH_MAX) + return EINVAL; + + istmp = 1; + + if (!(f = fopen(tmp, "w"))) { + istmp = 0; + if (!(f = fopen(path, "w"))) + return errno; + } + + for (i = 0; i < nentries; i++) { + if (fputs(entries[i].entry, f) == EOF || + fputc('\n', f) == EOF) { + fclose(f); + if (istmp) + unlink(tmp); + return errno; + } + } + + fclose(f); + + if (istmp && rename(tmp, path) < 0) { + unlink(tmp); + return errno; + } + + if (istmp) + unlink(tmp); + + return 0; +} + +static int +hostsfileSave(const dnsmasqContext *ctx) +{ + if (hostsfileWrite(ctx->hostsfile->path, ctx->hostsfile->entries, + ctx->hostsfile->nentries) < 0) { + VIR_WARN(_("cannot write config file '%s'\n"), ctx->hostsfile->path); + return -1; + } + + return 0; +} + +static int +hostsfileDelete(const dnsmasqContext *ctx) +{ + if (! virFileExists(ctx->hostsfile->path)) + return 0; + + if (unlink(ctx->hostsfile->path) < 0) { + VIR_WARN(_("cannot remove config file '%s'\n"), ctx->hostsfile->path); + return -1; + } + + return 0; +} + +/** + * dnsmasqContextNew: + * + * Create a new Dnsmasq context + * + * Returns a pointer to the new structure or NULL in case of error + */ +dnsmasqContext * +dnsmasqContextNew(const char *network_name, + const char *config_dir) +{ + dnsmasqContext *ctx; + + if (VIR_ALLOC(ctx) < 0) + return NULL; + + if (!(ctx->hostsfile = hostsfileNew(network_name, config_dir))) + goto error; + + return ctx; + + error: + dnsmasqContextFree(ctx); + return NULL; +} + +/** + * dnsmasqContextFree: + * @ctx: pointer to the dnsmasq context + * + * Free the resources associated with an dnsmasq context + */ +void +dnsmasqContextFree(dnsmasqContext *ctx) +{ + if (ctx->hostsfile) + hostsfileFree(ctx->hostsfile); + VIR_FREE(ctx); +} + +/** + * dnsmasqAddDhcpHost: + * @ctx: pointer to the dnsmasq context for each network + * @mac: pointer to the string contains mac address of the host + * @ip: pointer to the string contains ip address of the host + * @name: pointer to the string contains hostname of the host + * + * Add host entries. + */ +void +dnsmasqAddDhcpHost(dnsmasqContext *ctx, + const char *mac, + const char *ip, + const char *name) +{ + if (hostsfileAdd(ctx->hostsfile, mac, ip, name)) + VIR_WARN(_("Failed to add host: mac=%s, ip=%s, name=%s\n"), mac, ip, name); +} + +/** + * dnsmasqSave: + * @ctx: pointer to the dnsmasq context for each network + * + * Saves all the configurations associated with a context to disk. + */ +int +dnsmasqSave(const dnsmasqContext *ctx) +{ + if (hostsfileSave(ctx) < 0) + return 0; + + return 1; +} + + +/** + * dnsmasqDelete: + * @ctx: pointer to the dnsmasq context for each network + * + * Delete all the configuration files associated with a context. + */ +int +dnsmasqDelete(const dnsmasqContext *ctx) +{ + if (hostsfileDelete(ctx) < 0) + return 0; + + return 1; +} + +/** + * dnsmasqReload: + * @ctx: pointer to the dnsmasq context + * + * Reloads all the configurations associated to a context + */ +int +dnsmasqReload(pid_t pid) +{ + if (kill(pid, SIGHUP) != 0) { + VIR_WARN(_("Failed to make dnsmasq (PID: %d) reloading config files.\n"), pid); + return 0; + } + + return 1; +} + diff --git a/src/util/dnsmasq.h b/src/util/dnsmasq.h new file mode 100644 index 0000000..4343ed9 --- /dev/null +++ b/src/util/dnsmasq.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Satoru SATOH <satoru.satoh@gmail.com> + * Copyright (C) 2007, 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * based on iptables.h + */ + +#ifndef __DNSMASQ_H__ +#define __DNSMASQ_H__ + +typedef struct +{ + char *entry; /* "<mac>,<hostname>,<ip>", ex. "01:23:45:67:89:0a,foo,10.0.0.3" */ +} dhcpHostEntry; + +typedef struct +{ + int nentries; + dhcpHostEntry *entries; + + char path[PATH_MAX]; +} dnsmasqHostfile; + +typedef struct +{ + dnsmasqHostfile *hostsfile; +} dnsmasqContext; + +dnsmasqContext * dnsmasqContextNew(const char *network_name, + const char *config_dir); +void dnsmasqContextFree(dnsmasqContext *ctx); +void dnsmasqAddDhcpHost(dnsmasqContext *ctx, + const char *mac, + const char *ip, + const char *name); +int dnsmasqSave(const dnsmasqContext *ctx); +int dnsmasqDelete(const dnsmasqContext *ctx); +int dnsmasqReload(pid_t pid); + +#endif /* __DNSMASQ_H__ */ -- 1.6.6.1

This patch adds build support for the dnsmasq module. Signed-off-by: Satoru SATOH <satoru.satoh@gmail.com> --- src/Makefile.am | 1 + src/libvirt_private.syms | 9 +++++++++ 2 files changed, 10 insertions(+), 0 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index cd1848e..a66125a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -59,6 +59,7 @@ UTIL_SOURCES = \ util/hooks.c util/hooks.h \ util/iptables.c util/iptables.h \ util/ebtables.c util/ebtables.h \ + util/dnsmasq.c util/dnsmasq.h \ util/json.c util/json.h \ util/logging.c util/logging.h \ util/macvtap.c util/macvtap.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index edb23c2..b3a4058 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -316,6 +316,15 @@ iptablesRemoveTcpInput; iptablesRemoveUdpInput; +# dnsmasq.h +dnsmasqContextNew; +dnsmasqContextFree; +dnsmasqAddDhcpHost; +dnsmasqSave; +dnsmasqDelete; +dnsmasqReload; + + # libvirt_internal.h virDrvSupportsFeature; virDomainMigratePrepare; -- 1.6.6.1

This patch makes dnsmasq daemon started from libvirtd with a --dhcp-hostsfile option instead of --dhcp-host options for each '//ip/dhcp/host' entries. NOTE: I'm not sure where and when to save the dnsmasq hostsfile so that make it saved in dnsmasq's local statedir when network is defined or started in the meantime. Especially, I would like to get some advices for this. (I'm afraid that NETWORK_STATE_DIR is inappropriate as dnsmasq process will run as nobody and it will not be able to read the saved hostsfile.) Signed-off-by: Satoru SATOH <satoru.satoh@gmail.com> --- src/network/bridge_driver.c | 76 +++++++++++++++++++++++++++++++++--------- 1 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 83ab00e..65ffc8b 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -57,10 +57,13 @@ #include "iptables.h" #include "bridge.h" #include "logging.h" +#include "dnsmasq.h" #define NETWORK_PID_DIR LOCAL_STATE_DIR "/run/libvirt/network" #define NETWORK_STATE_DIR LOCAL_STATE_DIR "/lib/libvirt/network" +#define DNSMASQ_STATE_DIR LOCAL_STATE_DIR "/lib/dnsmasq" + #define VIR_FROM_THIS VIR_FROM_NETWORK #define networkReportError(code, ...) \ @@ -361,6 +364,29 @@ networkShutdown(void) { static int +networkSaveDnsmasqHostsfile(virNetworkObjPtr network, + dnsmasqContext *dnsmasqctx, + int force) +{ + int r; + + if (!force && virFileExists(dnsmasqctx->hostsfile->path)) + return 1; + + for (r = 0 ; r < network->def->nhosts ; r++) { + virNetworkDHCPHostDefPtr host = &(network->def->hosts[r]); + if ((host->mac) && (host->ip) && (host->name)) + dnsmasqAddDhcpHost(dnsmasqctx, host->mac, host->ip, host->name); + } + + if (! dnsmasqSave(dnsmasqctx)) + return 0; + + return 1; +} + + +static int networkBuildDnsmasqArgv(virNetworkObjPtr network, const char *pidfile, const char ***argv) { @@ -401,8 +427,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, (2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */ /* --dhcp-lease-max=xxx if needed */ (network->def->nranges ? 0 : 1) + - /* --dhcp-host 01:23:45:67:89:0a,hostname,10.0.0.3 */ - (2 * network->def->nhosts) + + /* --dhcp-hostsfile=/var/lib/libvirt/network/$netname.hostsfile */ + (network->def->nhosts > 0 ? 1 : 0) + /* --enable-tftp --tftp-root /srv/tftp */ (network->def->tftproot ? 3 : 0) + /* --dhcp-boot pxeboot.img[,,12.34.56.78] */ @@ -473,22 +499,20 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, APPEND_ARG(*argv, i++, buf); } - for (r = 0 ; r < network->def->nhosts ; r++) { - virNetworkDHCPHostDefPtr host = &(network->def->hosts[r]); - if ((host->mac) && (host->name)) { - snprintf(buf, sizeof(buf), "%s,%s,%s", - host->mac, host->name, host->ip); - } else if (host->mac) { - snprintf(buf, sizeof(buf), "%s,%s", - host->mac, host->ip); - } else if (host->name) { - snprintf(buf, sizeof(buf), "%s,%s", - host->name, host->ip); - } else - continue; + if (network->def->nhosts > 0) { + dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR); + char *hostsfileArg; - APPEND_ARG(*argv, i++, "--dhcp-host"); - APPEND_ARG(*argv, i++, buf); + if (dctx == NULL) + goto no_memory; + + if (networkSaveDnsmasqHostsfile(network, dctx, 0)) { + if (virAsprintf(&hostsfileArg, "--dhcp-hostsfile=%s", dctx->hostsfile->path) < 0) + goto no_memory; + APPEND_ARG_LIT(*argv, i++, hostsfileArg); + } + + dnsmasqContextFree(dctx); } if (network->def->tftproot) { @@ -1294,6 +1318,15 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) { goto cleanup; } + if (network->def->nhosts > 0) { + dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR); + if (dctx == NULL) + goto cleanup; + + networkSaveDnsmasqHostsfile(network, dctx, 1); + dnsmasqContextFree(dctx); + } + ret = virGetNetwork(conn, network->def->name, network->def->uuid); cleanup: @@ -1329,6 +1362,15 @@ static int networkUndefine(virNetworkPtr net) { network) < 0) goto cleanup; + if (network->def->nhosts > 0) { + dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR); + if (dctx == NULL) + goto cleanup; + + dnsmasqDelete(dctx); + dnsmasqContextFree(dctx); + } + virNetworkRemoveInactive(&driver->networks, network); network = NULL; -- 1.6.6.1
participants (1)
-
Satoru SATOH