diff options
Diffstat (limited to 'src/dhcp-manager/nm-dhcp-client.c')
-rw-r--r-- | src/dhcp-manager/nm-dhcp-client.c | 1524 |
1 files changed, 401 insertions, 1123 deletions
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 03c11c74d..47fa53171 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -17,7 +17,8 @@ * */ -#include <config.h> +#include "config.h" + #include <glib.h> #include <string.h> #include <sys/types.h> @@ -33,35 +34,35 @@ #include "nm-logging.h" #include "nm-dbus-glib-types.h" #include "nm-dhcp-client.h" +#include "nm-dhcp-utils.h" +#include "nm-platform.h" typedef struct { char * iface; + int ifindex; GByteArray * hwaddr; gboolean ipv6; char * uuid; - guint priority; + guint32 priority; guint32 timeout; GByteArray * duid; + GBytes * client_id; + char * hostname; - guchar state; - GPid pid; - gboolean dead; + NMDhcpState state; + pid_t pid; guint timeout_id; guint watch_id; - guint32 remove_id; - GHashTable * options; gboolean info_only; -} NMDHCPClientPrivate; +} NMDhcpClientPrivate; -#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDHCPClientPrivate)) +#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate)) -G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {}) +G_DEFINE_TYPE_EXTENDED (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {}) enum { SIGNAL_STATE_CHANGED, - SIGNAL_TIMEOUT, - SIGNAL_REMOVE, LAST_SIGNAL }; @@ -70,6 +71,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, PROP_IFACE, + PROP_IFINDEX, PROP_HWADDR, PROP_IPV6, PROP_UUID, @@ -80,8 +82,8 @@ enum { /********************************************/ -GPid -nm_dhcp_client_get_pid (NMDHCPClient *self) +pid_t +nm_dhcp_client_get_pid (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1); @@ -89,15 +91,23 @@ nm_dhcp_client_get_pid (NMDHCPClient *self) } const char * -nm_dhcp_client_get_iface (NMDHCPClient *self) +nm_dhcp_client_get_iface (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface; } +int +nm_dhcp_client_get_ifindex (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->ifindex; +} + gboolean -nm_dhcp_client_get_ipv6 (NMDHCPClient *self) +nm_dhcp_client_get_ipv6 (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); @@ -105,19 +115,120 @@ nm_dhcp_client_get_ipv6 (NMDHCPClient *self) } const char * -nm_dhcp_client_get_uuid (NMDHCPClient *self) +nm_dhcp_client_get_uuid (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid; } +const GByteArray * +nm_dhcp_client_get_duid (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid; +} + +const GByteArray * +nm_dhcp_client_get_hw_addr (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr; +} + +guint32 +nm_dhcp_client_get_priority (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), G_MAXUINT32); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority; +} + +GBytes * +nm_dhcp_client_get_client_id (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->client_id; +} + +void +nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id) +{ + NMDhcpClientPrivate *priv; + + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (priv->client_id && client_id && g_bytes_equal (priv->client_id, client_id)) + return; + g_clear_pointer (&priv->client_id, g_bytes_unref); + priv->client_id = client_id ? g_bytes_ref (client_id) : NULL; +} + +const char * +nm_dhcp_client_get_hostname (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname; +} + +/********************************************/ + +static const char *state_table[NM_DHCP_STATE_MAX + 1] = { + [NM_DHCP_STATE_UNKNOWN] = "unknown", + [NM_DHCP_STATE_BOUND] = "bound", + [NM_DHCP_STATE_TIMEOUT] = "timeout", + [NM_DHCP_STATE_EXPIRE] = "expire", + [NM_DHCP_STATE_DONE] = "done", + [NM_DHCP_STATE_FAIL] = "fail", +}; + +static const char * +state_to_string (NMDhcpState state) +{ + if ((gsize) state < G_N_ELEMENTS (state_table)) + return state_table[state]; + return NULL; +} + +static NMDhcpState +reason_to_state (const char *iface, const char *reason) +{ + if (g_ascii_strcasecmp (reason, "bound") == 0 || + g_ascii_strcasecmp (reason, "bound6") == 0 || + g_ascii_strcasecmp (reason, "renew") == 0 || + g_ascii_strcasecmp (reason, "renew6") == 0 || + g_ascii_strcasecmp (reason, "reboot") == 0 || + g_ascii_strcasecmp (reason, "rebind") == 0 || + g_ascii_strcasecmp (reason, "rebind6") == 0) + return NM_DHCP_STATE_BOUND; + else if (g_ascii_strcasecmp (reason, "timeout") == 0) + return NM_DHCP_STATE_TIMEOUT; + else if (g_ascii_strcasecmp (reason, "nak") == 0 || + g_ascii_strcasecmp (reason, "expire") == 0 || + g_ascii_strcasecmp (reason, "expire6") == 0) + return NM_DHCP_STATE_EXPIRE; + else if (g_ascii_strcasecmp (reason, "end") == 0) + return NM_DHCP_STATE_DONE; + else if (g_ascii_strcasecmp (reason, "fail") == 0 || + g_ascii_strcasecmp (reason, "abend") == 0) + return NM_DHCP_STATE_FAIL; + + nm_log_dbg (LOGD_DHCP, "(%s): unmapped DHCP state '%s'", iface, reason); + return NM_DHCP_STATE_UNKNOWN; +} + /********************************************/ static void -timeout_cleanup (NMDHCPClient *self) +timeout_cleanup (NMDhcpClient *self) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); if (priv->timeout_id) { g_source_remove (priv->timeout_id); @@ -126,9 +237,9 @@ timeout_cleanup (NMDHCPClient *self) } static void -watch_cleanup (NMDHCPClient *self) +watch_cleanup (NMDhcpClient *self) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); if (priv->watch_id) { g_source_remove (priv->watch_id); @@ -137,171 +248,158 @@ watch_cleanup (NMDHCPClient *self) } void -nm_dhcp_client_stop_pid (GPid pid, const char *iface) +nm_dhcp_client_stop_pid (pid_t pid, const char *iface) { - int i = 5; /* roughly 0.5 seconds */ - - g_return_if_fail (pid > 0); - - /* Tell it to quit; maybe it wants to send out a RELEASE message */ - kill (pid, SIGTERM); - - while (i-- > 0) { - gint child_status; - int ret; + char *name = iface ? g_strdup_printf ("dhcp-client-%s", iface) : NULL; - ret = waitpid (pid, &child_status, WNOHANG); - if (ret > 0) - break; + g_return_if_fail (pid > 1); - if (ret == -1) { - /* Child already exited */ - if (errno == ECHILD) { - /* Was it really our child and it exited? */ - if (kill (pid, 0) < 0 && errno == ESRCH) - break; - } else { - /* Took too long; shoot it in the head */ - i = 0; - break; - } - } - g_usleep (G_USEC_PER_SEC / 10); - } - - if (i <= 0) { - if (iface) { - nm_log_warn (LOGD_DHCP, "(%s): DHCP client pid %d didn't exit, will kill it.", - iface, pid); - } - kill (pid, SIGKILL); - - nm_log_dbg (LOGD_DHCP, "waiting for DHCP client pid %d to exit", pid); - waitpid (pid, NULL, 0); - nm_log_dbg (LOGD_DHCP, "DHCP client pid %d cleaned up", pid); - } + nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, name ? name : "dhcp-client", NULL, + 1000 / 2, 1000 / 20); + g_free (name); } static void -stop (NMDHCPClient *self, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *self, gboolean release, const GByteArray *duid) { - NMDHCPClientPrivate *priv; + NMDhcpClientPrivate *priv; 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); + if (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); + } + priv->pid = -1; priv->info_only = FALSE; } -static gboolean -daemon_timeout (gpointer user_data) +void +nm_dhcp_client_set_state (NMDhcpClient *self, + NMDhcpState new_state, + GObject *ip_config, + GHashTable *options) { - NMDHCPClient *self = NM_DHCP_CLIENT (user_data); - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - if (priv->ipv6) { - nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 request timed out.", priv->iface); + if (new_state >= NM_DHCP_STATE_BOUND) + timeout_cleanup (self); + if (new_state >= NM_DHCP_STATE_TIMEOUT) + watch_cleanup (self); + + if (new_state == NM_DHCP_STATE_BOUND) { + g_assert ( (priv->ipv6 && NM_IS_IP6_CONFIG (ip_config)) + || (!priv->ipv6 && NM_IS_IP4_CONFIG (ip_config))); + g_assert (options); + g_assert_cmpint (g_hash_table_size (options), >, 0); } else { - nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 request timed out.", priv->iface); + g_assert (ip_config == NULL); + g_assert (options == NULL); } - g_signal_emit (G_OBJECT (self), signals[SIGNAL_TIMEOUT], 0); - return FALSE; -} -static gboolean -signal_remove (gpointer user_data) -{ - NMDHCPClient *self = NM_DHCP_CLIENT (user_data); + /* The client may send same-state transitions for RENEW/REBIND events and + * the lease may have changed, so handle same-state transitions for the + * BOUND state. Ignore same-state transitions for other events since + * the lease won't have changed and the state was already handled. + */ + if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND)) + return; - NM_DHCP_CLIENT_GET_PRIVATE (self)->remove_id = 0; - g_signal_emit (G_OBJECT (self), signals[SIGNAL_REMOVE], 0); - return FALSE; + nm_log_info (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, + "(%s): DHCPv%c state changed %s -> %s", + priv->iface, + priv->ipv6 ? '6' : '4', + state_to_string (priv->state), + state_to_string (new_state)); + + priv->state = new_state; + g_signal_emit (G_OBJECT (self), + signals[SIGNAL_STATE_CHANGED], 0, + new_state, + ip_config, + options); } -static void -dhcp_client_set_state (NMDHCPClient *self, - NMDHCPState state, - gboolean emit_state, - gboolean remove_now) +static gboolean +daemon_timeout (gpointer user_data) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpClient *self = NM_DHCP_CLIENT (user_data); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - priv->state = state; - - if (emit_state) - g_signal_emit (G_OBJECT (self), signals[SIGNAL_STATE_CHANGED], 0, priv->state); - - if (state == DHC_END || state == DHC_ABEND) { - /* Start the remove signal timer */ - if (remove_now) { - g_signal_emit (G_OBJECT (self), signals[SIGNAL_REMOVE], 0); - } else { - if (!priv->remove_id) - priv->remove_id = g_timeout_add_seconds (5, signal_remove, self); - } - } + priv->timeout_id = 0; + nm_log_warn (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, + "(%s): DHCPv%c request timed out.", + priv->iface, + priv->ipv6 ? '6' : '4'); + nm_dhcp_client_set_state (self, NM_DHCP_STATE_TIMEOUT, NULL, NULL); + return G_SOURCE_REMOVE; } static void daemon_watch_cb (GPid pid, gint status, gpointer user_data) { - NMDHCPClient *self = NM_DHCP_CLIENT (user_data); - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - NMDHCPState new_state; - - if (priv->ipv6) { - nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 client pid %d exited with status %d", - priv->iface, pid, - WIFEXITED (status) ? WEXITSTATUS (status) : -1); - } else { - nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 client pid %d exited with status %d", - priv->iface, pid, - WIFEXITED (status) ? WEXITSTATUS (status) : -1); - } - - if (!WIFEXITED (status)) { - new_state = DHC_ABEND; + NMDhcpClient *self = NM_DHCP_CLIENT (user_data); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpState new_state; + guint64 log_domain; + guint ip_ver; + + log_domain = priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4; + ip_ver = priv->ipv6 ? 6 : 4; + + if (WIFEXITED (status)) + nm_log_info (log_domain, "(%s): DHCPv%d client pid %d exited with status %d", + priv->iface, ip_ver, pid, WEXITSTATUS (status)); + else if (WIFSIGNALED (status)) + nm_log_info (log_domain, "(%s): DHCPv%d client pid %d killed by signal %d", + priv->iface, ip_ver, pid, WTERMSIG (status)); + else if (WIFSTOPPED(status)) + nm_log_info (log_domain, "(%s): DHCPv%d client pid %d stopped by signal %d", + priv->iface, ip_ver, pid, WSTOPSIG (status)); + else if (WIFCONTINUED (status)) + nm_log_info (log_domain, "(%s): DHCPv%d client pid %d resumed (by SIGCONT)", + priv->iface, ip_ver, pid); + else nm_log_warn (LOGD_DHCP, "DHCP client died abnormally"); - } else - new_state = DHC_END; - watch_cleanup (self); - timeout_cleanup (self); - priv->dead = TRUE; + if (!WIFEXITED (status)) + new_state = NM_DHCP_STATE_FAIL; + else + new_state = NM_DHCP_STATE_DONE; + + priv->pid = -1; - dhcp_client_set_state (self, new_state, TRUE, FALSE); + nm_dhcp_client_set_state (self, new_state, NULL, NULL); } -static void -start_monitor (NMDHCPClient *self) +void +nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - g_return_if_fail (priv->pid > 0); + g_return_if_fail (priv->pid == -1); + priv->pid = pid; /* Set up a timeout on the transaction to kill it after the timeout */ + g_assert (priv->timeout_id == 0); priv->timeout_id = g_timeout_add_seconds (priv->timeout, daemon_timeout, self); - priv->watch_id = g_child_watch_add (priv->pid, - (GChildWatchFunc) daemon_watch_cb, - self); + g_assert (priv->watch_id == 0); + priv->watch_id = g_child_watch_add (pid, daemon_watch_cb, self); } gboolean -nm_dhcp_client_start_ip4 (NMDHCPClient *self, +nm_dhcp_client_start_ip4 (NMDhcpClient *self, const char *dhcp_client_id, - GByteArray *dhcp_anycast_addr, - const char *hostname) + const char *dhcp_anycast_addr, + const char *hostname, + const char *last_ip4_address) { - NMDHCPClientPrivate *priv; + NMDhcpClientPrivate *priv; g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); @@ -313,11 +411,12 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self, nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)", priv->iface, priv->timeout); - priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_client_id, dhcp_anycast_addr, hostname); - if (priv->pid) - start_monitor (self); + nm_dhcp_client_set_client_id (self, dhcp_client_id ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) : NULL); - return priv->pid ? TRUE : FALSE; + g_clear_pointer (&priv->hostname, g_free); + priv->hostname = g_strdup (hostname); + + return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address); } /* uuid_parse does not work for machine-id, so we use our own converter */ @@ -406,38 +505,21 @@ generate_duid_from_machine_id (void) return duid; } -static char * -escape_duid (const GByteArray *duid) -{ - guint32 i = 0; - GString *s; - - g_return_val_if_fail (duid != NULL, NULL); - - s = g_string_sized_new (40); - while (i < duid->len) { - if (s->len) - g_string_append_c (s, ':'); - g_string_append_printf (s, "%02x", duid->data[i++]); - } - return g_string_free (s, FALSE); -} - static GByteArray * -get_duid (NMDHCPClient *self) +get_duid (NMDhcpClient *self) { static GByteArray *duid = NULL; GByteArray *copy = NULL; - char *escaped; + char *str; if (G_UNLIKELY (duid == NULL)) { duid = generate_duid_from_machine_id (); g_assert (duid); if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) { - escaped = escape_duid (duid); - nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", escaped); - g_free (escaped); + str = nm_dhcp_utils_duid_to_string (duid); + nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", str); + g_free (str); } } @@ -450,13 +532,14 @@ get_duid (NMDHCPClient *self) } gboolean -nm_dhcp_client_start_ip6 (NMDHCPClient *self, - GByteArray *dhcp_anycast_addr, +nm_dhcp_client_start_ip6 (NMDhcpClient *self, + const char *dhcp_anycast_addr, const char *hostname, - gboolean info_only) + gboolean info_only, + NMSettingIP6ConfigPrivacy privacy) { - NMDHCPClientPrivate *priv; - char *escaped; + NMDhcpClientPrivate *priv; + char *str; g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); @@ -472,25 +555,24 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self, priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP)) { - escaped = escape_duid (priv->duid); - nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped); - g_free (escaped); + str = nm_dhcp_utils_duid_to_string (priv->duid); + nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, str); + g_free (str); } + g_clear_pointer (&priv->hostname, g_free); + priv->hostname = g_strdup (hostname); + 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, - dhcp_anycast_addr, - hostname, - info_only, - priv->duid); - if (priv->pid > 0) - start_monitor (self); - - return priv->pid ? TRUE : FALSE; + return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, + dhcp_anycast_addr, + info_only, + privacy, + priv->duid); } void @@ -506,11 +588,14 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) errno = 0; tmp = strtol (pid_contents, NULL, 10); if ((errno == 0) && (tmp > 1)) { + guint64 start_time; const char *exe; /* Ensure the process is a DHCP client */ + start_time = nm_utils_get_start_time_for_pid (tmp); proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp); - if (g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) { + if ( start_time + && g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) { exe = strrchr (proc_contents, '/'); if (exe) exe++; @@ -518,7 +603,8 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) exe = proc_contents; if (!strcmp (exe, binary_name)) - nm_dhcp_client_stop_pid ((GPid) tmp, NULL); + nm_utils_kill_process_sync (tmp, start_time, SIGTERM, LOGD_DHCP, + "dhcp-client", 1000 / 2, 1000 / 20); } } @@ -531,103 +617,30 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) } void -nm_dhcp_client_stop (NMDHCPClient *self, gboolean release) +nm_dhcp_client_stop (NMDhcpClient *self, gboolean release) { - NMDHCPClientPrivate *priv; + NMDhcpClientPrivate *priv; + pid_t old_pid = 0; 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->duid); - priv->dead = TRUE; - + old_pid = priv->pid; + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid); + if (old_pid > 0) { 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); + priv->iface, old_pid); + } else + nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction", priv->iface); + g_assert (priv->pid == -1); - timeout_cleanup (self); - watch_cleanup (self); + nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL); } /********************************************/ -static gboolean -state_is_bound (guint32 state) -{ - if ( (state == DHC_BOUND4) - || (state == DHC_BOUND6) - || (state == DHC_RENEW4) - || (state == DHC_RENEW6) - || (state == DHC_REBOOT) - || (state == DHC_REBIND4) - || (state == DHC_REBIND6) - || (state == DHC_IPV4LL)) - return TRUE; - - return FALSE; -} - -static const char *state_table[] = { - [DHC_NBI] = "nbi", - [DHC_PREINIT] = "preinit", - [DHC_PREINIT6] = "preinit6", - [DHC_BOUND4] = "bound", - [DHC_BOUND6] = "bound6", - [DHC_IPV4LL] = "ipv4ll", - [DHC_RENEW4] = "renew", - [DHC_RENEW6] = "renew6", - [DHC_REBOOT] = "reboot", - [DHC_REBIND4] = "rebind", - [DHC_REBIND6] = "rebind6", - [DHC_DEPREF6] = "depref6", - [DHC_STOP] = "stop", - [DHC_STOP6] = "stop6", - [DHC_MEDIUM] = "medium", - [DHC_TIMEOUT] = "timeout", - [DHC_FAIL] = "fail", - [DHC_EXPIRE] = "expire", - [DHC_EXPIRE6] = "expire6", - [DHC_RELEASE] = "release", - [DHC_RELEASE6] = "release6", - [DHC_START] = "start", - [DHC_ABEND] = "abend", - [DHC_END] = "end", -}; - -static const char * -state_to_string (NMDHCPState state) -{ - if (state >= 0 && state < G_N_ELEMENTS (state_table)) - return state_table[state]; - return NULL; -} - -static NMDHCPState -string_to_state (const char *name) -{ - int i; - - if (name) { - for (i = 0; i < G_N_ELEMENTS (state_table); i++) { - const char *n = state_table[i]; - - if (n && !strcasecmp (name, n)) - return i; - } - } - return 255; -} - static char * garray_to_string (GArray *array, const char *key) { @@ -661,848 +674,133 @@ garray_to_string (GArray *array, const char *key) return converted; } +#define OLD_TAG "old_" +#define NEW_TAG "new_" + static void -copy_option (gpointer key, - gpointer value, +copy_option (const char * key, + GValue *value, gpointer user_data) { GHashTable *hash = user_data; - const char *str_key = (const char *) key; char *str_value = NULL; + const char **p; + static const char *ignored_keys[] = { + "interface", + "pid", + "reason", + "dhcp_message_type", + NULL + }; - if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { - nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not " - "DBUS_TYPE_G_UCHAR_ARRAY", - str_key); + if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY)) { + nm_log_warn (LOGD_DHCP, "key %s value type was not DBUS_TYPE_G_UCHAR_ARRAY", key); return; } - str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key); - if (str_value) - g_hash_table_insert (hash, g_strdup (str_key), str_value); -} - -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 (NM_IS_DHCP_CLIENT (self)); - g_return_if_fail (options != NULL); - g_return_if_fail (reason != NULL); + if (g_str_has_prefix (key, OLD_TAG)) + return; - 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) + /* Filter out stuff that's not actually new DHCP options */ + for (p = ignored_keys; *p; p++) { + if (!strcmp (*p, key)) 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)); - } + if (g_str_has_prefix (key, NEW_TAG)) + key += STRLEN (NEW_TAG); + if (!key[0]) + return; - dhcp_client_set_state (self, new_state, TRUE, FALSE); + str_value = garray_to_string ((GArray *) g_value_get_boxed (value), key); + if (str_value) + g_hash_table_insert (hash, g_strdup (key), str_value); } -#define NEW_TAG "new_" -#define OLD_TAG "old_" - gboolean -nm_dhcp_client_foreach_option (NMDHCPClient *self, - GHFunc func, - gpointer user_data) -{ - NMDHCPClientPrivate *priv; - GHashTableIter iter; - gpointer iterkey, itervalue; +nm_dhcp_client_handle_event (gpointer unused, + const char *iface, + gint pid, + GHashTable *options, + const char *reason, + NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv; + guint32 old_state; + guint32 new_state; + GHashTable *str_options = NULL; + GObject *ip_config = NULL; 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; -} - -/********************************************/ - -static gboolean -ip4_process_dhcpcd_rfc3442_routes (NMDHCPClient *self, - const char *str, - NMIP4Config *ip4_config, - guint32 *gwaddr) -{ - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - char **routes, **r; - gboolean have_routes = FALSE; - - routes = g_strsplit (str, " ", 0); - if (g_strv_length (routes) == 0) - goto out; - - if ((g_strv_length (routes) % 2) != 0) { - nm_log_warn (LOGD_DHCP4, " classless static routes provided, but invalid"); - goto out; - } - - for (r = routes; *r; r += 2) { - char *slash; - NMPlatformIP4Route route; - int rt_cidr = 32; - guint32 rt_addr, rt_route; - - slash = strchr(*r, '/'); - if (slash) { - *slash = '\0'; - errno = 0; - rt_cidr = strtol (slash + 1, NULL, 10); - if ((errno == EINVAL) || (errno == ERANGE)) { - nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1); - continue; - } - } - if (inet_pton (AF_INET, *r, &rt_addr) <= 0) { - nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r); - continue; - } - if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) { - nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1)); - continue; - } - - have_routes = TRUE; - if (rt_cidr == 0 && rt_addr == 0) { - /* FIXME: how to handle multiple routers? */ - *gwaddr = rt_route; - } else { - nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1)); - memset (&route, 0, sizeof (route)); - route.network = rt_addr; - route.plen = rt_cidr; - route.gateway = rt_route; - route.source = NM_PLATFORM_SOURCE_DHCP; - route.metric = priv->priority; - nm_ip4_config_add_route (ip4_config, &route); - } - } - -out: - g_strfreev (routes); - return have_routes; -} - -static const char ** -process_dhclient_rfc3442_route (const char **octets, NMPlatformIP4Route *route, gboolean *success) -{ - const char **o = octets; - int addr_len = 0, i = 0; - long int tmp; - char *next_hop; - guint32 tmp_addr; - - *success = FALSE; - - if (!*o) - return o; /* no prefix */ - - tmp = strtol (*o, NULL, 10); - if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */ - return o; - - memset (route, 0, sizeof (*route)); - route->plen = tmp; - o++; - - if (tmp > 0) - addr_len = ((tmp - 1) / 8) + 1; - - /* ensure there's at least the address + next hop left */ - if (g_strv_length ((char **) o) < addr_len + 4) - goto error; - - if (tmp) { - const char *addr[4] = { "0", "0", "0", "0" }; - char *str_addr; - - for (i = 0; i < addr_len; i++) - addr[i] = *o++; - - str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL); - if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) { - g_free (str_addr); - goto error; - } - tmp_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp); - route->network = tmp_addr; - } - - /* Handle next hop */ - next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL); - if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) { - g_free (next_hop); - goto error; - } - route->gateway = tmp_addr; - g_free (next_hop); - - *success = TRUE; - return o + 4; /* advance to past the next hop */ - -error: - return o; -} - -static gboolean -ip4_process_dhclient_rfc3442_routes (NMDHCPClient *self, - const char *str, - NMIP4Config *ip4_config, - guint32 *gwaddr) -{ - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - char **octets, **o; - gboolean have_routes = FALSE; - NMPlatformIP4Route route; - gboolean success; - - o = octets = g_strsplit_set (str, " .", 0); - if (g_strv_length (octets) < 5) { - nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str); - goto out; - } - - while (*o) { - memset (&route, 0, sizeof (route)); - o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success); - if (!success) { - nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes"); - break; - } - - have_routes = TRUE; - if (!route.plen) { - /* gateway passed as classless static route */ - *gwaddr = route.gateway; - } else { - char addr[INET_ADDRSTRLEN]; - - /* normal route */ - route.source = NM_PLATFORM_SOURCE_DHCP; - route.metric = priv->priority; - nm_ip4_config_add_route (ip4_config, &route); - - nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", - nm_utils_inet4_ntop (route.network, addr), route.plen, - nm_utils_inet4_ntop (route.gateway, NULL)); - } - } - -out: - g_strfreev (octets); - return have_routes; -} - -static gboolean -ip4_process_classless_routes (NMDHCPClient *self, - GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr) -{ - const char *str, *p; - + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (pid > 0, FALSE); g_return_val_if_fail (options != NULL, FALSE); - g_return_val_if_fail (ip4_config != NULL, FALSE); - - *gwaddr = 0; - - /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a - * slightly different format: - * - * option classless-static-routes = array of (destination-descriptor ip-address); - * - * which results in: - * - * 0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6 - * - * dhcpcd supports classless static routes natively and uses this same - * option identifier with the following format: - * - * 192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41 - */ - str = g_hash_table_lookup (options, "new_classless_static_routes"); - - /* dhclient doesn't have actual support for rfc3442 classless static routes - * upstream. Thus, people resort to defining the option in dhclient.conf - * and using arbitrary formats like so: - * - * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; - * - * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html - */ - if (!str) - str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes"); - - /* Microsoft version; same as rfc3442 but with a different option # (249) */ - if (!str) - str = g_hash_table_lookup (options, "new_ms_classless_static_routes"); - - if (!str || !strlen (str)) - return FALSE; - - p = str; - while (*p) { - if (!g_ascii_isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) { - nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str); - return FALSE; - } - p++; - }; - - if (strchr (str, '/')) { - /* dhcpcd format */ - return ip4_process_dhcpcd_rfc3442_routes (self, str, ip4_config, gwaddr); - } - - return ip4_process_dhclient_rfc3442_routes (self, str, ip4_config, gwaddr); -} - -static void -process_classful_routes (NMDHCPClient *self, GHashTable *options, NMIP4Config *ip4_config) -{ - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - const char *str; - char **searches, **s; - - str = g_hash_table_lookup (options, "new_static_routes"); - if (!str) - return; - - searches = g_strsplit (str, " ", 0); - if ((g_strv_length (searches) % 2)) { - nm_log_info (LOGD_DHCP, " static routes provided, but invalid"); - goto out; - } - - for (s = searches; *s; s += 2) { - NMPlatformIP4Route route; - guint32 rt_addr, rt_route; - - if (inet_pton (AF_INET, *s, &rt_addr) <= 0) { - nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s); - continue; - } - if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) { - nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1)); - continue; - } - - // FIXME: ensure the IP address and route are sane - - memset (&route, 0, sizeof (route)); - route.network = rt_addr; - /* RFC 2132, updated by RFC 3442: - The Static Routes option (option 33) does not provide a subnet mask - for each route - it is assumed that the subnet mask is implicit in - whatever network number is specified in each route entry */ - route.plen = nm_utils_ip4_get_default_prefix (rt_addr); - if (rt_addr & ~nm_utils_ip4_prefix_to_netmask (route.plen)) { - /* RFC 943: target not "this network"; using host routing */ - route.plen = 32; - } - route.gateway = rt_route; - route.source = NM_PLATFORM_SOURCE_DHCP; - route.metric = priv->priority; - - nm_ip4_config_add_route (ip4_config, &route); - nm_log_info (LOGD_DHCP, " static route %s", - nm_platform_ip4_route_to_string (&route)); - } - -out: - g_strfreev (searches); -} - -static void -process_domain_search (const char *str, GFunc add_func, gpointer user_data) -{ - char **searches, **s; - char *unescaped, *p; - int i; - - g_return_if_fail (str != NULL); - g_return_if_fail (add_func != NULL); - - p = unescaped = g_strdup (str); - do { - p = strstr (p, "\\032"); - if (!p) - break; - - /* Clear the escaped space with real spaces */ - for (i = 0; i < 4; i++) - *p++ = ' '; - } while (*p++); - - if (strchr (unescaped, '\\')) { - nm_log_warn (LOGD_DHCP, " invalid domain search: '%s'", unescaped); - goto out; - } - - searches = g_strsplit (unescaped, " ", 0); - for (s = searches; *s; s++) { - if (strlen (*s)) { - nm_log_info (LOGD_DHCP, " domain search '%s'", *s); - add_func (*s, user_data); - } - } - g_strfreev (searches); - -out: - g_free (unescaped); -} - -static void -ip4_add_domain_search (gpointer data, gpointer user_data) -{ - nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data); -} - -/* 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; - guint32 tmp_addr; - NMPlatformIP4Address address; - char *str = NULL; - guint32 gwaddr = 0, plen = 0; - - g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + g_return_val_if_fail (reason != NULL, FALSE); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - g_return_val_if_fail (priv->options != NULL, NULL); - - ip4_config = nm_ip4_config_new (); - memset (&address, 0, sizeof (address)); - address.timestamp = nm_utils_get_monotonic_timestamp_s (); - str = g_hash_table_lookup (priv->options, "new_ip_address"); - if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { - address.address = tmp_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)) { - 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 (address.address); - nm_log_info (LOGD_DHCP4, " plen %d (default)", plen); - } - address.plen = plen; - - /* Routes: if the server returns classless static routes, we MUST ignore - * the 'static_routes' option. - */ - if (!ip4_process_classless_routes (self, priv->options, ip4_config, &gwaddr)) - process_classful_routes (self, priv->options, 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 (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, &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); - } - } + if (g_strcmp0 (priv->iface, iface) != 0) + return FALSE; + if (priv->pid != pid) + return FALSE; - /* - * 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 (priv->options, "new_dhcp_server_identifier"); - if (str) { - if (inet_pton (AF_INET, str, &tmp_addr) > 0) { - NMPlatformIP4Route route; - guint32 mask = nm_utils_ip4_prefix_to_netmask (address.plen); - - nm_log_info (LOGD_DHCP4, " server identifier %s", str); - if ((tmp_addr & mask) != (address.address & mask)) { - /* DHCP server not on assigned subnet, route needed */ - memset (&route, 0, sizeof (route)); - route.network = tmp_addr; - route.plen = 32; - /* this will be a device route if gwaddr is 0 */ - route.gateway = gwaddr; - route.source = NM_PLATFORM_SOURCE_DHCP; - route.metric = priv->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)); + old_state = priv->state; + new_state = reason_to_state (priv->iface, reason); + nm_log_dbg (LOGD_DHCP, "(%s): DHCP reason '%s' -> state '%s'", + iface, reason, state_to_string (new_state)); + + if (new_state == NM_DHCP_STATE_BOUND) { + /* Copy options */ + str_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_foreach (options, (GHFunc) copy_option, str_options); + + /* Create the IP config */ + g_warn_if_fail (g_hash_table_size (str_options)); + if (g_hash_table_size (str_options)) { + if (priv->ipv6) { + ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (priv->iface, + str_options, + priv->priority, + priv->info_only); + } else { + ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (priv->iface, + str_options, + priv->priority); } + g_warn_if_fail (ip_config != NULL); } - else - nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str); - } - - str = g_hash_table_lookup (priv->options, "new_dhcp_lease_time"); - if (str) { - address.lifetime = address.preferred = strtoul (str, NULL, 10); - nm_log_info (LOGD_DHCP4, " lease time %d", address.lifetime); - } - - address.source = NM_PLATFORM_SOURCE_DHCP; - nm_ip4_config_add_address (ip4_config, &address); - - 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); - 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); - 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); - 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: - g_object_unref (ip4_config); - return NULL; -} - -NMIP4Config * -nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test) -{ - NMDHCPClientPrivate *priv; - - 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_DHCP4, "(%s): DHCPv4 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 ip4_options_to_config (self); -} - -/********************************************/ - -static void -ip6_add_domain_search (gpointer data, gpointer user_data) -{ - nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data); -} - -/* 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; - NMPlatformIP6Address address; - char *str = NULL; - GHashTableIter iter; - gpointer key, value; - - g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); - - memset (&address, 0, sizeof (address)); - address.plen = 128; - address.timestamp = nm_utils_get_monotonic_timestamp_s (); - - 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 (); - - str = g_hash_table_lookup (priv->options, "new_max_life"); - if (str) { - address.lifetime = strtoul (str, NULL, 10); - nm_log_info (LOGD_DHCP6, " valid_lft %d", address.lifetime); - } - - str = g_hash_table_lookup (priv->options, "new_preferred_life"); - if (str) { - address.preferred = strtoul (str, NULL, 10); - nm_log_info (LOGD_DHCP6, " preferred_lft %d", address.preferred); - } - - 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; - } - - address.address = tmp_addr; - address.source = NM_PLATFORM_SOURCE_DHCP; - nm_ip6_config_add_address (ip6_config, &address); - nm_log_info (LOGD_DHCP6, " address %s", str); - } 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); + nm_dhcp_client_set_state (self, new_state, ip_config, str_options); - return ip6_config; + if (str_options) + g_hash_table_destroy (str_options); + g_clear_object (&ip_config); -error: - g_object_unref (ip6_config); - return NULL; -} - -NMIP6Config * -nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test) -{ - NMDHCPClientPrivate *priv; - - 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); + return TRUE; } /********************************************/ static void -nm_dhcp_client_init (NMDHCPClient *self) +nm_dhcp_client_init (NMDhcpClient *self) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - - priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - priv->pid = -1; + NM_DHCP_CLIENT_GET_PRIVATE (self)->pid = -1; } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); switch (prop_id) { case PROP_IFACE: g_value_set_string (value, priv->iface); break; + case PROP_IFINDEX: + g_value_set_int (value, priv->ifindex); + break; case PROP_HWADDR: g_value_set_boxed (value, priv->hwaddr); break; @@ -1528,13 +826,18 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); switch (prop_id) { case PROP_IFACE: /* construct-only */ priv->iface = g_strdup (g_value_get_string (value)); break; + case PROP_IFINDEX: + /* construct-only */ + priv->ifindex = g_value_get_int (value); + g_warn_if_fail (priv->ifindex > 0); + break; case PROP_HWADDR: /* construct only */ priv->hwaddr = g_value_dup_boxed (value); @@ -1563,23 +866,17 @@ set_property (GObject *object, guint prop_id, static void dispose (GObject *object) { - NMDHCPClient *self = NM_DHCP_CLIENT (object); - NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + NMDhcpClient *self = NM_DHCP_CLIENT (object); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); /* Stopping the client is left up to the controlling device * explicitly since we may want to quit NetworkManager but not terminate * the DHCP client. */ - if (priv->remove_id) { - g_source_remove (priv->remove_id); - priv->remove_id = 0; - } + watch_cleanup (self); + timeout_cleanup (self); - if (priv->options) { - g_hash_table_destroy (priv->options); - priv->options = NULL; - } g_clear_pointer (&priv->iface, g_free); if (priv->hwaddr) { @@ -1596,11 +893,11 @@ dispose (GObject *object) } static void -nm_dhcp_client_class_init (NMDHCPClientClass *client_class) +nm_dhcp_client_class_init (NMDhcpClientClass *client_class) { GObjectClass *object_class = G_OBJECT_CLASS (client_class); - g_type_class_add_private (client_class, sizeof (NMDHCPClientPrivate)); + g_type_class_add_private (client_class, sizeof (NMDhcpClientPrivate)); /* virtual methods */ object_class->dispose = dispose; @@ -1612,78 +909,59 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class) g_object_class_install_property (object_class, PROP_IFACE, - g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, - "iface", - "Interface", + g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_IFINDEX, + g_param_spec_int (NM_DHCP_CLIENT_IFINDEX, "", "", + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_HWADDR, - g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, - "hwaddr", - "hardware address", + g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, "", "", G_TYPE_BYTE_ARRAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IPV6, - g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, - "ipv6", - "IPv6", + g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, "", "", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_UUID, - g_param_spec_string (NM_DHCP_CLIENT_UUID, - "uuid", - "UUID", + g_param_spec_string (NM_DHCP_CLIENT_UUID, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PRIORITY, - g_param_spec_uint (NM_DHCP_CLIENT_PRIORITY, - "priority", - "Priority", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_param_spec_uint (NM_DHCP_CLIENT_PRIORITY, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_TIMEOUT, - g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, - "timeout", - "Timeout", + g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, "", "", 0, G_MAXUINT, 45, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); /* signals */ signals[SIGNAL_STATE_CHANGED] = g_signal_new (NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPClientClass, state_changed), - NULL, NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - - signals[SIGNAL_TIMEOUT] = - g_signal_new (NM_DHCP_CLIENT_SIGNAL_TIMEOUT, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPClientClass, timeout), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[SIGNAL_REMOVE] = - g_signal_new (NM_DHCP_CLIENT_SIGNAL_REMOVE, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPClientClass, remove), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + G_STRUCT_OFFSET (NMDhcpClientClass, state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE); } |