void
nm_dhcp_client_stop (NMDHCPClient *self, gboolean release)
{
	NMDHCPClientPrivate *priv;

	g_return_if_fail (self != NULL);
	g_return_if_fail (NM_IS_DHCP_CLIENT (self));

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	/* Kill the DHCP client */
	if (!priv->dead) {
		NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release);
		priv->dead = TRUE;

		nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d",
		             priv->iface, priv->pid);
	}

	/* And clean stuff up */

	priv->pid = -1;
	dhcp_client_set_state (self, DHC_END, FALSE, TRUE);

	g_hash_table_remove_all (priv->options);

	timeout_cleanup (self);
	watch_cleanup (self);
}
gboolean
nm_dhcp_client_start_ip6 (NMDHCPClient *self,
                          NMSettingIP6Config *s_ip6,
                          guint8 *dhcp_anycast_addr,
                          const char *hostname,
                          gboolean info_only)
{
	NMDHCPClientPrivate *priv;

	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
	g_return_val_if_fail (priv->pid == -1, FALSE);
	g_return_val_if_fail (priv->ipv6 == TRUE, FALSE);
	g_return_val_if_fail (priv->uuid != NULL, FALSE);

	priv->info_only = info_only;

	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
	             priv->iface, priv->timeout);

	priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, hostname, info_only);
	if (priv->pid > 0)
		start_monitor (self);

	return priv->pid ? TRUE : FALSE;
}
gboolean
nm_dhcp_client_get_ipv6 (NMDHCPClient *self)
{
	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6;
}
const char *
nm_dhcp_client_get_iface (NMDHCPClient *self)
{
	g_return_val_if_fail (self != NULL, NULL);
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface;
}
GPid
nm_dhcp_client_get_pid (NMDHCPClient *self)
{
	g_return_val_if_fail (self != NULL, -1);
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid;
}
void
nm_dhcp_client_new_options (NMDHCPClient *self,
                            GHashTable *options,
                            const char *reason)
{
	NMDHCPClientPrivate *priv;
	guint32 old_state;
	guint32 new_state;

	g_return_if_fail (self != NULL);
	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
	g_return_if_fail (options != NULL);
	g_return_if_fail (reason != NULL);

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
	old_state = priv->state;
	new_state = string_to_state (reason);

	/* Clear old and save new DHCP options */
	g_hash_table_remove_all (priv->options);
	g_hash_table_foreach (options, copy_option, priv->options);

	if (old_state == new_state) {
		/* dhclient will stay in the same state (or, really, provide the same
		 * reason) for operations like RENEW and REBIND.  We need to ensure
		 * that triggers various DHCP lease change code, so we need to pass
		 * along same-state transitions for these states.
		 */
		if (   new_state != DHC_BOUND4
		    && new_state != DHC_RENEW4
		    && new_state != DHC_REBIND4
		    && new_state != DHC_BOUND6
		    && new_state != DHC_RENEW6
		    && new_state != DHC_REBIND6)
			return;
	}

	/* Handle changed device state */
	if (state_is_bound (new_state)) {
		/* Cancel the timeout if the DHCP client is now bound */
		timeout_cleanup (self);
	}

	if (priv->ipv6) {
		nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s",
		            priv->iface,
		            state_to_string (old_state),
		            state_to_string (new_state));
	} else {
		nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s",
		            priv->iface,
		            state_to_string (old_state),
		            state_to_string (new_state));
	}

	dhcp_client_set_state (self, new_state, TRUE, FALSE);
}
gboolean
nm_dhcp_client_foreach_option (NMDHCPClient *self,
                               GHFunc func,
                               gpointer user_data)
{
	NMDHCPClientPrivate *priv;
	GHashTableIter iter;
	gpointer iterkey, itervalue;

	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
	g_return_val_if_fail (func != NULL, FALSE);

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	if (!state_is_bound (priv->state)) {
		if (priv->ipv6) {
			nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
		} else {
			nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
		}
	}

	g_hash_table_iter_init (&iter, priv->options);
	while (g_hash_table_iter_next (&iter, &iterkey, &itervalue)) {
		const char *key = iterkey, *value = itervalue;
		const char **p;
		static const char *filter_options[] = {
			"interface", "pid", "reason", "dhcp_message_type", NULL
		};
		gboolean ignore = FALSE;

		/* Filter out stuff that's not actually new DHCP options */
		for (p = filter_options; *p; p++) {
			if (!strcmp (*p, key) || !strncmp (key, OLD_TAG, strlen (OLD_TAG))) {
				ignore = TRUE;
				break;
			}
		}

		if (!ignore) {
			const char *tmp_key = key;

			/* Remove the "new_" prefix that dhclient passes back */
			if (!strncmp (key, NEW_TAG, strlen (NEW_TAG)))
				tmp_key = key + strlen (NEW_TAG);

			func ((gpointer) tmp_key, (gpointer) value, user_data);
		}
	}
	return TRUE;
}
gboolean
nm_dhcp_client_start_ip6 (NMDHCPClient *self,
                          NMSettingIP6Config *s_ip6,
                          guint8 *dhcp_anycast_addr,
                          const char *hostname,
                          gboolean info_only)
{
	NMDHCPClientPrivate *priv;
	char *escaped;

	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
	g_return_val_if_fail (priv->pid == -1, FALSE);
	g_return_val_if_fail (priv->ipv6 == TRUE, FALSE);
	g_return_val_if_fail (priv->uuid != NULL, FALSE);

	/* If we don't have one yet, read the default DUID for this DHCPv6 client
	 * from the client-specific persistent configuration.
	 */
	if (!priv->duid)
		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);

	if (nm_logging_level_enabled (LOGL_DEBUG)) {
		escaped = escape_duid (priv->duid);
		nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped);
		g_free (escaped);
	}

	priv->info_only = info_only;

	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
	             priv->iface, priv->timeout);

	priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
	                                                        s_ip6,
	                                                        dhcp_anycast_addr,
	                                                        hostname,
	                                                        info_only,
	                                                        priv->duid);
	if (priv->pid > 0)
		start_monitor (self);

	return priv->pid ? TRUE : FALSE;
}
Esempio n. 9
0
void
nm_dhcp_client_new_options (NMDHCPClient *self,
                            GHashTable *options,
                            const char *reason)
{
	NMDHCPClientPrivate *priv;
	guint32 old_state;
	guint32 new_state;

	g_return_if_fail (self != NULL);
	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
	g_return_if_fail (options != NULL);
	g_return_if_fail (reason != NULL);

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
	old_state = priv->state;
	new_state = string_to_state (reason);

	/* Clear old and save new DHCP options */
	g_hash_table_remove_all (priv->options);
	g_hash_table_foreach (options, copy_option, priv->options);

	if (old_state == new_state)
		return;

	/* Handle changed device state */
	if (state_is_bound (new_state)) {
		/* Cancel the timeout if the DHCP client is now bound */
		timeout_cleanup (self);
	}

	if (priv->ipv6) {
		nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s",
		            priv->iface,
		            state_to_string (old_state),
		            state_to_string (new_state));
	} else {
		nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s",
		            priv->iface,
		            state_to_string (old_state),
		            state_to_string (new_state));
	}

	dhcp_client_set_state (self, new_state, TRUE, FALSE);
}
static void
stop (NMDHCPClient *self, gboolean release)
{
	NMDHCPClientPrivate *priv;

	g_return_if_fail (self != NULL);
	g_return_if_fail (NM_IS_DHCP_CLIENT (self));

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
	g_return_if_fail (priv->pid > 0);

	/* Clean up the watch handler since we're explicitly killing the daemon */
	watch_cleanup (self);

	nm_dhcp_client_stop_pid (priv->pid, priv->iface, 0);

	priv->info_only = FALSE;
}
NMIP6Config *
nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test)
{
	NMDHCPClientPrivate *priv;

	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);

	if (test && !state_is_bound (priv->state)) {
		nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
		return NULL;
	}

	if (!g_hash_table_size (priv->options)) {
		/* We never got a response from the DHCP client */
		return NULL;
	}

	return ip6_options_to_config (self);
}
/* 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;
}
/* 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;
}
Esempio n. 14
0
/* 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;
}