static gboolean
parse_netmask (const char *str, guint32 *prefix)
{
	struct in_addr tmp_addr;
	glong tmp_prefix;

	if (!str || !*str)
		return FALSE;

	errno = 0;

	/* Is it a prefix? */
	if (!strchr (str, '.')) {
		tmp_prefix = strtol (str, NULL, 10);
		if (!errno && tmp_prefix > 0 && tmp_prefix <= 32) {
			*prefix = tmp_prefix;
			return TRUE;
		}
	}

	/* Is it a netmask? */
	if (inet_pton (AF_INET, str, &tmp_addr) > 0) {
		*prefix = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
		return TRUE;
	}

	return FALSE;
}
Example #2
0
static ip_block *
create_ip4_block (gchar * ip)
{
	ip_block *iblock = g_slice_new0 (ip_block);
	guint32 tmp_ip4_addr;
	int i;
	guint length;
	gchar **ip_mask;

	/* prefix format */
	if (strstr (ip, "/")) {
		gchar *prefix;

		ip_mask = g_strsplit (ip, "/", 0);
		length = g_strv_length (ip_mask);
		if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0]))
			goto error;
		iblock->ip = g_strdup (ip_mask[0]);
		prefix = ip_mask[1];
		i = 0;
		while (i < length && g_ascii_isdigit (prefix[i]))
			i++;
		prefix[i] = '\0';
		iblock->prefix = (guint32) atoi (ip_mask[1]);
	} else if (strstr (ip, "netmask")) {
		ip_mask = g_strsplit (ip, " ", 0);
		length = g_strv_length (ip_mask);
		if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0]))
			goto error;
		iblock->ip = g_strdup (ip_mask[0]);
		i = 0;
		while (i < length && !strstr (ip_mask[++i], "netmask")) ;
		while (i < length && ip_mask[++i][0] == '\0') ;
		if (i >= length)
			goto error;
		if (!inet_pton (AF_INET, ip_mask[i], &tmp_ip4_addr))
			goto error;
		iblock->prefix = nm_utils_ip4_netmask_to_prefix (tmp_ip4_addr);
	} else {
		g_slice_free (ip_block, iblock);
		if (!is_ip6_address (ip) && !strstr (ip, "dhcp"))
			nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, missing netmask or prefix", ip);
		return NULL;
	}
	if (iblock->prefix == 0 || iblock->prefix > 32) {
		nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, invalid prefix", ip);
		goto error;
	}
	g_strfreev (ip_mask);
	return iblock;
