diff options
Diffstat (limited to 'src/dhcp')
| -rw-r--r-- | src/dhcp/meson.build | 2 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-client.c | 96 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-client.h | 14 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient-utils.c | 38 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient-utils.h | 5 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient.c | 98 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhcpcanon.c | 19 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhcpcd.c | 5 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-helper.c | 2 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-manager.c | 90 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-manager.h | 2 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-systemd.c | 856 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-utils.c | 15 | ||||
| -rw-r--r-- | src/dhcp/tests/meson.build | 4 | ||||
| -rw-r--r-- | src/dhcp/tests/test-dhcp-dhclient.c | 69 |
15 files changed, 711 insertions, 604 deletions
diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build index 76707bca..a5dd3151 100644 --- a/src/dhcp/meson.build +++ b/src/dhcp/meson.build @@ -13,7 +13,7 @@ executable( link_args: ldflags_linker_script_binary, link_depends: linker_script_binary, install: true, - install_dir: nm_libexecdir + install_dir: nm_libexecdir, ) if enable_tests diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 16db8306..7ed7a686 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -28,7 +28,6 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> -#include <uuid/uuid.h> #include <linux/rtnetlink.h> #include "nm-utils/nm-dedup-multi.h" @@ -62,6 +61,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient, PROP_ROUTE_TABLE, PROP_TIMEOUT, PROP_UUID, + PROP_HOSTNAME, ); typedef struct _NMDhcpClientPrivate { @@ -69,7 +69,6 @@ typedef struct _NMDhcpClientPrivate { char * iface; GBytes * hwaddr; char * uuid; - GBytes * duid; GBytes * client_id; char * hostname; pid_t pid; @@ -140,14 +139,6 @@ nm_dhcp_client_get_uuid (NMDhcpClient *self) } GBytes * -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; -} - -GBytes * nm_dhcp_client_get_hw_addr (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); @@ -230,6 +221,18 @@ _set_client_id (NMDhcpClient *self, GBytes *client_id, gboolean take) 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 @@ -288,12 +291,13 @@ nm_dhcp_client_get_use_fqdn (NMDhcpClient *self) /*****************************************************************************/ 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", + [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", + [NM_DHCP_STATE_TERMINATED] = "terminated", }; static const char * @@ -362,7 +366,7 @@ nm_dhcp_client_stop_pid (pid_t pid, const char *iface) } static void -stop (NMDhcpClient *self, gboolean release, GBytes *duid) +stop (NMDhcpClient *self, gboolean release) { NMDhcpClientPrivate *priv; @@ -449,7 +453,6 @@ 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); - NMDhcpState new_state; g_return_if_fail (priv->watch_id); priv->watch_id = 0; @@ -465,14 +468,9 @@ daemon_watch_cb (GPid pid, int status, gpointer user_data) else _LOGW ("client died abnormally"); - if (!WIFEXITED (status)) - new_state = NM_DHCP_STATE_FAIL; - else - new_state = NM_DHCP_STATE_DONE; - priv->pid = -1; - nm_dhcp_client_set_state (self, new_state, NULL, NULL); + nm_dhcp_client_set_state (self, NM_DHCP_STATE_TERMINATED, NULL, NULL); } void @@ -509,7 +507,6 @@ gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, GBytes *client_id, const char *dhcp_anycast_addr, - const char *hostname, const char *last_ip4_address, GError **error) { @@ -529,9 +526,6 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, nm_dhcp_client_set_client_id (self, client_id); - 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, @@ -550,34 +544,29 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, gboolean enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - const char *hostname, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes, GError **error) { NMDhcpClientPrivate *priv; - gs_free char *str = NULL; + 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); - - nm_assert (!priv->duid); - nm_assert (client_id); + g_return_val_if_fail (!priv->client_id, FALSE); if (!enforce_duid) - priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); - - if (!priv->duid) - priv->duid = g_bytes_ref (client_id); + own_client_id = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); - _LOGD ("DUID is '%s'", (str = nm_dhcp_utils_duid_to_string (priv->duid))); - - g_clear_pointer (&priv->hostname, g_free); - priv->hostname = g_strdup (hostname); + _set_client_id (self, + own_client_id ?: client_id, + FALSE); if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) _LOGI ("activation: beginning transaction (no timeout)"); @@ -588,7 +577,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, dhcp_anycast_addr, ll_addr, privacy, - priv->duid, needed_prefixes, error); } @@ -655,7 +643,7 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release) /* Kill the DHCP client */ old_pid = priv->pid; - NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid); + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release); if (old_pid > 0) _LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid); else @@ -859,6 +847,9 @@ get_property (GObject *object, guint prop_id, case PROP_UUID: g_value_set_string (value, priv->uuid); 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; @@ -899,11 +890,13 @@ set_property (GObject *object, guint prop_id, case PROP_IFACE: /* construct-only */ priv->iface = g_value_dup_string (value); + g_return_if_fail ( priv->iface + && nm_utils_is_valid_iface_name (priv->iface, NULL)); break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); - g_warn_if_fail (priv->ifindex > 0); + g_return_if_fail (priv->ifindex > 0); break; case PROP_HWADDR: /* construct-only */ @@ -919,6 +912,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->uuid = g_value_dup_string (value); break; + case PROP_HOSTNAME: + /* construct-only */ + priv->hostname = g_value_dup_string (value); + break; case PROP_ROUTE_TABLE: priv->route_table = g_value_get_uint (value); break; @@ -971,7 +968,6 @@ dispose (GObject *object) g_clear_pointer (&priv->uuid, g_free); g_clear_pointer (&priv->client_id, g_bytes_unref); g_clear_pointer (&priv->hwaddr, g_bytes_unref); - g_clear_pointer (&priv->duid, g_bytes_unref); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); @@ -1028,6 +1024,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) 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_ROUTE_TABLE] = g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "", 0, G_MAXUINT32, RT_TABLE_MAIN, @@ -1058,7 +1060,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) 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), + 0, NULL, NULL, NULL, G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING); @@ -1066,7 +1068,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) g_signal_new (NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDhcpClientClass, state_changed), + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); } diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 86d60e38..1db7eac6 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -41,6 +41,7 @@ #define NM_DHCP_CLIENT_IFINDEX "ifindex" #define NM_DHCP_CLIENT_INTERFACE "iface" #define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" +#define NM_DHCP_CLIENT_HOSTNAME "hostname" #define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" #define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" #define NM_DHCP_CLIENT_TIMEOUT "timeout" @@ -56,6 +57,7 @@ typedef enum { NM_DHCP_STATE_DONE, /* client quit or stopped */ NM_DHCP_STATE_EXPIRE, /* lease expired or NAKed */ NM_DHCP_STATE_FAIL, /* failed for some reason */ + NM_DHCP_STATE_TERMINATED, /* client is no longer running */ __NM_DHCP_STATE_MAX, NM_DHCP_STATE_MAX = __NM_DHCP_STATE_MAX - 1, } NMDhcpState; @@ -85,13 +87,11 @@ typedef struct { const char *anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error); void (*stop) (NMDhcpClient *self, - gboolean release, - GBytes *duid); + gboolean release); /** * get_duid: @@ -103,12 +103,6 @@ typedef struct { * returned. */ GBytes *(*get_duid) (NMDhcpClient *self); - - /* Signals */ - void (*state_changed) (NMDhcpClient *self, - NMDhcpState state, - GObject *ip_config, - GHashTable *options); } NMDhcpClientClass; GType nm_dhcp_client_get_type (void); @@ -150,7 +144,6 @@ gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self); gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, GBytes *client_id, const char *dhcp_anycast_addr, - const char *hostname, const char *last_ip4_address, GError **error); @@ -159,7 +152,6 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, gboolean enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - const char *hostname, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes, GError **error); diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index a2c3bfb6..d6da3f5c 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -24,6 +24,7 @@ #include <string.h> #include <ctype.h> #include <arpa/inet.h> +#include <net/if.h> #include "nm-utils/nm-dedup-multi.h" @@ -233,29 +234,6 @@ read_client_id (const char *str) return nm_utils_hexstr2bin (s); } -GBytes * -nm_dhcp_dhclient_get_client_id_from_config_file (const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **lines = NULL; - char **line; - - g_return_val_if_fail (path != NULL, NULL); - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) - return NULL; - - if (!g_file_get_contents (path, &contents, NULL, NULL)) - return NULL; - - lines = g_strsplit_set (contents, "\n\r", 0); - for (line = lines; lines && *line; line++) { - if (!strncmp (*line, CLIENTID_TAG, NM_STRLEN (CLIENTID_TAG))) - return read_client_id (*line); - } - return NULL; -} - static gboolean read_interface (const char *line, char *interface, guint size) { @@ -449,6 +427,8 @@ nm_dhcp_dhclient_create_config (const char *interface, add_hostname6 (new_contents, hostname); add_request (reqs, "dhcp6.name-servers"); add_request (reqs, "dhcp6.domain-search"); + + /* FIXME: internal client does not support requesting client-id option. Does this even work? */ add_request (reqs, "dhcp6.client-id"); } @@ -570,6 +550,7 @@ error: #define DUID_PREFIX "default-duid \"" +/* Beware: @error may be unset even if the function returns %NULL. */ GBytes * nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) { @@ -606,9 +587,10 @@ nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, - const char *escaped_duid, + GBytes *duid, GError **error) { + gs_free char *escaped_duid = NULL; gs_strfreev char **lines = NULL; char **iter, *l; GString *s; @@ -616,6 +598,14 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, gsize len = 0; g_return_val_if_fail (leasefile != NULL, FALSE); + + if (!duid) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, + "missing duid"); + g_return_val_if_reached (FALSE); + } + + escaped_duid = nm_dhcp_dhclient_escape_duid (duid); g_return_val_if_fail (escaped_duid != NULL, FALSE); if (g_file_test (leasefile, G_FILE_TEST_EXISTS)) { diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index fab9196a..57a711db 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -40,10 +40,7 @@ GBytes *nm_dhcp_dhclient_unescape_duid (const char *duid); GBytes *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, - const char *escaped_duid, + GBytes *duid, GError **error); -GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path); - #endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */ - diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index 33c26712..0146c8b4 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -121,8 +121,8 @@ get_dhclient_leasefile (int addr_family, const char *uuid, char **out_preferred_path) { - char *rundir_path; - char *path; + gs_free char *rundir_path = NULL; + gs_free char *path = NULL; /* First, see if the lease file is in /run */ rundir_path = g_strdup_printf (NMRUNDIR "/dhclient%s-%s-%s.lease", @@ -132,7 +132,7 @@ get_dhclient_leasefile (int addr_family, if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) { NM_SET_OUT (out_preferred_path, g_strdup (rundir_path)); - return rundir_path; + return g_steal_pointer (&rundir_path); } /* /var/lib/NetworkManager is the preferred leasefile path */ @@ -142,18 +142,14 @@ get_dhclient_leasefile (int addr_family, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) { - g_free (rundir_path); NM_SET_OUT (out_preferred_path, g_strdup (path)); - return path; + return g_steal_pointer (&path); } - if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) { - g_free (path); - path = rundir_path; - } else { - g_free (rundir_path); - } - NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); + if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) + NM_SET_OUT (out_preferred_path, g_steal_pointer (&rundir_path)); + else + NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); /* If the leasefile we're looking for doesn't exist yet in the new location * (eg, /var/lib/NetworkManager) then look in old locations to maintain @@ -166,17 +162,16 @@ get_dhclient_leasefile (int addr_family, path = g_strdup_printf (LOCALSTATEDIR "/lib/dhcp/dhclient%s-%s-%s.lease", _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) - return path; + return g_steal_pointer (&path); /* Old Red Hat and Fedora location */ g_free (path); path = g_strdup_printf (LOCALSTATEDIR "/lib/dhclient/dhclient%s-%s-%s.lease", _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) - return path; + return g_steal_pointer (&path); /* Fail */ - g_free (path); return NULL; } @@ -327,8 +322,18 @@ create_dhclient_config (NMDhcpDhclient *self, else _LOGD ("no existing dhclient configuration to merge"); - if (!merge_dhclient_config (self, addr_family, iface, new, client_id, dhcp_anycast_addr, - hostname, timeout, use_fqdn, orig, out_new_client_id, &error)) { + if (!merge_dhclient_config (self, + addr_family, + iface, + new, + client_id, + dhcp_anycast_addr, + hostname, + timeout, + use_fqdn, + orig, + out_new_client_id, + &error)) { _LOGW ("error creating dhclient configuration: %s", error->message); g_clear_error (&error); } @@ -339,7 +344,6 @@ create_dhclient_config (NMDhcpDhclient *self, static gboolean dhclient_start (NMDhcpClient *client, const char *mode_opt, - GBytes *duid, gboolean release, pid_t *out_pid, int prefixes, @@ -418,10 +422,9 @@ dhclient_start (NMDhcpClient *client, /* Save the DUID to the leasefile dhclient will actually use */ if (addr_family == AF_INET6) { - gs_free char *escaped = NULL; - - escaped = nm_dhcp_dhclient_escape_duid (duid); - if (!nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &local)) { + if (!nm_dhcp_dhclient_save_duid (priv->lease_file, + nm_dhcp_client_get_client_id (client), + &local)) { nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "failed to save DUID to '%s': %s", @@ -541,7 +544,6 @@ ip4_start (NMDhcpClient *client, } return dhclient_start (client, NULL, - NULL, FALSE, NULL, 0, @@ -553,7 +555,6 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { @@ -581,7 +582,6 @@ ip6_start (NMDhcpClient *client, nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)) ? "-S" : "-N", - duid, FALSE, NULL, needed_prefixes, @@ -589,12 +589,12 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release); if (priv->conf_file) if (remove (priv->conf_file) == -1) @@ -613,7 +613,6 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) if (dhclient_start (client, NULL, - duid, TRUE, &rpid, 0, @@ -624,31 +623,13 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) } } -static void -state_changed (NMDhcpClient *client, - NMDhcpState state, - GObject *ip_config, - GHashTable *options) -{ - NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE ((NMDhcpDhclient *) client); - gs_unref_bytes GBytes *client_id = NULL; - - if (nm_dhcp_client_get_client_id (client)) - return; - if (state != NM_DHCP_STATE_BOUND) - return; - - client_id = nm_dhcp_dhclient_get_client_id_from_config_file (priv->conf_file); - nm_dhcp_client_set_client_id (client, client_id); -} - static GBytes * get_duid (NMDhcpClient *client) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); GBytes *duid = NULL; - char *leasefile; + gs_free char *leasefile = NULL; GError *error = NULL; /* Look in interface-specific leasefile first for backwards compat */ @@ -659,25 +640,23 @@ get_duid (NMDhcpClient *client) if (leasefile) { _LOGD ("looking for DUID in '%s'", leasefile); duid = nm_dhcp_dhclient_read_duid (leasefile, &error); - if (error) { _LOGW ("failed to read leasefile '%s': %s", leasefile, error->message); g_clear_error (&error); } - g_free (leasefile); + if (duid) + return duid; } - if (!duid) { - /* Otherwise read the default machine-wide DUID */ - _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); - duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); - if (error) { - _LOGW ("failed to read leasefile '%s': %s", - priv->def_leasefile, - error->message); - g_clear_error (&error); - } + /* Otherwise read the default machine-wide DUID */ + _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); + duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); + if (error) { + _LOGW ("failed to read leasefile '%s': %s", + priv->def_leasefile, + error->message); + g_clear_error (&error); } return duid; @@ -742,7 +721,6 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class) client_class->ip6_start = ip6_start; client_class->stop = stop; client_class->get_duid = get_duid; - client_class->state_changed = state_changed; } const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = { diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c index de403020..0f033e22 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -193,20 +193,20 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "dhcpcanon plugin does not support IPv6"); return FALSE; } + static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release); if (priv->pid_file) { if (remove (priv->pid_file) == -1) @@ -216,18 +216,6 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) } } -static void -state_changed (NMDhcpClient *client, - NMDhcpState state, - GObject *ip_config, - GHashTable *options) -{ - if (nm_dhcp_client_get_client_id (client)) - return; - if (state != NM_DHCP_STATE_BOUND) - return; -} - /*****************************************************************************/ static void @@ -270,7 +258,6 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class) client_class->ip4_start = ip4_start; client_class->ip6_start = ip6_start; client_class->stop = stop; - client_class->state_changed = state_changed; } const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = { diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c index 98ab5342..e2a1354f 100644 --- a/src/dhcp/nm-dhcp-dhcpcd.c +++ b/src/dhcp/nm-dhcp-dhcpcd.c @@ -187,7 +187,6 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { @@ -196,12 +195,12 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client); NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release); if (priv->pid_file) { if (remove (priv->pid_file) == -1) diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c index 7f1d2a7b..83cc4600 100644 --- a/src/dhcp/nm-dhcp-helper.c +++ b/src/dhcp/nm-dhcp-helper.c @@ -190,7 +190,7 @@ do_notify: _LOGW ("failure to call notify: %s (try signal via Event)", error->message); g_clear_error (&error); - /* for backward compatibilty, try to emit the signal. There is no stable + /* for backward compatibility, try to emit the signal. There is no stable * API between the dhcp-helper and NetworkManager. However, while upgrading * the NetworkManager package, a newer helper might want to notify an * older server, which still uses the "Event". */ diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index 5ae16d72..c13c3043 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -181,6 +181,7 @@ client_start (NMDhcpManager *self, gsize hwaddr_len; g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (uuid != NULL, NULL); g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL); @@ -221,6 +222,7 @@ client_start (NMDhcpManager *self, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, NM_DHCP_CLIENT_UUID, uuid, + NM_DHCP_CLIENT_HOSTNAME, hostname, NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table, NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric, NM_DHCP_CLIENT_TIMEOUT, (guint) timeout, @@ -233,11 +235,36 @@ client_start (NMDhcpManager *self, c_list_link_tail (&priv->dhcp_client_lst_head, &client->dhcp_client_lst); g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self); + /* unfortunately, our implementations work differently per address-family regarding client-id/DUID. + * + * - for IPv4, the calling code may determine a client-id (from NM's connection profile). + * If present, it is taken. If not present, the DHCP plugin uses a plugin specific default. + * - for "internal" plugin, the default is just "mac". + * - for "dhclient", we try to get the configuration from dhclient's /etc/dhcp or fallback + * to whatever dhclient uses by default. + * We do it this way, because for dhclient the user may configure a default + * outside of NM, and we want to honor that. Worse, dhclient could be a wapper + * script where the wrapper script overwrites the client-id. We need to distinguish + * between: force a particular client-id and leave it unspecified to whatever dhclient + * wants. + * + * - for IPv6, the calling code always determines a client-id. It also specifies @enforce_duid, + * to determine whether the given client-id must be used. + * - for "internal" plugin @enforce_duid doesn't matter and the given client-id is + * always used. + * - for "dhclient", @enforce_duid FALSE means to first try to load the DUID from the + * lease file, and only otherwise fallback to the given client-id. + * - other plugins don't support DHCPv6. + * It's done this way, so that existing dhclient setups don't change behavior on upgrade. + * + * This difference is cumbersome and only exists because of "dhclient" which supports hacking the + * default outside of NetworkManager API. + */ + if (addr_family == AF_INET) { success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, - hostname, last_ip4_address, error); } else { @@ -246,7 +273,6 @@ client_start (NMDhcpManager *self, enforce_duid, dhcp_anycast_addr, ipv6_ll_addr, - hostname, privacy, needed_prefixes, error); @@ -311,10 +337,27 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, } } - return client_start (self, AF_INET, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, NULL, - dhcp_client_id, 0, timeout, dhcp_anycast_addr, hostname, - use_fqdn, FALSE, 0, last_ip_address, 0, error); + return client_start (self, + AF_INET, + multi_idx, + iface, + ifindex, + hwaddr, + uuid, + route_table, + route_metric, + NULL, + dhcp_client_id, + FALSE, + timeout, + dhcp_anycast_addr, + hostname, + use_fqdn, + FALSE, + 0, + last_ip_address, + 0, + error); } /* Caller owns a reference to the NMDhcpClient on return */ @@ -349,10 +392,27 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, /* Always prefer the explicit dhcp-hostname if given */ hostname = dhcp_hostname ?: priv->default_hostname; } - return client_start (self, AF_INET6, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, ll_addr, duid, enforce_duid, - timeout, dhcp_anycast_addr, hostname, TRUE, info_only, - privacy, NULL, needed_prefixes, error); + return client_start (self, + AF_INET6, + multi_idx, + iface, + ifindex, + hwaddr, + uuid, + route_table, + route_metric, + ll_addr, + duid, + enforce_duid, + timeout, + dhcp_anycast_addr, + hostname, + TRUE, + info_only, + privacy, + NULL, + needed_prefixes, + error); } void @@ -384,6 +444,12 @@ nm_dhcp_manager_get_config (NMDhcpManager *self) NM_DEFINE_SINGLETON_GETTER (NMDhcpManager, nm_dhcp_manager_get, NM_TYPE_DHCP_MANAGER); +void +nmtst_dhcp_manager_unget (gpointer self) +{ + _nmtst_nm_dhcp_manager_get_reset (self); +} + static void nm_dhcp_manager_init (NMDhcpManager *self) { @@ -446,6 +512,10 @@ nm_dhcp_manager_init (NMDhcpManager *self) nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name); + /* NOTE: currently the DHCP plugin is chosen once at start. It's not + * possible to reload that configuration. If that ever becomes possible, + * beware that the "dhcp-plugin" device spec made decisions based on + * the previous plugin and may need reevaluation. */ priv->client_factory = client_factory; } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index 1d9e5c21..f8f39e53 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -87,4 +87,6 @@ extern const char* nm_dhcp_helper_path; extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4]; +void nmtst_dhcp_manager_unget (gpointer singleton_instance); + #endif /* __NETWORKMANAGER_DHCP_MANAGER_H__ */ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 5b7b5fbe..bcbe916f 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -34,6 +34,7 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-dhcp-utils.h" +#include "nm-core-utils.h" #include "NetworkManagerUtils.h" #include "platform/nm-platform.h" #include "nm-dhcp-client-logging.h" @@ -97,63 +98,77 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) #define DHCP6_OPTION_IAID 1034 typedef struct { - guint num; const char *name; - gboolean include; + uint16_t option_num; + bool include; } ReqOption; #define REQPREFIX "requested_" +#define REQ(_num, _name, _include) \ + { \ + .name = REQPREFIX""_name, \ + .option_num = _num, \ + .include = _include, \ + } + static const ReqOption dhcp4_requests[] = { - { SD_DHCP_OPTION_SUBNET_MASK, REQPREFIX "subnet_mask", TRUE }, - { SD_DHCP_OPTION_TIME_OFFSET, REQPREFIX "time_offset", TRUE }, - { SD_DHCP_OPTION_ROUTER, REQPREFIX "routers", TRUE }, - { SD_DHCP_OPTION_DOMAIN_NAME_SERVER, REQPREFIX "domain_name_servers", TRUE }, - { SD_DHCP_OPTION_HOST_NAME, REQPREFIX "host_name", TRUE }, - { SD_DHCP_OPTION_DOMAIN_NAME, REQPREFIX "domain_name", TRUE }, - { SD_DHCP_OPTION_INTERFACE_MTU, REQPREFIX "interface_mtu", TRUE }, - { SD_DHCP_OPTION_BROADCAST, REQPREFIX "broadcast_address", TRUE }, - { SD_DHCP_OPTION_STATIC_ROUTE, REQPREFIX "static_routes", TRUE }, - { DHCP_OPTION_NIS_DOMAIN, REQPREFIX "nis_domain", TRUE }, - { DHCP_OPTION_NIS_SERVERS, REQPREFIX "nis_servers", TRUE }, - { SD_DHCP_OPTION_NTP_SERVER, REQPREFIX "ntp_servers", TRUE }, - { SD_DHCP_OPTION_SERVER_IDENTIFIER, REQPREFIX "dhcp_server_identifier", TRUE }, - { SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, REQPREFIX "domain_search", TRUE }, - { SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, REQPREFIX "rfc3442_classless_static_routes", TRUE }, - { SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, REQPREFIX "ms_classless_static_routes", TRUE }, - { SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, REQPREFIX "wpad", TRUE }, - { SD_DHCP_OPTION_ROOT_PATH, REQPREFIX "root_path", TRUE }, + REQ (SD_DHCP_OPTION_SUBNET_MASK, "subnet_mask", TRUE ), + REQ (SD_DHCP_OPTION_TIME_OFFSET, "time_offset", TRUE ), + REQ (SD_DHCP_OPTION_DOMAIN_NAME_SERVER, "domain_name_servers", TRUE ), + REQ (SD_DHCP_OPTION_HOST_NAME, "host_name", TRUE ), + REQ (SD_DHCP_OPTION_DOMAIN_NAME, "domain_name", TRUE ), + REQ (SD_DHCP_OPTION_INTERFACE_MTU, "interface_mtu", TRUE ), + REQ (SD_DHCP_OPTION_BROADCAST, "broadcast_address", TRUE ), + + /* RFC 3442: The Classless Static Routes option code MUST appear in the parameter + * request list prior to both the Router option code and the Static + * Routes option code, if present. */ + REQ (SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, "rfc3442_classless_static_routes", TRUE ), + REQ (SD_DHCP_OPTION_ROUTER, "routers", TRUE ), + REQ (SD_DHCP_OPTION_STATIC_ROUTE, "static_routes", TRUE ), + + REQ (DHCP_OPTION_NIS_DOMAIN, "nis_domain", TRUE ), + REQ (DHCP_OPTION_NIS_SERVERS, "nis_servers", TRUE ), + REQ (SD_DHCP_OPTION_NTP_SERVER, "ntp_servers", TRUE ), + REQ (SD_DHCP_OPTION_SERVER_IDENTIFIER, "dhcp_server_identifier", TRUE ), + REQ (SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, "domain_search", TRUE ), + REQ (SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, "ms_classless_static_routes", TRUE ), + REQ (SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, "wpad", TRUE ), + REQ (SD_DHCP_OPTION_ROOT_PATH, "root_path", TRUE ), /* Internal values */ - { SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, REQPREFIX "expiry", FALSE }, - { SD_DHCP_OPTION_CLIENT_IDENTIFIER, REQPREFIX "dhcp_client_identifier", FALSE }, - { DHCP_OPTION_IP_ADDRESS, REQPREFIX "ip_address", FALSE }, - { 0, NULL, FALSE } + REQ (SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, "expiry", FALSE ), + REQ (SD_DHCP_OPTION_CLIENT_IDENTIFIER, "dhcp_client_identifier", FALSE ), + REQ (DHCP_OPTION_IP_ADDRESS, "ip_address", FALSE ), + + { 0 } }; static const ReqOption dhcp6_requests[] = { - { SD_DHCP6_OPTION_CLIENTID, REQPREFIX "dhcp6_client_id", TRUE }, + REQ (SD_DHCP6_OPTION_CLIENTID, "dhcp6_client_id", FALSE ), /* Don't request server ID by default; some servers don't reply to * Information Requests that request the Server ID. */ - { SD_DHCP6_OPTION_SERVERID, REQPREFIX "dhcp6_server_id", FALSE }, + REQ (SD_DHCP6_OPTION_SERVERID, "dhcp6_server_id", FALSE ), - { SD_DHCP6_OPTION_DNS_SERVERS, REQPREFIX "dhcp6_name_servers", TRUE }, - { SD_DHCP6_OPTION_DOMAIN_LIST, REQPREFIX "dhcp6_domain_search", TRUE }, - { SD_DHCP6_OPTION_SNTP_SERVERS, REQPREFIX "dhcp6_sntp_servers", TRUE }, + REQ (SD_DHCP6_OPTION_DNS_SERVERS, "dhcp6_name_servers", TRUE ), + REQ (SD_DHCP6_OPTION_DOMAIN_LIST, "dhcp6_domain_search", TRUE ), + REQ (SD_DHCP6_OPTION_SNTP_SERVERS, "dhcp6_sntp_servers", TRUE ), /* Internal values */ - { DHCP6_OPTION_IP_ADDRESS, REQPREFIX "ip6_address", FALSE }, - { DHCP6_OPTION_PREFIXLEN, REQPREFIX "ip6_prefixlen", FALSE }, - { DHCP6_OPTION_PREFERRED_LIFE, REQPREFIX "preferred_life", FALSE }, - { DHCP6_OPTION_MAX_LIFE, REQPREFIX "max_life", FALSE }, - { DHCP6_OPTION_STARTS, REQPREFIX "starts", FALSE }, - { DHCP6_OPTION_LIFE_STARTS, REQPREFIX "life_starts", FALSE }, - { DHCP6_OPTION_RENEW, REQPREFIX "renew", FALSE }, - { DHCP6_OPTION_REBIND, REQPREFIX "rebind", FALSE }, - { DHCP6_OPTION_IAID, REQPREFIX "iaid", FALSE }, - { 0, NULL, FALSE } + REQ (DHCP6_OPTION_IP_ADDRESS, "ip6_address", FALSE ), + REQ (DHCP6_OPTION_PREFIXLEN, "ip6_prefixlen", FALSE ), + REQ (DHCP6_OPTION_PREFERRED_LIFE, "preferred_life", FALSE ), + REQ (DHCP6_OPTION_MAX_LIFE, "max_life", FALSE ), + REQ (DHCP6_OPTION_STARTS, "starts", FALSE ), + REQ (DHCP6_OPTION_LIFE_STARTS, "life_starts", FALSE ), + REQ (DHCP6_OPTION_RENEW, "renew", FALSE ), + REQ (DHCP6_OPTION_REBIND, "rebind", FALSE ), + REQ (DHCP6_OPTION_IAID, "iaid", FALSE ), + + { 0 } }; static void @@ -164,18 +179,22 @@ take_option (GHashTable *options, { guint i; - g_return_if_fail (value != NULL); + nm_assert (options); + nm_assert (requests); + nm_assert (value); for (i = 0; requests[i].name; i++) { - if (requests[i].num == option) { + nm_assert (g_str_has_prefix (requests[i].name, REQPREFIX)); + if (requests[i].option_num == option) { g_hash_table_insert (options, (gpointer) (requests[i].name + NM_STRLEN (REQPREFIX)), value); - break; + return; } } + /* Option should always be found */ - g_assert (requests[i].name); + nm_assert_not_reached (); } static void @@ -186,13 +205,6 @@ add_option (GHashTable *options, const ReqOption *requests, guint option, const } static void -add_option_u32 (GHashTable *options, const ReqOption *requests, guint option, guint32 value) -{ - if (options) - take_option (options, requests, option, g_strdup_printf ("%u", value)); -} - -static void add_option_u64 (GHashTable *options, const ReqOption *requests, guint option, guint64 value) { if (options) @@ -204,12 +216,21 @@ add_requests_to_options (GHashTable *options, const ReqOption *requests) { guint i; - for (i = 0; options && requests[i].name; i++) { + if (!options) + return; + + for (i = 0; requests[i].name; i++) { if (requests[i].include) g_hash_table_insert (options, (gpointer) requests[i].name, g_strdup ("1")); } } +static GHashTable * +create_options_dict (void) +{ + return g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); +} + #define LOG_LEASE(domain, ...) \ G_STMT_START { \ if (log_lease) { \ @@ -222,76 +243,91 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, sd_dhcp_lease *lease, - GHashTable *options, guint32 route_table, guint32 route_metric, gboolean log_lease, + GHashTable **out_options, GError **error) { - NMIP4Config *ip4_config = NULL; - struct in_addr tmp_addr; + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; const struct in_addr *addr_list; - char buf[INET_ADDRSTRLEN]; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; const char *s; - guint32 lifetime = 0, i; - NMPlatformIP4Address address; nm_auto_free_gstring GString *str = NULL; gs_free sd_dhcp_route **routes = NULL; const char *const*search_domains = NULL; guint16 mtu; - int r, num; - guint64 end_time; + int i, num; const void *data; gsize data_len; gboolean metered = FALSE; - gboolean static_default_gateway = FALSE; - gboolean gateway_has = FALSE; - in_addr_t gateway = 0; + gboolean has_router_from_classless = FALSE; + gboolean has_classless_route = FALSE; + gboolean has_static_route = FALSE; + const gint32 ts = nm_utils_get_monotonic_timestamp_s (); + gint64 ts_time = time (NULL); + struct in_addr a_address; + struct in_addr a_netmask; + struct in_addr a_router; + guint32 a_plen; + guint32 a_lifetime; g_return_val_if_fail (lease != NULL, NULL); ip4_config = nm_ip4_config_new (multi_idx, ifindex); - /* Address */ - sd_dhcp_lease_get_address (lease, &tmp_addr); - memset (&address, 0, sizeof (address)); - address.address = tmp_addr.s_addr; - address.peer_address = tmp_addr.s_addr; - s = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, "address %s", s); - add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, s); - - /* Prefix/netmask */ - sd_dhcp_lease_get_netmask (lease, &tmp_addr); - address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); - LOG_LEASE (LOGD_DHCP4, "plen %d", address.plen); + options = out_options ? create_options_dict () : NULL; + + if (sd_dhcp_lease_get_address (lease, &a_address) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get address from lease"); + return NULL; + } + nm_utils_inet4_ntop (a_address.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "address %s", addr_str); + add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, addr_str); + + if (sd_dhcp_lease_get_netmask (lease, &a_netmask) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get netmask from lease"); + return NULL; + } + a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr); + LOG_LEASE (LOGD_DHCP4, "plen %u", (guint) a_plen); add_option (options, dhcp4_requests, SD_DHCP_OPTION_SUBNET_MASK, - nm_utils_inet4_ntop (tmp_addr.s_addr, NULL)); - - /* Lease time */ - sd_dhcp_lease_get_lifetime (lease, &lifetime); - address.timestamp = nm_utils_get_monotonic_timestamp_s (); - address.lifetime = address.preferred = lifetime; - end_time = (guint64) time (NULL) + lifetime; - LOG_LEASE (LOGD_DHCP4, "expires in %" G_GUINT32_FORMAT " seconds", lifetime); + nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); + + if (sd_dhcp_lease_get_lifetime (lease, &a_lifetime) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get lifetime from lease"); + return NULL; + } + LOG_LEASE (LOGD_DHCP4, "expires in %u seconds (at %lld)", + (guint) a_lifetime, + (long long) (ts_time + a_lifetime)); add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, - end_time); - - address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - nm_ip4_config_add_address (ip4_config, &address); + (guint64) (ts_time + a_lifetime)); + + nm_ip4_config_add_address (ip4_config, + &((const NMPlatformIP4Address) { + .address = a_address.s_addr, + .peer_address = a_address.s_addr, + .plen = a_plen, + .addr_source = NM_IP_CONFIG_SOURCE_DHCP, + .timestamp = ts, + .lifetime = a_lifetime, + .preferred = a_lifetime, + })); - /* DNS Servers */ num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { nm_gstring_prepare (&str); for (i = 0; i < num; i++) { if (addr_list[i].s_addr) { nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); - s = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL); + s = nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str); LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", s); g_string_append_printf (str, "%s%s", str->len ? " " : "", s); } @@ -300,7 +336,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, str->str); } - /* Search domains */ num = sd_dhcp_lease_get_search_domains (lease, (char ***) &search_domains); if (num > 0) { nm_gstring_prepare (&str); @@ -312,141 +347,191 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, str->str); } - /* Domain Name */ - r = sd_dhcp_lease_get_domainname (lease, &s); - if (r == 0) { - /* Multiple domains sometimes stuffed into option 15 "Domain Name". - * As systemd escapes such characters, split them at \\032. */ - char **domains = g_strsplit (s, "\\032", 0); + if ( sd_dhcp_lease_get_domainname (lease, &s) >= 0 + && s) { + gs_strfreev char **domains = NULL; char **d; + /* Multiple domains sometimes stuffed into option 15 "Domain Name". + * As systemd escapes such characters, split them at \\032. */ + domains = g_strsplit (s, "\\032", 0); for (d = domains; *d; d++) { LOG_LEASE (LOGD_DHCP4, "domain name '%s'", *d); nm_ip4_config_add_domain (ip4_config, *d); } - g_strfreev (domains); add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, s); } - /* Hostname */ - r = sd_dhcp_lease_get_hostname (lease, &s); - if (r == 0) { + if (sd_dhcp_lease_get_hostname (lease, &s) >= 0) { LOG_LEASE (LOGD_DHCP4, "hostname '%s'", s); add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, s); } - /* Routes */ num = sd_dhcp_lease_get_routes (lease, &routes); if (num > 0) { - nm_gstring_prepare (&str); + nm_auto_free_gstring GString *str_classless = NULL; + nm_auto_free_gstring GString *str_static = NULL; + guint32 default_route_metric = route_metric; + for (i = 0; i < num; i++) { - NMPlatformIP4Route route = { 0 }; - const char *gw_str; - guint8 plen; - struct in_addr a; + switch (sd_dhcp_route_get_option (routes[i])) { + case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + has_classless_route = TRUE; + break; + case SD_DHCP_OPTION_STATIC_ROUTE: + has_static_route = TRUE; + break; + } + } + + if (has_classless_route) + str_classless = g_string_sized_new (30); + if (has_static_route) + str_static = g_string_sized_new (30); - if (sd_dhcp_route_get_destination (routes[i], &a) < 0) + for (i = 0; i < num; i++) { + char network_net_str[NM_UTILS_INET_ADDRSTRLEN]; + char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; + guint8 r_plen; + struct in_addr r_network; + struct in_addr r_gateway; + in_addr_t network_net; + int option; + guint32 m; + + option = sd_dhcp_route_get_option (routes[i]); + if (!NM_IN_SET (option, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, + SD_DHCP_OPTION_STATIC_ROUTE)) continue; - if ( sd_dhcp_route_get_destination_prefix_length (routes[i], &plen) < 0 - || plen > 32) + if (sd_dhcp_route_get_destination (routes[i], &r_network) < 0) + continue; + if ( sd_dhcp_route_get_destination_prefix_length (routes[i], &r_plen) < 0 + || r_plen > 32) + continue; + if (sd_dhcp_route_get_gateway (routes[i], &r_gateway) < 0) continue; - route.plen = plen; - route.network = nm_utils_ip4_address_clear_host_address (a.s_addr, plen); + network_net = nm_utils_ip4_address_clear_host_address (r_network.s_addr, + r_plen); + nm_utils_inet4_ntop (network_net, network_net_str); + nm_utils_inet4_ntop (r_gateway.s_addr, gateway_str); + + LOG_LEASE (LOGD_DHCP4, + "%sstatic route %s/%d gw %s", + option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE + ? "classless " + : "", + network_net_str, + (int) r_plen, + gateway_str); + g_string_append_printf (nm_gstring_add_space_delimiter ( option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE + ? str_classless + : str_static), + "%s/%d %s", + network_net_str, + (int) r_plen, + gateway_str); + + if ( option == SD_DHCP_OPTION_STATIC_ROUTE + && has_classless_route) { + /* RFC 3443: if the DHCP server returns both a Classless Static Routes + * option and a Static Routes option, the DHCP client MUST ignore the + * Static Routes option. */ + continue; + } - if (sd_dhcp_route_get_gateway (routes[i], &a) < 0) + if ( r_plen == 0 + && option == SD_DHCP_OPTION_STATIC_ROUTE) { + /* for option 33 (static route), RFC 2132 says: + * + * The default route (0.0.0.0) is an illegal destination for a static + * route. */ continue; - route.gateway = a.s_addr; - - if (route.plen) { - route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = route_metric; - route.table_coerced = nm_platform_route_table_coerce (route_table); - nm_ip4_config_add_route (ip4_config, &route, NULL); - - s = nm_utils_inet4_ntop (route.network, buf); - gw_str = nm_utils_inet4_ntop (route.gateway, NULL); - LOG_LEASE (LOGD_DHCP4, "static route %s/%d gw %s", s, route.plen, gw_str); - - g_string_append_printf (str, "%s%s/%d %s", str->len ? " " : "", s, route.plen, gw_str); - } else { - if (!static_default_gateway) { - static_default_gateway = TRUE; - gateway_has = TRUE; - gateway = route.gateway; - - s = nm_utils_inet4_ntop (route.gateway, NULL); - LOG_LEASE (LOGD_DHCP4, "gateway %s", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); - } } - } - if (str->len) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str->str); - } - /* If the DHCP server returns both a Classless Static Routes option and a - * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. - * Be more lenient and ignore the Router option only if Classless Static - * Routes contain a default gateway (as other DHCP backends do). - */ - /* Gateway */ - if (!static_default_gateway) { - r = sd_dhcp_lease_get_router (lease, &tmp_addr); - if (r == 0) { - gateway_has = TRUE; - gateway = tmp_addr.s_addr; - s = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, "gateway %s", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); + if (r_plen == 0) { + /* if there are multiple default routes, we add them with differing + * metrics. */ + m = default_route_metric; + if (default_route_metric < G_MAXUINT32) + default_route_metric++; + + has_router_from_classless = TRUE; + } else + m = route_metric; + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .network = network_net, + .plen = r_plen, + .gateway = r_gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric = m, + .table_coerced = nm_platform_route_table_coerce (route_table), + }), + NULL); } - } - if (gateway_has) { - const NMPlatformIP4Route rt = { - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = gateway, - .table_coerced = nm_platform_route_table_coerce (route_table), - .metric = route_metric, - }; + if (str_classless && str_classless->len > 0) + add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str_classless->str); + if (str_static && str_static->len > 0) + add_option (options, dhcp4_requests, SD_DHCP_OPTION_STATIC_ROUTE, str_static->str); + } - nm_ip4_config_add_route (ip4_config, &rt, NULL); + /* FIXME: internal client only supports returing the first router. */ + if (sd_dhcp_lease_get_router (lease, &a_router) >= 0) { + s = nm_utils_inet4_ntop (a_router.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "gateway %s", s); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); + + /* If the DHCP server returns both a Classless Static Routes option and a + * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. + * + * Be more lenient and ignore the Router option only if Classless Static + * Routes contain a default gateway (as other DHCP backends do). + */ + if (!has_router_from_classless) { + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = a_router.s_addr, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }), + NULL); + } } - /* MTU */ - r = sd_dhcp_lease_get_mtu (lease, &mtu); - if (r == 0 && mtu) { + if ( sd_dhcp_lease_get_mtu (lease, &mtu) >= 0 + && mtu) { nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); - add_option_u32 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); + add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); } - /* NTP servers */ num = sd_dhcp_lease_get_ntp (lease, &addr_list); if (num > 0) { nm_gstring_prepare (&str); for (i = 0; i < num; i++) { - s = nm_utils_inet4_ntop (addr_list[i].s_addr, buf); + s = nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str); LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", s); g_string_append_printf (str, "%s%s", str->len ? " " : "", s); } add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, str->str); } - /* Root path */ - r = sd_dhcp_lease_get_root_path (lease, &s); - if (r >= 0) { + if (sd_dhcp_lease_get_root_path (lease, &s) >= 0) { LOG_LEASE (LOGD_DHCP4, "root path '%s'", s); add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROOT_PATH, s); } - r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len); - if (r >= 0) + if (sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len) >= 0) metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED")); nm_ip4_config_set_metered (ip4_config, metered); - return ip4_config; + NM_SET_OUT (out_options, g_steal_pointer (&options)); + return g_steal_pointer (&ip4_config); } /*****************************************************************************/ @@ -483,34 +568,17 @@ get_leasefile_path (int addr_family, const char *iface, const char *uuid) /*****************************************************************************/ static void -_save_client_id (NMDhcpSystemd *self, - uint8_t type, - const uint8_t *client_id, - size_t len) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (client_id != NULL); - g_return_if_fail (len > 0); - - if (!nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self))) { - nm_dhcp_client_set_client_id_bin (NM_DHCP_CLIENT (self), - type, client_id, len); - } -} - -static void bound4_handle (NMDhcpSystemd *self) { NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); sd_dhcp_lease *lease; - NMIP4Config *ip4_config; - GHashTable *options; + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; GError *error = NULL; - int r; - r = sd_dhcp_client_get_lease (priv->client4, &lease); - if (r < 0 || !lease) { + if ( sd_dhcp_client_get_lease (priv->client4, &lease) < 0 + || !lease) { _LOGW ("no lease!"); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); return; @@ -518,40 +586,29 @@ bound4_handle (NMDhcpSystemd *self) _LOGD ("lease available"); - options = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); ip4_config = lease_to_ip4_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, - options, nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)), nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)), TRUE, + &options, &error); - if (ip4_config) { - const uint8_t *client_id = NULL; - size_t client_id_len = 0; - uint8_t type = 0; - - add_requests_to_options (options, dhcp4_requests); - dhcp_lease_save (lease, priv->lease_file); - - sd_dhcp_client_get_client_id (priv->client4, &type, &client_id, &client_id_len); - if (client_id) - _save_client_id (self, type, client_id, client_id_len); - - nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), - NM_DHCP_STATE_BOUND, - NM_IP_CONFIG_CAST (ip4_config), - options); - } else { + if (!ip4_config) { _LOGW ("%s", error->message); - nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); g_clear_error (&error); + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + return; } - g_hash_table_destroy (options); - g_clear_object (&ip4_config); + add_requests_to_options (options, dhcp4_requests); + dhcp_lease_save (lease, priv->lease_file); + + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), + NM_DHCP_STATE_BOUND, + NM_IP_CONFIG_CAST (ip4_config), + options); } static void @@ -582,127 +639,124 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) } } -static guint16 -get_arp_type (GBytes *hwaddr) -{ - switch (g_bytes_get_size (hwaddr)) { - case ETH_ALEN: - return ARPHRD_ETHER; - case INFINIBAND_ALEN: - return ARPHRD_INFINIBAND; - default: - return ARPHRD_NONE; - } -} - static gboolean ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address, GError **error) { + nm_auto (sd_dhcp_client_unrefp) sd_dhcp_client *sd_client = NULL; NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - const char *iface = nm_dhcp_client_get_iface (client); + gs_free char *lease_file = NULL; GBytes *hwaddr; - sd_dhcp_lease *lease = NULL; - GBytes *override_client_id; - const uint8_t *client_id = NULL; - size_t client_id_len = 0; + const uint8_t *hwaddr_arr; + gsize hwaddr_len; + int arp_type; + GBytes *client_id; + gs_unref_bytes GBytes *client_id_new = NULL; + const uint8_t *client_id_arr; + size_t client_id_len; struct in_addr last_addr = { 0 }; const char *hostname; int r, i; - gboolean success = FALSE; - g_assert (priv->client4 == NULL); - g_assert (priv->client6 == NULL); + g_return_val_if_fail (!priv->client4, FALSE); + g_return_val_if_fail (!priv->client6, FALSE); - g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (AF_INET, iface, nm_dhcp_client_get_uuid (client)); - - r = sd_dhcp_client_new (&priv->client4, FALSE); + r = sd_dhcp_client_new (&sd_client, FALSE); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to create dhcp-client: %s"); return FALSE; } - _LOGT ("dhcp-client4: set %p", priv->client4); + _LOGT ("dhcp-client4: set %p", sd_client); - r = sd_dhcp_client_attach_event (priv->client4, NULL, 0); + r = sd_dhcp_client_attach_event (sd_client, NULL, 0); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to attach event: %s"); - goto errout; + return FALSE; } hwaddr = nm_dhcp_client_get_hw_addr (client); - if (hwaddr) { - const uint8_t *data; - gsize len; - - data = g_bytes_get_data (hwaddr, &len); - r = sd_dhcp_client_set_mac (priv->client4, - data, - len, - get_arp_type (hwaddr)); - if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); - goto errout; - } + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; } - - r = sd_dhcp_client_set_ifindex (priv->client4, nm_dhcp_client_get_ifindex (client)); + r = sd_dhcp_client_set_mac (sd_client, + hwaddr_arr, + hwaddr_len, + (guint16) arp_type); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); + return FALSE; } - r = sd_dhcp_client_set_callback (priv->client4, dhcp_event_cb, client); + r = sd_dhcp_client_set_ifindex (sd_client, + nm_dhcp_client_get_ifindex (client)); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set callback: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); + return FALSE; } - dhcp_lease_load (&lease, priv->lease_file); + lease_file = get_leasefile_path (AF_INET, + nm_dhcp_client_get_iface (client), + nm_dhcp_client_get_uuid (client)); if (last_ip4_address) inet_pton (AF_INET, last_ip4_address, &last_addr); - else if (lease) - sd_dhcp_lease_get_address (lease, &last_addr); + else { + nm_auto (sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; + + dhcp_lease_load (&lease, lease_file); + if (lease) + sd_dhcp_lease_get_address (lease, &last_addr); + } if (last_addr.s_addr) { - r = sd_dhcp_client_set_request_address (priv->client4, &last_addr); + r = sd_dhcp_client_set_request_address (sd_client, &last_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set last IPv4 address: %s"); - goto errout; + return FALSE; } } - override_client_id = nm_dhcp_client_get_client_id (client); - if (override_client_id) { - client_id = g_bytes_get_data (override_client_id, &client_id_len); - nm_assert (client_id && client_id_len >= 2); - sd_dhcp_client_set_client_id (priv->client4, - client_id[0], - client_id + 1, - NM_MIN (client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN)); - } else if (lease) { - r = sd_dhcp_lease_get_client_id (lease, (const void **) &client_id, &client_id_len); - if (r == 0 && client_id_len >= 2) { - sd_dhcp_client_set_client_id (priv->client4, - client_id[0], - client_id + 1, - client_id_len - 1); - _save_client_id (NM_DHCP_SYSTEMD (client), - client_id[0], - client_id + 1, - client_id_len - 1); - } + client_id = nm_dhcp_client_get_client_id (client); + if (!client_id) { + client_id_new = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_arr, hwaddr_len); + client_id = client_id_new; + } + + if ( !(client_id_arr = g_bytes_get_data (client_id, &client_id_len)) + || client_id_len < 2) { + + /* invalid client-ids are not expected. */ + nm_assert_not_reached (); + + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "no valid IPv4 client-id"); + return FALSE; + } + + /* Note that we always set a client-id. In particular for infiniband that is necessary, + * see https://tools.ietf.org/html/rfc4390#section-2.1 . */ + r = sd_dhcp_client_set_client_id (sd_client, + client_id_arr[0], + client_id_arr + 1, + NM_MIN (client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN)); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set IPv4 client-id: %s"); + return FALSE; } /* Add requested options */ for (i = 0; dhcp4_requests[i].name; i++) { - if (dhcp4_requests[i].include) - sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num); + if (dhcp4_requests[i].include) { + nm_assert (dhcp4_requests[i].option_num <= 255); + r = sd_dhcp_client_set_request_option (sd_client, dhcp4_requests[i].option_num); + nm_assert (r >= 0 || r == -EEXIST); + } } hostname = nm_dhcp_client_get_hostname (client); @@ -711,28 +765,36 @@ ip4_start (NMDhcpClient *client, * only based on whether the hostname has a domain part or not. At the * moment there is no way to force one or another. */ - r = sd_dhcp_client_set_hostname (priv->client4, hostname); + r = sd_dhcp_client_set_hostname (sd_client, hostname); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); - goto errout; + return FALSE; } } + r = sd_dhcp_client_set_callback (sd_client, dhcp_event_cb, client); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set callback: %s"); + return FALSE; + } + + priv->client4 = g_steal_pointer (&sd_client); + + g_free (priv->lease_file); + priv->lease_file = g_steal_pointer (&lease_file); + + nm_dhcp_client_set_client_id (client, client_id); + r = sd_dhcp_client_start (priv->client4); if (r < 0) { + sd_dhcp_client_set_callback (priv->client4, NULL, NULL); + nm_clear_pointer (&priv->client4, sd_dhcp_client_unref); nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s"); - goto errout; + return FALSE; } nm_dhcp_client_start_timeout (client); - - success = TRUE; - -errout: - sd_dhcp_lease_unref (lease); - if (!success) - sd_dhcp_client_unref (g_steal_pointer (&priv->client4)); - return success; + return TRUE; } static NMIP6Config * @@ -740,41 +802,45 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, sd_dhcp6_lease *lease, - GHashTable *options, gboolean log_lease, gboolean info_only, + GHashTable **out_options, GError **error) { + gs_unref_object NMIP6Config *ip6_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; struct in6_addr tmp_addr, *dns; uint32_t lft_pref, lft_valid; - NMIP6Config *ip6_config; - const char *addr_str; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; char **domains; nm_auto_free_gstring GString *str = NULL; int num, i; - gint32 ts; + const gint32 ts = nm_utils_get_monotonic_timestamp_s (); g_return_val_if_fail (lease, NULL); + ip6_config = nm_ip6_config_new (multi_idx, ifindex); - ts = nm_utils_get_monotonic_timestamp_s (); - /* Addresses */ + options = out_options ? create_options_dict () : NULL; + sd_dhcp6_lease_reset_address_iter (lease); nm_gstring_prepare (&str); while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) { - NMPlatformIP6Address address = { - .plen = 128, - .address = tmp_addr, - .timestamp = ts, - .lifetime = lft_valid, - .preferred = lft_pref, + const NMPlatformIP6Address address = { + .plen = 128, + .address = tmp_addr, + .timestamp = ts, + .lifetime = lft_valid, + .preferred = lft_pref, .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; nm_ip6_config_add_address (ip6_config, &address); - addr_str = nm_utils_inet6_ntop (&tmp_addr, NULL); - g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); + nm_utils_inet6_ntop (&tmp_addr, addr_str); + if (str->len) + g_string_append_c (str, ' '); + g_string_append (str, addr_str); LOG_LEASE (LOGD_DHCP6, "address %s", @@ -784,8 +850,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, if (str->len) add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); - if (!info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { - g_object_unref (ip6_config); + if ( !info_only + && nm_ip6_config_get_num_addresses (ip6_config) == 0) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, @@ -793,20 +859,20 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, return NULL; } - /* DNS servers */ num = sd_dhcp6_lease_get_dns (lease, &dns); if (num > 0) { nm_gstring_prepare (&str); for (i = 0; i < num; i++) { nm_ip6_config_add_nameserver (ip6_config, &dns[i]); - addr_str = nm_utils_inet6_ntop (&dns[i], NULL); - g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); + nm_utils_inet6_ntop (&dns[i], addr_str); + if (str->len) + g_string_append_c (str, ' '); + g_string_append (str, addr_str); LOG_LEASE (LOGD_DHCP6, "nameserver %s", addr_str); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); } - /* Search domains */ num = sd_dhcp6_lease_get_domains (lease, &domains); if (num > 0) { nm_gstring_prepare (&str); @@ -818,7 +884,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); } - return ip6_config; + NM_SET_OUT (out_options, g_steal_pointer (&options)); + return g_steal_pointer (&ip6_config); } static void @@ -830,10 +897,9 @@ bound6_handle (NMDhcpSystemd *self) gs_unref_hashtable GHashTable *options = NULL; gs_free_error GError *error = NULL; sd_dhcp6_lease *lease; - int r; - r = sd_dhcp6_client_get_lease (priv->client6, &lease); - if (r < 0 || !lease) { + if ( sd_dhcp6_client_get_lease (priv->client6, &lease) < 0 + || !lease) { _LOGW (" no lease!"); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); return; @@ -841,25 +907,25 @@ bound6_handle (NMDhcpSystemd *self) _LOGD ("lease available"); - options = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); ip6_config = lease_to_ip6_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, - options, TRUE, nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)), + &options, &error); - if (ip6_config) { - nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), - NM_DHCP_STATE_BOUND, - NM_IP_CONFIG_CAST (ip6_config), - options); - } else { + if (!ip6_config) { _LOGW ("%s", error->message); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + return; } + + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), + NM_DHCP_STATE_BOUND, + NM_IP_CONFIG_CAST (ip6_config), + options); } static void @@ -895,31 +961,34 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - const char *iface = nm_dhcp_client_get_iface (client); + nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL; GBytes *hwaddr; const char *hostname; + const char *iface; int r, i; const guint8 *duid_arr; gsize duid_len; - - g_assert (priv->client4 == NULL); - g_assert (priv->client6 == NULL); - g_return_val_if_fail (duid != NULL, FALSE); - - duid_arr = g_bytes_get_data (duid, &duid_len); - if (!duid_arr || duid_len < 2) + GBytes *duid; + const uint8_t *hwaddr_arr; + gsize hwaddr_len; + int arp_type; + + g_return_val_if_fail (!priv->client4, FALSE); + g_return_val_if_fail (!priv->client6, FALSE); + + if ( !(duid = nm_dhcp_client_get_client_id (client)) + || !(duid_arr = g_bytes_get_data (duid, &duid_len)) + || duid_len < 2) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "missing DUID"); g_return_val_if_reached (FALSE); + } - g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (AF_INET6, iface, nm_dhcp_client_get_uuid (client)); - - r = sd_dhcp6_client_new (&priv->client6); + r = sd_dhcp6_client_new (&sd_client); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to create dhcp-client: %s"); return FALSE; @@ -930,12 +999,23 @@ ip6_start (NMDhcpClient *client, needed_prefixes); } - _LOGT ("dhcp-client6: set %p", priv->client6); + _LOGT ("dhcp-client6: set %p", sd_client); if (nm_dhcp_client_get_info_only (client)) - sd_dhcp6_client_set_information_request (priv->client6, 1); + sd_dhcp6_client_set_information_request (sd_client, 1); + + iface = nm_dhcp_client_get_iface (client); - r = sd_dhcp6_client_set_duid (priv->client6, + r = sd_dhcp6_client_set_iaid (sd_client, + nm_utils_create_dhcp_iaid (TRUE, + (const guint8 *) iface, + strlen (iface))); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set IAID: %s"); + return FALSE; + } + + r = sd_dhcp6_client_set_duid (sd_client, unaligned_read_be16 (&duid_arr[0]), &duid_arr[2], duid_len - 2); @@ -944,82 +1024,84 @@ ip6_start (NMDhcpClient *client, return FALSE; } - r = sd_dhcp6_client_attach_event (priv->client6, NULL, 0); + r = sd_dhcp6_client_attach_event (sd_client, NULL, 0); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to attach event: %s"); - goto errout; + return FALSE; } hwaddr = nm_dhcp_client_get_hw_addr (client); - if (hwaddr) { - const uint8_t *data; - gsize len; - - data = g_bytes_get_data (hwaddr, &len); - r = sd_dhcp6_client_set_mac (priv->client6, - data, - len, - get_arp_type (hwaddr)); - if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); - goto errout; - } + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; } - - r = sd_dhcp6_client_set_ifindex (priv->client6, nm_dhcp_client_get_ifindex (client)); + r = sd_dhcp6_client_set_mac (sd_client, + hwaddr_arr, + hwaddr_len, + (guint16) arp_type); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); + return FALSE; } - r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client); + r = sd_dhcp6_client_set_ifindex (sd_client, + nm_dhcp_client_get_ifindex (client)); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set callback: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); + return FALSE; } /* Add requested options */ for (i = 0; dhcp6_requests[i].name; i++) { - if (dhcp6_requests[i].include) - sd_dhcp6_client_set_request_option (priv->client6, dhcp6_requests[i].num); + if (dhcp6_requests[i].include) { + r = sd_dhcp6_client_set_request_option (sd_client, dhcp6_requests[i].option_num); + nm_assert (r >= 0 || r == -EEXIST); + } } - r = sd_dhcp6_client_set_local_address (priv->client6, ll_addr); + r = sd_dhcp6_client_set_local_address (sd_client, ll_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set local address: %s"); - goto errout; + return FALSE; } hostname = nm_dhcp_client_get_hostname (client); - r = sd_dhcp6_client_set_fqdn (priv->client6, hostname); + r = sd_dhcp6_client_set_fqdn (sd_client, hostname); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); - goto errout; + return FALSE; + } + + r = sd_dhcp6_client_set_callback (sd_client, dhcp6_event_cb, client); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set callback: %s"); + return FALSE; } + priv->client6 = g_steal_pointer (&sd_client); + r = sd_dhcp6_client_start (priv->client6); if (r < 0) { + sd_dhcp6_client_set_callback (priv->client6, NULL, NULL); + nm_clear_pointer (&priv->client6, sd_dhcp6_client_unref); nm_utils_error_set_errno (error, r, "failed to start client: %s"); - goto errout; + return FALSE; } nm_dhcp_client_start_timeout (client); - return TRUE; - -errout: - sd_dhcp6_client_unref (g_steal_pointer (&priv->client6)); - return FALSE; } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); int r = 0; - NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release); _LOGT ("dhcp-client%d: stop %p", priv->client4 ? '4' : '6', diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 6bbc670b..768f9fd7 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -197,7 +197,8 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, /* gateway passed as classless static route */ *gwaddr = route.gateway; } else { - char addr[INET_ADDRSTRLEN]; + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; /* normal route */ route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; @@ -206,8 +207,9 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, nm_ip4_config_add_route (ip4_config, &route, NULL); _LOG2I (LOGD_DHCP4, iface, " classless static route %s/%d gw %s", - nm_utils_inet4_ntop (route.network, addr), route.plen, - nm_utils_inet4_ntop (route.gateway, NULL)); + nm_utils_inet4_ntop (route.network, b1), + route.plen, + nm_utils_inet4_ntop (route.gateway, b2)); } } @@ -408,6 +410,7 @@ nm_dhcp_utils_ip4_config_from_options (NMDedupMultiIndex *multi_idx, gboolean gateway_has = FALSE; guint32 gateway = 0; guint8 plen = 0; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; g_return_val_if_fail (options != NULL, NULL); @@ -439,7 +442,7 @@ nm_dhcp_utils_ip4_config_from_options (NMDedupMultiIndex *multi_idx, process_classful_routes (iface, options, route_table, route_metric, ip4_config); if (gateway) { - _LOG2I (LOGD_DHCP4, iface, " gateway %s", nm_utils_inet4_ntop (gateway, NULL)); + _LOG2I (LOGD_DHCP4, iface, " gateway %s", nm_utils_inet4_ntop (gateway, sbuf)); gateway_has = TRUE; } else { /* If the gateway wasn't provided as a classless static route with a @@ -726,10 +729,10 @@ nm_dhcp_utils_duid_to_string (GBytes *duid) gconstpointer data; gsize len; - g_return_val_if_fail (duid != NULL, NULL); + g_return_val_if_fail (duid, NULL); data = g_bytes_get_data (duid, &len); - return _nm_utils_bin2str (data, len, FALSE); + return _nm_utils_bin2hexstr_full (data, len, ':', FALSE, NULL); } /** diff --git a/src/dhcp/tests/meson.build b/src/dhcp/tests/meson.build index 0fee26b2..d2de4dc4 100644 --- a/src/dhcp/tests/meson.build +++ b/src/dhcp/tests/meson.build @@ -1,6 +1,6 @@ test_units = [ 'test-dhcp-dhclient', - 'test-dhcp-utils' + 'test-dhcp-utils', ] foreach test_unit: test_units @@ -13,6 +13,6 @@ foreach test_unit: test_units test( 'dhcp/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) endforeach diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index edac4257..ab1f5551 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -760,62 +760,74 @@ test_read_commented_duid_from_leasefile (void) g_assert (duid == NULL); } +/*****************************************************************************/ + +static void +_save_duid (const char *path, + const guint8 *duid_bin, + gsize duid_len) +{ + gs_unref_bytes GBytes *duid = NULL; + GError *error = NULL; + gboolean success; + + g_assert (path); + g_assert (duid_bin); + g_assert (duid_len > 0); + + duid = g_bytes_new (duid_bin, duid_len); + success = nm_dhcp_dhclient_save_duid (path, duid, &error); + nmtst_assert_success (success, error); +} + static void test_write_duid (void) { - const char *duid = "\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254"; + const guint8 duid[] = { 000, 001, 000, 001, 027, 'X', 0350, 'X', 0, '#', 025, 010, '~', 0254 }; const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-duid.leases"; - success = nm_dhcp_dhclient_save_duid (path, duid, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, duid, G_N_ELEMENTS (duid)); success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); - g_assert_cmpstr (expected_contents, ==, contents); - g_free (contents); + g_assert_cmpstr (expected_contents, ==, contents); } static void test_write_existing_duid (void) { - const char *duid = "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302"; + const guint8 duid[] = { 000, 001, 000, 001, 023, 'o', 023, 'n', 000, '\"', 0372, 0214, 0326, 0302 }; const char *original_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302\";\n"; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-duid.leases"; success = g_file_set_contents (path, original_contents, -1, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); /* Save other DUID; should be overwritten */ - success = nm_dhcp_dhclient_save_duid (path, duid, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, duid, G_N_ELEMENTS (duid)); /* reread original contents */ success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); g_assert_cmpstr (expected_contents, ==, contents); - - g_free (contents); } +static const guint8 DUID_BIN[] = { 000, 001, 000, 001, 023, 'o', 023, 'n', 000, '\"', 0372, 0214, 0326, 0302 }; #define DUID "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302" + static void test_write_existing_commented_duid (void) { @@ -824,28 +836,22 @@ test_write_existing_commented_duid (void) "default-duid \"" DUID "\";\n" ORIG_CONTENTS; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-commented-duid.leases"; success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); /* Save other DUID; should be saved on top */ - success = nm_dhcp_dhclient_save_duid (path, DUID, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, DUID_BIN, G_N_ELEMENTS (DUID_BIN)); /* reread original contents */ success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); g_assert_cmpstr (expected_contents, ==, contents); - - g_free (contents); #undef ORIG_CONTENTS } @@ -865,8 +871,7 @@ test_write_existing_multiline_duid (void) success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); nmtst_assert_success (success, error); - success = nm_dhcp_dhclient_save_duid (path, DUID, &error); - nmtst_assert_success (success, error); + _save_duid (path, DUID_BIN, G_N_ELEMENTS (DUID_BIN)); success = g_file_get_contents (path, &contents, NULL, &error); nmtst_assert_success (success, error); |