nss: Introduce libvirt-guest module

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>
This commit is contained in:
Michal Privoznik 2016-11-29 13:48:53 +01:00
parent 3225e5b26e
commit 22f7ceb695
14 changed files with 294 additions and 30 deletions

View File

@ -21,6 +21,10 @@
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>

View 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>

View 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 \

View 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)

23
tests/nssdata/virbr0.macs Normal file
View File

@ -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"
]
}
]

View File

@ -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
}
]

21
tests/nssdata/virbr1.macs Normal file
View File

@ -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"
]
}
]

View File

@ -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
}
]

View 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

View 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;
}

View 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

View File

@ -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: *;
};

View 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 (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);
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;
}

View File

@ -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,