diff options
Diffstat (limited to 'src/dhcp/nm-dhcp-client.c')
| -rw-r--r-- | src/dhcp/nm-dhcp-client.c | 1337 |
1 files changed, 0 insertions, 1337 deletions
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c deleted file mode 100644 index 46ab4895..00000000 --- a/src/dhcp/nm-dhcp-client.c +++ /dev/null @@ -1,1337 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2005 - 2010 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-dhcp-client.h" - -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <linux/rtnetlink.h> - -#include "nm-glib-aux/nm-dedup-multi.h" -#include "nm-glib-aux/nm-random-utils.h" - -#include "NetworkManagerUtils.h" -#include "nm-utils.h" -#include "nm-dhcp-utils.h" -#include "platform/nm-platform.h" - -#include "nm-dhcp-client-logging.h" - -/*****************************************************************************/ - -enum { SIGNAL_STATE_CHANGED, SIGNAL_PREFIX_DELEGATED, LAST_SIGNAL }; - -static guint signals[LAST_SIGNAL] = {0}; - -NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient, - PROP_ADDR_FAMILY, - PROP_FLAGS, - PROP_HWADDR, - PROP_BROADCAST_HWADDR, - PROP_IFACE, - PROP_IFINDEX, - PROP_MULTI_IDX, - PROP_ROUTE_METRIC, - PROP_ROUTE_TABLE, - PROP_TIMEOUT, - PROP_UUID, - PROP_IAID, - PROP_IAID_EXPLICIT, - PROP_HOSTNAME, - PROP_HOSTNAME_FLAGS, - PROP_MUD_URL, - PROP_VENDOR_CLASS_IDENTIFIER, - PROP_REJECT_SERVERS, ); - -typedef struct _NMDhcpClientPrivate { - NMDedupMultiIndex * multi_idx; - char * iface; - GBytes * hwaddr; - GBytes * bcast_hwaddr; - char * uuid; - GBytes * client_id; - char * hostname; - const char ** reject_servers; - char * mud_url; - GBytes * vendor_class_identifier; - pid_t pid; - guint timeout_id; - guint watch_id; - int addr_family; - int ifindex; - guint32 route_table; - guint32 route_metric; - guint32 timeout; - guint32 iaid; - NMDhcpState state; - NMDhcpHostnameFlags hostname_flags; - bool info_only : 1; - bool use_fqdn : 1; - bool iaid_explicit : 1; -} NMDhcpClientPrivate; - -G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT) - -#define NM_DHCP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDhcpClient, NM_IS_DHCP_CLIENT) - -/*****************************************************************************/ - -pid_t -nm_dhcp_client_get_pid(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), -1); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid; -} - -NMDedupMultiIndex * -nm_dhcp_client_get_multi_idx(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->multi_idx; -} - -const char * -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; -} - -int -nm_dhcp_client_get_addr_family(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), AF_UNSPEC); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->addr_family; -} - -const char * -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; -} - -GBytes * -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; -} - -GBytes * -nm_dhcp_client_get_broadcast_hw_addr(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->bcast_hwaddr; -} - -guint32 -nm_dhcp_client_get_route_table(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), RT_TABLE_MAIN); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->route_table; -} - -void -nm_dhcp_client_set_route_table(NMDhcpClient *self, guint32 route_table) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - if (route_table != priv->route_table) { - priv->route_table = route_table; - _notify(self, PROP_ROUTE_TABLE); - } -} - -guint32 -nm_dhcp_client_get_route_metric(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), G_MAXUINT32); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->route_metric; -} - -void -nm_dhcp_client_set_route_metric(NMDhcpClient *self, guint32 route_metric) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - if (route_metric != priv->route_metric) { - priv->route_metric = route_metric; - _notify(self, PROP_ROUTE_METRIC); - } -} - -guint32 -nm_dhcp_client_get_timeout(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), 0); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->timeout; -} - -guint32 -nm_dhcp_client_get_iaid(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), 0); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->iaid; -} - -gboolean -nm_dhcp_client_get_iaid_explicit(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->iaid_explicit; -} - -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; -} - -static void -_set_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - nm_assert(!client_id || g_bytes_get_size(client_id) >= 2); - - if (priv->client_id == client_id - || (priv->client_id && client_id && g_bytes_equal(priv->client_id, client_id))) { - if (take && client_id) - g_bytes_unref(client_id); - return; - } - - if (priv->client_id) - g_bytes_unref(priv->client_id); - priv->client_id = client_id; - if (!take && client_id) - g_bytes_ref(client_id); - - { - gs_free char *s = NULL; - - _LOGT("%s: set %s", - nm_dhcp_client_get_addr_family(self) == AF_INET6 ? "duid" : "client-id", - priv->client_id ? (s = nm_dhcp_utils_duid_to_string(priv->client_id)) : "default"); - } -} - -void -nm_dhcp_client_set_client_id(NMDhcpClient *self, GBytes *client_id) -{ - g_return_if_fail(NM_IS_DHCP_CLIENT(self)); - g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2); - - _set_client_id(self, client_id, FALSE); -} - -void -nm_dhcp_client_set_client_id_bin(NMDhcpClient *self, - guint8 type, - const guint8 *client_id, - gsize len) -{ - guint8 *buf; - GBytes *b; - - g_return_if_fail(NM_IS_DHCP_CLIENT(self)); - g_return_if_fail(client_id); - g_return_if_fail(len > 0); - - buf = g_malloc(len + 1); - buf[0] = type; - memcpy(buf + 1, client_id, len); - b = g_bytes_new_take(buf, len + 1); - _set_client_id(self, b, TRUE); -} - -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; -} - -NMDhcpHostnameFlags -nm_dhcp_client_get_hostname_flags(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NM_DHCP_HOSTNAME_FLAG_NONE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->hostname_flags; -} - -gboolean -nm_dhcp_client_get_info_only(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->info_only; -} - -gboolean -nm_dhcp_client_get_use_fqdn(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->use_fqdn; -} - -const char * -nm_dhcp_client_get_mud_url(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->mud_url; -} - -GBytes * -nm_dhcp_client_get_vendor_class_identifier(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->vendor_class_identifier; -} - -const char *const * -nm_dhcp_client_get_reject_servers(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return (const char *const *) NM_DHCP_CLIENT_GET_PRIVATE(self)->reject_servers; -} - -/*****************************************************************************/ - -static const char *state_table[NM_DHCP_STATE_MAX + 1] = { - [NM_DHCP_STATE_UNKNOWN] = "unknown", - [NM_DHCP_STATE_BOUND] = "bound", - [NM_DHCP_STATE_EXTENDED] = "extended", - [NM_DHCP_STATE_TIMEOUT] = "timeout", - [NM_DHCP_STATE_EXPIRE] = "expire", - [NM_DHCP_STATE_DONE] = "done", - [NM_DHCP_STATE_FAIL] = "fail", - [NM_DHCP_STATE_TERMINATED] = "terminated", -}; - -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(NMDhcpClient *self, const char *iface, const char *reason) -{ - if (g_ascii_strcasecmp(reason, "bound") == 0 || g_ascii_strcasecmp(reason, "bound6") == 0) - return NM_DHCP_STATE_BOUND; - else if (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_EXTENDED; - 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; - - _LOGD("unmapped DHCP state '%s'", reason); - return NM_DHCP_STATE_UNKNOWN; -} - -/*****************************************************************************/ - -static void -timeout_cleanup(NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - nm_clear_g_source(&priv->timeout_id); -} - -static void -watch_cleanup(NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - nm_clear_g_source(&priv->watch_id); -} - -void -nm_dhcp_client_stop_pid(pid_t pid, const char *iface) -{ - char *name = iface ? g_strdup_printf("dhcp-client-%s", iface) : NULL; - - g_return_if_fail(pid > 1); - - nm_utils_kill_child_sync(pid, - SIGTERM, - LOGD_DHCP, - name ?: "dhcp-client", - NULL, - 1000 / 2, - 1000 / 20); - g_free(name); -} - -static void -stop(NMDhcpClient *self, gboolean release) -{ - NMDhcpClientPrivate *priv; - - g_return_if_fail(NM_IS_DHCP_CLIENT(self)); - - priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - 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; -} - -void -nm_dhcp_client_set_state(NMDhcpClient *self, - NMDhcpState new_state, - NMIPConfig * ip_config, - GHashTable * options) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - gs_free char * event_id = NULL; - - if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) { - g_return_if_fail(NM_IS_IP_CONFIG_ADDR_FAMILY(ip_config, priv->addr_family)); - g_return_if_fail(options); - } else { - g_return_if_fail(!ip_config); - g_return_if_fail(!options); - } - - if (new_state >= NM_DHCP_STATE_BOUND) - timeout_cleanup(self); - if (new_state >= NM_DHCP_STATE_TIMEOUT) - watch_cleanup(self); - - /* The client may send same-state transitions for RENEW/REBIND events and - * the lease may have changed, so handle same-state transitions for the - * EXTENDED and BOUND states. 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) - && !NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) - return; - - if (_LOGI_ENABLED()) { - gs_free const char **keys = NULL; - guint i, nkeys; - - keys = nm_utils_strdict_get_keys(options, TRUE, &nkeys); - for (i = 0; i < nkeys; i++) { - _LOGI("option %-20s => '%s'", keys[i], (char *) g_hash_table_lookup(options, keys[i])); - } - } - - if (priv->addr_family == AF_INET6) - event_id = nm_dhcp_utils_get_dhcp6_event_id(options); - - _LOGI("state changed %s -> %s%s%s%s", - state_to_string(priv->state), - state_to_string(new_state), - NM_PRINT_FMT_QUOTED(event_id, ", event ID=\"", event_id, "\"", "")); - - priv->state = new_state; - g_signal_emit(G_OBJECT(self), signals[SIGNAL_STATE_CHANGED], 0, new_state, ip_config, options); -} - -static gboolean -transaction_timeout(gpointer user_data) -{ - NMDhcpClient * self = NM_DHCP_CLIENT(user_data); - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - priv->timeout_id = 0; - _LOGW("request timed out"); - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TIMEOUT, NULL, NULL); - return G_SOURCE_REMOVE; -} - -static void -daemon_watch_cb(GPid pid, int status, gpointer user_data) -{ - NMDhcpClient * self = NM_DHCP_CLIENT(user_data); - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - g_return_if_fail(priv->watch_id); - priv->watch_id = 0; - - if (WIFEXITED(status)) - _LOGI("client pid %d exited with status %d", pid, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - _LOGI("client pid %d killed by signal %d", pid, WTERMSIG(status)); - else if (WIFSTOPPED(status)) - _LOGI("client pid %d stopped by signal %d", pid, WSTOPSIG(status)); - else if (WIFCONTINUED(status)) - _LOGI("client pid %d resumed (by SIGCONT)", pid); - else - _LOGW("client died abnormally"); - - priv->pid = -1; - - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL); -} - -void -nm_dhcp_client_start_timeout(NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - g_return_if_fail(priv->timeout_id == 0); - - /* Set up a timeout on the transaction to kill it after the timeout */ - - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) - return; - - priv->timeout_id = g_timeout_add_seconds(priv->timeout, transaction_timeout, self); -} - -void -nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - g_return_if_fail(priv->pid == -1); - priv->pid = pid; - - nm_dhcp_client_start_timeout(self); - - g_return_if_fail(priv->watch_id == 0); - priv->watch_id = g_child_watch_add(pid, daemon_watch_cb, self); -} - -gboolean -nm_dhcp_client_start_ip4(NMDhcpClient *self, - GBytes * client_id, - const char * dhcp_anycast_addr, - const char * last_ip4_address, - GError ** error) -{ - NMDhcpClientPrivate *priv; - - 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->addr_family == AF_INET, FALSE); - g_return_val_if_fail(priv->uuid != NULL, FALSE); - - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) - _LOGI("activation: beginning transaction (no timeout)"); - else - _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); - - nm_dhcp_client_set_client_id(self, client_id); - - return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, - dhcp_anycast_addr, - last_ip4_address, - error); -} - -gboolean -nm_dhcp_client_accept(NMDhcpClient *self, GError **error) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - - if (NM_DHCP_CLIENT_GET_CLASS(self)->accept) { - return NM_DHCP_CLIENT_GET_CLASS(self)->accept(self, error); - } - - return TRUE; -} - -gboolean -nm_dhcp_client_decline(NMDhcpClient *self, const char *error_message, GError **error) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - - if (NM_DHCP_CLIENT_GET_CLASS(self)->decline) { - return NM_DHCP_CLIENT_GET_CLASS(self)->decline(self, error_message, error); - } - - return TRUE; -} - -static GBytes * -get_duid(NMDhcpClient *self) -{ - return NULL; -} - -gboolean -nm_dhcp_client_start_ip6(NMDhcpClient * self, - GBytes * client_id, - gboolean enforce_duid, - const char * dhcp_anycast_addr, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error) -{ - NMDhcpClientPrivate *priv; - gs_unref_bytes GBytes *own_client_id = NULL; - - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - g_return_val_if_fail(client_id, FALSE); - - priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - g_return_val_if_fail(priv->pid == -1, FALSE); - g_return_val_if_fail(priv->addr_family == AF_INET6, FALSE); - g_return_val_if_fail(priv->uuid != NULL, FALSE); - g_return_val_if_fail(!priv->client_id, FALSE); - - if (!enforce_duid) - own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self); - - _set_client_id(self, own_client_id ?: client_id, FALSE); - - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) - _LOGI("activation: beginning transaction (no timeout)"); - else - _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); - - return NM_DHCP_CLIENT_GET_CLASS(self) - ->ip6_start(self, dhcp_anycast_addr, ll_addr, privacy, needed_prefixes, error); -} - -void -nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name) -{ - guint64 start_time; - pid_t pid, ppid; - const char * exe; - char proc_path[NM_STRLEN("/proc/%lu/cmdline") + 100]; - gs_free char *pid_contents = NULL, *proc_contents = NULL; - - /* Check for an existing instance and stop it */ - if (!g_file_get_contents(pid_file, &pid_contents, NULL, NULL)) - return; - - pid = _nm_utils_ascii_str_to_int64(pid_contents, 10, 1, G_MAXINT64, 0); - if (pid <= 0) - goto out; - - start_time = nm_utils_get_start_time_for_pid(pid, NULL, &ppid); - if (start_time == 0) - goto out; - - nm_sprintf_buf(proc_path, "/proc/%lu/cmdline", (unsigned long) pid); - if (!g_file_get_contents(proc_path, &proc_contents, NULL, NULL)) - goto out; - - exe = strrchr(proc_contents, '/'); - if (exe) - exe++; - else - exe = proc_contents; - if (!nm_streq0(exe, binary_name)) - goto out; - - if (ppid == getpid()) { - /* the process is our own child. */ - nm_utils_kill_child_sync(pid, SIGTERM, LOGD_DHCP, "dhcp-client", NULL, 1000 / 2, 1000 / 20); - } else { - nm_utils_kill_process_sync(pid, - start_time, - SIGTERM, - LOGD_DHCP, - "dhcp-client", - 1000 / 2, - 1000 / 20, - 2000); - } - -out: - if (remove(pid_file) == -1) { - int errsv = errno; - - nm_log_dbg(LOGD_DHCP, - "dhcp: could not remove pid file \"%s\": %s (%d)", - pid_file, - nm_strerror_native(errsv), - errsv); - } -} - -void -nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) -{ - 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 */ - old_pid = priv->pid; - NM_DHCP_CLIENT_GET_CLASS(self)->stop(self, release); - if (old_pid > 0) - _LOGI("canceled DHCP transaction, DHCP client pid %d", old_pid); - else - _LOGI("canceled DHCP transaction"); - nm_assert(priv->pid == -1); - - nm_dhcp_client_set_state(self, NM_DHCP_STATE_DONE, NULL, NULL); -} - -/*****************************************************************************/ - -static char * -bytearray_variant_to_string(NMDhcpClient *self, GVariant *value, const char *key) -{ - const guint8 *array; - gsize length; - GString * str; - int i; - unsigned char c; - char * converted = NULL; - - g_return_val_if_fail(value != NULL, NULL); - - array = g_variant_get_fixed_array(value, &length, 1); - - /* Since the DHCP options come through environment variables, they should - * already be UTF-8 safe, but just make sure. - */ - str = g_string_sized_new(length); - for (i = 0; i < length; i++) { - c = array[i]; - - /* Convert NULLs to spaces and non-ASCII characters to ? */ - if (c == '\0') - c = ' '; - else if (c > 127) - c = '?'; - str = g_string_append_c(str, c); - } - str = g_string_append_c(str, '\0'); - - converted = str->str; - if (!g_utf8_validate(converted, -1, NULL)) - _LOGW("option '%s' couldn't be converted to UTF-8", key); - g_string_free(str, FALSE); - return converted; -} - -static int -label_is_unknown_xyz(const char *label) -{ - if (!NM_STR_HAS_PREFIX(label, "unknown_")) - return -EINVAL; - - label += NM_STRLEN("unknown_"); - if (label[0] != '2' || !g_ascii_isdigit(label[1]) || !g_ascii_isdigit(label[2]) - || label[3] != '\0') - return -EINVAL; - - return _nm_utils_ascii_str_to_int64(label, 10, 224, 254, -EINVAL); -} - -#define OLD_TAG "old_" -#define NEW_TAG "new_" - -static void -maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant *value) -{ - char *str_value = NULL; - - g_return_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING)); - - if (g_str_has_prefix(key, OLD_TAG)) - return; - - /* Filter out stuff that's not actually new DHCP options */ - if (NM_IN_STRSET(key, "interface", "pid", "reason", "dhcp_message_type")) - return; - - if (NM_STR_HAS_PREFIX(key, NEW_TAG)) - key += NM_STRLEN(NEW_TAG); - if (NM_STR_HAS_PREFIX(key, "private_") || !key[0]) - return; - - str_value = bytearray_variant_to_string(self, value, key); - if (str_value) { - int priv_opt_num; - - g_hash_table_insert(hash, g_strdup(key), str_value); - - /* dhclient has no special labels for private dhcp options: it uses "unknown_xyz" - * labels for that. We need to identify those to alias them to our "private_xyz" - * format unused in the internal dchp plugins. - */ - if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) { - gs_free guint8 *check_val = NULL; - char * hex_str = NULL; - gsize len; - - /* dhclient passes values from dhcp private options in its own "string" format: - * if the raw values are printable as ascii strings, it will pass the string - * representation; if the values are not printable as an ascii string, it will - * pass a string displaying the hex values (hex string). Try to enforce passing - * always an hex string, converting string representation if needed. - */ - check_val = nm_utils_hexstr2bin_alloc(str_value, FALSE, TRUE, ":", 0, &len); - hex_str = nm_utils_bin2hexstr_full(check_val ?: (guint8 *) str_value, - check_val ? len : strlen(str_value), - ':', - FALSE, - NULL); - g_hash_table_insert(hash, g_strdup_printf("private_%d", priv_opt_num), hex_str); - } - } -} - -void -nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *self, const NMPlatformIP6Address *prefix) -{ - g_signal_emit(G_OBJECT(self), signals[SIGNAL_PREFIX_DELEGATED], 0, prefix); -} - -gboolean -nm_dhcp_client_handle_event(gpointer unused, - const char * iface, - int pid, - GVariant * options, - const char * reason, - NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv; - guint32 old_state; - guint32 new_state; - gs_unref_hashtable GHashTable *str_options = NULL; - gs_unref_object NMIPConfig *ip_config = NULL; - NMPlatformIP6Address prefix = { - 0, - }; - - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - g_return_val_if_fail(iface != NULL, FALSE); - g_return_val_if_fail(pid > 0, FALSE); - g_return_val_if_fail(g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT), FALSE); - g_return_val_if_fail(reason != NULL, FALSE); - - priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - if (g_strcmp0(priv->iface, iface) != 0) - return FALSE; - if (priv->pid != pid) - return FALSE; - - old_state = priv->state; - new_state = reason_to_state(self, priv->iface, reason); - _LOGD("DHCP state '%s' -> '%s' (reason: '%s')", - state_to_string(old_state), - state_to_string(new_state), - reason); - - if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) { - GVariantIter iter; - const char * name; - GVariant * value; - - /* Copy options */ - str_options = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); - g_variant_iter_init(&iter, options); - while (g_variant_iter_next(&iter, "{&sv}", &name, &value)) { - maybe_add_option(self, str_options, name, value); - g_variant_unref(value); - } - - /* Create the IP config */ - if (g_hash_table_size(str_options) > 0) { - if (priv->addr_family == AF_INET) { - ip_config = NM_IP_CONFIG_CAST( - nm_dhcp_utils_ip4_config_from_options(nm_dhcp_client_get_multi_idx(self), - priv->ifindex, - priv->iface, - str_options, - priv->route_table, - priv->route_metric)); - } else { - prefix = nm_dhcp_utils_ip6_prefix_from_options(str_options); - ip_config = NM_IP_CONFIG_CAST( - nm_dhcp_utils_ip6_config_from_options(nm_dhcp_client_get_multi_idx(self), - priv->ifindex, - priv->iface, - str_options, - priv->info_only)); - } - } else - g_warn_if_reached(); - } - - if (!IN6_IS_ADDR_UNSPECIFIED(&prefix.address)) { - /* If we got an IPv6 prefix to delegate, we don't change the state - * of the DHCP client instance. Instead, we just signal the prefix - * to the device. */ - nm_dhcp_client_emit_ipv6_prefix_delegated(self, &prefix); - } else { - /* Fail if no valid IP config was received */ - if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED) && !ip_config) { - _LOGW("client bound but IP config not received"); - new_state = NM_DHCP_STATE_FAIL; - nm_clear_pointer(&str_options, g_hash_table_unref); - } - - nm_dhcp_client_set_state(self, new_state, ip_config, str_options); - } - - return TRUE; -} - -gboolean -nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - in_addr_t addr4 = *(in_addr_t *) addr; - guint i; - - /* IPv6 not implemented yet */ - nm_assert(priv->addr_family == AF_INET); - - if (!priv->reject_servers || !priv->reject_servers[0]) - return FALSE; - - for (i = 0; priv->reject_servers[i]; i++) { - in_addr_t r_addr; - in_addr_t mask; - int r_prefix; - - if (!nm_utils_parse_inaddr_prefix_bin(AF_INET, - priv->reject_servers[i], - NULL, - &r_addr, - &r_prefix)) - nm_assert_not_reached(); - mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix); - if ((addr4 & mask) == (r_addr & mask)) - return TRUE; - } - - return FALSE; -} - -/*****************************************************************************/ - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - 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; - case PROP_BROADCAST_HWADDR: - g_value_set_boxed(value, priv->bcast_hwaddr); - break; - case PROP_ADDR_FAMILY: - g_value_set_int(value, priv->addr_family); - break; - case PROP_UUID: - g_value_set_string(value, priv->uuid); - break; - case PROP_IAID: - g_value_set_uint(value, priv->iaid); - break; - case PROP_IAID_EXPLICIT: - g_value_set_boolean(value, priv->iaid_explicit); - break; - case PROP_HOSTNAME: - g_value_set_string(value, priv->hostname); - break; - case PROP_ROUTE_METRIC: - g_value_set_uint(value, priv->route_metric); - break; - case PROP_ROUTE_TABLE: - g_value_set_uint(value, priv->route_table); - break; - case PROP_TIMEOUT: - g_value_set_uint(value, priv->timeout); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object); - guint flags; - - switch (prop_id) { - case PROP_FLAGS: - /* construct-only */ - flags = g_value_get_uint(value); - nm_assert( - (flags & ~((guint)(NM_DHCP_CLIENT_FLAGS_INFO_ONLY | NM_DHCP_CLIENT_FLAGS_USE_FQDN))) - == 0); - priv->info_only = NM_FLAGS_HAS(flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY); - priv->use_fqdn = NM_FLAGS_HAS(flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN); - break; - case PROP_MULTI_IDX: - /* construct-only */ - priv->multi_idx = g_value_get_pointer(value); - if (!priv->multi_idx) - g_return_if_reached(); - nm_dedup_multi_index_ref(priv->multi_idx); - break; - case PROP_IFACE: - /* construct-only */ - priv->iface = g_value_dup_string(value); - g_return_if_fail(priv->iface); - nm_assert(nm_utils_ifname_valid_kernel(priv->iface, NULL)); - break; - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_int(value); - g_return_if_fail(priv->ifindex > 0); - break; - case PROP_HWADDR: - /* construct-only */ - priv->hwaddr = g_value_dup_boxed(value); - break; - case PROP_BROADCAST_HWADDR: - /* construct-only */ - priv->bcast_hwaddr = g_value_dup_boxed(value); - break; - case PROP_ADDR_FAMILY: - /* construct-only */ - priv->addr_family = g_value_get_int(value); - if (!NM_IN_SET(priv->addr_family, AF_INET, AF_INET6)) - g_return_if_reached(); - break; - case PROP_UUID: - /* construct-only */ - priv->uuid = g_value_dup_string(value); - break; - case PROP_IAID: - /* construct-only */ - priv->iaid = g_value_get_uint(value); - break; - case PROP_IAID_EXPLICIT: - /* construct-only */ - priv->iaid_explicit = g_value_get_boolean(value); - break; - case PROP_HOSTNAME: - /* construct-only */ - priv->hostname = g_value_dup_string(value); - break; - case PROP_HOSTNAME_FLAGS: - /* construct-only */ - priv->hostname_flags = g_value_get_uint(value); - break; - case PROP_MUD_URL: - /* construct-only */ - priv->mud_url = g_value_dup_string(value); - break; - case PROP_ROUTE_TABLE: - priv->route_table = g_value_get_uint(value); - break; - case PROP_ROUTE_METRIC: - priv->route_metric = g_value_get_uint(value); - break; - case PROP_TIMEOUT: - /* construct-only */ - priv->timeout = g_value_get_uint(value); - break; - case PROP_VENDOR_CLASS_IDENTIFIER: - /* construct-only */ - priv->vendor_class_identifier = g_value_dup_boxed(value); - break; - case PROP_REJECT_SERVERS: - /* construct-only */ - priv->reject_servers = nm_utils_strv_dup_packed(g_value_get_boxed(value), -1); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_dhcp_client_init(NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate); - self->_priv = priv; - - c_list_init(&self->dhcp_client_lst); - - priv->pid = -1; -} - -static void -dispose(GObject *object) -{ - 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. - */ - - nm_assert(c_list_is_empty(&self->dhcp_client_lst)); - - watch_cleanup(self); - timeout_cleanup(self); - - nm_clear_g_free(&priv->iface); - nm_clear_g_free(&priv->hostname); - nm_clear_g_free(&priv->uuid); - nm_clear_g_free(&priv->mud_url); - nm_clear_g_free(&priv->reject_servers); - nm_clear_pointer(&priv->client_id, g_bytes_unref); - nm_clear_pointer(&priv->hwaddr, g_bytes_unref); - nm_clear_pointer(&priv->bcast_hwaddr, g_bytes_unref); - nm_clear_pointer(&priv->vendor_class_identifier, g_bytes_unref); - - G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object); - - priv->multi_idx = nm_dedup_multi_index_unref(priv->multi_idx); -} - -static void -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)); - - object_class->dispose = dispose; - object_class->get_property = get_property; - object_class->set_property = set_property; - - client_class->stop = stop; - client_class->get_duid = get_duid; - - obj_properties[PROP_MULTI_IDX] = - g_param_spec_pointer(NM_DHCP_CLIENT_MULTI_IDX, - "", - "", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IFACE] = - g_param_spec_string(NM_DHCP_CLIENT_INTERFACE, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IFINDEX] = - g_param_spec_int(NM_DHCP_CLIENT_IFINDEX, - "", - "", - -1, - G_MAXINT, - -1, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_HWADDR] = - g_param_spec_boxed(NM_DHCP_CLIENT_HWADDR, - "", - "", - G_TYPE_BYTES, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_BROADCAST_HWADDR] = - g_param_spec_boxed(NM_DHCP_CLIENT_BROADCAST_HWADDR, - "", - "", - G_TYPE_BYTES, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ADDR_FAMILY] = - g_param_spec_int(NM_DHCP_CLIENT_ADDR_FAMILY, - "", - "", - 0, - G_MAXINT, - AF_UNSPEC, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_UUID] = - g_param_spec_string(NM_DHCP_CLIENT_UUID, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IAID] = - g_param_spec_uint(NM_DHCP_CLIENT_IAID, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IAID_EXPLICIT] = - g_param_spec_boolean(NM_DHCP_CLIENT_IAID_EXPLICIT, - "", - "", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_HOSTNAME] = - g_param_spec_string(NM_DHCP_CLIENT_HOSTNAME, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_HOSTNAME_FLAGS] = - g_param_spec_uint(NM_DHCP_CLIENT_HOSTNAME_FLAGS, - "", - "", - 0, - G_MAXUINT32, - NM_DHCP_HOSTNAME_FLAG_NONE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_MUD_URL] = - g_param_spec_string(NM_DHCP_CLIENT_MUD_URL, - "", - "", - NULL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ROUTE_TABLE] = - g_param_spec_uint(NM_DHCP_CLIENT_ROUTE_TABLE, - "", - "", - 0, - G_MAXUINT32, - RT_TABLE_MAIN, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ROUTE_METRIC] = - g_param_spec_uint(NM_DHCP_CLIENT_ROUTE_METRIC, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - G_STATIC_ASSERT_EXPR(G_MAXINT32 == NM_DHCP_TIMEOUT_INFINITY); - obj_properties[PROP_TIMEOUT] = - g_param_spec_uint(NM_DHCP_CLIENT_TIMEOUT, - "", - "", - 1, - G_MAXINT32, - NM_DHCP_TIMEOUT_DEFAULT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_FLAGS] = - g_param_spec_uint(NM_DHCP_CLIENT_FLAGS, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_VENDOR_CLASS_IDENTIFIER] = - g_param_spec_boxed(NM_DHCP_CLIENT_VENDOR_CLASS_IDENTIFIER, - "", - "", - G_TYPE_BYTES, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_REJECT_SERVERS] = - g_param_spec_boxed(NM_DHCP_CLIENT_REJECT_SERVERS, - "", - "", - G_TYPE_STRV, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[SIGNAL_STATE_CHANGED] = g_signal_new(NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 3, - G_TYPE_UINT, - G_TYPE_OBJECT, - G_TYPE_HASH_TABLE); - - signals[SIGNAL_PREFIX_DELEGATED] = g_signal_new(NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_POINTER); -} |