[libvirt] [PATCH v3] Add helper program to create custom leases

Introduce helper program to catch events from dnsmasq and maintain a custom lease file per network. It supports dhcpv4 and dhcpv6. The file is saved as "<interface-name>.status". Each lease contains the following info: <expiry-time (epoch time)> <mac> <iaid> <ip-address> <hostname> <clientid> Example of custom leases file content: [ { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 }, { "ip-address": "192.168.150.208", "mac-address": "52:54:00:11:56:b3", "hostname": "Wani-PC", "client-id": "01:52:54:00:11:56:b3", "expiry-time": 1393244248 } ] src/Makefile.am: * Add options to compile the helper program src/network/bridge_driver.c: * Introduce networkDnsmasqLeaseFileNameCustom() * Invoke helper program along with dnsmasq * Delete the .status file when corresponding n/w is destroyed. src/util/leaseshelper.c * Helper program to create the custom lease file --- v3: * Improved file handling, removed redundant copying, introduced --help and --version v2: * Changed format to JSON * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg01234.html v1: * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg00626.html src/Makefile.am | 16 +++ src/network/bridge_driver.c | 19 +++ src/util/leaseshelper.c | 303 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+), 0 deletions(-) create mode 100644 src/util/leaseshelper.c diff --git a/src/Makefile.am b/src/Makefile.am index 6d21e5d..b8e1993 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -849,6 +849,9 @@ STORAGE_HELPER_DISK_SOURCES = \ UTIL_IO_HELPER_SOURCES = \ util/iohelper.c +UTIL_LEASES_HELPER_SOURCES = \ + util/leaseshelper.c + # Network filters NWFILTER_DRIVER_SOURCES = \ nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \ @@ -2444,6 +2447,19 @@ libvirt_iohelper_CFLAGS = \ $(AM_CFLAGS) \ $(PIE_CFLAGS) \ $(NULL) + +libexec_PROGRAMS += libvirt_leaseshelper +libvirt_leaseshelper_SOURCES = $(UTIL_LEASES_HELPER_SOURCES) +libvirt_leaseshelper_LDADD = \ + libvirt_util.la \ + ../gnulib/lib/libgnu.la +if WITH_DTRACE_PROBES +libvirt_leaseshelper_LDADD += libvirt_probes.lo +endif WITH_DTRACE_PROBES + +libvirt_leaseshelper_CFLAGS = \ + $(PIE_CFLAGS) \ + $(NULL) endif WITH_LIBVIRTD if WITH_STORAGE_DISK diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index a6c719d..9fb750f 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -205,6 +205,16 @@ networkDnsmasqLeaseFileNameFunc networkDnsmasqLeaseFileName = networkDnsmasqLeaseFileNameDefault; static char * +networkDnsmasqLeaseFileNameCustom(const char *bridge) +{ + char *leasefile; + + ignore_value(virAsprintf(&leasefile, "%s/%s.status", + driverState->dnsmasqStateDir, bridge)); + return leasefile; +} + +static char * networkDnsmasqConfigFileName(const char *netname) { char *conffile; @@ -240,6 +250,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver, virNetworkObjPtr net) { char *leasefile = NULL; + char *customleasefile = NULL; char *radvdconfigfile = NULL; char *configfile = NULL; char *radvdpidbase = NULL; @@ -258,6 +269,9 @@ networkRemoveInactive(virNetworkDriverStatePtr driver, if (!(leasefile = networkDnsmasqLeaseFileName(def->name))) goto cleanup; + if (!(customleasefile = networkDnsmasqLeaseFileNameCustom(def->bridge))) + goto cleanup; + if (!(radvdconfigfile = networkRadvdConfigFileName(def->name))) goto cleanup; @@ -274,6 +288,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver, /* dnsmasq */ dnsmasqDelete(dctx); unlink(leasefile); + unlink(customleasefile); unlink(configfile); /* radvd */ @@ -1117,6 +1132,10 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, cmd = virCommandNew(dnsmasqCapsGetBinaryPath(caps)); virCommandAddArgFormat(cmd, "--conf-file=%s", configfile); + + /* This helper is used to create custom leases file for libvirt */ + virCommandAddArgFormat(cmd, "--dhcp-script=%s", LIBEXECDIR "/libvirt_leaseshelper"); + *cmdout = cmd; ret = 0; cleanup: diff --git a/src/util/leaseshelper.c b/src/util/leaseshelper.c new file mode 100644 index 0000000..bd8110f --- /dev/null +++ b/src/util/leaseshelper.c @@ -0,0 +1,303 @@ +/* + * leasehelper.c: Helper program to create custom leases file + * + * Copyright (C) 2014 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Nehal J Wani <nehaljw.kkd1@gmail.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> + +#include "virutil.h" +#include "virthread.h" +#include "virfile.h" +#include "virbuffer.h" +#include "virstring.h" +#include "virerror.h" +#include "viralloc.h" +#include "virjson.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_NETWORK + +/** + * VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX: + * + * Macro providing the upper limit on the size of leases file + */ +#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (2 * 1024 * 1024) + +static const char *program_name; + +/* Display version information. */ +static void +helperVersion(const char *argv0) +{ + printf("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION); +} + +ATTRIBUTE_NORETURN static void +usage(int status) +{ + if (status) { + fprintf(stderr, _("%s: try --help for more details\n"), program_name); + } else { + printf(_("Usage: %s ACTION MAC|CLIENTID IP HOSTNAME\n" + " or: %s ACTION MAC|CLIENTID IP\n"), + program_name, program_name); + } + exit(status); +} + +static int +customLeaseRewriteFile(int fd, void *opaque) +{ + char **data = opaque; + + if (safewrite(fd, *data, strlen(*data)) < 0) + return -1; + + return 0; +} + +int +main(int argc, char **argv) +{ + char *lease_entries = NULL; + char *custom_lease_file = NULL; + const char *ip = NULL; + const char *mac = NULL; + const char *action = NULL; + const char *iaid = virGetEnvAllowSUID("DNSMASQ_IAID"); + const char *clientid = virGetEnvAllowSUID("DNSMASQ_CLIENT_ID"); + const char *interface = virGetEnvAllowSUID("DNSMASQ_INTERFACE"); + const char *exptime = virGetEnvAllowSUID("DNSMASQ_LEASE_EXPIRES"); + const char *hostname = virGetEnvAllowSUID("DNSMASQ_SUPPLIED_HOSTNAME"); + const char *leases_str = NULL; + long long expirytime = 0; + size_t i = 0; + int rv = EXIT_FAILURE; + int size = 0; + int custom_lease_file_len = 0; + bool add = false; + bool delete = false; + virJSONValuePtr lease_new = NULL; + virJSONValuePtr lease_tmp = NULL; + virJSONValuePtr leases_array = NULL; + virJSONValuePtr leases_array_new = NULL; + + virSetErrorFunc(NULL, NULL); + virSetErrorLogPriorityFunc(NULL); + + program_name = argv[0]; + + if (setlocale(LC_ALL, "") == NULL || + bindtextdomain(PACKAGE, LOCALEDIR) == NULL || + textdomain(PACKAGE) == NULL) { + fprintf(stderr, _("%s: initialization failed\n"), program_name); + exit(EXIT_FAILURE); + } + + if (virThreadInitialize() < 0 || + virErrorInitialize() < 0) { + fprintf(stderr, _("%s: initialization failed\n"), program_name); + exit(EXIT_FAILURE); + } + + /* Doesn't hurt to check */ + if (argc > 1) { + if(STREQ(argv[1], "--help")) + usage(EXIT_SUCCESS); + + if (STREQ(argv[1], "--version")) { + helperVersion(argv[0]); + exit(EXIT_SUCCESS); + } + } + + if (argc != 4 && argc != 5) { + /* Refer man page of dnsmasq --dhcp-script for more details */ + usage(EXIT_FAILURE); + } + + /* Make sure dnsmasq knows the interface. The interface name is not known + * when dnsmasq (re)starts and throws 'del' events for expired leases. + * So, if any old lease has expired, it will be automatically removed the + * next time this program is invoked */ + if (!interface) + goto cleanup; + + ip = argv[3]; + mac = argv[2]; + action = argv[1]; + + /* In case hostname is known, it is the 5th argument */ + if (argc == 5) + hostname = argv[4]; + + if (virAsprintf(&custom_lease_file, "%s/%s.status", LOCALSTATEDIR + "/lib/libvirt/dnsmasq/", interface) < 0) + goto cleanup; + + /* Check if it is an IPv6 lease */ + if (virGetEnvAllowSUID("DNSMASQ_IAID")) { + mac = virGetEnvAllowSUID("DNSMASQ_MAC"); + clientid=argv[2]; + } + + /* Since interfaces can be hot plugged, we need to make sure that the + * corresponding custom lease file exists. If not, 'touch' it */ + if (virFileTouch(custom_lease_file, 0644) < 0) + goto cleanup; + + /* Read entire contents */ + if ((custom_lease_file_len = virFileReadAll(custom_lease_file, + VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX, + &lease_entries)) < 0) { + goto cleanup; + } + + if (STREQ(action, "add") || STREQ(action, "old") || STREQ(action, "del")) { + if (mac || STREQ(action, "del")) { + /* Delete the corresponding lease */ + delete = true; + if (STREQ(action, "add") || STREQ(action, "old")) { + add = true; + /* Create new lease */ + if (!(lease_new = virJSONValueNewObject())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + + if (virStrToLong_ll(exptime, NULL, 10, &expirytime) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to convert lease expiry time to long long: %s"), + exptime); + goto cleanup; + } + + if ((iaid && virJSONValueObjectAppendString(lease_new, "iaid", + iaid) < 0) || + (ip && virJSONValueObjectAppendString(lease_new, "ip-address", + ip) < 0) || + (mac && virJSONValueObjectAppendString(lease_new, "mac-address", + mac) < 0) || + (hostname && virJSONValueObjectAppendString(lease_new, "hostname", + hostname) < 0) || + (clientid && virJSONValueObjectAppendString(lease_new, "client-id", + clientid) < 0) || + (expirytime && virJSONValueObjectAppendNumberLong(lease_new, "expiry-time", + expirytime) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + } + } + else { + fprintf(stderr, _("Unsupported action: %s\n"), action); + exit(EXIT_FAILURE); + } + + /* Check for previous leases */ + if (custom_lease_file_len) { + if (!(leases_array = virJSONValueFromString(lease_entries))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid json in file: %s"), custom_lease_file); + goto cleanup; + } + + if ((size = virJSONValueArraySize(leases_array)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("couldn't fetch array of leases")); + goto cleanup; + } + } + + if (!(leases_array_new = virJSONValueNewArray())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + + for (i = 0; i < size; i++) { + const char *ip_tmp = NULL; + long long exptime_tmp = -1; + + if (!(lease_tmp = virJSONValueArrayGet(leases_array, i))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address")) || + (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", &exptime_tmp) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + /* Check whether lease has expired or not */ + if (exptime_tmp < (long long) time(NULL)) + continue; + + /* Check whether lease has to be included or not */ + if (delete && STREQ(ip_tmp, ip)) + continue; + + /* Add old lease to new array */ + if (virJSONValueArrayAppend(leases_array_new, lease_tmp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + + if (add) { + if (virJSONValueArrayAppend(leases_array_new, lease_new) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + + if (!(leases_str = virJSONValueToString(leases_array_new, true))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("empty json array")); + goto cleanup; + } + + /* Write to file */ + if (virFileRewrite(custom_lease_file, 0644, + customLeaseRewriteFile, &leases_str) < 0) + goto cleanup; + + rv = EXIT_SUCCESS; + +cleanup: + VIR_FREE(custom_lease_file); + virJSONValueFree(lease_new); + virJSONValueFree(leases_array); + virJSONValueFree(leases_array_new); + return rv; +} -- 1.7.1

