]> xenbits.xensource.com Git - libvirt.git/commitdiff
nss: Introduce libvirt-guest module
authorMichal Privoznik <mprivozn@redhat.com>
Tue, 29 Nov 2016 12:48:53 +0000 (13:48 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Tue, 6 Dec 2016 12:34:00 +0000 (13:34 +0100)
So far the NSS module looks up only hostnames as provided by
guests themselves. However, there are some cases where this is
not enough: e.g. when there's a fresh new guest being installed
(with some generic hostname) say from a live ISO image; or some
(older) systems don't advertise their hostname in DHCP
transactions at all.
In cases like that it would be helpful if we translate domain
name as seen by libvirt too so that users can:

  # virsh start $dom && ssh $dom

In order to achieve that new libvirt-guest module is introduced,
while older libvirt module maintains its current behaviour (that
is translating guest provided names into IP addresses).

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
14 files changed:
docs/news.html.in
docs/nss.html.in
src/Makefile.am
tests/Makefile.am
tests/nssdata/virbr0.macs [new file with mode: 0644]
tests/nssdata/virbr0.status
tests/nssdata/virbr1.macs [new file with mode: 0644]
tests/nssdata/virbr1.status
tests/nssmock.c
tests/nsstest.c
tools/Makefile.am
tools/nss/libvirt_guest_nss.syms [new file with mode: 0644]
tools/nss/libvirt_nss.c
tools/nss/libvirt_nss.h

index c3664ae1d4f085e4ca10eeeee5a2b52e2c04f49e..958bdd2b32c59e72f1708949c57c255cb225c5bb 100644 (file)
           domain &lt;disk&gt; &lt;iotune&gt; subelement "group_name"
           to allow sharing I/O throttling quota between multiple drives
           </li>
+          <li>nss: Introduce <code>libvirt-guest</code><br/>
+          New <code>libvirt-guest</code> nss module that translates libvirt
+          guest names into IP addresses
+          </li>
         </ul>
       </li>
       <li><strong>Improvements</strong>
index 84ea8df6dbe1c93f1414e3934dd3aecdffadae70..e07f9b774f99b2eac0f9c494d8868c6d1e27a330 100644 (file)
@@ -62,6 +62,48 @@ hosts:       files libvirt dns
     lookup given host name.
     </p>
 
+    <h2><a name="Sources">Sources of information</a></h2>
+
+    <p>
+    As of <code>v3.0.0</code> release, libvirt offers two NSS modules
+    implementing two different methods of hostname translation. The first and
+    older method is implemented by <code>libvirt</code> plugin and
+    basically looks up the hostname to IP address translation in DHCP server
+    records. Therefore this is dependent on hostname provided by guests. Thing
+    is, not all the guests out there provide one in DHCP transactions, or not
+    every sysadmin out there believes all the guests. Hence libvirt implements
+    second method in <code>libvirt-guest</code> module which does libvirt guest
+    name to IP address translation (regardless of hostname set in the guest).
+    </p>
+
+    <p>
+    To enable either of the modules put their name into the
+    <code>nsswitch.conf</code> file. For instance, to enable
+    <code>libvirt-guest</code> module:
+    </p>
+
+<pre>
+$ cat /etc/nsswitch.conf
+# /etc/nsswitch.conf:
+hosts:       files libvirt-guest dns
+# ...
+</pre>
+    <p>Or users can enable both at the same time:</p>
+<pre>
+$ cat /etc/nsswitch.conf
+# /etc/nsswitch.conf:
+hosts:       files libvirt libvirt-guest dns
+# ...
+</pre>
+
+    <p>
+    This configuration will mean that if hostname is not found by the
+    <code>libvirt</code> module (e.g. because a guest did not sent hostname
+    during DHCP transaction), the <code>libvirt-guest</code> module is
+    consulted (and if the hostname matches libvirt guest name it will be
+    resolved).
+    </p>
+
     <h2><a name="Internals">How does it work?</a></h2>
 
     <p>
@@ -100,15 +142,18 @@ hosts:       files libvirt dns
     <h2><a name="Limitations">Limitations</a></h2>
 
     <ol>
-      <li>The libvirt NSS module matches only hostnames provided by guest. If
-        the libvirt name and one advertised by guest differs, the latter is
-        matched.</li>
+      <li>The <code>libvirt</code> NSS module matches only hostnames provided by guest.
+        If the libvirt name and one advertised by guest differs, the latter is
+        matched. However, as of <code>v3.0.0</code> there are two libvirt NSS modules
+        translating both hostnames provided by guest and libvirt guest names.</li>
       <li>The module works only in that cases where IP addresses are assigned by
         dnsmasq spawned by libvirt. Libvirt NATed networks are typical
         example.</li>
     </ol>
 
     <p>
+    <i>The following paragraph describes implementation limitation of the
+    <code>libvirt</code> NSS module.</i>
     These limitation are result of libvirt's internal implementation. While
     libvirt can report IP addresses regardless of their origin, a public API
     must be used to obtain those. However, for the API a connection object is
@@ -134,8 +179,11 @@ virsh domifaddr --source lease $domain
 </pre>
 
     <p>
-    If there's no record for either of the aforementioned commands, it's very
-    likely that NSS module won't find anything and vice versa.
+    If there's no record for either of the aforementioned commands, it's
+    very likely that NSS module won't find anything and vice versa.
+    As of <code>v3.0.0</code> libvirt provides <code>libvirt-guest</code> NSS
+    module that doesn't have this limitation. However, the statement is still
+    true for the <code>libvirt</code> NSS module.
     </p>
   </body>
 </html>
index a9106fa97803210ab7f0aafe28f7c981867f471f..8c620d5e08d9e33d279b8a2cc8dbe2bf93c9326f 100644 (file)
@@ -3033,6 +3033,10 @@ libvirt_nss_la_SOURCES =         \
                util/virerror.h                 \
                util/virfile.c                  \
                util/virfile.h                  \
+               util/virhash.c                  \
+               util/virhash.h                  \
+               util/virhashcode.c              \
+               util/virhashcode.h              \
                util/virjson.c                  \
                util/virjson.h                  \
                util/virkmod.c                  \
@@ -3041,12 +3045,16 @@ libvirt_nss_la_SOURCES =                \
                util/virlease.h                 \
                util/virlog.c                   \
                util/virlog.h                   \
+               util/virmacmap.c                \
+               util/virmacmap.h                \
                util/virobject.c                \
                util/virobject.h                \
                util/virpidfile.c               \
                util/virpidfile.h               \
                util/virprocess.c               \
                util/virprocess.h               \
+               util/virrandom.c                \
+               util/virrandom.h                \
                util/virsocketaddr.c    \
                util/virsocketaddr.h    \
                util/virstring.c                \
index 9d5583d430f8ec769dc8287ed0844a939d7d6495..ecd04e895d457e83482ea28590256497c4c3f217 100644 (file)
@@ -335,8 +335,8 @@ test_programs += virscsitest
 endif WITH_LINUX
 
 if WITH_NSS
-test_helpers += nsslinktest
-test_programs += nsstest
+test_helpers += nsslinktest nssguestlinktest
+test_programs += nsstest nssguesttest
 endif WITH_NSS
 
 test_programs += storagevolxml2xmltest storagepoolxml2xmltest
@@ -1125,6 +1125,16 @@ nsstest_LDADD = \
        $(LDADDS)       \
        ../tools/nss/libnss_libvirt_impl.la
 
+nssguesttest_SOURCES = \
+       nsstest.c testutils.h testutils.c
+nssguesttest_CFLAGS = \
+       -DLIBVIRT_NSS_GUEST     \
+       $(AM_CFLAGS)    \
+       -I$(top_srcdir)/tools/nss
+nssguesttest_LDADD = \
+       $(LDADDS)       \
+       ../tools/nss/libnss_libvirt_guest_impl.la
+
 nssmock_la_SOURCES = \
        nssmock.c
 nssmock_la_CFLAGS = $(AM_CFLAGS)
@@ -1140,6 +1150,14 @@ nsslinktest_CFLAGS =     \
 nsslinktest_LDADD = ../tools/nss/libnss_libvirt_impl.la
 nsslinktest_LDFLAGS = $(NULL)
 
+nssguestlinktest_SOURCES = nsslinktest.c
+nssguestlinktest_CFLAGS =      \
+       -DLIBVIRT_NSS_GUEST     \
+       $(AM_CFLAGS)            \
+       -I$(top_srcdir)/tools/nss
+nssguestlinktest_LDADD = ../tools/nss/libnss_libvirt_guest_impl.la
+nssguestlinktest_LDFLAGS = $(NULL)
+
 virmacmapmock_la_SOURCES = \
        virmacmapmock.c
 virmacmapmock_la_CFLAGS = $(AM_CFLAGS)
diff --git a/tests/nssdata/virbr0.macs b/tests/nssdata/virbr0.macs
new file mode 100644 (file)
index 0000000..45f9e2b
--- /dev/null
@@ -0,0 +1,23 @@
+[
+  {
+    "domain": "fedora",
+    "macs": [
+      "52:54:00:a4:6f:91",
+      "52:54:00:a4:6f:92"
+    ]
+  },
+  {
+    "domain": "gentoo",
+    "macs": [
+      "52:54:00:3a:b5:0c"
+    ]
+  },
+  {
+    "domain": "debian",
+    "macs": [
+      "52:54:00:11:22:33",
+      "52:54:00:a2:37:4c",
+      "52:54:00:f8:5b:1d"
+    ]
+  }
+]
index 6ebe9545474c96188f527da136850aa7d172c086..d040774269354d4086f356baebe352fa07b8caab 100644 (file)
         "mac-address": "52:54:00:3a:b5:0c",
         "hostname": "gentoo",
         "expiry-time": 2000000000
+    },
+    {
+        "ip-address": "192.168.122.2",
+        "mac-address": "52:54:00:11:22:33",
+        "expiry-time": 2000000000
     }
 ]
