static void impl_ppp_manager_set_ip6_config (NMPPPManager *manager, GDBusMethodInvocation *context, GVariant *config_dict) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); NMIP6Config *config; NMPlatformIP6Address addr; struct in6_addr a; NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; _LOGI ("(IPv6 Config Get) reply received."); remove_timeout_handler (manager); config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); memset (&addr, 0, sizeof (addr)); addr.plen = 64; if (iid_value_to_ll6_addr (config_dict, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) { nm_ip6_config_set_gateway (config, &a); addr.peer_address = a; } if (iid_value_to_ll6_addr (config_dict, NM_PPP_IP6_CONFIG_OUR_IID, &addr.address, &iid)) { nm_ip6_config_add_address (config, &addr); if (set_ip_config_common (manager, config_dict, NM_PPP_IP6_CONFIG_INTERFACE, NULL)) { /* Push the IPv6 config and interface identifier up to the device */ g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config); } } else _LOGE ("invalid IPv6 address received!"); g_object_unref (config); g_dbus_method_invocation_return_value (context, NULL); }
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); }
static NMIP6Config * lease_to_ip6_config (const char *iface, int ifindex, sd_dhcp6_lease *lease, GHashTable *options, gboolean log_lease, gboolean info_only, GError **error) { struct in6_addr tmp_addr, *dns; uint32_t lft_pref, lft_valid; NMIP6Config *ip6_config; const char *addr_str; char **domains; GString *str; int num, i; gint32 ts; g_return_val_if_fail (lease, NULL); ip6_config = nm_ip6_config_new (ifindex); ts = nm_utils_get_monotonic_timestamp_s (); str = g_string_sized_new (30); /* Addresses */ sd_dhcp6_lease_reset_address_iter (lease); while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) { NMPlatformIP6Address address = { .plen = 128, .address = tmp_addr, .timestamp = ts, .lifetime = lft_valid, .preferred = lft_pref, .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; nm_ip6_config_add_address (ip6_config, &address); addr_str = nm_utils_inet6_ntop (&tmp_addr, NULL); g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); LOG_LEASE (LOGD_DHCP6, " address %s", nm_platform_ip6_address_to_string (&address, NULL, 0)); }; if (str->len) { add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); g_string_set_size (str , 0); } if (!info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { g_string_free (str, TRUE); g_object_unref (ip6_config); g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "no address received in managed mode"); return NULL; } /* DNS servers */ num = sd_dhcp6_lease_get_dns (lease, &dns); if (num > 0) { for (i = 0; i < num; i++) { nm_ip6_config_add_nameserver (ip6_config, &dns[i]); addr_str = nm_utils_inet6_ntop (&dns[i], NULL); g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); LOG_LEASE (LOGD_DHCP6, " nameserver %s", addr_str); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); g_string_set_size (str, 0); } /* Search domains */ num = sd_dhcp6_lease_get_domains (lease, &domains); if (num > 0) { for (i = 0; i < num; i++) { nm_ip6_config_add_search (ip6_config, domains[i]); g_string_append_printf (str, "%s%s", str->len ? " " : "", domains[i]); LOG_LEASE (LOGD_DHCP6, " domain name '%s'", domains[i]); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); g_string_set_size (str, 0); } g_string_free (str, TRUE); return ip6_config; } static void bound6_handle (NMDhcpSystemd *self) { NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); gs_unref_object NMIP6Config *ip6_config = NULL; gs_unref_hashtable GHashTable *options = NULL; gs_free_error GError *error = NULL; sd_dhcp6_lease *lease; int r; r = sd_dhcp6_client_get_lease (priv->client6, &lease); if (r < 0 || !lease) { _LOGW (" no lease!"); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); return; } _LOGD ("lease available"); options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); ip6_config = lease_to_ip6_config (iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, options, TRUE, priv->info_only, &error); if (ip6_config) { nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_BOUND, G_OBJECT (ip6_config), options); } else { _LOGW ("%s", error->message); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); } }
NMIP6Config * nm_dhcp_utils_ip6_config_from_options (int ifindex, const char *iface, GHashTable *options, guint32 priority, gboolean info_only) { NMIP6Config *ip6_config = NULL; struct in6_addr tmp_addr; NMPlatformIP6Address address; char *str = NULL; GHashTableIter iter; gpointer key, value; g_return_val_if_fail (options != NULL, NULL); memset (&address, 0, sizeof (address)); address.plen = 128; address.timestamp = nm_utils_get_monotonic_timestamp_s (); g_hash_table_iter_init (&iter, options); while (g_hash_table_iter_next (&iter, &key, &value)) { nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'", iface, (const char *) key, (const char *) value); } ip6_config = nm_ip6_config_new (ifindex); str = g_hash_table_lookup (options, "max_life"); if (str) { address.lifetime = strtoul (str, NULL, 10); nm_log_info (LOGD_DHCP6, " valid_lft %u", address.lifetime); } str = g_hash_table_lookup (options, "preferred_life"); if (str) { address.preferred = strtoul (str, NULL, 10); nm_log_info (LOGD_DHCP6, " preferred_lft %u", address.preferred); } str = g_hash_table_lookup (options, "ip6_address"); if (str) { if (!inet_pton (AF_INET6, str, &tmp_addr)) { nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'", iface, str); goto error; } address.address = tmp_addr; address.source = NM_IP_CONFIG_SOURCE_DHCP; nm_ip6_config_add_address (ip6_config, &address); nm_log_info (LOGD_DHCP6, " address %s", str); } else if (info_only == FALSE) { /* No address in Managed mode is a hard error */ goto error; } str = g_hash_table_lookup (options, "host_name"); if (str) nm_log_info (LOGD_DHCP6, " hostname '%s'", str); str = g_hash_table_lookup (options, "dhcp6_name_servers"); if (str) { char **dns = g_strsplit (str, " ", 0); char **s; for (s = dns; *s; s++) { if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { if (!IN6_IS_ADDR_UNSPECIFIED (&tmp_addr)) { nm_ip6_config_add_nameserver (ip6_config, &tmp_addr); nm_log_info (LOGD_DHCP6, " nameserver '%s'", *s); } } else nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s); } g_strfreev (dns); } str = g_hash_table_lookup (options, "dhcp6_domain_search"); if (str) process_domain_search (str, ip6_add_domain_search, ip6_config); return ip6_config; error: g_object_unref (ip6_config); return NULL; }
/* 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); }