NMIP4Config * nm_dhcp_utils_ip4_config_from_options (int ifindex, const char *iface, GHashTable *options, guint32 priority) { NMIP4Config *ip4_config = NULL; guint32 tmp_addr; in_addr_t addr; NMPlatformIP4Address address; char *str = NULL; guint32 gwaddr = 0; guint8 plen = 0; g_return_val_if_fail (options != NULL, NULL); ip4_config = nm_ip4_config_new (ifindex); memset (&address, 0, sizeof (address)); address.timestamp = nm_utils_get_monotonic_timestamp_s (); str = g_hash_table_lookup (options, "ip_address"); if (str && (inet_pton (AF_INET, str, &addr) > 0)) nm_log_info (LOGD_DHCP4, " address %s", str); else goto error; str = g_hash_table_lookup (options, "subnet_mask"); if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { plen = nm_utils_ip4_netmask_to_prefix (tmp_addr); nm_log_info (LOGD_DHCP4, " plen %d (%s)", plen, str); } else { /* Get default netmask for the IP according to appropriate class. */ plen = nm_utils_ip4_get_default_prefix (addr); nm_log_info (LOGD_DHCP4, " plen %d (default)", plen); } nm_platform_ip4_address_set_addr (&address, addr, plen); /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ if (!ip4_process_classless_routes (options, priority, ip4_config, &gwaddr)) process_classful_routes (options, priority, ip4_config); if (gwaddr) { nm_log_info (LOGD_DHCP4, " gateway %s", nm_utils_inet4_ntop (gwaddr, NULL)); nm_ip4_config_set_gateway (ip4_config, gwaddr); } else { /* If the gateway wasn't provided as a classless static route with a * subnet length of 0, try to find it using the old-style 'routers' option. */ str = g_hash_table_lookup (options, "routers"); if (str) { char **routers = g_strsplit (str, " ", 0); char **s; for (s = routers; *s; s++) { /* FIXME: how to handle multiple routers? */ if (inet_pton (AF_INET, *s, &gwaddr) > 0) { nm_ip4_config_set_gateway (ip4_config, gwaddr); nm_log_info (LOGD_DHCP4, " gateway %s", *s); break; } else nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s); } g_strfreev (routers); } } /* * RFC 2132, section 9.7 * DHCP clients use the contents of the 'server identifier' field * as the destination address for any DHCP messages unicast to * the DHCP server. * * Some ISP's provide leases from central servers that are on * different subnets that the address offered. If the host * does not configure the interface as the default route, the * dhcp server may not be reachable via unicast, and a host * specific route is needed. **/ str = g_hash_table_lookup (options, "dhcp_server_identifier"); if (str) { if (inet_pton (AF_INET, str, &tmp_addr) > 0) { nm_log_info (LOGD_DHCP4, " server identifier %s", str); if ( nm_utils_ip4_address_clear_host_address(tmp_addr, address.plen) != nm_utils_ip4_address_clear_host_address(address.address, address.plen) && !nm_ip4_config_get_direct_route_for_host (ip4_config, tmp_addr)) { /* DHCP server not on assigned subnet and the no direct route was returned. Add route */ NMPlatformIP4Route route = { 0 }; route.network = tmp_addr; route.plen = 32; /* this will be a device route if gwaddr is 0 */ route.gateway = gwaddr; route.source = NM_IP_CONFIG_SOURCE_DHCP; route.metric = priority; nm_ip4_config_add_route (ip4_config, &route); nm_log_dbg (LOGD_IP, "adding route for server identifier: %s", nm_platform_ip4_route_to_string (&route, NULL, 0)); } } else nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str); } str = g_hash_table_lookup (options, "dhcp_lease_time"); if (str) { address.lifetime = address.preferred = strtoul (str, NULL, 10); nm_log_info (LOGD_DHCP4, " lease time %u", address.lifetime); } address.source = NM_IP_CONFIG_SOURCE_DHCP; nm_ip4_config_add_address (ip4_config, &address); str = g_hash_table_lookup (options, "host_name"); if (str) nm_log_info (LOGD_DHCP4, " hostname '%s'", str); str = g_hash_table_lookup (options, "domain_name_servers"); if (str) { char **dns = g_strsplit (str, " ", 0); char **s; for (s = dns; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { nm_ip4_config_add_nameserver (ip4_config, tmp_addr); nm_log_info (LOGD_DHCP4, " nameserver '%s'", *s); } } else nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s); } g_strfreev (dns); } str = g_hash_table_lookup (options, "domain_name"); if (str) { char **domains = g_strsplit (str, " ", 0); char **s; for (s = domains; *s; s++) { nm_log_info (LOGD_DHCP4, " domain name '%s'", *s); nm_ip4_config_add_domain (ip4_config, *s); } g_strfreev (domains); } str = g_hash_table_lookup (options, "domain_search"); if (str) process_domain_search (str, ip4_add_domain_search, ip4_config); str = g_hash_table_lookup (options, "netbios_name_servers"); if (str) { char **nbns = g_strsplit (str, " ", 0); char **s; for (s = nbns; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { nm_ip4_config_add_wins (ip4_config, tmp_addr); nm_log_info (LOGD_DHCP4, " wins '%s'", *s); } } else nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s); } g_strfreev (nbns); } str = g_hash_table_lookup (options, "interface_mtu"); if (str) { int int_mtu; errno = 0; int_mtu = strtol (str, NULL, 10); if ((errno == EINVAL) || (errno == ERANGE)) goto error; if (int_mtu > 576) nm_ip4_config_set_mtu (ip4_config, int_mtu, NM_IP_CONFIG_SOURCE_DHCP); } str = g_hash_table_lookup (options, "nis_domain"); if (str) { nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str); nm_ip4_config_set_nis_domain (ip4_config, str); } str = g_hash_table_lookup (options, "nis_servers"); if (str) { char **nis = g_strsplit (str, " ", 0); char **s; for (s = nis; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { nm_ip4_config_add_nis_server (ip4_config, tmp_addr); nm_log_info (LOGD_DHCP4, " nis '%s'", *s); } } else nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s); } g_strfreev (nis); } str = g_hash_table_lookup (options, "vendor_encapsulated_options"); nm_ip4_config_set_metered (ip4_config, str && strstr (str, "ANDROID_METERED")); return ip4_config; error: g_object_unref (ip4_config); return NULL; }
/* Given a table of DHCP options from the client, convert into an IP4Config */ static NMIP4Config * ip4_options_to_config (NMDHCPClient *self) { NMDHCPClientPrivate *priv; NMIP4Config *ip4_config = NULL; struct in_addr tmp_addr; NMIP4Address *addr = NULL; char *str = NULL; guint32 gwaddr = 0, prefix = 0; 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); ip4_config = nm_ip4_config_new (); if (!ip4_config) { nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4Config!", priv->iface); return NULL; } addr = nm_ip4_address_new (); if (!addr) { nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4 Address!", priv->iface); goto error; } str = g_hash_table_lookup (priv->options, "new_ip_address"); if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { nm_ip4_address_set_address (addr, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " address %s", str); } else goto error; str = g_hash_table_lookup (priv->options, "new_subnet_mask"); if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { prefix = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " prefix %d (%s)", prefix, str); } else { /* Get default netmask for the IP according to appropriate class. */ prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr)); nm_log_info (LOGD_DHCP4, " prefix %d (default)", prefix); } nm_ip4_address_set_prefix (addr, prefix); /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ if (!ip4_process_classless_routes (priv->options, ip4_config, &gwaddr)) process_classful_routes (priv->options, ip4_config); if (gwaddr) { char buf[INET_ADDRSTRLEN + 1]; inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf)); nm_log_info (LOGD_DHCP4, " gateway %s", buf); nm_ip4_address_set_gateway (addr, gwaddr); } else { /* If the gateway wasn't provided as a classless static route with a * subnet length of 0, try to find it using the old-style 'routers' option. */ str = g_hash_table_lookup (priv->options, "new_routers"); if (str) { char **routers = g_strsplit (str, " ", 0); char **s; for (s = routers; *s; s++) { /* FIXME: how to handle multiple routers? */ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_address_set_gateway (addr, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " gateway %s", *s); break; } else nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s); } g_strfreev (routers); } } nm_ip4_config_take_address (ip4_config, addr); addr = NULL; str = g_hash_table_lookup (priv->options, "new_host_name"); if (str) nm_log_info (LOGD_DHCP4, " hostname '%s'", str); str = g_hash_table_lookup (priv->options, "new_domain_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " nameserver '%s'", *s); } else nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s); } g_strfreev (searches); } str = g_hash_table_lookup (priv->options, "new_domain_name"); if (str) { char **domains = g_strsplit (str, " ", 0); char **s; for (s = domains; *s; s++) { nm_log_info (LOGD_DHCP4, " domain name '%s'", *s); nm_ip4_config_add_domain (ip4_config, *s); } g_strfreev (domains); } str = g_hash_table_lookup (priv->options, "new_domain_search"); if (str) process_domain_search (str, ip4_add_domain_search, ip4_config); str = g_hash_table_lookup (priv->options, "new_netbios_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " wins '%s'", *s); } else nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s); } g_strfreev (searches); } str = g_hash_table_lookup (priv->options, "new_interface_mtu"); if (str) { int int_mtu; errno = 0; int_mtu = strtol (str, NULL, 10); if ((errno == EINVAL) || (errno == ERANGE)) goto error; if (int_mtu > 576) nm_ip4_config_set_mtu (ip4_config, int_mtu); } str = g_hash_table_lookup (priv->options, "new_nis_domain"); if (str) { nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str); nm_ip4_config_set_nis_domain (ip4_config, str); } str = g_hash_table_lookup (priv->options, "new_nis_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_config_add_nis_server (ip4_config, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " nis '%s'", *s); } else nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s); } g_strfreev (searches); } return ip4_config; error: if (addr) nm_ip4_address_unref (addr); g_object_unref (ip4_config); return NULL; }