diff --git a/tests/nssdata/virbr1.macs b/tests/nssdata/virbr1.macs
new file mode 100644 (file)
index 0000000..2140737
--- /dev/null
@@ -0,0 +1,21 @@
+[
+  {
+    "domain": "fedora",
+    "macs": [
+      "52:54:00:a4:6f:93",
+      "52:54:00:a4:6f:94"
+    ]
+  },
+  {
+   "domain": "gentoo",
+   "macs": [
+      "52:54:00:04:be:64"
+    ]
+  },
+  {
+   "domain": "suse",
+   "macs": [
+      "52:54:00:aa:bb:cc"
+    ]
+  }
+]
index f8a9157f34063747a19ac07a87fb04e3a8a67a34..4951d4513ef9a220bddd99f3b317cf38cb18677f 100644 (file)
         "mac-address": "52:54:00:a4:6f:94",
         "hostname": "fedora",
         "expiry-time": 1
+    },
+    {
+        "ip-address": "192.168.122.3",
+        "mac-address": "52:54:00:aa:bb:cc",
+        "expiry-time": 2000000000
     }
 ]
index 273af0654a7f8299591319e9f128fd81f12047a3..7929c3061c942935059e7e96c88e62d143204348 100644 (file)
@@ -26,6 +26,7 @@
 # include <dirent.h>
 # include <sys/stat.h>
 # include <fcntl.h>
