device: renew dhcp leases on awake for software devices
[NetworkManager.git] / src / nm-ip4-config.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright (C) 2005 - 2014 Red Hat, Inc.
19  * Copyright (C) 2006 - 2008 Novell, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include "nm-ip4-config.h"
25
26 #include <string.h>
27 #include <arpa/inet.h>
28
29 #include "nm-utils.h"
30 #include "nm-platform.h"
31 #include "nm-platform-utils.h"
32 #include "NetworkManagerUtils.h"
33 #include "nm-route-manager.h"
34 #include "nm-core-internal.h"
35
36 #include "nmdbus-ip4-config.h"
37
38 G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT)
39
40 #define NM_IP4_CONFIG_GET_PRIVATE(o) ((o)->priv)
41
42 typedef struct _NMIP4ConfigPrivate {
43         gboolean never_default;
44         guint32 gateway;
45         gboolean has_gateway;
46         GArray *addresses;
47         GArray *routes;
48         GArray *nameservers;
49         GPtrArray *domains;
50         GPtrArray *searches;
51         GPtrArray *dns_options;
52         guint32 mss;
53         GArray *nis;
54         char *nis_domain;
55         GArray *wins;
56         guint32 mtu;
57         NMIPConfigSource mtu_source;
58         int ifindex;
59         gint64 route_metric;
60         gboolean metered;
61 } NMIP4ConfigPrivate;
62
63 /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */
64 G_STATIC_ASSERT (sizeof (uint) >= sizeof (guint32));
65 G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF);
66
67 NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config,
68         PROP_IFINDEX,
69         PROP_ADDRESS_DATA,
70         PROP_ADDRESSES,
71         PROP_ROUTE_DATA,
72         PROP_ROUTES,
73         PROP_GATEWAY,
74         PROP_NAMESERVERS,
75         PROP_DOMAINS,
76         PROP_SEARCHES,
77         PROP_DNS_OPTIONS,
78         PROP_WINS_SERVERS,
79 );
80
81 NMIP4Config *
82 nm_ip4_config_new (int ifindex)
83 {
84         g_return_val_if_fail (ifindex >= -1, NULL);
85         return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG,
86                                              NM_IP4_CONFIG_IFINDEX, ifindex,
87                                              NULL);
88 }
89
90 int
91 nm_ip4_config_get_ifindex (const NMIP4Config *config)
92 {
93         return NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex;
94 }
95
96 /******************************************************************/
97
98 static gboolean
99 _ipv4_is_zeronet (in_addr_t network)
100 {
101         /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */
102         return (network & htonl(0xff000000)) == htonl(0x00000000);
103 }
104
105 /******************************************************************/
106
107 /**
108  * nm_ip4_config_capture_resolv_conf():
109  * @nameservers: array of guint32
110  * @rc_contents: the contents of a resolv.conf or %NULL to read /etc/resolv.conf
111  *
112  * Reads all resolv.conf IPv4 nameservers and adds them to @nameservers.
113  *
114  * Returns: %TRUE if nameservers were added, %FALSE if @nameservers is unchanged
115  */
116 gboolean
117 nm_ip4_config_capture_resolv_conf (GArray *nameservers,
118                                    GPtrArray *dns_options,
119                                    const char *rc_contents)
120 {
121         GPtrArray *read_ns, *read_options;
122         guint i, j;
123         gboolean changed = FALSE;
124
125         g_return_val_if_fail (nameservers != NULL, FALSE);
126
127         read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents);
128         if (!read_ns)
129                 return FALSE;
130
131         for (i = 0; i < read_ns->len; i++) {
132                 const char *s = g_ptr_array_index (read_ns, i);
133                 guint32 ns = 0;
134
135                 if (!inet_pton (AF_INET, s, (void *) &ns) || !ns)
136                         continue;
137
138                 /* Ignore duplicates */
139                 for (j = 0; j < nameservers->len; j++) {
140                         if (g_array_index (nameservers, guint32, j) == ns)
141                                 break;
142                 }
143
144                 if (j == nameservers->len) {
145                         g_array_append_val (nameservers, ns);
146                         changed = TRUE;
147                 }
148         }
149         g_ptr_array_unref (read_ns);
150
151         if (dns_options) {
152                 read_options = nm_utils_read_resolv_conf_dns_options (rc_contents);
153                 if (!read_options)
154                         return changed;
155
156                 for (i = 0; i < read_options->len; i++) {
157                         const char *s = g_ptr_array_index (read_options, i);
158
159                         if (_nm_utils_dns_option_validate (s, NULL, NULL, FALSE, _nm_utils_dns_option_descs) &&
160                                 _nm_utils_dns_option_find_idx (dns_options, s) < 0) {
161                                 g_ptr_array_add (dns_options, g_strdup (s));
162                                 changed = TRUE;
163                         }
164                 }
165                 g_ptr_array_unref (read_options);
166         }
167
168         return changed;
169 }
170
171 static gboolean
172 addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
173 {
174         return    a->address == b->address
175                && a->plen == b->plen
176                && ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0;
177 }
178
179 static gboolean
180 routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_gateway_and_metric)
181 {
182         return a->network == b->network && a->plen == b->plen &&
183                (!consider_gateway_and_metric || (a->gateway == b->gateway && a->metric == b->metric));
184 }
185
186 /*****************************************************************************/
187
188 static gint
189 _addresses_sort_cmp_get_prio (in_addr_t addr)
190 {
191         if (nm_utils_ip4_address_is_link_local (addr))
192                 return 0;
193         return 1;
194 }
195
196 static gint
197 _addresses_sort_cmp (gconstpointer a, gconstpointer b)
198 {
199         gint p1, p2, c;
200         const NMPlatformIP4Address *a1 = a, *a2 = b;
201
202         /* Sort by address type. For example link local will
203          * be sorted *after* a global address. */
204         p1 = _addresses_sort_cmp_get_prio (a1->address);
205         p2 = _addresses_sort_cmp_get_prio (a2->address);
206         if (p1 != p2)
207                 return p1 > p2 ? -1 : 1;
208
209         /* Sort the addresses based on their source. */
210         if (a1->source != a2->source)
211                 return a1->source > a2->source ? -1 : 1;
212
213         if ((a1->label[0] == '\0') != (a2->label[0] == '\0'))
214                 return (a1->label[0] == '\0') ? -1 : 1;
215
216         /* finally sort addresses lexically */
217         c = memcmp (&a1->address, &a2->address, sizeof (a2->address));
218         return c != 0 ? c : memcmp (a1, a2, sizeof (*a1));
219 }
220
221 gboolean
222 nm_ip4_config_addresses_sort (NMIP4Config *self)
223 {
224         NMIP4ConfigPrivate *priv;
225         size_t data_len = 0;
226         char *data_pre = NULL;
227         gboolean changed;
228
229         g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE);
230
231         priv = NM_IP4_CONFIG_GET_PRIVATE (self);
232         if (priv->addresses->len > 1) {
233                 data_len = priv->addresses->len * g_array_get_element_size (priv->addresses);
234                 data_pre = g_new (char, data_len);
235                 memcpy (data_pre, priv->addresses->data, data_len);
236
237                 g_array_sort (priv->addresses, _addresses_sort_cmp);
238
239                 changed = memcmp (data_pre, priv->addresses->data, data_len) != 0;
240                 g_free (data_pre);
241
242                 if (changed) {
243                         _notify (self, PROP_ADDRESS_DATA);
244                         _notify (self, PROP_ADDRESSES);
245                         return TRUE;
246                 }
247         }
248         return FALSE;
249 }
250
251 /*****************************************************************************/
252
253 NMIP4Config *
254 nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
255 {
256         NMIP4Config *config;
257         NMIP4ConfigPrivate *priv;
258         guint i;
259         guint32 lowest_metric = G_MAXUINT32;
260         guint32 old_gateway = 0;
261         gboolean old_has_gateway = FALSE;
262
263         /* Slaves have no IP configuration */
264         if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
265                 return NULL;
266
267         config = nm_ip4_config_new (ifindex);
268         priv = NM_IP4_CONFIG_GET_PRIVATE (config);
269
270         g_array_unref (priv->addresses);
271         g_array_unref (priv->routes);
272
273         priv->addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
274         priv->routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
275
276         /* Extract gateway from default route */
277         old_gateway = priv->gateway;
278         old_has_gateway = priv->has_gateway;
279         for (i = 0; i < priv->routes->len; ) {
280                 const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
281
282                 if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
283                         if (route->metric < lowest_metric) {
284                                 priv->gateway = route->gateway;
285                                 lowest_metric = route->metric;
286                         }
287                         priv->has_gateway = TRUE;
288                         /* Remove the default route from the list */
289                         g_array_remove_index_fast (priv->routes, i);
290                         continue;
291                 }
292                 i++;
293         }
294
295         /* we detect the route metric based on the default route. All non-default
296          * routes have their route metrics explicitly set. */
297         priv->route_metric = priv->has_gateway ? (gint64) lowest_metric : (gint64) -1;
298
299         /* If there is a host route to the gateway, ignore that route.  It is
300          * automatically added by NetworkManager when needed.
301          */
302         if (priv->has_gateway) {
303                 for (i = 0; i < priv->routes->len; i++) {
304                         const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
305
306                         if (   (route->plen == 32)
307                             && (route->network == priv->gateway)
308                             && (route->gateway == 0)) {
309                                 g_array_remove_index (priv->routes, i);
310                                 i--;
311                         }
312                 }
313         }
314
315         /* If the interface has the default route, and has IPv4 addresses, capture
316          * nameservers from /etc/resolv.conf.
317          */
318         if (priv->addresses->len && priv->has_gateway && capture_resolv_conf) {
319                 if (nm_ip4_config_capture_resolv_conf (priv->nameservers, priv->dns_options, NULL))
320                         _notify (config, PROP_NAMESERVERS);
321         }
322
323         /* actually, nobody should be connected to the signal, just to be sure, notify */
324         _notify (config, PROP_ADDRESS_DATA);
325         _notify (config, PROP_ROUTE_DATA);
326         _notify (config, PROP_ADDRESSES);
327         _notify (config, PROP_ROUTES);
328         if (   priv->gateway != old_gateway
329             || priv->has_gateway != old_has_gateway)
330                 _notify (config, PROP_GATEWAY);
331
332         return config;
333 }
334
335 gboolean
336 nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric)
337 {
338         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
339         int i;
340         gs_unref_ptrarray GPtrArray *added_addresses = NULL;
341
342         g_return_val_if_fail (ifindex > 0, FALSE);
343         g_return_val_if_fail (config != NULL, FALSE);
344
345         /* Addresses */
346         nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses,
347                                       default_route_metric >= 0 ? &added_addresses : NULL);
348
349         /* Routes */
350         {
351                 int count = nm_ip4_config_get_num_routes (config);
352                 GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count);
353                 gboolean success;
354                 gs_unref_array GArray *device_route_purge_list = NULL;
355
356                 if (   default_route_metric >= 0
357                     && added_addresses) {
358                         /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config.
359                          * As we don't do that for IPv4, add it here shortly before syncing
360                          * the routes. For NMRouteManager these routes are very much important. */
361                         for (i = 0; i < added_addresses->len; i++) {
362                                 const NMPlatformIP4Address *addr = added_addresses->pdata[i];
363                                 NMPlatformIP4Route route = { 0 };
364
365                                 if (addr->plen == 0)
366                                         continue;
367
368                                 route.ifindex = ifindex;
369                                 route.source = NM_IP_CONFIG_SOURCE_KERNEL;
370
371                                 /* The destination network depends on the peer-address. */
372                                 route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen);
373
374                                 if (_ipv4_is_zeronet (route.network)) {
375                                         /* Kernel doesn't add device-routes for destinations that
376                                          * start with 0.x.y.z. Skip them. */
377                                         continue;
378                                 }
379
380                                 route.plen = addr->plen;
381                                 route.pref_src = addr->address;
382                                 route.metric = default_route_metric;
383
384                                 g_array_append_val (routes, route);
385
386                                 if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) {
387                                         if (!device_route_purge_list)
388                                                 device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
389                                         route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE;
390                                         g_array_append_val (device_route_purge_list, route);
391                                 }
392                         }
393                 }
394
395                 for (i = 0; i < count; i++) {
396                         const NMPlatformIP4Route *route;
397
398                         route = nm_ip4_config_get_route (config, i);
399
400                         /* Don't add the route if it's more specific than one of the subnets
401                          * the device already has an IP address on.
402                          */
403                         if (   route->gateway == 0
404                             && nm_ip4_config_destination_is_direct (config, route->network, route->plen))
405                                 continue;
406
407                         /* duplicates in @routes are no problem as route-manager handles them
408                          * gracefully (by ignoring them). */
409                         g_array_append_vals (routes, route, 1);
410                 }
411
412                 nm_route_manager_ip4_route_register_device_route_purge_list (nm_route_manager_get (), device_route_purge_list);
413
414                 success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, default_route_metric < 0, routes_full_sync);
415                 g_array_unref (routes);
416                 if (!success)
417                         return FALSE;
418         }
419
420         return TRUE;
421 }
422
423 void
424 nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
425 {
426         NMIP4ConfigPrivate *priv;
427         guint naddresses, nroutes, nnameservers, nsearches;
428         int i;
429
430         if (!setting)
431                 return;
432
433         g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
434
435         priv = NM_IP4_CONFIG_GET_PRIVATE (config);
436
437         g_object_freeze_notify (G_OBJECT (config));
438
439         naddresses = nm_setting_ip_config_get_num_addresses (setting);
440         nroutes = nm_setting_ip_config_get_num_routes (setting);
441         nnameservers = nm_setting_ip_config_get_num_dns (setting);
442         nsearches = nm_setting_ip_config_get_num_dns_searches (setting);
443
444         /* Gateway */
445         if (nm_setting_ip_config_get_never_default (setting))
446                 nm_ip4_config_set_never_default (config, TRUE);
447         else if (nm_setting_ip_config_get_ignore_auto_routes (setting))
448                 nm_ip4_config_set_never_default (config, FALSE);
449         if (nm_setting_ip_config_get_gateway (setting)) {
450                 guint32 gateway;
451
452                 inet_pton (AF_INET, nm_setting_ip_config_get_gateway (setting), &gateway);
453                 nm_ip4_config_set_gateway (config, gateway);
454         }
455
456         if (priv->route_metric  == -1)
457                 priv->route_metric = nm_setting_ip_config_get_route_metric (setting);
458
459         /* Addresses */
460         for (i = 0; i < naddresses; i++) {
461                 NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
462                 GVariant *label;
463                 NMPlatformIP4Address address;
464
465                 memset (&address, 0, sizeof (address));
466                 nm_ip_address_get_address_binary (s_addr, &address.address);
467                 address.peer_address = address.address;
468                 address.plen = nm_ip_address_get_prefix (s_addr);
469                 address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
470                 address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
471                 address.source = NM_IP_CONFIG_SOURCE_USER;
472
473                 label = nm_ip_address_get_attribute (s_addr, "label");
474                 if (label)
475                         g_strlcpy (address.label, g_variant_get_string (label, NULL), sizeof (address.label));
476
477                 nm_ip4_config_add_address (config, &address);
478         }
479
480         /* Routes */
481         if (nm_setting_ip_config_get_ignore_auto_routes (setting))
482                 nm_ip4_config_reset_routes (config);
483         for (i = 0; i < nroutes; i++) {
484                 NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
485                 NMPlatformIP4Route route;
486
487                 memset (&route, 0, sizeof (route));
488                 nm_ip_route_get_dest_binary (s_route, &route.network);
489                 route.plen = nm_ip_route_get_prefix (s_route);
490                 nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
491                 if (nm_ip_route_get_metric (s_route) == -1)
492                         route.metric = default_route_metric;
493                 else
494                         route.metric = nm_ip_route_get_metric (s_route);
495                 route.source = NM_IP_CONFIG_SOURCE_USER;
496
497                 g_assert (route.plen > 0);
498
499                 nm_ip4_config_add_route (config, &route);
500         }
501
502         /* DNS */
503         if (nm_setting_ip_config_get_ignore_auto_dns (setting)) {
504                 nm_ip4_config_reset_nameservers (config);
505                 nm_ip4_config_reset_domains (config);
506                 nm_ip4_config_reset_searches (config);
507         }
508         for (i = 0; i < nnameservers; i++) {
509                 guint32 ip;
510
511                 if (inet_pton (AF_INET, nm_setting_ip_config_get_dns (setting, i), &ip) == 1)
512                         nm_ip4_config_add_nameserver (config, ip);
513         }
514         for (i = 0; i < nsearches; i++)
515                 nm_ip4_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i));
516
517         i = 0;
518         while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) {
519                 nm_ip4_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i));
520                 i++;
521         }
522
523         g_object_thaw_notify (G_OBJECT (config));
524 }
525
526 NMSetting *
527 nm_ip4_config_create_setting (const NMIP4Config *config)
528 {
529         NMSettingIPConfig *s_ip4;
530         guint32 gateway;
531         guint naddresses, nroutes, nnameservers, nsearches, noptions;
532         const char *method = NULL;
533         int i;
534         gint64 route_metric;
535
536         s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
537
538         if (!config) {
539                 g_object_set (s_ip4,
540                               NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
541                               NULL);
542                 return NM_SETTING (s_ip4);
543         }
544
545         gateway = nm_ip4_config_get_gateway (config);
546         naddresses = nm_ip4_config_get_num_addresses (config);
547         nroutes = nm_ip4_config_get_num_routes (config);
548         nnameservers = nm_ip4_config_get_num_nameservers (config);
549         nsearches = nm_ip4_config_get_num_searches (config);
550         noptions = nm_ip4_config_get_num_dns_options (config);
551         route_metric = nm_ip4_config_get_route_metric (config);
552
553         /* Addresses */
554         for (i = 0; i < naddresses; i++) {
555                 const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i);
556                 NMIPAddress *s_addr;
557
558                 /* Detect dynamic address */
559                 if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
560                         method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
561                         continue;
562                 }
563
564                 /* Static address found. */
565                 if (!method)
566                         method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
567
568                 s_addr = nm_ip_address_new_binary (AF_INET, &address->address, address->plen, NULL);
569                 if (*address->label)
570                         nm_ip_address_set_attribute (s_addr, "label", g_variant_new_string (address->label));
571
572                 nm_setting_ip_config_add_address (s_ip4, s_addr);
573                 nm_ip_address_unref (s_addr);
574         }
575
576         /* Gateway */
577         if (   nm_ip4_config_has_gateway (config)
578             && nm_setting_ip_config_get_num_addresses (s_ip4) > 0) {
579                 g_object_set (s_ip4,
580                               NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet4_ntop (gateway, NULL),
581                               NULL);
582         }
583
584         /* Use 'disabled' if the method wasn't previously set */
585         if (!method)
586                 method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
587
588         g_object_set (s_ip4,
589                       NM_SETTING_IP_CONFIG_METHOD, method,
590                       NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric,
591                       NULL);
592
593         /* Routes */
594         for (i = 0; i < nroutes; i++) {
595                 const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i);
596                 NMIPRoute *s_route;
597
598                 /* Ignore default route. */
599                 if (!route->plen)
600                         continue;
601
602                 /* Ignore routes provided by external sources */
603                 if (route->source != NM_IP_CONFIG_SOURCE_USER)
604                         continue;
605
606                 s_route = nm_ip_route_new_binary (AF_INET,
607                                                   &route->network, route->plen,
608                                                   &route->gateway, route->metric,
609                                                   NULL);
610                 nm_setting_ip_config_add_route (s_ip4, s_route);
611                 nm_ip_route_unref (s_route);
612         }
613
614         /* DNS */
615         for (i = 0; i < nnameservers; i++) {
616                 guint32 nameserver = nm_ip4_config_get_nameserver (config, i);
617
618                 nm_setting_ip_config_add_dns (s_ip4, nm_utils_inet4_ntop (nameserver, NULL));
619         }
620         for (i = 0; i < nsearches; i++) {
621                 const char *search = nm_ip4_config_get_search (config, i);
622
623                 nm_setting_ip_config_add_dns_search (s_ip4, search);
624         }
625
626         for (i = 0; i < noptions; i++) {
627                 const char *option = nm_ip4_config_get_dns_option (config, i);
628
629                 nm_setting_ip_config_add_dns_option (s_ip4, option);
630         }
631
632         return NM_SETTING (s_ip4);
633 }
634
635 /******************************************************************/
636
637 void
638 nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags)
639 {
640         NMIP4ConfigPrivate *dst_priv, *src_priv;
641         guint32 i;
642
643         g_return_if_fail (src != NULL);
644         g_return_if_fail (dst != NULL);
645
646         dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst);
647         src_priv = NM_IP4_CONFIG_GET_PRIVATE (src);
648
649         g_object_freeze_notify (G_OBJECT (dst));
650
651         /* addresses */
652         for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++)
653                 nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i));
654
655         /* nameservers */
656         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
657                 for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++)
658                         nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i));
659         }
660
661         /* default gateway */
662         if (nm_ip4_config_has_gateway (src))
663                 nm_ip4_config_set_gateway (dst, nm_ip4_config_get_gateway (src));
664
665         /* routes */
666         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
667                 for (i = 0; i < nm_ip4_config_get_num_routes (src); i++)
668                         nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i));
669         }
670
671         if (dst_priv->route_metric == -1)
672                 dst_priv->route_metric = src_priv->route_metric;
673         else if (src_priv->route_metric != -1)
674                 dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric);
675
676         /* domains */
677         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
678                 for (i = 0; i < nm_ip4_config_get_num_domains (src); i++)
679                         nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i));
680         }
681
682         /* dns searches */
683         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
684                 for (i = 0; i < nm_ip4_config_get_num_searches (src); i++)
685                         nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i));
686         }
687
688         /* dns options */
689         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
690                 for (i = 0; i < nm_ip4_config_get_num_dns_options (src); i++)
691                         nm_ip4_config_add_dns_option (dst, nm_ip4_config_get_dns_option (src, i));
692         }
693
694         /* MSS */
695         if (nm_ip4_config_get_mss (src))
696                 nm_ip4_config_set_mss (dst, nm_ip4_config_get_mss (src));
697
698         /* MTU */
699         if (nm_ip4_config_get_mtu (src))
700                 nm_ip4_config_set_mtu (dst, nm_ip4_config_get_mtu (src),
701                                        nm_ip4_config_get_mtu_source (src));
702
703         /* NIS */
704         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
705                 for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++)
706                         nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i));
707
708                 if (nm_ip4_config_get_nis_domain (src))
709                         nm_ip4_config_set_nis_domain (dst, nm_ip4_config_get_nis_domain (src));
710         }
711
712         /* WINS */
713         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
714                 for (i = 0; i < nm_ip4_config_get_num_wins (src); i++)
715                         nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i));
716         }
717
718         /* metered flag */
719         nm_ip4_config_set_metered (dst, nm_ip4_config_get_metered (dst) ||
720                                         nm_ip4_config_get_metered (src));
721
722         g_object_thaw_notify (G_OBJECT (dst));
723 }
724
725 /*******************************************************************************/
726
727 static int
728 _addresses_get_index (const NMIP4Config *self, const NMPlatformIP4Address *addr)
729 {
730         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
731         guint i;
732
733         for (i = 0; i < priv->addresses->len; i++) {
734                 const NMPlatformIP4Address *a = &g_array_index (priv->addresses, NMPlatformIP4Address, i);
735
736                 if (addresses_are_duplicate (addr, a))
737                         return (int) i;
738         }
739         return -1;
740 }
741
742 static int
743 _nameservers_get_index (const NMIP4Config *self, guint32 ns)
744 {
745         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
746         guint i;
747
748         for (i = 0; i < priv->nameservers->len; i++) {
749                 guint32 n = g_array_index (priv->nameservers, guint32, i);
750
751                 if (ns == n)
752                         return (int) i;
753         }
754         return -1;
755 }
756
757 static int
758 _routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route)
759 {
760         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
761         guint i;
762
763         for (i = 0; i < priv->routes->len; i++) {
764                 const NMPlatformIP4Route *r = &g_array_index (priv->routes, NMPlatformIP4Route, i);
765
766                 if (   route->network == r->network
767                     && route->plen == r->plen)
768                         return (int) i;
769         }
770         return -1;
771 }
772
773 static int
774 _domains_get_index (const NMIP4Config *self, const char *domain)
775 {
776         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
777         guint i;
778
779         for (i = 0; i < priv->domains->len; i++) {
780                 const char *d = g_ptr_array_index (priv->domains, i);
781
782                 if (g_strcmp0 (domain, d) == 0)
783                         return (int) i;
784         }
785         return -1;
786 }
787
788 static int
789 _searches_get_index (const NMIP4Config *self, const char *search)
790 {
791         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
792         guint i;
793
794         for (i = 0; i < priv->searches->len; i++) {
795                 const char *s = g_ptr_array_index (priv->searches, i);
796
797                 if (g_strcmp0 (search, s) == 0)
798                         return (int) i;
799         }
800         return -1;
801 }
802
803 static int
804 _dns_options_get_index (const NMIP4Config *self, const char *option)
805 {
806         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
807         guint i;
808
809         for (i = 0; i < priv->dns_options->len; i++) {
810                 const char *s = g_ptr_array_index (priv->dns_options, i);
811
812                 if (g_strcmp0 (option, s) == 0)
813                         return (int) i;
814         }
815         return -1;
816 }
817
818 static int
819 _nis_servers_get_index (const NMIP4Config *self, guint32 nis_server)
820 {
821         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
822         guint i;
823
824         for (i = 0; i < priv->nis->len; i++) {
825                 guint32 n = g_array_index (priv->nis, guint32, i);
826
827                 if (n == nis_server)
828                         return (int) i;
829         }
830         return -1;
831 }
832
833 static int
834 _wins_get_index (const NMIP4Config *self, guint32 wins_server)
835 {
836         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
837         guint i;
838
839         for (i = 0; i < priv->wins->len; i++) {
840                 guint32 n = g_array_index (priv->wins, guint32, i);
841
842                 if (n == wins_server)
843                         return (int) i;
844         }
845         return -1;
846 }
847
848 /*******************************************************************************/
849
850 /**
851  * nm_ip4_config_subtract:
852  * @dst: config from which to remove everything in @src
853  * @src: config to remove from @dst
854  *
855  * Removes everything in @src from @dst.
856  */
857 void
858 nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src)
859 {
860         guint32 i;
861         gint idx;
862
863         g_return_if_fail (src != NULL);
864         g_return_if_fail (dst != NULL);
865
866         g_object_freeze_notify (G_OBJECT (dst));
867
868         /* addresses */
869         for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++) {
870                 idx = _addresses_get_index (dst, nm_ip4_config_get_address (src, i));
871                 if (idx >= 0)
872                         nm_ip4_config_del_address (dst, idx);
873         }
874
875         /* nameservers */
876         for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++) {
877                 idx = _nameservers_get_index (dst, nm_ip4_config_get_nameserver (src, i));
878                 if (idx >= 0)
879                         nm_ip4_config_del_nameserver (dst, idx);
880         }
881
882         /* default gateway */
883         if (   (nm_ip4_config_has_gateway (src) == nm_ip4_config_has_gateway (dst))
884             && (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst)))
885                 nm_ip4_config_unset_gateway (dst);
886
887         if (!nm_ip4_config_get_num_addresses (dst))
888                 nm_ip4_config_unset_gateway (dst);
889
890         /* ignore route_metric */
891
892         /* routes */
893         for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) {
894                 idx = _routes_get_index (dst, nm_ip4_config_get_route (src, i));
895                 if (idx >= 0)
896                         nm_ip4_config_del_route (dst, idx);
897         }
898
899         /* domains */
900         for (i = 0; i < nm_ip4_config_get_num_domains (src); i++) {
901                 idx = _domains_get_index (dst, nm_ip4_config_get_domain (src, i));
902                 if (idx >= 0)
903                         nm_ip4_config_del_domain (dst, idx);
904         }
905
906         /* dns searches */
907         for (i = 0; i < nm_ip4_config_get_num_searches (src); i++) {
908                 idx = _searches_get_index (dst, nm_ip4_config_get_search (src, i));
909                 if (idx >= 0)
910                         nm_ip4_config_del_search (dst, idx);
911         }
912
913         /* dns options */
914         for (i = 0; i < nm_ip4_config_get_num_dns_options (src); i++) {
915                 idx = _dns_options_get_index (dst, nm_ip4_config_get_dns_option (src, i));
916                 if (idx >= 0)
917                         nm_ip4_config_del_dns_option (dst, idx);
918         }
919
920         /* MSS */
921         if (nm_ip4_config_get_mss (src) == nm_ip4_config_get_mss (dst))
922                 nm_ip4_config_set_mss (dst, 0);
923
924         /* MTU */
925         if (nm_ip4_config_get_mtu (src) == nm_ip4_config_get_mtu (dst))
926                 nm_ip4_config_set_mtu (dst, 0, NM_IP_CONFIG_SOURCE_UNKNOWN);
927
928         /* NIS */
929         for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++) {
930                 idx = _nis_servers_get_index (dst, nm_ip4_config_get_nis_server (src, i));
931                 if (idx >= 0)
932                         nm_ip4_config_del_nis_server (dst, idx);
933         }
934
935         if (g_strcmp0 (nm_ip4_config_get_nis_domain (src), nm_ip4_config_get_nis_domain (dst)) == 0)
936                 nm_ip4_config_set_nis_domain (dst, NULL);
937
938         /* WINS */
939         for (i = 0; i < nm_ip4_config_get_num_wins (src); i++) {
940                 idx = _wins_get_index (dst, nm_ip4_config_get_wins (src, i));
941                 if (idx >= 0)
942                         nm_ip4_config_del_wins (dst, idx);
943         }
944
945         g_object_thaw_notify (G_OBJECT (dst));
946 }
947
948 void
949 nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src)
950 {
951         guint32 i;
952         gint idx;
953
954         g_return_if_fail (src != NULL);
955         g_return_if_fail (dst != NULL);
956
957         g_object_freeze_notify (G_OBJECT (dst));
958
959         /* addresses */
960         for (i = 0; i < nm_ip4_config_get_num_addresses (dst); ) {
961                 idx = _addresses_get_index (src, nm_ip4_config_get_address (dst, i));
962                 if (idx < 0)
963                         nm_ip4_config_del_address (dst, i);
964                 else
965                         i++;
966         }
967
968         /* ignore route_metric */
969         /* ignore nameservers */
970
971         /* default gateway */
972         if (   !nm_ip4_config_get_num_addresses (dst)
973             || (nm_ip4_config_has_gateway (src) != nm_ip4_config_has_gateway (dst))
974             || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst))) {
975                 nm_ip4_config_unset_gateway (dst);
976         }
977
978         /* routes */
979         for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) {
980                 idx = _routes_get_index (src, nm_ip4_config_get_route (dst, i));
981                 if (idx < 0)
982                         nm_ip4_config_del_route (dst, i);
983                 else
984                         i++;
985         }
986
987         /* ignore domains */
988         /* ignore dns searches */
989         /* ignore dns options */
990         /* ignore NIS */
991         /* ignore WINS */
992
993         g_object_thaw_notify (G_OBJECT (dst));
994 }
995
996
997 /**
998  * nm_ip4_config_replace:
999  * @dst: config to replace with @src content
1000  * @src: source config to copy
1001  * @relevant_changes: return whether there are changes to the
1002  * destination object that are relevant. This is equal to
1003  * nm_ip4_config_equal() showing any difference.
1004  *
1005  * Replaces everything in @dst with @src so that the two configurations
1006  * contain the same content -- with the exception of the dbus path.
1007  *
1008  * Returns: whether the @dst instance changed in any way (including minor changes,
1009  * that are not signaled by the output parameter @relevant_changes).
1010  */
1011 gboolean
1012 nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes)
1013 {
1014 #if NM_MORE_ASSERTS
1015         gboolean config_equal;
1016 #endif
1017         gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
1018         guint i, num;
1019         NMIP4ConfigPrivate *dst_priv, *src_priv;
1020         const NMPlatformIP4Address *dst_addr, *src_addr;
1021         const NMPlatformIP4Route *dst_route, *src_route;
1022
1023         g_return_val_if_fail (src != NULL, FALSE);
1024         g_return_val_if_fail (dst != NULL, FALSE);
1025         g_return_val_if_fail (src != dst, FALSE);
1026
1027 #if NM_MORE_ASSERTS
1028         config_equal = nm_ip4_config_equal (dst, src);
1029 #endif
1030
1031         dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst);
1032         src_priv = NM_IP4_CONFIG_GET_PRIVATE (src);
1033
1034         g_object_freeze_notify (G_OBJECT (dst));
1035
1036         /* ifindex */
1037         if (src_priv->ifindex != dst_priv->ifindex) {
1038                 dst_priv->ifindex = src_priv->ifindex;
1039                 has_minor_changes = TRUE;
1040         }
1041
1042         /* never_default */
1043         if (src_priv->never_default != dst_priv->never_default) {
1044                 dst_priv->never_default = src_priv->never_default;
1045                 has_minor_changes = TRUE;
1046         }
1047
1048         /* default gateway */
1049         if (   src_priv->gateway != dst_priv->gateway
1050             || src_priv->has_gateway != dst_priv->has_gateway) {
1051                 if (src_priv->has_gateway)
1052                         nm_ip4_config_set_gateway (dst, src_priv->gateway);
1053                 else
1054                         nm_ip4_config_unset_gateway (dst);
1055                 has_relevant_changes = TRUE;
1056         }
1057
1058         if (src_priv->route_metric != dst_priv->route_metric) {
1059                 dst_priv->route_metric = src_priv->route_metric;
1060                 has_minor_changes = TRUE;
1061         }
1062
1063         /* addresses */
1064         num = nm_ip4_config_get_num_addresses (src);
1065         are_equal = num == nm_ip4_config_get_num_addresses (dst);
1066         if (are_equal) {
1067                 for (i = 0; i < num; i++ ) {
1068                         if (nm_platform_ip4_address_cmp (src_addr = nm_ip4_config_get_address (src, i),
1069                                                          dst_addr = nm_ip4_config_get_address (dst, i))) {
1070                                 are_equal = FALSE;
1071                                 if (   !addresses_are_duplicate (src_addr, dst_addr)
1072                                     || src_addr->peer_address != dst_addr->peer_address) {
1073                                         has_relevant_changes = TRUE;
1074                                         break;
1075                                 }
1076                         }
1077                 }
1078         } else
1079                 has_relevant_changes = TRUE;
1080         if (!are_equal) {
1081                 nm_ip4_config_reset_addresses (dst);
1082                 for (i = 0; i < num; i++)
1083                         nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i));
1084                 has_minor_changes = TRUE;
1085         }
1086
1087         /* routes */
1088         num = nm_ip4_config_get_num_routes (src);
1089         are_equal = num == nm_ip4_config_get_num_routes (dst);
1090         if (are_equal) {
1091                 for (i = 0; i < num; i++ ) {
1092                         if (nm_platform_ip4_route_cmp (src_route = nm_ip4_config_get_route (src, i),
1093                                                        dst_route = nm_ip4_config_get_route (dst, i))) {
1094                                 are_equal = FALSE;
1095                                 if (!routes_are_duplicate (src_route, dst_route, TRUE)) {
1096                                         has_relevant_changes = TRUE;
1097                                         break;
1098                                 }
1099                         }
1100                 }
1101         } else
1102                 has_relevant_changes = TRUE;
1103         if (!are_equal) {
1104                 nm_ip4_config_reset_routes (dst);
1105                 for (i = 0; i < num; i++)
1106                         nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i));
1107                 has_minor_changes = TRUE;
1108         }
1109
1110         /* nameservers */
1111         num = nm_ip4_config_get_num_nameservers (src);
1112         are_equal = num == nm_ip4_config_get_num_nameservers (dst);
1113         if (are_equal) {
1114                 for (i = 0; i < num; i++ ) {
1115                         if (nm_ip4_config_get_nameserver (src, i) != nm_ip4_config_get_nameserver (dst, i)) {
1116                                 are_equal = FALSE;
1117                                 break;
1118                         }
1119                 }
1120         }
1121         if (!are_equal) {
1122                 nm_ip4_config_reset_nameservers (dst);
1123                 for (i = 0; i < num; i++)
1124                         nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i));
1125                 has_relevant_changes = TRUE;
1126         }
1127
1128         /* domains */
1129         num = nm_ip4_config_get_num_domains (src);
1130         are_equal = num == nm_ip4_config_get_num_domains (dst);
1131         if (are_equal) {
1132                 for (i = 0; i < num; i++ ) {
1133                         if (g_strcmp0 (nm_ip4_config_get_domain (src, i),
1134                                        nm_ip4_config_get_domain (dst, i))) {
1135                                 are_equal = FALSE;
1136                                 break;
1137                         }
1138                 }
1139         }
1140         if (!are_equal) {
1141                 nm_ip4_config_reset_domains (dst);
1142                 for (i = 0; i < num; i++)
1143                         nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i));
1144                 has_relevant_changes = TRUE;
1145         }
1146
1147         /* dns searches */
1148         num = nm_ip4_config_get_num_searches (src);
1149         are_equal = num == nm_ip4_config_get_num_searches (dst);
1150         if (are_equal) {
1151                 for (i = 0; i < num; i++ ) {
1152                         if (g_strcmp0 (nm_ip4_config_get_search (src, i),
1153                                        nm_ip4_config_get_search (dst, i))) {
1154                                 are_equal = FALSE;
1155                                 break;
1156                         }
1157                 }
1158         }
1159         if (!are_equal) {
1160                 nm_ip4_config_reset_searches (dst);
1161                 for (i = 0; i < num; i++)
1162                         nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i));
1163                 has_relevant_changes = TRUE;
1164         }
1165
1166         /* dns options */
1167         num = nm_ip4_config_get_num_dns_options (src);
1168         are_equal = num == nm_ip4_config_get_num_dns_options (dst);
1169         if (are_equal) {
1170                 for (i = 0; i < num; i++ ) {
1171                         if (g_strcmp0 (nm_ip4_config_get_dns_option (src, i),
1172                                        nm_ip4_config_get_dns_option (dst, i))) {
1173                                 are_equal = FALSE;
1174                                 break;
1175                         }
1176                 }
1177         }
1178         if (!are_equal) {
1179                 nm_ip4_config_reset_dns_options (dst);
1180                 for (i = 0; i < num; i++)
1181                         nm_ip4_config_add_dns_option (dst, nm_ip4_config_get_dns_option (src, i));
1182                 has_relevant_changes = TRUE;
1183         }
1184
1185         /* mss */
1186         if (src_priv->mss != dst_priv->mss) {
1187                 nm_ip4_config_set_mss (dst, src_priv->mss);
1188                 has_minor_changes = TRUE;
1189         }
1190
1191         /* nis */
1192         num = nm_ip4_config_get_num_nis_servers (src);
1193         are_equal = num == nm_ip4_config_get_num_nis_servers (dst);
1194         if (are_equal) {
1195                 for (i = 0; i < num; i++ ) {
1196                         if (nm_ip4_config_get_nis_server (src, i) != nm_ip4_config_get_nis_server (dst, i)) {
1197                                 are_equal = FALSE;
1198                                 break;
1199                         }
1200                 }
1201         }
1202         if (!are_equal) {
1203                 nm_ip4_config_reset_nis_servers (dst);
1204                 for (i = 0; i < num; i++)
1205                         nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i));
1206                 has_relevant_changes = TRUE;
1207         }
1208
1209         /* nis_domain */
1210         if (g_strcmp0 (src_priv->nis_domain, dst_priv->nis_domain)) {
1211                 nm_ip4_config_set_nis_domain (dst, src_priv->nis_domain);
1212                 has_relevant_changes = TRUE;
1213         }
1214
1215         /* wins */
1216         num = nm_ip4_config_get_num_wins (src);
1217         are_equal = num == nm_ip4_config_get_num_wins (dst);
1218         if (are_equal) {
1219                 for (i = 0; i < num; i++ ) {
1220                         if (nm_ip4_config_get_wins (src, i) != nm_ip4_config_get_wins (dst, i)) {
1221                                 are_equal = FALSE;
1222                                 break;
1223                         }
1224                 }
1225         }
1226         if (!are_equal) {
1227                 nm_ip4_config_reset_wins (dst);
1228                 for (i = 0; i < num; i++)
1229                         nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i));
1230                 has_relevant_changes = TRUE;
1231         }
1232
1233         /* mtu */
1234         if (src_priv->mtu != dst_priv->mtu) {
1235                 nm_ip4_config_set_mtu (dst, src_priv->mtu, src_priv->mtu_source);
1236                 has_minor_changes = TRUE;
1237         }
1238
1239         /* metered */
1240         if (src_priv->metered != dst_priv->metered) {
1241                 dst_priv->metered = src_priv->metered;
1242                 has_minor_changes = TRUE;
1243         }
1244
1245 #if NM_MORE_ASSERTS
1246         /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes
1247          * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */
1248         nm_assert (config_equal == !has_relevant_changes);
1249 #endif
1250
1251         g_object_thaw_notify (G_OBJECT (dst));
1252
1253         if (relevant_changes)
1254                 *relevant_changes = has_relevant_changes;
1255
1256         return has_relevant_changes || has_minor_changes;
1257 }
1258
1259 void
1260 nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
1261 {
1262         guint32 i, tmp;
1263         const char *str;
1264
1265         g_message ("--------- NMIP4Config %p (%s)", config, detail);
1266
1267         if (config == NULL) {
1268                 g_message (" (null)");
1269                 return;
1270         }
1271
1272         str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config));
1273         if (str)
1274                 g_message ("   path: %s", str);
1275
1276         /* addresses */
1277         for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++)
1278                 g_message ("      a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i), NULL, 0));
1279
1280         /* default gateway */
1281         if (nm_ip4_config_has_gateway (config)) {
1282                 tmp = nm_ip4_config_get_gateway (config);
1283                 g_message ("     gw: %s", nm_utils_inet4_ntop (tmp, NULL));
1284         }
1285
1286         /* nameservers */
1287         for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) {
1288                 tmp = nm_ip4_config_get_nameserver (config, i);
1289                 g_message ("     ns: %s", nm_utils_inet4_ntop (tmp, NULL));
1290         }
1291
1292         /* routes */
1293         for (i = 0; i < nm_ip4_config_get_num_routes (config); i++)
1294                 g_message ("     rt: %s", nm_platform_ip4_route_to_string (nm_ip4_config_get_route (config, i), NULL, 0));
1295
1296         /* domains */
1297         for (i = 0; i < nm_ip4_config_get_num_domains (config); i++)
1298                 g_message (" domain: %s", nm_ip4_config_get_domain (config, i));
1299
1300         /* dns searches */
1301         for (i = 0; i < nm_ip4_config_get_num_searches (config); i++)
1302                 g_message (" search: %s", nm_ip4_config_get_search (config, i));
1303
1304         /* dns options */
1305         for (i = 0; i < nm_ip4_config_get_num_dns_options (config); i++)
1306                 g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (config, i));
1307
1308
1309         g_message ("    mss: %"G_GUINT32_FORMAT, nm_ip4_config_get_mss (config));
1310         g_message ("    mtu: %"G_GUINT32_FORMAT, nm_ip4_config_get_mtu (config));
1311
1312         /* NIS */
1313         for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++) {
1314                 tmp = nm_ip4_config_get_nis_server (config, i);
1315                 g_message ("    nis: %s", nm_utils_inet4_ntop (tmp, NULL));
1316         }
1317
1318         g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (config) ?: "(none)");
1319
1320         /* WINS */
1321         for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) {
1322                 tmp = nm_ip4_config_get_wins (config, i);
1323                 g_message ("   wins: %s", nm_utils_inet4_ntop (tmp, NULL));
1324         }
1325
1326         g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config));
1327         g_message (" mtrd:   %d", (int) nm_ip4_config_get_metered (config));
1328 }
1329
1330 gboolean
1331 nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 network, int plen)
1332 {
1333         guint naddresses = nm_ip4_config_get_num_addresses (config);
1334         int i;
1335         in_addr_t peer_network;
1336
1337         for (i = 0; i < naddresses; i++) {
1338                 const NMPlatformIP4Address *item = nm_ip4_config_get_address (config, i);
1339
1340                 if (item->plen > plen)
1341                         continue;
1342
1343                 peer_network = nm_utils_ip4_address_clear_host_address (item->peer_address, item->plen);
1344                 if (_ipv4_is_zeronet (peer_network))
1345                         continue;
1346
1347                 if (peer_network != nm_utils_ip4_address_clear_host_address (network, item->plen))
1348                         continue;
1349
1350                 return TRUE;
1351         }
1352
1353         return FALSE;
1354 }
1355
1356 /******************************************************************/
1357
1358 void
1359 nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default)
1360 {
1361         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1362
1363         priv->never_default = !!never_default;
1364 }
1365
1366 gboolean
1367 nm_ip4_config_get_never_default (const NMIP4Config *config)
1368 {
1369         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1370
1371         return priv->never_default;
1372 }
1373
1374 void
1375 nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway)
1376 {
1377         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1378
1379         if (priv->gateway != gateway || !priv->has_gateway) {
1380                 priv->gateway = gateway;
1381                 priv->has_gateway = TRUE;
1382                 _notify (config, PROP_GATEWAY);
1383         }
1384 }
1385
1386 void
1387 nm_ip4_config_unset_gateway (NMIP4Config *config)
1388 {
1389         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1390
1391         if (priv->has_gateway) {
1392                 priv->gateway = 0;
1393                 priv->has_gateway = FALSE;
1394                 _notify (config, PROP_GATEWAY);
1395         }
1396 }
1397
1398 /**
1399  * nm_ip4_config_has_gateway:
1400  * @config: the #NMIP4Config object
1401  *
1402  * NetworkManager's handling of default-routes is limited and usually a default-route
1403  * cannot have gateway 0.0.0.0. For peer-to-peer routes, we still want to
1404  * support that, so we need to differenciate between no-default-route and a
1405  * on-link-default route. Hence nm_ip4_config_has_gateway().
1406  *
1407  * Returns: whether the object has a gateway explicitly set. */
1408 gboolean
1409 nm_ip4_config_has_gateway (const NMIP4Config *config)
1410 {
1411         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1412
1413         return priv->has_gateway;
1414 }
1415
1416 guint32
1417 nm_ip4_config_get_gateway (const NMIP4Config *config)
1418 {
1419         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1420
1421         return priv->gateway;
1422 }
1423
1424 gint64
1425 nm_ip4_config_get_route_metric (const NMIP4Config *config)
1426 {
1427         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1428
1429         return priv->route_metric;
1430 }
1431
1432 /******************************************************************/
1433
1434 void
1435 nm_ip4_config_reset_addresses (NMIP4Config *config)
1436 {
1437         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1438
1439         if (priv->addresses->len != 0) {
1440                 g_array_set_size (priv->addresses, 0);
1441                 _notify (config, PROP_ADDRESS_DATA);
1442                 _notify (config, PROP_ADDRESSES);
1443         }
1444 }
1445
1446 /**
1447  * nm_ip4_config_add_address:
1448  * @config: the #NMIP4Config
1449  * @new: the new address to add to @config
1450  *
1451  * Adds the new address to @config.  If an address with the same basic properties
1452  * (address, prefix) already exists in @config, it is overwritten with the
1453  * lifetime and preferred of @new.  The source is also overwritten by the source
1454  * from @new if that source is higher priority.
1455  */
1456 void
1457 nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new)
1458 {
1459         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1460         NMPlatformIP4Address item_old;
1461         int i;
1462
1463         g_return_if_fail (new != NULL);
1464
1465         for (i = 0; i < priv->addresses->len; i++ ) {
1466                 NMPlatformIP4Address *item = &g_array_index (priv->addresses, NMPlatformIP4Address, i);
1467
1468                 if (addresses_are_duplicate (item, new)) {
1469                         if (nm_platform_ip4_address_cmp (item, new) == 0)
1470                                 return;
1471
1472                         /* remember the old values. */
1473                         item_old = *item;
1474                         /* Copy over old item to get new lifetime, timestamp, preferred */
1475                         *item = *new;
1476
1477                         /* But restore highest priority source */
1478                         item->source = MAX (item_old.source, new->source);
1479
1480                         /* for addresses that we read from the kernel, we keep the timestamps as defined
1481                          * by the previous source (item_old). The reason is, that the other source configured the lifetimes
1482                          * with "what should be" and the kernel values are "what turned out after configuring it".
1483                          *
1484                          * For other sources, the longer lifetime wins. */
1485                         if (   (new->source == NM_IP_CONFIG_SOURCE_KERNEL && new->source != item_old.source)
1486                             || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) {
1487                                 item->timestamp = item_old.timestamp;
1488                                 item->lifetime = item_old.lifetime;
1489                                 item->preferred = item_old.preferred;
1490                         }
1491                         if (nm_platform_ip4_address_cmp (&item_old, item) == 0)
1492                                 return;
1493                         goto NOTIFY;
1494                 }
1495         }
1496
1497         g_array_append_val (priv->addresses, *new);
1498 NOTIFY:
1499         _notify (config, PROP_ADDRESS_DATA);
1500         _notify (config, PROP_ADDRESSES);
1501 }
1502
1503 void
1504 nm_ip4_config_del_address (NMIP4Config *config, guint i)
1505 {
1506         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1507
1508         g_return_if_fail (i < priv->addresses->len);
1509
1510         g_array_remove_index (priv->addresses, i);
1511         _notify (config, PROP_ADDRESS_DATA);
1512         _notify (config, PROP_ADDRESSES);
1513 }
1514
1515 guint
1516 nm_ip4_config_get_num_addresses (const NMIP4Config *config)
1517 {
1518         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1519
1520         return priv->addresses->len;
1521 }
1522
1523 const NMPlatformIP4Address *
1524 nm_ip4_config_get_address (const NMIP4Config *config, guint i)
1525 {
1526         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1527
1528         return &g_array_index (priv->addresses, NMPlatformIP4Address, i);
1529 }
1530
1531 gboolean
1532 nm_ip4_config_address_exists (const NMIP4Config *config,
1533                               const NMPlatformIP4Address *needle)
1534 {
1535         return _addresses_get_index (config, needle) >= 0;
1536 }
1537
1538 /******************************************************************/
1539
1540 void
1541 nm_ip4_config_reset_routes (NMIP4Config *config)
1542 {
1543         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1544
1545         if (priv->routes->len != 0) {
1546                 g_array_set_size (priv->routes, 0);
1547                 _notify (config, PROP_ROUTE_DATA);
1548                 _notify (config, PROP_ROUTES);
1549         }
1550 }
1551
1552 /**
1553  * nm_ip4_config_add_route:
1554  * @config: the #NMIP4Config
1555  * @new: the new route to add to @config
1556  *
1557  * Adds the new route to @config.  If a route with the same basic properties
1558  * (network, prefix) already exists in @config, it is overwritten including the
1559  * gateway and metric of @new.  The source is also overwritten by the source
1560  * from @new if that source is higher priority.
1561  */
1562 void
1563 nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
1564 {
1565         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1566         NMIPConfigSource old_source;
1567         int i;
1568
1569         g_return_if_fail (new != NULL);
1570         g_return_if_fail (new->plen > 0);
1571         g_assert (priv->ifindex);
1572
1573         for (i = 0; i < priv->routes->len; i++ ) {
1574                 NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i);
1575
1576                 if (routes_are_duplicate (item, new, FALSE)) {
1577                         if (nm_platform_ip4_route_cmp (item, new) == 0)
1578                                 return;
1579                         old_source = item->source;
1580                         memcpy (item, new, sizeof (*item));
1581                         /* Restore highest priority source */
1582                         item->source = MAX (old_source, new->source);
1583                         item->ifindex = priv->ifindex;
1584                         goto NOTIFY;
1585                 }
1586         }
1587
1588         g_array_append_val (priv->routes, *new);
1589         g_array_index (priv->routes, NMPlatformIP4Route, priv->routes->len - 1).ifindex = priv->ifindex;
1590 NOTIFY:
1591         _notify (config, PROP_ROUTE_DATA);
1592         _notify (config, PROP_ROUTES);
1593 }
1594
1595 void
1596 nm_ip4_config_del_route (NMIP4Config *config, guint i)
1597 {
1598         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1599
1600         g_return_if_fail (i < priv->routes->len);
1601
1602         g_array_remove_index (priv->routes, i);
1603         _notify (config, PROP_ROUTE_DATA);
1604         _notify (config, PROP_ROUTES);
1605 }
1606
1607 guint
1608 nm_ip4_config_get_num_routes (const NMIP4Config *config)
1609 {
1610         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1611
1612         return priv->routes->len;
1613 }
1614
1615 const NMPlatformIP4Route *
1616 nm_ip4_config_get_route (const NMIP4Config *config, guint i)
1617 {
1618         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1619
1620         return &g_array_index (priv->routes, NMPlatformIP4Route, i);
1621 }
1622
1623 const NMPlatformIP4Route *
1624 nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host)
1625 {
1626         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1627         guint i;
1628         NMPlatformIP4Route *best_route = NULL;
1629
1630         g_return_val_if_fail (host, NULL);
1631
1632         for (i = 0; i < priv->routes->len; i++) {
1633                 NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i);
1634
1635                 if (item->gateway != 0)
1636                         continue;
1637
1638                 if (best_route && best_route->plen > item->plen)
1639                         continue;
1640
1641                 if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->network, item->plen))
1642                         continue;
1643
1644                 if (best_route && best_route->metric <= item->metric)
1645                         continue;
1646
1647                 best_route = item;
1648         }
1649
1650         return best_route;
1651 }
1652
1653 /******************************************************************/
1654
1655 void
1656 nm_ip4_config_reset_nameservers (NMIP4Config *config)
1657 {
1658         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1659
1660         if (priv->nameservers->len != 0) {
1661                 g_array_set_size (priv->nameservers, 0);
1662                 _notify (config, PROP_NAMESERVERS);
1663         }
1664 }
1665
1666 void
1667 nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 new)
1668 {
1669         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1670         int i;
1671
1672         g_return_if_fail (new != 0);
1673
1674         for (i = 0; i < priv->nameservers->len; i++)
1675                 if (new == g_array_index (priv->nameservers, guint32, i))
1676                         return;
1677
1678         g_array_append_val (priv->nameservers, new);
1679         _notify (config, PROP_NAMESERVERS);
1680 }
1681
1682 void
1683 nm_ip4_config_del_nameserver (NMIP4Config *config, guint i)
1684 {
1685         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1686
1687         g_return_if_fail (i < priv->nameservers->len);
1688
1689         g_array_remove_index (priv->nameservers, i);
1690         _notify (config, PROP_NAMESERVERS);
1691 }
1692
1693 guint32
1694 nm_ip4_config_get_num_nameservers (const NMIP4Config *config)
1695 {
1696         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1697
1698         return priv->nameservers->len;
1699 }
1700
1701 guint32
1702 nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i)
1703 {
1704         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1705
1706         return g_array_index (priv->nameservers, guint32, i);
1707 }
1708
1709 /******************************************************************/
1710
1711 void
1712 nm_ip4_config_reset_domains (NMIP4Config *config)
1713 {
1714         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1715
1716         if (priv->domains->len != 0) {
1717                 g_ptr_array_set_size (priv->domains, 0);
1718                 _notify (config, PROP_DOMAINS);
1719         }
1720 }
1721
1722 void
1723 nm_ip4_config_add_domain (NMIP4Config *config, const char *domain)
1724 {
1725         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1726         int i;
1727
1728         g_return_if_fail (domain != NULL);
1729         g_return_if_fail (domain[0] != '\0');
1730
1731         for (i = 0; i < priv->domains->len; i++)
1732                 if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain))
1733                         return;
1734
1735         g_ptr_array_add (priv->domains, g_strdup (domain));
1736         _notify (config, PROP_DOMAINS);
1737 }
1738
1739 void
1740 nm_ip4_config_del_domain (NMIP4Config *config, guint i)
1741 {
1742         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1743
1744         g_return_if_fail (i < priv->domains->len);
1745
1746         g_ptr_array_remove_index (priv->domains, i);
1747         _notify (config, PROP_DOMAINS);
1748 }
1749
1750 guint32
1751 nm_ip4_config_get_num_domains (const NMIP4Config *config)
1752 {
1753         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1754
1755         return priv->domains->len;
1756 }
1757
1758 const char *
1759 nm_ip4_config_get_domain (const NMIP4Config *config, guint i)
1760 {
1761         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1762
1763         return g_ptr_array_index (priv->domains, i);
1764 }
1765
1766 /******************************************************************/
1767
1768 void
1769 nm_ip4_config_reset_searches (NMIP4Config *config)
1770 {
1771         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1772
1773         if (priv->searches->len != 0) {
1774                 g_ptr_array_set_size (priv->searches, 0);
1775                 _notify (config, PROP_SEARCHES);
1776         }
1777 }
1778
1779 void
1780 nm_ip4_config_add_search (NMIP4Config *config, const char *new)
1781 {
1782         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1783         char *search;
1784         size_t len;
1785
1786         g_return_if_fail (new != NULL);
1787         g_return_if_fail (new[0] != '\0');
1788
1789         search = g_strdup (new);
1790
1791         /* Remove trailing dot as it has no effect */
1792         len = strlen (search);
1793         if (search[len - 1] == '.')
1794                 search[len - 1] = 0;
1795
1796         if (!search[0]) {
1797                 g_free (search);
1798                 return;
1799         }
1800
1801         if (_nm_utils_strv_find_first ((char **) priv->searches->pdata,
1802                                        priv->searches->len, search) >= 0) {
1803                 g_free (search);
1804                 return;
1805         }
1806
1807         g_ptr_array_add (priv->searches, search);
1808         _notify (config, PROP_SEARCHES);
1809 }
1810
1811 void
1812 nm_ip4_config_del_search (NMIP4Config *config, guint i)
1813 {
1814         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1815
1816         g_return_if_fail (i < priv->searches->len);
1817
1818         g_ptr_array_remove_index (priv->searches, i);
1819         _notify (config, PROP_SEARCHES);
1820 }
1821
1822 guint32
1823 nm_ip4_config_get_num_searches (const NMIP4Config *config)
1824 {
1825         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1826
1827         return priv->searches->len;
1828 }
1829
1830 const char *
1831 nm_ip4_config_get_search (const NMIP4Config *config, guint i)
1832 {
1833         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1834
1835         return g_ptr_array_index (priv->searches, i);
1836 }
1837
1838 /******************************************************************/
1839
1840 void
1841 nm_ip4_config_reset_dns_options (NMIP4Config *config)
1842 {
1843         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1844
1845         if (priv->dns_options->len != 0) {
1846                 g_ptr_array_set_size (priv->dns_options, 0);
1847                 _notify (config, PROP_DNS_OPTIONS);
1848         }
1849 }
1850
1851 void
1852 nm_ip4_config_add_dns_option (NMIP4Config *config, const char *new)
1853 {
1854         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1855         int i;
1856
1857         g_return_if_fail (new != NULL);
1858         g_return_if_fail (new[0] != '\0');
1859
1860         for (i = 0; i < priv->dns_options->len; i++)
1861                 if (!g_strcmp0 (g_ptr_array_index (priv->dns_options, i), new))
1862                         return;
1863
1864         g_ptr_array_add (priv->dns_options, g_strdup (new));
1865         _notify (config, PROP_DNS_OPTIONS);
1866 }
1867
1868 void
1869 nm_ip4_config_del_dns_option(NMIP4Config *config, guint i)
1870 {
1871         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1872
1873         g_return_if_fail (i < priv->dns_options->len);
1874
1875         g_ptr_array_remove_index (priv->dns_options, i);
1876         _notify (config, PROP_DNS_OPTIONS);
1877 }
1878
1879 guint32
1880 nm_ip4_config_get_num_dns_options (const NMIP4Config *config)
1881 {
1882         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1883
1884         return priv->dns_options->len;
1885 }
1886
1887 const char *
1888 nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i)
1889 {
1890         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1891
1892         return g_ptr_array_index (priv->dns_options, i);
1893 }
1894
1895 /******************************************************************/
1896
1897 void
1898 nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss)
1899 {
1900         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1901
1902         priv->mss = mss;
1903 }
1904
1905 guint32
1906 nm_ip4_config_get_mss (const NMIP4Config *config)
1907 {
1908         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1909
1910         return priv->mss;
1911 }
1912
1913 /******************************************************************/
1914
1915 void
1916 nm_ip4_config_reset_nis_servers (NMIP4Config *config)
1917 {
1918         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1919
1920         g_array_set_size (priv->nis, 0);
1921 }
1922
1923 void
1924 nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis)
1925 {
1926         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1927         int i;
1928
1929         for (i = 0; i < priv->nis->len; i++)
1930                 if (nis == g_array_index (priv->nis, guint32, i))
1931                         return;
1932
1933         g_array_append_val (priv->nis, nis);
1934 }
1935
1936 void
1937 nm_ip4_config_del_nis_server (NMIP4Config *config, guint i)
1938 {
1939         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1940
1941         g_return_if_fail (i < priv->nis->len);
1942
1943         g_array_remove_index (priv->nis, i);
1944 }
1945
1946 guint32
1947 nm_ip4_config_get_num_nis_servers (const NMIP4Config *config)
1948 {
1949         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1950
1951         return priv->nis->len;
1952 }
1953
1954 guint32
1955 nm_ip4_config_get_nis_server (const NMIP4Config *config, guint i)
1956 {
1957         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1958
1959         return g_array_index (priv->nis, guint32, i);
1960 }
1961
1962 void
1963 nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain)
1964 {
1965         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1966
1967         g_free (priv->nis_domain);
1968         priv->nis_domain = g_strdup (domain);
1969 }
1970
1971 const char *
1972 nm_ip4_config_get_nis_domain (const NMIP4Config *config)
1973 {
1974         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1975
1976         return priv->nis_domain;
1977 }
1978
1979 /******************************************************************/
1980
1981 void
1982 nm_ip4_config_reset_wins (NMIP4Config *config)
1983 {
1984         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1985
1986         if (priv->wins->len != 0) {
1987                 g_array_set_size (priv->wins, 0);
1988                 _notify (config, PROP_WINS_SERVERS);
1989         }
1990 }
1991
1992 void
1993 nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins)
1994 {
1995         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
1996         int i;
1997
1998         g_return_if_fail (wins != 0);
1999
2000         for (i = 0; i < priv->wins->len; i++)
2001                 if (wins == g_array_index (priv->wins, guint32, i))
2002                         return;
2003
2004         g_array_append_val (priv->wins, wins);
2005         _notify (config, PROP_WINS_SERVERS);
2006 }
2007
2008 void
2009 nm_ip4_config_del_wins (NMIP4Config *config, guint i)
2010 {
2011         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2012
2013         g_return_if_fail (i < priv->wins->len);
2014
2015         g_array_remove_index (priv->wins, i);
2016         _notify (config, PROP_WINS_SERVERS);
2017 }
2018
2019 guint32
2020 nm_ip4_config_get_num_wins (const NMIP4Config *config)
2021 {
2022         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2023
2024         return priv->wins->len;
2025 }
2026
2027 guint32
2028 nm_ip4_config_get_wins (const NMIP4Config *config, guint i)
2029 {
2030         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2031
2032         return g_array_index (priv->wins, guint32, i);
2033 }
2034
2035 /******************************************************************/
2036
2037 void
2038 nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source)
2039 {
2040         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2041
2042         if (source > priv->mtu_source) {
2043                 priv->mtu = mtu;
2044                 priv->mtu_source = source;
2045         } else if (source == priv->mtu_source && (!priv->mtu || priv->mtu > mtu))
2046                 priv->mtu = mtu;
2047 }
2048
2049 guint32
2050 nm_ip4_config_get_mtu (const NMIP4Config *config)
2051 {
2052         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2053
2054         return priv->mtu;
2055 }
2056
2057 NMIPConfigSource
2058 nm_ip4_config_get_mtu_source (const NMIP4Config *config)
2059 {
2060         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2061
2062         return priv->mtu_source;
2063 }
2064
2065 /******************************************************************/
2066
2067 void
2068 nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered)
2069 {
2070         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2071
2072         priv->metered = !!metered;
2073 }
2074
2075 gboolean
2076 nm_ip4_config_get_metered (const NMIP4Config *config)
2077 {
2078         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2079
2080         return priv->metered;
2081 }
2082
2083 /******************************************************************/
2084
2085 static inline void
2086 hash_u32 (GChecksum *sum, guint32 n)
2087 {
2088         g_checksum_update (sum, (const guint8 *) &n, sizeof (n));
2089 }
2090
2091 void
2092 nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only)
2093 {
2094         guint32 i;
2095         const char *s;
2096
2097         g_return_if_fail (config);
2098         g_return_if_fail (sum);
2099
2100         if (!dns_only) {
2101                 hash_u32 (sum, nm_ip4_config_has_gateway (config));
2102                 hash_u32 (sum, nm_ip4_config_get_gateway (config));
2103
2104                 for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) {
2105                         const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i);
2106                         hash_u32 (sum, address->address);
2107                         hash_u32 (sum, address->plen);
2108                         hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (address->plen));
2109                 }
2110
2111                 for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
2112                         const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i);
2113
2114                         hash_u32 (sum, route->network);
2115                         hash_u32 (sum, route->plen);
2116                         hash_u32 (sum, route->gateway);
2117                         hash_u32 (sum, route->metric);
2118                 }
2119
2120                 for (i = 0; i < nm_ip4_config_get_num_nis_servers (config); i++)
2121                         hash_u32 (sum, nm_ip4_config_get_nis_server (config, i));
2122
2123                 s = nm_ip4_config_get_nis_domain (config);
2124                 if (s)
2125                         g_checksum_update (sum, (const guint8 *) s, strlen (s));
2126         }
2127
2128         for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++)
2129                 hash_u32 (sum, nm_ip4_config_get_nameserver (config, i));
2130
2131         for (i = 0; i < nm_ip4_config_get_num_wins (config); i++)
2132                 hash_u32 (sum, nm_ip4_config_get_wins (config, i));
2133
2134         for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) {
2135                 s = nm_ip4_config_get_domain (config, i);
2136                 g_checksum_update (sum, (const guint8 *) s, strlen (s));
2137         }
2138
2139         for (i = 0; i < nm_ip4_config_get_num_searches (config); i++) {
2140                 s = nm_ip4_config_get_search (config, i);
2141                 g_checksum_update (sum, (const guint8 *) s, strlen (s));
2142         }
2143
2144         for (i = 0; i < nm_ip4_config_get_num_dns_options (config); i++) {
2145                 s = nm_ip4_config_get_dns_option (config, i);
2146                 g_checksum_update (sum, (const guint8 *) s, strlen (s));
2147         }
2148
2149 }
2150
2151 /**
2152  * nm_ip4_config_equal:
2153  * @a: first config to compare
2154  * @b: second config to compare
2155  *
2156  * Compares two #NMIP4Configs for basic equality.  This means that all
2157  * attributes must exist in the same order in both configs (addresses, routes,
2158  * domains, DNS servers, etc) but some attributes (address lifetimes, and address
2159  * and route sources) are ignored.
2160  *
2161  * Returns: %TRUE if the configurations are basically equal to each other,
2162  * %FALSE if not
2163  */
2164 gboolean
2165 nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b)
2166 {
2167         GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1);
2168         GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1);
2169         gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
2170         gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
2171         guchar a_data[a_len], b_data[b_len];
2172         gboolean equal;
2173
2174         if (a)
2175                 nm_ip4_config_hash (a, a_checksum, FALSE);
2176         if (b)
2177                 nm_ip4_config_hash (b, b_checksum, FALSE);
2178
2179         g_checksum_get_digest (a_checksum, a_data, &a_len);
2180         g_checksum_get_digest (b_checksum, b_data, &b_len);
2181
2182         g_assert (a_len == b_len);
2183         equal = !memcmp (a_data, b_data, a_len);
2184
2185         g_checksum_free (a_checksum);
2186         g_checksum_free (b_checksum);
2187
2188         return equal;
2189 }
2190
2191 /******************************************************************/
2192
2193 static void
2194 nm_ip4_config_init (NMIP4Config *config)
2195 {
2196         NMIP4ConfigPrivate *priv;
2197
2198         priv = G_TYPE_INSTANCE_GET_PRIVATE (config, NM_TYPE_IP4_CONFIG, NMIP4ConfigPrivate);
2199         config->priv = priv;
2200
2201         priv->addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address));
2202         priv->routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
2203         priv->nameservers = g_array_new (FALSE, FALSE, sizeof (guint32));
2204         priv->domains = g_ptr_array_new_with_free_func (g_free);
2205         priv->searches = g_ptr_array_new_with_free_func (g_free);
2206         priv->dns_options = g_ptr_array_new_with_free_func (g_free);
2207         priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32));
2208         priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32));
2209         priv->route_metric = -1;
2210 }
2211
2212 static void
2213 finalize (GObject *object)
2214 {
2215         NMIP4Config *self = NM_IP4_CONFIG (object);
2216         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
2217
2218         g_array_unref (priv->addresses);
2219         g_array_unref (priv->routes);
2220         g_array_unref (priv->nameservers);
2221         g_ptr_array_unref (priv->domains);
2222         g_ptr_array_unref (priv->searches);
2223         g_ptr_array_unref (priv->dns_options);
2224         g_array_unref (priv->nis);
2225         g_free (priv->nis_domain);
2226         g_array_unref (priv->wins);
2227
2228         G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object);
2229 }
2230
2231 static void
2232 get_property (GObject *object, guint prop_id,
2233                           GValue *value, GParamSpec *pspec)
2234 {
2235         NMIP4Config *config = NM_IP4_CONFIG (object);
2236         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
2237
2238         switch (prop_id) {
2239         case PROP_IFINDEX:
2240                 g_value_set_int (value, priv->ifindex);
2241                 break;
2242         case PROP_ADDRESS_DATA:
2243                 {
2244                         GVariantBuilder array_builder, addr_builder;
2245                         int naddr = nm_ip4_config_get_num_addresses (config);
2246                         int i;
2247
2248                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
2249                         for (i = 0; i < naddr; i++) {
2250                                 const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i);
2251
2252                                 g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}"));
2253                                 g_variant_builder_add (&addr_builder, "{sv}",
2254                                                        "address",
2255                                                        g_variant_new_string (nm_utils_inet4_ntop (address->address, NULL)));
2256                                 g_variant_builder_add (&addr_builder, "{sv}",
2257                                                        "prefix",
2258                                                        g_variant_new_uint32 (address->plen));
2259                                 if (address->peer_address != address->address) {
2260                                         g_variant_builder_add (&addr_builder, "{sv}",
2261                                                                "peer",
2262                                                                g_variant_new_string (nm_utils_inet4_ntop (address->peer_address, NULL)));
2263                                 }
2264
2265                                 if (*address->label) {
2266                                         g_variant_builder_add (&addr_builder, "{sv}",
2267                                                                "label",
2268                                                                g_variant_new_string (address->label));
2269                                 }
2270
2271                                 g_variant_builder_add (&array_builder, "a{sv}", &addr_builder);
2272                         }
2273
2274                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2275                 }
2276                 break;
2277         case PROP_ADDRESSES:
2278                 {
2279                         GVariantBuilder array_builder;
2280                         int naddr = nm_ip4_config_get_num_addresses (config);
2281                         int i;
2282
2283                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau"));
2284                         for (i = 0; i < naddr; i++) {
2285                                 const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i);
2286                                 guint32 dbus_addr[3];
2287
2288                                 dbus_addr[0] = address->address;
2289                                 dbus_addr[1] = address->plen;
2290                                 dbus_addr[2] = i == 0 ? priv->gateway : 0;
2291
2292                                 g_variant_builder_add (&array_builder, "@au",
2293                                                        g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
2294                                                                                   dbus_addr, 3, sizeof (guint32)));
2295                         }
2296
2297                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2298                 }
2299                 break;
2300         case PROP_ROUTE_DATA:
2301                 {
2302                         GVariantBuilder array_builder, route_builder;
2303                         guint nroutes = nm_ip4_config_get_num_routes (config);
2304                         int i;
2305
2306                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
2307                         for (i = 0; i < nroutes; i++) {
2308                                 const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i);
2309
2310                                 g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
2311                                 g_variant_builder_add (&route_builder, "{sv}",
2312                                                        "dest",
2313                                                        g_variant_new_string (nm_utils_inet4_ntop (route->network, NULL)));
2314                                 g_variant_builder_add (&route_builder, "{sv}",
2315                                                        "prefix",
2316                                                        g_variant_new_uint32 (route->plen));
2317                                 if (route->gateway) {
2318                                         g_variant_builder_add (&route_builder, "{sv}",
2319                                                                "next-hop",
2320                                                                g_variant_new_string (nm_utils_inet4_ntop (route->gateway, NULL)));
2321                                 }
2322                                 g_variant_builder_add (&route_builder, "{sv}",
2323                                                        "metric",
2324                                                        g_variant_new_uint32 (route->metric));
2325
2326                                 g_variant_builder_add (&array_builder, "a{sv}", &route_builder);
2327                         }
2328
2329                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2330                 }
2331                 break;
2332         case PROP_ROUTES:
2333                 {
2334                         GVariantBuilder array_builder;
2335                         guint nroutes = nm_ip4_config_get_num_routes (config);
2336                         int i;
2337
2338                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau"));
2339                         for (i = 0; i < nroutes; i++) {
2340                                 const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i);
2341                                 guint32 dbus_route[4];
2342
2343                                 /* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the
2344                                  * plen is positive. Skip the default routes not to break older clients. */
2345                                 if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
2346                                         continue;
2347
2348                                 dbus_route[0] = route->network;
2349                                 dbus_route[1] = route->plen;
2350                                 dbus_route[2] = route->gateway;
2351                                 dbus_route[3] = route->metric;
2352
2353                                 g_variant_builder_add (&array_builder, "@au",
2354                                                        g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
2355                                                                                   dbus_route, 4, sizeof (guint32)));
2356                         }
2357
2358                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2359                 }
2360                 break;
2361         case PROP_GATEWAY:
2362                 if (priv->has_gateway)
2363                         g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL));
2364                 else
2365                         g_value_set_string (value, NULL);
2366                 break;
2367         case PROP_NAMESERVERS:
2368                 g_value_take_variant (value,
2369                                       g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
2370                                                                  priv->nameservers->data,
2371                                                                  priv->nameservers->len,
2372                                                                  sizeof (guint32)));
2373                 break;
2374         case PROP_DOMAINS:
2375                 nm_utils_g_value_set_strv (value, priv->domains);
2376                 break;
2377         case PROP_SEARCHES:
2378                 nm_utils_g_value_set_strv (value, priv->searches);
2379                 break;
2380         case PROP_DNS_OPTIONS:
2381                 nm_utils_g_value_set_strv (value, priv->dns_options);
2382                 break;
2383         case PROP_WINS_SERVERS:
2384                 g_value_take_variant (value,
2385                                       g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
2386                                                                  priv->wins->data,
2387                                                                  priv->wins->len,
2388                                                                  sizeof (guint32)));
2389                 break;
2390         default:
2391                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2392                 break;
2393         }
2394 }
2395
2396 static void
2397 set_property (GObject *object,
2398               guint prop_id,
2399               const GValue *value,
2400               GParamSpec *pspec)
2401 {
2402         NMIP4Config *self = NM_IP4_CONFIG (object);
2403         NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
2404
2405         switch (prop_id) {
2406         case PROP_IFINDEX:
2407                 priv->ifindex = g_value_get_int (value);
2408                 break;
2409         default:
2410                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2411                 break;
2412         }
2413 }
2414
2415 static void
2416 nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
2417 {
2418         GObjectClass *object_class = G_OBJECT_CLASS (config_class);
2419         NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
2420
2421         g_type_class_add_private (config_class, sizeof (NMIP4ConfigPrivate));
2422
2423         exported_object_class->export_path = NM_DBUS_PATH "/IP4Config/%u";
2424
2425         object_class->get_property = get_property;
2426         object_class->set_property = set_property;
2427         object_class->finalize = finalize;
2428
2429         obj_properties[PROP_IFINDEX] =
2430                 g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "",
2431                                   -1, G_MAXINT, -1,
2432                                   G_PARAM_READWRITE |
2433                                   G_PARAM_CONSTRUCT_ONLY |
2434                                   G_PARAM_STATIC_STRINGS);
2435         obj_properties[PROP_ADDRESS_DATA] =
2436                 g_param_spec_variant (NM_IP4_CONFIG_ADDRESS_DATA, "", "",
2437                                       G_VARIANT_TYPE ("aa{sv}"),
2438                                       NULL,
2439                                       G_PARAM_READABLE |
2440                                       G_PARAM_STATIC_STRINGS);
2441         obj_properties[PROP_ADDRESSES] =
2442                 g_param_spec_variant (NM_IP4_CONFIG_ADDRESSES, "", "",
2443                                       G_VARIANT_TYPE ("aau"),
2444                                       NULL,
2445                                       G_PARAM_READABLE |
2446                                       G_PARAM_STATIC_STRINGS);
2447         obj_properties[PROP_ROUTE_DATA] =
2448                 g_param_spec_variant (NM_IP4_CONFIG_ROUTE_DATA, "", "",
2449                                       G_VARIANT_TYPE ("aa{sv}"),
2450                                       NULL,
2451                                       G_PARAM_READABLE |
2452                                       G_PARAM_STATIC_STRINGS);
2453         obj_properties[PROP_ROUTES] =
2454                 g_param_spec_variant (NM_IP4_CONFIG_ROUTES, "", "",
2455                                       G_VARIANT_TYPE ("aau"),
2456                                       NULL,
2457                                       G_PARAM_READABLE |
2458                                       G_PARAM_STATIC_STRINGS);
2459         obj_properties[PROP_GATEWAY] =
2460                 g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "",
2461                                      NULL,
2462                                      G_PARAM_READABLE |
2463                                      G_PARAM_STATIC_STRINGS);
2464         obj_properties[PROP_NAMESERVERS] =
2465                 g_param_spec_variant (NM_IP4_CONFIG_NAMESERVERS, "", "",
2466                                       G_VARIANT_TYPE ("au"),
2467                                       NULL,
2468                                       G_PARAM_READABLE |
2469                                       G_PARAM_STATIC_STRINGS);
2470         obj_properties[PROP_DOMAINS] =
2471                 g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "",
2472                                     G_TYPE_STRV,
2473                                     G_PARAM_READABLE |
2474                                     G_PARAM_STATIC_STRINGS);
2475         obj_properties[PROP_SEARCHES] =
2476                 g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "",
2477                                     G_TYPE_STRV,
2478                                     G_PARAM_READABLE |
2479                                     G_PARAM_STATIC_STRINGS);
2480         obj_properties[PROP_DNS_OPTIONS] =
2481                  g_param_spec_boxed (NM_IP4_CONFIG_DNS_OPTIONS, "", "",
2482                                      G_TYPE_STRV,
2483                                      G_PARAM_READABLE |
2484                                      G_PARAM_STATIC_STRINGS);
2485
2486         obj_properties[PROP_WINS_SERVERS] =
2487                 g_param_spec_variant (NM_IP4_CONFIG_WINS_SERVERS, "", "",
2488                                       G_VARIANT_TYPE ("au"),
2489                                       NULL,
2490                                       G_PARAM_READABLE |
2491                                       G_PARAM_STATIC_STRINGS);
2492
2493         g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
2494
2495         nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
2496                                                 NMDBUS_TYPE_IP4_CONFIG_SKELETON,
2497                                                 NULL);
2498 }