static void
rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
{
    static NMIP6Config *rdisc_config = NULL;
    NMIP6Config *existing;
    static int system_support = -1;
    guint32 ifa_flags = 0x00;
    int i;

    if (system_support == -1) {
        /*
         * Check, whether kernel is recent enough, to help user space handling RA.
         * If it's not supported, we have no ipv6-privacy and must add autoconf
         * addresses as /128.
         * The reason for the /128 is to prevent the kernel
         * from adding a prefix route for this address.
         **/
        system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
    }

    if (system_support)
        ifa_flags = IFA_F_NOPREFIXROUTE;
    if (global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
            || global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
    {
        /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
        ifa_flags |= IFA_F_MANAGETEMPADDR;
    }

    existing = nm_ip6_config_capture (ifindex, FALSE, global_opt.tempaddr);
    if (rdisc_config)
        nm_ip6_config_subtract (existing, rdisc_config);
    else
        rdisc_config = nm_ip6_config_new (ifindex);

    if (changed & NM_RDISC_CONFIG_GATEWAYS) {
        /* Use the first gateway as ordered in router discovery cache. */
        if (rdisc->gateways->len) {
            NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);

            nm_ip6_config_set_gateway (rdisc_config, &gateway->address);
        } else
            nm_ip6_config_set_gateway (rdisc_config, NULL);
    }

    if (changed & NM_RDISC_CONFIG_ADDRESSES) {
        /* Rebuild address list from router discovery cache. */
        nm_ip6_config_reset_addresses (rdisc_config);

        /* rdisc->addresses contains at most max_addresses entries.
         * This is different from what the kernel does, which
         * also counts static and temporary addresses when checking
         * max_addresses.
         **/
        for (i = 0; i < rdisc->addresses->len; i++) {
            NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
            NMPlatformIP6Address address;

            memset (&address, 0, sizeof (address));
            address.address = discovered_address->address;
            address.plen = system_support ? 64 : 128;
            address.timestamp = discovered_address->timestamp;
            address.lifetime = discovered_address->lifetime;
            address.preferred = discovered_address->preferred;
            if (address.preferred > address.lifetime)
                address.preferred = address.lifetime;
            address.source = NM_IP_CONFIG_SOURCE_RDISC;
            address.n_ifa_flags = ifa_flags;

            nm_ip6_config_add_address (rdisc_config, &address);
        }
    }

    if (changed & NM_RDISC_CONFIG_ROUTES) {
        /* Rebuild route list from router discovery cache. */
        nm_ip6_config_reset_routes (rdisc_config);

        for (i = 0; i < rdisc->routes->len; i++) {
            NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
            NMPlatformIP6Route route;

            /* Only accept non-default routes.  The router has no idea what the
             * local configuration or user preferences are, so sending routes
             * with a prefix length of 0 is quite rude and thus ignored.
             */
            if (   discovered_route->plen > 0
                    && discovered_route->plen <= 128) {
                memset (&route, 0, sizeof (route));
                route.network = discovered_route->network;
                route.plen = discovered_route->plen;
                route.gateway = discovered_route->gateway;
                route.source = NM_IP_CONFIG_SOURCE_RDISC;
                route.metric = global_opt.priority_v6;

                nm_ip6_config_add_route (rdisc_config, &route);
            }
        }
    }

    if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
        /* Unsupported until systemd DHCPv6 is ready */
    }

    if (changed & NM_RDISC_CONFIG_HOP_LIMIT)
        nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdisc->hop_limit);

    if (changed & NM_RDISC_CONFIG_MTU) {
        char val[16];

        g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
        nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val);
    }

    nm_ip6_config_merge (existing, rdisc_config, NM_IP_CONFIG_MERGE_DEFAULT);
    if (!nm_ip6_config_commit (existing, ifindex, TRUE))
        nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", global_opt.ifname);
}
/* This is exactly identical to nm_utils_merge_ip4_config, with s/4/6/,
 * except that we can't compare addresses with ==.
 */