+# include <unistd.h>
 
 # include "configmake.h"
 # include "virstring.h"
@@ -33,6 +34,7 @@
 
 static int (*real_open)(const char *path, int flags, ...);
 static DIR * (*real_opendir)(const char *name);
+static int (*real_access)(const char *path, int mode);
 
 # define LEASEDIR LOCALSTATEDIR "/lib/libvirt/dnsmasq/"
 
@@ -47,6 +49,7 @@ init_syms(void)
 
     VIR_MOCK_REAL_INIT(open);
     VIR_MOCK_REAL_INIT(opendir);
+    VIR_MOCK_REAL_INIT(access);
 }
 
 static int
@@ -112,6 +115,24 @@ opendir(const char *path)
     free(newpath);
     return ret;
 }
+
+int
+access(const char *path, int mode)
+{
+    int ret;
+    char *newpath = NULL;
+
+    init_syms();
+
+    if (STRPREFIX(path, LEASEDIR) &&
+        getrealpath(&newpath, path) < 0)
+        return -1;
+
+    ret = real_access(newpath ? newpath : path, mode);
+
+    free(newpath);
+    return ret;
+}
 #else
 /* Nothing to override if NSS plugin is not enabled */
 #endif
index c7fb11fd51da5b222b3e308cf5a64d48771665dc..915a8bceb57dd24805bdf1d9f81270fd2948e046 100644 (file)
@@ -184,11 +184,16 @@ mymain(void)
             ret = -1;                                           \
     } while (0)
 
