diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index c7ab976187b4db8a7b82cb3f43a96cf3fd220517..e7c2ec5b8b0009ac879331c7827a3fcae958c25f 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -3901,39 +3901,88 @@ out_ip4_address: #define DNS_ROUTES_FWMARK_TABLE_PRIO 20053 +static gboolean +_l3cfg_routed_dns_equal(GPtrArray *routes_old, GPtrArray *routes_new) +{ + guint i; + + if (nm_g_ptr_array_len(routes_old) != nm_g_ptr_array_len(routes_new)) + return FALSE; + + if (routes_old) { + nm_platform_route_objs_sort(routes_old, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY); + } + + if (routes_new) { + nm_platform_route_objs_sort(routes_new, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY); + } + + for (i = 0; i < nm_g_ptr_array_len(routes_old); i++) { + if (nmp_object_cmp(routes_old->pdata[i], routes_new->pdata[i]) != 0) + return FALSE; + } + + return TRUE; +} + +static GPtrArray * +_l3cfg_routed_dns_get_existing_routes(NML3Cfg *self, int addr_family) +{ + GPtrArray *routes = NULL; + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + CList *iter; + + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), + self->priv.ifindex); + + head_entry = nm_platform_lookup(self->priv.platform, &lookup); + if (!head_entry) + return NULL; + + c_list_for_each (iter, &head_entry->lst_entries_head) { + const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; + + if (nm_platform_route_table_uncoerce(obj->ipx_route.rx.table_coerced, FALSE) + != DNS_ROUTES_FWMARK_TABLE_PRIO) + continue; + + if (!routes) + routes = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + g_ptr_array_add(routes, (gpointer) nmp_object_ref(obj)); + } + + return routes; +} + static void -_l3cfg_routed_dns(NML3Cfg *self, NML3ConfigData *l3cd) +_l3cfg_routed_dns_apply(NML3Cfg *self, const NML3ConfigData *l3cd) { + if (!l3cd) + return; + for (int IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { - const char *const *nameservers; - guint i; - guint len; - int addr_family; - gboolean route_added = FALSE; + const char *const *nameservers; + guint i; + guint len; + int addr_family; + nm_auto_unref_ptrarray GPtrArray *old_routes = NULL; + nm_auto_unref_ptrarray GPtrArray *new_routes = NULL; addr_family = IS_IPv4 ? AF_INET : AF_INET6; - if (!nm_l3_config_data_get_routed_dns(l3cd, addr_family)) { - if (self->priv.dns_route_added_x[IS_IPv4]) { - /* Even if the DNS-routes feature is disabled, it was enabled - * before. Therefore, we need to set one last time the routing - * table sync mode to FULL, to clear the DNS routes added - * previously. */ - self->priv.dns_route_added_x[IS_IPv4] = FALSE; - nm_l3_config_data_set_route_table_sync( - l3cd, - addr_family, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL); - } - continue; - } - nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &len); - nm_l3_config_data_set_route_table_sync(l3cd, - addr_family, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL); + if (!nm_l3_config_data_get_routed_dns(l3cd, addr_family)) + goto update_routes; + + _LOGT("configuring IPv%c DNS routes", nm_utils_addr_family_to_char(addr_family)); + new_routes = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + + nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &len); for (i = 0; i < len; i++) { nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_new; const NMPlatformIPXRoute *route; NMPlatformIPXRoute route_new; char addr_buf[INET6_ADDRSTRLEN]; @@ -3955,7 +4004,7 @@ _l3cfg_routed_dns(NML3Cfg *self, NML3ConfigData *l3cd) self->priv.ifindex, &obj); if (r < 0) { - _LOGT("could not get route to DNS %s", + _LOGD("could not get route to DNS server %s", nm_inet_ntop(addr_family, dns.addr.addr_ptr, addr_buf)); continue; } @@ -3964,6 +4013,7 @@ _l3cfg_routed_dns(NML3Cfg *self, NML3ConfigData *l3cd) if (IS_IPv4) { route_new.r4 = (NMPlatformIP4Route) { + .ifindex = self->priv.ifindex, .network = dns.addr.addr4, .plen = 32, .table_any = FALSE, @@ -3975,18 +4025,15 @@ _l3cfg_routed_dns(NML3Cfg *self, NML3ConfigData *l3cd) nm_platform_ip_route_normalize(addr_family, &route_new.rx); - _LOGT("route to %s: %s", + _LOGT("route to DNS server %s: %s", nm_inet4_ntop(dns.addr.addr4, addr_buf), nm_platform_ip4_route_to_string(&route_new.r4, route_buf, sizeof(route_buf))); - nm_l3_config_data_add_route_4(l3cd, &route_new.r4); - nm_l3_config_data_set_route_table_sync( - l3cd, - AF_INET, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_EXCEPT_LOCAL); - route_added = TRUE; + obj_new = nmp_object_new(NMP_OBJECT_TYPE_IP4_ROUTE, &route_new); + g_ptr_array_add(new_routes, obj_new); } else { route_new.r6 = (NMPlatformIP6Route) { + .ifindex = self->priv.ifindex, .network = dns.addr.addr6, .plen = 128, .table_any = FALSE, @@ -3998,22 +4045,23 @@ _l3cfg_routed_dns(NML3Cfg *self, NML3ConfigData *l3cd) nm_platform_ip_route_normalize(addr_family, &route_new.rx); - _LOGT("route to %s: %s", + _LOGT("route to DNS server %s: %s", nm_inet6_ntop(&dns.addr.addr6, addr_buf), nm_platform_ip6_route_to_string(&route_new.r6, route_buf, sizeof(route_buf))); - nm_l3_config_data_add_route_6(l3cd, &route_new.r6); - route_added = TRUE; + obj_new = nmp_object_new(NMP_OBJECT_TYPE_IP6_ROUTE, &route_new); + g_ptr_array_add(new_routes, obj_new); } } - if (route_added) { + if (new_routes->len > 0) { NMPlatformRoutingRule rule; + NMPObject rule_obj; /* Add a routing rule that selects the table when not using the * special fwmark. Note that the rule is shared between all - * devices that use DNS routes. At the moment there is no cleanup - * mechanism: once added the rule stays forever. */ + * devices that use DNS routes. There is no cleanup mechanism: + * once added the rule stays forever. */ rule = ((NMPlatformRoutingRule) { .addr_family = addr_family, .flags = FIB_RULE_INVERT, @@ -4025,12 +4073,39 @@ _l3cfg_routed_dns(NML3Cfg *self, NML3ConfigData *l3cd) .protocol = RTPROT_STATIC, }); - /* FIXME: don't add the rule every time */ - nmp_global_tracker_track_rule(self->priv.global_tracker, &rule, 10, self, NULL); - nmp_global_tracker_sync(self->priv.global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, TRUE); + nmp_object_stackinit(&rule_obj, NMP_OBJECT_TYPE_ROUTING_RULE, &rule); + + if (!nm_platform_lookup_obj(self->priv.platform, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + &rule_obj)) { + _LOGT("adding rule to DNS routing table"); + nmp_global_tracker_track_rule(self->priv.global_tracker, &rule, 10, self, NULL); + nmp_global_tracker_sync(self->priv.global_tracker, + NMP_OBJECT_TYPE_ROUTING_RULE, + TRUE); + } } - self->priv.dns_route_added_x[IS_IPv4] = route_added; +update_routes: + old_routes = _l3cfg_routed_dns_get_existing_routes(self, addr_family); + + if (!_l3cfg_routed_dns_equal(old_routes, new_routes)) { + if (old_routes) { + _LOGT("deleting old DNS routes"); + for (i = 0; i < old_routes->len; i++) { + nm_platform_object_delete(self->priv.platform, old_routes->pdata[i]); + } + } + if (new_routes) { + _LOGT("adding new DNS routes"); + for (i = 0; i < new_routes->len; i++) { + nm_platform_ip_route_add(self->priv.platform, + NMP_NLM_FLAG_REPLACE, + new_routes->pdata[i], + NULL); + } + } + } } } @@ -4201,8 +4276,6 @@ _l3cfg_update_combined_config(NML3Cfg *self, nm_assert(l3cd); nm_assert(nm_l3_config_data_get_ifindex(l3cd) == self->priv.ifindex); - _l3cfg_routed_dns(self, l3cd); - nm_l3_config_data_seal(l3cd); } @@ -5326,6 +5399,8 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) _l3_commit_one(self, AF_INET, commit_type, l3cd_old); _l3_commit_one(self, AF_INET6, commit_type, l3cd_old); + _l3cfg_routed_dns_apply(self, self->priv.p->combined_l3cd_commited); + _failedobj_reschedule(self, 0); _l3_commit_mptcp(self, commit_type); diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index a352860d4c350421c42db58670b315ad4d9743aa..8581f6c45eab6577cf3003eceb049dc13d5a7cfa 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -198,7 +198,6 @@ struct _NML3Cfg { const NMPObject *plobj; const NMPObject *plobj_next; int ifindex; - gboolean dns_route_added_x[2]; /* index with IS_IPv4 */ } priv; /* NML3Cfg strongly cooperates with NMNetns. The latter is