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; }
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; }
/* 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; }