+# if !defined(LIBVIRT_NSS_GUEST)
     DO_TEST("fedora", AF_INET, "192.168.122.197", "192.168.122.198", "192.168.122.199");
     DO_TEST("gentoo", AF_INET, "192.168.122.254");
     DO_TEST("gentoo", AF_INET6, "2001:1234:dead:beef::2");
     DO_TEST("gentoo", AF_UNSPEC, "192.168.122.254");
     DO_TEST("non-existent", AF_UNSPEC, NULL);
+# else /* defined(LIBVIRT_NSS_GUEST) */
+    DO_TEST("debian", AF_INET, "192.168.122.2");
+    DO_TEST("suse", AF_INET, "192.168.122.3");
+# endif /* defined(LIBVIRT_NSS_GUEST) */
 
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
index 100e657f7b45381417f84ed25090192a69a54f44..e6ae150259169cc4498ab1917671cc486aaf0dc5 100644 (file)
@@ -436,18 +436,26 @@ endif WITH_WIRESHARK_DISSECTOR
 if WITH_BSD_NSS
 LIBVIRT_NSS_SYMBOL_FILE = \
        $(srcdir)/nss/libvirt_nss_bsd.syms
+LIBVIRT_GUEST_NSS_SYMBOL_FILE = \
+       $(LIBVIRT_NSS_SYMBOL_FILE)
 NSS_SO_VER = 1
 
 install-nss:
        ( cd $(DESTDIR)$(libdir) && \
          rm -f nss_libvirt.so.$(NSS_SO_VER) && \
-         $(LN_S) libnss_libvirt.so.$(NSS_SO_VER) nss_libvirt.so.$(NSS_SO_VER) )
+         $(LN_S) libnss_libvirt.so.$(NSS_SO_VER) nss_libvirt.so.$(NSS_SO_VER) && \
+         rm -f nss_libvirt_guest.so.$(NSS_SO_VER) && \
+         $(LN_S) libnss_libvirt_guest.so.$(NSS_SO_VER) \
+         nss_libvirt_guest.so.$(NSS_SO_VER))
 
 uninstall-nss:
        -rm -f $(DESTDIR)$(libdir)/nss_libvirt.so.$(NSS_SO_VER)
+       -rm -f $(DESTDIR)$(libdir)/nss_libvirt_guest.so.$(NSS_SO_VER)
 else ! WITH_BSD_NSS
 LIBVIRT_NSS_SYMBOL_FILE = \
        $(srcdir)/nss/libvirt_nss.syms
+LIBVIRT_GUEST_NSS_SYMBOL_FILE = \
+       $(srcdir)/nss/libvirt_guest_nss.syms
 NSS_SO_VER = 2
 
 install-nss:
@@ -488,14 +496,46 @@ nss_libnss_libvirt_la_LDFLAGS = \
 nss_libnss_libvirt_la_LIBADD =  \
        nss/libnss_libvirt_impl.la
 
