diff options
Diffstat (limited to 'src/dhcp-manager/nm-dhcp-dhclient.c')
| -rw-r--r-- | src/dhcp-manager/nm-dhcp-dhclient.c | 284 |
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) { |