summaryrefslogtreecommitdiff
path: root/src/dhcp-manager/nm-dhcp-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dhcp-manager/nm-dhcp-manager.c')
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c392
1 files changed, 108 insertions, 284 deletions
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index b19590a69b..419bdafbcb 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -23,7 +23,6 @@
#include "config.h"
#include <glib.h>
#include <glib/gi18n.h>
-#include <dbus/dbus.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@@ -37,20 +36,15 @@
#include "nm-dhcp-manager.h"
#include "nm-dhcp-dhclient.h"
#include "nm-dhcp-dhcpcd.h"
+#include "nm-dhcp-systemd.h"
#include "nm-logging.h"
-#include "nm-dbus-manager.h"
#include "nm-config.h"
#include "nm-dbus-glib-types.h"
#include "nm-glib-compat.h"
#include "NetworkManagerUtils.h"
-#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
-
#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
-#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
-#define PRIV_SOCK_TAG "dhcp"
-
/* default to installed helper, but can be modified for testing */
const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
@@ -58,78 +52,87 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo
typedef struct {
GType client_type;
- GetLeaseConfigFunc get_lease_ip_configs_func;
-
- NMDBusManager * dbus_mgr;
- guint new_conn_id;
- guint dis_conn_id;
- GHashTable * proxies;
-
GHashTable * clients;
- DBusGProxy * proxy;
char * default_hostname;
} NMDhcpManagerPrivate;
-
#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDhcpManagerPrivate))
G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT)
-static char *
-garray_to_string (GArray *array, const char *key)
+/***************************************************/
+
+typedef struct {
+ GType gtype;
+ const char *name;
+ NMDhcpClientGetPathFunc get_path_func;
+ NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func;
+} ClientDesc;
+
+static GSList *client_descs = NULL;
+
+void
+_nm_dhcp_client_register (GType gtype,
+ const char *name,
+ NMDhcpClientGetPathFunc get_path_func,
+ NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func)
{
- GString *str;
- int i;
- unsigned char c;
- char *converted = NULL;
+ ClientDesc *desc;
+ GSList *iter;
- g_return_val_if_fail (array != NULL, NULL);
+ g_return_if_fail (gtype != G_TYPE_INVALID);
+ g_return_if_fail (name != NULL);
- /* Since the DHCP options come through environment variables, they should
- * already be UTF-8 safe, but just make sure.
- */
- str = g_string_sized_new (array->len);
- for (i = 0; i < array->len; i++) {
- c = array->data[i];
-
- /* Convert NULLs to spaces and non-ASCII characters to ? */
- if (c == '\0')
- c = ' ';
- else if (c > 127)
- c = '?';
- str = g_string_append_c (str, c);
+ for (iter = client_descs; iter; iter = iter->next) {
+ desc = iter->data;
+ g_return_if_fail (desc->gtype != gtype);
+ g_return_if_fail (strcmp (desc->name, name) != 0);
}
- str = g_string_append_c (str, '\0');
- converted = str->str;
- if (!g_utf8_validate (converted, -1, NULL))
- nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
- g_string_free (str, FALSE);
- return converted;
+ desc = g_slice_new0 (ClientDesc);
+ desc->gtype = gtype;
+ desc->name = name;
+ desc->get_path_func = get_path_func;
+ desc->get_lease_configs_func = get_lease_configs_func;
+ client_descs = g_slist_prepend (client_descs, desc);
}
-static NMDhcpClient *
-get_client_for_pid (NMDhcpManager *manager, GPid pid)
+static ClientDesc *
+find_client_desc (const char *name, GType gtype)
{
- NMDhcpManagerPrivate *priv;
- GHashTableIter iter;
- gpointer value;
-
- g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
+ GSList *iter;
- priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+ g_return_val_if_fail (name || gtype, NULL);
- g_hash_table_iter_init (&iter, priv->clients);
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
+ for (iter = client_descs; iter; iter = iter->next) {
+ ClientDesc *desc = iter->data;
- if (nm_dhcp_client_get_pid (candidate) == pid)
- return candidate;
+ if (name && strcmp (desc->name, name) != 0)
+ continue;
+ if (gtype && desc->name != 0)
+ continue;
+ return desc;
}
-
return NULL;
}
+static GType
+is_client_enabled (const char *name, GError **error)
+{
+ ClientDesc *desc;
+
+ desc = find_client_desc (name, G_TYPE_INVALID);
+ if (desc && (!desc->get_path_func || desc->get_path_func()))
+ return desc->gtype;
+
+ g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+ _("'%s' support not found or not enabled."),
+ name);
+ return G_TYPE_INVALID;
+}
+
+/***************************************************/
+
static NMDhcpClient *
get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
{
@@ -154,169 +157,26 @@ get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
return NULL;
}
-static char *
-get_option (GHashTable *hash, const char *key)
-{
- GValue *value;
-
- value = g_hash_table_lookup (hash, key);
- if (value == NULL)
- return NULL;
-
- if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
- nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
- "DBUS_TYPE_G_UCHAR_ARRAY",
- (char *) key);
- return NULL;
- }
-
- return garray_to_string ((GArray *) g_value_get_boxed (value), key);
-}
-
-static void
-nm_dhcp_manager_handle_event (DBusGProxy *proxy,
- GHashTable *options,
- gpointer user_data)
-{
- NMDhcpManager *manager = NM_DHCP_MANAGER (user_data);
- NMDhcpClient *client;
- char *iface = NULL;
- char *pid_str = NULL;
- char *reason = NULL;
- long pid;
-
- iface = get_option (options, "interface");
- if (iface == NULL) {
- nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
- goto out;
- }
-
- pid_str = get_option (options, "pid");
- pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, LONG_MAX, -1);
- if (pid == -1 || pid != (GPid)pid) {
- nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
- goto out;
- }
-
- reason = get_option (options, "reason");
- client = get_client_for_pid (manager, (GPid) pid);
- if (client == NULL) {
- if (reason && g_ascii_strcasecmp (reason, "RELEASE") == 0) {
- /* This happens regularly, when the dhcp client gets killed and we receive its last message.
- * Don't log a warning in this case. */
- nm_log_dbg (LOGD_DHCP, "(pid %ld) unhandled RELEASE DHCP event for interface %s", pid, iface);
- } else
- nm_log_warn (LOGD_DHCP, "(pid %ld) unhandled DHCP event for interface %s", pid, iface);
- goto out;
- }
-
- if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
- nm_log_warn (LOGD_DHCP, "(pid %ld) received DHCP event from unexpected interface '%s' (expected '%s')",
- pid, iface, nm_dhcp_client_get_iface (client));
- goto out;
- }
-
- if (reason == NULL) {
- nm_log_warn (LOGD_DHCP, "(pid %ld) DHCP event didn't have a reason", pid);
- goto out;
- }
-
- nm_dhcp_client_new_options (client, options, reason);
-
-out:
- g_free (iface);
- g_free (pid_str);
- g_free (reason);
-}
-
-#if HAVE_DBUS_GLIB_100
-static void
-new_connection_cb (NMDBusManager *mgr,
- DBusGConnection *connection,
- NMDhcpManager *self)
-{
- NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- DBusGProxy *proxy;
-
- /* Create a new proxy for the client */
- proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
- dbus_g_proxy_add_signal (proxy,
- "Event",
- DBUS_TYPE_G_MAP_OF_VARIANT,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal (proxy,
- "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- self,
- NULL);
- g_hash_table_insert (priv->proxies, connection, proxy);
-}
-
-static void
-dis_connection_cb (NMDBusManager *mgr,
- DBusGConnection *connection,
- NMDhcpManager *self)
-{
- NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- DBusGProxy *proxy;
-
- proxy = g_hash_table_lookup (priv->proxies, connection);
- if (proxy) {
- dbus_g_proxy_disconnect_signal (proxy,
- "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- self);
- g_hash_table_remove (priv->proxies, connection);
- }
-}
-#endif
-
static GType
get_client_type (const char *client, GError **error)
{
- gboolean use_dhclient, use_dhcpcd;
-
- /* If a client was disabled at build-time, these will return FALSE */
- use_dhclient = !!nm_dhcp_dhclient_get_path ();
- use_dhcpcd = !!nm_dhcp_dhcpcd_get_path ();
-
- if (!client) {
- if (use_dhclient)
- return NM_TYPE_DHCP_DHCLIENT;
- else if (use_dhcpcd)
- return NM_TYPE_DHCP_DHCPCD;
- else {
- g_set_error_literal (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("no usable DHCP client could be found."));
- return G_TYPE_INVALID;
- }
- }
-
- if (!strcmp (client, "dhclient")) {
- if (!use_dhclient) {
- g_set_error_literal (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("'dhclient' could not be found or was disabled."));
- return G_TYPE_INVALID;
+ GType client_gtype;
+
+ if (client)
+ client_gtype = is_client_enabled (client, error);
+ else {
+ /* Fallbacks */
+ client_gtype = is_client_enabled ("dhclient", NULL);
+ if (client_gtype == G_TYPE_INVALID)
+ client_gtype = is_client_enabled ("dhcpcd", NULL);
+ if (client_gtype == G_TYPE_INVALID)
+ client_gtype = is_client_enabled ("internal", NULL);
+ if (client_gtype == G_TYPE_INVALID) {
+ g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+ _("no usable DHCP client could be found."));
}
- return NM_TYPE_DHCP_DHCLIENT;
}
-
- if (!strcmp (client, "dhcpcd")) {
- if (!use_dhcpcd) {
- g_set_error_literal (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("'dhcpcd' could not be found or was disabled."));
- return G_TYPE_INVALID;
- }
- return NM_TYPE_DHCP_DHCPCD;
- }
-
- g_set_error (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("unsupported DHCP client '%s'"), client);
- return G_TYPE_INVALID;
+ return client_gtype;
}
static void client_state_changed (NMDhcpClient *client,
@@ -355,14 +215,15 @@ client_start (NMDhcpManager *self,
int ifindex,
const GByteArray *hwaddr,
const char *uuid,
- guint priority,
+ guint32 priority,
gboolean ipv6,
const char *dhcp_client_id,
guint32 timeout,
const char *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
- NMSettingIP6ConfigPrivacy privacy)
+ NMSettingIP6ConfigPrivacy privacy,
+ const char *last_ip4_address)
{
NMDhcpManagerPrivate *priv;
NMDhcpClient *client;
@@ -403,7 +264,7 @@ client_start (NMDhcpManager *self,
if (ipv6)
success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only, privacy);
else
- success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname);
+ success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address);
if (!success) {
remove_client (self, client);
@@ -429,12 +290,13 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
int ifindex,
const GByteArray *hwaddr,
const char *uuid,
- guint priority,
+ guint32 priority,
gboolean send_hostname,
const char *dhcp_hostname,
const char *dhcp_client_id,
guint32 timeout,
- const char *dhcp_anycast_addr)
+ const char *dhcp_anycast_addr,
+ const char *last_ip_address)
{
const char *hostname = NULL;
@@ -444,7 +306,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE,
dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
- FALSE, 0);
+ FALSE, 0, last_ip_address);
}
/* Caller owns a reference to the NMDhcpClient on return */
@@ -454,22 +316,23 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
int ifindex,
const GByteArray *hwaddr,
const char *uuid,
- guint priority,
+ guint32 priority,
+ gboolean send_hostname,
const char *dhcp_hostname,
guint32 timeout,
const char *dhcp_anycast_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy)
{
- const char *hostname;
+ const char *hostname = NULL;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
- hostname = dhcp_hostname ? get_send_hostname (self, dhcp_hostname) : NULL;
-
+ if (send_hostname)
+ hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
NULL, timeout, dhcp_anycast_addr, hostname, info_only,
- privacy);
+ privacy, NULL);
}
void
@@ -492,16 +355,15 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
const char *uuid,
gboolean ipv6)
{
- NMDhcpManagerPrivate *priv;
+ ClientDesc *desc;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
g_return_val_if_fail (iface != NULL, NULL);
g_return_val_if_fail (uuid != NULL, NULL);
- priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-
- if (priv->get_lease_ip_configs_func)
- return priv->get_lease_ip_configs_func (iface, uuid, ipv6);
+ desc = find_client_desc (NULL, NM_DHCP_MANAGER_GET_PRIVATE (self)->client_type);
+ if (desc && desc->get_lease_configs_func)
+ return desc->get_lease_configs_func (iface, uuid, ipv6);
return NULL;
}
@@ -522,22 +384,21 @@ static void
nm_dhcp_manager_init (NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ NMConfig *config = nm_config_get ();
const char *client;
GError *error = NULL;
-#if !HAVE_DBUS_GLIB_100
- DBusGConnection *g_connection;
-#endif
-
- /* Maps DBusGConnection :: DBusGProxy */
- priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+ GSList *iter;
/* Client-specific setup */
- client = nm_config_get_dhcp_client (nm_config_get ());
- priv->client_type = get_client_type (client, &error);
+ client = nm_config_get_dhcp_client (config);
+ if (nm_config_get_configure_and_quit (config)) {
+ if (g_strcmp0 (client, "internal") != 0)
+ nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
+ client = "internal";
+ }
- if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
- priv->get_lease_ip_configs_func = nm_dhcp_dhclient_get_lease_ip_configs;
- else if (priv->client_type == G_TYPE_INVALID) {
+ priv->client_type = get_client_type (client, &error);
+ if (priv->client_type == G_TYPE_INVALID) {
nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
error->message);
}
@@ -548,32 +409,12 @@ nm_dhcp_manager_init (NMDhcpManager *self)
(GDestroyNotify) g_object_unref);
g_assert (priv->clients);
- priv->dbus_mgr = nm_dbus_manager_get ();
-
-#if HAVE_DBUS_GLIB_100
- /* Register the socket our DHCP clients will return lease info on */
- nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
- priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
- NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
- (GCallback) new_connection_cb,
- self);
- priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
- NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
- (GCallback) dis_connection_cb,
- self);
-#else
- g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
- priv->proxy = dbus_g_proxy_new_for_name (g_connection,
- "org.freedesktop.nm_dhcp_client",
- "/",
- NM_DHCP_CLIENT_DBUS_IFACE);
- g_assert (priv->proxy);
- dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal (priv->proxy, "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- self,
- NULL);
-#endif
+ for (iter = client_descs; iter; iter = iter->next) {
+ ClientDesc *desc = iter->data;
+
+ nm_log_dbg (LOGD_DHCP, "Registered DHCP client '%s' (%s)",
+ desc->name, g_type_name (desc->gtype));
+ }
}
static void
@@ -589,23 +430,6 @@ dispose (GObject *object)
g_list_free (values);
}
- if (priv->new_conn_id) {
- g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
- priv->new_conn_id = 0;
- }
- if (priv->dis_conn_id) {
- g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
- priv->dis_conn_id = 0;
- }
- priv->dbus_mgr = NULL;
-
- if (priv->proxies) {
- g_hash_table_destroy (priv->proxies);
- priv->proxies = NULL;
- }
- if (priv->proxy)
- g_object_unref (priv->proxy);
-
G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
}