+noinst_LTLIBRARIES += nss/libnss_libvirt_guest_impl.la
+nss_libnss_libvirt_guest_impl_la_SOURCES = \
+       $(LIBVIRT_NSS_SOURCES)
+
+nss_libnss_libvirt_guest_impl_la_CFLAGS = \
+       -DLIBVIRT_NSS           \
+       -DLIBVIRT_NSS_GUEST     \
+       $(AM_CFLAGS)            \
+       $(WARN_CFLAGS)          \
+       $(PIE_CFLAGS)           \
+       $(COVERAGE_CFLAGS)      \
+       $(LIBXML_CFLAGS)
+
+nss_libnss_libvirt_guest_impl_la_LIBADD = \
+       ../gnulib/lib/libgnu.la         \
+       ../src/libvirt-nss.la
+
+nss_libnss_libvirt_guest_la_SOURCES =
+nss_libnss_libvirt_guest_la_LDFLAGS = \
+       $(VERSION_SCRIPT_FLAGS)$(LIBVIRT_GUEST_NSS_SYMBOL_FILE) \
+       $(AM_LDFLAGS) \
+       -module \
+       -export-dynamic \
+       -avoid-version \
+       -shared \
+       -shrext .so.$(NSS_SO_VER)
+
+nss_libnss_libvirt_guest_la_LIBADD =  \
+       nss/libnss_libvirt_guest_impl.la
+
 lib_LTLIBRARIES = \
-       nss/libnss_libvirt.la
+       nss/libnss_libvirt.la \
+       nss/libnss_libvirt_guest.la
 
 endif WITH_NSS
 
 EXTRA_DIST += $(LIBVIRT_NSS_SOURCES) \
        $(srcdir)/nss/libvirt_nss.syms \
-       $(srcdir)/nss/libvirt_nss_bsd.syms
+       $(srcdir)/nss/libvirt_nss_bsd.syms      \
+       $(srcdir)/nss/libvirt_guest_nss.syms
 
 clean-local:
        -rm -rf wireshark/src/libvirt
diff --git a/tools/nss/libvirt_guest_nss.syms b/tools/nss/libvirt_guest_nss.syms
new file mode 100644 (file)
index 0000000..0612987
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Officially exported symbols.
+#
+
+{
+global:
+    _nss_libvirt_guest_gethostbyname_r;
+    _nss_libvirt_guest_gethostbyname2_r;
+    _nss_libvirt_guest_gethostbyname3_r;
+    _nss_libvirt_guest_gethostbyname4_r;
+local: *;
+};
index 9bf57c877479e4a4a5f7ba3cc61bf9e6ec103b0f..418c11fafe101b006b47736a57879c65a6e70d42 100644 (file)
@@ -47,6 +47,8 @@
 #include "virstring.h"
 #include "virsocketaddr.h"
 #include "configmake.h"
+#include "virmacmap.h"
+#include "virobject.h"
 
 #if 0
 # define ERROR(...)                                             \
