NMIP6Config * nm_ip6_config_new_for_interface (int ifindex) { NMIP6Config *ip6; GArray *addrs_array, *routes_array; NMPlatformIP6Address *addrs; NMPlatformIP6Route *routes; NMIP6Address *addr; NMIP6Route *route; int i; addrs_array = nm_platform_ip6_address_get_all (ifindex); if (addrs_array->len == 0) { g_array_unref (addrs_array); return NULL; } ip6 = nm_ip6_config_new (); addrs = (NMPlatformIP6Address *)addrs_array->data; for (i = 0; i < addrs_array->len; i++) { addr = nm_ip6_address_new (); nm_ip6_address_set_address (addr, &addrs[i].address); nm_ip6_address_set_prefix (addr, addrs[i].plen); nm_ip6_config_take_address (ip6, addr); } g_array_unref (addrs_array); routes_array = nm_platform_ip6_route_get_all (ifindex); routes = (NMPlatformIP6Route *)routes_array->data; for (i = 0; i < routes_array->len; i++) { /* Default route ignored; it's handled internally by NM and not * tracked in the device's IP config. */ if (routes[i].plen == 0) continue; route = nm_ip6_route_new (); nm_ip6_route_set_dest (route, &routes[i].network); nm_ip6_route_set_prefix (route, routes[i].plen); nm_ip6_route_set_next_hop (route, &routes[i].gateway); nm_ip6_route_set_metric (route, routes[i].metric); nm_ip6_config_take_route (ip6, route); } g_array_unref (routes_array); return ip6; }
/* * Parse IPv6 address from string to NMIP6Address stucture. * ip_str is the IPv6 address in the form address/prefix * gw_str is the gateway address (it is optional) */ NMIP6Address * nmc_parse_and_build_ip6_address (const char *ip_str, const char *gw_str, GError **error) { NMIP6Address *addr = NULL; struct in6_addr ip_addr, gw_addr; char *tmp; char *plen; long int prefix; g_return_val_if_fail (ip_str != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); tmp = g_strdup (ip_str); plen = strchr (tmp, '/'); /* prefix delimiter */ if (plen) *plen++ = '\0'; if (inet_pton (AF_INET6, tmp, &ip_addr) < 1) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("invalid IPv6 address '%s'"), tmp); goto finish; } prefix = 128; if (plen) { if (!nmc_string_to_int (plen, TRUE, 1, 128, &prefix)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("invalid prefix '%s'; <1-128> allowed"), plen); goto finish; } } if (inet_pton (AF_INET6, gw_str ? gw_str : "::", &gw_addr) < 1) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("invalid gateway '%s'"), gw_str); goto finish; } addr = nm_ip6_address_new (); nm_ip6_address_set_address (addr, &ip_addr); nm_ip6_address_set_prefix (addr, (guint32) prefix); nm_ip6_address_set_gateway (addr, &gw_addr); finish: g_free (tmp); return addr; }
NMIP6Config * nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex) { NMIP6ManagerPrivate *priv; NMIP6Device *device; NMIP6Config *config; struct rtnl_addr *rtnladdr; struct nl_addr *nladdr; struct in6_addr *addr; NMIP6Address *ip6addr; struct rtnl_route *rtnlroute; struct nl_addr *nldest, *nlgateway; struct in6_addr *dest, *gateway; gboolean defgw_set = FALSE; struct in6_addr defgw; uint32_t metric; NMIP6Route *ip6route; int i; g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL); g_return_val_if_fail (ifindex > 0, NULL); priv = NM_IP6_MANAGER_GET_PRIVATE (manager); device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); if (!device) { nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex); return NULL; } config = nm_ip6_config_new (); if (!config) { nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.", device->iface); return NULL; } /* Make sure we refill the route and address caches, otherwise we won't get * up-to-date information here since the netlink route/addr change messages * may be lagging a bit. */ nl_cache_refill (priv->nlh, priv->route_cache); nl_cache_refill (priv->nlh, priv->addr_cache); /* Add routes */ for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) { /* Make sure it's an IPv6 route for this device */ if (rtnl_route_get_oif (rtnlroute) != device->ifindex) continue; if (rtnl_route_get_family (rtnlroute) != AF_INET6) continue; nldest = rtnl_route_get_dst (rtnlroute); if (!nldest || nl_addr_get_family (nldest) != AF_INET6) continue; dest = nl_addr_get_binary_addr (nldest); nlgateway = rtnl_route_get_gateway (rtnlroute); if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6) continue; gateway = nl_addr_get_binary_addr (nlgateway); if (rtnl_route_get_dst_len (rtnlroute) == 0) { /* Default gateway route; don't add to normal routes but to each address */ if (!defgw_set) { memcpy (&defgw, gateway, sizeof (defgw)); defgw_set = TRUE; } continue; } /* Also ignore link-local routes where the destination and gateway are * the same, which apparently get added by the kernel but return -EINVAL * when we try to add them via netlink. */ if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway)) continue; ip6route = nm_ip6_route_new (); nm_ip6_route_set_dest (ip6route, dest); nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute)); nm_ip6_route_set_next_hop (ip6route, gateway); rtnl_route_get_metric(rtnlroute, 1, &metric); if (metric != UINT_MAX) nm_ip6_route_set_metric (ip6route, metric); nm_ip6_config_take_route (config, ip6route); } /* Add addresses */ for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) { if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) continue; nladdr = rtnl_addr_get_local (rtnladdr); if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) continue; addr = nl_addr_get_binary_addr (nladdr); ip6addr = nm_ip6_address_new (); nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr)); nm_ip6_address_set_address (ip6addr, addr); nm_ip6_config_take_address (config, ip6addr); if (defgw_set) nm_ip6_address_set_gateway (ip6addr, &defgw); } /* Add DNS servers */ if (device->rdnss_servers) { NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data); for (i = 0; i < device->rdnss_servers->len; i++) nm_ip6_config_add_nameserver (config, &rdnss[i].addr); } /* Add DNS domains */ if (device->dnssl_domains) { NMIP6DNSSL *dnssl = (NMIP6DNSSL *)(device->dnssl_domains->data); for (i = 0; i < device->dnssl_domains->len; i++) nm_ip6_config_add_domain (config, dnssl[i].domain); } return config; }
static gboolean ui_to_setting (CEPageIP6 *page) { gboolean valid = FALSE; const gchar *method; gboolean ignore_auto_dns; gboolean ignore_auto_routes; gboolean never_default; GList *children, *l; if (!gtk_switch_get_active (page->enabled)) { method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; } else { switch (gtk_combo_box_get_active (page->method)) { case IP6_METHOD_MANUAL: method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; break; case IP6_METHOD_LINK_LOCAL: method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; break; case IP6_METHOD_DHCP: method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; break; default: case IP6_METHOD_AUTO: method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; break; } } nm_setting_ip6_config_clear_addresses (page->setting); children = gtk_container_get_children (GTK_CONTAINER (page->address_list)); for (l = children; l; l = l->next) { GtkWidget *row = l->data; GtkEntry *entry; const gchar *text_address; const gchar *text_prefix; const gchar *text_gateway; struct in6_addr tmp_addr; struct in6_addr tmp_gateway; guint32 prefix; gchar *end; NMIP6Address *addr; gboolean have_gateway = FALSE; entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); if (!entry) continue; text_address = gtk_entry_get_text (entry); text_prefix = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "prefix"))); text_gateway = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"))); if (!*text_address && !*text_prefix && !*text_gateway) { /* ignore empty rows */ continue; } if (inet_pton (AF_INET6, text_address, &tmp_addr) <= 0) { g_warning ("IPv6 address %s missing or invalid", text_address); goto out; } prefix = strtoul (text_prefix, &end, 10); if (!end || *end || prefix == 0 || prefix > 128) { g_warning ("IPv6 prefix %s is invalid", text_prefix); goto out; } if (text_gateway && *text_gateway) { if (inet_pton (AF_INET6, text_gateway, &tmp_gateway) <= 0) { g_warning ("IPv6 gateway %s is invalid", text_gateway); goto out; } if (!IN6_IS_ADDR_UNSPECIFIED (&tmp_gateway)) have_gateway = TRUE; } addr = nm_ip6_address_new (); nm_ip6_address_set_address (addr, &tmp_addr); nm_ip6_address_set_prefix (addr, prefix); if (have_gateway) nm_ip6_address_set_gateway (addr, &tmp_gateway); nm_setting_ip6_config_add_address (page->setting, addr); } g_list_free (children); nm_setting_ip6_config_clear_dns (page->setting); children = gtk_container_get_children (GTK_CONTAINER (page->dns_list)); for (l = children; l; l = l->next) { GtkWidget *row = l->data; GtkEntry *entry; const gchar *text; struct in6_addr tmp_addr; entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); if (!entry) continue; text = gtk_entry_get_text (entry); if (!*text) { /* ignore empty rows */ continue; } if (inet_pton (AF_INET6, text, &tmp_addr) <= 0) { g_warning ("IPv6 dns server %s invalid", text); goto out; } nm_setting_ip6_config_add_dns (page->setting, &tmp_addr); } g_list_free (children); nm_setting_ip6_config_clear_routes (page->setting); children = gtk_container_get_children (GTK_CONTAINER (page->routes_list)); for (l = children; l; l = l->next) { GtkWidget *row = l->data; GtkEntry *entry; const gchar *text_address; const gchar *text_prefix; const gchar *text_gateway; const gchar *text_metric; struct in6_addr dest, gateway; guint32 prefix, metric; gchar *end; NMIP6Route *route; entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); if (!entry) continue; text_address = gtk_entry_get_text (entry); text_prefix = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "prefix"))); text_gateway = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"))); text_metric = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "metric"))); if (!*text_address && !*text_prefix && !*text_gateway && !*text_metric) { /* ignore empty rows */ continue; } if (inet_pton (AF_INET6, text_address, &dest) <= 0) { g_warning ("IPv6 route address %s invalid", text_address); goto out; } prefix = strtoul (text_prefix, &end, 10); if (!end || *end || prefix == 0 || prefix > 128) { g_warning ("IPv6 route prefix %s invalid", text_prefix); goto out; } if (inet_pton (AF_INET6, text_gateway, &gateway) <= 0) { g_warning ("IPv6 route gateway %s invalid", text_gateway); goto out; } metric = 0; if (*text_metric) { errno = 0; metric = strtoul (text_metric, NULL, 10); if (errno) { g_warning ("IPv6 route metric %s invalid", text_metric); goto out; } } route = nm_ip6_route_new (); nm_ip6_route_set_dest (route, &dest); nm_ip6_route_set_prefix (route, prefix); nm_ip6_route_set_next_hop (route, &gateway); nm_ip6_route_set_metric (route, metric); nm_setting_ip6_config_add_route (page->setting, route); nm_ip6_route_unref (route); } g_list_free (children); ignore_auto_dns = !gtk_switch_get_active (page->auto_dns); ignore_auto_routes = !gtk_switch_get_active (page->auto_routes); never_default = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (page->never_default)); g_object_set (page->setting, NM_SETTING_IP6_CONFIG_METHOD, method, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, ignore_auto_dns, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, ignore_auto_routes, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, never_default, NULL); valid = TRUE; out: return valid; }
/* Given a table of DHCP options from the client, convert into an IP6Config */ static NMIP6Config * ip6_options_to_config (NMDHCPClient *self) { NMDHCPClientPrivate *priv; NMIP6Config *ip6_config = NULL; struct in6_addr tmp_addr; NMIP6Address *addr = NULL; char *str = NULL; GHashTableIter iter; gpointer key, value; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->options != NULL, NULL); g_hash_table_iter_init (&iter, priv->options); while (g_hash_table_iter_next (&iter, &key, &value)) { nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'", priv->iface, (const char *) key, (const char *) value); } ip6_config = nm_ip6_config_new (); if (!ip6_config) { nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6Config!", priv->iface); return NULL; } str = g_hash_table_lookup (priv->options, "new_ip6_address"); if (str) { if (!inet_pton (AF_INET6, str, &tmp_addr)) { nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'", priv->iface, str); goto error; } addr = nm_ip6_address_new (); g_assert (addr); nm_ip6_address_set_address (addr, &tmp_addr); /* DHCPv6 IA_NA assignments are single address only */ nm_ip6_address_set_prefix (addr, 128); nm_log_info (LOGD_DHCP6, " address %s/128", str); nm_ip6_config_take_address (ip6_config, addr); } else if (priv->info_only == FALSE) { /* No address in Managed mode is a hard error */ goto error; } str = g_hash_table_lookup (priv->options, "new_host_name"); if (str) nm_log_info (LOGD_DHCP6, " hostname '%s'", str); str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { 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 (searches); } str = g_hash_table_lookup (priv->options, "new_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; }
/* Given a table of DHCP options from the client, convert into an IP6Config */ static NMIP6Config * ip6_options_to_config (NMDHCPClient *self) { NMDHCPClientPrivate *priv; NMIP6Config *ip6_config = NULL; struct in6_addr tmp_addr; NMIP6Address *addr = NULL; char *str = NULL; GHashTableIter iter; gpointer key, value; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->options != NULL, NULL); g_hash_table_iter_init (&iter, priv->options); while (g_hash_table_iter_next (&iter, &key, &value)) { nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'", priv->iface, (const char *) key, (const char *) value); } ip6_config = nm_ip6_config_new (); if (!ip6_config) { nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6Config!", priv->iface); return NULL; } addr = nm_ip6_address_new (); if (!addr) { nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6 Address!", priv->iface); goto error; } str = g_hash_table_lookup (priv->options, "new_ip6_address"); if (str) { if (!inet_pton (AF_INET6, str, &tmp_addr)) { nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'", priv->iface); goto error; } nm_ip6_address_set_address (addr, &tmp_addr); nm_log_info (LOGD_DHCP6, " address %s", str); } else { /* No address in managed mode is a hard error */ if (priv->info_only == FALSE) goto error; /* But "info-only" setups don't necessarily need an address */ nm_ip6_address_unref (addr); addr = NULL; } /* Only care about prefix if we got an address */ if (addr) { str = g_hash_table_lookup (priv->options, "new_ip6_prefixlen"); if (str) { long unsigned int prefix; errno = 0; prefix = strtoul (str, NULL, 10); if (errno != 0 || prefix > 128) goto error; nm_ip6_address_set_prefix (addr, (guint32) prefix); nm_log_info (LOGD_DHCP6, " prefix %lu", prefix); } nm_ip6_config_take_address (ip6_config, addr); addr = NULL; } str = g_hash_table_lookup (priv->options, "new_host_name"); if (str) nm_log_info (LOGD_DHCP6, " hostname '%s'", str); str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { 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 (searches); } str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search"); if (str) process_domain_search (str, ip6_add_domain_search, ip6_config); return ip6_config; error: if (addr) nm_ip6_address_unref (addr); g_object_unref (ip6_config); return NULL; }