summaryrefslogtreecommitdiff
path: root/src/dhcp/nm-dhcp-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dhcp/nm-dhcp-manager.c')
-rw-r--r--src/dhcp/nm-dhcp-manager.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
new file mode 100644
index 000000000..6cb5c105b
--- /dev/null
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -0,0 +1,426 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2013 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ *
+ */
+
+#include "nm-default.h"
+
+#include "nm-dhcp-manager.h"
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "nm-config.h"
+#include "NetworkManagerUtils.h"
+
+#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
+
+/*****************************************************************************/
+
+typedef struct {
+ const NMDhcpClientFactory *client_factory;
+ GHashTable * clients;
+ char * default_hostname;
+} NMDhcpManagerPrivate;
+
+struct _NMDhcpManager {
+ GObject parent;
+ NMDhcpManagerPrivate _priv;
+};
+
+struct _NMDhcpManagerClass {
+ GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT)
+
+#define NM_DHCP_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcpManager, NM_IS_DHCP_MANAGER)
+
+/*****************************************************************************/
+
+/* default to installed helper, but can be modified for testing */
+const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
+
+/*****************************************************************************/
+
+static const NMDhcpClientFactory *
+_client_factory_find_by_name (const char *name)
+{
+ int i;
+
+ g_return_val_if_fail (name, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (_nm_dhcp_manager_factories); i++) {
+ const NMDhcpClientFactory *f = _nm_dhcp_manager_factories[i];
+
+ if (f && nm_streq (f->name, name))
+ return f;
+ }
+ return NULL;
+}
+
+static const NMDhcpClientFactory *
+_client_factory_available (const NMDhcpClientFactory *client_factory)
+{
+ if ( client_factory
+ && (!client_factory->get_path || client_factory->get_path ()))
+ return client_factory;
+ return NULL;
+}
+
+/*****************************************************************************/
+
+static NMDhcpClient *
+get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
+{
+ NMDhcpManagerPrivate *priv;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
+ g_return_val_if_fail (ifindex > 0, NULL);
+
+ priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+
+ g_hash_table_iter_init (&iter, priv->clients);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
+
+ if ( nm_dhcp_client_get_ifindex (candidate) == ifindex
+ && nm_dhcp_client_get_ipv6 (candidate) == ip6)
+ return candidate;
+ }
+
+ return NULL;
+}
+
+static void client_state_changed (NMDhcpClient *client,
+ NMDhcpState state,
+ GObject *ip_config,
+ GVariant *options,
+ const char *event_id,
+ NMDhcpManager *self);
+
+static void
+remove_client (NMDhcpManager *self, NMDhcpClient *client)
+{
+ g_signal_handlers_disconnect_by_func (client, client_state_changed, self);
+
+ /* Stopping the client is left up to the controlling device
+ * explicitly since we may want to quit NetworkManager but not terminate
+ * the DHCP client.
+ */
+
+ g_hash_table_remove (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client);
+}
+
+static void
+client_state_changed (NMDhcpClient *client,
+ NMDhcpState state,
+ GObject *ip_config,
+ GVariant *options,
+ const char *event_id,
+ NMDhcpManager *self)
+{
+ if (state >= NM_DHCP_STATE_TIMEOUT)
+ remove_client (self, client);
+}
+
+static NMDhcpClient *
+client_start (NMDhcpManager *self,
+ const char *iface,
+ int ifindex,
+ const GByteArray *hwaddr,
+ const char *uuid,
+ guint32 priority,
+ gboolean ipv6,
+ const struct in6_addr *ipv6_ll_addr,
+ const char *dhcp_client_id,
+ guint32 timeout,
+ const char *dhcp_anycast_addr,
+ const char *hostname,
+ const char *fqdn,
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy,
+ const char *last_ip4_address,
+ guint needed_prefixes)
+{
+ NMDhcpManagerPrivate *priv;
+ NMDhcpClient *client;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+ g_return_val_if_fail (ifindex > 0, NULL);
+ g_return_val_if_fail (uuid != NULL, NULL);
+
+ priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+
+ /* Ensure we have a usable DHCP client */
+ if (!priv->client_factory)
+ return NULL;
+
+ /* Kill any old client instance */
+ client = get_client_for_ifindex (self, ifindex, ipv6);
+ if (client) {
+ g_object_ref (client);
+ remove_client (self, client);
+ nm_dhcp_client_stop (client, FALSE);
+ g_object_unref (client);
+ }
+
+ /* And make a new one */
+ client = g_object_new (priv->client_factory->get_type (),
+ NM_DHCP_CLIENT_INTERFACE, iface,
+ NM_DHCP_CLIENT_IFINDEX, ifindex,
+ NM_DHCP_CLIENT_HWADDR, hwaddr,
+ NM_DHCP_CLIENT_IPV6, ipv6,
+ NM_DHCP_CLIENT_UUID, uuid,
+ NM_DHCP_CLIENT_PRIORITY, priority,
+ NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
+ NULL);
+ g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client));
+ g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self);
+
+ if (ipv6)
+ success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes);
+ else
+ success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, fqdn, last_ip4_address);
+
+ if (!success) {
+ remove_client (self, client);
+ client = NULL;
+ }
+
+ return client;
+}
+
+static const char *
+get_send_hostname (NMDhcpManager *self, const char *setting_hostname)
+{
+ NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+
+ /* Always prefer the explicit dhcp-send-hostname if given */
+ return setting_hostname ? setting_hostname : priv->default_hostname;
+}
+
+/* Caller owns a reference to the NMDhcpClient on return */
+NMDhcpClient *
+nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
+ const char *iface,
+ int ifindex,
+ const GByteArray *hwaddr,
+ const char *uuid,
+ guint32 priority,
+ gboolean send_hostname,
+ const char *dhcp_hostname,
+ const char *dhcp_fqdn,
+ const char *dhcp_client_id,
+ guint32 timeout,
+ const char *dhcp_anycast_addr,
+ const char *last_ip_address)
+{
+ const char *hostname = NULL;
+ const char *fqdn = NULL;
+
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+
+ if (send_hostname) {
+ hostname = get_send_hostname (self, dhcp_hostname);
+ fqdn = dhcp_fqdn;
+ }
+ return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL,
+ dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
+ fqdn, FALSE, 0, last_ip_address, 0);
+}
+
+/* Caller owns a reference to the NMDhcpClient on return */
+NMDhcpClient *
+nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
+ const char *iface,
+ int ifindex,
+ const GByteArray *hwaddr,
+ const struct in6_addr *ll_addr,
+ const char *uuid,
+ guint32 priority,
+ gboolean send_hostname,
+ const char *dhcp_hostname,
+ guint32 timeout,
+ const char *dhcp_anycast_addr,
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy,
+ guint needed_prefixes)
+{
+ const char *hostname = NULL;
+
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+
+ if (send_hostname)
+ hostname = get_send_hostname (self, dhcp_hostname);
+ return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
+ ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, NULL, info_only,
+ privacy, NULL, needed_prefixes);
+}
+
+void
+nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostname)
+{
+ NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+
+ g_clear_pointer (&priv->default_hostname, g_free);
+
+ /* Never send 'localhost'-type names to the DHCP server */
+ if (!nm_utils_is_specific_hostname (hostname))
+ return;
+
+ priv->default_hostname = g_strdup (hostname);
+}
+
+GSList *
+nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
+ const char *iface,
+ int ifindex,
+ const char *uuid,
+ gboolean ipv6,
+ guint32 default_route_metric)
+{
+ NMDhcpManagerPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+ g_return_val_if_fail (iface != NULL, NULL);
+ g_return_val_if_fail (ifindex >= -1, NULL);
+ g_return_val_if_fail (uuid != NULL, NULL);
+
+ priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ if ( priv->client_factory
+ && priv->client_factory->get_lease_ip_configs)
+ return priv->client_factory->get_lease_ip_configs (iface, ifindex, uuid, ipv6, default_route_metric);
+ return NULL;
+}
+
+/*****************************************************************************/
+
+NM_DEFINE_SINGLETON_GETTER (NMDhcpManager, nm_dhcp_manager_get, NM_TYPE_DHCP_MANAGER);
+
+static void
+nm_dhcp_manager_init (NMDhcpManager *self)
+{
+ NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ NMConfig *config = nm_config_get ();
+ const char *client;
+ int i;
+ const NMDhcpClientFactory *client_factory = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS (_nm_dhcp_manager_factories); i++) {
+ const NMDhcpClientFactory *f = _nm_dhcp_manager_factories[i];
+
+ if (!f)
+ continue;
+
+ nm_log_dbg (LOGD_DHCP, "dhcp-init: enabled DHCP client '%s' (%s)%s",
+ f->name, g_type_name (f->get_type ()),
+ _client_factory_available (f) ? "" : " (not available)");
+ }
+
+ /* Client-specific setup */
+ client = nm_config_get_dhcp_client (config);
+ if (nm_config_get_configure_and_quit (config)) {
+ client_factory = &_nm_dhcp_client_factory_internal;
+ if (client && !nm_streq (client, client_factory->name))
+ nm_log_info (LOGD_DHCP, "dhcp-init: Using internal DHCP client since configure-and-quit is set.");
+ } else {
+ if (client) {
+ client_factory = _client_factory_available (_client_factory_find_by_name (client));
+ if (!client_factory)
+ nm_log_warn (LOGD_DHCP, "dhcp-init: DHCP client '%s' not available", client);
+ }
+ if (!client_factory) {
+ client_factory = _client_factory_find_by_name (""NM_CONFIG_DEFAULT_DHCP);
+ if (!client_factory)
+ nm_log_err (LOGD_DHCP, "dhcp-init: default DHCP client '%s' is not installed", NM_CONFIG_DEFAULT_DHCP);
+ else {
+ client_factory = _client_factory_available (client_factory);
+ if (!client_factory)
+ nm_log_info (LOGD_DHCP, "dhcp-init: default DHCP client '%s' is not available", NM_CONFIG_DEFAULT_DHCP);
+ }
+ }
+ if (!client_factory) {
+ for (i = 0; i < G_N_ELEMENTS (_nm_dhcp_manager_factories); i++) {
+ client_factory = _client_factory_available (_nm_dhcp_manager_factories[i]);
+ if (client_factory)
+ break;
+ }
+ }
+ }
+
+ nm_assert (client_factory);
+
+ nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name);
+
+ priv->client_factory = client_factory;
+ priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE ((NMDhcpManager *) object);
+ GList *values, *iter;
+
+ if (priv->clients) {
+ values = g_hash_table_get_values (priv->clients);
+ for (iter = values; iter; iter = g_list_next (iter))
+ remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data));
+ g_list_free (values);
+ }
+
+ G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE ((NMDhcpManager *) object);
+
+ g_free (priv->default_hostname);
+
+ if (priv->clients)
+ g_hash_table_destroy (priv->clients);
+
+ G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_dhcp_manager_class_init (NMDhcpManagerClass *manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+ object_class->finalize = finalize;
+ object_class->dispose = dispose;
+}