diff options
| author | Michael Biebl <biebl@debian.org> | 2020-12-06 21:57:59 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2020-12-06 21:57:59 +0100 |
| commit | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (patch) | |
| tree | 180827692f002e5f1dad6a0fa8ca489e6bb6438f /src/dns | |
| parent | f2ddac4cbc895837ddcc55015fae112f9859cd0a (diff) | |
New upstream version 1.28.0 upstream/1.28.0
Diffstat (limited to 'src/dns')
| -rw-r--r-- | src/dns/nm-dns-dnsmasq.c | 15 | ||||
| -rw-r--r-- | src/dns/nm-dns-manager.c | 238 | ||||
| -rw-r--r-- | src/dns/nm-dns-manager.h | 26 | ||||
| -rw-r--r-- | src/dns/nm-dns-systemd-resolved.c | 234 |
4 files changed, 388 insertions, 125 deletions
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index 451e4846..97f1dfab 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -820,11 +820,18 @@ add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigD for (i = 0; i < num; i++) { addr = nm_ip_config_get_nameserver(ip_config, i); ip_addr_to_string(addr_family, addr, iface, ip_addr_to_string_buf); - for (j = 0; ip_data->domains.search[j]; j++) { - domain = nm_utils_parse_dns_domain(ip_data->domains.search[j], NULL); - add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, domain[0] ? domain : NULL); - } + if (!ip_data->domains.has_default_route_explicit && ip_data->domains.has_default_route) + add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, NULL); + if (ip_data->domains.search) { + for (j = 0; ip_data->domains.search[j]; j++) { + domain = nm_utils_parse_dns_domain(ip_data->domains.search[j], NULL); + add_dnsmasq_nameserver(self, + servers, + ip_addr_to_string_buf, + domain[0] ? domain : NULL); + } + } if (ip_data->domains.reverse) { for (j = 0; ip_data->domains.reverse[j]; j++) { add_dnsmasq_nameserver(self, diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 635e94f6..3fc9a396 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -222,6 +222,26 @@ _ASSERT_ip_config_data(const NMDnsIPConfigData *ip_data) nm_assert(NM_IS_IP_CONFIG(ip_data->ip_config)); nm_assert(c_list_contains(&ip_data->data->data_lst_head, &ip_data->data_lst)); nm_assert(ip_data->data->ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); +#if NM_MORE_ASSERTS > 5 + { + gboolean has_default = FALSE; + gsize i; + + for (i = 0; ip_data->domains.search && ip_data->domains.search; i++) { + const char *d = ip_data->domains.search[i]; + + d = nm_utils_parse_dns_domain(d, NULL); + nm_assert(d); + if (d[0] == '\0') + has_default = TRUE; + } + nm_assert(has_default == ip_data->domains.has_default_route_explicit); + if (ip_data->domains.has_default_route_explicit) + nm_assert(ip_data->domains.has_default_route_exclusive); + if (ip_data->domains.has_default_route_exclusive) + nm_assert(ip_data->domains.has_default_route); + } +#endif } static NMDnsIPConfigData * @@ -233,10 +253,12 @@ _ip_config_data_new(NMDnsConfigData *data, NMIPConfig *ip_config, NMDnsIPConfigT nm_assert(NM_IS_IP_CONFIG(ip_config)); nm_assert(ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); - ip_data = g_slice_new0(NMDnsIPConfigData); - ip_data->data = data; - ip_data->ip_config = g_object_ref(ip_config); - ip_data->ip_config_type = ip_config_type; + ip_data = g_slice_new(NMDnsIPConfigData); + *ip_data = (NMDnsIPConfigData){ + .data = data, + .ip_config = g_object_ref(ip_config), + .ip_config_type = ip_config_type, + }; c_list_link_tail(&data->data_lst_head, &ip_data->data_lst); c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_config_lst_head, &ip_data->ip_config_lst); @@ -267,7 +289,7 @@ _ip_config_data_free(NMDnsIPConfigData *ip_data) ip_data); g_object_unref(ip_data->ip_config); - g_slice_free(NMDnsIPConfigData, ip_data); + nm_g_slice_free(ip_data); } static NMDnsIPConfigData * @@ -292,7 +314,7 @@ _config_data_free(NMDnsConfigData *data) _ASSERT_config_data(data); nm_assert(c_list_is_empty(&data->data_lst_head)); - g_slice_free(NMDnsConfigData, data); + nm_g_slice_free(data); } static int @@ -1284,6 +1306,19 @@ get_ip_rdns_domains(NMIPConfig *ip_config) return _nm_utils_strv_cleanup(strv, FALSE, FALSE, TRUE); } +static gboolean +domain_ht_get_priority(GHashTable *ht, const char *domain, int *out_priority) +{ + gpointer ptr; + + if (!ht || !g_hash_table_lookup_extended(ht, domain, NULL, &ptr)) { + *out_priority = 0; + return FALSE; + } + *out_priority = GPOINTER_TO_INT(ptr); + return TRUE; +} + /* Check if the domain is shadowed by a parent domain with more negative priority */ static gboolean domain_is_shadowed(GHashTable * ht, @@ -1300,21 +1335,25 @@ domain_is_shadowed(GHashTable * ht, nm_assert(!g_hash_table_contains(ht, domain)); - parent_priority = GPOINTER_TO_INT(g_hash_table_lookup(ht, "")); - if (parent_priority < 0 && parent_priority < priority) { - *out_parent = ""; - *out_parent_priority = parent_priority; - return TRUE; + if (domain_ht_get_priority(ht, "", &parent_priority)) { + nm_assert(parent_priority <= priority); + if (parent_priority < 0 && parent_priority < priority) { + *out_parent = ""; + *out_parent_priority = parent_priority; + return TRUE; + } } parent = strchr(domain, '.'); while (parent && parent[1]) { parent++; - parent_priority = GPOINTER_TO_INT(g_hash_table_lookup(ht, parent)); - if (parent_priority < 0 && parent_priority < priority) { - *out_parent = parent; - *out_parent_priority = parent_priority; - return TRUE; + if (domain_ht_get_priority(ht, parent, &parent_priority)) { + nm_assert(parent_priority <= priority); + if (parent_priority < 0 && parent_priority < priority) { + *out_parent = parent; + *out_parent_priority = parent_priority; + return TRUE; + } } parent = strchr(parent, '.'); } @@ -1329,8 +1368,22 @@ rebuild_domain_lists(NMDnsManager *self) gs_unref_hashtable GHashTable *ht = NULL; gs_unref_hashtable GHashTable *wildcard_entries = NULL; CList * head; + int prev_priority = G_MININT; head = _ip_config_lst_head(self); + +#if NM_MORE_ASSERTS + /* we call clear_domain_lists() at the end of update. We + * don't expect any domain settings here. */ + c_list_for_each_entry (ip_data, head, ip_config_lst) { + nm_assert(!ip_data->domains.search); + nm_assert(!ip_data->domains.reverse); + nm_assert(!ip_data->domains.has_default_route_explicit); + nm_assert(!ip_data->domains.has_default_route_exclusive); + nm_assert(!ip_data->domains.has_default_route); + } +#endif + c_list_for_each_entry (ip_data, head, ip_config_lst) { NMIPConfig *ip_config = ip_data->ip_config; gboolean add_wildcard = FALSE; @@ -1368,8 +1421,11 @@ rebuild_domain_lists(NMDnsManager *self) guint n_domains; guint num_dom1; guint num_dom2; - guint cap_dom; + guint n_domains_allocated; guint i; + gboolean has_default_route_maybe = FALSE; + gboolean has_default_route_explicit = FALSE; + gboolean has_default_route_auto = FALSE; if (!nm_ip_config_get_num_nameservers(ip_config)) continue; @@ -1378,15 +1434,10 @@ rebuild_domain_lists(NMDnsManager *self) n_domains = nm_ip_config_get_num_domains(ip_config); priority = nm_ip_config_get_dns_priority(ip_config); - nm_assert(priority != 0); - - cap_dom = 2u + NM_MAX(n_domains, n_searches); - - g_free(ip_data->domains.search); - domains = g_new(const char *, cap_dom); - ip_data->domains.search = domains; - num_dom1 = 0; + nm_assert(priority != 0); + nm_assert(prev_priority <= priority); + prev_priority = priority; /* Add wildcard lookup domain to connections with the default route. * If there is no default route, add the wildcard domain to all non-VPN @@ -1398,12 +1449,17 @@ rebuild_domain_lists(NMDnsManager *self) * whether it is suitable for certain operations (like having an automatically * added "~" domain). */ if (g_hash_table_contains(wildcard_entries, ip_data)) - domains[num_dom1++] = "~"; + has_default_route_maybe = TRUE; } else { if (ip_data->ip_config_type != NM_DNS_IP_CONFIG_TYPE_VPN) - domains[num_dom1++] = "~"; + has_default_route_maybe = TRUE; } + n_domains_allocated = (n_searches > 0 ? n_searches : n_domains) + 1u; + domains = g_new(const char *, n_domains_allocated); + + num_dom1 = 0; + /* searches are preferred over domains */ if (n_searches > 0) { for (i = 0; i < n_searches; i++) @@ -1413,32 +1469,55 @@ rebuild_domain_lists(NMDnsManager *self) domains[num_dom1++] = nm_ip_config_get_domain(ip_config, i); } - nm_assert(num_dom1 < cap_dom); + nm_assert(num_dom1 < n_domains_allocated); num_dom2 = 0; - for (i = 0; i < num_dom1; i++) { + for (i = 0; TRUE; i++) { + const char *domain_full; const char *domain_clean; const char *parent; int old_priority; int parent_priority; - - domain_clean = nm_utils_parse_dns_domain(domains[i], NULL); + gboolean check_default_route; + + if (i < num_dom1) { + check_default_route = FALSE; + domain_full = domains[i]; + domain_clean = nm_utils_parse_dns_domain(domains[i], NULL); + } else if (i == num_dom1) { + if (!has_default_route_maybe) + continue; + if (has_default_route_explicit) + continue; + check_default_route = TRUE; + domain_full = "~"; + domain_clean = ""; + } else + break; /* Remove domains with lower priority */ - old_priority = GPOINTER_TO_INT(nm_g_hash_table_lookup(ht, domain_clean)); - if (old_priority != 0) { + if (domain_ht_get_priority(ht, domain_clean, &old_priority)) { + nm_assert(old_priority <= priority); if (old_priority < priority) { - _LOGT( - "plugin: drop domain '%s' (i=%d, p=%d) because it already exists with p=%d", - domains[i], - ip_data->data->ifindex, - priority, - old_priority); + _LOGT("plugin: drop domain %s%s%s (i=%d, p=%d) because it already exists " + "with p=%d", + NM_PRINT_FMT_QUOTED(!check_default_route, + "'", + domain_full, + "'", + "<auto-default>"), + ip_data->data->ifindex, + priority, + old_priority); continue; } } else if (domain_is_shadowed(ht, domain_clean, priority, &parent, &parent_priority)) { - _LOGT("plugin: drop domain '%s' (i=%d, p=%d) shadowed by '%s' (p=%d)", - domains[i], + _LOGT("plugin: drop domain %s%s%s (i=%d, p=%d) shadowed by '%s' (p=%d)", + NM_PRINT_FMT_QUOTED(!check_default_route, + "'", + domain_full, + "'", + "<auto-default>"), ip_data->data->ifindex, priority, parent, @@ -1446,20 +1525,55 @@ rebuild_domain_lists(NMDnsManager *self) continue; } - _LOGT("plugin: add domain '%s' (i=%d, p=%d)", - domains[i], - ip_data->data->ifindex, - priority); + _LOGT( + "plugin: add domain %s%s%s (i=%d, p=%d)", + NM_PRINT_FMT_QUOTED(!check_default_route, "'", domain_full, "'", "<auto-default>"), + ip_data->data->ifindex, + priority); + if (!ht) ht = g_hash_table_new(nm_str_hash, g_str_equal); g_hash_table_insert(ht, (gpointer) domain_clean, GINT_TO_POINTER(priority)); - domains[num_dom2++] = domains[i]; + + if (check_default_route) + has_default_route_auto = TRUE; + else { + nm_assert(num_dom2 <= num_dom1); + nm_assert(num_dom2 < n_domains_allocated); + domains[num_dom2++] = domain_full; + if (domain_clean[0] == '\0') + has_default_route_explicit = TRUE; + } } - nm_assert(num_dom2 < cap_dom); + nm_assert(num_dom2 < n_domains_allocated); domains[num_dom2] = NULL; - g_strfreev(ip_data->domains.reverse); - ip_data->domains.reverse = get_ip_rdns_domains(ip_config); + nm_assert(!ip_data->domains.search); + nm_assert(!ip_data->domains.reverse); + ip_data->domains.search = domains; + ip_data->domains.reverse = get_ip_rdns_domains(ip_config); + ip_data->domains.has_default_route_explicit = has_default_route_explicit; + ip_data->domains.has_default_route_exclusive = + has_default_route_explicit || (priority < 0 && has_default_route_auto); + ip_data->domains.has_default_route = + ip_data->domains.has_default_route_exclusive || has_default_route_auto; + + { + gs_free char *str1 = NULL; + gs_free char *str2 = NULL; + + _LOGT("plugin: settings: ifindex=%d, priority=%d, default-route=%d%s, search=%s, " + "reverse=%s", + ip_data->data->ifindex, + priority, + ip_data->domains.has_default_route, + ip_data->domains.has_default_route_explicit + ? " (explicit)" + : (ip_data->domains.has_default_route_exclusive ? " (exclusive)" : ""), + (str1 = g_strjoinv(",", (char **) ip_data->domains.search)), + (ip_data->domains.reverse ? (str2 = g_strjoinv(",", ip_data->domains.reverse)) + : "")); + } } } @@ -1473,6 +1587,9 @@ clear_domain_lists(NMDnsManager *self) c_list_for_each_entry (ip_data, head, ip_config_lst) { nm_clear_g_free(&ip_data->domains.search); nm_clear_pointer(&ip_data->domains.reverse, g_strfreev); + ip_data->domains.has_default_route_explicit = FALSE; + ip_data->domains.has_default_route_exclusive = FALSE; + ip_data->domains.has_default_route = FALSE; } } @@ -1697,7 +1814,7 @@ nm_dns_manager_set_ip_config(NMDnsManager * self, priv = NM_DNS_MANAGER_GET_PRIVATE(self); - data = g_hash_table_lookup(priv->configs, GINT_TO_POINTER(ifindex)); + data = g_hash_table_lookup(priv->configs, &ifindex); if (!data) ip_data = NULL; else @@ -1713,7 +1830,7 @@ nm_dns_manager_set_ip_config(NMDnsManager * self, /* deleting a config doesn't invalidate the configs' sort order. */ _ip_config_data_free(ip_data); if (c_list_is_empty(&data->data_lst_head)) - g_hash_table_remove(priv->configs, GINT_TO_POINTER(ifindex)); + g_hash_table_remove(priv->configs, &ifindex); goto changed; } @@ -1723,12 +1840,14 @@ nm_dns_manager_set_ip_config(NMDnsManager * self, } if (!data) { - data = g_slice_new0(NMDnsConfigData); - data->ifindex = ifindex; - data->self = self; - c_list_init(&data->data_lst_head); + data = g_slice_new(NMDnsConfigData); + *data = (NMDnsConfigData){ + .ifindex = ifindex, + .self = self, + .data_lst_head = C_LIST_INIT(data->data_lst_head), + }; _ASSERT_config_data(data); - g_hash_table_insert(priv->configs, GINT_TO_POINTER(ifindex), data); + g_hash_table_add(priv->configs, data); } if (!ip_data) @@ -2381,8 +2500,11 @@ nm_dns_manager_init(NMDnsManager *self) priv->config = g_object_ref(nm_config_get()); - priv->configs = - g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _config_data_free); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDnsConfigData, ifindex) == 0); + priv->configs = g_hash_table_new_full(nm_pint_hash, + nm_pint_equals, + (GDestroyNotify) _config_data_free, + NULL); /* Set the initial hash */ compute_hash(self, NULL, NM_DNS_MANAGER_GET_PRIVATE(self)->hash); diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index f91d1556..fb2f36d2 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -22,7 +22,7 @@ typedef enum { enum { NM_DNS_PRIORITY_DEFAULT_NORMAL = 100, - NM_DNS_PRIORITY_DEFAULT_VPN = -50, + NM_DNS_PRIORITY_DEFAULT_VPN = 50, }; struct _NMDnsConfigData; @@ -37,13 +37,35 @@ typedef struct { struct { const char **search; char ** reverse; + + /* Whether "search" explicitly contains a default route "~" + * or "". It is redundant information, but for faster lookup. */ + bool has_default_route_explicit : 1; + + /* Whether an explicit "~" search domain should be added. + * For systemd-resolved, this configured an explicit wildcard + * search domain, and should be used for profiles with negative + * DNS priority. + * + * If "has_default_route_explicit", this is always TRUE and implied. + * + * With systemd-resolved, if TRUE we will set a "." search domain. + */ + bool has_default_route_exclusive : 1; + + /* Whether the device should be used for any domains "~". + * + * If "has_default_route_exclusive", this is always TRUE and implied. + * + * With systemd-resolved, this is the value for SetLinkDefaultRoute(). */ + bool has_default_route : 1; } domains; } NMDnsIPConfigData; typedef struct _NMDnsConfigData { + int ifindex; struct _NMDnsManager *self; CList data_lst_head; - int ifindex; } NMDnsConfigData; #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type()) diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index 24c0ddb5..fe2e78af 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -34,6 +34,9 @@ #define SYSTEMD_RESOLVED_MANAGER_IFACE "org.freedesktop.resolve1.Manager" #define SYSTEMD_RESOLVED_DBUS_PATH "/org/freedesktop/resolve1" +/* define a variable, so that we can compare the operation with pointer equality. */ +static const char *const DBUS_OP_SET_LINK_DEFAULT_ROUTE = "SetLinkDefaultRoute"; + /*****************************************************************************/ typedef struct { @@ -42,15 +45,18 @@ typedef struct { } InterfaceConfig; typedef struct { - CList request_queue_lst; - const char *operation; - GVariant * argument; + CList request_queue_lst; + const char * operation; + GVariant * argument; + NMDnsSystemdResolved *self; + int ifindex; } RequestItem; /*****************************************************************************/ typedef struct { GDBusConnection *dbus_connection; + GHashTable * dirty_interfaces; GCancellable * cancellable; CList request_queue_lst_head; guint name_owner_changed_id; @@ -58,6 +64,8 @@ typedef struct { bool try_start_blocked : 1; bool dbus_has_owner : 1; bool dbus_initied : 1; + bool request_queue_to_send : 1; + NMTernary has_link_default_route : 3; } NMDnsSystemdResolvedPrivate; struct _NMDnsSystemdResolved { @@ -87,18 +95,26 @@ _request_item_free(RequestItem *request_item) { c_list_unlink_stale(&request_item->request_queue_lst); g_variant_unref(request_item->argument); - g_slice_free(RequestItem, request_item); + nm_g_slice_free(request_item); } static void -_request_item_append(CList *request_queue_lst_head, const char *operation, GVariant *argument) +_request_item_append(NMDnsSystemdResolved *self, + const char * operation, + int ifindex, + GVariant * argument) { - RequestItem *request_item; + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + RequestItem * request_item; - request_item = g_slice_new(RequestItem); - request_item->operation = operation; - request_item->argument = g_variant_ref_sink(argument); - c_list_link_tail(request_queue_lst_head, &request_item->request_queue_lst); + request_item = g_slice_new(RequestItem); + *request_item = (RequestItem){ + .operation = operation, + .argument = g_variant_ref_sink(argument), + .self = self, + .ifindex = ifindex, + }; + c_list_link_tail(&priv->request_queue_lst_head, &request_item->request_queue_lst); } /*****************************************************************************/ @@ -115,43 +131,69 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data) { gs_unref_variant GVariant *v = NULL; gs_free_error GError * error = NULL; - NMDnsSystemdResolved * self = (NMDnsSystemdResolved *) user_data; + NMDnsSystemdResolved * self; NMDnsSystemdResolvedPrivate *priv; + RequestItem * request_item; + NMLogLevel log_level; v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error); - if (!v && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (nm_utils_error_is_cancelled(error)) return; - priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + request_item = user_data; + self = request_item->self; + priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - if (!v) { - if (!priv->send_updates_warn_ratelimited) { - priv->send_updates_warn_ratelimited = TRUE; - _LOGW("send-updates failed to update systemd-resolved: %s", error->message); - } else - _LOGD("send-updates failed: %s", error->message); - } else + if (v) { + if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE + && priv->has_link_default_route == NM_TERNARY_DEFAULT) { + priv->has_link_default_route = NM_TERNARY_TRUE; + _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API supported"); + } priv->send_updates_warn_ratelimited = FALSE; + return; + } + + if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE + && nm_g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + if (priv->has_link_default_route == NM_TERNARY_DEFAULT) { + priv->has_link_default_route = NM_TERNARY_FALSE; + _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API not supported"); + } + return; + } + + log_level = LOGL_DEBUG; + if (!priv->send_updates_warn_ratelimited) { + priv->send_updates_warn_ratelimited = TRUE; + log_level = LOGL_WARN; + } + _NMLOG(log_level, + "send-updates %s@%d failed: %s", + request_item->operation, + request_item->ifindex, + error->message); } -static void +static gboolean update_add_ip_config(NMDnsSystemdResolved *self, GVariantBuilder * dns, GVariantBuilder * domains, NMDnsIPConfigData * data) { - int addr_family; - gsize addr_size; - guint i, n; - gboolean is_routing; - const char **iter; - const char * domain; + int addr_family; + gsize addr_size; + guint i, n; + gboolean is_routing; + const char *domain; + gboolean has_config = FALSE; addr_family = nm_ip_config_get_addr_family(data->ip_config); addr_size = nm_utils_addr_family_to_size(addr_family); - if (!data->domains.search || !data->domains.search[0]) - return; + if ((!data->domains.search || !data->domains.search[0]) + && !data->domains.has_default_route_exclusive && !data->domains.has_default_route) + return FALSE; n = nm_ip_config_get_num_nameservers(data->ip_config); for (i = 0; i < n; i++) { @@ -164,12 +206,22 @@ update_add_ip_config(NMDnsSystemdResolved *self, addr_size, 1)); g_variant_builder_close(dns); + has_config = TRUE; } - for (iter = data->domains.search; *iter; iter++) { - domain = nm_utils_parse_dns_domain(*iter, &is_routing); - g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); + if (!data->domains.has_default_route_explicit && data->domains.has_default_route_exclusive) { + g_variant_builder_add(domains, "(sb)", ".", TRUE); + has_config = TRUE; } + if (data->domains.search) { + for (i = 0; data->domains.search[i]; i++) { + domain = nm_utils_parse_dns_domain(data->domains.search[i], &is_routing); + g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); + has_config = TRUE; + } + } + + return has_config; } static void @@ -183,15 +235,17 @@ free_pending_updates(NMDnsSystemdResolved *self) _request_item_free(request_item); } -static void +static gboolean prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) { - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - GVariantBuilder dns, domains; - NMCListElem * elem; - NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; - NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; - const char * mdns_arg = NULL, *llmnr_arg = NULL; + GVariantBuilder dns; + GVariantBuilder domains; + NMCListElem * elem; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; + const char * mdns_arg = NULL, *llmnr_arg = NULL; + gboolean has_config = FALSE; + gboolean has_default_route = FALSE; g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))")); g_variant_builder_add(&dns, "i", ic->ifindex); @@ -205,7 +259,10 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) NMDnsIPConfigData *data = elem->data; NMIPConfig * ip_config = data->ip_config; - update_add_ip_config(self, &dns, &domains, data); + has_config |= update_add_ip_config(self, &dns, &domains, data); + + if (data->domains.has_default_route) + has_default_route = TRUE; if (NM_IS_IP4_CONFIG(ip_config)) { mdns = NM_MAX(mdns, nm_ip4_config_mdns_get(NM_IP4_CONFIG(ip_config))); @@ -248,16 +305,25 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) } nm_assert(llmnr_arg); - _request_item_append(&priv->request_queue_lst_head, "SetLinkDNS", g_variant_builder_end(&dns)); - _request_item_append(&priv->request_queue_lst_head, - "SetLinkDomains", - g_variant_builder_end(&domains)); - _request_item_append(&priv->request_queue_lst_head, + if (!nm_str_is_empty(mdns_arg) || !nm_str_is_empty(llmnr_arg)) + has_config = TRUE; + + _request_item_append(self, "SetLinkDomains", ic->ifindex, g_variant_builder_end(&domains)); + _request_item_append(self, + DBUS_OP_SET_LINK_DEFAULT_ROUTE, + ic->ifindex, + g_variant_new("(ib)", ic->ifindex, has_default_route)); + _request_item_append(self, "SetLinkMulticastDNS", + ic->ifindex, g_variant_new("(is)", ic->ifindex, mdns_arg ?: "")); - _request_item_append(&priv->request_queue_lst_head, + _request_item_append(self, "SetLinkLLMNR", + ic->ifindex, g_variant_new("(is)", ic->ifindex, llmnr_arg ?: "")); + _request_item_append(self, "SetLinkDNS", ic->ifindex, g_variant_builder_end(&dns)); + + return has_config; } static void @@ -266,7 +332,7 @@ send_updates(NMDnsSystemdResolved *self) NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); RequestItem * request_item; - if (c_list_is_empty(&priv->request_queue_lst_head)) { + if (!priv->request_queue_to_send) { /* nothing to do. */ return; } @@ -296,15 +362,30 @@ send_updates(NMDnsSystemdResolved *self) return; } - _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head)); - nm_clear_g_cancellable(&priv->cancellable); + if (c_list_is_empty(&priv->request_queue_lst_head)) { + _LOGT("send-updates: no requests to send"); + priv->request_queue_to_send = FALSE; + return; + } + + _LOGT("send-updates: start %lu requests", c_list_length(&priv->request_queue_lst_head)); + priv->cancellable = g_cancellable_new(); - while ( - (request_item = - c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst))) { + priv->request_queue_to_send = FALSE; + + c_list_for_each_entry (request_item, &priv->request_queue_lst_head, request_queue_lst) { + if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE + && priv->has_link_default_route == NM_TERNARY_FALSE) { + /* The "SetLinkDefaultRoute" API is only supported since v240. + * We detected that it is not supported, and skip the call. There + * is no special workaround, because in this case we rely on systemd-resolved + * to do the right thing automatically. */ + continue; + } + /* Above we explicitly call "StartServiceByName" trying to avoid D-Bus activating systmd-resolved * multiple times. There is still a race, were we might hit this line although actually * the service just quit this very moment. In that case, we would try to D-Bus activate the @@ -324,8 +405,7 @@ send_updates(NMDnsSystemdResolved *self) -1, priv->cancellable, call_done, - self); - _request_item_free(request_item); + request_item); } } @@ -336,19 +416,22 @@ update(NMDnsPlugin * plugin, const char * hostname, GError ** error) { - NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin); + NMDnsSystemdResolved * self = NM_DNS_SYSTEMD_RESOLVED(plugin); + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); gs_unref_hashtable GHashTable *interfaces = NULL; gs_free gpointer * interfaces_keys = NULL; guint interfaces_len; - guint i; + int ifindex; + gpointer pointer; NMDnsIPConfigData *ip_data; + GHashTableIter iter; + guint i; interfaces = g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _interface_config_free); c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { InterfaceConfig *ic = NULL; - int ifindex; ifindex = ip_data->data->ifindex; nm_assert(ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); @@ -371,11 +454,33 @@ update(NMDnsPlugin * plugin, for (i = 0; i < interfaces_len; i++) { InterfaceConfig *ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(interfaces_keys[i])); - prepare_one_interface(self, ic); + if (prepare_one_interface(self, ic)) + g_hash_table_add(priv->dirty_interfaces, GINT_TO_POINTER(ic->ifindex)); + else + g_hash_table_remove(priv->dirty_interfaces, GINT_TO_POINTER(ic->ifindex)); } - send_updates(self); + /* If we previously configured an ifindex with non-empty values in + * resolved, and the current update doesn't contain that interface, + * reset the resolved configuration for that ifindex. */ + g_hash_table_iter_init(&iter, priv->dirty_interfaces); + while (g_hash_table_iter_next(&iter, (gpointer *) &pointer, NULL)) { + ifindex = GPOINTER_TO_INT(pointer); + if (!g_hash_table_contains(interfaces, GINT_TO_POINTER(ifindex))) { + InterfaceConfig ic; + + _LOGT("clear previously configured ifindex %d", ifindex); + ic = (InterfaceConfig){ + .ifindex = ifindex, + .configs_lst_head = C_LIST_INIT(ic.configs_lst_head), + }; + prepare_one_interface(self, &ic); + g_hash_table_iter_remove(&iter); + } + } + priv->request_queue_to_send = TRUE; + send_updates(self); return TRUE; } @@ -394,8 +499,11 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) _LOGT("D-Bus name for systemd-resolved has owner %s", owner); priv->dbus_has_owner = !!owner; - if (owner) - priv->try_start_blocked = FALSE; + if (owner) { + priv->try_start_blocked = FALSE; + priv->request_queue_to_send = TRUE; + } else + priv->has_link_default_route = NM_TERNARY_DEFAULT; send_updates(self); } @@ -468,7 +576,10 @@ nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + priv->has_link_default_route = NM_TERNARY_DEFAULT; + c_list_init(&priv->request_queue_lst_head); + priv->dirty_interfaces = g_hash_table_new(nm_direct_hash, NULL); priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET); if (!priv->dbus_connection) { @@ -510,6 +621,7 @@ dispose(GObject *object) nm_clear_g_cancellable(&priv->cancellable); g_clear_object(&priv->dbus_connection); + nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_unref); G_OBJECT_CLASS(nm_dns_systemd_resolved_parent_class)->dispose(object); } |