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
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
docs/nss.html.in | 13 ++--
src/Makefile.am | 8 ++
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 | 2 +
tools/nss/libvirt_nss.c | 173 ++++++++++++++++++++++++++++++++++----------
9 files changed, 226 insertions(+), 45 deletions(-)
create mode 100644 tests/nssdata/virbr0.macs
create mode 100644 tests/nssdata/virbr1.macs
diff --git a/docs/nss.html.in b/docs/nss.html.in
index 84ea8df..dc0ebfa 100644
--- a/docs/nss.html.in
+++ b/docs/nss.html.in
@@ -100,9 +100,10 @@ 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>v2.6.0</code> the libvirt nss
module
+ translates both hostnames provided by guest and libvirt domain 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>
@@ -134,8 +135,10 @@ 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>v2.6.0</code> the NSS module should provide hostname
+ translation even if no output had been produced by aforementioned command.
</p>
</body>
</html>
diff --git a/src/Makefile.am b/src/Makefile.am
index a9106fa..8c620d5 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/nssdata/virbr0.macs b/tests/nssdata/virbr0.macs
new file mode 100644
index 0000000..45f9e2b
--- /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 6ebe954..d040774 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 0000000..2140737
--- /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 f8a9157..4951d45 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 273af06..7929c30 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 8648c4a..3cf2795 100644
--- a/tests/nsstest.c
+++ b/tests/nsstest.c
@@ -189,6 +189,8 @@ mymain(void)
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);
+ DO_TEST("debian", AF_INET, "192.168.122.2");
+ DO_TEST("suse", AF_INET, "192.168.122.3");
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c
index 0d59825..c7802fc 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(...) \
@@ -79,6 +81,68 @@ typedef struct {
int af;
} leaseAddress;
+
+static int
+appendAddr(leaseAddress **tmpAddress,
+ size_t *ntmpAddress,
+ virJSONValuePtr lease,
+ int af)
+{
+ int ret = -1;
+ const char *ipAddr;
+ virSocketAddr sa;
+ int family;
+ size_t i;
+
+ if (!(ipAddr = virJSONValueObjectGetString(lease, "ip-address"))) {
+ ERROR("ip-address field missing for %s", name);
+ goto cleanup;
+ }
+
+ DEBUG("IP address: %s", ipAddr);
+
+ if (virSocketAddrParse(&sa, ipAddr, AF_UNSPEC) < 0) {
+ ERROR("Unable to parse %s", ipAddr);
+ goto cleanup;
+ }
+
+ family = VIR_SOCKET_ADDR_FAMILY(&sa);
+ if (af != AF_UNSPEC && af != family) {
+ DEBUG("Skipping address which family is %d, %d requested", family,
af);
+ ret = 0;
+ goto cleanup;
+ }
+
+ for (i = 0; i < *ntmpAddress; i++) {
+ if (memcmp((*tmpAddress)[i].addr,
+ (family == AF_INET ?
+ (void *) &sa.data.inet4.sin_addr.s_addr :
+ (void *) &sa.data.inet6.sin6_addr.s6_addr),
+ FAMILY_ADDRESS_SIZE(family)) == 0) {
+ DEBUG("IP address already in the list");
+ ret = 0;
+ goto cleanup;
+ }
+ }
+
+ if (VIR_REALLOC_N_QUIET(*tmpAddress, *ntmpAddress + 1) < 0) {
+ ERROR("Out of memory");
+ goto cleanup;
+ }
+
+ (*tmpAddress)[*ntmpAddress].af = family;
+ memcpy((*tmpAddress)[*ntmpAddress].addr,
+ (family == AF_INET ?
+ (void *) &sa.data.inet4.sin_addr.s_addr :
+ (void *) &sa.data.inet6.sin6_addr.s6_addr),
+ FAMILY_ADDRESS_SIZE(family));
+ (*ntmpAddress)++;
+ ret = 0;
+ cleanup:
+ return ret;
+}
+
+
/**
* findLease:
* @name: domain name to lookup
@@ -112,11 +176,13 @@ findLease(const char *name,
const char *leaseDir = LEASEDIR;
struct dirent *entry;
virJSONValuePtr leases_array = NULL;
- ssize_t i, nleases;
+ ssize_t i, j, nleases;
leaseAddress *tmpAddress = NULL;
size_t ntmpAddress = 0;
time_t currtime;
long long expirytime;
+ virMACMapMgrPtr *macmaps = NULL;
+ size_t nMacmaps = 0;
*address = NULL;
*naddress = 0;
@@ -127,7 +193,6 @@ findLease(const char *name,
goto cleanup;
}
-
if (virDirOpenQuiet(&dir, leaseDir) < 0) {
ERROR("Failed to open dir '%s'", leaseDir);
goto cleanup;
@@ -142,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);
@@ -172,9 +250,6 @@ findLease(const char *name,
for (i = 0; i < nleases; i++) {
virJSONValuePtr lease;
const char *lease_name;
- virSocketAddr sa;
- const char *ipAddr;
- int family;
lease = virJSONValueArrayGet(leases_array, i);
@@ -204,36 +279,51 @@ findLease(const char *name,
DEBUG("Found record for %s", lease_name);
*found = true;
- if (!(ipAddr = virJSONValueObjectGetString(lease, "ip-address"))) {
- ERROR("ip-address field missing for %s", name);
+ if (appendAddr(&tmpAddress, &ntmpAddress, lease, af) < 0)
goto cleanup;
- }
+ }
- DEBUG("IP address: %s", ipAddr);
+ for (i = 0; i < nMacmaps; i++) {
+ const char **macs = (const char **) virMACMapMgrLookup(macmaps[i], name);
- if (virSocketAddrParse(&sa, ipAddr, AF_UNSPEC) < 0) {
- ERROR("Unable to parse %s", ipAddr);
- goto cleanup;
- }
-
- family = VIR_SOCKET_ADDR_FAMILY(&sa);
- if (af != AF_UNSPEC && af != family) {
- DEBUG("Skipping address which family is %d, %d requested", family,
af);
+ if (!macs)
continue;
- }
- if (VIR_REALLOC_N_QUIET(tmpAddress, ntmpAddress + 1) < 0) {
- ERROR("Out of memory");
- goto cleanup;
- }
+ 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;
- tmpAddress[ntmpAddress].af = family;
- memcpy(tmpAddress[ntmpAddress].addr,
- (family == AF_INET ?
- (void *) &sa.data.inet4.sin_addr.s_addr :
- (void *) &sa.data.inet6.sin6_addr.s6_addr),
- FAMILY_ADDRESS_SIZE(family));
- ntmpAddress++;
+ 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;
+ }
}
*address = tmpAddress;
@@ -248,6 +338,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;
}
--
2.8.4