Introduce 3 new APIs, virNetworkGetDHCPLeases, virNetworkGetDHCPLeasesForMAC and virNetworkDHCPLeaseFree. * virNetworkGetDHCPLeases: returns the dhcp leases information for a given virtual network. For DHCPv4, the information returned: - Network Interface Name - Expiry Time - MAC address (can be NULL, only in rare cases) - IAID (NULL) - IPv4 address (with type and prefix) - Hostname (can be NULL) - Client ID (can be NULL) For DHCPv6, the information returned: - Network Interface Name - Expiry Time - MAC address (can be NULL, only in rare cases) - IAID (can be NULL, only in rare cases) - IPv6 address (with type and prefix) - Hostname (can be NULL) - Client DUID Note: @mac, @iaid, @ipaddr, @clientid are in ASCII form, not raw bytes. Note: @expirytime can 0, in case the lease is for infinite time. * virNetworkGetDHCPLeasesForMAC: returns the dhcp leases information for a given virtual network and specified MAC Address. * virNetworkDHCPLeaseFree: allows the upper layer application to free the network interface object conveniently. There is no support for flags, so user is expected to pass 0 for both the APIs. include/libvirt/libvirt.h.in: * Define virNetworkGetDHCPLeases * Define virNetworkGetDHCPLeasesForMAC * Define virNetworkDHCPLeaseFree src/driver.h: * Define networkGetDHCPLeases * Define networkGetDHCPLeasesForMAC src/libvirt.c: * Implement virNetworkGetDHCPLeases * Implement virNetworkGetDHCPLeasesForMAC * Implement virNetworkDHCPLeaseFree src/libvirt_public.syms: * Export the new symbols src/util/leaseshelper.c: * Helper program to create custom leases file src/Makefile.am: * Add compiler flags for the helper file leaseshelper.c * Add virmacaddr.c to libvirt_setuid_rpc_client_la_SOURCES --- include/libvirt/libvirt.h.in | 35 ++++++++ src/Makefile.am | 21 +++++ src/driver.h | 13 +++ src/libvirt.c | 197 +++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 7 ++ src/util/leaseshelper.c | 185 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 458 insertions(+) create mode 100644 src/util/leaseshelper.c diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5aad75c..20ea40a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2826,6 +2826,41 @@ int virConnectNumOfDefinedInterfaces (virConnectPtr conn); int virConnectListDefinedInterfaces (virConnectPtr conn, char **const names, int maxnames); + +typedef enum { + VIR_IP_ADDR_TYPE_IPV4, + VIR_IP_ADDR_TYPE_IPV6, + +#ifdef VIR_ENUM_SENTINELS + VIR_IP_ADDR_TYPE_LAST +#endif +} virIPAddrType; + +typedef struct _virNetworkDHCPLeases virNetworkDHCPLeases; +typedef virNetworkDHCPLeases *virNetworkDHCPLeasesPtr; +struct _virNetworkDHCPLeases { + char *interface; /* Network interface name */ + long long expirytime; /* Seconds since epoch */ + int type; /* virIPAddrType */ + char *mac; /* MAC address */ + char *iaid; /* IAID */ + char *ipaddr; /* IP address */ + unsigned int prefix; /* IP address prefix */ + char *hostname; /* Hostname */ + char *clientid; /* Client ID or DUID */ +}; + +void virNetworkDHCPLeaseFree(virNetworkDHCPLeasesPtr lease); + +int virNetworkGetDHCPLeases(virNetworkPtr network, + virNetworkDHCPLeasesPtr **leases, + unsigned int flags); + +int virNetworkGetDHCPLeasesForMAC(virNetworkPtr network, + const char *mac, + virNetworkDHCPLeasesPtr **leases, + unsigned int flags); + /* * virConnectListAllInterfaces: * diff --git a/src/Makefile.am b/src/Makefile.am index 87f5101..772171c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -829,6 +829,9 @@ STORAGE_HELPER_DISK_SOURCES = \ UTIL_IO_HELPER_SOURCES = \ util/iohelper.c +UTIL_IO_LEASES_SOURCES = \ + util/leaseshelper.c + # Network filters NWFILTER_DRIVER_SOURCES = \ nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \ @@ -2019,6 +2022,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \ util/virtypedparam.c \ util/viruri.c \ util/virutil.c \ + util/virmacaddr.c \ util/viruuid.c \ conf/domain_event.c \ rpc/virnetsocket.c \ @@ -2397,6 +2401,23 @@ libvirt_iohelper_CFLAGS = \ $(NULL) endif WITH_LIBVIRTD +if WITH_LIBVIRTD +libexec_PROGRAMS += libvirt_leaseshelper +libvirt_leaseshelper_SOURCES = $(UTIL_IO_LEASES_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/driver.h b/src/driver.h index 8cd164a..e000b17 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1127,6 +1127,17 @@ typedef int int cookieinlen, unsigned int flags, int cancelled); +typedef int +(*virDrvNetworkGetDHCPLeases)(virNetworkPtr network, + virNetworkDHCPLeasesPtr **leases, + unsigned int flags); + +typedef int +(*virDrvNetworkGetDHCPLeasesForMAC)(virNetworkPtr network, + const char *mac, + virNetworkDHCPLeasesPtr **leases, + unsigned int flags); + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr; @@ -1458,6 +1469,8 @@ struct _virNetworkDriver { virDrvNetworkSetAutostart networkSetAutostart; virDrvNetworkIsActive networkIsActive; virDrvNetworkIsPersistent networkIsPersistent; + virDrvNetworkGetDHCPLeases networkGetDHCPLeases; + virDrvNetworkGetDHCPLeasesForMAC networkGetDHCPLeasesForMAC; }; diff --git a/src/libvirt.c b/src/libvirt.c index eff44eb..9a0b872 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -68,6 +68,7 @@ #include "virstring.h" #include "virutil.h" #include "virtypedparam.h" +#include "virmacaddr.h" #ifdef WITH_TEST # include "test/test_driver.h" @@ -22051,3 +22052,199 @@ error: virDispatchError(dom->conn); return -1; } + +/** + * virNetworkGetDHCPLeases: + * @network: Pointer to network object + * @leases: Pointer to a variable to store the array containing details on + * obtained leases, or NULL if the list is not required (just returns + * number of leases). + * @flags: Extra flags, not used yet, so callers should always pass 0 + * + * For DHCPv4, the information returned: + * - Network Interface Name + * - Expiry Time + * - MAC address (can be NULL, only in rare cases) + * - IAID (NULL) + * - IPv4 address (with type and prefix) + * - Hostname (can be NULL) + * - Client ID (can be NULL) + * + * For DHCPv6, the information returned: + * - Network Interface Name + * - Expiry Time + * - MAC address (can be NULL, only in rare cases) + * - IAID (can be NULL, only in rare cases) + * - IPv6 address (with type and prefix) + * - Hostname (can be NULL) + * - Client DUID + * + * Note: @mac, @iaid, @ipaddr, @clientid are in ASCII form, not raw bytes. + * Note: @expirytime can 0, in case the lease is for infinite time. + * + * Returns the number of leases found or -1 and sets @leases to NULL in + * case of error. On success, the array stored into @leases is guaranteed to + * have an extra allocated element set to NULL but not included in the return + * count, to make iteration easier. The caller is responsible for calling + * virNetworkDHCPLeaseFree() on each array element, then calling free() on @leases. + * + * See also virNetworkGetDHCPLeasesForMAC() as a convenience for filtering + * the list to a single MAC address. + * + * Example of usage: + * + * virNetworkDHCPLeasesPtr *leases = NULL; + * virNetworkPtr network = ... obtain a network pointer here ...; + * size_t i; + * int nleases; + * unsigned int flags = 0; + * + * nleases = virNetworkGetDHCPLeases(network, &leases, flags); + * if (nleases < 0) + * error(); + * + * ... do something with returned values, for example: + * + * for (i = 0; i < nleases; i++) { + * virNetworkDHCPLeasesPtr lease = leases[i]; + * + * printf("Time(epoch): %lu, MAC address: %s, " + * "IP address: %s, Hostname: %s, ClientID: %s\n", + * leas->expirytime, lease->mac, lease->ipaddr, + * lease->hostname, lease->clientid); + * + * virNetworkDHCPLeaseFree(leases[i]); + * } + * + * free(leases); + * + */ +int +virNetworkGetDHCPLeases(virNetworkPtr network, + virNetworkDHCPLeasesPtr **leases, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("network=%p, leases=%p, flags=%x", + network, leases, flags); + + virResetLastError(); + + if (leases) + *leases = NULL; + + if (!VIR_IS_CONNECTED_NETWORK(network)) { + virLibNetworkError(VIR_ERR_INVALID_NETWORK, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = network->conn; + + if (conn->networkDriver && conn->networkDriver->networkGetDHCPLeases) { + int ret; + ret = conn->networkDriver->networkGetDHCPLeases(network, leases, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(network->conn); + return -1; +} + +/** + * virNetworkGetDHCPLeasesForMAC: + * @network: Pointer to network object + * @mac: ASCII formatted MAC address of an interface + * @leases: Pointer to a variable to store the array containing details on + * obtained leases, or NULL if the list is not required (just returns + * number of leases). + * @flags: extra flags, not used yet, so callers should always pass 0 + * + * The API fetches leases info of the interface which matches with the + * given @mac. There can be multiple leases for a single @mac because this + * API supports DHCPv6 too. + * + * Returns the number of leases found or -1 and sets @leases to NULL in case of + * error. On success, the array stored into @leases is guaranteed to have an + * extra allocated element set to NULL but not included in the return count, + * to make iteration easier. The caller is responsible for calling + * virNetworkDHCPLeaseFree() on each array element, then calling free() on @leases. + * + * See virNetworkGetDHCPLeases() for more details on list contents. + */ +int +virNetworkGetDHCPLeasesForMAC(virNetworkPtr network, + const char *mac, + virNetworkDHCPLeasesPtr **leases, + unsigned int flags) +{ + virConnectPtr conn; + virMacAddr addr; + + VIR_DEBUG("network=%p, mac=%s, leases=%p, flags=%x", + network, mac, leases, flags); + + virResetLastError(); + + if (leases) + *leases = NULL; + + virCheckNonNullArgGoto(mac, error); + + if (!VIR_IS_CONNECTED_NETWORK(network)) { + virLibNetworkError(VIR_ERR_INVALID_NETWORK, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + /* Validate the MAC address */ + if (virMacAddrParse(mac, &addr) < 0) { + virReportInvalidArg(mac, "%s", + _("Given MAC Address doesn't comply " + "with the standard (IEEE 802) format")); + goto error; + } + + conn = network->conn; + + if (conn->networkDriver && + conn->networkDriver->networkGetDHCPLeasesForMAC) { + int ret; + ret = conn->networkDriver->networkGetDHCPLeasesForMAC(network, mac, + leases, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(network->conn); + return -1; +} + +/** + * virNetworkDHCPLeaseFree: + * @lease: pointer to a leases object + * + * Frees all the memory occupied by @lease. + */ +void +virNetworkDHCPLeaseFree(virNetworkDHCPLeasesPtr lease) +{ + if (!lease) + return; + VIR_FREE(lease->interface); + VIR_FREE(lease->mac); + VIR_FREE(lease->iaid); + VIR_FREE(lease->ipaddr); + VIR_FREE(lease->hostname); + VIR_FREE(lease->clientid); + VIR_FREE(lease); +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index fe9b497..f1a9707 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -639,4 +639,11 @@ LIBVIRT_1.1.3 { virConnectGetCPUModelNames; } LIBVIRT_1.1.1; +LIBVIRT_1.1.4 { + global: + virNetworkDHCPLeaseFree; + virNetworkGetDHCPLeases; + virNetworkGetDHCPLeasesForMAC; +} LIBVIRT_1.1.3; + # .... define new API here using predicted next version number .... diff --git a/src/util/leaseshelper.c b/src/util/leaseshelper.c new file mode 100644 index 0000000..53d7d22 --- /dev/null +++ b/src/util/leaseshelper.c @@ -0,0 +1,185 @@ +/* + * 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 "virfile.h" +#include "virbuffer.h" +#include "virstring.h" +#include "virerror.h" +#include "viralloc.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_NETWORK + +/** + * VIR_NETWORK_DHCP_LEASE_FIELDS: + * + * Macro providing the maximum number of fields in an entry in + * the leases file + */ +#define VIR_NETWORK_DHCP_LEASE_FIELDS 6 +/** + * 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 NULL_STR(s) ((s) ? (s) : "*") + +int +main(int argc, char **argv) { + + /* Doesn't hurt to check */ + if (argc < 4) + return -1; + + const char *action = argv[1]; + const char *interface = NULL_STR(getenv("DNSMASQ_INTERFACE")); + const char *expirytime = NULL_STR(getenv("DNSMASQ_LEASE_EXPIRES")); + const char *mac = argv[2]; + const char *ip = argv[3]; + const char *iaid = NULL_STR(getenv("DNSMASQ_IAID")); + const char *hostname = NULL_STR(getenv("DNSMASQ_SUPPLIED_HOSTNAME")); + const char *clientid = NULL_STR(getenv("DNSMASQ_CLIENT_ID")); + const char *leases_str = NULL; + char *lease_file = NULL; + char *lease_entries = NULL; + char *lease_entry = NULL; + char **lease_fields = NULL; + bool delete = false; + bool add = false; + int rv = -1; + int lease_file_len = 0; + FILE *fp = NULL; + virBuffer buf_new_lease = VIR_BUFFER_INITIALIZER; + virBuffer buf_all_leases = VIR_BUFFER_INITIALIZER; + + if (virAsprintf(&lease_file, "%s/%s.status", LOCALSTATEDIR + "/lib/libvirt/dnsmasq/", interface) < 0) + goto cleanup; + + if (getenv("DNSMASQ_IAID")) { + mac = NULL_STR(getenv("DNSMASQ_MAC")); + clientid = argv[2]; + } + + /* Make sure the file exists. If not, 'touch' it */ + fp = fopen(lease_file, "a+"); + fclose(fp); + + /* 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 */ + virBufferAsprintf(&buf_new_lease, "%s %s %s %s %s %s\n", + expirytime, mac, iaid, ip, hostname, clientid); + + if (virBufferError(&buf_new_lease)) { + virBufferFreeAndReset(&buf_new_lease); + virReportOOMError(); + goto cleanup; + } + } + } + } + + lease_entry = lease_entries[0] == '\0' ? NULL : lease_entries; + + while (lease_entry) { + int nfields = 0; + + char *eol = strchr(lease_entry, '\n'); + *eol = '\0'; + + /* Split the lease line */ + if (!(lease_fields = virStringSplit(lease_entry, " ", + VIR_NETWORK_DHCP_LEASE_FIELDS))) + goto cleanup; + + nfields = virStringListLength(lease_fields); + + /* Forward lease_entry to the next lease */ + lease_entry = strchr(lease_entry, '\0'); + if (lease_entry - lease_entries + 1 < lease_file_len) + lease_entry++; + else + lease_entry = NULL; + + if (nfields != VIR_NETWORK_DHCP_LEASE_FIELDS) + goto cleanup; + + if (delete && STREQ(lease_fields[3], ip)) + continue; + else { + virBufferAsprintf(&buf_all_leases, "%s %s %s %s %s %s\n", + lease_fields[0], lease_fields[1], lease_fields[2], + lease_fields[3], lease_fields[4], lease_fields[5]); + + if (virBufferError(&buf_all_leases)) { + virBufferFreeAndReset(&buf_all_leases); + virReportOOMError(); + goto cleanup; + } + } + } + + if (add) + virBufferAsprintf(&buf_all_leases, "%s", virBufferContentAndReset(&buf_new_lease)); + + rv = 0; + + /* Write to file */ + leases_str = virBufferContentAndReset(&buf_all_leases); + if (!leases_str) + leases_str = ""; + + if (virFileWriteStr(lease_file, leases_str, 0) < 0) + rv = -1; + +cleanup: + VIR_FREE(lease_file); + VIR_FREE(lease_entries); + if (lease_fields) + virStringFreeList(lease_fields); + return rv; +} -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list