error:
	if (!is_ip6_address (ip))
		nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 address: %s", ip);
	g_strfreev (ip_mask);
	g_free (iblock->ip);
	g_slice_free (ip_block, iblock);
	return NULL;
}
void NetInfoIPv4::set_netmask(uint32_t mask)
{
  netdev_netmask = mask;
  memset(netmask_string, 0, sizeof(netmask_string));
  if (AM_UNLIKELY(NULL == inet_ntop(AF_INET, (void*)&netdev_netmask,
          netmask_string, INET_ADDRSTRLEN))) {
    PERROR("inet_ntop");
  }
  netdev_prefix = nm_utils_ip4_netmask_to_prefix (netdev_netmask);
}
void NetInfoIPv4::set_netmask(const char* mask)
{
  memset(netmask_string, 0, sizeof(netmask_string));
  netdev_netmask = 0;
  if (AM_LIKELY(mask)) {
    if (AM_LIKELY(inet_pton(AF_INET, mask, &netdev_netmask) > 0)) {
      strncpy(netmask_string, mask, strlen(mask));
    } else {
      PERROR("inet_pton");
    }
  }
  netdev_prefix = nm_utils_ip4_netmask_to_prefix (netdev_netmask);
}
Example #5
0
static gboolean
get_one_prefix (GtkTreeModel *model,
                GtkTreeIter *iter,
                int column,
                gboolean fail_if_missing,
                guint32 *out)
{
	char *item = NULL;
	struct in_addr tmp_addr = { 0 };
	gboolean success = FALSE;
	glong tmp_prefix;

	gtk_tree_model_get (model, iter, column, &item, -1);
	if (!item || !strlen (item)) {
		g_free (item);
		return fail_if_missing ? FALSE : TRUE;
	}

	errno = 0;

	/* Is it a prefix? */
	if (!strchr (item, '.')) {
		tmp_prefix = strtol (item, NULL, 10);
		if (!errno && tmp_prefix >= 0 && tmp_prefix <= 32) {
			*out = tmp_prefix;
			success = TRUE;
			goto out;
		}
	}

	/* Is it a netmask? */
	if (inet_pton (AF_INET, item, &tmp_addr) > 0) {
		*out = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
		success = TRUE;
	}

out:
	g_free (item);
	return success;
}
Example #6
0
static gboolean
update_ip4_setting_from_if_block(NMConnection *connection,
						   if_block *block,
						   GError **error)
{

	NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new());
	const char *type = ifparser_getkey(block, "inet");
	gboolean is_static = type && !strcmp("static", type);

	if (!is_static) {
		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
	} else {
		guint32 tmp_mask;
		NMIPAddress *addr;
		const char *address_v;
		const char *netmask_v;
		const char *gateway_v;
		const char *nameserver_v;
		const char *nameservers_v;
		const char *search_v;
		char **list, **iter;
		guint32 netmask_int = 32;

		/* Address */
		address_v = ifparser_getkey (block, "address");
		if (!address_v) {
			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
			             "Missing IPv4 address");
			goto error;
		}

		/* mask/prefix */
		netmask_v = ifparser_getkey (block, "netmask");
		if (netmask_v) {
			if (strlen (netmask_v) < 7) {
				netmask_int = atoi (netmask_v);
			} else if (!inet_pton (AF_INET, netmask_v, &tmp_mask)) {
				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
						   "Invalid IPv4 netmask '%s'", netmask_v);
				goto error;
			} else {
				netmask_int = nm_utils_ip4_netmask_to_prefix (tmp_mask);
			}
		}

		/* Add the new address to the setting */
		addr = nm_ip_address_new (AF_INET, address_v, netmask_int, error);
		if (!addr)
			goto error;

		if (nm_setting_ip_config_add_address (s_ip4, addr)) {
			nm_log_info (LOGD_SETTINGS, "addresses count: %d",
			             nm_setting_ip_config_get_num_addresses (s_ip4));
		} else {
			nm_log_info (LOGD_SETTINGS, "ignoring duplicate IP4 address");
		}
		nm_ip_address_unref (addr);

		/* gateway */
		gateway_v = ifparser_getkey (block, "gateway");
		if (gateway_v) {
			if (!nm_utils_ipaddr_valid (AF_INET, gateway_v)) {
				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
				             "Invalid IPv4 gateway '%s'", gateway_v);
				goto error;
			}
			if (!nm_setting_ip_config_get_gateway (s_ip4))
				g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_v, NULL);
		}

		nameserver_v = ifparser_getkey (block, "dns-nameserver");
		ifupdown_ip4_add_dns (s_ip4, nameserver_v);

		nameservers_v = ifparser_getkey (block, "dns-nameservers");
		ifupdown_ip4_add_dns (s_ip4, nameservers_v);

		if (!nm_setting_ip_config_get_num_dns (s_ip4))
			nm_log_info (LOGD_SETTINGS, "No dns-nameserver configured in /etc/network/interfaces");

		/* DNS searches */
		search_v = ifparser_getkey (block, "dns-search");
		if (search_v) {
			list = g_strsplit_set (search_v, " \t", -1);
			for (iter = list; iter && *iter; iter++) {
				g_strstrip (*iter);
				if (g_ascii_isspace (*iter[0]))
					continue;
				if (!nm_setting_ip_config_add_dns_search (s_ip4, *iter))
					nm_log_warn (LOGD_SETTINGS, "    duplicate DNS domain '%s'", *iter);
			}
			g_strfreev (list);
		}

		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
	}

	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
	return TRUE;

