diff options
Diffstat (limited to 'src/dhcp-manager')
| -rw-r--r-- | src/dhcp-manager/Makefile.am | 1 | ||||
| -rw-r--r-- | src/dhcp-manager/Makefile.in | 18 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-client.c | 141 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-client.h | 18 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient-utils.c | 163 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient-utils.h | 10 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient.c | 77 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhcpcd.c | 7 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.c | 11 | ||||
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.h | 2 | ||||
| -rw-r--r-- | src/dhcp-manager/tests/Makefile.am | 8 | ||||
| -rw-r--r-- | src/dhcp-manager/tests/Makefile.in | 24 | ||||
| -rw-r--r-- | src/dhcp-manager/tests/test-dhclient-commented-duid.leases | 2 | ||||
| -rw-r--r-- | src/dhcp-manager/tests/test-dhclient-duid.leases | 2 | ||||
| -rw-r--r-- | src/dhcp-manager/tests/test-dhcp-dhclient.c | 176 |
15 files changed, 613 insertions, 47 deletions
diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am index 04ae1489..40a05834 100644 --- a/src/dhcp-manager/Makefile.am +++ b/src/dhcp-manager/Makefile.am @@ -52,6 +52,7 @@ libdhcp_manager_la_SOURCES = \ libdhcp_manager_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ diff --git a/src/dhcp-manager/Makefile.in b/src/dhcp-manager/Makefile.in index f161c210..acd13114 100644 --- a/src/dhcp-manager/Makefile.in +++ b/src/dhcp-manager/Makefile.in @@ -61,12 +61,11 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ - $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/vapigen.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d @@ -269,12 +268,6 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ -LIBNL1_CFLAGS = @LIBNL1_CFLAGS@ -LIBNL1_LIBS = @LIBNL1_LIBS@ -LIBNL2_CFLAGS = @LIBNL2_CFLAGS@ -LIBNL2_LIBS = @LIBNL2_LIBS@ -LIBNL3_CFLAGS = @LIBNL3_CFLAGS@ -LIBNL3_LIBS = @LIBNL3_LIBS@ LIBNL_CFLAGS = @LIBNL_CFLAGS@ LIBNL_LIBS = @LIBNL_LIBS@ LIBOBJS = @LIBOBJS@ @@ -466,6 +459,7 @@ libdhcp_manager_la_SOURCES = \ libdhcp_manager_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index f721adf5..2cdd304d 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -25,9 +25,8 @@ #include <errno.h> #include <unistd.h> #include <stdio.h> -#include <netinet/in.h> -#include <arpa/inet.h> #include <stdlib.h> +#include <uuid/uuid.h> #include "nm-utils.h" #include "nm-logging.h" @@ -36,9 +35,11 @@ typedef struct { char * iface; + GByteArray * hwaddr; gboolean ipv6; char * uuid; guint32 timeout; + GByteArray * duid; guchar state; GPid pid; @@ -67,6 +68,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, PROP_IFACE, + PROP_HWADDR, PROP_IPV6, PROP_UUID, PROP_TIMEOUT, @@ -182,7 +184,7 @@ nm_dhcp_client_stop_pid (GPid pid, const char *iface, guint timeout_secs) } static void -stop (NMDHCPClient *self, gboolean release) +stop (NMDHCPClient *self, gboolean release, const GByteArray *duid) { NMDHCPClientPrivate *priv; @@ -321,6 +323,96 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self, return priv->pid ? TRUE : FALSE; } +static GByteArray * +generate_duid_from_machine_id (void) +{ + GByteArray *duid; + char *contents = NULL; + GError *error = NULL; + GChecksum *sum; + guint8 buffer[32]; /* SHA256 digest size */ + gsize sumlen = sizeof (buffer); + const guint16 duid_type = g_htons (4); + uuid_t uuid; + int ret; + + /* Get the machine ID from /etc/machine-id; it's always in /etc no matter + * where our configured SYSCONFDIR is. + */ + if (!g_file_get_contents ("/etc/machine-id", &contents, NULL, &error)) { + nm_log_warn (LOGD_DHCP6, "Failed to read " SYSCONFDIR "/machine-id to generate DHCPv6 DUID: (%d) %s", + error ? error->code : -1, + error ? error->message : "(unknown)"); + g_clear_error (&error); + return NULL; + } + + contents = g_strstrip (contents); + ret = uuid_parse (contents, uuid); + g_free (contents); + + if (ret != 0) { + nm_log_warn (LOGD_DHCP6, "Failed to parse " SYSCONFDIR "/machine-id to generate DHCPv6 DUID."); + return NULL; + } + + /* Hash the machine ID so it's not leaked to the network */ + sum = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); + g_checksum_get_digest (sum, buffer, &sumlen); + g_checksum_free (sum); + + /* Generate a DHCP Unique Identifier for DHCPv6 using the + * DUID-UUID method (see RFC 6355 section 4). Format is: + * + * u16: type (DUID-UUID = 4) + * u8[16]: UUID bytes + */ + duid = g_byte_array_sized_new (18); + g_byte_array_append (duid, (guint8 *) &duid_type, sizeof (duid_type)); + + /* Since SHA256 is 256 bits, but UUID is 128 bits, we just take the first + * 128 bits of the SHA256 as the DUID-UUID. + */ + g_byte_array_append (duid, buffer, 16); + + return duid; +} + +static GByteArray * +get_duid (NMDHCPClient *self) +{ + static GByteArray *duid = NULL; + GByteArray *copy = NULL; + + if (G_UNLIKELY (duid == NULL)) + duid = generate_duid_from_machine_id (); + + if (G_LIKELY (duid)) { + copy = g_byte_array_sized_new (duid->len); + g_byte_array_append (copy, duid->data, duid->len); + } + + return copy; +} + +static char * +escape_duid (const GByteArray *duid) +{ + guint32 i = 0; + GString *s; + + g_return_val_if_fail (duid != NULL, NULL); + + s = g_string_sized_new (40); + while (i < duid->len) { + if (s->len) + g_string_append_c (s, ':'); + g_string_append_printf (s, "%02x", duid->data[i++]); + } + return g_string_free (s, FALSE); +} + gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, NMSettingIP6Config *s_ip6, @@ -329,6 +421,7 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self, gboolean info_only) { NMDHCPClientPrivate *priv; + char *escaped; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); @@ -338,12 +431,29 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self, g_return_val_if_fail (priv->ipv6 == TRUE, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); + /* If we don't have one yet, read the default DUID for this DHCPv6 client + * from the client-specific persistent configuration. + */ + if (!priv->duid) + priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); + + if (nm_logging_level_enabled (LOGL_DEBUG)) { + escaped = escape_duid (priv->duid); + nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped); + g_free (escaped); + } + priv->info_only = info_only; nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)", priv->iface, priv->timeout); - priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, hostname, info_only); + priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, + s_ip6, + dhcp_anycast_addr, + hostname, + info_only, + priv->duid); if (priv->pid > 0) start_monitor (self); @@ -397,7 +507,7 @@ nm_dhcp_client_stop (NMDHCPClient *self, gboolean release) /* Kill the DHCP client */ if (!priv->dead) { - NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release); + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid); priv->dead = TRUE; nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d", @@ -1323,6 +1433,9 @@ get_property (GObject *object, guint prop_id, case PROP_IFACE: g_value_set_string (value, priv->iface); break; + case PROP_HWADDR: + g_value_set_boxed (value, priv->hwaddr); + break; case PROP_IPV6: g_value_set_boolean (value, priv->ipv6); break; @@ -1349,6 +1462,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->iface = g_strdup (g_value_get_string (value)); break; + case PROP_HWADDR: + /* construct only */ + priv->hwaddr = g_value_dup_boxed (value); + break; case PROP_IPV6: /* construct-only */ priv->ipv6 = g_value_get_boolean (value); @@ -1382,6 +1499,11 @@ dispose (GObject *object) g_hash_table_destroy (priv->options); g_free (priv->iface); + if (priv->hwaddr) + g_byte_array_free (priv->hwaddr, TRUE); + + if (priv->duid) + g_byte_array_free (priv->duid, TRUE); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); } @@ -1399,6 +1521,7 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class) object_class->set_property = set_property; client_class->stop = stop; + client_class->get_duid = get_duid; g_object_class_install_property (object_class, PROP_IFACE, @@ -1409,6 +1532,14 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property + (object_class, PROP_HWADDR, + g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, + "hwaddr", + "hardware address", + G_TYPE_BYTE_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_IPV6, g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, "ipv6", diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index b93515f4..930ec1a5 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -35,6 +35,7 @@ #define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass)) #define NM_DHCP_CLIENT_INTERFACE "iface" +#define NM_DHCP_CLIENT_HWADDR "hwaddr" #define NM_DHCP_CLIENT_IPV6 "ipv6" #define NM_DHCP_CLIENT_UUID "uuid" #define NM_DHCP_CLIENT_TIMEOUT "timeout" @@ -85,10 +86,23 @@ typedef struct { NMSettingIP6Config *s_ip6, guint8 *anycast_addr, const char *hostname, - gboolean info_only); + gboolean info_only, + const GByteArray *duid); void (*stop) (NMDHCPClient *self, - gboolean release); + gboolean release, + const GByteArray *duid); + + /** + * get_duid: + * @self: the #NMDHCPClient + * + * Attempts to find an existing DHCPv6 DUID for this client in the DHCP + * client's persistent configuration. Returned DUID should be the binary + * representation of the DUID. If no DUID is found, %NULL should be + * returned. + */ + GByteArray * (*get_duid) (NMDHCPClient *self); /* Signals */ void (*state_changed) (NMDHCPClient *self, NMDHCPState state); diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c index f3d3b1b6..8dc7902f 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -22,6 +22,7 @@ #include <glib.h> #include <glib/gi18n.h> #include <string.h> +#include <ctype.h> #include "nm-dhcp-dhclient-utils.h" @@ -255,3 +256,165 @@ nm_dhcp_dhclient_create_config (const char *interface, return g_string_free (new_contents, FALSE); } +/* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */ +char * +nm_dhcp_dhclient_escape_duid (const GByteArray *duid) +{ + char *escaped; + const guint8 *s = duid->data; + char *d; + + d = escaped = g_malloc0 ((duid->len * 4) + 1); + while (s < (duid->data + duid->len)) { + if (!g_ascii_isprint (*s)) { + *d++ = '\\'; + *d++ = '0' + ((*s >> 6) & 0x7); + *d++ = '0' + ((*s >> 3) & 0x7); + *d++ = '0' + (*s++ & 0x7); + } else if (*s == '"' || *s == '\'' || *s == '$' || + *s == '`' || *s == '\\' || *s == '|' || + *s == '&') { + *d++ = '\\'; + *d++ = *s++; + } else + *d++ = *s++; + } + return escaped; +} + +static inline gboolean +isoctal (const guint8 *p) +{ + return ( p[0] >= '0' && p[0] <= '3' + && p[1] >= '0' && p[1] <= '7' + && p[2] >= '0' && p[2] <= '7'); +} + +GByteArray * +nm_dhcp_dhclient_unescape_duid (const char *duid) +{ + GByteArray *unescaped; + const guint8 *p = (const guint8 *) duid; + guint i, len; + guint8 octal; + + len = strlen (duid); + unescaped = g_byte_array_sized_new (len); + for (i = 0; i < len; i++) { + if (p[i] == '\\') { + i++; + if (isdigit (p[i])) { + /* Octal escape sequence */ + if (i + 2 >= len || !isoctal (p + i)) + goto error; + octal = ((p[i] - '0') << 6) + ((p[i + 1] - '0') << 3) + (p[i + 2] - '0'); + g_byte_array_append (unescaped, &octal, 1); + i += 2; + } else { + /* One of ", ', $, `, \, |, or & */ + g_warn_if_fail (p[i] == '"' || p[i] == '\'' || p[i] == '$' || + p[i] == '`' || p[i] == '\\' || p[i] == '|' || + p[i] == '&'); + g_byte_array_append (unescaped, &p[i], 1); + } + } else + g_byte_array_append (unescaped, &p[i], 1); + } + + return unescaped; + +error: + g_byte_array_free (unescaped, TRUE); + return NULL; +} + +#define DUID_PREFIX "default-duid \"" + +GByteArray * +nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) +{ + GByteArray *duid = NULL; + char *contents; + char **line, **split, *p, *e; + + if (!g_file_test (leasefile, G_FILE_TEST_EXISTS)) + return NULL; + + if (!g_file_get_contents (leasefile, &contents, NULL, error)) + return NULL; + + split = g_strsplit_set (contents, "\n\r", -1); + for (line = split; line && *line && (duid == NULL); line++) { + p = g_strstrip (*line); + if (g_str_has_prefix (p, DUID_PREFIX)) { + p += strlen (DUID_PREFIX); + + /* look for trailing "; */ + e = p + strlen (p) - 2; + if (strcmp (e, "\";") != 0) + continue; + *e = '\0'; + + duid = nm_dhcp_dhclient_unescape_duid (p); + } + } + g_free (contents); + g_strfreev (split); + + return duid; +} + +gboolean +nm_dhcp_dhclient_save_duid (const char *leasefile, + const char *escaped_duid, + GError **error) +{ + char **lines = NULL, **iter, *l; + GString *s; + gboolean success; + gsize len = 0; + + g_return_val_if_fail (leasefile != NULL, FALSE); + g_return_val_if_fail (escaped_duid != NULL, FALSE); + + if (g_file_test (leasefile, G_FILE_TEST_EXISTS)) { + char *contents = NULL; + + if (!g_file_get_contents (leasefile, &contents, &len, error)) { + g_prefix_error (error, "failed to read lease file %s: ", leasefile); + return FALSE; + } + + /* If the file already contains an uncommented DUID, leave it */ + g_assert (contents); + lines = g_strsplit_set (contents, "\n\r", -1); + g_free (contents); + for (iter = lines; iter && *iter; iter++) { + l = *iter; + while (g_ascii_isspace (*l)) + l++; + if (g_str_has_prefix (l, DUID_PREFIX)) { + g_strfreev (lines); + return TRUE; + } + } + } + + s = g_string_sized_new (len + 50); + g_string_append_printf (s, DUID_PREFIX "%s\";\n", escaped_duid); + + /* Preserve existing leasefile contents */ + if (lines) { + for (iter = lines; iter && *iter; iter++) + g_string_append (s, *iter[0] ? *iter : "\n"); + g_strfreev (lines); + } + + success = g_file_set_contents (leasefile, s->str, -1, error); + if (!success) + g_prefix_error (error, "failed to set DUID in lease file %s: ", leasefile); + + g_string_free (s, TRUE); + return success; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h index 2f9f7c45..95cb0568 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h @@ -34,5 +34,15 @@ char *nm_dhcp_dhclient_create_config (const char *interface, const char *orig_path, const char *orig_contents); +char *nm_dhcp_dhclient_escape_duid (const GByteArray *duid); + +GByteArray *nm_dhcp_dhclient_unescape_duid (const char *duid); + +GByteArray *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); + +gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, + const char *escaped_duid, + GError **error); + #endif /* NM_DHCP_DHCLIENT_UTILS_H */ diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 347d97c4..d9f51352 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -50,6 +50,7 @@ G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT) typedef struct { const char *path; char *conf_file; + const char *def_leasefile; char *lease_file; char *pid_file; } NMDHCPDhclientPrivate; @@ -445,6 +446,7 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) static GPid dhclient_start (NMDHCPClient *client, const char *mode_opt, + const GByteArray *duid, gboolean release) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); @@ -575,7 +577,7 @@ ip4_start (NMDHCPClient *client, return -1; } - return dhclient_start (client, NULL, FALSE); + return dhclient_start (client, NULL, NULL, FALSE); } static GPid @@ -583,7 +585,8 @@ ip6_start (NMDHCPClient *client, NMSettingIP6Config *s_ip6, guint8 *dhcp_anycast_addr, const char *hostname, - gboolean info_only) + gboolean info_only, + const GByteArray *duid) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); const char *iface; @@ -596,16 +599,16 @@ ip6_start (NMDHCPClient *client, return -1; } - return dhclient_start (client, info_only ? "-S" : "-N", FALSE); + return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE); } static void -stop (NMDHCPClient *client, gboolean release) +stop (NMDHCPClient *client, gboolean release, const GByteArray *duid) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); /* Chain up to parent */ - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid); if (priv->conf_file) remove (priv->conf_file); @@ -618,7 +621,7 @@ stop (NMDHCPClient *client, gboolean release) if (release) { GPid rpid; - rpid = dhclient_start (client, NULL, TRUE); + rpid = dhclient_start (client, NULL, duid, TRUE); if (rpid > 0) { /* Wait a few seconds for the release to happen */ nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client), 5); @@ -626,14 +629,75 @@ stop (NMDHCPClient *client, gboolean release) } } +static GByteArray * +get_duid (NMDHCPClient *client) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + GByteArray *duid = NULL; + char *leasefile; + GError *error = NULL; + + /* Look in interface-specific leasefile first for backwards compat */ + leasefile = get_dhclient_leasefile (nm_dhcp_client_get_iface (client), + nm_dhcp_client_get_uuid (client), + TRUE); + nm_log_dbg (LOGD_DHCP, "Looking for DHCPv6 DUID in '%s'.", leasefile); + duid = nm_dhcp_dhclient_read_duid (leasefile, &error); + g_free (leasefile); + + if (error) { + nm_log_warn (LOGD_DHCP, "Failed to read leasefile '%s': (%d) %s", + leasefile, + error ? error->code : -1, + error ? error->message : "(unknown)"); + g_clear_error (&error); + } + + if (!duid && priv->def_leasefile) { + /* Otherwise read the default machine-wide DUID */ + nm_log_dbg (LOGD_DHCP, "Looking for default DHCPv6 DUID in '%s'.", priv->def_leasefile); + duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); + if (error) { + nm_log_warn (LOGD_DHCP, "Failed to read leasefile '%s': (%d) %s", + priv->def_leasefile, + error ? error->code : -1, + error ? error->message : "(unknown)"); + g_clear_error (&error); + } + } + + /* return our DUID, otherwise let the parent class make a default DUID */ + return duid ? duid : NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->get_duid (client); +} + /***************************************************/ +static const char *def_leasefiles[] = { + SYSCONFDIR "/dhclient6.leases", + LOCALSTATEDIR "/lib/dhcp/dhclient6.leases", + LOCALSTATEDIR "/lib/dhclient/dhclient6.leases", + NULL +}; + static void nm_dhcp_dhclient_init (NMDHCPDhclient *self) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); + const char **iter = &def_leasefiles[0]; priv->path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH); + + while (iter && *iter) { + if (g_file_test (*iter, G_FILE_TEST_EXISTS)) { + priv->def_leasefile = *iter; + break; + } + iter++; + } + + /* Fallback option */ + if (!priv->def_leasefile) + priv->def_leasefile = SYSCONFDIR "/dhclient6.leases"; } static void @@ -662,5 +726,6 @@ nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class) client_class->ip4_start = ip4_start; client_class->ip6_start = ip6_start; client_class->stop = stop; + client_class->get_duid = get_duid; } diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index dda96e2a..f3245ee5 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -170,19 +170,20 @@ ip6_start (NMDHCPClient *client, NMSettingIP6Config *s_ip6, guint8 *dhcp_anycast_addr, const char *hostname, - gboolean info_only) + gboolean info_only, + const GByteArray *duid) { nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6."); return -1; } static void -stop (NMDHCPClient *client, gboolean release) +stop (NMDHCPClient *client, gboolean release, const GByteArray *duid) { NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client); /* Chain up to parent */ - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid); if (priv->pid_file) remove (priv->pid_file); diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 995bff47..1bd7f984 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -384,6 +384,7 @@ add_client (NMDHCPManager *self, NMDHCPClient *client) static NMDHCPClient * client_start (NMDHCPManager *self, const char *iface, + const GByteArray *hwaddr, const char *uuid, gboolean ipv6, NMSettingIP4Config *s_ip4, @@ -417,6 +418,7 @@ client_start (NMDHCPManager *self, /* And make a new one */ client = g_object_new (priv->client_type, NM_DHCP_CLIENT_INTERFACE, iface, + NM_DHCP_CLIENT_HWADDR, hwaddr, NM_DHCP_CLIENT_IPV6, ipv6, NM_DHCP_CLIENT_UUID, uuid, NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT, @@ -442,13 +444,13 @@ client_start (NMDHCPManager *self, NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *self, const char *iface, + const GByteArray *hwaddr, const char *uuid, NMSettingIP4Config *s_ip4, guint32 timeout, guint8 *dhcp_anycast_addr) { NMDHCPManagerPrivate *priv; - NMDHCPClient *client = NULL; const char *hostname = NULL; gboolean send_hostname = TRUE; @@ -485,15 +487,14 @@ nm_dhcp_manager_start_ip4 (NMDHCPManager *self, } } - client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, hostname, FALSE); - - return client; + return client_start (self, iface, hwaddr, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, hostname, FALSE); } /* Caller owns a reference to the NMDHCPClient on return */ NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *self, const char *iface, + const GByteArray *hwaddr, const char *uuid, NMSettingIP6Config *s_ip6, guint32 timeout, @@ -516,7 +517,7 @@ nm_dhcp_manager_start_ip6 (NMDHCPManager *self, hostname = NULL; } - return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, hostname, info_only); + return client_start (self, iface, hwaddr, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, hostname, info_only); } static void diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 68db7e37..a29a33fd 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -67,6 +67,7 @@ void nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager, NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *manager, const char *iface, + const GByteArray *hwaddr, const char *uuid, NMSettingIP4Config *s_ip4, guint32 timeout, @@ -74,6 +75,7 @@ NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *manager, NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager, const char *iface, + const GByteArray *hwaddr, const char *uuid, NMSettingIP6Config *s_ip6, guint32 timeout, diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am index 251aa224..8c9d06c1 100644 --- a/src/dhcp-manager/tests/Makefile.am +++ b/src/dhcp-manager/tests/Makefile.am @@ -15,7 +15,8 @@ test_dhcp_dhclient_SOURCES = \ test-dhcp-dhclient.c test_dhcp_dhclient_CPPFLAGS = \ - $(GLIB_CFLAGS) + $(GLIB_CFLAGS) \ + -DTESTDIR="\"$(abs_srcdir)\"" test_dhcp_dhclient_LDADD = \ -ldl \ @@ -27,3 +28,8 @@ check-local: test-dhcp-dhclient $(abs_builddir)/test-dhcp-dhclient endif + +EXTRA_DIST = \ + test-dhclient-duid.leases \ + test-dhclient-commented-duid.leases + diff --git a/src/dhcp-manager/tests/Makefile.in b/src/dhcp-manager/tests/Makefile.in index 32cdf4db..921ccaf3 100644 --- a/src/dhcp-manager/tests/Makefile.in +++ b/src/dhcp-manager/tests/Makefile.in @@ -62,12 +62,11 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ - $(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/vapigen.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d @@ -219,12 +218,6 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ -LIBNL1_CFLAGS = @LIBNL1_CFLAGS@ -LIBNL1_LIBS = @LIBNL1_LIBS@ -LIBNL2_CFLAGS = @LIBNL2_CFLAGS@ -LIBNL2_LIBS = @LIBNL2_LIBS@ -LIBNL3_CFLAGS = @LIBNL3_CFLAGS@ -LIBNL3_LIBS = @LIBNL3_LIBS@ LIBNL_CFLAGS = @LIBNL_CFLAGS@ LIBNL_LIBS = @LIBNL_LIBS@ LIBOBJS = @LIBOBJS@ @@ -377,7 +370,8 @@ with_resolvconf = @with_resolvconf@ @ENABLE_TESTS_TRUE@ test-dhcp-dhclient.c @ENABLE_TESTS_TRUE@test_dhcp_dhclient_CPPFLAGS = \ -@ENABLE_TESTS_TRUE@ $(GLIB_CFLAGS) +@ENABLE_TESTS_TRUE@ $(GLIB_CFLAGS) \ +@ENABLE_TESTS_TRUE@ -DTESTDIR="\"$(abs_srcdir)\"" @ENABLE_TESTS_TRUE@test_dhcp_dhclient_LDADD = \ @ENABLE_TESTS_TRUE@ -ldl \ @@ -385,6 +379,10 @@ with_resolvconf = @with_resolvconf@ @ENABLE_TESTS_TRUE@ $(top_builddir)/libnm-util/libnm-util.la \ @ENABLE_TESTS_TRUE@ $(GLIB_LIBS) +EXTRA_DIST = \ + test-dhclient-duid.leases \ + test-dhclient-commented-duid.leases + all: all-am .SUFFIXES: diff --git a/src/dhcp-manager/tests/test-dhclient-commented-duid.leases b/src/dhcp-manager/tests/test-dhclient-commented-duid.leases new file mode 100644 index 00000000..3e46ae7d --- /dev/null +++ b/src/dhcp-manager/tests/test-dhclient-commented-duid.leases @@ -0,0 +1,2 @@ +#default-duid "\000\001\000\001\030y\246\023`g \354Lp"; + diff --git a/src/dhcp-manager/tests/test-dhclient-duid.leases b/src/dhcp-manager/tests/test-dhclient-duid.leases new file mode 100644 index 00000000..229331d4 --- /dev/null +++ b/src/dhcp-manager/tests/test-dhclient-duid.leases @@ -0,0 +1,2 @@ +default-duid "\000\001\000\001\030y\246\023`g \354Lp"; + diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c index a87c29aa..7eb2d358 100644 --- a/src/dhcp-manager/tests/test-dhcp-dhclient.c +++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c @@ -20,6 +20,7 @@ #include <glib.h> #include <string.h> +#include <unistd.h> #include "nm-dhcp-dhclient-utils.h" #include "nm-utils.h" @@ -225,6 +226,173 @@ test_existing_multiline_alsoreq (void) /*******************************************/ +static void +test_one_duid (const char *escaped, const guint8 *unescaped, guint len) +{ + GByteArray *t; + char *w; + + t = nm_dhcp_dhclient_unescape_duid (escaped); + g_assert (t); + g_assert_cmpint (t->len, ==, len); + g_assert_cmpint (memcmp (t->data, unescaped, len), ==, 0); + g_byte_array_free (t, TRUE); + + t = g_byte_array_sized_new (len); + g_byte_array_append (t, unescaped, len); + w = nm_dhcp_dhclient_escape_duid (t); + g_assert (w); + g_assert_cmpint (strlen (escaped), ==, strlen (w)); + g_assert_cmpstr (escaped, ==, w); +} + +static void +test_duids (void) +{ + const guint8 test1_u[] = { 0x00, 0x01, 0x00, 0x01, 0x13, 0x6f, 0x13, 0x6e, + 0x00, 0x22, 0xfa, 0x8c, 0xd6, 0xc2 }; + const char *test1_s = "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302"; + + const guint8 test2_u[] = { 0x00, 0x01, 0x00, 0x01, 0x17, 0x57, 0xee, 0x39, + 0x00, 0x23, 0x15, 0x08, 0x7E, 0xac }; + const char *test2_s = "\\000\\001\\000\\001\\027W\\3569\\000#\\025\\010~\\254"; + + const guint8 test3_u[] = { 0x00, 0x01, 0x00, 0x01, 0x17, 0x58, 0xe8, 0x58, + 0x00, 0x23, 0x15, 0x08, 0x7e, 0xac }; + const char *test3_s = "\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254"; + + const guint8 test4_u[] = { 0x00, 0x01, 0x00, 0x01, 0x15, 0xd5, 0x31, 0x97, + 0x00, 0x16, 0xeb, 0x04, 0x45, 0x18 }; + const char *test4_s = "\\000\\001\\000\\001\\025\\3251\\227\\000\\026\\353\\004E\\030"; + + const char *bad_s = "\\000\\001\\000\\001\\425\\3251\\227\\000\\026\\353\\004E\\030"; + + test_one_duid (test1_s, test1_u, sizeof (test1_u)); + test_one_duid (test2_s, test2_u, sizeof (test2_u)); + test_one_duid (test3_s, test3_u, sizeof (test3_u)); + test_one_duid (test4_s, test4_u, sizeof (test4_u)); + + /* Invalid octal digit */ + g_assert (nm_dhcp_dhclient_unescape_duid (bad_s) == NULL); +} + +static void +test_read_duid_from_leasefile (void) +{ + const guint8 expected[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0x79, 0xa6, + 0x13, 0x60, 0x67, 0x20, 0xec, 0x4c, 0x70 }; + GByteArray *duid; + GError *error = NULL; + + duid = nm_dhcp_dhclient_read_duid (TESTDIR "/test-dhclient-duid.leases", &error); + g_assert_no_error (error); + g_assert (duid); + g_assert_cmpint (duid->len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (duid->data, expected, duid->len), ==, 0); + + g_byte_array_free (duid, TRUE); +} + +static void +test_read_commented_duid_from_leasefile (void) +{ + GByteArray *duid; + GError *error = NULL; + + duid = nm_dhcp_dhclient_read_duid (TESTDIR "/test-dhclient-commented-duid.leases", &error); + g_assert_no_error (error); + g_assert (duid == NULL); +} + +static void +test_write_duid (void) +{ + const char *duid = "\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254"; + const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; + GError *error = NULL; + 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); + + success = g_file_get_contents (path, &contents, NULL, &error); + g_assert_no_error (error); + g_assert (success); + + unlink (path); + g_assert_cmpstr (expected_contents, ==, contents); + + g_free (contents); +} + +static void +test_write_existing_duid (void) +{ + const char *duid = "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302"; + const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; + GError *error = NULL; + char *contents = NULL; + gboolean success; + const char *path = "test-dhclient-write-existing-duid.leases"; + + success = g_file_set_contents (path, expected_contents, -1, &error); + g_assert_no_error (error); + g_assert (success); + + /* Save other DUID; should be a no-op */ + success = nm_dhcp_dhclient_save_duid (path, duid, &error); + g_assert_no_error (error); + g_assert (success); + + /* reread original contents */ + success = g_file_get_contents (path, &contents, NULL, &error); + g_assert_no_error (error); + g_assert (success); + + unlink (path); + g_assert_cmpstr (expected_contents, ==, contents); + + g_free (contents); +} + +static void +test_write_existing_commented_duid (void) +{ + #define DUID "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302" + #define ORIG_CONTENTS "#default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n" + const char *expected_contents = \ + "default-duid \"" DUID "\";\n" + ORIG_CONTENTS; + GError *error = NULL; + 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); + + /* Save other DUID; should be a no-op */ + success = nm_dhcp_dhclient_save_duid (path, DUID, &error); + g_assert_no_error (error); + g_assert (success); + + /* reread original contents */ + success = g_file_get_contents (path, &contents, NULL, &error); + g_assert_no_error (error); + g_assert (success); + + unlink (path); + g_assert_cmpstr (expected_contents, ==, contents); + + g_free (contents); +} + +/*******************************************/ + #if GLIB_CHECK_VERSION(2,25,12) typedef GTestFixtureFunc TCFunc; #else @@ -248,6 +416,14 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_override_hostname, NULL)); g_test_suite_add (suite, TESTCASE (test_existing_alsoreq, NULL)); g_test_suite_add (suite, TESTCASE (test_existing_multiline_alsoreq, NULL)); + g_test_suite_add (suite, TESTCASE (test_duids, NULL)); + + g_test_suite_add (suite, TESTCASE (test_read_duid_from_leasefile, NULL)); + g_test_suite_add (suite, TESTCASE (test_read_commented_duid_from_leasefile, NULL)); + + g_test_suite_add (suite, TESTCASE (test_write_duid, NULL)); + g_test_suite_add (suite, TESTCASE (test_write_existing_duid, NULL)); + g_test_suite_add (suite, TESTCASE (test_write_existing_commented_duid, NULL)); return g_test_run (); } |