1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
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.
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.
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.
18 * Copyright (C) 2005 - 2013 Red Hat, Inc.
19 * Copyright (C) 2006 - 2008 Novell, Inc.
22 #include "nm-default.h"
24 #include "nm-ip6-config.h"
27 #include <arpa/inet.h>
30 #include "nm-platform.h"
31 #include "nm-route-manager.h"
32 #include "nm-core-internal.h"
33 #include "NetworkManagerUtils.h"
35 #include "nmdbus-ip6-config.h"
37 G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT)
39 #define NM_IP6_CONFIG_GET_PRIVATE(o) ((o)->priv)
41 typedef struct _NMIP6ConfigPrivate {
42 gboolean never_default;
43 struct in6_addr gateway;
49 GPtrArray *dns_options;
56 NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config,
70 nm_ip6_config_new (int ifindex)
72 g_return_val_if_fail (ifindex >= -1, NULL);
73 return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG,
74 NM_IP6_CONFIG_IFINDEX, ifindex,
79 nm_ip6_config_new_cloned (const NMIP6Config *src)
83 g_return_val_if_fail (NM_IS_IP6_CONFIG (src), NULL);
85 new = nm_ip6_config_new (nm_ip6_config_get_ifindex (src));
86 nm_ip6_config_replace (new, src, NULL);
91 nm_ip6_config_get_ifindex (const NMIP6Config *config)
93 return NM_IP6_CONFIG_GET_PRIVATE (config)->ifindex;
96 /******************************************************************/
99 same_prefix (const struct in6_addr *address1, const struct in6_addr *address2, int plen)
101 const guint8 *bytes1 = (const guint8 *) address1;
102 const guint8 *bytes2 = (const guint8 *) address2;
103 int nbytes = plen / 8;
104 int nbits = plen % 8;
105 int masked1 = bytes1[nbytes] >> (8 - nbits);
106 int masked2 = bytes2[nbytes] >> (8 - nbits);
108 if (nbytes && memcmp (bytes1, bytes2, nbytes))
111 return masked1 == masked2;
114 /******************************************************************/
117 * nm_ip6_config_capture_resolv_conf():
118 * @nameservers: array of struct in6_addr
119 * @rc_contents: the contents of a resolv.conf or %NULL to read /etc/resolv.conf
121 * Reads all resolv.conf IPv6 nameservers and adds them to @nameservers.
123 * Returns: %TRUE if nameservers were added, %FALSE if @nameservers is unchanged
126 nm_ip6_config_capture_resolv_conf (GArray *nameservers,
127 GPtrArray *dns_options,
128 const char *rc_contents)
130 GPtrArray *read_ns, *read_options;
132 gboolean changed = FALSE;
134 g_return_val_if_fail (nameservers != NULL, FALSE);
136 read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents);
140 for (i = 0; i < read_ns->len; i++) {
141 const char *s = g_ptr_array_index (read_ns, i);
142 struct in6_addr ns = IN6ADDR_ANY_INIT;
144 if (!inet_pton (AF_INET6, s, (void *) &ns) || IN6_IS_ADDR_UNSPECIFIED (&ns))
147 /* Ignore duplicates */
148 for (j = 0; j < nameservers->len; j++) {
149 struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, j);
151 if (IN6_ARE_ADDR_EQUAL (t, &ns))
155 if (j == nameservers->len) {
156 g_array_append_val (nameservers, ns);
160 g_ptr_array_unref (read_ns);
163 read_options = nm_utils_read_resolv_conf_dns_options (rc_contents);
167 for (i = 0; i < read_options->len; i++) {
168 const char *s = g_ptr_array_index (read_options, i);
170 if (_nm_utils_dns_option_validate (s, NULL, NULL, TRUE, _nm_utils_dns_option_descs) &&
171 _nm_utils_dns_option_find_idx (dns_options, s) < 0) {
172 g_ptr_array_add (dns_options, g_strdup (s));
176 g_ptr_array_unref (read_options);
183 addresses_are_duplicate (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
185 return IN6_ARE_ADDR_EQUAL (&a->address, &b->address);
189 routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_gateway_and_metric)
191 return IN6_ARE_ADDR_EQUAL (&a->network, &b->network) && a->plen == b->plen &&
192 ( !consider_gateway_and_metric
193 || ( IN6_ARE_ADDR_EQUAL (&a->gateway, &b->gateway)
194 && nm_utils_ip6_route_metric_normalize (a->metric) == nm_utils_ip6_route_metric_normalize (b->metric)));
198 _addresses_sort_cmp_get_prio (const struct in6_addr *addr)
200 if (IN6_IS_ADDR_V4MAPPED (addr))
202 if (IN6_IS_ADDR_V4COMPAT (addr))
204 if (IN6_IS_ADDR_UNSPECIFIED (addr))
206 if (IN6_IS_ADDR_LOOPBACK (addr))
208 if (IN6_IS_ADDR_LINKLOCAL (addr))
210 if (IN6_IS_ADDR_SITELOCAL (addr))
216 _addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
219 gboolean perm1, perm2, tent1, tent2;
220 gboolean ipv6_privacy1, ipv6_privacy2;
221 const NMPlatformIP6Address *a1 = a, *a2 = b;
223 /* tentative addresses are always sorted back... */
224 /* sort tentative addresses after non-tentative. */
225 tent1 = (a1->n_ifa_flags & IFA_F_TENTATIVE);
226 tent2 = (a2->n_ifa_flags & IFA_F_TENTATIVE);
228 return tent1 ? 1 : -1;
230 /* Sort by address type. For example link local will
231 * be sorted *after* site local or global. */
232 p1 = _addresses_sort_cmp_get_prio (&a1->address);
233 p2 = _addresses_sort_cmp_get_prio (&a2->address);
235 return p1 > p2 ? -1 : 1;
237 ipv6_privacy1 = !!(a1->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY));
238 ipv6_privacy2 = !!(a2->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY));
239 if (ipv6_privacy1 || ipv6_privacy2) {
240 gboolean prefer_temp = ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR;
241 gboolean public1 = TRUE, public2 = TRUE;
244 if (a1->n_ifa_flags & IFA_F_TEMPORARY)
245 public1 = prefer_temp;
247 public1 = !prefer_temp;
250 if (a2->n_ifa_flags & IFA_F_TEMPORARY)
251 public2 = prefer_temp;
253 public2 = !prefer_temp;
256 if (public1 != public2)
257 return public1 ? -1 : 1;
260 /* Sort the addresses based on their source. */
261 if (a1->source != a2->source)
262 return a1->source > a2->source ? -1 : 1;
264 /* sort permanent addresses before non-permanent. */
265 perm1 = (a1->n_ifa_flags & IFA_F_PERMANENT);
266 perm2 = (a2->n_ifa_flags & IFA_F_PERMANENT);
268 return perm1 ? -1 : 1;
270 /* finally sort addresses lexically */
271 c = memcmp (&a1->address, &a2->address, sizeof (a2->address));
272 return c != 0 ? c : memcmp (a1, a2, sizeof (*a1));
276 nm_ip6_config_addresses_sort (NMIP6Config *self, NMSettingIP6ConfigPrivacy use_temporary)
278 NMIP6ConfigPrivate *priv;
280 char *data_pre = NULL;
283 g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE);
285 priv = NM_IP6_CONFIG_GET_PRIVATE (self);
286 if (priv->addresses->len > 1) {
287 data_len = priv->addresses->len * g_array_get_element_size (priv->addresses);
288 data_pre = g_new (char, data_len);
289 memcpy (data_pre, priv->addresses->data, data_len);
291 g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary));
293 changed = memcmp (data_pre, priv->addresses->data, data_len) != 0;
297 _notify (self, PROP_ADDRESS_DATA);
298 _notify (self, PROP_ADDRESSES);
306 nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary)
309 NMIP6ConfigPrivate *priv;
311 guint32 lowest_metric = G_MAXUINT32;
312 struct in6_addr old_gateway = IN6ADDR_ANY_INIT;
313 gboolean has_gateway = FALSE;
314 gboolean notify_nameservers = FALSE;
316 /* Slaves have no IP configuration */
317 if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
320 config = nm_ip6_config_new (ifindex);
321 priv = NM_IP6_CONFIG_GET_PRIVATE (config);
323 g_array_unref (priv->addresses);
324 g_array_unref (priv->routes);
326 priv->addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
327 priv->routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
329 /* Extract gateway from default route */
330 old_gateway = priv->gateway;
331 for (i = 0; i < priv->routes->len; ) {
332 const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i);
334 if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
335 if (route->metric < lowest_metric) {
336 priv->gateway = route->gateway;
337 lowest_metric = route->metric;
340 /* Remove the default route from the list */
341 g_array_remove_index_fast (priv->routes, i);
347 /* we detect the route metric based on the default route. All non-default
348 * routes have their route metrics explicitly set. */
349 priv->route_metric = has_gateway ? (gint64) lowest_metric : (gint64) -1;
351 /* If there is a host route to the gateway, ignore that route. It is
352 * automatically added by NetworkManager when needed.
355 for (i = 0; i < priv->routes->len; i++) {
356 const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i);
358 if ( route->plen == 128
359 && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway)
360 && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) {
361 g_array_remove_index (priv->routes, i);
367 /* If the interface has the default route, and has IPv6 addresses, capture
368 * nameservers from /etc/resolv.conf.
370 if (priv->addresses->len && has_gateway && capture_resolv_conf)
371 notify_nameservers = nm_ip6_config_capture_resolv_conf (priv->nameservers,
375 g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary));
377 /* actually, nobody should be connected to the signal, just to be sure, notify */
378 if (notify_nameservers)
379 _notify (config, PROP_NAMESERVERS);
380 _notify (config, PROP_ADDRESS_DATA);
381 _notify (config, PROP_ADDRESSES);
382 _notify (config, PROP_ROUTE_DATA);
383 _notify (config, PROP_ROUTES);
384 if (!IN6_ARE_ADDR_EQUAL (&priv->gateway, &old_gateway))
385 _notify (config, PROP_GATEWAY);
391 nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync)
393 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
397 g_return_val_if_fail (ifindex > 0, FALSE);
398 g_return_val_if_fail (config != NULL, FALSE);
401 nm_platform_ip6_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, TRUE);
405 int count = nm_ip6_config_get_num_routes (config);
406 GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), count);
407 const NMPlatformIP6Route *route;
409 for (i = 0; i < count; i++) {
410 route = nm_ip6_config_get_route (config, i);
412 /* Don't add the route if it's more specific than one of the subnets
413 * the device already has an IP address on.
415 if ( IN6_IS_ADDR_UNSPECIFIED (&route->gateway)
416 && nm_ip6_config_destination_is_direct (config, &route->network, route->plen))
419 g_array_append_vals (routes, route, 1);
422 success = nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, routes_full_sync);
423 g_array_unref (routes);
430 nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
432 NMIP6ConfigPrivate *priv;
433 guint naddresses, nroutes, nnameservers, nsearches;
434 const char *gateway_str;
440 g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
442 priv = NM_IP6_CONFIG_GET_PRIVATE (config);
444 naddresses = nm_setting_ip_config_get_num_addresses (setting);
445 nroutes = nm_setting_ip_config_get_num_routes (setting);
446 nnameservers = nm_setting_ip_config_get_num_dns (setting);
447 nsearches = nm_setting_ip_config_get_num_dns_searches (setting);
449 g_object_freeze_notify (G_OBJECT (config));
452 if (nm_setting_ip_config_get_never_default (setting))
453 nm_ip6_config_set_never_default (config, TRUE);
454 else if (nm_setting_ip_config_get_ignore_auto_routes (setting))
455 nm_ip6_config_set_never_default (config, FALSE);
456 gateway_str = nm_setting_ip_config_get_gateway (setting);
458 struct in6_addr gateway;
460 inet_pton (AF_INET6, gateway_str, &gateway);
461 nm_ip6_config_set_gateway (config, &gateway);
464 if (priv->route_metric == -1)
465 priv->route_metric = nm_setting_ip_config_get_route_metric (setting);
468 for (i = 0; i < naddresses; i++) {
469 NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
470 NMPlatformIP6Address address;
472 memset (&address, 0, sizeof (address));
473 nm_ip_address_get_address_binary (s_addr, &address.address);
474 address.plen = nm_ip_address_get_prefix (s_addr);
475 address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
476 address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
477 address.source = NM_IP_CONFIG_SOURCE_USER;
479 nm_ip6_config_add_address (config, &address);
483 if (nm_setting_ip_config_get_ignore_auto_routes (setting))
484 nm_ip6_config_reset_routes (config);
485 for (i = 0; i < nroutes; i++) {
486 NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
487 NMPlatformIP6Route route;
489 memset (&route, 0, sizeof (route));
490 nm_ip_route_get_dest_binary (s_route, &route.network);
491 route.plen = nm_ip_route_get_prefix (s_route);
492 nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
493 if (nm_ip_route_get_metric (s_route) == -1)
494 route.metric = default_route_metric;
496 route.metric = nm_ip_route_get_metric (s_route);
497 route.source = NM_IP_CONFIG_SOURCE_USER;
499 g_assert (route.plen > 0);
501 nm_ip6_config_add_route (config, &route);
505 if (nm_setting_ip_config_get_ignore_auto_dns (setting)) {
506 nm_ip6_config_reset_nameservers (config);
507 nm_ip6_config_reset_domains (config);
508 nm_ip6_config_reset_searches (config);
510 for (i = 0; i < nnameservers; i++) {
513 if (inet_pton (AF_INET6, nm_setting_ip_config_get_dns (setting, i), &ip) == 1)
514 nm_ip6_config_add_nameserver (config, &ip);
516 for (i = 0; i < nsearches; i++)
517 nm_ip6_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i));
520 while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) {
521 nm_ip6_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i));
525 g_object_thaw_notify (G_OBJECT (config));
529 nm_ip6_config_create_setting (const NMIP6Config *config)
531 NMSettingIPConfig *s_ip6;
532 const struct in6_addr *gateway;
533 guint naddresses, nroutes, nnameservers, nsearches, noptions;
534 const char *method = NULL;
538 s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
542 NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
544 return NM_SETTING (s_ip6);
547 gateway = nm_ip6_config_get_gateway (config);
548 naddresses = nm_ip6_config_get_num_addresses (config);
549 nroutes = nm_ip6_config_get_num_routes (config);
550 nnameservers = nm_ip6_config_get_num_nameservers (config);
551 nsearches = nm_ip6_config_get_num_searches (config);
552 noptions = nm_ip6_config_get_num_dns_options (config);
553 route_metric = nm_ip6_config_get_route_metric (config);
556 for (i = 0; i < naddresses; i++) {
557 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
560 /* Ignore link-local address. */
561 if (IN6_IS_ADDR_LINKLOCAL (&address->address)) {
563 method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
567 /* Detect dynamic address */
568 if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
569 method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
573 /* Static address found. */
574 if (!method || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
575 method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
577 s_addr = nm_ip_address_new_binary (AF_INET6, &address->address, address->plen, NULL);
578 nm_setting_ip_config_add_address (s_ip6, s_addr);
579 nm_ip_address_unref (s_addr);
584 && nm_setting_ip_config_get_num_addresses (s_ip6) > 0) {
586 NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet6_ntop (gateway, NULL),
590 /* Use 'ignore' if the method wasn't previously set */
592 method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
595 NM_SETTING_IP_CONFIG_METHOD, method,
596 NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric,
600 for (i = 0; i < nroutes; i++) {
601 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
604 /* Ignore link-local route. */
605 if (IN6_IS_ADDR_LINKLOCAL (&route->network))
608 /* Ignore default route. */
612 /* Ignore routes provided by external sources */
613 if (route->source != NM_IP_CONFIG_SOURCE_USER)
616 s_route = nm_ip_route_new_binary (AF_INET6,
617 &route->network, route->plen,
618 &route->gateway, route->metric,
620 nm_setting_ip_config_add_route (s_ip6, s_route);
621 nm_ip_route_unref (s_route);
625 for (i = 0; i < nnameservers; i++) {
626 const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (config, i);
628 nm_setting_ip_config_add_dns (s_ip6, nm_utils_inet6_ntop (nameserver, NULL));
630 for (i = 0; i < nsearches; i++) {
631 const char *search = nm_ip6_config_get_search (config, i);
633 nm_setting_ip_config_add_dns_search (s_ip6, search);
635 for (i = 0; i < noptions; i++) {
636 const char *option = nm_ip6_config_get_dns_option (config, i);
638 nm_setting_ip_config_add_dns_option (s_ip6, option);
642 return NM_SETTING (s_ip6);
645 /******************************************************************/
648 nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags)
650 NMIP6ConfigPrivate *dst_priv, *src_priv;
653 g_return_if_fail (src != NULL);
654 g_return_if_fail (dst != NULL);
656 dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
657 src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
659 g_object_freeze_notify (G_OBJECT (dst));
662 for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++)
663 nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
666 if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
667 for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++)
668 nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
671 /* default gateway */
672 if (nm_ip6_config_get_gateway (src))
673 nm_ip6_config_set_gateway (dst, nm_ip6_config_get_gateway (src));
676 if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
677 for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
678 nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
681 if (dst_priv->route_metric == -1)
682 dst_priv->route_metric = src_priv->route_metric;
683 else if (src_priv->route_metric != -1)
684 dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric);
687 if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
688 for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
689 nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
693 if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
694 for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
695 nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
699 if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
700 for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++)
701 nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
704 if (nm_ip6_config_get_mss (src))
705 nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
707 g_object_thaw_notify (G_OBJECT (dst));
711 nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6_addr *network, int plen)
713 int num = nm_ip6_config_get_num_addresses (config);
716 for (i = 0; i < num; i++) {
717 const NMPlatformIP6Address *item = nm_ip6_config_get_address (config, i);
719 if (item->plen <= plen && same_prefix (&item->address, network, item->plen) &&
720 !(item->n_ifa_flags & IFA_F_NOPREFIXROUTE))
727 /*******************************************************************************/
730 _addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr)
732 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
735 for (i = 0; i < priv->addresses->len; i++) {
736 const NMPlatformIP6Address *a = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
738 if (addresses_are_duplicate (a, addr))
745 _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns)
747 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
750 for (i = 0; i < priv->nameservers->len; i++) {
751 const struct in6_addr *n = &g_array_index (priv->nameservers, struct in6_addr, i);
753 if (IN6_ARE_ADDR_EQUAL (ns, n))
760 _routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route)
762 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
765 for (i = 0; i < priv->routes->len; i++) {
766 const NMPlatformIP6Route *r = &g_array_index (priv->routes, NMPlatformIP6Route, i);
768 if (routes_are_duplicate (route, r, FALSE))
775 _domains_get_index (const NMIP6Config *self, const char *domain)
777 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
780 for (i = 0; i < priv->domains->len; i++) {
781 const char *d = g_ptr_array_index (priv->domains, i);
783 if (g_strcmp0 (domain, d) == 0)
790 _searches_get_index (const NMIP6Config *self, const char *search)
792 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
795 for (i = 0; i < priv->searches->len; i++) {
796 const char *s = g_ptr_array_index (priv->searches, i);
798 if (g_strcmp0 (search, s) == 0)
805 _dns_options_get_index (const NMIP6Config *self, const char *option)
807 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
810 for (i = 0; i < priv->dns_options->len; i++) {
811 const char *s = g_ptr_array_index (priv->dns_options, i);
813 if (g_strcmp0 (option, s) == 0)
819 /*******************************************************************************/
822 * nm_ip6_config_subtract:
823 * @dst: config from which to remove everything in @src
824 * @src: config to remove from @dst
826 * Removes everything in @src from @dst.
829 nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src)
833 const struct in6_addr *dst_tmp, *src_tmp;
835 g_return_if_fail (src != NULL);
836 g_return_if_fail (dst != NULL);
838 g_object_freeze_notify (G_OBJECT (dst));
841 for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) {
842 idx = _addresses_get_index (dst, nm_ip6_config_get_address (src, i));
844 nm_ip6_config_del_address (dst, idx);
848 for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) {
849 idx = _nameservers_get_index (dst, nm_ip6_config_get_nameserver (src, i));
851 nm_ip6_config_del_nameserver (dst, idx);
854 /* default gateway */
855 src_tmp = nm_ip6_config_get_gateway (src);
856 dst_tmp = nm_ip6_config_get_gateway (dst);
857 if (src_tmp && dst_tmp && IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp))
858 nm_ip6_config_set_gateway (dst, NULL);
860 if (!nm_ip6_config_get_num_addresses (dst))
861 nm_ip6_config_set_gateway (dst, NULL);
863 /* ignore route_metric */
866 for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) {
867 idx = _routes_get_index (dst, nm_ip6_config_get_route (src, i));
869 nm_ip6_config_del_route (dst, idx);
873 for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) {
874 idx = _domains_get_index (dst, nm_ip6_config_get_domain (src, i));
876 nm_ip6_config_del_domain (dst, idx);
880 for (i = 0; i < nm_ip6_config_get_num_searches (src); i++) {
881 idx = _searches_get_index (dst, nm_ip6_config_get_search (src, i));
883 nm_ip6_config_del_search (dst, idx);
887 for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++) {
888 idx = _dns_options_get_index (dst, nm_ip6_config_get_dns_option (src, i));
890 nm_ip6_config_del_dns_option (dst, idx);
893 if (nm_ip6_config_get_mss (src) == nm_ip6_config_get_mss (dst))
894 nm_ip6_config_set_mss (dst, 0);
896 g_object_thaw_notify (G_OBJECT (dst));
900 nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src)
904 const struct in6_addr *dst_tmp, *src_tmp;
906 g_return_if_fail (src != NULL);
907 g_return_if_fail (dst != NULL);
909 g_object_freeze_notify (G_OBJECT (dst));
912 for (i = 0; i < nm_ip6_config_get_num_addresses (dst); ) {
913 idx = _addresses_get_index (src, nm_ip6_config_get_address (dst, i));
915 nm_ip6_config_del_address (dst, i);
920 /* ignore route_metric */
921 /* ignore nameservers */
923 /* default gateway */
924 dst_tmp = nm_ip6_config_get_gateway (dst);
926 src_tmp = nm_ip6_config_get_gateway (src);
927 if ( !nm_ip6_config_get_num_addresses (dst)
929 || !IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp))
930 nm_ip6_config_set_gateway (dst, NULL);
934 for (i = 0; i < nm_ip6_config_get_num_routes (dst); ) {
935 idx = _routes_get_index (src, nm_ip6_config_get_route (dst, i));
937 nm_ip6_config_del_route (dst, i);
943 /* ignore dns searches */
944 /* ignome dns options */
946 g_object_thaw_notify (G_OBJECT (dst));
950 * nm_ip6_config_replace:
951 * @dst: config which will be replaced with everything in @src
952 * @src: config to copy over to @dst
953 * @relevant_changes: return whether there are changes to the
954 * destination object that are relevant. This is equal to
955 * nm_ip6_config_equal() showing any difference.
957 * Replaces everything in @dst with @src so that the two configurations
958 * contain the same content -- with the exception of the dbus path.
960 * Returns: whether the @dst instance changed in any way (including minor changes,
961 * that are not signaled by the output parameter @relevant_changes).
964 nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes)
967 gboolean config_equal;
969 gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
971 NMIP6ConfigPrivate *dst_priv, *src_priv;
972 const NMPlatformIP6Address *dst_addr, *src_addr;
973 const NMPlatformIP6Route *dst_route, *src_route;
975 g_return_val_if_fail (NM_IS_IP6_CONFIG (src), FALSE);
976 g_return_val_if_fail (NM_IS_IP6_CONFIG (dst), FALSE);
977 g_return_val_if_fail (src != dst, FALSE);
980 config_equal = nm_ip6_config_equal (dst, src);
983 dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
984 src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
986 g_object_freeze_notify (G_OBJECT (dst));
989 if (src_priv->ifindex != dst_priv->ifindex) {
990 dst_priv->ifindex = src_priv->ifindex;
991 has_minor_changes = TRUE;
995 if (src_priv->never_default != dst_priv->never_default) {
996 dst_priv->never_default = src_priv->never_default;
997 has_minor_changes = TRUE;
1000 /* default gateway */
1001 if (!IN6_ARE_ADDR_EQUAL (&src_priv->gateway, &dst_priv->gateway)) {
1002 nm_ip6_config_set_gateway (dst, &src_priv->gateway);
1003 has_relevant_changes = TRUE;
1006 if (src_priv->route_metric != dst_priv->route_metric) {
1007 dst_priv->route_metric = src_priv->route_metric;
1008 has_minor_changes = TRUE;
1012 num = nm_ip6_config_get_num_addresses (src);
1013 are_equal = num == nm_ip6_config_get_num_addresses (dst);
1015 for (i = 0; i < num; i++ ) {
1016 if (nm_platform_ip6_address_cmp (src_addr = nm_ip6_config_get_address (src, i),
1017 dst_addr = nm_ip6_config_get_address (dst, i))) {
1019 if ( !addresses_are_duplicate (src_addr, dst_addr)
1020 || src_addr->plen != dst_addr->plen
1021 || !IN6_ARE_ADDR_EQUAL (nm_platform_ip6_address_get_peer (src_addr),
1022 nm_platform_ip6_address_get_peer (dst_addr))) {
1023 has_relevant_changes = TRUE;
1029 has_relevant_changes = TRUE;
1031 nm_ip6_config_reset_addresses (dst);
1032 for (i = 0; i < num; i++)
1033 nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
1034 has_minor_changes = TRUE;
1038 num = nm_ip6_config_get_num_routes (src);
1039 are_equal = num == nm_ip6_config_get_num_routes (dst);
1041 for (i = 0; i < num; i++ ) {
1042 if (nm_platform_ip6_route_cmp (src_route = nm_ip6_config_get_route (src, i),
1043 dst_route = nm_ip6_config_get_route (dst, i))) {
1045 if (!routes_are_duplicate (src_route, dst_route, TRUE)) {
1046 has_relevant_changes = TRUE;
1052 has_relevant_changes = TRUE;
1054 nm_ip6_config_reset_routes (dst);
1055 for (i = 0; i < num; i++)
1056 nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
1057 has_minor_changes = TRUE;
1061 num = nm_ip6_config_get_num_nameservers (src);
1062 are_equal = num == nm_ip6_config_get_num_nameservers (dst);
1064 for (i = 0; i < num; i++ ) {
1065 if (!IN6_ARE_ADDR_EQUAL (nm_ip6_config_get_nameserver (src, i),
1066 nm_ip6_config_get_nameserver (dst, i))) {
1073 nm_ip6_config_reset_nameservers (dst);
1074 for (i = 0; i < num; i++)
1075 nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
1076 has_relevant_changes = TRUE;
1080 num = nm_ip6_config_get_num_domains (src);
1081 are_equal = num == nm_ip6_config_get_num_domains (dst);
1083 for (i = 0; i < num; i++ ) {
1084 if (g_strcmp0 (nm_ip6_config_get_domain (src, i),
1085 nm_ip6_config_get_domain (dst, i))) {
1092 nm_ip6_config_reset_domains (dst);
1093 for (i = 0; i < num; i++)
1094 nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
1095 has_relevant_changes = TRUE;
1099 num = nm_ip6_config_get_num_searches (src);
1100 are_equal = num == nm_ip6_config_get_num_searches (dst);
1102 for (i = 0; i < num; i++ ) {
1103 if (g_strcmp0 (nm_ip6_config_get_search (src, i),
1104 nm_ip6_config_get_search (dst, i))) {
1111 nm_ip6_config_reset_searches (dst);
1112 for (i = 0; i < num; i++)
1113 nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
1114 has_relevant_changes = TRUE;
1118 num = nm_ip6_config_get_num_dns_options (src);
1119 are_equal = num == nm_ip6_config_get_num_dns_options (dst);
1121 for (i = 0; i < num; i++ ) {
1122 if (g_strcmp0 (nm_ip6_config_get_dns_option (src, i),
1123 nm_ip6_config_get_dns_option (dst, i))) {
1130 nm_ip6_config_reset_dns_options (dst);
1131 for (i = 0; i < num; i++)
1132 nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
1133 has_relevant_changes = TRUE;
1137 if (src_priv->mss != dst_priv->mss) {
1138 nm_ip6_config_set_mss (dst, src_priv->mss);
1139 has_minor_changes = TRUE;
1143 /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes
1144 * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */
1145 nm_assert (config_equal == !has_relevant_changes);
1148 g_object_thaw_notify (G_OBJECT (dst));
1150 if (relevant_changes)
1151 *relevant_changes = has_relevant_changes;
1153 return has_relevant_changes || has_minor_changes;
1157 nm_ip6_config_dump (const NMIP6Config *config, const char *detail)
1159 const struct in6_addr *tmp;
1163 g_return_if_fail (config != NULL);
1165 g_message ("--------- NMIP6Config %p (%s)", config, detail);
1167 str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config));
1169 g_message (" path: %s", str);
1172 for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++)
1173 g_message (" a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0));
1175 /* default gateway */
1176 tmp = nm_ip6_config_get_gateway (config);
1178 g_message (" gw: %s", nm_utils_inet6_ntop (tmp, NULL));
1181 for (i = 0; i < nm_ip6_config_get_num_nameservers (config); i++) {
1182 tmp = nm_ip6_config_get_nameserver (config, i);
1183 g_message (" ns: %s", nm_utils_inet6_ntop (tmp, NULL));
1187 for (i = 0; i < nm_ip6_config_get_num_routes (config); i++)
1188 g_message (" rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i), NULL, 0));
1191 for (i = 0; i < nm_ip6_config_get_num_domains (config); i++)
1192 g_message (" domain: %s", nm_ip6_config_get_domain (config, i));
1195 for (i = 0; i < nm_ip6_config_get_num_searches (config); i++)
1196 g_message (" search: %s", nm_ip6_config_get_search (config, i));
1199 for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++)
1200 g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (config, i));
1202 g_message (" mss: %"G_GUINT32_FORMAT, nm_ip6_config_get_mss (config));
1203 g_message (" n-dflt: %d", nm_ip6_config_get_never_default (config));
1206 /******************************************************************/
1209 nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default)
1211 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1213 priv->never_default = !!never_default;
1217 nm_ip6_config_get_never_default (const NMIP6Config *config)
1219 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1221 return priv->never_default;
1225 nm_ip6_config_set_gateway (NMIP6Config *config, const struct in6_addr *gateway)
1227 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1230 if (IN6_ARE_ADDR_EQUAL (&priv->gateway, gateway))
1232 priv->gateway = *gateway;
1234 if (IN6_IS_ADDR_UNSPECIFIED (&priv->gateway))
1236 memset (&priv->gateway, 0, sizeof (priv->gateway));
1238 _notify (config, PROP_GATEWAY);
1241 const struct in6_addr *
1242 nm_ip6_config_get_gateway (const NMIP6Config *config)
1244 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1246 return IN6_IS_ADDR_UNSPECIFIED (&priv->gateway) ? NULL : &priv->gateway;
1250 nm_ip6_config_get_route_metric (const NMIP6Config *config)
1252 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1254 return priv->route_metric;
1257 /******************************************************************/
1260 nm_ip6_config_reset_addresses (NMIP6Config *config)
1262 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1264 if (priv->addresses->len != 0) {
1265 g_array_set_size (priv->addresses, 0);
1266 _notify (config, PROP_ADDRESS_DATA);
1267 _notify (config, PROP_ADDRESSES);
1272 * nm_ip6_config_add_address:
1273 * @config: the #NMIP6Config
1274 * @new: the new address to add to @config
1276 * Adds the new address to @config. If an address with the same basic properties
1277 * (address, prefix) already exists in @config, it is overwritten with the
1278 * lifetime and preferred of @new. The source is also overwritten by the source
1279 * from @new if that source is higher priority.
1282 nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new)
1284 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1285 NMPlatformIP6Address item_old;
1288 g_return_if_fail (new != NULL);
1290 for (i = 0; i < priv->addresses->len; i++ ) {
1291 NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1293 if (addresses_are_duplicate (item, new)) {
1294 if (nm_platform_ip6_address_cmp (item, new) == 0)
1297 /* remember the old values. */
1299 /* Copy over old item to get new lifetime, timestamp, preferred */
1302 /* But restore highest priority source */
1303 item->source = MAX (item_old.source, new->source);
1305 /* for addresses that we read from the kernel, we keep the timestamps as defined
1306 * by the previous source (item_old). The reason is, that the other source configured the lifetimes
1307 * with "what should be" and the kernel values are "what turned out after configuring it".
1309 * For other sources, the longer lifetime wins. */
1310 if ( (new->source == NM_IP_CONFIG_SOURCE_KERNEL && new->source != item_old.source)
1311 || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) {
1312 item->timestamp = item_old.timestamp;
1313 item->lifetime = item_old.lifetime;
1314 item->preferred = item_old.preferred;
1316 if (nm_platform_ip6_address_cmp (&item_old, item) == 0)
1322 g_array_append_val (priv->addresses, *new);
1324 _notify (config, PROP_ADDRESS_DATA);
1325 _notify (config, PROP_ADDRESSES);
1329 nm_ip6_config_del_address (NMIP6Config *config, guint i)
1331 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1333 g_return_if_fail (i < priv->addresses->len);
1335 g_array_remove_index (priv->addresses, i);
1336 _notify (config, PROP_ADDRESS_DATA);
1337 _notify (config, PROP_ADDRESSES);
1341 nm_ip6_config_get_num_addresses (const NMIP6Config *config)
1343 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1345 return priv->addresses->len;
1348 const NMPlatformIP6Address *
1349 nm_ip6_config_get_address (const NMIP6Config *config, guint i)
1351 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1353 return &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1357 nm_ip6_config_address_exists (const NMIP6Config *config,
1358 const NMPlatformIP6Address *needle)
1360 return _addresses_get_index (config, needle) >= 0;
1363 const NMPlatformIP6Address *
1364 nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal)
1366 NMIP6ConfigPrivate *priv;
1369 g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
1371 priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1373 linklocal = !!linklocal;
1375 for (i = 0; i < priv->addresses->len; i++) {
1376 const NMPlatformIP6Address *addr = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1378 if ( ((!!IN6_IS_ADDR_LINKLOCAL (&addr->address)) == linklocal)
1379 && !(addr->n_ifa_flags & IFA_F_TENTATIVE))
1386 /******************************************************************/
1389 nm_ip6_config_reset_routes (NMIP6Config *config)
1391 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1393 if (priv->routes->len != 0) {
1394 g_array_set_size (priv->routes, 0);
1395 _notify (config, PROP_ROUTE_DATA);
1396 _notify (config, PROP_ROUTES);
1401 * nm_ip6_config_add_route:
1402 * @config: the #NMIP6Config
1403 * @new: the new route to add to @config
1405 * Adds the new route to @config. If a route with the same basic properties
1406 * (network, prefix) already exists in @config, it is overwritten including the
1407 * gateway and metric of @new. The source is also overwritten by the source
1408 * from @new if that source is higher priority.
1411 nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
1413 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1414 NMIPConfigSource old_source;
1417 g_return_if_fail (new != NULL);
1418 g_return_if_fail (new->plen > 0);
1419 g_assert (priv->ifindex);
1421 for (i = 0; i < priv->routes->len; i++ ) {
1422 NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i);
1424 if (routes_are_duplicate (item, new, FALSE)) {
1425 if (nm_platform_ip6_route_cmp (item, new) == 0)
1427 old_source = item->source;
1429 /* Restore highest priority source */
1430 item->source = MAX (old_source, new->source);
1431 item->ifindex = priv->ifindex;
1436 g_array_append_val (priv->routes, *new);
1437 g_array_index (priv->routes, NMPlatformIP6Route, priv->routes->len - 1).ifindex = priv->ifindex;
1439 _notify (config, PROP_ROUTE_DATA);
1440 _notify (config, PROP_ROUTES);
1444 nm_ip6_config_del_route (NMIP6Config *config, guint i)
1446 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1448 g_return_if_fail (i < priv->routes->len);
1450 g_array_remove_index (priv->routes, i);
1451 _notify (config, PROP_ROUTE_DATA);
1452 _notify (config, PROP_ROUTES);
1456 nm_ip6_config_get_num_routes (const NMIP6Config *config)
1458 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1460 return priv->routes->len;
1463 const NMPlatformIP6Route *
1464 nm_ip6_config_get_route (const NMIP6Config *config, guint i)
1466 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1468 return &g_array_index (priv->routes, NMPlatformIP6Route, i);
1471 const NMPlatformIP6Route *
1472 nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host)
1474 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1476 struct in6_addr network2, host2;
1477 NMPlatformIP6Route *best_route = NULL;
1479 g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL);
1481 for (i = 0; i < priv->routes->len; i++) {
1482 NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i);
1484 if (!IN6_IS_ADDR_UNSPECIFIED (&item->gateway))
1487 if (best_route && best_route->plen > item->plen)
1490 nm_utils_ip6_address_clear_host_address (&host2, host, item->plen);
1491 nm_utils_ip6_address_clear_host_address (&network2, &item->network, item->plen);
1493 if (!IN6_ARE_ADDR_EQUAL (&network2, &host2))
1497 nm_utils_ip6_route_metric_normalize (best_route->metric) <= nm_utils_ip6_route_metric_normalize (item->metric))
1506 const NMPlatformIP6Address *
1507 nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_addr *host)
1509 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1511 NMPlatformIP6Address *subnet = NULL;
1512 struct in6_addr subnet2, host2;
1514 g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL);
1516 for (i = 0; i < priv->addresses->len; i++) {
1517 NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1519 if (subnet && subnet->plen >= item->plen)
1522 nm_utils_ip6_address_clear_host_address (&host2, host, item->plen);
1523 nm_utils_ip6_address_clear_host_address (&subnet2, &item->address, item->plen);
1525 if (IN6_ARE_ADDR_EQUAL (&subnet2, &host2))
1533 /******************************************************************/
1536 nm_ip6_config_reset_nameservers (NMIP6Config *config)
1538 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1540 if (priv->nameservers->len != 0) {
1541 g_array_set_size (priv->nameservers, 0);
1542 _notify (config, PROP_NAMESERVERS);
1547 nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *new)
1549 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1552 g_return_if_fail (new != NULL);
1554 for (i = 0; i < priv->nameservers->len; i++)
1555 if (IN6_ARE_ADDR_EQUAL (new, &g_array_index (priv->nameservers, struct in6_addr, i)))
1558 g_array_append_val (priv->nameservers, *new);
1559 _notify (config, PROP_NAMESERVERS);
1563 nm_ip6_config_del_nameserver (NMIP6Config *config, guint i)
1565 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1567 g_return_if_fail (i < priv->nameservers->len);
1569 g_array_remove_index (priv->nameservers, i);
1570 _notify (config, PROP_NAMESERVERS);
1574 nm_ip6_config_get_num_nameservers (const NMIP6Config *config)
1576 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1578 return priv->nameservers->len;
1581 const struct in6_addr *
1582 nm_ip6_config_get_nameserver (const NMIP6Config *config, guint i)
1584 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1586 return &g_array_index (priv->nameservers, struct in6_addr, i);
1589 /******************************************************************/
1592 nm_ip6_config_reset_domains (NMIP6Config *config)
1594 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1596 if (priv->domains->len != 0) {
1597 g_ptr_array_set_size (priv->domains, 0);
1598 _notify (config, PROP_DOMAINS);
1603 nm_ip6_config_add_domain (NMIP6Config *config, const char *domain)
1605 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1608 g_return_if_fail (domain != NULL);
1609 g_return_if_fail (domain[0] != '\0');
1611 for (i = 0; i < priv->domains->len; i++)
1612 if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain))
1615 g_ptr_array_add (priv->domains, g_strdup (domain));
1616 _notify (config, PROP_DOMAINS);
1620 nm_ip6_config_del_domain (NMIP6Config *config, guint i)
1622 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1624 g_return_if_fail (i < priv->domains->len);
1626 g_ptr_array_remove_index (priv->domains, i);
1627 _notify (config, PROP_DOMAINS);
1631 nm_ip6_config_get_num_domains (const NMIP6Config *config)
1633 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1635 return priv->domains->len;
1639 nm_ip6_config_get_domain (const NMIP6Config *config, guint i)
1641 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1643 return g_ptr_array_index (priv->domains, i);
1646 /******************************************************************/
1649 nm_ip6_config_reset_searches (NMIP6Config *config)
1651 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1653 if (priv->searches->len != 0) {
1654 g_ptr_array_set_size (priv->searches, 0);
1655 _notify (config, PROP_SEARCHES);
1660 nm_ip6_config_add_search (NMIP6Config *config, const char *new)
1662 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1666 g_return_if_fail (new != NULL);
1667 g_return_if_fail (new[0] != '\0');
1669 search = g_strdup (new);
1671 /* Remove trailing dot as it has no effect */
1672 len = strlen (search);
1673 if (search[len - 1] == '.')
1674 search[len - 1] = 0;
1681 if (_nm_utils_strv_find_first ((char **) priv->searches->pdata,
1682 priv->searches->len, search) >= 0) {
1687 g_ptr_array_add (priv->searches, search);
1688 _notify (config, PROP_SEARCHES);
1692 nm_ip6_config_del_search (NMIP6Config *config, guint i)
1694 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1696 g_return_if_fail (i < priv->searches->len);
1698 g_ptr_array_remove_index (priv->searches, i);
1699 _notify (config, PROP_SEARCHES);
1703 nm_ip6_config_get_num_searches (const NMIP6Config *config)
1705 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1707 return priv->searches->len;
1711 nm_ip6_config_get_search (const NMIP6Config *config, guint i)
1713 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1715 return g_ptr_array_index (priv->searches, i);
1718 /******************************************************************/
1721 nm_ip6_config_reset_dns_options (NMIP6Config *config)
1723 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1725 if (priv->dns_options->len != 0) {
1726 g_ptr_array_set_size (priv->dns_options, 0);
1727 _notify (config, PROP_DNS_OPTIONS);
1732 nm_ip6_config_add_dns_option (NMIP6Config *config, const char *new)
1734 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1737 g_return_if_fail (new != NULL);
1738 g_return_if_fail (new[0] != '\0');
1740 for (i = 0; i < priv->dns_options->len; i++)
1741 if (!g_strcmp0 (g_ptr_array_index (priv->dns_options, i), new))
1744 g_ptr_array_add (priv->dns_options, g_strdup (new));
1745 _notify (config, PROP_DNS_OPTIONS);
1749 nm_ip6_config_del_dns_option (NMIP6Config *config, guint i)
1751 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1753 g_return_if_fail (i < priv->dns_options->len);
1755 g_ptr_array_remove_index (priv->dns_options, i);
1756 _notify (config, PROP_DNS_OPTIONS);
1760 nm_ip6_config_get_num_dns_options (const NMIP6Config *config)
1762 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1764 return priv->dns_options->len;
1768 nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i)
1770 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1772 return g_ptr_array_index (priv->dns_options, i);
1775 /******************************************************************/
1778 nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss)
1780 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1786 nm_ip6_config_get_mss (const NMIP6Config *config)
1788 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1793 /******************************************************************/
1796 hash_u32 (GChecksum *sum, guint32 n)
1798 g_checksum_update (sum, (const guint8 *) &n, sizeof (n));
1802 hash_in6addr (GChecksum *sum, const struct in6_addr *a)
1805 g_checksum_update (sum, (const guint8 *) a, sizeof (*a));
1807 g_checksum_update (sum, (const guint8 *) &in6addr_any, sizeof (in6addr_any));
1811 nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only)
1816 g_return_if_fail (config);
1817 g_return_if_fail (sum);
1819 if (dns_only == FALSE) {
1820 hash_in6addr (sum, nm_ip6_config_get_gateway (config));
1822 for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++) {
1823 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
1825 hash_in6addr (sum, &address->address);
1826 hash_u32 (sum, address->plen);
1829 for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) {
1830 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
1832 hash_in6addr (sum, &route->network);
1833 hash_u32 (sum, route->plen);
1834 hash_in6addr (sum, &route->gateway);
1835 hash_u32 (sum, route->metric);
1839 for (i = 0; i < nm_ip6_config_get_num_nameservers (config); i++)
1840 hash_in6addr (sum, nm_ip6_config_get_nameserver (config, i));
1842 for (i = 0; i < nm_ip6_config_get_num_domains (config); i++) {
1843 s = nm_ip6_config_get_domain (config, i);
1844 g_checksum_update (sum, (const guint8 *) s, strlen (s));
1847 for (i = 0; i < nm_ip6_config_get_num_searches (config); i++) {
1848 s = nm_ip6_config_get_search (config, i);
1849 g_checksum_update (sum, (const guint8 *) s, strlen (s));
1852 for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++) {
1853 s = nm_ip6_config_get_dns_option (config, i);
1854 g_checksum_update (sum, (const guint8 *) s, strlen (s));
1860 * nm_ip6_config_equal:
1861 * @a: first config to compare
1862 * @b: second config to compare
1864 * Compares two #NMIP6Configs for basic equality. This means that all
1865 * attributes must exist in the same order in both configs (addresses, routes,
1866 * domains, DNS servers, etc) but some attributes (address lifetimes, and address
1867 * and route sources) are ignored.
1869 * Returns: %TRUE if the configurations are basically equal to each other,
1873 nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
1875 GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1);
1876 GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1);
1877 gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
1878 gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
1879 guchar a_data[a_len], b_data[b_len];
1883 nm_ip6_config_hash (a, a_checksum, FALSE);
1885 nm_ip6_config_hash (b, b_checksum, FALSE);
1887 g_checksum_get_digest (a_checksum, a_data, &a_len);
1888 g_checksum_get_digest (b_checksum, b_data, &b_len);
1890 g_assert (a_len == b_len);
1891 equal = !memcmp (a_data, b_data, a_len);
1893 g_checksum_free (a_checksum);
1894 g_checksum_free (b_checksum);
1899 /******************************************************************/
1902 nm_ip6_config_init (NMIP6Config *config)
1904 NMIP6ConfigPrivate *priv;
1906 priv = G_TYPE_INSTANCE_GET_PRIVATE (config, NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate);
1907 config->priv = priv;
1909 priv->addresses = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Address));
1910 priv->routes = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Route));
1911 priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr));
1912 priv->domains = g_ptr_array_new_with_free_func (g_free);
1913 priv->searches = g_ptr_array_new_with_free_func (g_free);
1914 priv->dns_options = g_ptr_array_new_with_free_func (g_free);
1915 priv->route_metric = -1;
1919 finalize (GObject *object)
1921 NMIP6Config *self = NM_IP6_CONFIG (object);
1922 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
1924 g_array_unref (priv->addresses);
1925 g_array_unref (priv->routes);
1926 g_array_unref (priv->nameservers);
1927 g_ptr_array_unref (priv->domains);
1928 g_ptr_array_unref (priv->searches);
1929 g_ptr_array_unref (priv->dns_options);
1931 G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object);
1935 nameservers_to_gvalue (GArray *array, GValue *value)
1937 GVariantBuilder builder;
1940 g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
1942 while (array && (i < array->len)) {
1943 struct in6_addr *addr;
1945 addr = &g_array_index (array, struct in6_addr, i++);
1946 g_variant_builder_add (&builder, "@ay",
1947 g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
1951 g_value_take_variant (value, g_variant_builder_end (&builder));
1955 get_property (GObject *object, guint prop_id,
1956 GValue *value, GParamSpec *pspec)
1958 NMIP6Config *config = NM_IP6_CONFIG (object);
1959 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1963 g_value_set_int (value, priv->ifindex);
1965 case PROP_ADDRESS_DATA:
1967 GVariantBuilder array_builder, addr_builder;
1968 int naddr = nm_ip6_config_get_num_addresses (config);
1971 g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
1972 for (i = 0; i < naddr; i++) {
1973 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
1975 g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}"));
1976 g_variant_builder_add (&addr_builder, "{sv}",
1978 g_variant_new_string (nm_utils_inet6_ntop (&address->address, NULL)));
1979 g_variant_builder_add (&addr_builder, "{sv}",
1981 g_variant_new_uint32 (address->plen));
1982 if ( !IN6_IS_ADDR_UNSPECIFIED (&address->peer_address)
1983 && !IN6_ARE_ADDR_EQUAL (&address->peer_address, &address->address)) {
1984 g_variant_builder_add (&addr_builder, "{sv}",
1986 g_variant_new_string (nm_utils_inet6_ntop (&address->peer_address, NULL)));
1989 g_variant_builder_add (&array_builder, "a{sv}", &addr_builder);
1992 g_value_take_variant (value, g_variant_builder_end (&array_builder));
1995 case PROP_ADDRESSES:
1997 GVariantBuilder array_builder;
1998 const struct in6_addr *gateway = nm_ip6_config_get_gateway (config);
1999 int naddr = nm_ip6_config_get_num_addresses (config);
2002 g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuay)"));
2003 for (i = 0; i < naddr; i++) {
2004 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
2006 g_variant_builder_add (&array_builder, "(@ayu@ay)",
2007 g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2008 &address->address, 16, 1),
2010 g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2011 (i == 0 && gateway ? gateway : &in6addr_any),
2015 g_value_take_variant (value, g_variant_builder_end (&array_builder));
2018 case PROP_ROUTE_DATA:
2020 GVariantBuilder array_builder, route_builder;
2021 guint nroutes = nm_ip6_config_get_num_routes (config);
2024 g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
2025 for (i = 0; i < nroutes; i++) {
2026 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
2028 g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
2029 g_variant_builder_add (&route_builder, "{sv}",
2031 g_variant_new_string (nm_utils_inet6_ntop (&route->network, NULL)));
2032 g_variant_builder_add (&route_builder, "{sv}",
2034 g_variant_new_uint32 (route->plen));
2035 if (!IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) {
2036 g_variant_builder_add (&route_builder, "{sv}",
2038 g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, NULL)));
2041 g_variant_builder_add (&route_builder, "{sv}",
2043 g_variant_new_uint32 (route->metric));
2045 g_variant_builder_add (&array_builder, "a{sv}", &route_builder);
2048 g_value_take_variant (value, g_variant_builder_end (&array_builder));
2053 GVariantBuilder array_builder;
2054 int nroutes = nm_ip6_config_get_num_routes (config);
2057 g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuayu)"));
2058 for (i = 0; i < nroutes; i++) {
2059 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
2061 /* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the
2062 * plen is positive. Skip the default routes not to break older clients. */
2063 if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
2066 g_variant_builder_add (&array_builder, "(@ayu@ayu)",
2067 g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2068 &route->network, 16, 1),
2069 (guint32) route->plen,
2070 g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2071 &route->gateway, 16, 1),
2072 (guint32) route->metric);
2075 g_value_take_variant (value, g_variant_builder_end (&array_builder));
2079 if (!IN6_IS_ADDR_UNSPECIFIED (&priv->gateway))
2080 g_value_set_string (value, nm_utils_inet6_ntop (&priv->gateway, NULL));
2082 g_value_set_string (value, NULL);
2084 case PROP_NAMESERVERS:
2085 nameservers_to_gvalue (priv->nameservers, value);
2088 nm_utils_g_value_set_strv (value, priv->domains);
2091 nm_utils_g_value_set_strv (value, priv->searches);
2093 case PROP_DNS_OPTIONS:
2094 nm_utils_g_value_set_strv (value, priv->dns_options);
2097 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2103 set_property (GObject *object,
2105 const GValue *value,
2108 NMIP6Config *config = NM_IP6_CONFIG (object);
2109 NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
2113 priv->ifindex = g_value_get_int (value);
2116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2122 nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
2124 GObjectClass *object_class = G_OBJECT_CLASS (config_class);
2125 NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
2127 g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate));
2129 exported_object_class->export_path = NM_DBUS_PATH "/IP6Config/%u";
2131 /* virtual methods */
2132 object_class->get_property = get_property;
2133 object_class->set_property = set_property;
2134 object_class->finalize = finalize;
2137 obj_properties[PROP_IFINDEX] =
2138 g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "",
2141 G_PARAM_CONSTRUCT_ONLY |
2142 G_PARAM_STATIC_STRINGS);
2143 obj_properties[PROP_ADDRESS_DATA] =
2144 g_param_spec_variant (NM_IP6_CONFIG_ADDRESS_DATA, "", "",
2145 G_VARIANT_TYPE ("aa{sv}"),
2148 G_PARAM_STATIC_STRINGS);
2149 obj_properties[PROP_ADDRESSES] =
2150 g_param_spec_variant (NM_IP6_CONFIG_ADDRESSES, "", "",
2151 G_VARIANT_TYPE ("a(ayuay)"),
2154 G_PARAM_STATIC_STRINGS);
2155 obj_properties[PROP_ROUTE_DATA] =
2156 g_param_spec_variant (NM_IP6_CONFIG_ROUTE_DATA, "", "",
2157 G_VARIANT_TYPE ("aa{sv}"),
2160 G_PARAM_STATIC_STRINGS);
2161 obj_properties[PROP_ROUTES] =
2162 g_param_spec_variant (NM_IP6_CONFIG_ROUTES, "", "",
2163 G_VARIANT_TYPE ("a(ayuayu)"),
2166 G_PARAM_STATIC_STRINGS);
2167 obj_properties[PROP_GATEWAY] =
2168 g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "",
2171 G_PARAM_STATIC_STRINGS);
2172 obj_properties[PROP_NAMESERVERS] =
2173 g_param_spec_variant (NM_IP6_CONFIG_NAMESERVERS, "", "",
2174 G_VARIANT_TYPE ("aay"),
2177 G_PARAM_STATIC_STRINGS);
2178 obj_properties[PROP_DOMAINS] =
2179 g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
2182 G_PARAM_STATIC_STRINGS);
2183 obj_properties[PROP_SEARCHES] =
2184 g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
2187 G_PARAM_STATIC_STRINGS);
2188 obj_properties[PROP_DNS_OPTIONS] =
2189 g_param_spec_boxed (NM_IP6_CONFIG_DNS_OPTIONS, "", "",
2192 G_PARAM_STATIC_STRINGS);
2194 g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
2196 nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
2197 NMDBUS_TYPE_IP6_CONFIG_SKELETON,