:p
atchew
Login
See 6/7 for more details. Jiri Denemark (7): util: Refactor virSystemdHas{Machined,Logind} util: Introduce virSystemdHasResolved util: Introduce virSocketAddrBytes util: Introduce virSystemdResolvedRegisterNameServer tests: Add tests for virSystemdResolvedRegisterNameServer network: Make virtual domains resolvable from the host NEWS: Mention systemd-resolved support in network driver NEWS.rst | 7 ++ docs/formatnetwork.rst | 9 +- src/conf/network_conf.c | 18 ++++ src/conf/network_conf.h | 1 + src/conf/schemas/network.rng | 3 + src/libvirt_private.syms | 4 + src/network/bridge_driver.c | 32 ++++++- src/util/virsocketaddr.c | 63 +++++++++++++ src/util/virsocketaddr.h | 4 + src/util/virsystemd.c | 166 +++++++++++++++++++++++++++-------- src/util/virsystemd.h | 7 ++ src/util/virsystemdpriv.h | 1 + tests/virsystemdtest.c | 147 +++++++++++++++++++++++++++++++ 13 files changed, 421 insertions(+), 41 deletions(-) -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
To reduce code duplication both function now use a common virSystemdHasService helper. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/util/virsystemd.c | 76 ++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.c +++ b/src/util/virsystemd.c @@ -XXX,XX +XXX,XX @@ void virSystemdHasLogindResetCachedValue(void) } -/* -2 = machine1 is not supported on this machine - * -1 = error - * 0 = machine1 is available +/** + * virSystemdHasService: + * + * Check whether a DBus @service is enabled and either the service itself or + * systemd1 service is registered. If @requireSystemd == true, the systemd1 + * service has to be registered even if @service is registered. + * + * Returns + * -2 when service is not supported on this machine + * -1 on error + * 0 when service is available */ -int -virSystemdHasMachined(void) +static int +virSystemdHasService(const char *service, + bool requireSystemd, + int *cached) { int ret; int val; - val = g_atomic_int_get(&virSystemdHasMachinedCachedValue); + val = g_atomic_int_get(cached); if (val != -1) return val; - if ((ret = virGDBusIsServiceEnabled("org.freedesktop.machine1")) < 0) { + if ((ret = virGDBusIsServiceEnabled(service)) < 0) { if (ret == -2) - g_atomic_int_set(&virSystemdHasMachinedCachedValue, -2); + g_atomic_int_set(cached, -2); return ret; } - if ((ret = virGDBusIsServiceRegistered("org.freedesktop.systemd1")) == -1) + if ((ret = virGDBusIsServiceRegistered(service)) == -1) return ret; - g_atomic_int_set(&virSystemdHasMachinedCachedValue, ret); + + if (requireSystemd || ret == -2) { + if ((ret = virGDBusIsServiceRegistered("org.freedesktop.systemd1")) == -1) + return ret; + } + + g_atomic_int_set(cached, ret); return ret; } + int -virSystemdHasLogind(void) +virSystemdHasMachined(void) { - int ret; - int val; - - val = g_atomic_int_get(&virSystemdHasLogindCachedValue); - if (val != -1) - return val; - - ret = virGDBusIsServiceEnabled("org.freedesktop.login1"); - if (ret < 0) { - if (ret == -2) - g_atomic_int_set(&virSystemdHasLogindCachedValue, -2); - return ret; - } - - /* - * Want to use logind if: - * - logind is already running - * Or - * - logind is not running, but this is a systemd host - * (rely on dbus activation) - */ - if ((ret = virGDBusIsServiceRegistered("org.freedesktop.login1")) == -1) - return ret; + return virSystemdHasService("org.freedesktop.machine1", true, + &virSystemdHasMachinedCachedValue); +} - if (ret == -2) { - if ((ret = virGDBusIsServiceRegistered("org.freedesktop.systemd1")) == -1) - return ret; - } - g_atomic_int_set(&virSystemdHasLogindCachedValue, ret); - return ret; +int +virSystemdHasLogind(void) +{ + return virSystemdHasService("org.freedesktop.login1", false, + &virSystemdHasLogindCachedValue); } -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/libvirt_private.syms | 2 ++ src/util/virsystemd.c | 15 +++++++++++++++ src/util/virsystemd.h | 2 ++ src/util/virsystemdpriv.h | 1 + 4 files changed, 20 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virSystemdHasLogind; virSystemdHasLogindResetCachedValue; virSystemdHasMachined; virSystemdHasMachinedResetCachedValue; +virSystemdHasResolved; +virSystemdHasResolvedResetCachedValue; virSystemdMakeScopeName; virSystemdMakeSliceName; virSystemdNotifyStartup; diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.c +++ b/src/util/virsystemd.c @@ -XXX,XX +XXX,XX @@ char *virSystemdMakeSliceName(const char *partition) static int virSystemdHasMachinedCachedValue = -1; static int virSystemdHasLogindCachedValue = -1; +static int virSystemdHasResolvedCachedValue = -1; /* Reset the cache from tests for testing the underlying dbus calls * as well */ @@ -XXX,XX +XXX,XX @@ void virSystemdHasLogindResetCachedValue(void) virSystemdHasLogindCachedValue = -1; } +void +virSystemdHasResolvedResetCachedValue(void) +{ + virSystemdHasResolvedCachedValue = -1; +} + /** * virSystemdHasService: @@ -XXX,XX +XXX,XX @@ virSystemdHasLogind(void) } +int +virSystemdHasResolved(void) +{ + return virSystemdHasService("org.freedesktop.resolve1", false, + &virSystemdHasResolvedCachedValue); +} + + /** * virSystemdGetMachineByPID: * @conn: dbus connection diff --git a/src/util/virsystemd.h b/src/util/virsystemd.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.h +++ b/src/util/virsystemd.h @@ -XXX,XX +XXX,XX @@ int virSystemdHasMachined(void); int virSystemdHasLogind(void); +int virSystemdHasResolved(void); + int virSystemdCanSuspend(bool *result); int virSystemdCanHibernate(bool *result); diff --git a/src/util/virsystemdpriv.h b/src/util/virsystemdpriv.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemdpriv.h +++ b/src/util/virsystemdpriv.h @@ -XXX,XX +XXX,XX @@ void virSystemdHasMachinedResetCachedValue(void); void virSystemdHasLogindResetCachedValue(void); +void virSystemdHasResolvedResetCachedValue(void); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virsocketaddr.c | 63 ++++++++++++++++++++++++++++++++++++++++ src/util/virsocketaddr.h | 4 +++ 3 files changed, 68 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virSocketSendFD; # util/virsocketaddr.h virSocketAddrBroadcast; virSocketAddrBroadcastByPrefix; +virSocketAddrBytes; virSocketAddrCheckNetmask; virSocketAddrEqual; virSocketAddrFormat; diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsocketaddr.c +++ b/src/util/virsocketaddr.c @@ -XXX,XX +XXX,XX @@ */ typedef unsigned char virSocketAddrIPv4[4]; typedef unsigned short virSocketAddrIPv6[8]; +typedef unsigned char virSocketAddrIPv6Bytes[16]; typedef unsigned char virSocketAddrIPv6Nibbles[32]; static int @@ -XXX,XX +XXX,XX @@ virSocketAddrGetIPv6Addr(const virSocketAddr *addr, virSocketAddrIPv6 *tab) return 0; } + +static int +virSocketAddrGetIPv6Bytes(const virSocketAddr *addr, + virSocketAddrIPv6Bytes *tab) +{ + size_t i; + + if (!addr || !tab || addr->data.stor.ss_family != AF_INET6) + return -1; + + for (i = 0; i < 16; i++) + (*tab)[i] = addr->data.inet6.sin6_addr.s6_addr[i]; + + return 0; +} + + static int virSocketAddrGetIPv6Nibbles(const virSocketAddr *addr, virSocketAddrIPv6Nibbles *tab) @@ -XXX,XX +XXX,XX @@ virSocketAddrPTRDomain(const virSocketAddr *addr, return 0; } + +/** + * virSocketAddrBytes: + * @addr: address to convert to byte array + * @bytes: a preallocated array to store the address bytes to + * @maxBytes: the size of @bytes + * + * Extracts individual bytes of an IPv4 or IPv6 address in the provided @bytes + * array, which should be large enough to store 16 bytes (the size of an IPv6 + * address). Bytes are stored in network order. + * + * Returns the number of bytes stored in @bytes on success or 0 when @bytes + * array is not big enough or @addr is not IPv4 or IPv6. + */ +int +virSocketAddrBytes(const virSocketAddr *addr, + unsigned char *bytes, + int maxBytes) +{ + if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) { + virSocketAddrIPv6Bytes ip; + + if (maxBytes < sizeof(ip)) + return 0; + + virSocketAddrGetIPv6Bytes(addr, &ip); + memcpy(bytes, ip, sizeof(ip)); + return sizeof(ip); + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) { + virSocketAddrIPv4 ip; + + if (maxBytes < sizeof(ip)) + return 0; + + virSocketAddrGetIPv4Addr(addr, &ip); + memcpy(bytes, ip, sizeof(ip)); + return sizeof(ip); + } + + return 0; +} + + void virSocketAddrFree(virSocketAddr *addr) { diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -XXX,XX +XXX,XX @@ int virSocketAddrPTRDomain(const virSocketAddr *addr, char **ptr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); +int virSocketAddrBytes(const virSocketAddr *addr, + unsigned char *bytes, + int maxBytes); + void virSocketAddrFree(virSocketAddr *addr); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSocketAddr, virSocketAddrFree); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virsystemd.c | 79 +++++++++++++++++++++++++++++++++++++++- src/util/virsystemd.h | 5 +++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virSystemdHasResolvedResetCachedValue; virSystemdMakeScopeName; virSystemdMakeSliceName; virSystemdNotifyStartup; +virSystemdResolvedRegisterNameServer; virSystemdTerminateMachine; diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.c +++ b/src/util/virsystemd.c @@ -XXX,XX +XXX,XX @@ #include "virerror.h" #include "virfile.h" #include "virhash.h" -#include "virsocketaddr.h" #define VIR_FROM_THIS VIR_FROM_SYSTEMD @@ -XXX,XX +XXX,XX @@ virSystemdActivationFree(virSystemdActivation *act) g_free(act); } + + +/** + * virSystemdResolvedRegisterNameServer: + * @link: network interface ID + * @domain: registered domain + * @addr: address the DNS server is listening on + * + * Talk to systemd-resolved and register a DNS server listening on @addr + * as a resolver for @domain. This configuration is bound to @link interface + * and automatically dropped when the interface goes away. + * + * Returns -2 when systemd-resolved is unavailable, + * -1 on error, + * 0 on success. + */ +int +virSystemdResolvedRegisterNameServer(int link, + const char *domain, + virSocketAddr *addr) +{ + int rc; + GDBusConnection *conn; + GVariant *params; + GVariant *byteArray; + unsigned char addrBytes[16]; + int nBytes; + + if ((rc = virSystemdHasResolved()) < 0) + return rc; + + if (!(conn = virGDBusGetSystemBus())) + return -1; + + /* + * SetLinkDomains(in i ifindex, + * in a(sb) domains); + */ + params = g_variant_new_parsed("(%i, [(%s, true)])", + (gint32) link, + domain); + + rc = virGDBusCallMethod(conn, NULL, NULL, NULL, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "SetLinkDomains", + params); + g_variant_unref(params); + + if (rc < 0) + return -1; + + /* + * SetLinkDNS(in i ifindex, + * in a(iay) addresses); + */ + nBytes = virSocketAddrBytes(addr, addrBytes, sizeof(addrBytes)); + byteArray = g_variant_new_fixed_array(G_VARIANT_TYPE("y"), + addrBytes, nBytes, 1); + params = g_variant_new_parsed("(%i, [(%i, %@ay)])", + (gint32) link, + VIR_SOCKET_ADDR_FAMILY(addr), + byteArray); + + rc = virGDBusCallMethod(conn, NULL, NULL, NULL, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "SetLinkDNS", + params); + g_variant_unref(params); + + if (rc < 0) + return -1; + + return 0; +} diff --git a/src/util/virsystemd.h b/src/util/virsystemd.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.h +++ b/src/util/virsystemd.h @@ -XXX,XX +XXX,XX @@ #pragma once #include "internal.h" +#include "virsocketaddr.h" typedef struct _virSystemdActivation virSystemdActivation; @@ -XXX,XX +XXX,XX @@ void virSystemdActivationClaimFDs(virSystemdActivation *act, void virSystemdActivationFree(virSystemdActivation *act); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSystemdActivation, virSystemdActivationFree); + +int virSystemdResolvedRegisterNameServer(int link, + const char *domain, + virSocketAddr *addr); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- tests/virsystemdtest.c | 147 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/tests/virsystemdtest.c b/tests/virsystemdtest.c index XXXXXXX..XXXXXXX 100644 --- a/tests/virsystemdtest.c +++ b/tests/virsystemdtest.c @@ -XXX,XX +XXX,XX @@ VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync, reply = g_variant_new("()"); } } + } else if (STREQ(bus_name, "org.freedesktop.resolve1")) { + g_autofree char *actual = NULL; + + if (getenv("FAIL_BAD_SERVICE")) { + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Contacting resolved failed"); + } else if (STREQ(method_name, "SetLinkDomains")) { + const char *expected = getenv("TEST_RESOLVED_LINK_DOMAINS"); + actual = g_variant_print(params, FALSE); + + if (virTestCompareToString(expected, actual) < 0) + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Unexpected params to SetLinkDomains"); + else + reply = g_variant_new("()"); + } else if (STREQ(method_name, "SetLinkDNS")) { + const char *expected = getenv("TEST_RESOLVED_LINK_DNS"); + actual = g_variant_print(params, FALSE); + + if (virTestCompareToString(expected, actual) < 0) + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Unexpected params to SetLinkDNS"); + else + reply = g_variant_new("()"); + } else { + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Unknown resolved method"); + } } else if (STREQ(bus_name, "org.freedesktop.login1")) { reply = g_variant_new("(s)", getenv("RESULT_SUPPORT")); } else if (STREQ(bus_name, "org.freedesktop.DBus") && @@ -XXX,XX +XXX,XX @@ VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync, if (!getenv("FAIL_NO_SERVICE")) { g_variant_builder_add(&builder, "s", "org.freedesktop.machine1"); g_variant_builder_add(&builder, "s", "org.freedesktop.login1"); + g_variant_builder_add(&builder, "s", "org.freedesktop.resolve1"); } reply = g_variant_new("(@as)", g_variant_builder_end(&builder)); @@ -XXX,XX +XXX,XX @@ VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync, if (!getenv("FAIL_NO_SERVICE") && !getenv("FAIL_NOT_REGISTERED")) { g_variant_builder_add(&builder, "s", "org.freedesktop.systemd1"); g_variant_builder_add(&builder, "s", "org.freedesktop.login1"); + g_variant_builder_add(&builder, "s", "org.freedesktop.resolve1"); } reply = g_variant_new("(@as)", g_variant_builder_end(&builder)); @@ -XXX,XX +XXX,XX @@ testActivationEmpty(const void *opaque G_GNUC_UNUSED) return 0; } + +struct testResolvedData { + int link; + const char *env; + const char *dom; + const char *addr; + const char *expDomains; + const char *expDNS; +}; + + +static int +testResolvedFailed(const void *opaque) +{ + const struct testResolvedData *data = opaque; + virSocketAddr addr = {0}; + int ret = -1; + int expected = -2; + int rv; + + if (!data->env) { + fprintf(stderr, "%s", + "testResolvedFailed called without environment variable name"); + return -1; + } + + if (data->addr && + virSocketAddrParse(&addr, data->addr, AF_UNSPEC) < 0) + return -1; + + if (STREQ(data->env, "FAIL_BAD_SERVICE")) + expected = -1; + + g_setenv(data->env, "1", TRUE); + + rv = virSystemdResolvedRegisterNameServer(data->link, data->dom, &addr); + + if (rv == 0) + fprintf(stderr, "%s", "Updating resolved succeeded unexpectedly\n"); + else if (rv != expected) + fprintf(stderr, "%s", "Updating resolved failed unexpectedly\n"); + else + ret = 0; + + g_unsetenv(data->env); + return ret; +} + + +static int +testResolved(const void *opaque) +{ + const struct testResolvedData *data = opaque; + virSocketAddr addr = {0}; + int ret = -1; + + if (data->addr && + virSocketAddrParse(&addr, data->addr, AF_UNSPEC) < 0) + return -1; + + g_setenv("TEST_RESOLVED_LINK_DOMAINS", data->expDomains, TRUE); + g_setenv("TEST_RESOLVED_LINK_DNS", data->expDNS, TRUE); + + if (virSystemdResolvedRegisterNameServer(data->link, data->dom, &addr) < 0) { + fprintf(stderr, "%s", "Updating resolved failed unexpectedly\n"); + goto cleanup; + } + + ret = 0; + + cleanup: + g_unsetenv("TEST_RESOLVED_LINK_DOMAINS"); + g_unsetenv("TEST_RESOLVED_LINK_DNS"); + return ret; +} + + static int mymain(void) { @@ -XXX,XX +XXX,XX @@ mymain(void) VIR_INFO("Skipping activation tests as FD 3/4/5 is open"); } +# define TEST_RESOLVED_HELPER(name, func, linkId, variable, domain, ip, \ + expectedDomains, expectedDNS) \ + do { \ + struct testResolvedData data = { \ + .link = linkId, \ + .env = variable, \ + .dom = domain, \ + .addr = ip, \ + .expDomains = expectedDomains, \ + .expDNS = expectedDNS, \ + }; \ + if (virTestRun("Test resolved " name, func, &data) < 0) \ + ret = -1; \ + virSystemdHasResolvedResetCachedValue(); \ + } while (0) + +# define TEST_RESOLVED_FAILURE(name, variable, ip) \ + TEST_RESOLVED_HELPER(name, testResolvedFailed, 42, variable, "virt", ip, NULL, NULL) + +# define TEST_RESOLVED_EXP(name, linkId, domain, ip, ipFamily, ipBytes) \ + TEST_RESOLVED_HELPER(name, testResolved, linkId, NULL, domain, ip, \ + "(" #linkId ", [('" domain "', true)])", \ + "(" #linkId ", [(" #ipFamily ", [" ipBytes "])])") + +# define TEST_RESOLVED(name, linkId, domain, ip, ipFamily, ipBytes) \ + TEST_RESOLVED_EXP(name, linkId, domain, ip, ipFamily, ipBytes) + + TEST_RESOLVED_FAILURE("not enabled", "FAIL_NO_SERVICE", NULL); + TEST_RESOLVED_FAILURE("not registered", "FAIL_NOT_REGISTERED", NULL); + TEST_RESOLVED_FAILURE("bad service", "FAIL_BAD_SERVICE", "1.2.3.4"); + + TEST_RESOLVED("IPv4", 84, "private", + "172.17.18.19", AF_INET, + "0xac, 0x11, 0x12, 0x13"); + + TEST_RESOLVED("IPv6", 21, "virt.local", + "e8f7:ae03:b089:d4e1:764a:53ec:658b:2547", AF_INET6, + "0xe8, 0xf7, 0xae, 0x03, 0xb0, 0x89, 0xd4, 0xe1, " + "0x76, 0x4a, 0x53, 0xec, 0x65, 0x8b, 0x25, 0x47"); + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
This patch adds a new attribute "register" to the <domain> element. If set to "yes", the DNS server created for the virtual network is registered with systemd-resolved as a name server for the associated domain. The names known to the dnsmasq process serving DNS and DHCP requests for the virtual network will then be resolvable from the host by appending the domain name to them. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- docs/formatnetwork.rst | 9 ++++++++- src/conf/network_conf.c | 18 ++++++++++++++++++ src/conf/network_conf.h | 1 + src/conf/schemas/network.rng | 3 +++ src/network/bridge_driver.c | 32 +++++++++++++++++++++++++++++++- 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/formatnetwork.rst b/docs/formatnetwork.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/formatnetwork.rst +++ b/docs/formatnetwork.rst @@ -XXX,XX +XXX,XX @@ to the physical LAN (if at all). ... <bridge name="virbr0" stp="on" delay="5" macTableManager="libvirt"/> <mtu size="9000"/> - <domain name="example.com" localOnly="no"/> + <domain name="example.com" localOnly="no" register="no"/> <forward mode="nat" dev="eth0"/> ... @@ -XXX,XX +XXX,XX @@ to the physical LAN (if at all). DNS server. If ``localOnly`` is "no", and by default, unresolved requests **will** be forwarded. :since:`Since 1.2.12` + :since:`Since 10.1.0` the optional ``register`` attribute can be used to + request registering the DNS server for resolving this domain with the host's + DNS resolver. When set to "yes", the host resolver will forward all requests + for domain names from this domain to the DNS server created for this virtual + network. To avoid DNS loops ``localOnly`` has to be set to "yes" as well. + This feature requires ``systemd-resolved`` to be running on the host. + ``forward`` Inclusion of the ``forward`` element indicates that the virtual network is to be connected to the physical LAN. :since:`Since 0.3.0.` The ``mode`` diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index XXXXXXX..XXXXXXX 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -XXX,XX +XXX,XX @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt, &def->domainLocalOnly) < 0) return NULL; + if (virXMLPropTristateBool(domain_node, "register", + VIR_XML_PROP_NONE, + &def->domainRegister) < 0) + return NULL; + + if (def->domainRegister == VIR_TRISTATE_BOOL_YES && + def->domainLocalOnly != VIR_TRISTATE_BOOL_YES) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("attribute 'register=yes' in <domain> element requires 'localOnly=yes' in network %1$s"), + def->name); + return NULL; + } + if ((bandwidthNode = virXPathNode("./bandwidth", ctxt)) && virNetDevBandwidthParse(&def->bandwidth, NULL, bandwidthNode, false) < 0) return NULL; @@ -XXX,XX +XXX,XX @@ virNetworkDefFormatBuf(virBuffer *buf, virBufferAsprintf(buf, " localOnly='%s'", local); } + if (def->domainRegister) { + virBufferAsprintf(buf, " register='%s'", + virTristateBoolTypeToString(def->domainRegister)); + } + virBufferAddLit(buf, "/>\n"); } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index XXXXXXX..XXXXXXX 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -XXX,XX +XXX,XX @@ struct _virNetworkDef { int macTableManager; /* enum virNetworkBridgeMACTableManager */ char *domain; virTristateBool domainLocalOnly; /* yes disables dns forwarding */ + virTristateBool domainRegister; unsigned long delay; /* Bridge forward delay (ms) */ bool stp; /* Spanning tree protocol */ unsigned int mtu; /* MTU for bridge, 0 means "default" i.e. unset in config */ diff --git a/src/conf/schemas/network.rng b/src/conf/schemas/network.rng index XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/network.rng +++ b/src/conf/schemas/network.rng @@ -XXX,XX +XXX,XX @@ <optional> <attribute name="localOnly"><ref name="virYesNo"/></attribute> </optional> + <optional> + <attribute name="register"><ref name="virYesNo"/></attribute> + </optional> </element> </optional> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index XXXXXXX..XXXXXXX 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -XXX,XX +XXX,XX @@ #include "virjson.h" #include "virnetworkportdef.h" #include "virutil.h" - +#include "virsystemd.h" #include "netdev_bandwidth_conf.h" #define VIR_FROM_THIS VIR_FROM_NETWORK @@ -XXX,XX +XXX,XX @@ networkStartNetworkVirtual(virNetworkDriverState *driver, bool dnsmasqStarted = false; bool devOnline = false; bool firewalRulesAdded = false; + virSocketAddr *dnsServer = NULL; /* Check to see if any network IP collides with an existing route */ if (networkCheckRouteCollision(def) < 0) @@ -XXX,XX +XXX,XX @@ networkStartNetworkVirtual(virNetworkDriverState *driver, if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) v6present = true; + if (!dnsServer) + dnsServer = &ipdef->address; + /* Add the IP address/netmask to the bridge */ if (networkAddAddrToBridge(obj, ipdef) < 0) goto error; @@ -XXX,XX +XXX,XX @@ networkStartNetworkVirtual(virNetworkDriverState *driver, goto error; dnsmasqStarted = true; + + if (def->domain && def->domainRegister && dnsServer) { + unsigned int link; + int rc; + + if ((link = if_nametoindex(def->bridge)) == 0) { + virReportSystemError(ENODEV, + _("unable to get interface index for %1$s"), + def->bridge); + goto error; + } + + rc = virSystemdResolvedRegisterNameServer(link, def->domain, + dnsServer); + if (rc == -2) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("failed to register name server: systemd-resolved is not available")); + goto error; + } + + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to register name server")); + goto error; + } + } } if (virNetDevBandwidthSet(def->bridge, def->bandwidth, true, true) < 0) -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index XXXXXXX..XXXXXXX 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -XXX,XX +XXX,XX @@ v10.1.0 (unreleased) Additionally, if CPU clusters are present in the host topology, they will be reported as part of the capabilities XML. + * network: Make virtual domains resolvable from the host + + When starting a virtual network with a new ``register='yes'`` attribute + in the ``<domain>`` element, libvirt will configure ``systemd-resolved`` + to resolve names of the connected guests using the name server started + for this network. + * **Improvements** * **Bug fixes** -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
See 6/7 for more details. Version 2: - Patch 2/7 no longer tries (or pretends) to be a refactor. Instead it unifies they way we check for systemd services. - requireSystemd argument of virSystemdHasService was dropped Jiri Denemark (7): util: Unify virSystemdHas{Machined,Logind} util: Introduce virSystemdHasResolved util: Introduce virSocketAddrBytes util: Introduce virSystemdResolvedRegisterNameServer tests: Add tests for virSystemdResolvedRegisterNameServer network: Make virtual domains resolvable from the host NEWS: Mention systemd-resolved support in network driver NEWS.rst | 7 ++ docs/formatnetwork.rst | 9 +- src/conf/network_conf.c | 18 ++++ src/conf/network_conf.h | 1 + src/conf/schemas/network.rng | 3 + src/libvirt_private.syms | 4 + src/network/bridge_driver.c | 32 ++++++- src/util/virsocketaddr.c | 63 ++++++++++++++ src/util/virsocketaddr.h | 4 + src/util/virsystemd.c | 164 ++++++++++++++++++++++++++--------- src/util/virsystemd.h | 7 ++ src/util/virsystemdpriv.h | 1 + tests/virsystemdtest.c | 147 +++++++++++++++++++++++++++++++ 13 files changed, 419 insertions(+), 41 deletions(-) -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
When checking for machined we do not really care whether systemd itself is running, we just need machined to be either running or socket activated by systemd. That is, exactly the same we do for logind. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/util/virsystemd.c | 74 ++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.c +++ b/src/util/virsystemd.c @@ -XXX,XX +XXX,XX @@ void virSystemdHasLogindResetCachedValue(void) } -/* -2 = machine1 is not supported on this machine - * -1 = error - * 0 = machine1 is available +/** + * virSystemdHasService: + * + * Check whether a DBus @service is enabled and either the service itself is + * running or will be started on demand by a running systemd1 service. + * + * Returns + * -2 when service is not supported on this machine + * -1 on error + * 0 when service is available */ -int -virSystemdHasMachined(void) -{ - int ret; - int val; - - val = g_atomic_int_get(&virSystemdHasMachinedCachedValue); - if (val != -1) - return val; - - if ((ret = virGDBusIsServiceEnabled("org.freedesktop.machine1")) < 0) { - if (ret == -2) - g_atomic_int_set(&virSystemdHasMachinedCachedValue, -2); - return ret; - } - - if ((ret = virGDBusIsServiceRegistered("org.freedesktop.systemd1")) == -1) - return ret; - g_atomic_int_set(&virSystemdHasMachinedCachedValue, ret); - return ret; -} - -int -virSystemdHasLogind(void) +static int +virSystemdHasService(const char *service, + int *cached) { int ret; int val; - val = g_atomic_int_get(&virSystemdHasLogindCachedValue); + val = g_atomic_int_get(cached); if (val != -1) return val; - ret = virGDBusIsServiceEnabled("org.freedesktop.login1"); - if (ret < 0) { + if ((ret = virGDBusIsServiceEnabled(service)) < 0) { if (ret == -2) - g_atomic_int_set(&virSystemdHasLogindCachedValue, -2); + g_atomic_int_set(cached, -2); return ret; } - /* - * Want to use logind if: - * - logind is already running - * Or - * - logind is not running, but this is a systemd host - * (rely on dbus activation) - */ - if ((ret = virGDBusIsServiceRegistered("org.freedesktop.login1")) == -1) + if ((ret = virGDBusIsServiceRegistered(service)) == -1) return ret; if (ret == -2) { @@ -XXX,XX +XXX,XX @@ virSystemdHasLogind(void) return ret; } - g_atomic_int_set(&virSystemdHasLogindCachedValue, ret); + g_atomic_int_set(cached, ret); return ret; } +int +virSystemdHasMachined(void) +{ + return virSystemdHasService("org.freedesktop.machine1", + &virSystemdHasMachinedCachedValue); +} + + +int +virSystemdHasLogind(void) +{ + return virSystemdHasService("org.freedesktop.login1", + &virSystemdHasLogindCachedValue); +} + + /** * virSystemdGetMachineByPID: * @conn: dbus connection -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> --- src/libvirt_private.syms | 2 ++ src/util/virsystemd.c | 15 +++++++++++++++ src/util/virsystemd.h | 2 ++ src/util/virsystemdpriv.h | 1 + 4 files changed, 20 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virSystemdHasLogind; virSystemdHasLogindResetCachedValue; virSystemdHasMachined; virSystemdHasMachinedResetCachedValue; +virSystemdHasResolved; +virSystemdHasResolvedResetCachedValue; virSystemdMakeScopeName; virSystemdMakeSliceName; virSystemdNotifyStartup; diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.c +++ b/src/util/virsystemd.c @@ -XXX,XX +XXX,XX @@ char *virSystemdMakeSliceName(const char *partition) static int virSystemdHasMachinedCachedValue = -1; static int virSystemdHasLogindCachedValue = -1; +static int virSystemdHasResolvedCachedValue = -1; /* Reset the cache from tests for testing the underlying dbus calls * as well */ @@ -XXX,XX +XXX,XX @@ void virSystemdHasLogindResetCachedValue(void) virSystemdHasLogindCachedValue = -1; } +void +virSystemdHasResolvedResetCachedValue(void) +{ + virSystemdHasResolvedCachedValue = -1; +} + /** * virSystemdHasService: @@ -XXX,XX +XXX,XX @@ virSystemdHasLogind(void) } +int +virSystemdHasResolved(void) +{ + return virSystemdHasService("org.freedesktop.resolve1", + &virSystemdHasResolvedCachedValue); +} + + /** * virSystemdGetMachineByPID: * @conn: dbus connection diff --git a/src/util/virsystemd.h b/src/util/virsystemd.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.h +++ b/src/util/virsystemd.h @@ -XXX,XX +XXX,XX @@ int virSystemdHasMachined(void); int virSystemdHasLogind(void); +int virSystemdHasResolved(void); + int virSystemdCanSuspend(bool *result); int virSystemdCanHibernate(bool *result); diff --git a/src/util/virsystemdpriv.h b/src/util/virsystemdpriv.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemdpriv.h +++ b/src/util/virsystemdpriv.h @@ -XXX,XX +XXX,XX @@ void virSystemdHasMachinedResetCachedValue(void); void virSystemdHasLogindResetCachedValue(void); +void virSystemdHasResolvedResetCachedValue(void); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virsocketaddr.c | 63 ++++++++++++++++++++++++++++++++++++++++ src/util/virsocketaddr.h | 4 +++ 3 files changed, 68 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virSocketSendMsgWithFDs; # util/virsocketaddr.h virSocketAddrBroadcast; virSocketAddrBroadcastByPrefix; +virSocketAddrBytes; virSocketAddrCheckNetmask; virSocketAddrEqual; virSocketAddrFormat; diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsocketaddr.c +++ b/src/util/virsocketaddr.c @@ -XXX,XX +XXX,XX @@ */ typedef unsigned char virSocketAddrIPv4[4]; typedef unsigned short virSocketAddrIPv6[8]; +typedef unsigned char virSocketAddrIPv6Bytes[16]; typedef unsigned char virSocketAddrIPv6Nibbles[32]; static int @@ -XXX,XX +XXX,XX @@ virSocketAddrGetIPv6Addr(const virSocketAddr *addr, virSocketAddrIPv6 *tab) return 0; } + +static int +virSocketAddrGetIPv6Bytes(const virSocketAddr *addr, + virSocketAddrIPv6Bytes *tab) +{ + size_t i; + + if (!addr || !tab || addr->data.stor.ss_family != AF_INET6) + return -1; + + for (i = 0; i < 16; i++) + (*tab)[i] = addr->data.inet6.sin6_addr.s6_addr[i]; + + return 0; +} + + static int virSocketAddrGetIPv6Nibbles(const virSocketAddr *addr, virSocketAddrIPv6Nibbles *tab) @@ -XXX,XX +XXX,XX @@ virSocketAddrPTRDomain(const virSocketAddr *addr, return 0; } + +/** + * virSocketAddrBytes: + * @addr: address to convert to byte array + * @bytes: a preallocated array to store the address bytes to + * @maxBytes: the size of @bytes + * + * Extracts individual bytes of an IPv4 or IPv6 address in the provided @bytes + * array, which should be large enough to store 16 bytes (the size of an IPv6 + * address). Bytes are stored in network order. + * + * Returns the number of bytes stored in @bytes on success or 0 when @bytes + * array is not big enough or @addr is not IPv4 or IPv6. + */ +int +virSocketAddrBytes(const virSocketAddr *addr, + unsigned char *bytes, + int maxBytes) +{ + if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) { + virSocketAddrIPv6Bytes ip; + + if (maxBytes < sizeof(ip)) + return 0; + + virSocketAddrGetIPv6Bytes(addr, &ip); + memcpy(bytes, ip, sizeof(ip)); + return sizeof(ip); + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) { + virSocketAddrIPv4 ip; + + if (maxBytes < sizeof(ip)) + return 0; + + virSocketAddrGetIPv4Addr(addr, &ip); + memcpy(bytes, ip, sizeof(ip)); + return sizeof(ip); + } + + return 0; +} + + void virSocketAddrFree(virSocketAddr *addr) { diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -XXX,XX +XXX,XX @@ int virSocketAddrPTRDomain(const virSocketAddr *addr, char **ptr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); +int virSocketAddrBytes(const virSocketAddr *addr, + unsigned char *bytes, + int maxBytes); + void virSocketAddrFree(virSocketAddr *addr); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSocketAddr, virSocketAddrFree); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virsystemd.c | 79 +++++++++++++++++++++++++++++++++++++++- src/util/virsystemd.h | 5 +++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virSystemdHasResolvedResetCachedValue; virSystemdMakeScopeName; virSystemdMakeSliceName; virSystemdNotifyStartup; +virSystemdResolvedRegisterNameServer; virSystemdTerminateMachine; diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.c +++ b/src/util/virsystemd.c @@ -XXX,XX +XXX,XX @@ #include "virerror.h" #include "virfile.h" #include "virhash.h" -#include "virsocketaddr.h" #define VIR_FROM_THIS VIR_FROM_SYSTEMD @@ -XXX,XX +XXX,XX @@ virSystemdActivationFree(virSystemdActivation *act) g_free(act); } + + +/** + * virSystemdResolvedRegisterNameServer: + * @link: network interface ID + * @domain: registered domain + * @addr: address the DNS server is listening on + * + * Talk to systemd-resolved and register a DNS server listening on @addr + * as a resolver for @domain. This configuration is bound to @link interface + * and automatically dropped when the interface goes away. + * + * Returns -2 when systemd-resolved is unavailable, + * -1 on error, + * 0 on success. + */ +int +virSystemdResolvedRegisterNameServer(int link, + const char *domain, + virSocketAddr *addr) +{ + int rc; + GDBusConnection *conn; + GVariant *params; + GVariant *byteArray; + unsigned char addrBytes[16]; + int nBytes; + + if ((rc = virSystemdHasResolved()) < 0) + return rc; + + if (!(conn = virGDBusGetSystemBus())) + return -1; + + /* + * SetLinkDomains(in i ifindex, + * in a(sb) domains); + */ + params = g_variant_new_parsed("(%i, [(%s, true)])", + (gint32) link, + domain); + + rc = virGDBusCallMethod(conn, NULL, NULL, NULL, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "SetLinkDomains", + params); + g_variant_unref(params); + + if (rc < 0) + return -1; + + /* + * SetLinkDNS(in i ifindex, + * in a(iay) addresses); + */ + nBytes = virSocketAddrBytes(addr, addrBytes, sizeof(addrBytes)); + byteArray = g_variant_new_fixed_array(G_VARIANT_TYPE("y"), + addrBytes, nBytes, 1); + params = g_variant_new_parsed("(%i, [(%i, %@ay)])", + (gint32) link, + VIR_SOCKET_ADDR_FAMILY(addr), + byteArray); + + rc = virGDBusCallMethod(conn, NULL, NULL, NULL, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "SetLinkDNS", + params); + g_variant_unref(params); + + if (rc < 0) + return -1; + + return 0; +} diff --git a/src/util/virsystemd.h b/src/util/virsystemd.h index XXXXXXX..XXXXXXX 100644 --- a/src/util/virsystemd.h +++ b/src/util/virsystemd.h @@ -XXX,XX +XXX,XX @@ #pragma once #include "internal.h" +#include "virsocketaddr.h" typedef struct _virSystemdActivation virSystemdActivation; @@ -XXX,XX +XXX,XX @@ void virSystemdActivationClaimFDs(virSystemdActivation *act, void virSystemdActivationFree(virSystemdActivation *act); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSystemdActivation, virSystemdActivationFree); + +int virSystemdResolvedRegisterNameServer(int link, + const char *domain, + virSocketAddr *addr); -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> --- tests/virsystemdtest.c | 147 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/tests/virsystemdtest.c b/tests/virsystemdtest.c index XXXXXXX..XXXXXXX 100644 --- a/tests/virsystemdtest.c +++ b/tests/virsystemdtest.c @@ -XXX,XX +XXX,XX @@ VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync, reply = g_variant_new("()"); } } + } else if (STREQ(bus_name, "org.freedesktop.resolve1")) { + g_autofree char *actual = NULL; + + if (getenv("FAIL_BAD_SERVICE")) { + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Contacting resolved failed"); + } else if (STREQ(method_name, "SetLinkDomains")) { + const char *expected = getenv("TEST_RESOLVED_LINK_DOMAINS"); + actual = g_variant_print(params, FALSE); + + if (virTestCompareToString(expected, actual) < 0) + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Unexpected params to SetLinkDomains"); + else + reply = g_variant_new("()"); + } else if (STREQ(method_name, "SetLinkDNS")) { + const char *expected = getenv("TEST_RESOLVED_LINK_DNS"); + actual = g_variant_print(params, FALSE); + + if (virTestCompareToString(expected, actual) < 0) + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Unexpected params to SetLinkDNS"); + else + reply = g_variant_new("()"); + } else { + *error = g_dbus_error_new_for_dbus_error("org.freedesktop.systemd.badthing", + "Unknown resolved method"); + } } else if (STREQ(bus_name, "org.freedesktop.login1")) { reply = g_variant_new("(s)", getenv("RESULT_SUPPORT")); } else if (STREQ(bus_name, "org.freedesktop.DBus") && @@ -XXX,XX +XXX,XX @@ VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync, if (!getenv("FAIL_NO_SERVICE")) { g_variant_builder_add(&builder, "s", "org.freedesktop.machine1"); g_variant_builder_add(&builder, "s", "org.freedesktop.login1"); + g_variant_builder_add(&builder, "s", "org.freedesktop.resolve1"); } reply = g_variant_new("(@as)", g_variant_builder_end(&builder)); @@ -XXX,XX +XXX,XX @@ VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync, if (!getenv("FAIL_NO_SERVICE") && !getenv("FAIL_NOT_REGISTERED")) { g_variant_builder_add(&builder, "s", "org.freedesktop.systemd1"); g_variant_builder_add(&builder, "s", "org.freedesktop.login1"); + g_variant_builder_add(&builder, "s", "org.freedesktop.resolve1"); } reply = g_variant_new("(@as)", g_variant_builder_end(&builder)); @@ -XXX,XX +XXX,XX @@ testActivationEmpty(const void *opaque G_GNUC_UNUSED) return 0; } + +struct testResolvedData { + int link; + const char *env; + const char *dom; + const char *addr; + const char *expDomains; + const char *expDNS; +}; + + +static int +testResolvedFailed(const void *opaque) +{ + const struct testResolvedData *data = opaque; + virSocketAddr addr = {0}; + int ret = -1; + int expected = -2; + int rv; + + if (!data->env) { + fprintf(stderr, "%s", + "testResolvedFailed called without environment variable name"); + return -1; + } + + if (data->addr && + virSocketAddrParse(&addr, data->addr, AF_UNSPEC) < 0) + return -1; + + if (STREQ(data->env, "FAIL_BAD_SERVICE")) + expected = -1; + + g_setenv(data->env, "1", TRUE); + + rv = virSystemdResolvedRegisterNameServer(data->link, data->dom, &addr); + + if (rv == 0) + fprintf(stderr, "%s", "Updating resolved succeeded unexpectedly\n"); + else if (rv != expected) + fprintf(stderr, "%s", "Updating resolved failed unexpectedly\n"); + else + ret = 0; + + g_unsetenv(data->env); + return ret; +} + + +static int +testResolved(const void *opaque) +{ + const struct testResolvedData *data = opaque; + virSocketAddr addr = {0}; + int ret = -1; + + if (data->addr && + virSocketAddrParse(&addr, data->addr, AF_UNSPEC) < 0) + return -1; + + g_setenv("TEST_RESOLVED_LINK_DOMAINS", data->expDomains, TRUE); + g_setenv("TEST_RESOLVED_LINK_DNS", data->expDNS, TRUE); + + if (virSystemdResolvedRegisterNameServer(data->link, data->dom, &addr) < 0) { + fprintf(stderr, "%s", "Updating resolved failed unexpectedly\n"); + goto cleanup; + } + + ret = 0; + + cleanup: + g_unsetenv("TEST_RESOLVED_LINK_DOMAINS"); + g_unsetenv("TEST_RESOLVED_LINK_DNS"); + return ret; +} + + static int mymain(void) { @@ -XXX,XX +XXX,XX @@ mymain(void) VIR_INFO("Skipping activation tests as FD 3/4/5 is open"); } +# define TEST_RESOLVED_HELPER(name, func, linkId, variable, domain, ip, \ + expectedDomains, expectedDNS) \ + do { \ + struct testResolvedData data = { \ + .link = linkId, \ + .env = variable, \ + .dom = domain, \ + .addr = ip, \ + .expDomains = expectedDomains, \ + .expDNS = expectedDNS, \ + }; \ + if (virTestRun("Test resolved " name, func, &data) < 0) \ + ret = -1; \ + virSystemdHasResolvedResetCachedValue(); \ + } while (0) + +# define TEST_RESOLVED_FAILURE(name, variable, ip) \ + TEST_RESOLVED_HELPER(name, testResolvedFailed, 42, variable, "virt", ip, NULL, NULL) + +# define TEST_RESOLVED_EXP(name, linkId, domain, ip, ipFamily, ipBytes) \ + TEST_RESOLVED_HELPER(name, testResolved, linkId, NULL, domain, ip, \ + "(" #linkId ", [('" domain "', true)])", \ + "(" #linkId ", [(" #ipFamily ", [" ipBytes "])])") + +# define TEST_RESOLVED(name, linkId, domain, ip, ipFamily, ipBytes) \ + TEST_RESOLVED_EXP(name, linkId, domain, ip, ipFamily, ipBytes) + + TEST_RESOLVED_FAILURE("not enabled", "FAIL_NO_SERVICE", NULL); + TEST_RESOLVED_FAILURE("not registered", "FAIL_NOT_REGISTERED", NULL); + TEST_RESOLVED_FAILURE("bad service", "FAIL_BAD_SERVICE", "1.2.3.4"); + + TEST_RESOLVED("IPv4", 84, "private", + "172.17.18.19", AF_INET, + "0xac, 0x11, 0x12, 0x13"); + + TEST_RESOLVED("IPv6", 21, "virt.local", + "e8f7:ae03:b089:d4e1:764a:53ec:658b:2547", AF_INET6, + "0xe8, 0xf7, 0xae, 0x03, 0xb0, 0x89, 0xd4, 0xe1, " + "0x76, 0x4a, 0x53, 0xec, 0x65, 0x8b, 0x25, 0x47"); + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
This patch adds a new attribute "register" to the <domain> element. If set to "yes", the DNS server created for the virtual network is registered with systemd-resolved as a name server for the associated domain. The names known to the dnsmasq process serving DNS and DHCP requests for the virtual network will then be resolvable from the host by appending the domain name to them. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> --- docs/formatnetwork.rst | 9 ++++++++- src/conf/network_conf.c | 18 ++++++++++++++++++ src/conf/network_conf.h | 1 + src/conf/schemas/network.rng | 3 +++ src/network/bridge_driver.c | 32 +++++++++++++++++++++++++++++++- 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/formatnetwork.rst b/docs/formatnetwork.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/formatnetwork.rst +++ b/docs/formatnetwork.rst @@ -XXX,XX +XXX,XX @@ to the physical LAN (if at all). ... <bridge name="virbr0" stp="on" delay="5" macTableManager="libvirt"/> <mtu size="9000"/> - <domain name="example.com" localOnly="no"/> + <domain name="example.com" localOnly="no" register="no"/> <forward mode="nat" dev="eth0"/> ... @@ -XXX,XX +XXX,XX @@ to the physical LAN (if at all). DNS server. If ``localOnly`` is "no", and by default, unresolved requests **will** be forwarded. :since:`Since 1.2.12` + :since:`Since 10.1.0` the optional ``register`` attribute can be used to + request registering the DNS server for resolving this domain with the host's + DNS resolver. When set to "yes", the host resolver will forward all requests + for domain names from this domain to the DNS server created for this virtual + network. To avoid DNS loops ``localOnly`` has to be set to "yes" as well. + This feature requires ``systemd-resolved`` to be running on the host. + ``forward`` Inclusion of the ``forward`` element indicates that the virtual network is to be connected to the physical LAN. :since:`Since 0.3.0.` The ``mode`` diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index XXXXXXX..XXXXXXX 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -XXX,XX +XXX,XX @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt, &def->domainLocalOnly) < 0) return NULL; + if (virXMLPropTristateBool(domain_node, "register", + VIR_XML_PROP_NONE, + &def->domainRegister) < 0) + return NULL; + + if (def->domainRegister == VIR_TRISTATE_BOOL_YES && + def->domainLocalOnly != VIR_TRISTATE_BOOL_YES) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("attribute 'register=yes' in <domain> element requires 'localOnly=yes' in network %1$s"), + def->name); + return NULL; + } + if ((bandwidthNode = virXPathNode("./bandwidth", ctxt)) && virNetDevBandwidthParse(&def->bandwidth, NULL, bandwidthNode, false) < 0) return NULL; @@ -XXX,XX +XXX,XX @@ virNetworkDefFormatBuf(virBuffer *buf, virBufferAsprintf(buf, " localOnly='%s'", local); } + if (def->domainRegister) { + virBufferAsprintf(buf, " register='%s'", + virTristateBoolTypeToString(def->domainRegister)); + } + virBufferAddLit(buf, "/>\n"); } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index XXXXXXX..XXXXXXX 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -XXX,XX +XXX,XX @@ struct _virNetworkDef { int macTableManager; /* enum virNetworkBridgeMACTableManager */ char *domain; virTristateBool domainLocalOnly; /* yes disables dns forwarding */ + virTristateBool domainRegister; unsigned long delay; /* Bridge forward delay (ms) */ bool stp; /* Spanning tree protocol */ unsigned int mtu; /* MTU for bridge, 0 means "default" i.e. unset in config */ diff --git a/src/conf/schemas/network.rng b/src/conf/schemas/network.rng index XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/network.rng +++ b/src/conf/schemas/network.rng @@ -XXX,XX +XXX,XX @@ <optional> <attribute name="localOnly"><ref name="virYesNo"/></attribute> </optional> + <optional> + <attribute name="register"><ref name="virYesNo"/></attribute> + </optional> </element> </optional> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index XXXXXXX..XXXXXXX 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -XXX,XX +XXX,XX @@ #include "virjson.h" #include "virnetworkportdef.h" #include "virutil.h" - +#include "virsystemd.h" #include "netdev_bandwidth_conf.h" #define VIR_FROM_THIS VIR_FROM_NETWORK @@ -XXX,XX +XXX,XX @@ networkStartNetworkVirtual(virNetworkDriverState *driver, bool dnsmasqStarted = false; bool devOnline = false; bool firewalRulesAdded = false; + virSocketAddr *dnsServer = NULL; /* Check to see if any network IP collides with an existing route */ if (networkCheckRouteCollision(def) < 0) @@ -XXX,XX +XXX,XX @@ networkStartNetworkVirtual(virNetworkDriverState *driver, if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) v6present = true; + if (!dnsServer) + dnsServer = &ipdef->address; + /* Add the IP address/netmask to the bridge */ if (networkAddAddrToBridge(obj, ipdef) < 0) goto error; @@ -XXX,XX +XXX,XX @@ networkStartNetworkVirtual(virNetworkDriverState *driver, goto error; dnsmasqStarted = true; + + if (def->domain && def->domainRegister && dnsServer) { + unsigned int link; + int rc; + + if ((link = if_nametoindex(def->bridge)) == 0) { + virReportSystemError(ENODEV, + _("unable to get interface index for %1$s"), + def->bridge); + goto error; + } + + rc = virSystemdResolvedRegisterNameServer(link, def->domain, + dnsServer); + if (rc == -2) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("failed to register name server: systemd-resolved is not available")); + goto error; + } + + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to register name server")); + goto error; + } + } } if (virNetDevBandwidthSet(def->bridge, def->bandwidth, true, true) < 0) -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index XXXXXXX..XXXXXXX 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -XXX,XX +XXX,XX @@ v10.1.0 (unreleased) Additionally, if CPU clusters are present in the host topology, they will be reported as part of the capabilities XML. + * network: Make virtual domains resolvable from the host + + When starting a virtual network with a new ``register='yes'`` attribute + in the ``<domain>`` element, libvirt will configure ``systemd-resolved`` + to resolve names of the connected guests using the name server started + for this network. + * **Improvements** * **Bug fixes** -- 2.43.0 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org