about summary refs log tree commit diff
path: root/src/dhcp-manager/nm-dhcp-dhclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dhcp-manager/nm-dhcp-dhclient.c')
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c284
1 files changed, 58 insertions, 226 deletions
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index 9a42f0e4..4232ee0c 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -32,6 +32,7 @@
 #include <stdio.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <ctype.h>
 
 #include <config.h>
 
@@ -39,14 +40,13 @@
 #include "nm-utils.h"
 #include "nm-logging.h"
 #include "nm-dhcp-dhclient-utils.h"
+#include "nm-dhcp-manager.h"
 #include "nm-posix-signals.h"
 
 G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
 
 #define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate))
 
-#define ACTION_SCRIPT_PATH	LIBEXECDIR "/nm-dhcp-client.action"
-
 typedef struct {
 	const char *path;
 	char *conf_file;
@@ -136,223 +136,37 @@ get_dhclient_leasefile (const char *iface,
 	return NULL;
 }
 
-static void
-add_lease_option (GHashTable *hash, char *line)
-{
-	char *spc;
-
-	spc = strchr (line, ' ');
-	if (!spc) {
-		nm_log_warn (LOGD_DHCP, "DHCP lease file line '%s' did not contain a space", line);
-		return;
-	}
-
-	/* If it's an 'option' line, split at second space */
-	if (g_str_has_prefix (line, "option ")) {
-		spc = strchr (spc + 1, ' ');
-		if (!spc) {
-			nm_log_warn (LOGD_DHCP, "DHCP lease file option line '%s' did not contain a second space",
-			             line);
-			return;
-		}
-	}
-
-	/* Split the line at the space */
-	*spc = '\0';
-	spc++;
-
-	/* Kill the ';' at the end of the line, if any */
-	if (*(spc + strlen (spc) - 1) == ';')
-		*(spc + strlen (spc) - 1) = '\0';
-
-	/* Treat 'interface' specially */
-	if (g_str_has_prefix (line, "interface")) {
-		if (*(spc) == '"')
-			spc++; /* Jump past the " */
-		if (*(spc + strlen (spc) - 1) == '"')
-			*(spc + strlen (spc) - 1) = '\0';  /* Kill trailing " */
-	}
-
-	g_hash_table_insert (hash, g_strdup (line), g_strdup (spc));
-}
-
 GSList *
-nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid, gboolean ipv6)
+nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
+                                       const char *uuid,
+                                       gboolean ipv6)
 {
-	GSList *parsed = NULL, *iter, *leases = NULL;
 	char *contents = NULL;
 	char *leasefile;
-	char **line, **split = NULL;
-	GHashTable *hash = NULL;
-
-	/* IPv6 not supported */
-	if (ipv6)
-		return NULL;
+	GSList *leases = NULL;
 
 	leasefile = get_dhclient_leasefile (iface, uuid, FALSE, NULL);
 	if (!leasefile)
 		return NULL;
 
-	if (!g_file_test (leasefile, G_FILE_TEST_EXISTS))
-		goto out;
-
-	if (!g_file_get_contents (leasefile, &contents, NULL, NULL))
-		goto out;
+	if (   g_file_test (leasefile, G_FILE_TEST_EXISTS)
+	    && g_file_get_contents (leasefile, &contents, NULL, NULL)
+	    && contents
+	    && contents[0])
+		leases = nm_dhcp_dhclient_read_lease_ip_configs (iface, contents, ipv6, NULL);
 
-	split = g_strsplit_set (contents, "\n\r", -1);
+	g_free (leasefile);
 	g_free (contents);
-	if (!split)
-		goto out;
-
-	for (line = split; line && *line; line++) {
-		*line = g_strstrip (*line);
-
-		if (!strcmp (*line, "}")) {
-			/* Lease ends */
-			parsed = g_slist_append (parsed, hash);
-			hash = NULL;
-		} else if (!strcmp (*line, "lease {")) {
-			/* Beginning of a new lease */
-			if (hash) {
-				nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started "
-				             "without ending previous lease",
-				             leasefile);
-				g_hash_table_destroy (hash);
-			}
-
-			hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-		} else if (strlen (*line))
-			add_lease_option (hash, *line);
-	}
-	g_strfreev (split);
-
-	/* Check if the last lease in the file was properly ended */
-	if (hash) {
-		nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started "
-		             "without ending previous lease",
-		             leasefile);
-		g_hash_table_destroy (hash);
-		hash = NULL;
-	}
-
-	for (iter = parsed; iter; iter = g_slist_next (iter)) {
-		NMIP4Config *ip4;
-		NMIP4Address *addr;
-		const char *data;
-		struct in_addr tmp;
-		guint32 prefix;
-		struct tm expire;
-
-		hash = iter->data;
-
-		/* Make sure this lease is for the interface we want */
-		data = g_hash_table_lookup (hash, "interface");
-		if (!data || strcmp (data, iface))
-			continue;
-
-		data = g_hash_table_lookup (hash, "expire");
-		if (data) {
-			time_t now_tt;
-			struct tm *now;
-
-			/* Read lease expiration (in UTC) */
-			if (!strptime (data, "%w %Y/%m/%d %H:%M:%S", &expire)) {
-				nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'",
-				             data);
-				continue;
-			}
-
-			now_tt = time (NULL);
-			now = gmtime(&now_tt);
-
-			/* Ignore this lease if it's already expired */
-			if (expire.tm_year < now->tm_year)
-				continue;
-			else if (expire.tm_year == now->tm_year) {
-				if (expire.tm_mon < now->tm_mon)
-					continue;
-				else if (expire.tm_mon == now->tm_mon) {
-					if (expire.tm_mday < now->tm_mday)
-						continue;
-					else if (expire.tm_mday == now->tm_mday) {
-						if (expire.tm_hour < now->tm_hour)
-							continue;
-						else if (expire.tm_hour == now->tm_hour) {
-							if (expire.tm_min < now->tm_min)
-								continue;
-							else if (expire.tm_min == now->tm_min) {
-								if (expire.tm_sec <= now->tm_sec)
-									continue;
-							}
-						}
-					}
-				}
-			}
-			/* If we get this far, the lease hasn't expired */
-		}
 