error:
	g_object_unref (s_ip4);
	return FALSE;
}
static NMIP4Config *
lease_to_ip4_config (const char *iface,
                     int ifindex,
                     sd_dhcp_lease *lease,
                     GHashTable *options,
                     guint32 default_priority,
                     gboolean log_lease,
                     GError **error)
{
	NMIP4Config *ip4_config = NULL;
	struct in_addr tmp_addr;
	const struct in_addr *addr_list;
	char buf[INET_ADDRSTRLEN];
	const char *str;
	guint32 lifetime = 0, i;
	NMPlatformIP4Address address;
	GString *l;
	gs_free sd_dhcp_route **routes = NULL;
	guint16 mtu;
	int r, num;
	guint64 end_time;
	const void *data;
	gsize data_len;
	gboolean metered = FALSE;
	gboolean static_default_gateway = FALSE;

	g_return_val_if_fail (lease != NULL, NULL);

	ip4_config = nm_ip4_config_new (ifindex);

	/* Address */
	sd_dhcp_lease_get_address (lease, &tmp_addr);
	memset (&address, 0, sizeof (address));
	address.address = tmp_addr.s_addr;
	address.peer_address = tmp_addr.s_addr;
	str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
	LOG_LEASE (LOGD_DHCP4, "  address %s", str);
	add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str);

	/* Prefix/netmask */
	sd_dhcp_lease_get_netmask (lease, &tmp_addr);
	address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
	LOG_LEASE (LOGD_DHCP4, "  plen %d", address.plen);
	add_option (options,
	            dhcp4_requests,
	            SD_DHCP_OPTION_SUBNET_MASK,
	            nm_utils_inet4_ntop (tmp_addr.s_addr, NULL));

	/* Lease time */
	sd_dhcp_lease_get_lifetime (lease, &lifetime);
	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
	address.lifetime = address.preferred = lifetime;
	end_time = (guint64) time (NULL) + lifetime;
	LOG_LEASE (LOGD_DHCP4, "  expires in %" G_GUINT32_FORMAT " seconds", lifetime);
	add_option_u64 (options,
	                dhcp4_requests,
	                SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
	                end_time);

	address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
	nm_ip4_config_add_address (ip4_config, &address);

	/* DNS Servers */
	num = sd_dhcp_lease_get_dns (lease, &addr_list);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			if (addr_list[i].s_addr) {
				nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
				str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL);
				LOG_LEASE (LOGD_DHCP4, "  nameserver '%s'", str);
				g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
			}
		}
		if (l->len)
			add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, l->str);
		g_string_free (l, TRUE);
	}

	/* Domain Name */
	r = sd_dhcp_lease_get_domainname (lease, &str);
	if (r == 0) {
		/* Multiple domains sometimes stuffed into option 15 "Domain Name".
		 * As systemd escapes such characters, split them at \\032. */
		char **domains = g_strsplit (str, "\\032", 0);
		char **s;

		for (s = domains; *s; s++) {
			LOG_LEASE (LOGD_DHCP4, "  domain name '%s'", *s);
			nm_ip4_config_add_domain (ip4_config, *s);
		}
		g_strfreev (domains);
		add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, str);
	}

	/* Hostname */
	r = sd_dhcp_lease_get_hostname (lease, &str);
	if (r == 0) {
		LOG_LEASE (LOGD_DHCP4, "  hostname '%s'", str);
		add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, str);
	}

	/* Routes */
	num = sd_dhcp_lease_get_routes (lease, &routes);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			NMPlatformIP4Route route = { 0 };
			const char *gw_str;
			guint8 plen;
			struct in_addr a;

			if (sd_dhcp_route_get_destination (routes[i], &a) < 0)
				continue;
			route.network = a.s_addr;

			if (   sd_dhcp_route_get_destination_prefix_length (routes[i], &plen) < 0
			    || plen > 32)
				continue;
			route.plen = plen;

			if (sd_dhcp_route_get_gateway (routes[i], &a) < 0)
				continue;
			route.gateway = a.s_addr;

			if (route.plen) {
				route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
				route.metric = default_priority;
				nm_ip4_config_add_route (ip4_config, &route);

				str = nm_utils_inet4_ntop (route.network, buf);
				gw_str = nm_utils_inet4_ntop (route.gateway, NULL);
				LOG_LEASE (LOGD_DHCP4, "  static route %s/%d gw %s", str, route.plen, gw_str);

				g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str);
			} else {
				if (!static_default_gateway) {
					static_default_gateway = TRUE;
					nm_ip4_config_set_gateway (ip4_config, route.gateway);

					str = nm_utils_inet4_ntop (route.gateway, NULL);
					LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
					add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str);
				}
			}
		}
		if (l->len)
			add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, l->str);
		g_string_free (l, TRUE);
	}

	/* If the DHCP server returns both a Classless Static Routes option and a
	 * Router option, the DHCP client MUST ignore the Router option [RFC 3442].
	 * Be more lenient and ignore the Router option only if Classless Static
	 * Routes contain a default gateway (as other DHCP backends do).
	 */
	/* Gateway */
	if (!static_default_gateway) {
		r = sd_dhcp_lease_get_router (lease, &tmp_addr);
		if (r == 0) {
			nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr);
			str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
			LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
			add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str);
		}
	}

	/* MTU */
	r = sd_dhcp_lease_get_mtu (lease, &mtu);
	if (r == 0 && mtu) {
		nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP);
		add_option_u32 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu);
		LOG_LEASE (LOGD_DHCP4, "  mtu %u", mtu);
	}

	/* NTP servers */
	num = sd_dhcp_lease_get_ntp (lease, &addr_list);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf);
			LOG_LEASE (LOGD_DHCP4, "  ntp server '%s'", str);
			g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
		}
		add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, l->str);
		g_string_free (l, TRUE);
	}

	r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len);
	if (r >= 0)
		metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED"));
	nm_ip4_config_set_metered (ip4_config, metered);

	return ip4_config;
}
static gboolean
nm_openssh_send_ip4_config (sshtun_handle_t handle)
{
	DBusGConnection *connection;
	GError *err = NULL;
	GHashTable *config;
	GValue *val;
	struct in_addr temp_addr;
	const char *tmp;

	connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
	if (!connection)
		return FALSE;

	config = g_hash_table_new (g_str_hash, g_str_equal);

	/* Gateway */
	val = addr_to_gvalue (sshtun_get_param (handle, SSHTUN_PARAM_GW_ADDR));
	if (val)
		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY, val);
	else {
		helper_failed (connection, "VPN Gateway");
		dbus_g_connection_unref (connection);
		return FALSE;
	}

	/* Tunnel device */
	val = str_to_gvalue (sshtun_get_param (handle, SSHTUN_PARAM_TUN_DEV),
						 FALSE);
	if (val)
		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, val);
	else {
		helper_failed (connection, "Tunnel Device");
		dbus_g_connection_unref (connection);
		return FALSE;
	}

	/* IP address */
	val = addr_to_gvalue (sshtun_get_param (handle, SSHTUN_PARAM_ADDR));
	if (val)
		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
	else {
		helper_failed (connection, "IP4 Address");
		dbus_g_connection_unref (connection);
		return FALSE;
	}

	/* PTP address */
	val = addr_to_gvalue (sshtun_get_param (handle, SSHTUN_PARAM_PEER_ADDR));
	if (val)
		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);

	/* Netmask */
	tmp = sshtun_get_param (handle, SSHTUN_PARAM_NETMASK);
	if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) {
		val = uint_to_gvalue (nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
		g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
	}

	/* MTU */
	tmp = sshtun_get_param (handle, SSHTUN_PARAM_MTU);
	if (tmp && strlen (tmp)) {
		long int mtu;

		errno = 0;
		mtu = strtol (tmp, NULL, 10);
		if (errno || mtu < 0 || mtu > 20000) {
			nm_warning ("Ignoring invalid tunnel MTU '%s'", tmp);
		} else {
			val = uint_to_gvalue ((guint32) mtu);
			g_hash_table_insert (config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, val);
		}
	}

	send_ip4_config (connection, config);
	dbus_g_connection_unref (connection);

	return TRUE;
}
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;
}
int
main (int argc, char *argv[])
{
	GDBusProxy *proxy;
	GVariantBuilder builder, ip4builder, ip6builder;
	GVariant *ip4config, *ip6config;
	char *tmp;
	GVariant *val;
	int i;
	GError *err = NULL;
	GPtrArray *dns4_list, *dns6_list;
	GPtrArray *nbns_list;
	GPtrArray *dns_domains;
	struct in_addr temp_addr;
	int tapdev = -1;
	char **iter;
	int shift = 0;
	gboolean is_restart;
	gboolean has_ip4_prefix = FALSE;
	gboolean has_ip4_address = FALSE;
	gboolean has_ip6_address = FALSE;
	gchar *bus_name = NM_DBUS_SERVICE_OPENVPN;

#if !GLIB_CHECK_VERSION (2, 35, 0)
	g_type_init ();
#endif

	for (i = 1; i < argc; i++) {
		if (!strcmp (argv[i], "--")) {
			i++;
			break;
		}
		if (nm_streq (argv[i], "--debug")) {
			if (i + 2 >= argc) {
				g_printerr ("Missing debug arguments (requires <LEVEL> <PREFIX_TOKEN>)\n");
				exit (1);
			}
			gl.log_level = _nm_utils_ascii_str_to_int64 (argv[++i], 10, 0, LOG_DEBUG, 0);
			gl.log_prefix_token = argv[++i];
		} else if (!strcmp (argv[i], "--tun"))
			tapdev = 0;
		else if (!strcmp (argv[i], "--tap"))
			tapdev = 1;
		else if (!strcmp (argv[i], "--bus-name")) {
			if (++i == argc) {
				g_printerr ("Missing bus name argument\n");
				exit (1);
			}
			if (!g_dbus_is_name (argv[i])) {
				g_printerr ("Invalid bus name\n");
				exit (1);
			}
			bus_name = argv[i];
		} else
			break;
	}
	shift = i - 1;

	if (_LOGD_enabled ()) {
		GString *args;

		args = g_string_new (NULL);
		for (i = 0; i < argc; i++) {
			if (i > 0)
				g_string_append_c (args, ' ');
			if (shift && 1 + shift == i)
				g_string_append (args, "  ");
			tmp = g_strescape (argv[i], NULL);
			g_string_append_printf (args, "\"%s\"", tmp);
			g_free (tmp);
		}

		_LOGD ("command line: %s", args->str);
		g_string_free (args, TRUE);

		for (iter = environ; iter && *iter; iter++)
			_LOGD ("environment: %s", *iter);
	}

	/* shift the arguments to the right leaving only those provided by openvpn */
	argv[shift] = argv[0];
	argv += shift;
	argc -= shift;

	is_restart = argc >= 7 && !g_strcmp0 (argv[6], "restart");

	proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
	                                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
	                                       NULL,
	                                       bus_name,
	                                       NM_VPN_DBUS_PLUGIN_PATH,
	                                       NM_VPN_DBUS_PLUGIN_INTERFACE,
	                                       NULL, &err);
	if (!proxy) {
		_LOGW ("Could not create a D-Bus proxy: %s", err->message);
		g_error_free (err);
		exit (1);
	}

	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
	g_variant_builder_init (&ip4builder, G_VARIANT_TYPE_VARDICT);
	g_variant_builder_init (&ip6builder, G_VARIANT_TYPE_VARDICT);

	/* External world-visible VPN gateway */
	val = trusted_remote_to_gvariant ();
	if (val)
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, val);
	else
		helper_failed (proxy, "VPN Gateway");

	/* Internal VPN subnet gateway */
	tmp = getenv ("route_vpn_gateway");
	val = addr4_to_gvariant (tmp);
	if (val)
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
	else {
		val = addr6_to_gvariant (tmp);
		if (val)
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, val);
	}

	/* VPN device */
	tmp = getenv ("dev");
	val = str_to_gvariant (tmp, FALSE);
	if (val)
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV, val);
	else
		helper_failed (proxy, "Tunnel Device");

	if (tapdev == -1)
		tapdev = strncmp (tmp, "tap", 3) == 0;

	/* IPv4 address */
	tmp = getenv ("ifconfig_local");
	if (!tmp && is_restart)
		tmp = argv[4];
	if (tmp && strlen (tmp)) {
		val = addr4_to_gvariant (tmp);
		if (val) {
			has_ip4_address = TRUE;
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
		} else
			helper_failed (proxy, "IP4 Address");
	}

	/* PTP address; for vpnc PTP address == internal IP4 address */
	tmp = getenv ("ifconfig_remote");
	if (!tmp && is_restart)
		tmp = argv[5];
	val = addr4_to_gvariant (tmp);
	if (val) {
		/* Sigh.  Openvpn added 'topology' stuff in 2.1 that changes the meaning
		 * of the ifconfig bits without actually telling you what they are
		 * supposed to mean; basically relying on specific 'ifconfig' behavior.
		 */
		if (tmp && !strncmp (tmp, "255.", 4)) {
			guint32 addr;

			/* probably a netmask, not a PTP address; topology == subnet */
			addr = g_variant_get_uint32 (val);
			g_variant_unref (val);
			val = g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (addr));
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
			has_ip4_prefix = TRUE;
		} else
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
	}

	/* Netmask
	 *
	 * Either TAP or TUN modes can have an arbitrary netmask in newer versions
	 * of openvpn, while in older versions only TAP mode would.  So accept a
	 * netmask if passed, otherwise default to /32 for TUN devices since they
	 * are usually point-to-point.
	 */
	tmp = getenv ("ifconfig_netmask");
	if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) {
		val = g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
	} else if (!tapdev) {
		if (has_ip4_address && !has_ip4_prefix) {
			val = g_variant_new_uint32 (32);
			g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
		}
	} else
		_LOGW ("No IP4 netmask/prefix (missing or invalid 'ifconfig_netmask')");

	val = get_ip4_routes ();
	if (val)
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val);
	else if (is_restart) {
		g_variant_builder_add (&ip4builder, "{sv}",
		                       NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES,
		                       g_variant_new_boolean (TRUE));
	}

	/* IPv6 address */
	tmp = getenv ("ifconfig_ipv6_local");
	if (tmp && strlen (tmp)) {
		val = addr6_to_gvariant (tmp);
		if (val) {
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, val);
			has_ip6_address = TRUE;
		} else
			helper_failed (proxy, "IP6 Address");
	}

	/* IPv6 remote address */
	tmp = getenv ("ifconfig_ipv6_remote");
	if (tmp && strlen (tmp)) {
		val = addr6_to_gvariant (tmp);
		if (val)
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PTP, val);
		else
			helper_failed (proxy, "IP6 PTP Address");
	}

	/* IPv6 netbits */
	tmp = getenv ("ifconfig_ipv6_netbits");
	if (tmp && strlen (tmp)) {
		long int netbits;

		errno = 0;
		netbits = strtol (tmp, NULL, 10);
		if (errno || netbits < 0 || netbits > 128) {
			_LOGW ("Ignoring invalid prefix '%s'", tmp);
		} else {
			val = g_variant_new_uint32 ((guint32) netbits);
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, val);
		}
	}

	val = get_ip6_routes ();
	if (val)
		g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, val);
	else if (is_restart) {
		g_variant_builder_add (&ip6builder, "{sv}",
		                       NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES,
		                       g_variant_new_boolean (TRUE));
	}

	/* DNS and WINS servers */
	dns_domains = g_ptr_array_sized_new (3);
	dns4_list = g_ptr_array_new ();
	dns6_list = g_ptr_array_new ();
	nbns_list = g_ptr_array_new ();

	for (i = 1; i < 256; i++) {
		char *env_name;

		env_name = g_strdup_printf ("foreign_option_%d", i);
		tmp = getenv (env_name);
		g_free (env_name);

		if (!tmp || strlen (tmp) < 1)
			break;

		if (!g_str_has_prefix (tmp, "dhcp-option "))
			continue;

		tmp += 12; /* strlen ("dhcp-option ") */

		if (g_str_has_prefix (tmp, "DNS "))
			parse_addr_list (dns4_list, dns6_list, tmp + 4);
		else if (g_str_has_prefix (tmp, "WINS "))
			parse_addr_list (nbns_list, NULL, tmp + 5);
		else if (g_str_has_prefix (tmp, "DOMAIN ") && is_domain_valid (tmp + 7))
			g_ptr_array_add (dns_domains, tmp + 7);
	}

	if (dns4_list->len) {
		val = g_variant_new_array (G_VARIANT_TYPE_UINT32, (GVariant **) dns4_list->pdata, dns4_list->len);
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, val);
	}

	if (has_ip6_address && dns6_list->len) {
		val = g_variant_new_array (G_VARIANT_TYPE ("ay"), (GVariant **) dns6_list->pdata, dns6_list->len);
		g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DNS, val);
	}

	if (nbns_list->len) {
		val = g_variant_new_array (G_VARIANT_TYPE_UINT32, (GVariant **) nbns_list->pdata, nbns_list->len);
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS, val);
	}

	if (dns_domains->len) {
		val = g_variant_new_strv ((const gchar **) dns_domains->pdata, dns_domains->len);
		g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, val);

		/* Domains apply to both IPv4 and IPv6 configurations */
		if (has_ip6_address) {
			val = g_variant_new_strv ((const gchar **) dns_domains->pdata, dns_domains->len);
			g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, val);
		}
	}

	g_ptr_array_unref (dns4_list);
	g_ptr_array_unref (dns6_list);
	g_ptr_array_unref (nbns_list);
	g_ptr_array_unref (dns_domains);

	/* Tunnel MTU */
	tmp = getenv ("tun_mtu");
	if (tmp && strlen (tmp)) {
		long int mtu;

		errno = 0;
		mtu = strtol (tmp, NULL, 10);
		if (errno || mtu < 0 || mtu > 20000) {
			_LOGW ("Ignoring invalid tunnel MTU '%s'", tmp);
		} else {
			val = g_variant_new_uint32 ((guint32) mtu);
			g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_MTU, val);
		}
	}

	ip4config = g_variant_builder_end (&ip4builder);

	if (g_variant_n_children (ip4config)) {
		val = g_variant_new_boolean (TRUE);
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP4, val);
	} else {
		g_variant_unref (ip4config);
		ip4config = NULL;
	}

	ip6config = g_variant_builder_end (&ip6builder);

	if (g_variant_n_children (ip6config)) {
		val = g_variant_new_boolean (TRUE);
		g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP6, val);
	} else {
		g_variant_unref (ip6config);
		ip6config = NULL;
	}

	if (!ip4config && !ip6config)
		helper_failed (proxy, "IPv4 or IPv6 configuration");

	/* Send the config info to nm-openvpn-service */
	send_config (proxy, g_variant_builder_end (&builder), ip4config, ip6config);

	g_object_unref (proxy);

	return 0;
}
static GVariant *
get_ip4_routes (void)
{
	GVariantBuilder builder;
	GVariant *value;
	char *tmp;
	int i, size = 0;

	g_variant_builder_init (&builder, G_VARIANT_TYPE ("aau"));

	for (i = 1; i < 256; i++) {
		GVariantBuilder array;
		char buf[BUFLEN];
		struct in_addr network;
		struct in_addr netmask;
		struct in_addr gateway = { 0, };
		guint32 prefix, metric = 0;

		snprintf (buf, BUFLEN, "route_network_%d", i);
		tmp = getenv (buf);
		if (!tmp || strlen (tmp) < 1)
			break;

		if (inet_pton (AF_INET, tmp, &network) <= 0) {
			_LOGW ("Ignoring invalid static route address '%s'", tmp ? tmp : "NULL");
			continue;
		}

		snprintf (buf, BUFLEN, "route_netmask_%d", i);
		tmp = getenv (buf);
		if (!tmp || inet_pton (AF_INET, tmp, &netmask) <= 0) {
			_LOGW ("Ignoring invalid static route netmask '%s'", tmp ? tmp : "NULL");
			continue;
		}

		snprintf (buf, BUFLEN, "route_gateway_%d", i);
		tmp = getenv (buf);
		/* gateway can be missing */
		if (tmp && (inet_pton (AF_INET, tmp, &gateway) <= 0)) {
			_LOGW ("Ignoring invalid static route gateway '%s'", tmp ? tmp : "NULL");
			continue;
		}

		snprintf (buf, BUFLEN, "route_metric_%d", i);
		tmp = getenv (buf);
		/* metric can be missing */
		if (tmp && strlen (tmp)) {
			long int tmp_metric;

			errno = 0;
			tmp_metric = strtol (tmp, NULL, 10);
			if (errno || tmp_metric < 0 || tmp_metric > G_MAXUINT32) {
				_LOGW ("Ignoring invalid static route metric '%s'", tmp);
				continue;
			}
			metric = (guint32) tmp_metric;
		}

		g_variant_builder_init (&array, G_VARIANT_TYPE ("au"));
		g_variant_builder_add_value (&array, g_variant_new_uint32 (network.s_addr));
		prefix = nm_utils_ip4_netmask_to_prefix (netmask.s_addr);
		g_variant_builder_add_value (&array, g_variant_new_uint32 (prefix));
		g_variant_builder_add_value (&array, g_variant_new_uint32 (gateway.s_addr));
		g_variant_builder_add_value (&array, g_variant_new_uint32 (metric));
		g_variant_builder_add_value (&builder, g_variant_builder_end (&array));
		size++;
	}

	value = g_variant_builder_end (&builder);
	if (size > 0)
		return value;

	g_variant_unref (value);
	return NULL;
}
static NMIP4Config *
lease_to_ip4_config (const char *iface,
                     int ifindex,
                     sd_dhcp_lease *lease,
                     GHashTable *options,
                     guint32 default_priority,
                     gboolean log_lease,
                     GError **error)
{
	NMIP4Config *ip4_config = NULL;
	struct in_addr tmp_addr;
	const struct in_addr *addr_list;
	char buf[INET_ADDRSTRLEN];
	const char *str;
	guint32 lifetime = 0, i;
	NMPlatformIP4Address address;
	GString *l;
	struct sd_dhcp_route *routes;
	guint16 mtu;
	int r, num;
	guint64 end_time;
	const void *data;
	gsize data_len;
	gboolean metered = FALSE;

	g_return_val_if_fail (lease != NULL, NULL);

	ip4_config = nm_ip4_config_new (ifindex);

	/* Address */
	sd_dhcp_lease_get_address (lease, &tmp_addr);
	memset (&address, 0, sizeof (address));
	address.address = tmp_addr.s_addr;
	address.peer_address = tmp_addr.s_addr;
	str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
	LOG_LEASE (LOGD_DHCP4, "  address %s", str);
	add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str);

	/* Prefix/netmask */
	sd_dhcp_lease_get_netmask (lease, &tmp_addr);
	address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
	LOG_LEASE (LOGD_DHCP4, "  plen %d", address.plen);
	add_option (options,
	            dhcp4_requests,
	            DHCP_OPTION_SUBNET_MASK,
	            nm_utils_inet4_ntop (tmp_addr.s_addr, NULL));

	/* Lease time */
	sd_dhcp_lease_get_lifetime (lease, &lifetime);
	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
	address.lifetime = address.preferred = lifetime;
	end_time = (guint64) time (NULL) + lifetime;
	LOG_LEASE (LOGD_DHCP4, "  expires in %" G_GUINT32_FORMAT " seconds", lifetime);
	add_option_u64 (options,
	                dhcp4_requests,
	                DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
	                end_time);

	address.source = NM_IP_CONFIG_SOURCE_DHCP;
	nm_ip4_config_add_address (ip4_config, &address);

	/* Gateway */
	r = sd_dhcp_lease_get_router (lease, &tmp_addr);
	if (r == 0) {
		nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr);
		str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
		LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
		add_option (options, dhcp4_requests, DHCP_OPTION_ROUTER, str);
	}

	/* DNS Servers */
	num = sd_dhcp_lease_get_dns (lease, &addr_list);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			if (addr_list[i].s_addr) {
				nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
				str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL);
				LOG_LEASE (LOGD_DHCP4, "  nameserver '%s'", str);
				g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
			}
		}
		if (l->len)
			add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME_SERVER, l->str);
		g_string_free (l, TRUE);
	}

	/* Domain Name */
	r = sd_dhcp_lease_get_domainname (lease, &str);
	if (r == 0) {
		/* Multiple domains sometimes stuffed into the option */
		char **domains = g_strsplit (str, " ", 0);
		char **s;

		for (s = domains; *s; s++) {
			LOG_LEASE (LOGD_DHCP4, "  domain name '%s'", *s);
			nm_ip4_config_add_domain (ip4_config, *s);
		}
		g_strfreev (domains);
		add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME, str);
	}

	/* Hostname */
	r = sd_dhcp_lease_get_hostname (lease, &str);
	if (r == 0) {
		LOG_LEASE (LOGD_DHCP4, "  hostname '%s'", str);
		add_option (options, dhcp4_requests, DHCP_OPTION_HOST_NAME, str);
	}

	/* Routes */
	num = sd_dhcp_lease_get_routes (lease, &routes);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			NMPlatformIP4Route route;
			const char *gw_str;

			memset (&route, 0, sizeof (route));
			route.network = routes[i].dst_addr.s_addr;
			route.plen = routes[i].dst_prefixlen;
			route.gateway = routes[i].gw_addr.s_addr;
			route.source = NM_IP_CONFIG_SOURCE_DHCP;
			route.metric = default_priority;
			nm_ip4_config_add_route (ip4_config, &route);

			str = nm_utils_inet4_ntop (route.network, buf);
			gw_str = nm_utils_inet4_ntop (route.gateway, NULL);
			LOG_LEASE (LOGD_DHCP4, "  static route %s/%d gw %s", str, route.plen, gw_str);

			g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str);
		}
		add_option (options, dhcp4_requests, DHCP_OPTION_RFC3442_ROUTES, l->str);
		g_string_free (l, TRUE);
	}

	/* MTU */
	r = sd_dhcp_lease_get_mtu (lease, &mtu);
	if (r == 0 && mtu) {
		nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP);
		add_option_u32 (options, dhcp4_requests, DHCP_OPTION_INTERFACE_MTU, mtu);
		LOG_LEASE (LOGD_DHCP4, "  mtu %u", mtu);
	}

	/* NTP servers */
	num = sd_dhcp_lease_get_ntp (lease, &addr_list);
	if (num > 0) {
		l = g_string_sized_new (30);
		for (i = 0; i < num; i++) {
			str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf);
			LOG_LEASE (LOGD_DHCP4, "  ntp server '%s'", str);
			g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
		}
		add_option (options, dhcp4_requests, DHCP_OPTION_NTP_SERVER, l->str);
		g_string_free (l, TRUE);
	}

	r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len);
	if (r >= 0)
		metered = !!memmem (data, data_len, "ANDROID_METERED", STRLEN ("ANDROID_METERED"));
	nm_ip4_config_set_metered (ip4_config, metered);

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