On Mon, Feb 24, 2014 at 5:27 PM, Nehal J Wani <nehaljw.kkd1@gmail.com> wrote:
Introduce helper program to catch events from dnsmasq and maintain a custom lease file per network. It supports dhcpv4 and dhcpv6. The file is saved as "<interface-name>.status".
Each lease contains the following info: <expiry-time (epoch time)> <mac> <iaid> <ip-address> <hostname> <clientid>
Example of custom leases file content: [ { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 }, { "ip-address": "192.168.150.208", "mac-address": "52:54:00:11:56:b3", "hostname": "Wani-PC", "client-id": "01:52:54:00:11:56:b3", "expiry-time": 1393244248 } ]
src/Makefile.am: * Add options to compile the helper program
src/network/bridge_driver.c: * Introduce networkDnsmasqLeaseFileNameCustom() * Invoke helper program along with dnsmasq * Delete the .status file when corresponding n/w is destroyed.
src/util/leaseshelper.c * Helper program to create the custom lease file
--- v3: * Improved file handling, removed redundant copying, introduced --help and --version
v2: * Changed format to JSON * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg01234.html
v1: * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg00626.html
src/Makefile.am | 16 +++ src/network/bridge_driver.c | 19 +++ src/util/leaseshelper.c | 303 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+), 0 deletions(-) create mode 100644 src/util/leaseshelper.c
Ping!