void
nm_utils_merge_ip6_config (NMIP6Config *ip6_config, NMSettingIP6Config *setting)
{
	int i, j;

	if (!setting)
		return; /* Defaults are just fine */

	if (nm_setting_ip6_config_get_ignore_auto_dns (setting)) {
		nm_ip6_config_reset_nameservers (ip6_config);
		nm_ip6_config_reset_domains (ip6_config);
		nm_ip6_config_reset_searches (ip6_config);
	}

	if (nm_setting_ip6_config_get_ignore_auto_routes (setting))
		nm_ip6_config_reset_routes (ip6_config);

	for (i = 0; i < nm_setting_ip6_config_get_num_dns (setting); i++) {
		const struct in6_addr *ns;
		gboolean found = FALSE;

		/* Avoid dupes */
		ns = nm_setting_ip6_config_get_dns (setting, i);
		for (j = 0; j < nm_ip6_config_get_num_nameservers (ip6_config); j++) {
			if (ip6_addresses_equal (nm_ip6_config_get_nameserver (ip6_config, j), ns)) {
				found = TRUE;
				break;
			}
		}

		if (!found)
			nm_ip6_config_add_nameserver (ip6_config, ns);
	}

	/* DNS search domains */
	for (i = 0; i < nm_setting_ip6_config_get_num_dns_searches (setting); i++) {
		const char *search = nm_setting_ip6_config_get_dns_search (setting, i);
		gboolean found = FALSE;

		/* Avoid dupes */
		for (j = 0; j < nm_ip6_config_get_num_searches (ip6_config); j++) {
			if (!strcmp (search, nm_ip6_config_get_search (ip6_config, j))) {
				found = TRUE;
				break;
			}
		}

		if (!found)
			nm_ip6_config_add_search (ip6_config, search);
	}

	/* IPv6 addresses */
	for (i = 0; i < nm_setting_ip6_config_get_num_addresses (setting); i++) {
		NMIP6Address *setting_addr = nm_setting_ip6_config_get_address (setting, i);
		guint32 num;

		num = nm_ip6_config_get_num_addresses (ip6_config);
		for (j = 0; j < num; j++) {
			NMIP6Address *cfg_addr = nm_ip6_config_get_address (ip6_config, j);

			/* Dupe, override with user-specified address */
			if (ip6_addresses_equal (nm_ip6_address_get_address (cfg_addr), nm_ip6_address_get_address (setting_addr))) {
				nm_ip6_config_replace_address (ip6_config, j, setting_addr);
				break;
			}
		}

		if (j == num)
			nm_ip6_config_add_address (ip6_config, setting_addr);
	}

	/* IPv6 routes */
	for (i = 0; i < nm_setting_ip6_config_get_num_routes (setting); i++) {
		NMIP6Route *setting_route = nm_setting_ip6_config_get_route (setting, i);
		guint32 num;

		num = nm_ip6_config_get_num_routes (ip6_config);
		for (j = 0; j < num; j++) {
			NMIP6Route *cfg_route = nm_ip6_config_get_route (ip6_config, j);

			/* Dupe, override with user-specified route */
			if (   ip6_addresses_equal (nm_ip6_route_get_dest (cfg_route), nm_ip6_route_get_dest (setting_route))
			    && (nm_ip6_route_get_prefix (cfg_route) == nm_ip6_route_get_prefix (setting_route))
				&& ip6_addresses_equal (nm_ip6_route_get_next_hop (cfg_route), nm_ip6_route_get_next_hop (setting_route))) {
				nm_ip6_config_replace_route (ip6_config, j, setting_route);
				break;
			}
		}

		if (j == num)
			nm_ip6_config_add_route (ip6_config, setting_route);
	}

	if (nm_setting_ip6_config_get_never_default (setting))
		nm_ip6_config_set_never_default (ip6_config, TRUE);
}