diff options
Diffstat (limited to 'src/nm-ip4-config.c')
| -rw-r--r-- | src/nm-ip4-config.c | 2364 |
1 files changed, 1452 insertions, 912 deletions
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index ae6af9c3..e2f99e24 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -25,12 +25,16 @@ #include <string.h> #include <arpa/inet.h> +#include <resolv.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" #include "nm-utils.h" +#include "platform/nmp-object.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" @@ -43,7 +47,232 @@ G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF); /*****************************************************************************/ +static gboolean +_route_valid (const NMPlatformIP4Route *r) +{ + return r + && r->plen <= 32 + && r->network == nm_utils_ip4_address_clear_host_address (r->network, r->plen); +} + +/*****************************************************************************/ + +static void +_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) +{ + nmp_object_id_hash_update ((NMPObject *) obj, h); +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return nmp_object_id_equal ((NMPObject *) obj_a, (NMPObject *) obj_b); +} + +void +nm_ip_config_dedup_multi_idx_type_init (NMIPConfigDedupMultiIdxType *idx_type, + NMPObjectType obj_type) +{ + static const NMDedupMultiIdxTypeClass idx_type_class = { + .idx_obj_id_hash_update = _idx_obj_id_hash_update, + .idx_obj_id_equal = _idx_obj_id_equal, + }; + + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, + &idx_type_class); + idx_type->obj_type = obj_type; +} + +/*****************************************************************************/ + +gboolean +_nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, + NMIPConfigDedupMultiIdxType *idx_type, + int ifindex, + const NMPObject *obj_new, + const NMPlatformObject *pl_new, + gboolean merge, + gboolean append_force, + const NMPObject **out_obj_old /* returns a reference! */, + const NMPObject **out_obj_new /* does not return a reference */) +{ + NMPObject obj_new_stackinit; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (ifindex > 0); + + /* we go through extra lengths to accept a full obj_new object. That one, + * can be reused by increasing the ref-count. */ + if (!obj_new) { + nm_assert (pl_new); + obj_new = nmp_object_stackinit (&obj_new_stackinit, idx_type->obj_type, pl_new); + obj_new_stackinit.object.ifindex = ifindex; + } else { + nm_assert (!pl_new); + nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); + if (obj_new->object.ifindex != ifindex) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.object.ifindex = ifindex; + } + } + nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); + nm_assert (nmp_object_is_alive (obj_new)); + + entry_old = nm_dedup_multi_index_lookup_obj (multi_idx, &idx_type->parent, obj_new); + + if (entry_old) { + gboolean modified = FALSE; + const NMPObject *obj_old = entry_old->obj; + + if (nmp_object_equal (obj_new, obj_old)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + + /* if @merge, we merge the new object with the existing one. + * Otherwise, we replace it entirely. */ + if (merge) { + switch (idx_type->obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + /* 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 ( ( obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL + && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL) + || nm_platform_ip_address_cmp_expiry (NMP_OBJECT_CAST_IP_ADDRESS (obj_old), NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) > 0) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.timestamp = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->timestamp; + obj_new_stackinit.ip_address.lifetime = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->lifetime; + obj_new_stackinit.ip_address.preferred = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->preferred; + modified = TRUE; + } + + /* keep the maximum addr_source. */ + if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source; + modified = TRUE; + } + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + /* keep the maximum rt_source. */ + if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; + modified = TRUE; + } + break; + default: + nm_assert_not_reached (); + break; + } + + if ( modified + && nmp_object_equal (obj_new, obj_old)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + } + } + + if (!nm_dedup_multi_index_add_full (multi_idx, + &idx_type->parent, + obj_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, + NULL, + &entry_new, + out_obj_old)) { + nm_assert_not_reached (); + NM_SET_OUT (out_obj_new, NULL); + return FALSE; + } + + NM_SET_OUT (out_obj_new, entry_new->obj); + return TRUE; + +append_force_and_out: + NM_SET_OUT (out_obj_old, nmp_object_ref (entry_old->obj)); + NM_SET_OUT (out_obj_new, entry_old->obj); + if (append_force) { + if (nm_dedup_multi_entry_reorder (entry_old, NULL, TRUE)) + return TRUE; + } + return FALSE; +} + +/** + * _nm_ip_config_lookup_ip_route: + * @multi_idx: + * @idx_type: + * @needle: + * @cmp_type: after lookup, filter the result by comparing with @cmp_type. Only + * return the result, if it compares equal to @needle according to this @cmp_type. + * Note that the index uses %NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST type, so passing + * that compare-type means not to filter any further. + * + * Returns: the found entry or %NULL. + */ +const NMDedupMultiEntry * +_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMDedupMultiEntry *entry; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == idx_type->obj_type); + + entry = nm_dedup_multi_index_lookup_obj (multi_idx, + &idx_type->parent, + needle); + if (!entry) + return NULL; + + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + nm_assert ( ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE + && nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), cmp_type) == 0) + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE + && nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), NMP_OBJECT_CAST_IP6_ROUTE (needle), cmp_type) == 0)); + } else { + if (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE) { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP4_ROUTE (needle), + cmp_type) != 0) + return NULL; + } else { + if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP6_ROUTE (needle), + cmp_type) != 0) + return NULL; + } + } + return entry; +} + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, + PROP_MULTI_IDX, PROP_IFINDEX, PROP_ADDRESS_DATA, PROP_ADDRESSES, @@ -59,18 +288,11 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, ); 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; @@ -80,6 +302,18 @@ typedef struct { GArray *wins; GVariant *address_data_variant; GVariant *addresses_variant; + GVariant *route_data_variant; + GVariant *routes_variant; + NMDedupMultiIndex *multi_idx; + const NMPObject *best_default_route; + union { + NMIPConfigDedupMultiIdxType idx_ip4_addresses_; + NMDedupMultiIdxType idx_ip4_addresses; + }; + union { + NMIPConfigDedupMultiIdxType idx_ip4_routes_; + NMDedupMultiIdxType idx_ip4_routes; + }; } NMIP4ConfigPrivate; struct _NMIP4Config { @@ -97,10 +331,24 @@ G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) /*****************************************************************************/ +static void _add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new); +static void _add_route (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Route *new, const NMPObject **out_obj_new); +static const NMDedupMultiEntry *_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); + +/*****************************************************************************/ + int -nm_ip4_config_get_ifindex (const NMIP4Config *config) +nm_ip4_config_get_ifindex (const NMIP4Config *self) +{ + return NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex; +} + +NMDedupMultiIndex * +nm_ip4_config_get_multi_idx (const NMIP4Config *self) { - return NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex; + return NM_IP4_CONFIG_GET_PRIVATE (self)->multi_idx; } /*****************************************************************************/ @@ -114,83 +362,167 @@ _ipv4_is_zeronet (in_addr_t network) /*****************************************************************************/ -/** - * 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) +const NMDedupMultiHeadEntry * +nm_ip4_config_lookup_addresses (const NMIP4Config *self) { - GPtrArray *read_ns, *read_options; - guint i, j; - gboolean changed = FALSE; + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - g_return_val_if_fail (nameservers != NULL, FALSE); + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip4_addresses, + NULL); +} - read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents); - if (!read_ns) - return FALSE; +void +nm_ip_config_iter_ip4_address_init (NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, nm_ip4_config_lookup_addresses (self)); +} - 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; +const NMDedupMultiHeadEntry * +nm_ip4_config_lookup_routes (const NMIP4Config *self) +{ + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - /* Ignore duplicates */ - for (j = 0; j < nameservers->len; j++) { - if (g_array_index (nameservers, guint32, j) == ns) - break; - } + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip4_routes, + NULL); +} - if (j == nameservers->len) { - g_array_append_val (nameservers, ns); - changed = TRUE; +void +nm_ip_config_iter_ip4_route_init (NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, nm_ip4_config_lookup_routes (self)); +} + +/*****************************************************************************/ + +const NMPObject * +_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp) +{ + int addr_family; + int c; + guint metric_cur, metric_cmp; + + nm_assert ( !obj_cur + || NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cur), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert ( !obj_cmp + || ( !obj_cur + && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cmp), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) + || NMP_OBJECT_GET_TYPE (obj_cur) == NMP_OBJECT_GET_TYPE (obj_cmp)); + nm_assert ( !obj_cur + || nm_ip_config_best_default_route_is (obj_cur)); + + /* assumes that @obj_cur is already the best default route (or NULL). It checks whether + * @obj_cmp is also a default route and returns the best of both. */ + if ( obj_cmp + && nm_ip_config_best_default_route_is (obj_cmp)) { + if (!obj_cur) + return obj_cmp; + + addr_family = NMP_OBJECT_GET_CLASS (obj_cmp)->addr_family; + metric_cur = nm_utils_ip_route_metric_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_cur)->metric); + metric_cmp = nm_utils_ip_route_metric_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_cmp)->metric); + + if (metric_cmp < metric_cur) + return obj_cmp; + + if (metric_cmp == metric_cur) { + /* Routes have the same metric. We still want to deterministically + * prefer one or the other. It's important to consistently choose one + * or the other, so that the order doesn't matter how routes are added + * (and merged). */ + c = nmp_object_cmp (obj_cur, obj_cmp); + if (c != 0) + return c < 0 ? obj_cur : obj_cmp; + + /* as last resort, compare pointers. */ + if (obj_cmp < obj_cur) + return obj_cmp; } } - g_ptr_array_unref (read_ns); + return obj_cur; +} - if (dns_options) { - read_options = nm_utils_read_resolv_conf_dns_options (rc_contents); - if (!read_options) - return changed; +gboolean +_nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + if (new_candidate == *best_default_route) + return FALSE; + nmp_object_ref (new_candidate); + nm_clear_nmp_object (best_default_route); + *best_default_route = new_candidate; + return TRUE; +} - for (i = 0; i < read_options->len; i++) { - const char *s = g_ptr_array_index (read_options, i); +gboolean +_nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + new_candidate = _nm_ip_config_best_default_route_find_better (*best_default_route, + new_candidate); + return _nm_ip_config_best_default_route_set (best_default_route, new_candidate); +} - 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); +const NMPObject * +nm_ip4_config_best_default_route_get (const NMIP4Config *self) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), NULL); + + return NM_IP4_CONFIG_GET_PRIVATE (self)->best_default_route; +} + +const NMPObject * +_nm_ip4_config_best_default_route_find (const NMIP4Config *self) +{ + NMDedupMultiIter ipconf_iter; + const NMPObject *new_best_default_route = NULL; + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, NULL) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, + ipconf_iter.current->obj); } + return new_best_default_route; +} + +in_addr_t +nmtst_ip4_config_get_gateway (NMIP4Config *config) +{ + const NMPObject *rt; - return changed; + g_assert (NM_IS_IP4_CONFIG (config)); + + rt = nm_ip4_config_best_default_route_get (config); + if (!rt) + return 0; + return NMP_OBJECT_CAST_IP4_ROUTE (rt)->gateway; } -static gboolean -addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +/*****************************************************************************/ + +static void +_notify_addresses (NMIP4Config *self) { - return a->address == b->address - && a->plen == b->plen - && ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0; + 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); } -static gboolean -routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_gateway_and_metric) +static void +_notify_routes (NMIP4Config *self) { - return a->network == b->network && a->plen == b->plen && - (!consider_gateway_and_metric || (a->gateway == b->gateway && a->metric == b->metric)); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nm_assert (priv->best_default_route == _nm_ip4_config_best_default_route_find (self)); + nm_clear_g_variant (&priv->route_data_variant); + nm_clear_g_variant (&priv->routes_variant); + _notify (self, PROP_ROUTE_DATA); + _notify (self, PROP_ROUTES); } /*****************************************************************************/ @@ -203,11 +535,12 @@ _addresses_sort_cmp_get_prio (in_addr_t addr) return 1; } -static gint -_addresses_sort_cmp (gconstpointer a, gconstpointer b) +static int +_addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data) { gint p1, p2; - const NMPlatformIP4Address *a1 = a, *a2 = b; + const NMPlatformIP4Address *a1 = NMP_OBJECT_CAST_IP4_ADDRESS (*((const NMPObject **) a)); + const NMPlatformIP4Address *a2 = NMP_OBJECT_CAST_IP4_ADDRESS (*((const NMPObject **) b)); guint32 n1, n2; /* Sort by address type. For example link local will @@ -229,29 +562,19 @@ _addresses_sort_cmp (gconstpointer a, gconstpointer b) * 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); + 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); -} - -static gint -sort_captured_addresses (gconstpointer a, gconstpointer b) +static int +sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer user_data) { - const NMPlatformIP4Address *addr_a = a, *addr_b = b; + const NMPlatformIP4Address *addr_a = NMP_OBJECT_CAST_IP4_ADDRESS (c_list_entry (lst_a, NMDedupMultiEntry, lst_entries)->obj); + const NMPlatformIP4Address *addr_b = NMP_OBJECT_CAST_IP4_ADDRESS (c_list_entry (lst_b, NMDedupMultiEntry, lst_entries)->obj); /* Primary addresses first */ return NM_FLAGS_HAS (addr_a->n_ifa_flags, IFA_F_SECONDARY) - @@ -259,175 +582,237 @@ sort_captured_addresses (gconstpointer a, gconstpointer b) } NMIP4Config * -nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) +nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) { - NMIP4Config *config; + NMIP4Config *self; NMIP4ConfigPrivate *priv; - guint i; - guint32 lowest_metric = G_MAXUINT32; - guint32 old_gateway = 0; - gboolean old_has_gateway = FALSE; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; + gboolean has_addresses = FALSE; + + nm_assert (ifindex > 0); /* Slaves have no IP configuration */ if (nm_platform_link_get_master (platform, 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 (platform, ifindex); - g_array_sort (priv->addresses, sort_captured_addresses); - - priv->routes = nm_platform_ip4_route_get_all (platform, 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; + self = nm_ip4_config_new (multi_idx, ifindex); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + head_entry = nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex); + if (head_entry) { + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + if (!_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip4_addresses_, + ifindex, + plobj, + NULL, + FALSE, + TRUE, + NULL, + NULL)) + nm_assert_not_reached (); } - i++; + head_entry = nm_ip4_config_lookup_addresses (self); + nm_assert (head_entry); + nm_dedup_multi_head_entry_sort (head_entry, + sort_captured_addresses, + NULL); + has_addresses = TRUE; + _notify_addresses (self); } - /* 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; + head_entry = nm_platform_lookup_addrroute (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex); - /* 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--; - } - } - } + /* Extract gateway from default route */ + nmp_cache_iter_for_each (&iter, head_entry, &plobj) + _add_route (self, plobj, NULL, NULL); /* 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); + if ( has_addresses + && priv->best_default_route + && capture_resolv_conf) { + gs_free char *rc_contents = NULL; + + if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) { + if (nm_utils_resolve_conf_parse (AF_INET, + rc_contents, + priv->nameservers, + priv->dns_options)) + _notify (self, 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; + return self; } -gboolean -nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteManager *route_manager, 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; +void +nm_ip4_config_add_dependent_routes (NMIP4Config *self, + guint32 route_table, + guint32 route_metric, + GPtrArray **out_ip4_dev_route_blacklist) +{ + const NMIP4ConfigPrivate *priv; + GPtrArray *ip4_dev_route_blacklist = NULL; + const NMPlatformIP4Address *my_addr; + const NMPlatformIP4Route *my_route; + int ifindex; + NMDedupMultiIter iter; - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (config != NULL, FALSE); + g_return_if_fail (NM_IS_IP4_CONFIG (self)); - /* Addresses */ - nm_platform_ip4_address_sync (platform, ifindex, priv->addresses, - default_route_metric >= 0 ? &added_addresses : NULL); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); - /* 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; + ifindex = nm_ip4_config_get_ifindex (self); + g_return_if_fail (ifindex > 0); - nm_assert (addr->plen <= 32); + /* For IPv6 slaac, we explicitly add the device-routes (onlink) to NMIP6Config. + * As we don't do that for IPv4 (and manual IPv6 addresses), add them explicitly. */ - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip_config_iter_ip4_address_for_each (&iter, self, &my_addr) { + nm_auto_nmpobj NMPObject *r = NULL; + NMPlatformIP4Route *route; + in_addr_t network; - /* The destination network depends on the peer-address. */ - route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + if (my_addr->plen == 0) + continue; - if (_ipv4_is_zeronet (route.network)) { - /* Kernel doesn't add device-routes for destinations that - * start with 0.x.y.z. Skip them. */ - continue; - } + nm_assert (my_addr->plen <= 32); - route.plen = addr->plen; - route.pref_src = addr->address; - route.metric = default_route_metric; + /* The destination network depends on the peer-address. */ + network = nm_utils_ip4_address_clear_host_address (my_addr->peer_address, my_addr->plen); - g_array_append_val (routes, route); + if (_ipv4_is_zeronet (network)) { + /* Kernel doesn't add device-routes for destinations that + * start with 0.x.y.z. Skip them. */ + continue; + } - 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); - } + r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP4_ROUTE (r); + + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->network = network; + route->plen = my_addr->plen; + route->pref_src = my_addr->address; + route->table_coerced = nm_platform_route_table_coerce (route_table); + route->metric = route_metric; + route->scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK); + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we already track this route. Don't add it again. */ + } else + _add_route (self, r, NULL, NULL); + + if ( out_ip4_dev_route_blacklist + && ( route_table != RT_TABLE_MAIN + || route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) { + nm_auto_nmpobj NMPObject *r_dev = NULL; + + r_dev = nmp_object_clone (r, FALSE); + route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev); + route->table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN); + route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r_dev, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we track such a route explicitly. Don't blacklist it. */ + } else { + if (!ip4_dev_route_blacklist) + ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (ip4_dev_route_blacklist, + g_steal_pointer (&r_dev)); } } + } - for (i = 0; i < count; i++) { - const NMPlatformIP4Route *route; +again: + nm_ip_config_iter_ip4_route_for_each (&iter, self, &my_route) { + NMPlatformIP4Route rt; - 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 (route_manager, device_route_purge_list); + if ( !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (my_route) + || my_route->gateway == 0 + || NM_IS_IP_CONFIG_SOURCE_RTPROT (my_route->rt_source) + || nm_ip4_config_get_direct_route_for_host (self, + my_route->gateway, + nm_platform_route_table_uncoerce (my_route->table_coerced, TRUE))) + continue; - success = nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, default_route_metric < 0, routes_full_sync); - g_array_unref (routes); - if (!success) - return FALSE; + rt = *my_route; + rt.network = my_route->gateway; + rt.plen = 32; + rt.gateway = 0; + _add_route (self, NULL, &rt, NULL); + /* adding the route might have invalidated the iteration. Start again. */ + goto again; } - return TRUE; + NM_SET_OUT (out_ip4_dev_route_blacklist, ip4_dev_route_blacklist); +} + +gboolean +nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + NMIPRouteTableSyncMode route_table_sync) +{ + gs_unref_ptrarray GPtrArray *addresses = NULL; + gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *routes_prune = NULL; + int ifindex; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + ifindex = nm_ip4_config_get_ifindex (self); + g_return_val_if_fail (ifindex > 0, FALSE); + + addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_addresses (self), + NULL, NULL); + + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_routes (self), + NULL, NULL); + + routes_prune = nm_platform_ip_route_get_prune_list (platform, + AF_INET, + ifindex, + route_table_sync); + + nm_platform_ip4_address_sync (platform, ifindex, addresses); + + if (!nm_platform_ip_route_sync (platform, + AF_INET, + ifindex, + routes, + routes_prune, + NULL)) + success = FALSE; + + return success; } static void -merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) +merge_route_attributes (NMIPRoute *s_route, + NMPlatformIP4Route *r, + guint32 route_table) { GVariant *variant; + guint32 u32; in_addr_t addr; #define GET_ATTR(name, field, variant_type, type) \ @@ -435,6 +820,12 @@ merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \ r->field = g_variant_get_ ## type (variant); + variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TABLE); + u32 = variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32) + ? g_variant_get_uint32 (variant) + : 0; + r->table_coerced = nm_platform_route_table_coerce (u32 ?: (route_table ?: RT_TABLE_MAIN)); + 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); @@ -457,20 +848,25 @@ merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) } void -nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric) +nm_ip4_config_merge_setting (NMIP4Config *self, + NMSettingIPConfig *setting, + guint32 route_table, + guint32 route_metric) { NMIP4ConfigPrivate *priv; guint naddresses, nroutes, nnameservers, nsearches; int i, priority; + const char *gateway_str; + guint32 gateway_bin; if (!setting) return; g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); - priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); - g_object_freeze_notify (G_OBJECT (config)); + g_object_freeze_notify (G_OBJECT (self)); naddresses = nm_setting_ip_config_get_num_addresses (setting); nroutes = nm_setting_ip_config_get_num_routes (setting); @@ -478,20 +874,20 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu 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; + if ( !nm_setting_ip_config_get_never_default (setting) + && (gateway_str = nm_setting_ip_config_get_gateway (setting)) + && inet_pton (AF_INET, gateway_str, &gateway_bin) == 1 + && gateway_bin) { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .gateway = gateway_bin, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; - inet_pton (AF_INET, nm_setting_ip_config_get_gateway (setting), &gateway); - nm_ip4_config_set_gateway (config, gateway); + _add_route (self, NULL, &r, NULL); } - 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); @@ -511,16 +907,21 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu if (label) g_strlcpy (address.label, g_variant_get_string (label, NULL), sizeof (address.label)); - nm_ip4_config_add_address (config, &address); + _add_address (self, NULL, &address); } /* Routes */ if (nm_setting_ip_config_get_ignore_auto_routes (setting)) - nm_ip4_config_reset_routes (config); + nm_ip4_config_reset_routes (self); for (i = 0; i < nroutes; i++) { NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i); NMPlatformIP4Route route; + if (nm_ip_route_get_family (s_route) != AF_INET) { + nm_assert_not_reached (); + continue; + } + memset (&route, 0, sizeof (route)); nm_ip_route_get_dest_binary (s_route, &route.network); @@ -531,73 +932,74 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu 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; + route.metric = 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); + route.network = nm_utils_ip4_address_clear_host_address (route.network, route.plen); + + merge_route_attributes (s_route, &route, route_table); + _add_route (self, NULL, &route, NULL); } /* 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); + nm_ip4_config_reset_nameservers (self); + nm_ip4_config_reset_domains (self); + nm_ip4_config_reset_searches (self); } 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); + nm_ip4_config_add_nameserver (self, ip); } for (i = 0; i < nsearches; i++) - nm_ip4_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i)); + nm_ip4_config_add_search (self, 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)); + nm_ip4_config_add_dns_option (self, 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); + nm_ip4_config_set_dns_priority (self, priority); - g_object_thaw_notify (G_OBJECT (config)); + g_object_thaw_notify (G_OBJECT (self)); } NMSetting * -nm_ip4_config_create_setting (const NMIP4Config *config) +nm_ip4_config_create_setting (const NMIP4Config *self) { + const NMIP4ConfigPrivate *priv; NMSettingIPConfig *s_ip4; - guint32 gateway; - guint naddresses, nroutes, nnameservers, nsearches, noptions; + guint nnameservers, nsearches, noptions; const char *method = NULL; int i; - gint64 route_metric; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - if (!config) { + if (!self) { 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); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nnameservers = nm_ip4_config_get_num_nameservers (self); + nsearches = nm_ip4_config_get_num_searches (self); + noptions = nm_ip4_config_get_num_dns_options (self); /* Addresses */ - for (i = 0; i < naddresses; i++) { - const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) { NMIPAddress *s_addr; /* Detect dynamic address */ @@ -619,10 +1021,12 @@ nm_ip4_config_create_setting (const NMIP4Config *config) } /* Gateway */ - if ( nm_ip4_config_has_gateway (config) + if ( priv->best_default_route && 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), + NM_SETTING_IP_CONFIG_GATEWAY, + nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway, + NULL), NULL); } @@ -632,16 +1036,13 @@ nm_ip4_config_create_setting (const NMIP4Config *config) 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); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { NMIPRoute *s_route; - /* Ignore default route. */ - if (!route->plen) + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; /* Ignore routes provided by external sources */ @@ -658,25 +1059,25 @@ nm_ip4_config_create_setting (const NMIP4Config *config) /* DNS */ for (i = 0; i < nnameservers; i++) { - guint32 nameserver = nm_ip4_config_get_nameserver (config, i); + guint32 nameserver = nm_ip4_config_get_nameserver (self, 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); + const char *search = nm_ip4_config_get_search (self, 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); + const char *option = nm_ip4_config_get_dns_option (self, 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), + nm_ip4_config_get_dns_priority (self), NULL); return NM_SETTING (s_ip4); @@ -685,11 +1086,16 @@ nm_ip4_config_create_setting (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags) +nm_ip4_config_merge (NMIP4Config *dst, + const NMIP4Config *src, + NMIPConfigMergeFlags merge_flags, + guint32 default_route_metric_penalty) { NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; guint32 i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address = NULL; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -700,8 +1106,8 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl 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)); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &address) + _add_address (dst, NMP_OBJECT_UP_CAST (address), NULL); /* nameservers */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { @@ -709,20 +1115,25 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl 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)); - } + const NMPlatformIP4Route *r_src; - 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); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r_src) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r_src)) { + if (NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES)) + continue; + if (default_route_metric_penalty) { + NMPlatformIP4Route r = *r_src; + + r.metric = nm_utils_ip_route_metric_penalize (AF_INET, r.metric, default_route_metric_penalty); + _add_route (dst, NULL, &r, NULL); + continue; + } + } + _add_route (dst, ipconf_iter.current->obj, NULL, NULL); + } + } /* domains */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { @@ -742,10 +1153,6 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl 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 @@ -782,21 +1189,6 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl /*****************************************************************************/ 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); @@ -812,22 +1204,6 @@ _nameservers_get_index (const NMIP4Config *self, guint32 ns) } 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); @@ -908,26 +1284,45 @@ _wins_get_index (const NMIP4Config *self, guint32 wins_server) * nm_ip4_config_subtract: * @dst: config from which to remove everything in @src * @src: config to remove from @dst + * @default_route_metric_penalty: pretend that on source we applied + * a route penalty on the default-route. It means, for default routes + * we don't remove routes that match exactly, but those with a lower + * metric (with the penalty removed). * * Removes everything in @src from @dst. */ void -nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) +nm_ip4_config_subtract (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty) { - guint32 i; + NMIP4ConfigPrivate *dst_priv; + guint i; gint idx; + const NMPlatformIP4Address *a; + const NMPlatformIP4Route *r; + NMDedupMultiIter ipconf_iter; + gboolean changed; + gboolean changed_default_route; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); + dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst); + 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); + changed = FALSE; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &a) { + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_addresses, + NMP_OBJECT_UP_CAST (a), + NULL)) + changed = TRUE; } + if (changed) + _notify_addresses (dst); /* nameservers */ for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++) { @@ -936,22 +1331,46 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) 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); + changed = FALSE; + changed_default_route = FALSE; + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) { + const NMPObject *o_src = NMP_OBJECT_UP_CAST (r); + NMPObject o_lookup_copy; + const NMPObject *o_lookup; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && default_route_metric_penalty) { + NMPlatformIP4Route *rr; + + /* the default route was penalized when merging it to the combined ip-config. + * When subtracting the routes, we must re-do that process when comparing + * the routes. */ + o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_src); + rr = NMP_OBJECT_CAST_IP4_ROUTE (&o_lookup_copy); + rr->metric = nm_utils_ip_route_metric_penalize (AF_INET, rr->metric, default_route_metric_penalty); + } else + o_lookup = o_src; + + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_routes, + o_lookup, + (gconstpointer *) &obj_old)) { + if (dst_priv->best_default_route == obj_old) { + nm_clear_nmp_object (&dst_priv->best_default_route); + changed_default_route = TRUE; + } + changed = TRUE; + } + } + if (changed_default_route) { + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, + _nm_ip4_config_best_default_route_find (dst)); + _notify (dst, PROP_GATEWAY); } + if (changed) + _notify_routes (dst); /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (src); i++) { @@ -974,10 +1393,6 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) 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)) @@ -1008,43 +1423,83 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) } void -nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) +nm_ip4_config_intersect (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty) { - guint32 i; - gint idx; + NMIP4ConfigPrivate *dst_priv; + const NMIP4ConfigPrivate *src_priv; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *a; + const NMPlatformIP4Route *r; + const NMPObject *new_best_default_route; + gboolean changed; - g_return_if_fail (src != NULL); - g_return_if_fail (dst != NULL); + g_return_if_fail (src); + g_return_if_fail (dst); + + 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 (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++; + changed = FALSE; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, dst, &a) { + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, + &src_priv->idx_ip4_addresses, + NMP_OBJECT_UP_CAST (a))) + continue; + + if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); + changed = TRUE; } + if (changed) + _notify_addresses (dst); - /* 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++; + changed = FALSE; + new_best_default_route = NULL; + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) { + const NMPObject *o_dst = NMP_OBJECT_UP_CAST (r); + const NMPObject *o_lookup; + NMPObject o_lookup_copy; + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && default_route_metric_penalty) { + NMPlatformIP4Route *rr; + + /* the default route was penalized when merging it to the combined ip-config. + * When intersecting the routes, we must re-do that process when comparing + * the routes. */ + o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_dst); + rr = NMP_OBJECT_CAST_IP4_ROUTE (&o_lookup_copy); + rr->metric = nm_utils_ip_route_metric_penalize (AF_INET, rr->metric, default_route_metric_penalty); + } else + o_lookup = o_dst; + + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, + &src_priv->idx_ip4_routes, + o_lookup)) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, o_dst); + continue; + } + + if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); + changed = TRUE; + } + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) { + nm_assert (changed); + _notify (dst, PROP_GATEWAY); } + if (changed) + _notify_routes (dst); /* ignore domains */ /* ignore dns searches */ @@ -1080,8 +1535,9 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev guint i, num; NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; - const NMPlatformIP4Address *dst_addr, *src_addr; - const NMPlatformIP4Route *dst_route, *src_route; + NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; + const NMDedupMultiHeadEntry *head_entry_src; + const NMPObject *new_best_default_route; g_return_val_if_fail (src != NULL, FALSE); g_return_val_if_fail (dst != NULL, FALSE); @@ -1102,72 +1558,106 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev 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; - } + head_entry_src = nm_ip4_config_lookup_addresses (src); + nm_dedup_multi_iter_init (&ipconf_iter_src, head_entry_src); + nm_ip_config_iter_ip4_address_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP4Address *r_src = NULL; + const NMPlatformIP4Address *r_dst = NULL; + + has = nm_ip_config_iter_ip4_address_next (&ipconf_iter_src, &r_src); + if (has != nm_ip_config_iter_ip4_address_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip4_address_cmp (r_src, r_dst) != 0) { + are_equal = FALSE; + if ( r_src->address != r_dst->address + || r_src->plen != r_dst->plen + || r_src->peer_address != r_dst->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; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_addresses); + nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_addresses_, + dst_priv->ifindex, + ipconf_iter_src.current->obj, + NULL, + FALSE, + TRUE, + NULL, + NULL); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_addresses, FALSE); + _notify_addresses (dst); } /* 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; - } + head_entry_src = nm_ip4_config_lookup_routes (src); + nm_dedup_multi_iter_init (&ipconf_iter_src, head_entry_src); + nm_ip_config_iter_ip4_route_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP4Route *r_src = NULL; + const NMPlatformIP4Route *r_dst = NULL; + + has = nm_ip_config_iter_ip4_route_next (&ipconf_iter_src, &r_src); + if (has != nm_ip_config_iter_ip4_route_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip4_route_cmp_full (r_src, r_dst) != 0) { + are_equal = FALSE; + if ( r_src->plen != r_dst->plen + || !nm_utils_ip4_address_same_prefix (r_src->network, r_dst->network, r_src->plen) + || r_src->gateway != r_dst->gateway + || r_src->metric != r_dst->metric) { + 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; + new_best_default_route = NULL; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes); + nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + const NMPObject *o = ipconf_iter_src.current->obj; + const NMPObject *obj_new; + + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip4_routes_, + dst_priv->ifindex, + o, + NULL, + FALSE, + TRUE, + NULL, + &obj_new); + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes, FALSE); + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + _notify (dst, PROP_GATEWAY); + _notify_routes (dst); } /* nameservers */ @@ -1251,12 +1741,6 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev 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); @@ -1327,386 +1811,344 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev } void -nm_ip4_config_dump (const NMIP4Config *config, const char *detail) +nm_ip4_config_dump (const NMIP4Config *self, const char *detail) { - guint32 i, tmp; + guint32 tmp; + guint i; const char *str; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; - g_message ("--------- NMIP4Config %p (%s)", config, detail); + g_message ("--------- NMIP4Config %p (%s)", self, detail); - if (config == NULL) { + if (self == NULL) { g_message (" (null)"); return; } - str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config)); + str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); 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)); - } + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) + g_message (" a: %s", nm_platform_ip4_address_to_string (address, NULL, 0)); /* nameservers */ - for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) { - tmp = nm_ip4_config_get_nameserver (config, i); + for (i = 0; i < nm_ip4_config_get_num_nameservers (self); i++) { + tmp = nm_ip4_config_get_nameserver (self, 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)); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) + g_message (" rt: %s", nm_platform_ip4_route_to_string (route, 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)); + for (i = 0; i < nm_ip4_config_get_num_domains (self); i++) + g_message (" domain: %s", nm_ip4_config_get_domain (self, 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)); + for (i = 0; i < nm_ip4_config_get_num_searches (self); i++) + g_message (" search: %s", nm_ip4_config_get_search (self, 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)); + for (i = 0; i < nm_ip4_config_get_num_dns_options (self); i++) + g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (self, i)); - g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (config)); + g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (self)); - 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)); + g_message (" mtu: %"G_GUINT32_FORMAT" (source: %d)", nm_ip4_config_get_mtu (self), (int) nm_ip4_config_get_mtu_source (self)); /* NIS */ - for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) { - tmp = nm_ip4_config_get_nis_server (config, i); + for (i = 0; i < nm_ip4_config_get_num_nis_servers (self); i++) { + tmp = nm_ip4_config_get_nis_server (self, i); g_message (" nis: %s", nm_utils_inet4_ntop (tmp, NULL)); } - g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (config) ?: "(none)"); + g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (self) ?: "(none)"); /* WINS */ - for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) { - tmp = nm_ip4_config_get_wins (config, i); + for (i = 0; i < nm_ip4_config_get_num_wins (self); i++) { + tmp = nm_ip4_config_get_wins (self, 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; + g_message (" mtrd: %d", (int) nm_ip4_config_get_metered (self)); } /*****************************************************************************/ void -nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default) +nm_ip4_config_reset_addresses (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - priv->never_default = never_default; + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip4_addresses) > 0) + _notify_addresses (self); } -gboolean -nm_ip4_config_get_never_default (const NMIP4Config *config) +static void +_add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - return priv->never_default; + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip4_addresses_, + priv->ifindex, + obj_new, + (const NMPlatformObject *) new, + TRUE, + FALSE, + NULL, + NULL)) + _notify_addresses (self); } +/** + * nm_ip4_config_add_address: + * @self: the #NMIP4Config + * @new: the new address to add to @self + * + * Adds the new address to @self. If an address with the same basic properties + * (address, prefix) already exists in @self, 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_set_gateway (NMIP4Config *config, guint32 gateway) +nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + g_return_if_fail (self); + g_return_if_fail (new); + g_return_if_fail (new->plen > 0 && new->plen <= 32); + g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex > 0); - if (priv->gateway != gateway || !priv->has_gateway) { - priv->gateway = gateway; - priv->has_gateway = TRUE; - _notify (config, PROP_GATEWAY); - } + _add_address (self, NULL, new); } void -nm_ip4_config_unset_gateway (NMIP4Config *config) +_nmtst_ip4_config_del_address (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMPlatformIP4Address *a; - if (priv->has_gateway) { - priv->gateway = 0; - priv->has_gateway = FALSE; - _notify (config, PROP_GATEWAY); - } + a = _nmtst_ip4_config_get_address (self, i); + if (!nm_ip4_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (a))) + g_assert_not_reached (); } -/** - * 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) +guint +nm_ip4_config_get_num_addresses (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->gateway; + head_entry = nm_ip4_config_lookup_addresses (self); + return head_entry ? head_entry->len : 0; } -gint64 -nm_ip4_config_get_route_metric (const NMIP4Config *config) +const NMPlatformIP4Address * +nm_ip4_config_get_first_address (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP4Address *a = NULL; - return priv->route_metric; + nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) + return a; + return NULL; } -/*****************************************************************************/ - -void -nm_ip4_config_reset_addresses (NMIP4Config *config) +const NMPlatformIP4Address * +_nmtst_ip4_config_get_address (const NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP4Address *a = NULL; + guint j; - if (priv->addresses->len != 0) { - g_array_set_size (priv->addresses, 0); - notify_addresses (config); + j = 0; + nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) { + if (i == j) + return a; + j++; } + g_return_val_if_reached (NULL); } -/** - * 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) +gboolean +nm_ip4_config_address_exists (const NMIP4Config *self, + const NMPlatformIP4Address *needle) { - 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; - } - } + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + NMPObject obj_stack; - g_array_append_val (priv->addresses, *new); -NOTIFY: - notify_addresses (config); + nmp_object_stackinit_id_ip4_address (&obj_stack, + priv->ifindex, + needle->address, + needle->plen, + needle->peer_address); + return !!nm_dedup_multi_index_lookup_obj (priv->multi_idx, + &priv->idx_ip4_addresses, + &obj_stack); } -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) +static const NMDedupMultiEntry * +_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv; - return priv->addresses->len; -} + nm_assert (NM_IS_IP4_CONFIG (self)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE); -const NMPlatformIP4Address * -nm_ip4_config_get_address (const NMIP4Config *config, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + priv = NM_IP4_CONFIG_GET_PRIVATE (self); - return &g_array_index (priv->addresses, NMPlatformIP4Address, i); + return _nm_ip_config_lookup_ip_route (priv->multi_idx, + &priv->idx_ip4_routes_, + needle, + cmp_type); } -gboolean -nm_ip4_config_address_exists (const NMIP4Config *config, - const NMPlatformIP4Address *needle) +void +nm_ip4_config_reset_routes (NMIP4Config *self) { - return _addresses_get_index (config, needle) >= 0; -} + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); -/*****************************************************************************/ + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip4_routes) > 0) { + if (nm_clear_nmp_object (&priv->best_default_route)) + _notify (self, PROP_GATEWAY); + _notify_routes (self); + } +} -void -nm_ip4_config_reset_routes (NMIP4Config *config) +static void +_add_route (NMIP4Config *self, + const NMPObject *obj_new, + const NMPlatformIP4Route *new, + const NMPObject **out_obj_new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_auto_nmpobj const NMPObject *obj_old = NULL; + const NMPObject *obj_new_2; + + nm_assert ((!new) != (!obj_new)); + nm_assert (!new || _route_valid (new)); + nm_assert (!obj_new || _route_valid (NMP_OBJECT_CAST_IP4_ROUTE (obj_new))); + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip4_routes_, + priv->ifindex, + obj_new, + (const NMPlatformObject *) new, + TRUE, + FALSE, + &obj_old, + &obj_new_2)) { + gboolean changed_default_route = FALSE; + + if ( priv->best_default_route == obj_old + && obj_old != obj_new_2) { + changed_default_route = TRUE; + nm_clear_nmp_object (&priv->best_default_route); + } + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); + if (_nm_ip_config_best_default_route_merge (&priv->best_default_route, obj_new_2)) + changed_default_route = TRUE; - if (priv->routes->len != 0) { - g_array_set_size (priv->routes, 0); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); - } + if (changed_default_route) + _notify (self, PROP_GATEWAY); + _notify_routes (self); + } else + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); } /** * nm_ip4_config_add_route: - * @config: the #NMIP4Config - * @new: the new route to add to @config + * @self: the #NMIP4Config + * @new: the new route to add to @self + * @out_obj_new: (allow-none): (out): the added route object. Must be unrefed + * by caller. * - * 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 + * Adds the new route to @self. If a route with the same basic properties + * (network, prefix) already exists in @self, 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) +nm_ip4_config_add_route (NMIP4Config *self, + const NMPlatformIP4Route *new, + const NMPObject **out_obj_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_return_if_fail (self); + g_return_if_fail (new); + g_return_if_fail (new->plen <= 32); + g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex > 0); - 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); + _add_route (self, NULL, new, out_obj_new); } void -nm_ip4_config_del_route (NMIP4Config *config, guint i) +_nmtst_ip4_config_del_route (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - - g_return_if_fail (i < priv->routes->len); + const NMPlatformIP4Route *r; - g_array_remove_index (priv->routes, i); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + r = _nmtst_ip4_config_get_route (self, i); + if (!nm_ip4_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (r))) + g_assert_not_reached (); } guint -nm_ip4_config_get_num_routes (const NMIP4Config *config) +nm_ip4_config_get_num_routes (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->routes->len; + head_entry = nm_ip4_config_lookup_routes (self); + nm_assert (!head_entry || head_entry->len == c_list_length (&head_entry->lst_entries_head)); + return head_entry ? head_entry->len : 0; } const NMPlatformIP4Route * -nm_ip4_config_get_route (const NMIP4Config *config, guint i) +_nmtst_ip4_config_get_route (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter iter; + const NMPlatformIP4Route *r = NULL; + guint j; - return &g_array_index (priv->routes, NMPlatformIP4Route, i); + j = 0; + nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) { + if (i == j) + return r; + j++; + } + g_return_val_if_reached (NULL); } const NMPlatformIP4Route * -nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host) +nm_ip4_config_get_direct_route_for_host (const NMIP4Config *self, + in_addr_t host, + guint32 route_table) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP4Route *best_route = NULL; + const NMPlatformIP4Route *best_route = NULL; + const NMPlatformIP4Route *item; + NMDedupMultiIter ipconf_iter; g_return_val_if_fail (host, NULL); - for (i = 0; i < priv->routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); - + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &item) { if (item->gateway != 0) continue; if (best_route && best_route->plen > item->plen) continue; + if (nm_platform_route_table_uncoerce (item->table_coerced, TRUE) != route_table) + continue; + if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->network, item->plen)) continue; @@ -1715,27 +2157,26 @@ nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host best_route = item; } - return best_route; } /*****************************************************************************/ void -nm_ip4_config_reset_nameservers (NMIP4Config *config) +nm_ip4_config_reset_nameservers (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->nameservers->len != 0) { g_array_set_size (priv->nameservers, 0); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } } void -nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 new) +nm_ip4_config_add_nameserver (NMIP4Config *self, guint32 new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (new != 0); @@ -1745,32 +2186,32 @@ nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 new) return; g_array_append_val (priv->nameservers, new); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } void -nm_ip4_config_del_nameserver (NMIP4Config *config, guint i) +nm_ip4_config_del_nameserver (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->nameservers->len); g_array_remove_index (priv->nameservers, i); - _notify (config, PROP_NAMESERVERS); + _notify (self, PROP_NAMESERVERS); } guint -nm_ip4_config_get_num_nameservers (const NMIP4Config *config) +nm_ip4_config_get_num_nameservers (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->nameservers->len; } guint32 -nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i) +nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_array_index (priv->nameservers, guint32, i); } @@ -1778,20 +2219,20 @@ nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_reset_domains (NMIP4Config *config) +nm_ip4_config_reset_domains (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->domains->len != 0) { g_ptr_array_set_size (priv->domains, 0); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } } void -nm_ip4_config_add_domain (NMIP4Config *config, const char *domain) +nm_ip4_config_add_domain (NMIP4Config *self, const char *domain) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (domain != NULL); @@ -1802,32 +2243,32 @@ nm_ip4_config_add_domain (NMIP4Config *config, const char *domain) return; g_ptr_array_add (priv->domains, g_strdup (domain)); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } void -nm_ip4_config_del_domain (NMIP4Config *config, guint i) +nm_ip4_config_del_domain (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->domains->len); g_ptr_array_remove_index (priv->domains, i); - _notify (config, PROP_DOMAINS); + _notify (self, PROP_DOMAINS); } guint -nm_ip4_config_get_num_domains (const NMIP4Config *config) +nm_ip4_config_get_num_domains (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->domains->len; } const char * -nm_ip4_config_get_domain (const NMIP4Config *config, guint i) +nm_ip4_config_get_domain (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->domains, i); } @@ -1835,20 +2276,20 @@ nm_ip4_config_get_domain (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_reset_searches (NMIP4Config *config) +nm_ip4_config_reset_searches (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->searches->len != 0) { g_ptr_array_set_size (priv->searches, 0); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } } void -nm_ip4_config_add_search (NMIP4Config *config, const char *new) +nm_ip4_config_add_search (NMIP4Config *self, const char *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); char *search; size_t len; @@ -1874,32 +2315,32 @@ nm_ip4_config_add_search (NMIP4Config *config, const char *new) } g_ptr_array_add (priv->searches, search); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } void -nm_ip4_config_del_search (NMIP4Config *config, guint i) +nm_ip4_config_del_search (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->searches->len); g_ptr_array_remove_index (priv->searches, i); - _notify (config, PROP_SEARCHES); + _notify (self, PROP_SEARCHES); } guint -nm_ip4_config_get_num_searches (const NMIP4Config *config) +nm_ip4_config_get_num_searches (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->searches->len; } const char * -nm_ip4_config_get_search (const NMIP4Config *config, guint i) +nm_ip4_config_get_search (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->searches, i); } @@ -1907,20 +2348,20 @@ nm_ip4_config_get_search (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_reset_dns_options (NMIP4Config *config) +nm_ip4_config_reset_dns_options (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->dns_options->len != 0) { g_ptr_array_set_size (priv->dns_options, 0); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } } void -nm_ip4_config_add_dns_option (NMIP4Config *config, const char *new) +nm_ip4_config_add_dns_option (NMIP4Config *self, const char *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (new != NULL); @@ -1931,32 +2372,32 @@ nm_ip4_config_add_dns_option (NMIP4Config *config, const char *new) return; g_ptr_array_add (priv->dns_options, g_strdup (new)); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } void -nm_ip4_config_del_dns_option(NMIP4Config *config, guint i) +nm_ip4_config_del_dns_option(NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->dns_options->len); g_ptr_array_remove_index (priv->dns_options, i); - _notify (config, PROP_DNS_OPTIONS); + _notify (self, PROP_DNS_OPTIONS); } guint -nm_ip4_config_get_num_dns_options (const NMIP4Config *config) +nm_ip4_config_get_num_dns_options (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->dns_options->len; } const char * -nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i) +nm_ip4_config_get_dns_option (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_ptr_array_index (priv->dns_options, i); } @@ -1964,20 +2405,20 @@ nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_set_dns_priority (NMIP4Config *config, gint priority) +nm_ip4_config_set_dns_priority (NMIP4Config *self, gint priority) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priority != priv->dns_priority) { priv->dns_priority = priority; - _notify (config, PROP_DNS_PRIORITY); + _notify (self, PROP_DNS_PRIORITY); } } gint -nm_ip4_config_get_dns_priority (const NMIP4Config *config) +nm_ip4_config_get_dns_priority (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->dns_priority; } @@ -1985,35 +2426,17 @@ nm_ip4_config_get_dns_priority (const NMIP4Config *config) /*****************************************************************************/ 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) +nm_ip4_config_reset_nis_servers (NMIP4Config *self) { - 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); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_array_set_size (priv->nis, 0); } void -nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis) +nm_ip4_config_add_nis_server (NMIP4Config *self, guint32 nis) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; for (i = 0; i < priv->nis->len; i++) @@ -2024,9 +2447,9 @@ nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis) } void -nm_ip4_config_del_nis_server (NMIP4Config *config, guint i) +nm_ip4_config_del_nis_server (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->nis->len); @@ -2034,34 +2457,34 @@ nm_ip4_config_del_nis_server (NMIP4Config *config, guint i) } guint -nm_ip4_config_get_num_nis_servers (const NMIP4Config *config) +nm_ip4_config_get_num_nis_servers (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->nis->len; } guint32 -nm_ip4_config_get_nis_server (const NMIP4Config *config, guint i) +nm_ip4_config_get_nis_server (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_array_index (priv->nis, guint32, i); } void -nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain) +nm_ip4_config_set_nis_domain (NMIP4Config *self, const char *domain) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_free (priv->nis_domain); priv->nis_domain = g_strdup (domain); } const char * -nm_ip4_config_get_nis_domain (const NMIP4Config *config) +nm_ip4_config_get_nis_domain (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->nis_domain; } @@ -2069,20 +2492,20 @@ nm_ip4_config_get_nis_domain (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_reset_wins (NMIP4Config *config) +nm_ip4_config_reset_wins (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (priv->wins->len != 0) { g_array_set_size (priv->wins, 0); - _notify (config, PROP_WINS_SERVERS); + _notify (self, PROP_WINS_SERVERS); } } void -nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins) +nm_ip4_config_add_wins (NMIP4Config *self, guint32 wins) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); int i; g_return_if_fail (wins != 0); @@ -2092,32 +2515,32 @@ nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins) return; g_array_append_val (priv->wins, wins); - _notify (config, PROP_WINS_SERVERS); + _notify (self, PROP_WINS_SERVERS); } void -nm_ip4_config_del_wins (NMIP4Config *config, guint i) +nm_ip4_config_del_wins (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); g_return_if_fail (i < priv->wins->len); g_array_remove_index (priv->wins, i); - _notify (config, PROP_WINS_SERVERS); + _notify (self, PROP_WINS_SERVERS); } guint -nm_ip4_config_get_num_wins (const NMIP4Config *config) +nm_ip4_config_get_num_wins (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->wins->len; } guint32 -nm_ip4_config_get_wins (const NMIP4Config *config, guint i) +nm_ip4_config_get_wins (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return g_array_index (priv->wins, guint32, i); } @@ -2125,9 +2548,9 @@ nm_ip4_config_get_wins (const NMIP4Config *config, guint i) /*****************************************************************************/ void -nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source) +nm_ip4_config_set_mtu (NMIP4Config *self, guint32 mtu, NMIPConfigSource source) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (!mtu) source = NM_IP_CONFIG_SOURCE_UNKNOWN; @@ -2137,17 +2560,17 @@ nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source } guint32 -nm_ip4_config_get_mtu (const NMIP4Config *config) +nm_ip4_config_get_mtu (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->mtu; } NMIPConfigSource -nm_ip4_config_get_mtu_source (const NMIP4Config *config) +nm_ip4_config_get_mtu_source (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->mtu_source; } @@ -2155,23 +2578,102 @@ nm_ip4_config_get_mtu_source (const NMIP4Config *config) /*****************************************************************************/ void -nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered) +nm_ip4_config_set_metered (NMIP4Config *self, gboolean metered) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); priv->metered = metered; } gboolean -nm_ip4_config_get_metered (const NMIP4Config *config) +nm_ip4_config_get_metered (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); return priv->metered; } /*****************************************************************************/ +const NMPObject * +nm_ip4_config_nmpobj_lookup (const NMIP4Config *self, const NMPObject *needle) +{ + const NMIP4ConfigPrivate *priv; + const NMDedupMultiIdxType *idx_type; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + idx_type = &priv->idx_ip4_addresses; + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + idx_type = &priv->idx_ip4_routes; + break; + default: + g_return_val_if_reached (NULL); + } + + return nm_dedup_multi_entry_get_obj (nm_dedup_multi_index_lookup_obj (priv->multi_idx, + idx_type, + needle)); +} + +gboolean +nm_ip4_config_nmpobj_remove (NMIP4Config *self, + const NMPObject *needle) +{ + NMIP4ConfigPrivate *priv; + NMDedupMultiIdxType *idx_type; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + guint n; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + idx_type = &priv->idx_ip4_addresses; + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + idx_type = &priv->idx_ip4_routes; + break; + default: + g_return_val_if_reached (FALSE); + } + + n = nm_dedup_multi_index_remove_obj (priv->multi_idx, + idx_type, + needle, + (gconstpointer *) &obj_old); + if (n != 1) { + nm_assert (n == 0); + return FALSE; + } + + nm_assert (NMP_OBJECT_GET_TYPE (obj_old) == NMP_OBJECT_GET_TYPE (needle)); + + switch (NMP_OBJECT_GET_TYPE (obj_old)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + _notify_addresses (self); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + if (priv->best_default_route == obj_old) { + if (_nm_ip_config_best_default_route_set (&priv->best_default_route, + _nm_ip4_config_best_default_route_find (self))) + _notify (self, PROP_GATEWAY); + } + _notify_routes (self); + break; + default: + nm_assert_not_reached (); + } + return TRUE; +} + +/*****************************************************************************/ + static inline void hash_u32 (GChecksum *sum, guint32 n) { @@ -2179,60 +2681,57 @@ hash_u32 (GChecksum *sum, guint32 n) } void -nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only) +nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only) { guint i; const char *s; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; - g_return_if_fail (config); + g_return_if_fail (self); 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); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) { hash_u32 (sum, address->address); hash_u32 (sum, address->plen); - hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (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); - + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { 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)); + for (i = 0; i < nm_ip4_config_get_num_nis_servers (self); i++) + hash_u32 (sum, nm_ip4_config_get_nis_server (self, i)); - s = nm_ip4_config_get_nis_domain (config); + s = nm_ip4_config_get_nis_domain (self); 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_nameservers (self); i++) + hash_u32 (sum, nm_ip4_config_get_nameserver (self, 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_wins (self); i++) + hash_u32 (sum, nm_ip4_config_get_wins (self, i)); - for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) { - s = nm_ip4_config_get_domain (config, i); + for (i = 0; i < nm_ip4_config_get_num_domains (self); i++) { + s = nm_ip4_config_get_domain (self, 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); + for (i = 0; i < nm_ip4_config_get_num_searches (self); i++) { + s = nm_ip4_config_get_search (self, 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); + for (i = 0; i < nm_ip4_config_get_num_dns_options (self); i++) { + s = nm_ip4_config_get_dns_option (self, i); g_checksum_update (sum, (const guint8 *) s, strlen (s)); } } @@ -2283,8 +2782,12 @@ 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); + NMIP4Config *self = NM_IP4_CONFIG (object); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; + GVariantBuilder builder_data, builder_legacy; switch (prop_id) { case PROP_IFINDEX: @@ -2292,25 +2795,32 @@ get_property (GObject *object, guint prop_id, break; case PROP_ADDRESS_DATA: case PROP_ADDRESSES: - { - GVariantBuilder array_builder, addr_builder; - gs_unref_array GArray *new = NULL; - guint naddr, i; + nm_assert (!!priv->address_data_variant == !!priv->addresses_variant); - g_return_if_fail (!!priv->address_data_variant == !!priv->addresses_variant); + if (priv->address_data_variant) + goto out_addresses_cached; - if (priv->address_data_variant) - goto return_cached; + g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("aau")); - 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); + head_entry = nm_ip4_config_lookup_addresses (self); + if (head_entry) { + gs_free const NMPObject **addresses = NULL; + guint naddr, i; + + addresses = (const NMPObject **) nm_dedup_multi_objs_to_array_head (head_entry, NULL, NULL, &naddr); + nm_assert (addresses && naddr); + + g_qsort_with_data (addresses, + naddr, + sizeof (addresses[0]), + _addresses_sort_cmp, + NULL); /* 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); + GVariantBuilder addr_builder; + const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (addresses[i]); g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&addr_builder, "{sv}", @@ -2331,98 +2841,105 @@ get_property (GObject *object, guint prop_id, g_variant_new_string (address->label)); } - g_variant_builder_add (&array_builder, "a{sv}", &addr_builder); + g_variant_builder_add (&builder_data, "a{sv}", &addr_builder); + + { + const guint32 dbus_addr[3] = { + address->address, + address->plen, + ( i == 0 + && priv->best_default_route) + ? NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway + : (guint32) 0, + }; + + g_variant_builder_add (&builder_legacy, "@au", + g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, + dbus_addr, 3, sizeof (guint32))); + } } - 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; + priv->address_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data)); + priv->addresses_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy)); - 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); - } +out_addresses_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; + case PROP_ROUTES: + nm_assert (!!priv->route_data_variant == !!priv->routes_variant); - 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); + if (priv->route_data_variant) + goto out_routes_cached; - 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_init (&builder_data, G_VARIANT_TYPE ("aa{sv}")); + g_variant_builder_init (&builder_legacy, G_VARIANT_TYPE ("aau")); + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { + GVariantBuilder route_builder; + + nm_assert (_route_valid (route)); + + 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}", - "metric", - g_variant_new_uint32 (route->metric)); + "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); + if (!nm_platform_route_table_is_main (route->table_coerced)) { + g_variant_builder_add (&route_builder, "{sv}", + "table", + g_variant_new_uint32 (nm_platform_route_table_uncoerce (route->table_coerced, TRUE))); } - 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; + g_variant_builder_add (&builder_data, "a{sv}", &route_builder); - dbus_route[0] = route->network; - dbus_route[1] = route->plen; - dbus_route[2] = route->gateway; - dbus_route[3] = route->metric; + /* 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_route_table_is_main (route->table_coerced) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { + const guint32 dbus_route[4] = { + route->network, + route->plen, + route->gateway, + route->metric, + }; - g_variant_builder_add (&array_builder, "@au", + g_variant_builder_add (&builder_legacy, "@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)); } + + priv->route_data_variant = g_variant_ref_sink (g_variant_builder_end (&builder_data)); + priv->routes_variant = g_variant_ref_sink (g_variant_builder_end (&builder_legacy)); + +out_routes_cached: + g_value_set_variant (value, + prop_id == PROP_ROUTE_DATA ? + priv->route_data_variant : + priv->routes_variant); break; case PROP_GATEWAY: - if (priv->has_gateway) - g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL)); - else + if (priv->best_default_route) { + g_value_set_string (value, + nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway, + NULL)); + } else g_value_set_string (value, NULL); break; case PROP_NAMESERVERS: @@ -2467,6 +2984,13 @@ set_property (GObject *object, NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); @@ -2480,26 +3004,29 @@ set_property (GObject *object, /*****************************************************************************/ static void -nm_ip4_config_init (NMIP4Config *config) +nm_ip4_config_init (NMIP4Config *self) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_addresses, + NMP_OBJECT_TYPE_IP4_ADDRESS); + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_routes, + NMP_OBJECT_TYPE_IP4_ROUTE); - 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) +nm_ip4_config_new (NMDedupMultiIndex *multi_idx, int ifindex) { g_return_val_if_fail (ifindex >= -1, NULL); return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG, + NM_IP4_CONFIG_MULTI_IDX, multi_idx, NM_IP4_CONFIG_IFINDEX, ifindex, NULL); } @@ -2510,10 +3037,16 @@ finalize (GObject *object) NMIP4Config *self = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_clear_nmp_object (&priv->best_default_route); + + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_addresses); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_routes); + 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); + nm_clear_g_variant (&priv->route_data_variant); + nm_clear_g_variant (&priv->routes_variant); + g_array_unref (priv->nameservers); g_ptr_array_unref (priv->domains); g_ptr_array_unref (priv->searches); @@ -2523,6 +3056,8 @@ finalize (GObject *object) g_array_unref (priv->wins); G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object); + + nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -2537,6 +3072,11 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class) object_class->set_property = set_property; object_class->finalize = finalize; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_IP4_CONFIG_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); obj_properties[PROP_IFINDEX] = g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "", -1, G_MAXINT, -1, |