-		data = g_hash_table_lookup (hash, "fixed-address");
-		if (!data)
-			continue;
-
-		ip4 = nm_ip4_config_new ();
-		addr = nm_ip4_address_new ();
-
-		/* IP4 address */
-		if (!inet_pton (AF_INET, data, &tmp)) {
-			nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 address '%s'", data);
-			goto error;
-		}
-		nm_ip4_address_set_address (addr, tmp.s_addr);
-
-		/* Netmask */
-		data = g_hash_table_lookup (hash, "option subnet-mask");
-		if (data) {
-			if (!inet_pton (AF_INET, data, &tmp)) {
-				nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 subnet mask '%s'", data);
-				goto error;
-			}
-			prefix = nm_utils_ip4_netmask_to_prefix (tmp.s_addr);
-		} else {
-			/* Get default netmask for the IP according to appropriate class. */
-			prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr));
-		}
-		nm_ip4_address_set_prefix (addr, prefix);
-
-		/* Gateway */
-		data = g_hash_table_lookup (hash, "option routers");
-		if (data) {
-			if (!inet_pton (AF_INET, data, &tmp)) {
-				nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 gateway '%s'", data);
-				goto error;
-			}
-			nm_ip4_address_set_gateway (addr, tmp.s_addr);
-		}
-
-		nm_ip4_config_take_address (ip4, addr);
-		leases = g_slist_append (leases, ip4);
-		continue;
-
-	error:
-		nm_ip4_address_unref (addr);
-		g_object_unref (ip4);
-	}
-
-out:
-	g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy);
-	g_free (leasefile);
 	return leases;
 }
 
