diff options
Diffstat (limited to 'src/core/dhcp/nm-dhcp-client.c')
| -rw-r--r-- | src/core/dhcp/nm-dhcp-client.c | 1331 |
1 files changed, 580 insertions, 751 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index f88c79c0..ec910389 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -20,9 +20,13 @@ #include "NetworkManagerUtils.h" #include "nm-utils.h" +#include "nm-l3cfg.h" +#include "nm-l3-config-data.h" #include "nm-dhcp-utils.h" #include "nm-dhcp-options.h" #include "libnm-platform/nm-platform.h" +#include "nm-hostname-manager.h" +#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-dhcp-client-logging.h" @@ -32,53 +36,24 @@ enum { SIGNAL_NOTIFY, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; -NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient, - PROP_ADDR_FAMILY, - PROP_ANYCAST_ADDRESS, - 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, ); +NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient, PROP_CONFIG, ); 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; - char * anycast_address; - 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; - NMDhcpClientFlags client_flags; - bool iaid_explicit : 1; - bool is_stopped : 1; + NMDhcpClientConfig config; + const NML3ConfigData *l3cd; + GSource *no_lease_timeout_source; + GSource *ipv6_lladdr_timeout_source; + GBytes *effective_client_id; + pid_t pid; + guint watch_id; + NMDhcpState state; + bool iaid_explicit : 1; + bool is_stopped : 1; + struct { + gulong id; + bool wait_dhcp_commit : 1; + bool wait_ll_address : 1; + } l3cfg_notify; } NMDhcpClientPrivate; G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT) @@ -87,6 +62,11 @@ G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT) /*****************************************************************************/ +static void +l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self); + +/*****************************************************************************/ + /* we use pid=-1 for invalid PIDs. Ensure that pid_t can hold negative values. */ G_STATIC_ASSERT(!(((pid_t) -1) > 0)); @@ -98,178 +78,55 @@ _emit_notify(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data) g_signal_emit(G_OBJECT(self), signals[SIGNAL_NOTIFY], 0, notify_data); } -static void -_emit_notify_state_changed(NMDhcpClient *self, - NMDhcpState dhcp_state, - NMIPConfig * ip_config, - GHashTable * options) -{ - const NMDhcpClientNotifyData notify_data = { - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED, - .state_changed = - { - .dhcp_state = dhcp_state, - .ip_config = ip_config, - .options = options, - }, - }; - - _emit_notify(self, ¬ify_data); -} - /*****************************************************************************/ -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) +static void +connect_l3cfg_notify(NMDhcpClient *self) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + gboolean do_connect; - 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); + do_connect = priv->l3cfg_notify.wait_dhcp_commit | priv->l3cfg_notify.wait_ll_address; - if (route_metric != priv->route_metric) { - priv->route_metric = route_metric; - _notify(self, PROP_ROUTE_METRIC); + if (!do_connect) { + nm_clear_g_signal_handler(priv->config.l3cfg, &priv->l3cfg_notify.id); + return; } -} -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; + if (priv->l3cfg_notify.id == 0) { + priv->l3cfg_notify.id = g_signal_connect(priv->config.l3cfg, + NM_L3CFG_SIGNAL_NOTIFY, + G_CALLBACK(l3_cfg_notify_cb), + self); + } } -GBytes * -nm_dhcp_client_get_client_id(NMDhcpClient *self) +pid_t +nm_dhcp_client_get_pid(NMDhcpClient *self) { - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); + g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), -1); - return NM_DHCP_CLIENT_GET_PRIVATE(self)->client_id; + return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid; } static void -_set_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) +_set_effective_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 (priv->effective_client_id == client_id + || (priv->effective_client_id && client_id + && g_bytes_equal(priv->effective_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 (priv->effective_client_id) + g_bytes_unref(priv->effective_client_id); + priv->effective_client_id = client_id; if (!take && client_id) g_bytes_ref(client_id); @@ -277,94 +134,20 @@ _set_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) 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"); + priv->config.addr_family == AF_INET6 ? "duid" : "client-id", + priv->effective_client_id + ? (s = nm_dhcp_utils_duid_to_string(priv->effective_client_id)) + : "default"); } } void -nm_dhcp_client_set_client_id(NMDhcpClient *self, GBytes *client_id) +nm_dhcp_client_set_effective_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_anycast_address(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->anycast_address; -} - -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; -} - -NMDhcpClientFlags -nm_dhcp_client_get_client_flags(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NM_DHCP_CLIENT_FLAGS_NONE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->client_flags; -} - -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; + _set_effective_client_id(self, client_id, FALSE); } /*****************************************************************************/ @@ -413,14 +196,6 @@ reason_to_state(NMDhcpClient *self, const char *iface, const char *reason) /*****************************************************************************/ 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); @@ -457,95 +232,181 @@ stop(NMDhcpClient *self, gboolean release) 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); + nm_dhcp_client_stop_pid(priv->pid, priv->config.iface); } priv->pid = -1; } -void -nm_dhcp_client_set_state(NMDhcpClient *self, - NMDhcpState new_state, - NMIPConfig * ip_config, - GHashTable * options) +static gboolean +_no_lease_timeout(gpointer user_data) { + NMDhcpClient *self = user_data; NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - 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); + nm_clear_g_source_inst(&priv->no_lease_timeout_source); + + _emit_notify(self, + &((NMDhcpClientNotifyData){ + .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT, + })); + return G_SOURCE_CONTINUE; +} + +const NMDhcpClientConfig * +nm_dhcp_client_get_config(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return &priv->config; +} + +static void +schedule_no_lease_timeout(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + if (priv->no_lease_timeout_source) + return; + + if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) { + _LOGI("activation: beginning transaction (no timeout)"); + priv->no_lease_timeout_source = g_source_ref(nm_g_source_sentinel_get(0)); } else { - g_return_if_fail(!ip_config); - g_return_if_fail(!options); + _LOGI("activation: beginning transaction (timeout in %u seconds)", + (guint) priv->config.timeout); + priv->no_lease_timeout_source = + nm_g_timeout_add_seconds_source(priv->config.timeout, _no_lease_timeout, self); } +} + +void +nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3ConfigData *l3cd) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + GHashTable *options; + const int IS_IPv4 = NM_IS_IPv4(priv->config.addr_family); + nm_auto_unref_l3cd const NML3ConfigData *l3cd_merged = NULL; + + g_return_if_fail(NM_IS_DHCP_CLIENT(self)); + + if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) { + g_return_if_fail(NM_IS_L3_CONFIG_DATA(l3cd)); + g_return_if_fail(nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family)); + } else + g_return_if_fail(!l3cd); + + if (l3cd) + nm_l3_config_data_seal(l3cd); - 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 (!IS_IPv4 && l3cd) { + if (nm_dhcp_utils_merge_new_dhcp6_lease(priv->l3cd, l3cd, &l3cd_merged)) { + l3cd = nm_l3_config_data_seal(l3cd_merged); + } + } - if (_LOGD_ENABLED()) { - gs_free const char **keys = NULL; - guint i, nkeys; + if (priv->l3cd == l3cd) + return; - keys = nm_strdict_get_keys(options, TRUE, &nkeys); - for (i = 0; i < nkeys; i++) { - _LOGD("option %-20s => '%s'", keys[i], (char *) g_hash_table_lookup(options, keys[i])); - } + if (l3cd) { + nm_clear_g_source_inst(&priv->no_lease_timeout_source); + } else { + if (priv->l3cd) + schedule_no_lease_timeout(self); } - if (_LOGT_ENABLED() && priv->addr_family == AF_INET6) { - gs_free char *event_id = NULL; + /* FIXME(l3cfg:dhcp): the API of NMDhcpClient is changing to expose a simpler API. + * The internals like NMDhcpState should not be exposed (or possibly dropped in large + * parts). */ + + nm_l3_config_data_reset(&priv->l3cd, l3cd); + + options = l3cd ? nm_dhcp_lease_get_options( + nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family)) + : NULL; + + if (_LOGD_ENABLED()) { + if (options) { + gs_free const char **keys = NULL; + guint nkeys; + guint i; + + keys = nm_strdict_get_keys(options, TRUE, &nkeys); + for (i = 0; i < nkeys; i++) { + _LOGD("option %-20s => '%s'", + keys[i], + (char *) g_hash_table_lookup(options, keys[i])); + } + + if (priv->config.addr_family == AF_INET6) { + gs_free char *event_id = NULL; - event_id = nm_dhcp_utils_get_dhcp6_event_id(options); - if (event_id) - _LOGT("event-id: \"%s\"", event_id); + event_id = nm_dhcp_utils_get_dhcp6_event_id(options); + if (event_id) + _LOGT("event-id: \"%s\"", event_id); + } + } } if (_LOGI_ENABLED()) { const char *req_str = - NM_IS_IPv4(priv->addr_family) - ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS) - : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS); + IS_IPv4 ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS) + : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS); const char *addr = nm_g_hash_table_lookup(options, req_str); - _LOGI("state changed %s -> %s%s%s%s", - nm_dhcp_state_to_string(priv->state), - nm_dhcp_state_to_string(new_state), + _LOGI("state changed %s%s%s%s", + priv->l3cd ? "new lease" : "no lease", NM_PRINT_FMT_QUOTED(addr, ", address=", addr, "", "")); } - priv->state = new_state; - - _emit_notify_state_changed(self, 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); + /* FIXME(l3cfg:dhcp:acd): NMDhcpClient must also do ACD. It needs acd_timeout_msec + * as a configuration parameter (in NMDhcpClientConfig). When ACD is enabled, + * when a new lease gets announced, it must first use NML3Cfg to run ACD on the + * interface (the previous lease -- if any -- will still be used at that point). + * If ACD fails, we call nm_dhcp_client_decline() and try to get a different + * lease. + * If ACD passes, we need to notify the new lease, and the user (NMDevice) may + * then configure the address. We need to watch the configured addresses (in NML3Cfg), + * and if the address appears there, we need to accept the lease. That is complicated + * but necessary, because we can only accept the lease after we configured the + * address. + * + * As a whole, ACD is transparent for the user (NMDevice). It's entirely managed + * by NMDhcpClient. Note that we do ACD through NML3Cfg, which centralizes IP handling + * for one interface, so for example if the same address happens to be configured + * as a static address (bypassing ACD), then NML3Cfg is aware of that and signals + * immediate success. */ + + if (nm_dhcp_client_can_accept(self) && new_state == NM_DHCP_STATE_BOUND && priv->l3cd + && nm_l3_config_data_get_num_addresses(priv->l3cd, priv->config.addr_family) > 0) { + priv->l3cfg_notify.wait_dhcp_commit = TRUE; + } else { + priv->l3cfg_notify.wait_dhcp_commit = FALSE; + } + connect_l3cfg_notify(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; + { + const NMDhcpClientNotifyData notify_data = { + .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + .lease_update = + { + .l3cd = priv->l3cd, + .accepted = !priv->l3cfg_notify.wait_dhcp_commit, + }, + }; + + _emit_notify(self, ¬ify_data); + } } static void daemon_watch_cb(GPid pid, int status, gpointer user_data) { - NMDhcpClient * self = NM_DHCP_CLIENT(user_data); + NMDhcpClient *self = NM_DHCP_CLIENT(user_data); NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - gs_free char * desc = NULL; + gs_free char *desc = NULL; g_return_if_fail(priv->watch_id); priv->watch_id = 0; @@ -554,22 +415,7 @@ daemon_watch_cb(GPid pid, int status, gpointer user_data) 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); + nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL); } void @@ -580,8 +426,6 @@ nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid) 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); } @@ -595,14 +439,10 @@ nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid) priv->pid = -1; watch_cleanup(self); - timeout_cleanup(self); } gboolean -nm_dhcp_client_start_ip4(NMDhcpClient *self, - GBytes * client_id, - const char * last_ip4_address, - GError ** error) +nm_dhcp_client_start_ip4(NMDhcpClient *self, GError **error) { NMDhcpClientPrivate *priv; @@ -610,24 +450,25 @@ nm_dhcp_client_start_ip4(NMDhcpClient *self, 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); + g_return_val_if_fail(priv->config.addr_family == AF_INET, FALSE); + g_return_val_if_fail(priv->config.uuid, 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); + schedule_no_lease_timeout(self); - return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, last_ip4_address, error); + return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error); } gboolean nm_dhcp_client_accept(NMDhcpClient *self, 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->l3cd, FALSE); + if (NM_DHCP_CLIENT_GET_CLASS(self)->accept) { return NM_DHCP_CLIENT_GET_CLASS(self)->accept(self, error); } @@ -652,8 +493,14 @@ nm_dhcp_client_can_accept(NMDhcpClient *self) gboolean nm_dhcp_client_decline(NMDhcpClient *self, const char *error_message, 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->l3cd, FALSE); + if (NM_DHCP_CLIENT_GET_CLASS(self)->decline) { return NM_DHCP_CLIENT_GET_CLASS(self)->decline(self, error_message, error); } @@ -667,43 +514,185 @@ get_duid(NMDhcpClient *self) return NULL; } +static gboolean +ipv6_lladdr_timeout(gpointer user_data) +{ + NMDhcpClient *self = user_data; + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); + + _emit_notify( + self, + &((NMDhcpClientNotifyData){ + .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = "timeout reached while waiting for an IPv6 link-local address", + })); + return G_SOURCE_CONTINUE; +} + +static const NMPlatformIP6Address * +ipv6_lladdr_find(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + NML3Cfg *l3cfg; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject *obj; + + l3cfg = priv->config.l3cfg; + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, nm_l3cfg_get_ifindex(l3cfg)); + + nm_platform_iter_obj_for_each (&iter, nm_l3cfg_get_platform(l3cfg), &lookup, &obj) { + const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); + + if (!IN6_IS_ADDR_LINKLOCAL(&pladdr->address)) + continue; + if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) + continue; + return pladdr; + } + return NULL; +} + +static void +l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_assert(l3cfg == priv->config.l3cfg); + + switch (notify_data->notify_type) { + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: + { + const NMPlatformIP6Address *addr; + gs_free_error GError *error = NULL; + + if (!priv->l3cfg_notify.wait_ll_address) + return; + + addr = ipv6_lladdr_find(self); + if (addr) { + _LOGD("got IPv6LL address, starting transaction"); + priv->l3cfg_notify.wait_ll_address = FALSE; + connect_l3cfg_notify(self); + nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); + + schedule_no_lease_timeout(self); + + if (!NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, &error)) { + _emit_notify(self, + &((NMDhcpClientNotifyData){ + .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = error->message, + })); + } + } + + break; + } + case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT: + { + const NML3ConfigData *committed_l3cd; + NMDedupMultiIter ipconf_iter; + const NMPlatformIPAddress *lease_address; + gs_free_error GError *error = NULL; + + /* A new configuration was committed to the interface. If we previously + * got a lease, check whether we are waiting for the address to be + * configured. If the address was added, we can proceed accepting the + * lease and notifying NMDevice. */ + + if (!priv->l3cfg_notify.wait_dhcp_commit) + return; + + nm_l3_config_data_iter_ip_address_for_each (&ipconf_iter, + priv->l3cd, + priv->config.addr_family, + &lease_address) + break; + nm_assert(lease_address); + committed_l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE); + + if (priv->config.addr_family == AF_INET) { + const NMPlatformIP4Address *address4 = (const NMPlatformIP4Address *) lease_address; + + if (!nm_l3_config_data_lookup_address_4(committed_l3cd, + address4->address, + address4->plen, + address4->peer_address)) + return; + } else { + const NMPlatformIP6Address *address6 = (const NMPlatformIP6Address *) lease_address; + + if (!nm_l3_config_data_lookup_address_6(committed_l3cd, &address6->address)) + return; + } + + priv->l3cfg_notify.wait_dhcp_commit = FALSE; + connect_l3cfg_notify(self); + + _LOGD("accept address"); + + if (!nm_dhcp_client_accept(self, &error)) { + gs_free char *reason = g_strdup_printf("error accepting lease: %s", error->message); + + _emit_notify(self, + &((NMDhcpClientNotifyData){ + .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = reason, + })); + return; + } + + _emit_notify( + self, + &((NMDhcpClientNotifyData){.notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + .lease_update = { + .l3cd = priv->l3cd, + .accepted = TRUE, + }})); + break; + }; + default: + /* ignore */; + } +} + gboolean -nm_dhcp_client_start_ip6(NMDhcpClient * self, - GBytes * client_id, - gboolean enforce_duid, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error) +nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error) { - NMDhcpClientPrivate *priv; - gs_unref_bytes GBytes *own_client_id = NULL; + NMDhcpClientPrivate *priv; + gs_unref_bytes GBytes *own_client_id = NULL; + const NMPlatformIP6Address *addr; 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); + g_return_val_if_fail(priv->config.addr_family == AF_INET6, FALSE); + g_return_val_if_fail(priv->config.uuid, FALSE); + g_return_val_if_fail(!priv->effective_client_id, FALSE); - if (!enforce_duid) + if (!priv->config.v6.enforce_duid) own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self); - _set_client_id(self, own_client_id ?: client_id, FALSE); + _set_effective_client_id(self, own_client_id ?: priv->config.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); + addr = ipv6_lladdr_find(self); + if (!addr) { + _LOGD("waiting for IPv6LL address"); + priv->l3cfg_notify.wait_ll_address = TRUE; + connect_l3cfg_notify(self); + priv->ipv6_lladdr_timeout_source = + nm_g_timeout_add_seconds_source(10, ipv6_lladdr_timeout, self); + return TRUE; + } - return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, - ll_addr, - privacy, - needed_prefixes, - error); + schedule_no_lease_timeout(self); + + return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, error); } void @@ -711,7 +700,7 @@ nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name) { guint64 start_time; pid_t pid, ppid; - const char * exe; + const char *exe; char proc_path[NM_STRLEN("/proc/%lu/cmdline") + 100]; gs_free char *pid_contents = NULL, *proc_contents = NULL; @@ -780,6 +769,10 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) priv->is_stopped = TRUE; + priv->l3cfg_notify.wait_dhcp_commit = FALSE; + priv->l3cfg_notify.wait_ll_address = FALSE; + connect_l3cfg_notify(self); + /* Kill the DHCP client */ old_pid = priv->pid; NM_DHCP_CLIENT_GET_CLASS(self)->stop(self, release); @@ -789,7 +782,7 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) _LOGI("canceled DHCP transaction"); nm_assert(priv->pid == -1); - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL); + nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL); } /*****************************************************************************/ @@ -799,10 +792,10 @@ bytearray_variant_to_string(NMDhcpClient *self, GVariant *value, const char *key { const guint8 *array; gsize length; - GString * str; + GString *str; int i; unsigned char c; - char * converted = NULL; + char *converted = NULL; g_return_val_if_fail(value != NULL, NULL); @@ -879,7 +872,7 @@ maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant */ if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) { gs_free guint8 *check_val = NULL; - char * hex_str = NULL; + char *hex_str = NULL; gsize len; /* dhclient passes values from dhcp private options in its own "string" format: @@ -915,18 +908,16 @@ nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *self, const NMPlatformIP gboolean nm_dhcp_client_handle_event(gpointer unused, - const char * iface, + const char *iface, int pid, - GVariant * options, - const char * reason, + 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 = { + NMDhcpClientPrivate *priv; + guint32 new_state; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMPlatformIP6Address prefix = { 0, }; @@ -938,25 +929,26 @@ nm_dhcp_client_handle_event(gpointer unused, priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - if (g_strcmp0(priv->iface, iface) != 0) + if (!nm_streq0(priv->config.iface, iface)) return FALSE; if (priv->pid != pid) return FALSE; - old_state = priv->state; - new_state = reason_to_state(self, priv->iface, reason); + new_state = reason_to_state(self, priv->config.iface, reason); + if (new_state == NM_DHCP_STATE_NOOP) + return TRUE; + _LOGD("DHCP state '%s' -> '%s' (reason: '%s')", - nm_dhcp_state_to_string(old_state), + nm_dhcp_state_to_string(priv->state), nm_dhcp_state_to_string(new_state), reason); - - if (new_state == NM_DHCP_STATE_NOOP) - return TRUE; + priv->state = new_state; if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) { - GVariantIter iter; - const char * name; - GVariant * value; + gs_unref_hashtable GHashTable *str_options = NULL; + 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); @@ -968,25 +960,29 @@ nm_dhcp_client_handle_event(gpointer unused, /* 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)); + if (priv->config.addr_family == AF_INET) { + l3cd = nm_dhcp_utils_ip4_config_from_options( + nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + priv->config.iface, + str_options); } 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, + prefix = nm_dhcp_utils_ip6_prefix_from_options(str_options); + l3cd = nm_dhcp_utils_ip6_config_from_options( + nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + priv->config.iface, str_options, - NM_FLAGS_HAS(priv->client_flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY))); + priv->config.v6.info_only); } } else g_warn_if_reached(); + + if (l3cd) { + nm_l3_config_data_set_dhcp_lease_from_options(l3cd, + priv->config.addr_family, + g_steal_pointer(&str_options)); + } } if (!IN6_IS_ADDR_UNSPECIFIED(&prefix.address)) { @@ -994,17 +990,16 @@ nm_dhcp_client_handle_event(gpointer unused, * 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); - } + return TRUE; + } - nm_dhcp_client_set_state(self, new_state, ip_config, str_options); + /* Fail if no valid IP config was received */ + if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED) && !l3cd) { + _LOGW("client bound but IP config not received"); + new_state = NM_DHCP_STATE_FAIL; } + nm_dhcp_client_set_state(self, new_state, l3cd); return TRUE; } @@ -1016,18 +1011,18 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) guint i; /* IPv6 not implemented yet */ - nm_assert(priv->addr_family == AF_INET); + nm_assert(priv->config.addr_family == AF_INET); - if (!priv->reject_servers || !priv->reject_servers[0]) + if (!priv->config.reject_servers || !priv->config.reject_servers[0]) return FALSE; - for (i = 0; priv->reject_servers[i]; i++) { + for (i = 0; priv->config.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], + priv->config.reject_servers[i], NULL, &r_addr, &r_prefix)) @@ -1040,146 +1035,148 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) return FALSE; } -/*****************************************************************************/ +static void +config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) +{ + *config = *src; + + g_object_ref(config->l3cfg); + + if (config->hwaddr) + g_bytes_ref(config->hwaddr); + if (config->bcast_hwaddr) + g_bytes_ref(config->bcast_hwaddr); + if (config->vendor_class_identifier) + g_bytes_ref(config->vendor_class_identifier); + if (config->client_id) + g_bytes_ref(config->client_id); + + config->iface = g_strdup(config->iface); + config->uuid = g_strdup(config->uuid); + config->anycast_address = g_strdup(config->anycast_address); + config->hostname = g_strdup(config->hostname); + config->mud_url = g_strdup(config->mud_url); + + config->reject_servers = (const char *const *) nm_strv_dup(config->reject_servers, -1, TRUE); + + if (config->addr_family == AF_INET) { + config->v4.last_address = g_strdup(config->v4.last_address); + } else if (config->addr_family == AF_INET6) { + config->hwaddr = NULL; + config->bcast_hwaddr = NULL; + config->use_fqdn = TRUE; + } else { + nm_assert_not_reached(); + } + + if (!config->hostname && config->send_hostname) { + const char *hostname; + gs_free char *hostname_tmp = NULL; + + hostname = nm_hostname_manager_get_static_hostname(nm_hostname_manager_get()); + + if (nm_utils_is_specific_hostname(hostname)) { + if (config->addr_family == AF_INET) { + char *dot; + + hostname_tmp = g_strdup(hostname); + dot = strchr(hostname_tmp, '.'); + if (dot) + *dot = '\0'; + } + config->hostname = hostname_tmp ? g_steal_pointer(&hostname_tmp) : g_strdup(hostname); + } + } + + if (config->hostname) { + if (!config->send_hostname) { + nm_clear_g_free((gpointer *) &config->hostname); + } else if ((config->use_fqdn && !nm_sd_dns_name_is_valid(config->hostname)) + || (!config->use_fqdn && !nm_sd_hostname_is_valid(config->hostname, FALSE))) { + nm_log_warn(LOGD_DHCP, + "dhcp%c: %s '%s' is invalid, will be ignored", + nm_utils_addr_family_to_char(config->addr_family), + config->use_fqdn ? "FQDN" : "hostname", + config->hostname); + nm_clear_g_free((gpointer *) &config->hostname); + } + } +} static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +config_clear(NMDhcpClientConfig *config) { - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object); + g_object_unref(config->l3cfg); - 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; + nm_clear_pointer(&config->hwaddr, g_bytes_unref); + nm_clear_pointer(&config->bcast_hwaddr, g_bytes_unref); + nm_clear_pointer(&config->vendor_class_identifier, g_bytes_unref); + nm_clear_pointer(&config->client_id, g_bytes_unref); + + nm_clear_g_free((gpointer *) &config->iface); + nm_clear_g_free((gpointer *) &config->uuid); + nm_clear_g_free((gpointer *) &config->anycast_address); + nm_clear_g_free((gpointer *) &config->hostname); + nm_clear_g_free((gpointer *) &config->mud_url); + + nm_clear_pointer((gpointer *) &config->reject_servers, g_strfreev); + + if (config->addr_family == AF_INET) { + nm_clear_g_free((gpointer *) &config->v4.last_address); } } +int +nm_dhcp_client_get_addr_family(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return priv->config.addr_family; +} + +const char * +nm_dhcp_client_get_iface(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return priv->config.iface; +} + +NMDedupMultiIndex * +nm_dhcp_client_get_multi_idx(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return nm_l3cfg_get_multi_idx(priv->config.l3cfg); +} + +int +nm_dhcp_client_get_ifindex(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return nm_l3cfg_get_ifindex(priv->config.l3cfg); +} + +GBytes * +nm_dhcp_client_get_effective_client_id(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return priv->effective_client_id; +} + +/*****************************************************************************/ + 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(!NM_FLAGS_ANY(flags, ~((guint) NM_DHCP_CLIENT_FLAGS_ALL))); - priv->client_flags = flags; - 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_ANYCAST_ADDRESS: - /* construct-only */ - priv->anycast_address = g_value_dup_string(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: + case PROP_CONFIG: /* 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_strv_dup_packed(g_value_get_boxed(value), -1); + config_init(&priv->config, g_value_get_pointer(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -1200,53 +1197,31 @@ nm_dhcp_client_init(NMDhcpClient *self) priv->pid = -1; } -#if NM_MORE_ASSERTS static void -constructed(GObject *object) +dispose(GObject *object) { - NMDhcpClient * self = NM_DHCP_CLIENT(object); + NMDhcpClient *self = NM_DHCP_CLIENT(object); NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - /* certain flags only make sense with certain address family. Assert - * for that. */ - if (NM_IS_IPv4(priv->addr_family)) - nm_assert(!NM_FLAGS_ANY(priv->client_flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY)); - else { - nm_assert(NM_FLAGS_HAS(priv->client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN)); - nm_assert(!NM_FLAGS_ANY(priv->client_flags, NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST)); - } + nm_dhcp_client_stop(self, FALSE); - nm_assert(!priv->anycast_address || nm_utils_hwaddr_valid(priv->anycast_address, ETH_ALEN)); + watch_cleanup(self); + + nm_clear_g_source_inst(&priv->no_lease_timeout_source); + nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); - G_OBJECT_CLASS(nm_dhcp_client_parent_class)->constructed(object); + G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object); } -#endif static void -dispose(GObject *object) +finalize(GObject *object) { - NMDhcpClient * self = NM_DHCP_CLIENT(object); + NMDhcpClient *self = NM_DHCP_CLIENT(object); NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - nm_dhcp_client_stop(self, FALSE); - - 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->anycast_address); - 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); + config_clear(&priv->config); - priv->multi_idx = nm_dedup_multi_index_unref(priv->multi_idx); + G_OBJECT_CLASS(nm_dhcp_client_parent_class)->finalize(object); } static void @@ -1256,165 +1231,19 @@ nm_dhcp_client_class_init(NMDhcpClientClass *client_class) g_type_class_add_private(client_class, sizeof(NMDhcpClientPrivate)); -#if NM_MORE_ASSERTS - object_class->constructed = constructed; -#endif object_class->dispose = dispose; - object_class->get_property = get_property; + object_class->finalize = finalize; 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, + obj_properties[PROP_CONFIG] = + g_param_spec_pointer(NM_DHCP_CLIENT_CONFIG, "", "", 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_ANYCAST_ADDRESS] = - g_param_spec_string(NM_DHCP_CLIENT_ANYCAST_ADDRESS, - "", - "", - NULL, - G_PARAM_WRITABLE | 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_NOTIFY] = |