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@xxxxxxxxxx> --- docs/news.html.in | 4 ++ docs/nss.html.in | 58 ++++++++++++++++++++++--- src/Makefile.am | 8 ++++ tests/Makefile.am | 22 +++++++++- tests/nssdata/virbr0.macs | 23 ++++++++++ tests/nssdata/virbr0.status | 5 +++ tests/nssdata/virbr1.macs | 21 +++++++++ tests/nssdata/virbr1.status | 5 +++ tests/nssmock.c | 21 +++++++++ tests/nsstest.c | 5 +++ tools/Makefile.am | 46 ++++++++++++++++++-- tools/nss/libvirt_guest_nss.syms | 12 ++++++ tools/nss/libvirt_nss.c | 92 ++++++++++++++++++++++++++++++++++------ tools/nss/libvirt_nss.h | 6 ++- 14 files changed, 305 insertions(+), 23 deletions(-) create mode 100644 tests/nssdata/virbr0.macs create mode 100644 tests/nssdata/virbr1.macs create mode 100644 tools/nss/libvirt_guest_nss.syms diff --git a/docs/news.html.in b/docs/news.html.in index 12dcb0141..2ea409894 100644 --- a/docs/news.html.in +++ b/docs/news.html.in @@ -15,6 +15,10 @@ <h3>v3.0.0 (<i>unreleased</i>)</h3> <ul> <li><strong>New features</strong> + <ul> + <li>New <code>libvirt-guest</code> nss module that translates libvirt + guest names into IP addresses</li> + </ul> </li> <li><strong>Improvements</strong> </li> diff --git a/docs/nss.html.in b/docs/nss.html.in index 84ea8df6d..ee18c9b78 100644 --- a/docs/nss.html.in +++ b/docs/nss.html.in @@ -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, nor + 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><del>The libvirt NSS module matches only hostnames provided by guest. + If the libvirt name and one advertised by guest differs, the latter is + matched.</del> 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. + <del>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.</del> + 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> diff --git a/src/Makefile.am b/src/Makefile.am index a9106fa97..8c620d5e0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/tests/Makefile.am b/tests/Makefile.am index 9d5583d43..ecd04e895 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 000000000..45f9e2bed --- /dev/null +++ b/tests/nssdata/virbr0.macs @@ -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" + ] + } +] diff --git a/tests/nssdata/virbr0.status b/tests/nssdata/virbr0.status index 6ebe95454..d04077426 100644 --- a/tests/nssdata/virbr0.status +++ b/tests/nssdata/virbr0.status @@ -16,5 +16,10 @@ "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 index 000000000..214073755 --- /dev/null +++ b/tests/nssdata/virbr1.macs @@ -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" + ] + } +] diff --git a/tests/nssdata/virbr1.status b/tests/nssdata/virbr1.status index f8a9157f3..4951d4513 100644 --- a/tests/nssdata/virbr1.status +++ b/tests/nssdata/virbr1.status @@ -16,5 +16,10 @@ "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 } ] diff --git a/tests/nssmock.c b/tests/nssmock.c index 273af0654..7929c3061 100644 --- a/tests/nssmock.c +++ b/tests/nssmock.c @@ -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 diff --git a/tests/nsstest.c b/tests/nsstest.c index c7fb11fd5..915a8bceb 100644 --- a/tests/nsstest.c +++ b/tests/nsstest.c @@ -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; } diff --git a/tools/Makefile.am b/tools/Makefile.am index 100e657f7..e6ae15025 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -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 index 000000000..061298725 --- /dev/null +++ b/tools/nss/libvirt_guest_nss.syms @@ -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: *; +}; diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c index 979bf81e1..6cadf8c43 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -47,6 +47,8 @@ #include "virstring.h" #include "virsocketaddr.h" #include "configmake.h" +#include "virmacmap.h" +#include "virobject.h" #if 0 # define ERROR(...) \ @@ -179,6 +181,8 @@ findLease(const char *name, size_t ntmpAddress = 0; time_t currtime; long long expirytime; + virMACMapMgrPtr *macmaps = NULL; + size_t nMacmaps = 0; *address = NULL; *naddress = 0; @@ -189,7 +193,6 @@ findLease(const char *name, goto cleanup; } - if (virDirOpenQuiet(&dir, leaseDir) < 0) { ERROR("Failed to open dir '%s'", leaseDir); goto cleanup; @@ -204,23 +207,36 @@ findLease(const char *name, while ((ret = virDirRead(dir, &entry, leaseDir)) > 0) { char *path; - if (!virFileHasSuffix(entry->d_name, ".status")) - continue; + if (virFileHasSuffix(entry->d_name, ".status")) { + if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL))) + goto cleanup; - if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL))) - goto cleanup; + 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; - DEBUG("Processing %s", path); + if (VIR_REALLOC_N_QUIET(macmaps, nMacmaps + 1) < 0) { + VIR_FREE(path); + goto cleanup; + } - if (virLeaseReadCustomLeaseFile(leases_array, path, NULL, NULL) < 0) { - ERROR("Unable to parse %s", path); + DEBUG("Processing %s", path); + if (!(macmaps[nMacmaps] = virMACMapMgrNew(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); @@ -231,6 +247,7 @@ findLease(const char *name, goto cleanup; } +#if !defined(LIBVIRT_NSS_GUEST) for (i = 0; i < nleases; i++) { virJSONValuePtr lease; const char *lease_name; @@ -267,6 +284,54 @@ findLease(const char *name, goto cleanup; } +#else /* defined(LIBVIRT_NSS_GUEST) */ + + for (i = 0; i < nMacmaps; i++) { + const char **macs = (const char **) virMACMapMgrLookup(macmaps[i], name); + size_t j; + + if (!macs) + continue; + + for (j = 0; j < nleases; j++) { + virJSONValuePtr lease = virJSONValueArrayGet(leases_array, j); + const char *macAddr; + + if (!lease) { + /* This should never happen (TM) */ + ERROR("Unable to get element %zd of %zd", j, nleases); + goto cleanup; + } + + macAddr = virJSONValueObjectGetString(lease, "mac-address"); + if (!macAddr) + continue; + + if (!virStringListHasString(macs, macAddr)) + continue; + + if (virJSONValueObjectGetNumberLong(lease, "expiry-time", &expirytime) < 0) { + /* A lease cannot be present without expiry-time */ + ERROR("expiry-time field missing for %s", name); + goto cleanup; + } + + /* Do not report expired lease */ + if (expirytime < (long long) currtime) { + DEBUG("Skipping expired lease for %s", name); + continue; + } + + DEBUG("Found record for %s", name); + *found = true; + + if (appendAddr(&tmpAddress, &ntmpAddress, lease, af) < 0) + goto cleanup; + } + } + +#endif /* defined(LIBVIRT_NSS_GUEST) */ + *address = tmpAddress; *naddress = ntmpAddress; tmpAddress = NULL; @@ -279,6 +344,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; } diff --git a/tools/nss/libvirt_nss.h b/tools/nss/libvirt_nss.h index 481d8828b..a25fe0110 100644 --- a/tools/nss/libvirt_nss.h +++ b/tools/nss/libvirt_nss.h @@ -32,7 +32,11 @@ # 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, -- 2.11.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list