-
-
 static gboolean
 merge_dhclient_config (const char *iface,
                        const char *conf_file,
                        gboolean is_ip6,
-                       NMSettingIP4Config *s_ip4,
-                       NMSettingIP6Config *s_ip6,
-                       guint8 *anycast_addr,
+                       const char *dhcp_client_id,
+                       GByteArray *anycast_addr,
                        const char *hostname,
                        const char *orig_path,
                        GError **error)
@@ -363,7 +177,7 @@ merge_dhclient_config (const char *iface,
 	g_return_val_if_fail (iface != NULL, FALSE);
 	g_return_val_if_fail (conf_file != NULL, FALSE);
 
-	if (g_file_test (orig_path, G_FILE_TEST_EXISTS)) {
+	if (orig_path && g_file_test (orig_path, G_FILE_TEST_EXISTS)) {
 		GError *read_error = NULL;
 
 		if (!g_file_get_contents (orig_path, &orig, NULL, &read_error)) {
@@ -373,7 +187,7 @@ merge_dhclient_config (const char *iface,
 		}
 	}
 
-	new = nm_dhcp_dhclient_create_config (iface, is_ip6, s_ip4, s_ip6, anycast_addr, hostname, orig_path, orig);
+	new = nm_dhcp_dhclient_create_config (iface, is_ip6, dhcp_client_id, anycast_addr, hostname, orig_path, orig);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
 	g_free (new);
@@ -383,7 +197,7 @@ merge_dhclient_config (const char *iface,
 }
 
 static char *
-get_dhclient_config (const char * iface, const char *uuid, gboolean ipv6)
+find_existing_config (const char *iface, const char *uuid, gboolean ipv6)
 {
 	char *path;
 
@@ -393,17 +207,20 @@ get_dhclient_config (const char * iface, const char *uuid, gboolean ipv6)
 	 */
 	if (uuid) {
 		path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", uuid);
+		nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 		if (g_file_test (path, G_FILE_TEST_EXISTS))
 			return path;
 		g_free (path);
 	}
 
 	path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", iface);
+	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (NMCONFDIR "/dhclient%s.conf", ipv6 ? "6" : "");
+	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
@@ -417,21 +234,25 @@ get_dhclient_config (const char * iface, const char *uuid, gboolean ipv6)
 	 * (including Fedora) don't even provide a default configuration file.
 	 */
 	path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s-%s.conf", ipv6 ? "6" : "", iface);
+	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (SYSCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", iface);
+	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s.conf", ipv6 ? "6" : "");
+	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (SYSCONFDIR "/dhclient%s.conf", ipv6 ? "6" : "");
+	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
@@ -449,9 +270,9 @@ get_dhclient_config (const char * iface, const char *uuid, gboolean ipv6)
 static char *
 create_dhclient_config (const char *iface,
                         gboolean is_ip6,
-                        NMSettingIP4Config *s_ip4,
-                        NMSettingIP6Config *s_ip6,
-                        guint8 *dhcp_anycast_addr,
+                        const char *uuid,
+                        const char *dhcp_client_id,
+                        GByteArray *dhcp_anycast_addr,
                         const char *hostname)
 {
 	char *orig = NULL, *new = NULL;
@@ -461,11 +282,23 @@ create_dhclient_config (const char *iface,
 	g_return_val_if_fail (iface != NULL, NULL);
 
 	new = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s.conf", is_ip6 ? "6" : "", iface);
+	nm_log_dbg (is_ip6 ? LOGD_DHCP6 : LOGD_DHCP4,
+	            "(%s): creating composite dhclient config %s",
+	            iface, new);
+
+	orig = find_existing_config (iface, uuid, is_ip6);
+	if (orig) {
+		nm_log_dbg (is_ip6 ? LOGD_DHCP6 : LOGD_DHCP4,
+		            "(%s): merging existing dhclient config %s",
+		            iface, orig);
+	} else {
+		nm_log_dbg (is_ip6 ? LOGD_DHCP6 : LOGD_DHCP4,
+		            "(%s): no existing dhclient configuration to merge",
+		            iface);
+	}
 
-	/* TODO: also support UUID */
-	orig = get_dhclient_config (iface, NULL, is_ip6);
 	error = NULL;
-	success = merge_dhclient_config (iface, new, is_ip6, s_ip4, s_ip6, dhcp_anycast_addr, hostname, orig, &error);
+	success = merge_dhclient_config (iface, new, is_ip6, dhcp_client_id, dhcp_anycast_addr, hostname, orig, &error);
 	if (!success) {
 		nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient%s configuration: %s",
 		             iface, is_ip6 ? "6" : "", error->message);
@@ -523,10 +356,6 @@ dhclient_start (NMDHCPClient *client,
 	pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
 		                        ipv6 ? "6" : "",
 		                        iface);
-	if (!pid_file) {
-		nm_log_warn (log_domain, "(%s): not enough memory for dhcpcd options.", iface);
-		return -1;
-	}
 
 	/* Kill any existing dhclient from the pidfile */
 	binary_name = g_path_get_basename (priv->path);
@@ -593,7 +422,7 @@ dhclient_start (NMDHCPClient *client,
 			g_ptr_array_add (argv, (gpointer) mode_opt);
 	}
 	g_ptr_array_add (argv, (gpointer) "-sf");	/* Set script file */
-	g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH );
+	g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path);
 
 	if (pid_file) {
 		g_ptr_array_add (argv, (gpointer) "-pf");	/* Set pid file */
@@ -644,16 +473,17 @@ dhclient_start (NMDHCPClient *client,
 
 static GPid
 ip4_start (NMDHCPClient *client,
-           NMSettingIP4Config *s_ip4,
-           guint8 *dhcp_anycast_addr,
+           const char *dhcp_client_id,
+           GByteArray *dhcp_anycast_addr,
            const char *hostname)
 {
 	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
-	const char *iface;
+	const char *iface, *uuid;
 
 	iface = nm_dhcp_client_get_iface (client);
+	uuid = nm_dhcp_client_get_uuid (client);
 
-	priv->conf_file = create_dhclient_config (iface, FALSE, s_ip4, NULL, dhcp_anycast_addr, hostname);
+	priv->conf_file = create_dhclient_config (iface, FALSE, uuid, dhcp_client_id, dhcp_anycast_addr, hostname);
 	if (!priv->conf_file) {
 		nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
 		return -1;
@@ -664,18 +494,18 @@ ip4_start (NMDHCPClient *client,
 
 static GPid
 ip6_start (NMDHCPClient *client,
-           NMSettingIP6Config *s_ip6,
-           guint8 *dhcp_anycast_addr,
+           GByteArray *dhcp_anycast_addr,
            const char *hostname,
            gboolean info_only,
            const GByteArray *duid)
 {
 	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
-	const char *iface;
+	const char *iface, *uuid;
 
 	iface = nm_dhcp_client_get_iface (client);
+	uuid = nm_dhcp_client_get_uuid (client);
 
-	priv->conf_file = create_dhclient_config (iface, TRUE, NULL, s_ip6, dhcp_anycast_addr, hostname);
+	priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname);
 	if (!priv->conf_file) {
 		nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
 		return -1;
@@ -693,9 +523,11 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid);
 
 	if (priv->conf_file)
-		remove (priv->conf_file);
+		if (remove (priv->conf_file) == -1)
+			nm_log_dbg (LOGD_DHCP, "Could not remove dhcp config file \"%s\": %d (%s)", priv->conf_file, errno, g_strerror (errno));
 	if (priv->pid_file) {
-		remove (priv->pid_file);
+		if (remove (priv->pid_file) == -1)
+			nm_log_dbg (LOGD_DHCP, "Could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno));
 		g_free (priv->pid_file);
 		priv->pid_file = NULL;
 	}
@@ -706,7 +538,7 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
 		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);
+			nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client));
 		}
 	}
 }
@@ -727,13 +559,13 @@ get_duid (NMDHCPClient *client)
 	if (leasefile) {
 		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->code, error->message);
 			g_clear_error (&error);
 		}
+		g_free (leasefile);
 	}
 
 	if (!duid && priv->def_leasefile) {