On Tue, Mar 4, 2014 at 12:18 AM, Nehal J Wani <nehaljw.kkd1@gmail.com> wrote:
On Mon, Feb 24, 2014 at 5:27 PM, Nehal J Wani <nehaljw.kkd1@gmail.com> wrote:
Introduce helper program to catch events from dnsmasq and maintain a custom lease file per network. It supports dhcpv4 and dhcpv6. The file is saved as "<interface-name>.status".
Each lease contains the following info: <expiry-time (epoch time)> <mac> <iaid> <ip-address> <hostname> <clientid>
Example of custom leases file content: [ { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 }, { "ip-address": "192.168.150.208", "mac-address": "52:54:00:11:56:b3", "hostname": "Wani-PC", "client-id": "01:52:54:00:11:56:b3", "expiry-time": 1393244248 } ]
src/Makefile.am: * Add options to compile the helper program
src/network/bridge_driver.c: * Introduce networkDnsmasqLeaseFileNameCustom() * Invoke helper program along with dnsmasq * Delete the .status file when corresponding n/w is destroyed.
src/util/leaseshelper.c * Helper program to create the custom lease file
--- v3: * Improved file handling, removed redundant copying, introduced --help and --version
v2: * Changed format to JSON * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg01234.html
v1: * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg00626.html
src/Makefile.am | 16 +++ src/network/bridge_driver.c | 19 +++ src/util/leaseshelper.c | 303 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+), 0 deletions(-) create mode 100644 src/util/leaseshelper.c
Ping!
Ping! -- Nehal J Wani