@@ -147,6 +149,7 @@ findLeaseInJSON(leaseAddress **tmpAddress,
                 virJSONValuePtr leases_array,
                 size_t nleases,
                 const char *name,
+                const char **macs,
                 int af,
                 bool *found)
 {
@@ -162,7 +165,6 @@ findLeaseInJSON(leaseAddress **tmpAddress,
 
     for (i = 0; i < nleases; i++) {
         virJSONValuePtr lease = virJSONValueArrayGet(leases_array, i);
-        const char *lease_name;
 
         if (!lease) {
             /* This should never happen (TM) */
@@ -170,10 +172,23 @@ findLeaseInJSON(leaseAddress **tmpAddress,
             goto cleanup;
         }
 
-        lease_name = virJSONValueObjectGetString(lease, "hostname");
+        if (macs) {
+            const char *macAddr;
 
-        if (STRNEQ_NULLABLE(name, lease_name))
-            continue;
+            macAddr = virJSONValueObjectGetString(lease, "mac-address");
+            if (!macAddr)
+                continue;
+
+            if (!virStringListHasString(macs, macAddr))
+                continue;
+        } else {
+            const char *lease_name;
+
+            lease_name = virJSONValueObjectGetString(lease, "hostname");
+
+            if (STRNEQ_NULLABLE(name, lease_name))
+                continue;
+        }
 
         if (virJSONValueObjectGetNumberLong(lease, "expiry-time", &expirytime) < 0) {
             /* A lease cannot be present without expiry-time */
@@ -236,6 +251,8 @@ findLease(const char *name,
     ssize_t nleases;
     leaseAddress *tmpAddress = NULL;
     size_t ntmpAddress = 0;
+    virMacMapPtr *macmaps = NULL;
+    size_t nMacmaps = 0;
 
     *address = NULL;
     *naddress = 0;
@@ -246,7 +263,6 @@ findLease(const char *name,
         goto cleanup;
     }
 
-
     if (virDirOpenQuiet(&dir, leaseDir) < 0) {
         ERROR("Failed to open dir '%s'", leaseDir);
         goto cleanup;
@@ -261,33 +277,64 @@ findLease(const char *name,
     while ((ret = virDirRead(dir, &entry, leaseDir)) > 0) {
         char *path;
 
-        if (!virFileHasSuffix(entry->d_name, ".status"))
-            continue;
-
-        if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL)))
-            goto cleanup;
-
-        DEBUG("Processing %s", path);
+        if (virFileHasSuffix(entry->d_name, ".status")) {
+            if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL)))
+                goto cleanup;
 
-        if (virLeaseReadCustomLeaseFile(leases_array, path, NULL, NULL) < 0) {
-            ERROR("Unable to parse %s", path);
+            DEBUG("Processing %s", path);
+            if (virLeaseReadCustomLeaseFile(leases_array, path, NULL, NULL) < 0) {
+                ERROR("Unable to parse %s", path);
+                VIR_FREE(path);
+                goto cleanup;
+            }
+            VIR_FREE(path);
+        } else if (virFileHasSuffix(entry->d_name, ".macs")) {
+            if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL)))
+                goto cleanup;
+
+            if (VIR_REALLOC_N_QUIET(macmaps, nMacmaps + 1) < 0) {
+                VIR_FREE(path);
+                goto cleanup;
+            }
+
+            DEBUG("Processing %s", path);
+            if (!(macmaps[nMacmaps] = virMacMapNew(path))) {
+                ERROR("Unable to parse %s", path);
+                VIR_FREE(path);
+                goto cleanup;
+            }
+            nMacmaps++;
             VIR_FREE(path);
-            goto cleanup;
         }
-
-        VIR_FREE(path);
     }
-
     VIR_DIR_CLOSE(dir);
 
     nleases = virJSONValueArraySize(leases_array);
     DEBUG("Read %zd leases", nleases);
 
+#if !defined(LIBVIRT_NSS_GUEST)
     if (findLeaseInJSON(&tmpAddress, &ntmpAddress,
                         leases_array, nleases,
-                        name, af, found) < 0)
+                        name, NULL, af, found) < 0)
         goto cleanup;
 
+#else /* defined(LIBVIRT_NSS_GUEST) */
+
+    size_t i;
+    for (i = 0; i < nMacmaps; i++) {
+        const char **macs = (const char **) virMacMapLookup(macmaps[i], name);
+
+        if (!macs)
+            continue;
+
+        if (findLeaseInJSON(&tmpAddress, &ntmpAddress,
+                            leases_array, nleases,
+                            name, macs, af, found) < 0)
+            goto cleanup;
+    }
+
+#endif /* defined(LIBVIRT_NSS_GUEST) */
+
     *address = tmpAddress;
     *naddress = ntmpAddress;
     tmpAddress = NULL;
@@ -300,6 +347,9 @@ findLease(const char *name,
     VIR_FREE(tmpAddress);
     virJSONValueFree(leases_array);
     VIR_DIR_CLOSE(dir);
+    while (nMacmaps)
+        virObjectUnref(macmaps[--nMacmaps]);
+    VIR_FREE(macmaps);
     return ret;
 }
 
index 481d8828b6479123efb2d9a865832b614edbb60a..a25fe01106b64a008a612faaa3005ea7f8902338 100644 (file)
 # include <nss.h>
 # include <netdb.h>
 
-# define NSS_NAME(s) _nss_libvirt_##s##_r
+# if !defined(LIBVIRT_NSS_GUEST)
+#  define NSS_NAME(s) _nss_libvirt_##s##_r
+# else
+#  define NSS_NAME(s) _nss_libvirt_guest_##s##_r
+# endif
 
 enum nss_status
 NSS_NAME(gethostbyname)(const char *name, struct hostent *result,