/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2005 - 2014 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ #include "nm-default.h" #include "nm-ip4-config.h" #include #include #include "nm-utils.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-route-manager.h" #include "nm-core-internal.h" #include "introspection/org.freedesktop.NetworkManager.IP4Config.h" /*****************************************************************************/ /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */ G_STATIC_ASSERT (sizeof (uint) >= sizeof (guint32)); G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF); /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, PROP_IFINDEX, PROP_ADDRESS_DATA, PROP_ADDRESSES, PROP_ROUTE_DATA, PROP_ROUTES, PROP_GATEWAY, PROP_NAMESERVERS, PROP_DOMAINS, PROP_SEARCHES, PROP_DNS_OPTIONS, PROP_WINS_SERVERS, PROP_DNS_PRIORITY, ); typedef struct { bool never_default:1; bool metered:1; bool has_gateway:1; guint32 gateway; guint32 mss; guint32 mtu; int ifindex; NMIPConfigSource mtu_source; gint dns_priority; gint64 route_metric; GArray *addresses; GArray *routes; GArray *nameservers; GPtrArray *domains; GPtrArray *searches; GPtrArray *dns_options; GArray *nis; char *nis_domain; GArray *wins; GVariant *address_data_variant; GVariant *addresses_variant; } NMIP4ConfigPrivate; struct _NMIP4Config { NMExportedObject parent; NMIP4ConfigPrivate _priv; }; struct _NMIP4ConfigClass { NMExportedObjectClass parent; }; G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) #define NM_IP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP4Config, NM_IS_IP4_CONFIG) /*****************************************************************************/ int nm_ip4_config_get_ifindex (const NMIP4Config *config) { return NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex; } /*****************************************************************************/ static gboolean _ipv4_is_zeronet (in_addr_t network) { /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */ return (network & htonl(0xff000000)) == htonl(0x00000000); } /*****************************************************************************/ /** * nm_ip4_config_capture_resolv_conf(): * @nameservers: array of guint32 * @rc_contents: the contents of a resolv.conf or %NULL to read /etc/resolv.conf * * Reads all resolv.conf IPv4 nameservers and adds them to @nameservers. * * Returns: %TRUE if nameservers were added, %FALSE if @nameservers is unchanged */ gboolean nm_ip4_config_capture_resolv_conf (GArray *nameservers, GPtrArray *dns_options, const char *rc_contents) { GPtrArray *read_ns, *read_options; guint i, j; gboolean changed = FALSE; g_return_val_if_fail (nameservers != NULL, FALSE); read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents); if (!read_ns) return FALSE; for (i = 0; i < read_ns->len; i++) { const char *s = g_ptr_array_index (read_ns, i); guint32 ns = 0; if (!inet_pton (AF_INET, s, (void *) &ns) || !ns) continue; /* Ignore duplicates */ for (j = 0; j < nameservers->len; j++) { if (g_array_index (nameservers, guint32, j) == ns) break; } if (j == nameservers->len) { g_array_append_val (nameservers, ns); changed = TRUE; } } g_ptr_array_unref (read_ns); if (dns_options) { read_options = nm_utils_read_resolv_conf_dns_options (rc_contents); if (!read_options) return changed; for (i = 0; i < read_options->len; i++) { const char *s = g_ptr_array_index (read_options, i); if (_nm_utils_dns_option_validate (s, NULL, NULL, FALSE, _nm_utils_dns_option_descs) && _nm_utils_dns_option_find_idx (dns_options, s) < 0) { g_ptr_array_add (dns_options, g_strdup (s)); changed = TRUE; } } g_ptr_array_unref (read_options); } return changed; } static gboolean addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { return a->address == b->address && a->plen == b->plen && ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0; } static gboolean routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_gateway_and_metric) { return a->network == b->network && a->plen == b->plen && (!consider_gateway_and_metric || (a->gateway == b->gateway && a->metric == b->metric)); } /*****************************************************************************/ static gint _addresses_sort_cmp_get_prio (in_addr_t addr) { if (nm_utils_ip4_address_is_link_local (addr)) return 0; return 1; } static gint _addresses_sort_cmp (gconstpointer a, gconstpointer b) { gint p1, p2; const NMPlatformIP4Address *a1 = a, *a2 = b; guint32 n1, n2; /* Sort by address type. For example link local will * be sorted *after* a global address. */ p1 = _addresses_sort_cmp_get_prio (a1->address); p2 = _addresses_sort_cmp_get_prio (a2->address); if (p1 != p2) return p1 > p2 ? -1 : 1; /* Sort the addresses based on their source. */ if (a1->addr_source != a2->addr_source) return a1->addr_source > a2->addr_source ? -1 : 1; if ((a1->label[0] == '\0') != (a2->label[0] == '\0')) return (a1->label[0] == '\0') ? -1 : 1; /* Finally, sort addresses lexically. We compare only the * network part so that the order of addresses in the same * subnet (and thus also the primary/secondary role) is * preserved. */ n1 = a1->address & nm_utils_ip4_prefix_to_netmask (a1->plen); n2 = a2->address & nm_utils_ip4_prefix_to_netmask (a2->plen); return memcmp (&n1, &n2, sizeof (guint32)); } /*****************************************************************************/ static void notify_addresses (NMIP4Config *self) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); nm_clear_g_variant (&priv->address_data_variant); nm_clear_g_variant (&priv->addresses_variant); _notify (self, PROP_ADDRESS_DATA); _notify (self, PROP_ADDRESSES); } NMIP4Config * nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf) { NMIP4Config *config; NMIP4ConfigPrivate *priv; guint i; guint32 lowest_metric = G_MAXUINT32; guint32 old_gateway = 0; gboolean old_has_gateway = FALSE; /* Slaves have no IP configuration */ if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0) return NULL; config = nm_ip4_config_new (ifindex); priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_array_unref (priv->addresses); g_array_unref (priv->routes); priv->addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); priv->routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); /* Extract gateway from default route */ old_gateway = priv->gateway; old_has_gateway = priv->has_gateway; for (i = 0; i < priv->routes->len; ) { const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { if (route->metric < lowest_metric) { priv->gateway = route->gateway; lowest_metric = route->metric; } priv->has_gateway = TRUE; /* Remove the default route from the list */ g_array_remove_index_fast (priv->routes, i); continue; } i++; } /* we detect the route metric based on the default route. All non-default * routes have their route metrics explicitly set. */ priv->route_metric = priv->has_gateway ? (gint64) lowest_metric : (gint64) -1; /* If there is a host route to the gateway, ignore that route. It is * automatically added by NetworkManager when needed. */ if (priv->has_gateway) { for (i = 0; i < priv->routes->len; i++) { const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); if ( (route->plen == 32) && (route->network == priv->gateway) && (route->gateway == 0)) { g_array_remove_index (priv->routes, i); i--; } } } /* If the interface has the default route, and has IPv4 addresses, capture * nameservers from /etc/resolv.conf. */ if (priv->addresses->len && priv->has_gateway && capture_resolv_conf) { if (nm_ip4_config_capture_resolv_conf (priv->nameservers, priv->dns_options, NULL)) _notify (config, PROP_NAMESERVERS); } /* actually, nobody should be connected to the signal, just to be sure, notify */ _notify (config, PROP_ADDRESS_DATA); _notify (config, PROP_ROUTE_DATA); _notify (config, PROP_ADDRESSES); _notify (config, PROP_ROUTES); if ( priv->gateway != old_gateway || priv->has_gateway != old_has_gateway) _notify (config, PROP_GATEWAY); return config; } gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); gs_unref_ptrarray GPtrArray *added_addresses = NULL; g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (config != NULL, FALSE); /* Addresses */ nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, default_route_metric >= 0 ? &added_addresses : NULL); /* Routes */ { guint i; guint count = nm_ip4_config_get_num_routes (config); GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count); gboolean success; gs_unref_array GArray *device_route_purge_list = NULL; if ( default_route_metric >= 0 && added_addresses) { /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. * As we don't do that for IPv4, add it here shortly before syncing * the routes. For NMRouteManager these routes are very much important. */ for (i = 0; i < added_addresses->len; i++) { const NMPlatformIP4Address *addr = added_addresses->pdata[i]; NMPlatformIP4Route route = { 0 }; if (addr->plen == 0) continue; nm_assert (addr->plen <= 32); route.ifindex = ifindex; route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; /* The destination network depends on the peer-address. */ route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); if (_ipv4_is_zeronet (route.network)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ continue; } route.plen = addr->plen; route.pref_src = addr->address; route.metric = default_route_metric; g_array_append_val (routes, route); if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { if (!device_route_purge_list) device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; g_array_append_val (device_route_purge_list, route); } } } for (i = 0; i < count; i++) { const NMPlatformIP4Route *route; route = nm_ip4_config_get_route (config, i); /* duplicates in @routes are no problem as route-manager handles them * gracefully (by ignoring them). */ g_array_append_vals (routes, route, 1); } nm_route_manager_ip4_route_register_device_route_purge_list (nm_route_manager_get (), device_route_purge_list); success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, default_route_metric < 0, routes_full_sync); g_array_unref (routes); if (!success) return FALSE; } return TRUE; } static void merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) { GVariant *variant; in_addr_t addr; #define GET_ATTR(name, field, variant_type, type) \ variant = nm_ip_route_get_attribute (s_route, name); \ if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \ r->field = g_variant_get_ ## type (variant); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, tos, BYTE, byte); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, window, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, cwnd, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, initcwnd, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, initrwnd, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU, mtu, UINT32, uint32); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, lock_window, BOOLEAN, boolean); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, lock_cwnd, BOOLEAN, boolean); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, lock_initcwnd, BOOLEAN, boolean); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, lock_initrwnd, BOOLEAN, boolean); GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, lock_mtu, BOOLEAN, boolean); if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_SRC)) && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { if (inet_pton (AF_INET, g_variant_get_string (variant, NULL), &addr) == 1) r->pref_src = addr; } #undef GET_ATTR } void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric) { NMIP4ConfigPrivate *priv; guint naddresses, nroutes, nnameservers, nsearches; int i, priority; if (!setting) return; g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_object_freeze_notify (G_OBJECT (config)); naddresses = nm_setting_ip_config_get_num_addresses (setting); nroutes = nm_setting_ip_config_get_num_routes (setting); nnameservers = nm_setting_ip_config_get_num_dns (setting); nsearches = nm_setting_ip_config_get_num_dns_searches (setting); /* Gateway */ if (nm_setting_ip_config_get_never_default (setting)) nm_ip4_config_set_never_default (config, TRUE); else if (nm_setting_ip_config_get_ignore_auto_routes (setting)) nm_ip4_config_set_never_default (config, FALSE); if (nm_setting_ip_config_get_gateway (setting)) { guint32 gateway; inet_pton (AF_INET, nm_setting_ip_config_get_gateway (setting), &gateway); nm_ip4_config_set_gateway (config, gateway); } if (priv->route_metric == -1) priv->route_metric = nm_setting_ip_config_get_route_metric (setting); /* Addresses */ for (i = 0; i < naddresses; i++) { NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i); GVariant *label; NMPlatformIP4Address address; memset (&address, 0, sizeof (address)); nm_ip_address_get_address_binary (s_addr, &address.address); address.peer_address = address.address; address.plen = nm_ip_address_get_prefix (s_addr); nm_assert (address.plen <= 32); address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT; address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; address.addr_source = NM_IP_CONFIG_SOURCE_USER; label = nm_ip_address_get_attribute (s_addr, "label"); if (label) g_strlcpy (address.label, g_variant_get_string (label, NULL), sizeof (address.label)); nm_ip4_config_add_address (config, &address); } /* Routes */ if (nm_setting_ip_config_get_ignore_auto_routes (setting)) nm_ip4_config_reset_routes (config); for (i = 0; i < nroutes; i++) { NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i); NMPlatformIP4Route route; memset (&route, 0, sizeof (route)); nm_ip_route_get_dest_binary (s_route, &route.network); route.plen = nm_ip_route_get_prefix (s_route); nm_assert (route.plen <= 32); if (route.plen == 0) continue; nm_ip_route_get_next_hop_binary (s_route, &route.gateway); if (nm_ip_route_get_metric (s_route) == -1) route.metric = default_route_metric; else route.metric = nm_ip_route_get_metric (s_route); route.rt_source = NM_IP_CONFIG_SOURCE_USER; merge_route_attributes (s_route, &route); nm_ip4_config_add_route (config, &route); } /* DNS */ if (nm_setting_ip_config_get_ignore_auto_dns (setting)) { nm_ip4_config_reset_nameservers (config); nm_ip4_config_reset_domains (config); nm_ip4_config_reset_searches (config); } for (i = 0; i < nnameservers; i++) { guint32 ip; if (inet_pton (AF_INET, nm_setting_ip_config_get_dns (setting, i), &ip) == 1) nm_ip4_config_add_nameserver (config, ip); } for (i = 0; i < nsearches; i++) nm_ip4_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i)); i = 0; while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) { nm_ip4_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i)); i++; } priority = nm_setting_ip_config_get_dns_priority (setting); if (priority) nm_ip4_config_set_dns_priority (config, priority); g_object_thaw_notify (G_OBJECT (config)); } NMSetting * nm_ip4_config_create_setting (const NMIP4Config *config) { NMSettingIPConfig *s_ip4; guint32 gateway; guint naddresses, nroutes, nnameservers, nsearches, noptions; const char *method = NULL; int i; gint64 route_metric; s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); if (!config) { g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); return NM_SETTING (s_ip4); } gateway = nm_ip4_config_get_gateway (config); naddresses = nm_ip4_config_get_num_addresses (config); nroutes = nm_ip4_config_get_num_routes (config); nnameservers = nm_ip4_config_get_num_nameservers (config); nsearches = nm_ip4_config_get_num_searches (config); noptions = nm_ip4_config_get_num_dns_options (config); route_metric = nm_ip4_config_get_route_metric (config); /* Addresses */ for (i = 0; i < naddresses; i++) { const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); NMIPAddress *s_addr; /* Detect dynamic address */ if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; continue; } /* Static address found. */ if (!method) method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; s_addr = nm_ip_address_new_binary (AF_INET, &address->address, address->plen, NULL); if (*address->label) nm_ip_address_set_attribute (s_addr, "label", g_variant_new_string (address->label)); nm_setting_ip_config_add_address (s_ip4, s_addr); nm_ip_address_unref (s_addr); } /* Gateway */ if ( nm_ip4_config_has_gateway (config) && nm_setting_ip_config_get_num_addresses (s_ip4) > 0) { g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet4_ntop (gateway, NULL), NULL); } /* Use 'disabled' if the method wasn't previously set */ if (!method) method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric, NULL); /* Routes */ for (i = 0; i < nroutes; i++) { const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); NMIPRoute *s_route; /* Ignore default route. */ if (!route->plen) continue; /* Ignore routes provided by external sources */ if (route->rt_source != nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER)) continue; s_route = nm_ip_route_new_binary (AF_INET, &route->network, route->plen, &route->gateway, route->metric, NULL); nm_setting_ip_config_add_route (s_ip4, s_route); nm_ip_route_unref (s_route); } /* DNS */ for (i = 0; i < nnameservers; i++) { guint32 nameserver = nm_ip4_config_get_nameserver (config, i); nm_setting_ip_config_add_dns (s_ip4, nm_utils_inet4_ntop (nameserver, NULL)); } for (i = 0; i < nsearches; i++) { const char *search = nm_ip4_config_get_search (config, i); nm_setting_ip_config_add_dns_search (s_ip4, search); } for (i = 0; i < noptions; i++) { const char *option = nm_ip4_config_get_dns_option (config, i); nm_setting_ip_config_add_dns_option (s_ip4, option); } g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DNS_PRIORITY, nm_ip4_config_get_dns_priority (config), NULL); return NM_SETTING (s_ip4); } /*****************************************************************************/ void nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags) { NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; guint32 i; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst); src_priv = NM_IP4_CONFIG_GET_PRIVATE (src); g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++) nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i)); /* nameservers */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++) nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i)); } /* default gateway */ if (nm_ip4_config_has_gateway (src)) nm_ip4_config_set_gateway (dst, nm_ip4_config_get_gateway (src)); /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i)); } if (dst_priv->route_metric == -1) dst_priv->route_metric = src_priv->route_metric; else if (src_priv->route_metric != -1) dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric); /* domains */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { for (i = 0; i < nm_ip4_config_get_num_domains (src); i++) nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i)); } /* dns searches */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { for (i = 0; i < nm_ip4_config_get_num_searches (src); i++) nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i)); } /* dns options */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { for (i = 0; i < nm_ip4_config_get_num_dns_options (src); i++) nm_ip4_config_add_dns_option (dst, nm_ip4_config_get_dns_option (src, i)); } /* MSS */ if (nm_ip4_config_get_mss (src)) nm_ip4_config_set_mss (dst, nm_ip4_config_get_mss (src)); /* MTU */ if ( src_priv->mtu_source > dst_priv->mtu_source || ( src_priv->mtu_source == dst_priv->mtu_source && ( (!dst_priv->mtu && src_priv->mtu) || (dst_priv->mtu && src_priv->mtu < dst_priv->mtu)))) nm_ip4_config_set_mtu (dst, src_priv->mtu, src_priv->mtu_source); /* NIS */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++) nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i)); if (nm_ip4_config_get_nis_domain (src)) nm_ip4_config_set_nis_domain (dst, nm_ip4_config_get_nis_domain (src)); } /* WINS */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { for (i = 0; i < nm_ip4_config_get_num_wins (src); i++) nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i)); } /* metered flag */ nm_ip4_config_set_metered (dst, nm_ip4_config_get_metered (dst) || nm_ip4_config_get_metered (src)); /* DNS priority */ if (nm_ip4_config_get_dns_priority (src)) nm_ip4_config_set_dns_priority (dst, nm_ip4_config_get_dns_priority (src)); g_object_thaw_notify (G_OBJECT (dst)); } /*****************************************************************************/ static int _addresses_get_index (const NMIP4Config *self, const NMPlatformIP4Address *addr) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->addresses->len; i++) { const NMPlatformIP4Address *a = &g_array_index (priv->addresses, NMPlatformIP4Address, i); if (addresses_are_duplicate (addr, a)) return (int) i; } return -1; } static int _nameservers_get_index (const NMIP4Config *self, guint32 ns) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->nameservers->len; i++) { guint32 n = g_array_index (priv->nameservers, guint32, i); if (ns == n) return (int) i; } return -1; } static int _routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->routes->len; i++) { const NMPlatformIP4Route *r = &g_array_index (priv->routes, NMPlatformIP4Route, i); if ( route->network == r->network && route->plen == r->plen) return (int) i; } return -1; } static int _domains_get_index (const NMIP4Config *self, const char *domain) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->domains->len; i++) { const char *d = g_ptr_array_index (priv->domains, i); if (g_strcmp0 (domain, d) == 0) return (int) i; } return -1; } static int _searches_get_index (const NMIP4Config *self, const char *search) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->searches->len; i++) { const char *s = g_ptr_array_index (priv->searches, i); if (g_strcmp0 (search, s) == 0) return (int) i; } return -1; } static int _dns_options_get_index (const NMIP4Config *self, const char *option) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->dns_options->len; i++) { const char *s = g_ptr_array_index (priv->dns_options, i); if (g_strcmp0 (option, s) == 0) return (int) i; } return -1; } static int _nis_servers_get_index (const NMIP4Config *self, guint32 nis_server) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->nis->len; i++) { guint32 n = g_array_index (priv->nis, guint32, i); if (n == nis_server) return (int) i; } return -1; } static int _wins_get_index (const NMIP4Config *self, guint32 wins_server) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); guint i; for (i = 0; i < priv->wins->len; i++) { guint32 n = g_array_index (priv->wins, guint32, i); if (n == wins_server) return (int) i; } return -1; } /*****************************************************************************/ /** * nm_ip4_config_subtract: * @dst: config from which to remove everything in @src * @src: config to remove from @dst * * Removes everything in @src from @dst. */ void nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) { guint32 i; gint idx; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++) { idx = _addresses_get_index (dst, nm_ip4_config_get_address (src, i)); if (idx >= 0) nm_ip4_config_del_address (dst, idx); } /* nameservers */ for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++) { idx = _nameservers_get_index (dst, nm_ip4_config_get_nameserver (src, i)); if (idx >= 0) nm_ip4_config_del_nameserver (dst, idx); } /* default gateway */ if ( (nm_ip4_config_has_gateway (src) == nm_ip4_config_has_gateway (dst)) && (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst))) nm_ip4_config_unset_gateway (dst); if (!nm_ip4_config_get_num_addresses (dst)) nm_ip4_config_unset_gateway (dst); /* ignore route_metric */ /* routes */ for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) { idx = _routes_get_index (dst, nm_ip4_config_get_route (src, i)); if (idx >= 0) nm_ip4_config_del_route (dst, idx); } /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (src); i++) { idx = _domains_get_index (dst, nm_ip4_config_get_domain (src, i)); if (idx >= 0) nm_ip4_config_del_domain (dst, idx); } /* dns searches */ for (i = 0; i < nm_ip4_config_get_num_searches (src); i++) { idx = _searches_get_index (dst, nm_ip4_config_get_search (src, i)); if (idx >= 0) nm_ip4_config_del_search (dst, idx); } /* dns options */ for (i = 0; i < nm_ip4_config_get_num_dns_options (src); i++) { idx = _dns_options_get_index (dst, nm_ip4_config_get_dns_option (src, i)); if (idx >= 0) nm_ip4_config_del_dns_option (dst, idx); } /* MSS */ if (nm_ip4_config_get_mss (src) == nm_ip4_config_get_mss (dst)) nm_ip4_config_set_mss (dst, 0); /* MTU */ if ( nm_ip4_config_get_mtu (src) == nm_ip4_config_get_mtu (dst) && nm_ip4_config_get_mtu_source (src) == nm_ip4_config_get_mtu_source (dst)) nm_ip4_config_set_mtu (dst, 0, NM_IP_CONFIG_SOURCE_UNKNOWN); /* NIS */ for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++) { idx = _nis_servers_get_index (dst, nm_ip4_config_get_nis_server (src, i)); if (idx >= 0) nm_ip4_config_del_nis_server (dst, idx); } if (g_strcmp0 (nm_ip4_config_get_nis_domain (src), nm_ip4_config_get_nis_domain (dst)) == 0) nm_ip4_config_set_nis_domain (dst, NULL); /* WINS */ for (i = 0; i < nm_ip4_config_get_num_wins (src); i++) { idx = _wins_get_index (dst, nm_ip4_config_get_wins (src, i)); if (idx >= 0) nm_ip4_config_del_wins (dst, idx); } /* DNS priority */ if (nm_ip4_config_get_dns_priority (src) == nm_ip4_config_get_dns_priority (dst)) nm_ip4_config_set_dns_priority (dst, 0); g_object_thaw_notify (G_OBJECT (dst)); } void nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) { guint32 i; gint idx; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (dst); ) { idx = _addresses_get_index (src, nm_ip4_config_get_address (dst, i)); if (idx < 0) nm_ip4_config_del_address (dst, i); else i++; } /* ignore route_metric */ /* ignore nameservers */ /* default gateway */ if ( !nm_ip4_config_get_num_addresses (dst) || (nm_ip4_config_has_gateway (src) != nm_ip4_config_has_gateway (dst)) || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst))) { nm_ip4_config_unset_gateway (dst); } /* routes */ for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) { idx = _routes_get_index (src, nm_ip4_config_get_route (dst, i)); if (idx < 0) nm_ip4_config_del_route (dst, i); else i++; } /* ignore domains */ /* ignore dns searches */ /* ignore dns options */ /* ignore NIS */ /* ignore WINS */ g_object_thaw_notify (G_OBJECT (dst)); } /** * nm_ip4_config_replace: * @dst: config to replace with @src content * @src: source config to copy * @relevant_changes: return whether there are changes to the * destination object that are relevant. This is equal to * nm_ip4_config_equal() showing any difference. * * Replaces everything in @dst with @src so that the two configurations * contain the same content -- with the exception of the dbus path. * * Returns: whether the @dst instance changed in any way (including minor changes, * that are not signaled by the output parameter @relevant_changes). */ gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes) { #if NM_MORE_ASSERTS gboolean config_equal; #endif gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal; guint i, num; NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; const NMPlatformIP4Address *dst_addr, *src_addr; const NMPlatformIP4Route *dst_route, *src_route; g_return_val_if_fail (src != NULL, FALSE); g_return_val_if_fail (dst != NULL, FALSE); g_return_val_if_fail (src != dst, FALSE); #if NM_MORE_ASSERTS config_equal = nm_ip4_config_equal (dst, src); #endif dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst); src_priv = NM_IP4_CONFIG_GET_PRIVATE (src); g_object_freeze_notify (G_OBJECT (dst)); /* ifindex */ if (src_priv->ifindex != dst_priv->ifindex) { dst_priv->ifindex = src_priv->ifindex; has_minor_changes = TRUE; } /* never_default */ if (src_priv->never_default != dst_priv->never_default) { dst_priv->never_default = src_priv->never_default; has_minor_changes = TRUE; } /* default gateway */ if ( src_priv->gateway != dst_priv->gateway || src_priv->has_gateway != dst_priv->has_gateway) { if (src_priv->has_gateway) nm_ip4_config_set_gateway (dst, src_priv->gateway); else nm_ip4_config_unset_gateway (dst); has_relevant_changes = TRUE; } if (src_priv->route_metric != dst_priv->route_metric) { dst_priv->route_metric = src_priv->route_metric; has_minor_changes = TRUE; } /* addresses */ num = nm_ip4_config_get_num_addresses (src); are_equal = num == nm_ip4_config_get_num_addresses (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (nm_platform_ip4_address_cmp (src_addr = nm_ip4_config_get_address (src, i), dst_addr = nm_ip4_config_get_address (dst, i))) { are_equal = FALSE; if ( !addresses_are_duplicate (src_addr, dst_addr) || src_addr->peer_address != dst_addr->peer_address) { has_relevant_changes = TRUE; break; } } } } else has_relevant_changes = TRUE; if (!are_equal) { nm_ip4_config_reset_addresses (dst); for (i = 0; i < num; i++) nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i)); has_minor_changes = TRUE; } /* routes */ num = nm_ip4_config_get_num_routes (src); are_equal = num == nm_ip4_config_get_num_routes (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (nm_platform_ip4_route_cmp (src_route = nm_ip4_config_get_route (src, i), dst_route = nm_ip4_config_get_route (dst, i))) { are_equal = FALSE; if (!routes_are_duplicate (src_route, dst_route, TRUE)) { has_relevant_changes = TRUE; break; } } } } else has_relevant_changes = TRUE; if (!are_equal) { nm_ip4_config_reset_routes (dst); for (i = 0; i < num; i++) nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i)); has_minor_changes = TRUE; } /* nameservers */ num = nm_ip4_config_get_num_nameservers (src); are_equal = num == nm_ip4_config_get_num_nameservers (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (nm_ip4_config_get_nameserver (src, i) != nm_ip4_config_get_nameserver (dst, i)) { are_equal = FALSE; break; } } } if (!are_equal) { nm_ip4_config_reset_nameservers (dst); for (i = 0; i < num; i++) nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i)); has_relevant_changes = TRUE; } /* domains */ num = nm_ip4_config_get_num_domains (src); are_equal = num == nm_ip4_config_get_num_domains (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (g_strcmp0 (nm_ip4_config_get_domain (src, i), nm_ip4_config_get_domain (dst, i))) { are_equal = FALSE; break; } } } if (!are_equal) { nm_ip4_config_reset_domains (dst); for (i = 0; i < num; i++) nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i)); has_relevant_changes = TRUE; } /* dns searches */ num = nm_ip4_config_get_num_searches (src); are_equal = num == nm_ip4_config_get_num_searches (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (g_strcmp0 (nm_ip4_config_get_search (src, i), nm_ip4_config_get_search (dst, i))) { are_equal = FALSE; break; } } } if (!are_equal) { nm_ip4_config_reset_searches (dst); for (i = 0; i < num; i++) nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i)); has_relevant_changes = TRUE; } /* dns options */ num = nm_ip4_config_get_num_dns_options (src); are_equal = num == nm_ip4_config_get_num_dns_options (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (g_strcmp0 (nm_ip4_config_get_dns_option (src, i), nm_ip4_config_get_dns_option (dst, i))) { are_equal = FALSE; break; } } } if (!are_equal) { nm_ip4_config_reset_dns_options (dst); for (i = 0; i < num; i++) nm_ip4_config_add_dns_option (dst, nm_ip4_config_get_dns_option (src, i)); has_relevant_changes = TRUE; } /* DNS priority */ if (src_priv->dns_priority != dst_priv->dns_priority) { nm_ip4_config_set_dns_priority (dst, src_priv->dns_priority); has_minor_changes = TRUE; } /* mss */ if (src_priv->mss != dst_priv->mss) { nm_ip4_config_set_mss (dst, src_priv->mss); has_minor_changes = TRUE; } /* nis */ num = nm_ip4_config_get_num_nis_servers (src); are_equal = num == nm_ip4_config_get_num_nis_servers (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (nm_ip4_config_get_nis_server (src, i) != nm_ip4_config_get_nis_server (dst, i)) { are_equal = FALSE; break; } } } if (!are_equal) { nm_ip4_config_reset_nis_servers (dst); for (i = 0; i < num; i++) nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i)); has_relevant_changes = TRUE; } /* nis_domain */ if (g_strcmp0 (src_priv->nis_domain, dst_priv->nis_domain)) { nm_ip4_config_set_nis_domain (dst, src_priv->nis_domain); has_relevant_changes = TRUE; } /* wins */ num = nm_ip4_config_get_num_wins (src); are_equal = num == nm_ip4_config_get_num_wins (dst); if (are_equal) { for (i = 0; i < num; i++ ) { if (nm_ip4_config_get_wins (src, i) != nm_ip4_config_get_wins (dst, i)) { are_equal = FALSE; break; } } } if (!are_equal) { nm_ip4_config_reset_wins (dst); for (i = 0; i < num; i++) nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i)); has_relevant_changes = TRUE; } /* mtu */ if ( src_priv->mtu != dst_priv->mtu || src_priv->mtu_source != dst_priv->mtu_source) { nm_ip4_config_set_mtu (dst, src_priv->mtu, src_priv->mtu_source); has_minor_changes = TRUE; } /* metered */ if (src_priv->metered != dst_priv->metered) { dst_priv->metered = src_priv->metered; has_minor_changes = TRUE; } #if NM_MORE_ASSERTS /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */ nm_assert (config_equal == !has_relevant_changes); #endif g_object_thaw_notify (G_OBJECT (dst)); if (relevant_changes) *relevant_changes = has_relevant_changes; return has_relevant_changes || has_minor_changes; } void nm_ip4_config_dump (const NMIP4Config *config, const char *detail) { guint32 i, tmp; const char *str; g_message ("--------- NMIP4Config %p (%s)", config, detail); if (config == NULL) { g_message (" (null)"); return; } str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config)); if (str) g_message (" path: %s", str); /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) g_message (" a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i), NULL, 0)); /* default gateway */ if (nm_ip4_config_has_gateway (config)) { tmp = nm_ip4_config_get_gateway (config); g_message (" gw: %s", nm_utils_inet4_ntop (tmp, NULL)); } /* nameservers */ for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) { tmp = nm_ip4_config_get_nameserver (config, i); g_message (" ns: %s", nm_utils_inet4_ntop (tmp, NULL)); } /* routes */ for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) g_message (" rt: %s", nm_platform_ip4_route_to_string (nm_ip4_config_get_route (config, i), NULL, 0)); /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) g_message (" domain: %s", nm_ip4_config_get_domain (config, i)); /* dns searches */ for (i = 0; i < nm_ip4_config_get_num_searches (config); i++) g_message (" search: %s", nm_ip4_config_get_search (config, i)); /* dns options */ for (i = 0; i < nm_ip4_config_get_num_dns_options (config); i++) g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (config, i)); g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (config)); g_message (" mss: %"G_GUINT32_FORMAT, nm_ip4_config_get_mss (config)); g_message (" mtu: %"G_GUINT32_FORMAT" (source: %d)", nm_ip4_config_get_mtu (config), (int) nm_ip4_config_get_mtu_source (config)); /* NIS */ for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) { tmp = nm_ip4_config_get_nis_server (config, i); g_message (" nis: %s", nm_utils_inet4_ntop (tmp, NULL)); } g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (config) ?: "(none)"); /* WINS */ for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) { tmp = nm_ip4_config_get_wins (config, i); g_message (" wins: %s", nm_utils_inet4_ntop (tmp, NULL)); } g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config)); g_message (" mtrd: %d", (int) nm_ip4_config_get_metered (config)); } gboolean nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 network, guint8 plen) { guint naddresses = nm_ip4_config_get_num_addresses (config); guint i; in_addr_t peer_network; for (i = 0; i < naddresses; i++) { const NMPlatformIP4Address *item = nm_ip4_config_get_address (config, i); if (item->plen > plen) continue; peer_network = nm_utils_ip4_address_clear_host_address (item->peer_address, item->plen); if (_ipv4_is_zeronet (peer_network)) continue; if (peer_network != nm_utils_ip4_address_clear_host_address (network, item->plen)) continue; return TRUE; } return FALSE; } /*****************************************************************************/ void nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); priv->never_default = never_default; } gboolean nm_ip4_config_get_never_default (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->never_default; } void nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->gateway != gateway || !priv->has_gateway) { priv->gateway = gateway; priv->has_gateway = TRUE; _notify (config, PROP_GATEWAY); } } void nm_ip4_config_unset_gateway (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->has_gateway) { priv->gateway = 0; priv->has_gateway = FALSE; _notify (config, PROP_GATEWAY); } } /** * nm_ip4_config_has_gateway: * @config: the #NMIP4Config object * * NetworkManager's handling of default-routes is limited and usually a default-route * cannot have gateway 0.0.0.0. For peer-to-peer routes, we still want to * support that, so we need to differenciate between no-default-route and a * on-link-default route. Hence nm_ip4_config_has_gateway(). * * Returns: whether the object has a gateway explicitly set. */ gboolean nm_ip4_config_has_gateway (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->has_gateway; } guint32 nm_ip4_config_get_gateway (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->gateway; } gint64 nm_ip4_config_get_route_metric (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->route_metric; } /*****************************************************************************/ void nm_ip4_config_reset_addresses (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->addresses->len != 0) { g_array_set_size (priv->addresses, 0); notify_addresses (config); } } /** * nm_ip4_config_add_address: * @config: the #NMIP4Config * @new: the new address to add to @config * * Adds the new address to @config. If an address with the same basic properties * (address, prefix) already exists in @config, it is overwritten with the * lifetime and preferred of @new. The source is also overwritten by the source * from @new if that source is higher priority. */ void nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); NMPlatformIP4Address item_old; int i; g_return_if_fail (new != NULL); for (i = 0; i < priv->addresses->len; i++ ) { NMPlatformIP4Address *item = &g_array_index (priv->addresses, NMPlatformIP4Address, i); if (addresses_are_duplicate (item, new)) { if (nm_platform_ip4_address_cmp (item, new) == 0) return; /* remember the old values. */ item_old = *item; /* Copy over old item to get new lifetime, timestamp, preferred */ *item = *new; /* But restore highest priority source */ item->addr_source = MAX (item_old.addr_source, new->addr_source); /* for addresses that we read from the kernel, we keep the timestamps as defined * by the previous source (item_old). The reason is, that the other source configured the lifetimes * with "what should be" and the kernel values are "what turned out after configuring it". * * For other sources, the longer lifetime wins. */ if ( (new->addr_source == NM_IP_CONFIG_SOURCE_KERNEL && new->addr_source != item_old.addr_source) || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) { item->timestamp = item_old.timestamp; item->lifetime = item_old.lifetime; item->preferred = item_old.preferred; } if (nm_platform_ip4_address_cmp (&item_old, item) == 0) return; goto NOTIFY; } } g_array_append_val (priv->addresses, *new); NOTIFY: notify_addresses (config); } void nm_ip4_config_del_address (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->addresses->len); g_array_remove_index (priv->addresses, i); notify_addresses (config); } guint nm_ip4_config_get_num_addresses (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->addresses->len; } const NMPlatformIP4Address * nm_ip4_config_get_address (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return &g_array_index (priv->addresses, NMPlatformIP4Address, i); } gboolean nm_ip4_config_address_exists (const NMIP4Config *config, const NMPlatformIP4Address *needle) { return _addresses_get_index (config, needle) >= 0; } /*****************************************************************************/ void nm_ip4_config_reset_routes (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->routes->len != 0) { g_array_set_size (priv->routes, 0); _notify (config, PROP_ROUTE_DATA); _notify (config, PROP_ROUTES); } } /** * nm_ip4_config_add_route: * @config: the #NMIP4Config * @new: the new route to add to @config * * Adds the new route to @config. If a route with the same basic properties * (network, prefix) already exists in @config, it is overwritten including the * gateway and metric of @new. The source is also overwritten by the source * from @new if that source is higher priority. */ void nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); NMIPConfigSource old_source; int i; g_return_if_fail (new != NULL); g_return_if_fail (new->plen > 0 && new->plen <= 32); g_return_if_fail (priv->ifindex > 0); for (i = 0; i < priv->routes->len; i++ ) { NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); if (routes_are_duplicate (item, new, FALSE)) { if (nm_platform_ip4_route_cmp (item, new) == 0) return; old_source = item->rt_source; memcpy (item, new, sizeof (*item)); /* Restore highest priority source */ item->rt_source = MAX (old_source, new->rt_source); item->ifindex = priv->ifindex; goto NOTIFY; } } g_array_append_val (priv->routes, *new); g_array_index (priv->routes, NMPlatformIP4Route, priv->routes->len - 1).ifindex = priv->ifindex; NOTIFY: _notify (config, PROP_ROUTE_DATA); _notify (config, PROP_ROUTES); } void nm_ip4_config_del_route (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->routes->len); g_array_remove_index (priv->routes, i); _notify (config, PROP_ROUTE_DATA); _notify (config, PROP_ROUTES); } guint nm_ip4_config_get_num_routes (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->routes->len; } const NMPlatformIP4Route * nm_ip4_config_get_route (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return &g_array_index (priv->routes, NMPlatformIP4Route, i); } const NMPlatformIP4Route * nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); guint i; NMPlatformIP4Route *best_route = NULL; g_return_val_if_fail (host, NULL); for (i = 0; i < priv->routes->len; i++) { NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); if (item->gateway != 0) continue; if (best_route && best_route->plen > item->plen) continue; if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->network, item->plen)) continue; if (best_route && best_route->metric <= item->metric) continue; best_route = item; } return best_route; } /*****************************************************************************/ void nm_ip4_config_reset_nameservers (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->nameservers->len != 0) { g_array_set_size (priv->nameservers, 0); _notify (config, PROP_NAMESERVERS); } } void nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 new) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); int i; g_return_if_fail (new != 0); for (i = 0; i < priv->nameservers->len; i++) if (new == g_array_index (priv->nameservers, guint32, i)) return; g_array_append_val (priv->nameservers, new); _notify (config, PROP_NAMESERVERS); } void nm_ip4_config_del_nameserver (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->nameservers->len); g_array_remove_index (priv->nameservers, i); _notify (config, PROP_NAMESERVERS); } guint nm_ip4_config_get_num_nameservers (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->nameservers->len; } guint32 nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return g_array_index (priv->nameservers, guint32, i); } /*****************************************************************************/ void nm_ip4_config_reset_domains (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->domains->len != 0) { g_ptr_array_set_size (priv->domains, 0); _notify (config, PROP_DOMAINS); } } void nm_ip4_config_add_domain (NMIP4Config *config, const char *domain) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); int i; g_return_if_fail (domain != NULL); g_return_if_fail (domain[0] != '\0'); for (i = 0; i < priv->domains->len; i++) if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain)) return; g_ptr_array_add (priv->domains, g_strdup (domain)); _notify (config, PROP_DOMAINS); } void nm_ip4_config_del_domain (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->domains->len); g_ptr_array_remove_index (priv->domains, i); _notify (config, PROP_DOMAINS); } guint nm_ip4_config_get_num_domains (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->domains->len; } const char * nm_ip4_config_get_domain (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return g_ptr_array_index (priv->domains, i); } /*****************************************************************************/ void nm_ip4_config_reset_searches (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->searches->len != 0) { g_ptr_array_set_size (priv->searches, 0); _notify (config, PROP_SEARCHES); } } void nm_ip4_config_add_search (NMIP4Config *config, const char *new) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); char *search; size_t len; g_return_if_fail (new != NULL); g_return_if_fail (new[0] != '\0'); search = g_strdup (new); /* Remove trailing dot as it has no effect */ len = strlen (search); if (search[len - 1] == '.') search[len - 1] = 0; if (!search[0]) { g_free (search); return; } if (nm_utils_strv_find_first ((char **) priv->searches->pdata, priv->searches->len, search) >= 0) { g_free (search); return; } g_ptr_array_add (priv->searches, search); _notify (config, PROP_SEARCHES); } void nm_ip4_config_del_search (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->searches->len); g_ptr_array_remove_index (priv->searches, i); _notify (config, PROP_SEARCHES); } guint nm_ip4_config_get_num_searches (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->searches->len; } const char * nm_ip4_config_get_search (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return g_ptr_array_index (priv->searches, i); } /*****************************************************************************/ void nm_ip4_config_reset_dns_options (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->dns_options->len != 0) { g_ptr_array_set_size (priv->dns_options, 0); _notify (config, PROP_DNS_OPTIONS); } } void nm_ip4_config_add_dns_option (NMIP4Config *config, const char *new) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); int i; g_return_if_fail (new != NULL); g_return_if_fail (new[0] != '\0'); for (i = 0; i < priv->dns_options->len; i++) if (!g_strcmp0 (g_ptr_array_index (priv->dns_options, i), new)) return; g_ptr_array_add (priv->dns_options, g_strdup (new)); _notify (config, PROP_DNS_OPTIONS); } void nm_ip4_config_del_dns_option(NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->dns_options->len); g_ptr_array_remove_index (priv->dns_options, i); _notify (config, PROP_DNS_OPTIONS); } guint nm_ip4_config_get_num_dns_options (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->dns_options->len; } const char * nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return g_ptr_array_index (priv->dns_options, i); } /*****************************************************************************/ void nm_ip4_config_set_dns_priority (NMIP4Config *config, gint priority) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priority != priv->dns_priority) { priv->dns_priority = priority; _notify (config, PROP_DNS_PRIORITY); } } gint nm_ip4_config_get_dns_priority (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->dns_priority; } /*****************************************************************************/ void nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); priv->mss = mss; } guint32 nm_ip4_config_get_mss (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->mss; } /*****************************************************************************/ void nm_ip4_config_reset_nis_servers (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_array_set_size (priv->nis, 0); } void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); int i; for (i = 0; i < priv->nis->len; i++) if (nis == g_array_index (priv->nis, guint32, i)) return; g_array_append_val (priv->nis, nis); } void nm_ip4_config_del_nis_server (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->nis->len); g_array_remove_index (priv->nis, i); } guint nm_ip4_config_get_num_nis_servers (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->nis->len; } guint32 nm_ip4_config_get_nis_server (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return g_array_index (priv->nis, guint32, i); } void nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_free (priv->nis_domain); priv->nis_domain = g_strdup (domain); } const char * nm_ip4_config_get_nis_domain (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->nis_domain; } /*****************************************************************************/ void nm_ip4_config_reset_wins (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (priv->wins->len != 0) { g_array_set_size (priv->wins, 0); _notify (config, PROP_WINS_SERVERS); } } void nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); int i; g_return_if_fail (wins != 0); for (i = 0; i < priv->wins->len; i++) if (wins == g_array_index (priv->wins, guint32, i)) return; g_array_append_val (priv->wins, wins); _notify (config, PROP_WINS_SERVERS); } void nm_ip4_config_del_wins (NMIP4Config *config, guint i) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_return_if_fail (i < priv->wins->len); g_array_remove_index (priv->wins, i); _notify (config, PROP_WINS_SERVERS); } guint nm_ip4_config_get_num_wins (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->wins->len; } guint32 nm_ip4_config_get_wins (const NMIP4Config *config, guint i) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return g_array_index (priv->wins, guint32, i); } /*****************************************************************************/ void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); if (!mtu) source = NM_IP_CONFIG_SOURCE_UNKNOWN; priv->mtu = mtu; priv->mtu_source = source; } guint32 nm_ip4_config_get_mtu (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->mtu; } NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->mtu_source; } /*****************************************************************************/ void nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); priv->metered = metered; } gboolean nm_ip4_config_get_metered (const NMIP4Config *config) { const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); return priv->metered; } /*****************************************************************************/ static inline void hash_u32 (GChecksum *sum, guint32 n) { g_checksum_update (sum, (const guint8 *) &n, sizeof (n)); } void nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only) { guint i; const char *s; g_return_if_fail (config); g_return_if_fail (sum); if (!dns_only) { hash_u32 (sum, nm_ip4_config_has_gateway (config)); hash_u32 (sum, nm_ip4_config_get_gateway (config)); for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) { const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); hash_u32 (sum, address->address); hash_u32 (sum, address->plen); hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (address->plen)); } for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); hash_u32 (sum, route->network); hash_u32 (sum, route->plen); hash_u32 (sum, route->gateway); hash_u32 (sum, route->metric); } for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) hash_u32 (sum, nm_ip4_config_get_nis_server (config, i)); s = nm_ip4_config_get_nis_domain (config); if (s) g_checksum_update (sum, (const guint8 *) s, strlen (s)); } for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) hash_u32 (sum, nm_ip4_config_get_nameserver (config, i)); for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) hash_u32 (sum, nm_ip4_config_get_wins (config, i)); for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) { s = nm_ip4_config_get_domain (config, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } for (i = 0; i < nm_ip4_config_get_num_searches (config); i++) { s = nm_ip4_config_get_search (config, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } for (i = 0; i < nm_ip4_config_get_num_dns_options (config); i++) { s = nm_ip4_config_get_dns_option (config, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } } /** * nm_ip4_config_equal: * @a: first config to compare * @b: second config to compare * * Compares two #NMIP4Configs for basic equality. This means that all * attributes must exist in the same order in both configs (addresses, routes, * domains, DNS servers, etc) but some attributes (address lifetimes, and address * and route sources) are ignored. * * Returns: %TRUE if the configurations are basically equal to each other, * %FALSE if not */ gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b) { GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); guchar a_data[a_len], b_data[b_len]; gboolean equal; if (a) nm_ip4_config_hash (a, a_checksum, FALSE); if (b) nm_ip4_config_hash (b, b_checksum, FALSE); g_checksum_get_digest (a_checksum, a_data, &a_len); g_checksum_get_digest (b_checksum, b_data, &b_len); g_assert (a_len == b_len); equal = !memcmp (a_data, b_data, a_len); g_checksum_free (a_checksum); g_checksum_free (b_checksum); return equal; } /*****************************************************************************/ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMIP4Config *config = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); switch (prop_id) { case PROP_IFINDEX: g_value_set_int (value, priv->ifindex); break; case PROP_ADDRESS_DATA: case PROP_ADDRESSES: { GVariantBuilder array_builder, addr_builder; gs_unref_array GArray *new = NULL; guint naddr, i; g_return_if_fail (!!priv->address_data_variant == !!priv->addresses_variant); if (priv->address_data_variant) goto return_cached; naddr = nm_ip4_config_get_num_addresses (config); new = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Address), naddr); g_array_append_vals (new, priv->addresses->data, priv->addresses->len); g_array_sort (new, _addresses_sort_cmp); /* Build address data variant */ g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); for (i = 0; i < naddr; i++) { const NMPlatformIP4Address *address = &g_array_index (new, NMPlatformIP4Address, i); g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&addr_builder, "{sv}", "address", g_variant_new_string (nm_utils_inet4_ntop (address->address, NULL))); g_variant_builder_add (&addr_builder, "{sv}", "prefix", g_variant_new_uint32 (address->plen)); if (address->peer_address != address->address) { g_variant_builder_add (&addr_builder, "{sv}", "peer", g_variant_new_string (nm_utils_inet4_ntop (address->peer_address, NULL))); } if (*address->label) { g_variant_builder_add (&addr_builder, "{sv}", "label", g_variant_new_string (address->label)); } g_variant_builder_add (&array_builder, "a{sv}", &addr_builder); } priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); /* Build addresses variant */ g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau")); for (i = 0; i < naddr; i++) { const NMPlatformIP4Address *address = &g_array_index (new, NMPlatformIP4Address, i); guint32 dbus_addr[3]; dbus_addr[0] = address->address; dbus_addr[1] = address->plen; dbus_addr[2] = i == 0 ? priv->gateway : 0; g_variant_builder_add (&array_builder, "@au", g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, dbus_addr, 3, sizeof (guint32))); } priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); return_cached: g_value_set_variant (value, prop_id == PROP_ADDRESS_DATA ? priv->address_data_variant : priv->addresses_variant); } break; case PROP_ROUTE_DATA: { GVariantBuilder array_builder, route_builder; guint nroutes = nm_ip4_config_get_num_routes (config); guint i; g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); for (i = 0; i < nroutes; i++) { const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&route_builder, "{sv}", "dest", g_variant_new_string (nm_utils_inet4_ntop (route->network, NULL))); g_variant_builder_add (&route_builder, "{sv}", "prefix", g_variant_new_uint32 (route->plen)); if (route->gateway) { g_variant_builder_add (&route_builder, "{sv}", "next-hop", g_variant_new_string (nm_utils_inet4_ntop (route->gateway, NULL))); } g_variant_builder_add (&route_builder, "{sv}", "metric", g_variant_new_uint32 (route->metric)); g_variant_builder_add (&array_builder, "a{sv}", &route_builder); } g_value_take_variant (value, g_variant_builder_end (&array_builder)); } break; case PROP_ROUTES: { GVariantBuilder array_builder; guint nroutes = nm_ip4_config_get_num_routes (config); guint i; g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau")); for (i = 0; i < nroutes; i++) { const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); guint32 dbus_route[4]; /* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the * plen is positive. Skip the default routes not to break older clients. */ if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; dbus_route[0] = route->network; dbus_route[1] = route->plen; dbus_route[2] = route->gateway; dbus_route[3] = route->metric; g_variant_builder_add (&array_builder, "@au", g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, dbus_route, 4, sizeof (guint32))); } g_value_take_variant (value, g_variant_builder_end (&array_builder)); } break; case PROP_GATEWAY: if (priv->has_gateway) g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL)); else g_value_set_string (value, NULL); break; case PROP_NAMESERVERS: g_value_take_variant (value, g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, priv->nameservers->data, priv->nameservers->len, sizeof (guint32))); break; case PROP_DOMAINS: nm_utils_g_value_set_strv (value, priv->domains); break; case PROP_SEARCHES: nm_utils_g_value_set_strv (value, priv->searches); break; case PROP_DNS_OPTIONS: nm_utils_g_value_set_strv (value, priv->dns_options); break; case PROP_DNS_PRIORITY: g_value_set_int (value, priv->dns_priority); break; case PROP_WINS_SERVERS: g_value_take_variant (value, g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, priv->wins->data, priv->wins->len, sizeof (guint32))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMIP4Config *self = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); switch (prop_id) { case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /*****************************************************************************/ static void nm_ip4_config_init (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); priv->addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address)); priv->routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); priv->nameservers = g_array_new (FALSE, FALSE, sizeof (guint32)); priv->domains = g_ptr_array_new_with_free_func (g_free); priv->searches = g_ptr_array_new_with_free_func (g_free); priv->dns_options = g_ptr_array_new_with_free_func (g_free); priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32)); priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32)); priv->route_metric = -1; } NMIP4Config * nm_ip4_config_new (int ifindex) { g_return_val_if_fail (ifindex >= -1, NULL); return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG, NM_IP4_CONFIG_IFINDEX, ifindex, NULL); } static void finalize (GObject *object) { NMIP4Config *self = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); nm_clear_g_variant (&priv->address_data_variant); nm_clear_g_variant (&priv->addresses_variant); g_array_unref (priv->addresses); g_array_unref (priv->routes); g_array_unref (priv->nameservers); g_ptr_array_unref (priv->domains); g_ptr_array_unref (priv->searches); g_ptr_array_unref (priv->dns_options); g_array_unref (priv->nis); g_free (priv->nis_domain); g_array_unref (priv->wins); G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object); } static void nm_ip4_config_class_init (NMIP4ConfigClass *config_class) { GObjectClass *object_class = G_OBJECT_CLASS (config_class); NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class); exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP4Config"); object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; obj_properties[PROP_IFINDEX] = g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_ADDRESS_DATA] = g_param_spec_variant (NM_IP4_CONFIG_ADDRESS_DATA, "", "", G_VARIANT_TYPE ("aa{sv}"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_ADDRESSES] = g_param_spec_variant (NM_IP4_CONFIG_ADDRESSES, "", "", G_VARIANT_TYPE ("aau"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_ROUTE_DATA] = g_param_spec_variant (NM_IP4_CONFIG_ROUTE_DATA, "", "", G_VARIANT_TYPE ("aa{sv}"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_ROUTES] = g_param_spec_variant (NM_IP4_CONFIG_ROUTES, "", "", G_VARIANT_TYPE ("aau"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_GATEWAY] = g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_NAMESERVERS] = g_param_spec_variant (NM_IP4_CONFIG_NAMESERVERS, "", "", G_VARIANT_TYPE ("au"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_DOMAINS] = g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_SEARCHES] = g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_DNS_OPTIONS] = g_param_spec_boxed (NM_IP4_CONFIG_DNS_OPTIONS, "", "", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_DNS_PRIORITY] = g_param_spec_int (NM_IP4_CONFIG_DNS_PRIORITY, "", "", G_MININT32, G_MAXINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_WINS_SERVERS] = g_param_spec_variant (NM_IP4_CONFIG_WINS_SERVERS, "", "", G_VARIANT_TYPE ("au"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class), NMDBUS_TYPE_IP4_CONFIG_SKELETON, NULL); }