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: [ { "expiry-time": "1390775837", "mac-address": "52:54:00:93:8c:63", "iaid": "*", "ip-address": "192.168.150.209", "hostname": "iit-ad885e4aa1", "client-id": "01:52:54:00:44:7c:d7" }, { "expiry-time": "1390775950", "mac-address": "52:54:00:7b:6f:ba", "iaid": "8089530", "ip-address": "2001:db8:ca2:2:1::6d", "hostname": "*", "client-id": "00:04:76:00:cf:ae:b3:0b:fc:cd:0e:22:2e:97:76:65:74:ec" } ] 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 --- v2: * Changed format to JSON v1: * Refer: https://www.redhat.com/archives/libvir-list/2014-January/msg00626.html src/Makefile.am | 20 ++++ src/network/bridge_driver.c | 19 ++++ src/util/leaseshelper.c | 271 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 src/util/leaseshelper.c diff --git a/src/Makefile.am b/src/Makefile.am index 7844efa..1fa6263 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -840,6 +840,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 \ @@ -2410,6 +2413,23 @@ libvirt_iohelper_CFLAGS = \ $(NULL) endif WITH_LIBVIRTD +if WITH_LIBVIRTD +libexec_PROGRAMS += libvirt_leaseshelper +libvirt_leaseshelper_SOURCES = $(UTIL_LEASES_HELPER_SOURCES) +libvirt_leaseshelper_LDFLAGS = \ + $(NULL) +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 if WITH_LIBVIRTD libexec_PROGRAMS += libvirt_parthelper diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 0b43a67..5440aef 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -148,6 +148,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; @@ -183,6 +193,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver, virNetworkObjPtr net) { char *leasefile = NULL; + char *customleasefile = NULL; char *radvdconfigfile = NULL; char *configfile = NULL; char *radvdpidbase = NULL; @@ -201,6 +212,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; @@ -217,6 +231,7 @@ networkRemoveInactive(virNetworkDriverStatePtr driver, /* dnsmasq */ dnsmasqDelete(dctx); unlink(leasefile); + unlink(customleasefile); unlink(configfile); /* radvd */ @@ -1063,6 +1078,10 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, cmd = virCommandNew(dnsmasqCapsGetBinaryPath(caps)); virCommandAddArgFormat(cmd, "--conf-file=%s", configfile); + + /* This helper is used to create cutom 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..a9aaf3f --- /dev/null +++ b/src/util/leaseshelper.c @@ -0,0 +1,271 @@ +/* + * leasehelper.c: Helper program to create custom leases file + * + * Copyright (C) 2013 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@xxxxxxxxx> + * + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.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 2097152 + +/* + * Use this when passing possibly-NULL strings to printf-a-likes. + */ +# define EMPTY_STR(s) ((s) ? (s) : "*") + +int +main(int argc, char **argv) { + + /* Doesn't hurt to check */ + if (argc < 4) { + /* Refer man page of dnsmasq --dhcp-script for more details */ + fprintf(stderr, "Usage: $program $action ${mac|clientid} $ip\n"); + return -1; + } + + char *lease_file = NULL; + char *lease_entries = NULL; + const char *ip = argv[3]; + const char *mac = argv[2]; + const char *action = argv[1]; + const char *program_name = argv[0]; + const char *iaid = EMPTY_STR(virGetEnvAllowSUID("DNSMASQ_IAID")); + const char *clientid = EMPTY_STR(virGetEnvAllowSUID("DNSMASQ_CLIENT_ID")); + const char *interface = EMPTY_STR(virGetEnvAllowSUID("DNSMASQ_INTERFACE")); + const char *exptime = EMPTY_STR(virGetEnvAllowSUID("DNSMASQ_LEASE_EXPIRES")); + const char *hostname = EMPTY_STR(virGetEnvAllowSUID("DNSMASQ_SUPPLIED_HOSTNAME")); + const char *leases_str = NULL; + size_t i = 0; + int rv = -1; + int size = 0; + int lease_file_len = 0; + FILE *fp = NULL; + bool add = false; + bool delete = false; + virJSONValuePtr lease_new; + virJSONValuePtr lease_tmp; + virJSONValuePtr leases_array; + virJSONValuePtr lease_new_tmp; + virJSONValuePtr leases_array_new; + + 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); + } + + if (virAsprintf(&lease_file, "%s/%s.status", LOCALSTATEDIR + "/lib/libvirt/dnsmasq/", interface) < 0) + goto cleanup; + + if (virGetEnvAllowSUID("DNSMASQ_IAID")) { + mac = EMPTY_STR(virGetEnvAllowSUID("DNSMASQ_MAC")); + clientid = argv[2]; + } + + /* Make sure dnsmasq knows the interface, otherwise something is wrong */ + if (STREQ(interface, "*")) + goto cleanup; + + /* Make sure the file exists. If not, 'touch' it */ + if (virFileTouch(lease_file, 0644) < 0) + goto cleanup; + + /* Read entire contents */ + if ((lease_file_len = virFileReadAll(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; + /* Enter new lease */ + if (!(lease_new = virJSONValueNewObject())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + if (virJSONValueObjectAppendString(lease_new, "expiry-time", + exptime) < 0 || + virJSONValueObjectAppendString(lease_new, "mac-address", + mac) < 0 || + virJSONValueObjectAppendString(lease_new, "iaid", + iaid) < 0 || + virJSONValueObjectAppendString(lease_new, "ip-address", + ip) < 0 || + virJSONValueObjectAppendString(lease_new, "hostname", + hostname) < 0 || + virJSONValueObjectAppendString(lease_new, "client-id", + clientid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + } + } + } + + /* Check for previous leases */ + if (lease_file_len) { + if (!(leases_array = virJSONValueFromString(lease_entries))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid json in file: %s"), 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; + const char *mac_tmp = NULL; + const char *iaid_tmp = NULL; + const char *exptime_tmp = NULL; + const char *clientid_tmp = NULL; + const char *hostname_tmp = NULL; + long long expirytime_tmp = 0; + + if (!(lease_tmp = virJSONValueArrayGet(leases_array, i))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + if (!(iaid_tmp = virJSONValueObjectGetString(lease_tmp, "iaid")) || + !(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address")) || + !(mac_tmp = virJSONValueObjectGetString(lease_tmp, "mac-address")) || + !(hostname_tmp = virJSONValueObjectGetString(lease_tmp, "hostname")) || + !(clientid_tmp = virJSONValueObjectGetString(lease_tmp, "client-id")) || + !(exptime_tmp = virJSONValueObjectGetString(lease_tmp, "expiry-time"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + if (virStrToLong_ll(exptime_tmp, NULL, 10, &expirytime_tmp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to convert lease expiry time to integer: %s"), + exptime_tmp); + goto cleanup; + } + + /* Check whether lease has expired or not */ + if (expirytime_tmp < (long long) time(NULL)) + continue; + else if (delete && STREQ(ip_tmp, ip)) + continue; + else { + if (!(lease_new_tmp = virJSONValueNewObject())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + if (virJSONValueObjectAppendString(lease_new_tmp, "expiry-time", + exptime_tmp) < 0 || + virJSONValueObjectAppendString(lease_new_tmp, "mac-address", + mac_tmp) < 0 || + virJSONValueObjectAppendString(lease_new_tmp, "iaid", + iaid_tmp) < 0 || + virJSONValueObjectAppendString(lease_new_tmp, "ip-address", + ip_tmp) < 0 || + virJSONValueObjectAppendString(lease_new_tmp, "hostname", + hostname_tmp) < 0 || + virJSONValueObjectAppendString(lease_new_tmp, "client-id", + clientid_tmp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + if (virJSONValueArrayAppend(leases_array_new, lease_new_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; + } + } + + rv = 0; + + /* Write to file */ + leases_str = virJSONValueToString(leases_array_new, true); + if (!leases_str) + leases_str = ""; + + if (virFileWriteStr(lease_file, leases_str, 0) < 0) + rv = -1; + +cleanup: + VIR_FREE(lease_file); + virJSONValueFree(lease_new); + virJSONValueFree(leases_array); + virJSONValueFree(lease_new_tmp); + virJSONValueFree(leases_array_new); + return rv; +} -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list