On Mon, Feb 24, 2014 at 05:27:14PM +0530, Nehal J Wani wrote:
Introduce helper program to catch events from dnsmasq and maintain a custom lease file per network. It supports dhcpv4 and dhcpv6. The file is saved as "<interface-name>.status".
Each lease contains the following info: <expiry-time (epoch time)> <mac> <iaid> <ip-address> <hostname> <clientid>
Example of custom leases file content: [ { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 }, { "ip-address": "192.168.150.208", "mac-address": "52:54:00:11:56:b3", "hostname": "Wani-PC", "client-id": "01:52:54:00:11:56:b3", "expiry-time": 1393244248 } ]
diff --git a/src/Makefile.am b/src/Makefile.am index 6d21e5d..b8e1993 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -849,6 +849,9 @@ STORAGE_HELPER_DISK_SOURCES = \ UTIL_IO_HELPER_SOURCES = \ util/iohelper.c
+UTIL_LEASES_HELPER_SOURCES = \ + util/leaseshelper.c
I'd suggest this should go into the 'network' directory rather than 'util' since this is only for use by the network driver, and also call the variable NETWORK_LEASES_HELPER_SOURCES
+ # Network filters NWFILTER_DRIVER_SOURCES = \ nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \ @@ -2444,6 +2447,19 @@ libvirt_iohelper_CFLAGS = \ $(AM_CFLAGS) \ $(PIE_CFLAGS) \ $(NULL) + +libexec_PROGRAMS += libvirt_leaseshelper +libvirt_leaseshelper_SOURCES = $(UTIL_LEASES_HELPER_SOURCES) +libvirt_leaseshelper_LDADD = \ + libvirt_util.la \ + ../gnulib/lib/libgnu.la +if WITH_DTRACE_PROBES +libvirt_leaseshelper_LDADD += libvirt_probes.lo +endif WITH_DTRACE_PROBES + +libvirt_leaseshelper_CFLAGS = \ + $(PIE_CFLAGS) \ + $(NULL)
This whole block needs to be surround in 'if WITH_NETWORK' and in the else clause add UTIL_LEASES_HELPER_SOURCES to EXTRA_DIST
diff --git a/src/util/leaseshelper.c b/src/util/leaseshelper.c new file mode 100644 index 0000000..bd8110f --- /dev/null +++ b/src/util/leaseshelper.c
+/** + * VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX: + * + * Macro providing the upper limit on the size of leases file + */ +#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (2 * 1024 * 1024)
Do you think this is large enough ? Lets imagine a case of 65,000 leases - will they all fit in 2 MB given the JSON formatting we're doing ?
+static int +customLeaseRewriteFile(int fd, void *opaque) +{ + char **data = opaque; + + if (safewrite(fd, *data, strlen(*data)) < 0) + return -1; + + return 0; +} + +int +main(int argc, char **argv) +{ + char *lease_entries = NULL; + char *custom_lease_file = NULL; + const char *ip = NULL; + const char *mac = NULL; + const char *action = NULL; + const char *iaid = virGetEnvAllowSUID("DNSMASQ_IAID"); + const char *clientid = virGetEnvAllowSUID("DNSMASQ_CLIENT_ID"); + const char *interface = virGetEnvAllowSUID("DNSMASQ_INTERFACE"); + const char *exptime = virGetEnvAllowSUID("DNSMASQ_LEASE_EXPIRES"); + const char *hostname = virGetEnvAllowSUID("DNSMASQ_SUPPLIED_HOSTNAME"); + const char *leases_str = NULL; + long long expirytime = 0; + size_t i = 0; + int rv = EXIT_FAILURE; + int size = 0; + int custom_lease_file_len = 0; + bool add = false; + bool delete = false; + virJSONValuePtr lease_new = NULL; + virJSONValuePtr lease_tmp = NULL; + virJSONValuePtr leases_array = NULL; + virJSONValuePtr leases_array_new = NULL; + + virSetErrorFunc(NULL, NULL); + virSetErrorLogPriorityFunc(NULL); + + program_name = argv[0]; + + if (setlocale(LC_ALL, "") == NULL || + bindtextdomain(PACKAGE, LOCALEDIR) == NULL || + textdomain(PACKAGE) == NULL) { + fprintf(stderr, _("%s: initialization failed\n"), program_name); + exit(EXIT_FAILURE); + } + + if (virThreadInitialize() < 0 || + virErrorInitialize() < 0) { + fprintf(stderr, _("%s: initialization failed\n"), program_name); + exit(EXIT_FAILURE); + } + + /* Doesn't hurt to check */ + if (argc > 1) { + if(STREQ(argv[1], "--help")) + usage(EXIT_SUCCESS); + + if (STREQ(argv[1], "--version")) { + helperVersion(argv[0]); + exit(EXIT_SUCCESS); + } + } + + if (argc != 4 && argc != 5) { + /* Refer man page of dnsmasq --dhcp-script for more details */ + usage(EXIT_FAILURE); + } + + /* Make sure dnsmasq knows the interface. The interface name is not known + * when dnsmasq (re)starts and throws 'del' events for expired leases. + * So, if any old lease has expired, it will be automatically removed the + * next time this program is invoked */ + if (!interface) + goto cleanup; + + ip = argv[3]; + mac = argv[2]; + action = argv[1]; + + /* In case hostname is known, it is the 5th argument */ + if (argc == 5) + hostname = argv[4];
Looks like a '<tab>' character in the source - use 'make syntax-check' to avoid that.
+ + if (virAsprintf(&custom_lease_file, "%s/%s.status", LOCALSTATEDIR + "/lib/libvirt/dnsmasq/", interface) < 0) + goto cleanup;
I think that before we do anything to read/write the lease file it is probably a wise precaution to acquire a lock on a pidfile. Our current pidfile acquire APis (virPidFileAcquire) will simply return -1 upon failure to acquire. What we want though is to make it block and wait for the lock. So you'll need to add an extra parameter to virPidFileAcquire to say whether to block or not. Then just add if ( virPidFileAcquire(....) < 0) goto cleanup;
+ + /* Check if it is an IPv6 lease */ + if (virGetEnvAllowSUID("DNSMASQ_IAID")) { + mac = virGetEnvAllowSUID("DNSMASQ_MAC"); + clientid=argv[2]; + } + + /* Since interfaces can be hot plugged, we need to make sure that the + * corresponding custom lease file exists. If not, 'touch' it */ + if (virFileTouch(custom_lease_file, 0644) < 0) + goto cleanup; + + /* Read entire contents */ + if ((custom_lease_file_len = virFileReadAll(custom_lease_file, + VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX, + &lease_entries)) < 0) { + goto cleanup; + } + + if (STREQ(action, "add") || STREQ(action, "old") || STREQ(action, "del")) { + if (mac || STREQ(action, "del")) { + /* Delete the corresponding lease */ + delete = true; + if (STREQ(action, "add") || STREQ(action, "old")) { + add = true; + /* Create new lease */ + if (!(lease_new = virJSONValueNewObject())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + + if (virStrToLong_ll(exptime, NULL, 10, &expirytime) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to convert lease expiry time to long long: %s"), + exptime); + goto cleanup; + } + + if ((iaid && virJSONValueObjectAppendString(lease_new, "iaid", + iaid) < 0) || + (ip && virJSONValueObjectAppendString(lease_new, "ip-address", + ip) < 0) || + (mac && virJSONValueObjectAppendString(lease_new, "mac-address", + mac) < 0) || + (hostname && virJSONValueObjectAppendString(lease_new, "hostname", + hostname) < 0) || + (clientid && virJSONValueObjectAppendString(lease_new, "client-id", + clientid) < 0) || + (expirytime && virJSONValueObjectAppendNumberLong(lease_new, "expiry-time", + expirytime) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + } + } + else {
Put 'else {' on the same line as }.
+ fprintf(stderr, _("Unsupported action: %s\n"), action); + exit(EXIT_FAILURE); + } + + /* Check for previous leases */ + if (custom_lease_file_len) { + if (!(leases_array = virJSONValueFromString(lease_entries))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid json in file: %s"), custom_lease_file); + goto cleanup; + } + + if ((size = virJSONValueArraySize(leases_array)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("couldn't fetch array of leases")); + goto cleanup; + } + } + + if (!(leases_array_new = virJSONValueNewArray())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + + for (i = 0; i < size; i++) { + const char *ip_tmp = NULL; + long long exptime_tmp = -1; + + if (!(lease_tmp = virJSONValueArrayGet(leases_array, i))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address")) || + (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", &exptime_tmp) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + /* Check whether lease has expired or not */ + if (exptime_tmp < (long long) time(NULL)) + continue; + + /* Check whether lease has to be included or not */ + if (delete && STREQ(ip_tmp, ip)) + continue; + + /* Add old lease to new array */ + if (virJSONValueArrayAppend(leases_array_new, lease_tmp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + + if (add) { + if (virJSONValueArrayAppend(leases_array_new, lease_new) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + + if (!(leases_str = virJSONValueToString(leases_array_new, true))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("empty json array")); + goto cleanup; + } + + /* Write to file */ + if (virFileRewrite(custom_lease_file, 0644, + customLeaseRewriteFile, &leases_str) < 0) + goto cleanup; + + rv = EXIT_SUCCESS; + +cleanup: + VIR_FREE(custom_lease_file); + virJSONValueFree(lease_new); + virJSONValueFree(leases_array); + virJSONValueFree(leases_array_new); + return rv; +}
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

+/** + * VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX: + * + * Macro providing the upper limit on the size of leases file + */ +#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (2 * 1024 * 1024)
Do you think this is large enough ? Lets imagine a case of 65,000 leases - will they all fit in 2 MB given the JSON formatting we're doing ?
The following JSON formatted lease takes 274 bytes: { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 }, So, assuming an upper limit of 512 bytes for each such entry, is it safe to change the above limit to 32 MB?
+ /* In case hostname is known, it is the 5th argument */ + if (argc == 5) + hostname = argv[4];
Looks like a '<tab>' character in the source - use 'make syntax-check' to avoid that.
I did run make syntax-check, but it didn't throw any error. Maybe some hacks will be required to enable checks on this source file. -- Nehal J Wani

On Thu, Mar 13, 2014 at 04:45:35PM +0530, Nehal J Wani wrote:
+/** + * VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX: + * + * Macro providing the upper limit on the size of leases file + */ +#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (2 * 1024 * 1024)
Do you think this is large enough ? Lets imagine a case of 65,000 leases - will they all fit in 2 MB given the JSON formatting we're doing ?
The following JSON formatted lease takes 274 bytes: { "iaid": "1221229", "ip-address": "2001:db8:ca2:2:1::95", "mac-address": "52:54:00:12:a2:6d", "hostname": "Fedora20", "client-id": "00:04:1a:c1:d9:6b:5a:0a:e2:bc:f8:4b:1e:37:2e:38:22:55", "expiry-time": 1393244216 },
So, assuming an upper limit of 512 bytes for each such entry, is it safe to change the above limit to 32 MB?
Yep, if they're running so many VMs on a host, then they must have a lot of RAM so 32mb will be tiny by comparison. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Nehal J Wani