[libvirt] [PATCHv5 1/4] net-dhcp-leases: Implement the public APIs

Nehal J Wani nehaljw.kkd1 at gmail.com
Mon Nov 25 21:05:58 UTC 2013


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 at gmail.com>
+ *
+ */
+
+#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




More information about the libvir-list mailing list