diff --git a/configure.ac b/configure.ac
index 6c70b458c..ecd70854f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -292,67 +292,73 @@ else
fi
AC_SUBST(PPPD_PLUGIN_DIR)
-# DHCP client
-AC_ARG_WITH([dhcp-client], AS_HELP_STRING([--with-dhcp-client=dhcpcd|dhclient], [path to the chosen dhcp client]))
+# dhclient support
+AC_ARG_WITH([dhclient], AS_HELP_STRING([--with-dhclient=yes|no|path], [Enable dhclient 4.x support]))
# If a full path is given, use that and do not test if it works or not.
-case "${with_dhcp_client}" in
+case "${with_dhclient}" in
/*)
- DHCP_CLIENT_PATH="${with_dhcp_client}"
- AC_MSG_NOTICE(using the DHCP client ${DHCP_CLIENT_PATH})
+ DHCLIENT_PATH="${with_dhclient}"
+ AC_MSG_NOTICE(using dhclient at ${DHCLIENT_PATH})
+ ;;
+ no) AC_MSG_NOTICE(dhclient support disabled)
+ ;;
+ *)
+ AC_MSG_CHECKING(for dhclient)
+ # NM only works with ISC dhclient - other derivatives don't have
+ # the same userland. NM also requires dhclient 4.x since older
+ # versions do not have IPv6 support.
+ for path in /sbin /usr/sbin /usr/pkg/sbin /usr/local/sbin; do
+ test -x "${path}/dhclient" || continue
+ case `"$path/dhclient" --version 2>&1` in
+ "isc-dhclient-4"*) DHCLIENT_PATH="$path/dhclient"; break;;
+ esac
+ done
+ if test -n "${DHCLIENT_PATH}"; then
+ AC_MSG_RESULT($DHCLIENT_PATH)
+ else
+ AC_MSG_RESULT(no)
+ fi
;;
esac
-if test -z "$DHCP_CLIENT_PATH" -a \( -z "$with_dhcp_client" -o x`basename "$with_dhcp_client"` = "xdhclient" \); then
- # We only work with ISC dhclient - the FreeBSD and OpenBSD derivatives don't have the same userland.
- AC_MSG_CHECKING(for dhclient)
- for client in "$with_dhcp_client" /sbin/dhclient /usr/pkg/sbin/dhclient /usr/local/sbin/dhclient; do
- test -x "$client" || continue
- case `"$client" --version 2>&1` in
- "isc-dhclient-"*) DHCP_CLIENT_PATH="$client"; break;;
- esac
- done
- if test -z "$DHCP_CLIENT_PATH"; then
- AC_MSG_RESULT(no)
- if test -n "$with_dhcp_client"; then
- AC_MSG_ERROR([Could not find ISC dhclient])
+
+# dhcpcd support
+AC_ARG_WITH([dhcpcd], AS_HELP_STRING([--with-dhcpcd=yes|no|path], [Enable dhcpcd 4.x support]))
+# If a full path is given, use that and do not test if it works or not.
+case "${with_dhcpcd}" in
+ /*)
+ DHCPCD_PATH="${with_dhcpcd}"
+ AC_MSG_NOTICE(using dhcpcd at ${DHCPCD_PATH})
+ ;;
+ no) AC_MSG_NOTICE(dhcpcd support disabled)
+ ;;
+ *)
+ AC_MSG_CHECKING(for dhcpcd)
+ # We fully work with upstream dhcpcd-4
+ for path in /sbin /usr/sbin /usr/pkg/sbin /usr/local/sbin; do
+ test -x "${path}/dhclient" || continue
+ case `"$path/dhcpcd" --version 2>/dev/null` in
+ "dhcpcd "[123]*);;
+ "dhcpcd "*) DHCP_CLIENT_PATH="$path/dhcpcd"; break;;
+ esac
+ done
+ if test -n "${DHCPCD_PATH}"; then
+ AC_MSG_RESULT($DHCPCD_PATH)
+ else
+ AC_MSG_RESULT(no)
fi
- else
- AC_MSG_RESULT($DHCP_CLIENT_PATH)
- fi
-fi
-if test -z "$DHCP_CLIENT_PATH" -a \( -z "$with_dhcp_client" -o x`basename "$with_dhcp_client"` = "xdhcpcd" \); then
- test -n "$DHCP_CLIENT_PATH" && echo bar
- # We fully work with upstream dhcpcd-4
- AC_MSG_CHECKING([for dhcpcd])
- for client in "$with_dhcp_client" /sbin/dhcpcd /usr/pkg/sbin/dhcpcd /usr/local/sbin/dhcpcd; do
- test -x "$client" || continue
- case `"$client" --version 2>/dev/null` in
- "dhcpcd "[123]*);;
- "dhcpcd "*) DHCP_CLIENT_PATH="$client"; break;;
- esac
- done
- if test -z "$DHCP_CLIENT_PATH"; then
- AC_MSG_RESULT(no)
- if test -n "$with_dhcp_client"; then
- AC_MSG_ERROR([Could not find dhcpcd-4 or newer])
- fi
- else
- AC_MSG_RESULT($DHCP_CLIENT_PATH)
- fi
-fi
-if test -z "$DHCP_CLIENT_PATH"; then
+ ;;
+esac
+
+if test -z "$DHCPCD_PATH" -a -z "$DHCLIENT_PATH"; then
# DHCP clients are not a build time dependency, only runtime.
# dhclient has been the longtime default for NM and it's in /sbin
# in most distros, so use it.
AC_MSG_WARN([Could not find a suitable DHCP client])
- DHCP_CLIENT_PATH=/sbin/dhclient
+ DHCLIENT_PATH=/sbin/dhclient
AC_MSG_WARN([Falling back to ISC dhclient, ${DHCP_CLIENT_PATH}])
fi
-AC_SUBST(DHCP_CLIENT_PATH)
-DHCP_CLIENT=`basename "$DHCP_CLIENT_PATH"`
-if test "$DHCP_CLIENT" != "dhclient" -a "$DHCP_CLIENT" != "dhcpcd"; then
- AC_MSG_ERROR([No backend for the DHCP client ${DHCP_CLIENT}])
-fi
-AC_SUBST(DHCP_CLIENT)
+AC_SUBST(DHCLIENT_PATH)
+AC_SUBST(DHCPCD_PATH)
# resolvconf support
AC_ARG_WITH([resolvconf],
@@ -500,12 +506,25 @@ examples/python/Makefile
AC_OUTPUT
echo
-echo Distribution targeting: ${with_distro}
+echo Distribution target: ${with_distro}
echo 'if this is not correct, please specifiy your distro with --with-distro=DISTRO'
+echo
+
+if test -n "${DHCLIENT_PATH}"; then
+ echo ISC dhclient support: ${DHCLIENT_PATH}
+else
+ echo ISC dhclient support: no
+fi
+
+if test -n "${DHCPCD_PATH}"; then
+ echo dhcpcd support: ${DHCPCD_PATH}
+else
+ echo dhcpcd support: no
+fi
+
echo
echo Building documentation: ${with_docs}
-echo
echo Building tests: ${with_tests}
echo
diff --git a/include/NetworkManager.h b/include/NetworkManager.h
index c8d5074a1..b25cfbc2b 100644
--- a/include/NetworkManager.h
+++ b/include/NetworkManager.h
@@ -42,6 +42,7 @@
#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config"
#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config"
#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config"
+#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config"
#define NM_DBUS_SERVICE_USER_SETTINGS "org.freedesktop.NetworkManagerUserSettings"
diff --git a/introspection/Makefile.am b/introspection/Makefile.am
index 0dc286a9c..212da3ab9 100644
--- a/introspection/Makefile.am
+++ b/introspection/Makefile.am
@@ -23,5 +23,6 @@ EXTRA_DIST = \
nm-vpn-connection.xml \
nm-ppp-manager.xml \
nm-active-connection.xml \
- nm-dhcp4-config.xml
+ nm-dhcp4-config.xml \
+ nm-dhcp6-config.xml
diff --git a/introspection/all.xml b/introspection/all.xml
index 67f1e5421..d6b2f2367 100644
--- a/introspection/all.xml
+++ b/introspection/all.xml
@@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
diff --git a/introspection/nm-dhcp6-config.xml b/introspection/nm-dhcp6-config.xml
new file mode 100644
index 000000000..93b0f1c8f
--- /dev/null
+++ b/introspection/nm-dhcp6-config.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Options and configuration returned by the IPv6 DHCP server.
+
+
+ Configuration options returned by a DHCP server, if any.
+
+
+
+
+
+ A dictionary mapping property names to variant boxed values
+
+
+
+
+
diff --git a/libnm-glib/Makefile.am b/libnm-glib/Makefile.am
index 2aa6124e7..c177fa3cb 100644
--- a/libnm-glib/Makefile.am
+++ b/libnm-glib/Makefile.am
@@ -20,7 +20,8 @@ BUILT_SOURCES = \
nm-active-connection-bindings.h \
nm-ip4-config-bindings.h \
nm-dhcp4-config-bindings.h \
- nm-ip6-config-bindings.h
+ nm-ip6-config-bindings.h \
+ nm-dhcp6-config-bindings.h
lib_LTLIBRARIES = libnm-glib.la libnm-glib-vpn.la
@@ -52,6 +53,7 @@ libnminclude_HEADERS = \
nm-active-connection.h \
nm-dhcp4-config.h \
nm-ip6-config.h \
+ nm-dhcp6-config.h \
nm-remote-connection.h \
nm-settings-interface.h \
nm-settings-system-interface.h \
@@ -86,6 +88,7 @@ libnm_glib_la_SOURCES = \
nm-active-connection.c \
nm-dhcp4-config.c \
nm-ip6-config.c \
+ nm-dhcp6-config.c \
nm-remote-connection.c \
nm-remote-connection-private.h \
nm-settings-interface.c \
@@ -172,6 +175,9 @@ nm-dhcp4-config-bindings.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml
nm-ip6-config-bindings.h: $(top_srcdir)/introspection/nm-ip6-config.xml
dbus-binding-tool --prefix=nm_ip6_config --mode=glib-client --output=$@ $<
+nm-dhcp6-config-bindings.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml
+ dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-client --output=$@ $<
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libnm-glib.pc libnm-glib-vpn.pc
diff --git a/libnm-glib/nm-device.c b/libnm-glib/nm-device.c
index 17caa99d5..a24eb06ef 100644
--- a/libnm-glib/nm-device.c
+++ b/libnm-glib/nm-device.c
@@ -59,6 +59,8 @@ typedef struct {
gboolean null_dhcp4_config;
NMIP6Config *ip6_config;
gboolean null_ip6_config;
+ NMDHCP6Config *dhcp6_config;
+ gboolean null_dhcp6_config;
NMDeviceState state;
GUdevClient *client;
@@ -79,6 +81,7 @@ enum {
PROP_STATE,
PROP_PRODUCT,
PROP_VENDOR,
+ PROP_DHCP6_CONFIG,
LAST_PROP
};
@@ -220,6 +223,46 @@ demarshal_ip6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpoint
return TRUE;
}
+static gboolean
+demarshal_dhcp6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+ const char *path;
+ NMDHCP6Config *config = NULL;
+ DBusGConnection *connection;
+
+ if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
+ return FALSE;
+
+ priv->null_dhcp6_config = FALSE;
+
+ path = g_value_get_boxed (value);
+ if (path) {
+ if (!strcmp (path, "/"))
+ priv->null_dhcp6_config = TRUE;
+ else {
+ config = NM_DHCP6_CONFIG (_nm_object_cache_get (path));
+ if (config)
+ config = g_object_ref (config);
+ else {
+ connection = nm_object_get_connection (object);
+ config = NM_DHCP6_CONFIG (nm_dhcp6_config_new (connection, path));
+ }
+ }
+ }
+
+ if (priv->dhcp6_config) {
+ g_object_unref (priv->dhcp6_config);
+ priv->dhcp6_config = NULL;
+ }
+
+ if (config)
+ priv->dhcp6_config = config;
+
+ _nm_object_queue_notify (object, NM_DEVICE_DHCP6_CONFIG);
+ return TRUE;
+}
+
static void
register_for_property_changed (NMDevice *device)
{
@@ -230,9 +273,10 @@ register_for_property_changed (NMDevice *device)
{ NM_DEVICE_DRIVER, _nm_object_demarshal_generic, &priv->driver },
{ NM_DEVICE_CAPABILITIES, _nm_object_demarshal_generic, &priv->capabilities },
{ NM_DEVICE_MANAGED, _nm_object_demarshal_generic, &priv->managed },
- { NM_DEVICE_IP4_CONFIG, demarshal_ip4_config, &priv->ip4_config },
- { NM_DEVICE_DHCP4_CONFIG, demarshal_dhcp4_config, &priv->dhcp4_config },
- { NM_DEVICE_IP6_CONFIG, demarshal_ip6_config, &priv->ip6_config },
+ { NM_DEVICE_IP4_CONFIG, demarshal_ip4_config, &priv->ip4_config },
+ { NM_DEVICE_DHCP4_CONFIG, demarshal_dhcp4_config, &priv->dhcp4_config },
+ { NM_DEVICE_IP6_CONFIG, demarshal_ip6_config, &priv->ip6_config },
+ { NM_DEVICE_DHCP6_CONFIG, demarshal_dhcp6_config, &priv->dhcp6_config },
{ NULL },
};
@@ -318,6 +362,8 @@ dispose (GObject *object)
g_object_unref (priv->dhcp4_config);
if (priv->ip6_config)
g_object_unref (priv->ip6_config);
+ if (priv->dhcp6_config)
+ g_object_unref (priv->dhcp6_config);
if (priv->client)
g_object_unref (priv->client);
@@ -371,6 +417,9 @@ get_property (GObject *object,
case PROP_IP6_CONFIG:
g_value_set_object (value, nm_device_get_ip6_config (device));
break;
+ case PROP_DHCP6_CONFIG:
+ g_value_set_object (value, nm_device_get_dhcp6_config (device));
+ break;
case PROP_STATE:
g_value_set_uint (value, nm_device_get_state (device));
break;
@@ -505,6 +554,19 @@ nm_device_class_init (NMDeviceClass *device_class)
NM_TYPE_IP6_CONFIG,
G_PARAM_READABLE));
+ /**
+ * NMDevice:dhcp6-config:
+ *
+ * The #NMDHCP6Config of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP6_CONFIG,
+ g_param_spec_object (NM_DEVICE_DHCP6_CONFIG,
+ "DHCP6 Config",
+ "DHCP6 Config",
+ NM_TYPE_DHCP6_CONFIG,
+ G_PARAM_READABLE));
+
/**
* NMDevice:state:
*
@@ -870,6 +932,41 @@ nm_device_get_ip6_config (NMDevice *device)
return priv->ip6_config;
}
+/**
+ * nm_device_get_dhcp6_config:
+ * @device: a #NMDevice
+ *
+ * Gets the current #NMDHCP6Config associated with the #NMDevice.
+ *
+ * Returns: the #NMDHCPConfig or %NULL if the device is not activated or not
+ * using DHCP.
+ **/
+NMDHCP6Config *
+nm_device_get_dhcp6_config (NMDevice *device)
+{
+ NMDevicePrivate *priv;
+ char *path;
+ GValue value = { 0, };
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+ if (priv->dhcp6_config)
+ return priv->dhcp6_config;
+ if (priv->null_dhcp6_config)
+ return NULL;
+
+ path = _nm_object_get_object_path_property (NM_OBJECT (device), NM_DBUS_INTERFACE_DEVICE, "Dhcp6Config");
+ if (path) {
+ g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
+ g_value_take_boxed (&value, path);
+ demarshal_dhcp6_config (NM_OBJECT (device), NULL, &value, &priv->dhcp6_config);
+ g_value_unset (&value);
+ }
+
+ return priv->dhcp6_config;
+}
+
/**
* nm_device_get_state:
* @device: a #NMDevice
diff --git a/libnm-glib/nm-device.h b/libnm-glib/nm-device.h
index da015d6c4..64694ec83 100644
--- a/libnm-glib/nm-device.h
+++ b/libnm-glib/nm-device.h
@@ -32,6 +32,7 @@
#include "nm-ip4-config.h"
#include "nm-dhcp4-config.h"
#include "nm-ip6-config.h"
+#include "nm-dhcp6-config.h"
#include "nm-connection.h"
G_BEGIN_DECLS
@@ -51,6 +52,7 @@ G_BEGIN_DECLS
#define NM_DEVICE_IP4_CONFIG "ip4-config"
#define NM_DEVICE_DHCP4_CONFIG "dhcp4-config"
#define NM_DEVICE_IP6_CONFIG "ip6-config"
+#define NM_DEVICE_DHCP6_CONFIG "dhcp6-config"
#define NM_DEVICE_STATE "state"
#define NM_DEVICE_VENDOR "vendor"
#define NM_DEVICE_PRODUCT "product"
@@ -89,6 +91,7 @@ gboolean nm_device_get_managed (NMDevice *device);
NMIP4Config * nm_device_get_ip4_config (NMDevice *device);
NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *device);
NMIP6Config * nm_device_get_ip6_config (NMDevice *device);
+NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *device);
NMDeviceState nm_device_get_state (NMDevice *device);
const char * nm_device_get_product (NMDevice *device);
const char * nm_device_get_vendor (NMDevice *device);
diff --git a/libnm-glib/nm-dhcp6-config.c b/libnm-glib/nm-dhcp6-config.c
new file mode 100644
index 000000000..49eeda39e
--- /dev/null
+++ b/libnm-glib/nm-dhcp6-config.c
@@ -0,0 +1,248 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * libnm_glib -- Access network status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
+ */
+
+#include
+
+#include "nm-dhcp6-config.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-utils.h"
+
+G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, NM_TYPE_OBJECT)
+
+#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ GHashTable *options;
+} NMDHCP6ConfigPrivate;
+
+enum {
+ PROP_0,
+ PROP_OPTIONS,
+
+ LAST_PROP
+};
+
+static void
+nm_dhcp6_config_init (NMDHCP6Config *config)
+{
+}
+
+static void
+copy_options (gpointer key, gpointer data, gpointer user_data)
+{
+ GHashTable *options = (GHashTable *) user_data;
+ GValue *value = (GValue *) data;
+
+ g_hash_table_insert (options, g_strdup (key), g_value_dup_string (value));
+}
+
+static gboolean
+demarshal_dhcp6_options (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+ GHashTable *new_options;
+
+ g_hash_table_remove_all (priv->options);
+
+ new_options = g_value_get_boxed (value);
+ if (new_options)
+ g_hash_table_foreach (new_options, copy_options, priv->options);
+
+ _nm_object_queue_notify (object, NM_DHCP6_CONFIG_OPTIONS);
+ return TRUE;
+}
+
+static void
+register_for_property_changed (NMDHCP6Config *config)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (config);
+ const NMPropertiesChangedInfo property_changed_info[] = {
+ { NM_DHCP6_CONFIG_OPTIONS, demarshal_dhcp6_options, &priv->options },
+ { NULL },
+ };
+
+ _nm_object_handle_properties_changed (NM_OBJECT (config),
+ priv->proxy,
+ property_changed_info);
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ NMObject *object;
+ DBusGConnection *connection;
+ NMDHCP6ConfigPrivate *priv;
+
+ object = (NMObject *) G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+ priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ connection = nm_object_get_connection (object);
+
+ priv->proxy = dbus_g_proxy_new_for_name (connection,
+ NM_DBUS_SERVICE,
+ nm_object_get_path (object),
+ NM_DBUS_INTERFACE_DHCP6_CONFIG);
+
+ register_for_property_changed (NM_DHCP6_CONFIG (object));
+
+ return G_OBJECT (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+ if (priv->options)
+ g_hash_table_destroy (priv->options);
+
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDHCP6Config *self = NM_DHCP6_CONFIG (object);
+
+ switch (prop_id) {
+ case PROP_OPTIONS:
+ g_value_set_boxed (value, nm_dhcp6_config_get_options (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+ g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate));
+
+ /* virtual methods */
+ object_class->constructor = constructor;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMDHCP6Config:options:
+ *
+ * The #GHashTable containing options of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_OPTIONS,
+ g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS,
+ "Options",
+ "Options",
+ G_TYPE_HASH_TABLE,
+ G_PARAM_READABLE));
+}
+
+/**
+ * nm_dhcp6_config_new:
+ * @connection: the #DBusGConnection
+ * @object_path: the DBus object path of the device
+ *
+ * Creates a new #NMDHCP6Config.
+ *
+ * Returns: a new configuration
+ **/
+GObject *
+nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path)
+{
+ return (GObject *) g_object_new (NM_TYPE_DHCP6_CONFIG,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, object_path,
+ NULL);
+}
+
+/**
+ * nm_dhcp6_config_get_options:
+ * @config: a #NMDHCP6Config
+ *
+ * Gets all the options contained in the configuration.
+ *
+ * Returns: the #GHashTable containing strings for keys and values.
+ * This is the internal copy used by the configuration, and must not be modified.
+ **/
+GHashTable *
+nm_dhcp6_config_get_options (NMDHCP6Config *config)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (config);
+ GValue value = { 0, };
+
+ if (g_hash_table_size (priv->options))
+ return priv->options;
+
+ if (!_nm_object_get_property (NM_OBJECT (config),
+ "org.freedesktop.DBus.Properties",
+ "Options",
+ &value))
+ goto out;
+
+ demarshal_dhcp6_options (NM_OBJECT (config), NULL, &value, &priv->options);
+ g_value_unset (&value);
+
+out:
+ return priv->options;
+}
+
+/**
+ * nm_dhcp6_config_get_one_option:
+ * @config: a #NMDHCP6Config
+ * @option: the option to retrieve
+ *
+ * Gets one option by option name.
+ *
+ * Returns: the configuration option's value. This is the internal string used by the
+ * configuration, and must not be modified.
+ **/
+const char *
+nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option)
+{
+ g_return_val_if_fail (NM_IS_DHCP6_CONFIG (config), NULL);
+
+ return g_hash_table_lookup (nm_dhcp6_config_get_options (config), option);
+}
+
diff --git a/libnm-glib/nm-dhcp6-config.h b/libnm-glib/nm-dhcp6-config.h
new file mode 100644
index 000000000..91f32fa03
--- /dev/null
+++ b/libnm-glib/nm-dhcp6-config.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * libnm_glib -- Access network status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
+ */
+
+#ifndef NM_DHCP6_CONFIG_H
+#define NM_DHCP6_CONFIG_H
+
+#include
+#include
+#include
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type ())
+#define NM_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config))
+#define NM_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass))
+#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+
+typedef struct {
+ NMObject parent;
+} NMDHCP6Config;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDHCP6ConfigClass;
+
+#define NM_DHCP6_CONFIG_OPTIONS "options"
+
+GType nm_dhcp6_config_get_type (void);
+
+GObject *nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path);
+
+GHashTable * nm_dhcp6_config_get_options (NMDHCP6Config *config);
+
+const char * nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option);
+
+G_END_DECLS
+
+#endif /* NM_DHCP6_CONFIG_H */
diff --git a/libnm-util/nm-setting-ip6-config.c b/libnm-util/nm-setting-ip6-config.c
index b1577f06f..a62b69741 100644
--- a/libnm-util/nm-setting-ip6-config.c
+++ b/libnm-util/nm-setting-ip6-config.c
@@ -19,7 +19,7 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
*/
#include
@@ -435,26 +435,23 @@ verify (NMSetting *setting, GSList *all_settings, GError **error)
NM_SETTING_IP6_CONFIG_ADDRESSES);
return FALSE;
}
- } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
- || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)
+ } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)
|| !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)
|| !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
- if (!priv->ignore_auto_dns) {
- if (priv->dns && g_slist_length (priv->dns)) {
- g_set_error (error,
- NM_SETTING_IP6_CONFIG_ERROR,
- NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
- NM_SETTING_IP6_CONFIG_DNS);
- return FALSE;
- }
+ if (g_slist_length (priv->dns)) {
+ g_set_error (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ NM_SETTING_IP6_CONFIG_DNS);
+ return FALSE;
+ }
- if (g_slist_length (priv->dns_search)) {
- g_set_error (error,
- NM_SETTING_IP6_CONFIG_ERROR,
- NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
- NM_SETTING_IP6_CONFIG_DNS_SEARCH);
- return FALSE;
- }
+ if (g_slist_length (priv->dns_search)) {
+ g_set_error (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+ return FALSE;
}
if (g_slist_length (priv->addresses)) {
@@ -464,6 +461,9 @@ verify (NMSetting *setting, GSList *all_settings, GError **error)
NM_SETTING_IP6_CONFIG_ADDRESSES);
return FALSE;
}
+ } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
+ || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
+ /* nothing to do */
} else {
g_set_error (error,
NM_SETTING_IP6_CONFIG_ERROR,
@@ -598,30 +598,33 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
*
* IPv6 configuration method. If 'auto' is specified then the appropriate
* automatic method (DHCP, PPP, advertisement, etc) is used for the
- * interface and most other properties can be left unset. If 'link-local'
- * is specified, then an IPv6 link-local address will be assigned to the
- * interface. If 'manual' is specified, static IP addressing is used and
- * at least one IP address must be given in the 'addresses' property. If
- * 'ignored' is specified, IPv6 configuration is not done. This property
- * must be set. NOTE: DHCP configuration and the 'shared' method are not
- * yet supported.
+ * interface and most other properties can be left unset. To force the use
+ * of DHCP only, specify 'dhcp'; this method is only valid for ethernet-
+ * based hardware. If 'link-local' is specified, then an IPv6 link-local
+ * address will be assigned to the interface. If 'manual' is specified,
+ * static IP addressing is used and at least one IP address must be given
+ * in the 'addresses' property. If 'ignored' is specified, IPv6
+ * configuration is not done. This property must be set. NOTE: the 'shared'
+ * method are not yet supported.
**/
g_object_class_install_property
(object_class, PROP_METHOD,
g_param_spec_string (NM_SETTING_IP6_CONFIG_METHOD,
"Method",
"IPv6 configuration method. If 'auto' is specified "
- "then the appropriate automatic method (DHCP, PPP, "
+ "then the appropriate automatic method (PPP, router "
"advertisement, etc) is used for the device and "
- "most other properties can be left unset. If "
+ "most other properties can be left unset. To force "
+ "the use of DHCP only, specify 'dhcp'; this method "
+ "is only valid for ethernet-based hardware. If "
"'link-local' is specified, then an IPv6 link-local "
"address will be assigned to the interface. If "
"'manual' is specified, static IP addressing is "
"used and at least one IP address must be given in "
" the 'addresses' property. If 'ignored' is "
"specified, IPv6 configuration is not done. This "
- "property must be set. NOTE: DHCP configuration "
- "and the 'shared' method are not yet supported.",
+ "property must be set. NOTE: the 'shared' method"
+ "is not yet supported.",
NULL,
G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
@@ -740,38 +743,39 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
/**
* NMSettingIP6Config:ignore-auto-routes:
*
- * When the method is set to 'auto' and this property to TRUE, automatically
- * configured routes are ignored and only routes specified in
- * #NMSettingIP6Config:routes, if any, are used.
+ * When the method is set to 'auto' or 'dhcp' and this property is set to
+ * TRUE, automatically configured routes are ignored and only routes
+ * specified in #NMSettingIP6Config:routes, if any, are used.
**/
g_object_class_install_property
(object_class, PROP_IGNORE_AUTO_ROUTES,
g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
"Ignore automatic routes",
- "When the method is set to 'auto' and this property "
- "to TRUE, automatically configured routes are "
- "ignored and only routes specified in the 'routes' "
- "property, if any, are used.",
+ "When the method is set to 'auto' or 'dhcp' and this "
+ "property is set to TRUE, automatically configured "
+ "routes are ignored and only routes specified in the "
+ "'routes' property, if any, are used.",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
/**
* NMSettingIP6Config:ignore-auto-dns:
*
- * When the method is set to 'auto' and this property to TRUE, automatically
- * configured nameservers and search domains are ignored and only namservers
- * and search domains specified in #NMSettingIP6Config:dns and
- * #NMSettingIP6Config:dns-search, if any, are used.
+ * When the method is set to 'auto' or 'dhcp' and this property is set to
+ * TRUE, automatically configured nameservers and search domains are ignored
+ * and only namservers and search domains specified in
+ * #NMSettingIP6Config:dns and #NMSettingIP6Config:dns-search, if any, are
+ * used.
**/
g_object_class_install_property
(object_class, PROP_IGNORE_AUTO_DNS,
g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS,
"Ignore DHCPv6/RDNSS DNS",
- "When the method is set to 'auto' and this property "
- "to TRUE, automatically configured nameservers and "
- "search domains are ignored and only namservers and "
- "search domains specified in the 'dns' and 'dns-search' "
- "properties, if any, are used.",
+ "When the method is set to 'auto' or 'dhcp' and this "
+ "property is set to TRUE, automatically configured "
+ "nameservers and search domains are ignored and only "
+ "namservers and search domains specified in the 'dns' "
+ "and 'dns-search' properties, if any, are used.",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
diff --git a/libnm-util/nm-setting-ip6-config.h b/libnm-util/nm-setting-ip6-config.h
index 18082e4ea..b089679e4 100644
--- a/libnm-util/nm-setting-ip6-config.h
+++ b/libnm-util/nm-setting-ip6-config.h
@@ -19,7 +19,7 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
*/
#ifndef NM_SETTING_IP6_CONFIG_H
@@ -65,6 +65,7 @@ GQuark nm_setting_ip6_config_error_quark (void);
#define NM_SETTING_IP6_CONFIG_METHOD_IGNORE "ignore"
#define NM_SETTING_IP6_CONFIG_METHOD_AUTO "auto"
+#define NM_SETTING_IP6_CONFIG_METHOD_DHCP "dhcp"
#define NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL "link-local"
#define NM_SETTING_IP6_CONFIG_METHOD_MANUAL "manual"
#define NM_SETTING_IP6_CONFIG_METHOD_SHARED "shared"
diff --git a/libnm-util/tests/test-settings-defaults.c b/libnm-util/tests/test-settings-defaults.c
index 7f0adc561..9f38a1455 100644
--- a/libnm-util/tests/test-settings-defaults.c
+++ b/libnm-util/tests/test-settings-defaults.c
@@ -118,7 +118,7 @@ int main (int argc, char **argv)
test_defaults (NM_TYPE_SETTING_CDMA, NM_SETTING_CDMA_SETTING_NAME);
test_defaults (NM_TYPE_SETTING_GSM, NM_SETTING_GSM_SETTING_NAME);
test_defaults (NM_TYPE_SETTING_IP4_CONFIG, NM_SETTING_IP4_CONFIG_SETTING_NAME);
-// test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME);
test_defaults (NM_TYPE_SETTING_PPP, NM_SETTING_PPP_SETTING_NAME);
test_defaults (NM_TYPE_SETTING_PPPOE, NM_SETTING_PPPOE_SETTING_NAME);
test_defaults (NM_TYPE_SETTING_SERIAL, NM_SETTING_SERIAL_SETTING_NAME);
diff --git a/marshallers/nm-marshal.list b/marshallers/nm-marshal.list
index 38669dd32..36e0fbe6d 100644
--- a/marshallers/nm-marshal.list
+++ b/marshallers/nm-marshal.list
@@ -16,6 +16,7 @@ VOID:STRING,STRING,STRING,UINT
VOID:OBJECT,UINT,UINT
VOID:STRING,INT
VOID:STRING,UINT
+VOID:STRING,UINT,BOOLEAN
VOID:OBJECT,OBJECT,ENUM
VOID:POINTER,STRING
VOID:STRING,BOXED
diff --git a/src/Makefile.am b/src/Makefile.am
index f351c787d..db255ca35 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,6 +37,7 @@ noinst_LTLIBRARIES = libtest-dhcp.la
libtest_dhcp_la_SOURCES = \
nm-ip4-config.c \
+ nm-ip6-config.c \
nm-hostname-provider.c \
nm-dbus-manager.c
@@ -46,6 +47,7 @@ libtest_dhcp_la_CPPFLAGS = \
$(LIBNL_CFLAGS)
libtest_dhcp_la_LIBADD = \
+ $(top_builddir)/marshallers/libmarshallers.la \
$(top_builddir)/libnm-util/libnm-util.la \
$(GLIB_LIBS) \
$(DBUS_LIBS) \
@@ -113,6 +115,8 @@ NetworkManager_SOURCES = \
nm-netlink.h \
nm-dhcp4-config.c \
nm-dhcp4-config.h \
+ nm-dhcp6-config.c \
+ nm-dhcp6-config.h \
nm-rfkill.h
nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml
@@ -148,6 +152,9 @@ nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xm
nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml
dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $<
+nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml
+ dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $<
+
BUILT_SOURCES = \
nm-access-point-glue.h \
nm-manager-glue.h \
@@ -159,7 +166,8 @@ BUILT_SOURCES = \
nm-ip4-config-glue.h \
nm-ip6-config-glue.h \
nm-active-connection-glue.h \
- nm-dhcp4-config-glue.h
+ nm-dhcp4-config-glue.h \
+ nm-dhcp6-config-glue.h
NetworkManager_CPPFLAGS = \
$(DBUS_CFLAGS) \
diff --git a/src/NetworkManager.c b/src/NetworkManager.c
index 09a4320c7..a6639b8c8 100644
--- a/src/NetworkManager.c
+++ b/src/NetworkManager.c
@@ -302,7 +302,10 @@ done:
}
static gboolean
-parse_config_file (const char *filename, char **plugins, GError **error)
+parse_config_file (const char *filename,
+ char **plugins,
+ char **dhcp_client,
+ GError **error)
{
GKeyFile *config;
@@ -321,6 +324,8 @@ parse_config_file (const char *filename, char **plugins, GError **error)
if (*error)
return FALSE;
+ *dhcp_client = g_key_file_get_value (config, "main", "dhcp", NULL);
+
g_key_file_free (config);
return TRUE;
}
@@ -435,7 +440,7 @@ main (int argc, char *argv[])
gboolean become_daemon = FALSE;
gboolean g_fatal_warnings = FALSE;
char *pidfile = NULL, *user_pidfile = NULL;
- char *config = NULL, *plugins = NULL;
+ char *config = NULL, *plugins = NULL, *dhcp = NULL;
char *state_file = NM_DEFAULT_SYSTEM_STATE_FILE;
gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE;
gboolean success;
@@ -498,7 +503,7 @@ main (int argc, char *argv[])
/* Parse the config file */
if (config) {
- if (!parse_config_file (config, &plugins, &error)) {
+ if (!parse_config_file (config, &plugins, &dhcp, &error)) {
g_warning ("Config file %s invalid: (%d) %s.",
config,
error ? error->code : -1,
@@ -507,7 +512,7 @@ main (int argc, char *argv[])
}
} else {
config = NM_DEFAULT_SYSTEM_CONF_FILE;
- if (!parse_config_file (config, &plugins, &error)) {
+ if (!parse_config_file (config, &plugins, &dhcp, &error)) {
g_warning ("Default config file %s invalid: (%d) %s.",
config,
error ? error->code : -1,
@@ -620,9 +625,9 @@ main (int argc, char *argv[])
goto done;
}
- dhcp_mgr = nm_dhcp_manager_get ();
+ dhcp_mgr = nm_dhcp_manager_new (dhcp ? dhcp : "dhclient", &error);
if (!dhcp_mgr) {
- nm_warning ("Failed to start the DHCP manager.");
+ nm_warning ("Failed to start the DHCP manager: %s.", error->message);
goto done;
}
diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am
index b7b0c6581..ff9041c39 100644
--- a/src/dhcp-manager/Makefile.am
+++ b/src/dhcp-manager/Makefile.am
@@ -9,9 +9,14 @@ INCLUDES = \
noinst_LTLIBRARIES = libdhcp-manager.la
libdhcp_manager_la_SOURCES = \
+ nm-dhcp-client.c \
+ nm-dhcp-client.h \
nm-dhcp-manager.c \
nm-dhcp-manager.h \
- nm-dhcp-@DHCP_CLIENT@.c
+ nm-dhcp-dhclient.h \
+ nm-dhcp-dhclient.c \
+ nm-dhcp-dhcpcd.h \
+ nm-dhcp-dhcpcd.c
libdhcp_manager_la_CPPFLAGS = \
$(DBUS_CFLAGS) \
@@ -22,13 +27,11 @@ libdhcp_manager_la_CPPFLAGS = \
-DSYSCONFDIR=\"$(sysconfdir)\" \
-DLIBEXECDIR=\"$(libexecdir)\" \
-DLOCALSTATEDIR=\"$(localstatedir)\" \
- -DDHCP_CLIENT_PATH=\"$(DHCP_CLIENT_PATH)\"
+ -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+ -DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
libdhcp_manager_la_LIBADD = \
$(top_builddir)/marshallers/libmarshallers.la \
$(DBUS_LIBS) \
$(GLIB_LIBS)
-EXTRA_DIST = \
- nm-dhcp-dhclient.c \
- nm-dhcp-dhcpcd.c
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
new file mode 100644
index 000000000..a47f929a5
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -0,0 +1,1114 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 - 2010 Red Hat, Inc.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-dhcp-client.h"
+
+typedef struct {
+ char * iface;
+ gboolean ipv6;
+ char * uuid;
+ guint32 timeout;
+
+ guchar state;
+ GPid pid;
+ guint timeout_id;
+ guint watch_id;
+ GHashTable * options;
+} NMDHCPClientPrivate;
+
+#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDHCPClientPrivate))
+
+G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+
+enum {
+ STATE_CHANGED,
+ TIMEOUT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+ PROP_IFACE,
+ PROP_IPV6,
+ PROP_UUID,
+ PROP_TIMEOUT,
+ LAST_PROP
+};
+
+/********************************************/
+
+GPid
+nm_dhcp_client_get_pid (NMDHCPClient *self)
+{
+ g_return_val_if_fail (self != NULL, -1);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid;
+}
+
+const char *
+nm_dhcp_client_get_iface (NMDHCPClient *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface;
+}
+
+gboolean
+nm_dhcp_client_get_ipv6 (NMDHCPClient *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6;
+}
+
+const char *
+nm_dhcp_client_get_uuid (NMDHCPClient *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid;
+}
+
+/********************************************/
+
+static void
+timeout_cleanup (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (priv->timeout_id) {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+}
+
+static void
+watch_cleanup (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (priv->watch_id) {
+ g_source_remove (priv->watch_id);
+ priv->watch_id = 0;
+ }
+}
+
+static void
+stop_process (GPid pid, const char *iface)
+{
+ int i = 15; /* 3 seconds */
+
+ g_return_if_fail (pid > 0);
+
+ /* Tell it to quit; maybe it wants to send out a RELEASE message */
+ kill (pid, SIGTERM);
+
+ while (i-- > 0) {
+ gint child_status;
+ int ret;
+
+ ret = waitpid (pid, &child_status, WNOHANG);
+ if (ret > 0)
+ break;
+
+ if (ret == -1) {
+ /* Child already exited */
+ if (errno == ECHILD)
+ break;
+ /* Took too long; shoot it in the head */
+ i = 0;
+ break;
+ }
+ g_usleep (G_USEC_PER_SEC / 5);
+ }
+
+ if (i <= 0) {
+ if (iface) {
+ g_warning ("%s: dhcp client pid %d didn't exit, will kill it.",
+ iface, pid);
+ }
+ kill (pid, SIGKILL);
+
+ g_warning ("waiting for dhcp client pid %d to exit", pid);
+ waitpid (pid, NULL, 0);
+ g_warning ("dhcp client pid %d cleaned up", pid);
+ }
+}
+
+static void
+real_stop (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+ g_return_if_fail (priv->pid > 0);
+
+ /* Clean up the watch handler since we're explicitly killing the daemon */
+ watch_cleanup (self);
+
+ stop_process (priv->pid, priv->iface);
+}
+
+static gboolean
+daemon_timeout (gpointer user_data)
+{
+ NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ g_message ("(%s): DHCPv%c request timed out.",
+ priv->iface, priv->ipv6 ? '6' : '4');
+ g_signal_emit (G_OBJECT (self), signals[TIMEOUT], 0);
+ return FALSE;
+}
+
+static void
+daemon_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+ NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (!WIFEXITED (status)) {
+ priv->state = DHC_ABEND;
+ g_warning ("dhcp client died abnormally");
+ }
+ priv->pid = 0;
+
+ watch_cleanup (self);
+ timeout_cleanup (self);
+
+ g_signal_emit (G_OBJECT (self), signals[STATE_CHANGED], 0, priv->state);
+}
+
+static void
+start_monitor (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->pid > 0);
+
+ /* Set up a timeout on the transaction to kill it after the timeout */
+ priv->timeout_id = g_timeout_add_seconds (priv->timeout,
+ daemon_timeout,
+ self);
+ priv->watch_id = g_child_watch_add (priv->pid,
+ (GChildWatchFunc) daemon_watch_cb,
+ self);
+}
+
+gboolean
+nm_dhcp_client_start_ip4 (NMDHCPClient *self,
+ NMSettingIP4Config *s_ip4,
+ guint8 *dhcp_anycast_addr)
+{
+ NMDHCPClientPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+ g_return_val_if_fail (priv->pid == -1, FALSE);
+ g_return_val_if_fail (priv->ipv6 == FALSE, FALSE);
+ g_return_val_if_fail (priv->uuid != NULL, FALSE);
+
+ g_message ("Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
+ priv->iface, priv->timeout);
+
+ priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, s_ip4, dhcp_anycast_addr);
+ if (priv->pid)
+ start_monitor (self);
+
+ return priv->pid ? TRUE : FALSE;
+}
+
+gboolean
+nm_dhcp_client_start_ip6 (NMDHCPClient *self,
+ NMSettingIP6Config *s_ip6,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only)
+{
+ NMDHCPClientPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+ g_return_val_if_fail (priv->pid == -1, FALSE);
+ g_return_val_if_fail (priv->ipv6 == TRUE, FALSE);
+ g_return_val_if_fail (priv->uuid != NULL, FALSE);
+
+ g_message ("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, info_only);
+ if (priv->pid > 0)
+ start_monitor (self);
+
+ return priv->pid ? TRUE : FALSE;
+}
+
+void
+nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
+{
+ char *pid_contents = NULL, *proc_contents = NULL, *proc_path = NULL;
+ long int tmp;
+
+ /* Check for an existing instance and stop it */
+ if (!g_file_get_contents (pid_file, &pid_contents, NULL, NULL))
+ return;
+
+ errno = 0;
+ tmp = strtol (pid_contents, NULL, 10);
+ if ((errno == 0) && (tmp > 1)) {
+ const char *exe;
+
+ /* Ensure the process is a DHCP client */
+ proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp);
+ if (g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) {
+ exe = strrchr (proc_contents, '/');
+ if (exe)
+ exe++;
+ else
+ exe = proc_contents;
+
+ if (!strcmp (exe, binary_name))
+ stop_process ((GPid) tmp, NULL);
+ }
+ }
+
+ remove (pid_file);
+ g_free (proc_path);
+ g_free (pid_contents);
+ g_free (proc_contents);
+}
+
+void
+nm_dhcp_client_stop (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ /* Kill the DHCP client */
+ if (priv->pid > 0) {
+ NM_DHCP_CLIENT_GET_CLASS (self)->stop (self);
+
+ g_message ("(%s): canceled DHCP transaction, dhcp client pid %d",
+ priv->iface,
+ priv->pid);
+ }
+
+ /* And clean stuff up */
+
+ priv->pid = -1;
+ priv->state = DHC_END;
+
+ g_hash_table_remove_all (priv->options);
+
+ timeout_cleanup (self);
+ watch_cleanup (self);
+}
+
+/********************************************/
+
+static gboolean
+state_is_bound (guint32 state)
+{
+ if ( (state == DHC_BOUND4)
+ || (state == DHC_BOUND6)
+ || (state == DHC_RENEW4)
+ || (state == DHC_RENEW6)
+ || (state == DHC_REBOOT)
+ || (state == DHC_REBIND4)
+ || (state == DHC_REBIND6)
+ || (state == DHC_IPV4LL))
+ return TRUE;
+
+ return FALSE;
+}
+
+typedef struct {
+ NMDHCPState state;
+ const char *name;
+} DhcState;
+
+#define STATE_TABLE_SIZE (sizeof (state_table) / sizeof (state_table[0]))
+
+static DhcState state_table[] = {
+ { DHC_NBI, "nbi" },
+ { DHC_PREINIT, "preinit" },
+ { DHC_PREINIT6,"preinit6" },
+ { DHC_BOUND4, "bound" },
+ { DHC_BOUND6, "bound6" },
+ { DHC_IPV4LL, "ipv4ll" },
+ { DHC_RENEW4, "renew" },
+ { DHC_RENEW6, "renew6" },
+ { DHC_REBOOT, "reboot" },
+ { DHC_REBIND4, "rebind" },
+ { DHC_REBIND6, "rebind6" },
+ { DHC_STOP, "stop" },
+ { DHC_MEDIUM, "medium" },
+ { DHC_TIMEOUT, "timeout" },
+ { DHC_FAIL, "fail" },
+ { DHC_EXPIRE, "expire" },
+ { DHC_RELEASE, "release" },
+ { DHC_START, "start" },
+ { DHC_ABEND, "abend" },
+ { DHC_END, "end" },
+ { DHC_DEPREF6, "depref6" },
+};
+
+static inline const char *
+state_to_string (guint32 state)
+{
+ int i;
+
+ for (i = 0; i < STATE_TABLE_SIZE; i++) {
+ if (state == state_table[i].state)
+ return state_table[i].name;
+ }
+
+ return NULL;
+}
+
+static inline NMDHCPState
+string_to_state (const char *name)
+{
+ int i;
+
+ for (i = 0; i < STATE_TABLE_SIZE; i++) {
+ if (!strcasecmp (name, state_table[i].name))
+ return state_table[i].state;
+ }
+
+ return 255;
+}
+
+static char *
+garray_to_string (GArray *array, const char *key)
+{
+ GString *str;
+ int i;
+ unsigned char c;
+ char *converted = NULL;
+
+ g_return_val_if_fail (array != NULL, 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);
+ }
+ str = g_string_append_c (str, '\0');
+
+ converted = str->str;
+ if (!g_utf8_validate (converted, -1, NULL))
+ g_warning ("%s: DHCP option '%s' couldn't be converted to UTF-8", __func__, key);
+ g_string_free (str, FALSE);
+ return converted;
+}
+
+static void
+copy_option (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GHashTable *hash = user_data;
+ const char *str_key = (const char *) key;
+ char *str_value = NULL;
+
+ if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
+ g_warning ("Unexpected key %s value type was not "
+ "DBUS_TYPE_G_UCHAR_ARRAY",
+ str_key);
+ return;
+ }
+
+ str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key);
+ if (str_value)
+ g_hash_table_insert (hash, g_strdup (str_key), str_value);
+}
+
+void
+nm_dhcp_client_new_options (NMDHCPClient *self,
+ GHashTable *options,
+ const char *reason)
+{
+ NMDHCPClientPrivate *priv;
+ guint32 old_state;
+ guint32 new_state;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+ g_return_if_fail (options != NULL);
+ g_return_if_fail (reason != NULL);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+ old_state = priv->state;
+ new_state = string_to_state (reason);
+
+ /* Clear old and save new DHCP options */
+ g_hash_table_remove_all (priv->options);
+ g_hash_table_foreach (options, copy_option, priv->options);
+
+ if (old_state == new_state)
+ return;
+
+ /* Handle changed device state */
+ if (state_is_bound (new_state)) {
+ /* Cancel the timeout if the DHCP client is now bound */
+ timeout_cleanup (self);
+ }
+
+ priv->state = new_state;
+ g_message ("DHCPv%c: device %s state changed %s -> %s",
+ priv->ipv6 ? '6' : '4',
+ priv->iface,
+ state_to_string (old_state),
+ state_to_string (priv->state));
+
+ g_signal_emit (G_OBJECT (self),
+ signals[STATE_CHANGED],
+ 0,
+ priv->state);
+}
+
+#define NEW_TAG "new_"
+#define OLD_TAG "old_"
+
+typedef struct {
+ GHFunc func;
+ gpointer user_data;
+} DhcpForeachInfo;
+
+static void
+iterate_dhcp_config_option (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ DhcpForeachInfo *info = (DhcpForeachInfo *) user_data;
+ char *tmp_key = NULL;
+ const char **p;
+ static const char *filter_options[] = {
+ "interface", "pid", "reason", "dhcp_message_type", NULL
+ };
+
+ /* Filter out stuff that's not actually new DHCP options */
+ for (p = filter_options; *p; p++) {
+ if (!strcmp (*p, (const char *) key))
+ return;
+ if (!strncmp ((const char *) key, OLD_TAG, strlen (OLD_TAG)))
+ return;
+ }
+
+ /* Remove the "new_" prefix that dhclient passes back */
+ if (!strncmp ((const char *) key, NEW_TAG, strlen (NEW_TAG)))
+ tmp_key = g_strdup ((const char *) (key + strlen (NEW_TAG)));
+ else
+ tmp_key = g_strdup ((const char *) key);
+
+ (*info->func) ((gpointer) tmp_key, value, info->user_data);
+ g_free (tmp_key);
+}
+
+gboolean
+nm_dhcp_client_foreach_option (NMDHCPClient *self,
+ GHFunc func,
+ gpointer user_data)
+{
+ NMDHCPClientPrivate *priv;
+ DhcpForeachInfo info = { NULL, NULL };
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (!state_is_bound (priv->state)) {
+ g_warning ("%s: dhclient didn't bind to a lease.", priv->iface);
+ return FALSE;
+ }
+
+ info.func = func;
+ info.user_data = user_data;
+ g_hash_table_foreach (priv->options, iterate_dhcp_config_option, &info);
+ return TRUE;
+}
+
+/********************************************/
+
+static void
+process_classful_routes (GHashTable *options, NMIP4Config *ip4_config)
+{
+ const char *str;
+ char **searches, **s;
+
+ str = g_hash_table_lookup (options, "new_static_routes");
+ if (!str)
+ return;
+
+ searches = g_strsplit (str, " ", 0);
+ if ((g_strv_length (searches) % 2)) {
+ g_message (" static routes provided, but invalid");
+ goto out;
+ }
+
+ for (s = searches; *s; s += 2) {
+ NMIP4Route *route;
+ struct in_addr rt_addr;
+ struct in_addr rt_route;
+
+ if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
+ g_warning ("DHCP provided invalid static route address: '%s'", *s);
+ continue;
+ }
+ if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
+ g_warning ("DHCP provided invalid static route gateway: '%s'", *(s + 1));
+ continue;
+ }
+
+ // FIXME: ensure the IP addresse and route are sane
+
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr);
+ nm_ip4_route_set_prefix (route, 32); /* 255.255.255.255 */
+ nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr);
+
+ nm_ip4_config_take_route (ip4_config, route);
+ g_message (" static route %s gw %s", *s, *(s + 1));
+ }
+
+out:
+ g_strfreev (searches);
+}
+
+static void
+process_domain_search (const char *str, GFunc add_func, gpointer user_data)
+{
+ char **searches, **s;
+ char *unescaped, *p;
+ int i;
+
+ g_return_if_fail (str != NULL);
+ g_return_if_fail (add_func != NULL);
+
+ p = unescaped = g_strdup (str);
+ do {
+ p = strstr (p, "\\032");
+ if (!p)
+ break;
+
+ /* Clear the escaped space with real spaces */
+ for (i = 0; i < 4; i++)
+ *p++ = ' ';
+ } while (*p++);
+
+ if (strchr (unescaped, '\\')) {
+ g_message (" invalid domain search: '%s'", unescaped);
+ goto out;
+ }
+
+ searches = g_strsplit (unescaped, " ", 0);
+ for (s = searches; *s; s++) {
+ if (strlen (*s)) {
+ g_message (" domain search '%s'", *s);
+ add_func (*s, user_data);
+ }
+ }
+ g_strfreev (searches);
+
+out:
+ g_free (unescaped);
+}
+
+static void
+ip4_add_domain_search (gpointer data, gpointer user_data)
+{
+ nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data);
+}
+
+/* Given a table of DHCP options from the client, convert into an IP4Config */
+static NMIP4Config *
+ip4_options_to_config (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv;
+ NMIP4Config *ip4_config = NULL;
+ struct in_addr tmp_addr;
+ NMIP4Address *addr = NULL;
+ char *str = NULL;
+ guint32 gwaddr = 0;
+ gboolean have_classless = FALSE;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+ g_return_val_if_fail (priv->options != NULL, NULL);
+
+ ip4_config = nm_ip4_config_new ();
+ if (!ip4_config) {
+ g_warning ("%s: couldn't allocate memory for an IP4Config!", priv->iface);
+ return NULL;
+ }
+
+ addr = nm_ip4_address_new ();
+ if (!addr) {
+ g_warning ("%s: couldn't allocate memory for an IP4 Address!", priv->iface);
+ goto error;
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_ip_address");
+ if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+ nm_ip4_address_set_address (addr, tmp_addr.s_addr);
+ g_message (" address %s", str);
+ } else
+ goto error;
+
+ str = g_hash_table_lookup (priv->options, "new_subnet_mask");
+ if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+ nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr));
+ g_message (" prefix %d (%s)", nm_ip4_address_get_prefix (addr), str);
+ }
+
+ /* Routes: if the server returns classless static routes, we MUST ignore
+ * the 'static_routes' option.
+ */
+ if (NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes) {
+ have_classless = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes (self,
+ priv->options,
+ ip4_config,
+ &gwaddr);
+ }
+
+ if (!have_classless) {
+ gwaddr = 0; /* Ensure client code doesn't lie */
+ process_classful_routes (priv->options, ip4_config);
+ }
+
+ if (gwaddr) {
+ char buf[INET_ADDRSTRLEN + 1];
+
+ inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf));
+ g_message (" gateway %s", buf);
+ nm_ip4_address_set_gateway (addr, gwaddr);
+ } else {
+ /* If the gateway wasn't provided as a classless static route with a
+ * subnet length of 0, try to find it using the old-style 'routers' option.
+ */
+ str = g_hash_table_lookup (priv->options, "new_routers");
+ if (str) {
+ char **routers = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = routers; *s; s++) {
+ /* FIXME: how to handle multiple routers? */
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_address_set_gateway (addr, tmp_addr.s_addr);
+ g_message (" gateway %s", *s);
+ break;
+ } else
+ g_warning ("Ignoring invalid gateway '%s'", *s);
+ }
+ g_strfreev (routers);
+ }
+ }
+
+ nm_ip4_config_take_address (ip4_config, addr);
+ addr = NULL;
+
+ str = g_hash_table_lookup (priv->options, "new_host_name");
+ if (str)
+ g_message (" hostname '%s'", str);
+
+ str = g_hash_table_lookup (priv->options, "new_domain_name_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr);
+ g_message (" nameserver '%s'", *s);
+ } else
+ g_warning ("Ignoring invalid nameserver '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_domain_name");
+ if (str) {
+ char **domains = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = domains; *s; s++) {
+ g_message (" domain name '%s'", *s);
+ nm_ip4_config_add_domain (ip4_config, *s);
+ }
+ g_strfreev (domains);
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_domain_search");
+ if (str)
+ process_domain_search (str, ip4_add_domain_search, ip4_config);
+
+ str = g_hash_table_lookup (priv->options, "new_netbios_name_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr);
+ g_message (" wins '%s'", *s);
+ } else
+ g_warning ("Ignoring invalid WINS server '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_interface_mtu");
+ if (str) {
+ int int_mtu;
+
+ errno = 0;
+ int_mtu = strtol (str, NULL, 10);
+ if ((errno == EINVAL) || (errno == ERANGE))
+ goto error;
+
+ if (int_mtu > 576)
+ nm_ip4_config_set_mtu (ip4_config, int_mtu);
+ }
+
+ return ip4_config;
+
+error:
+ if (addr)
+ nm_ip4_address_unref (addr);
+ g_object_unref (ip4_config);
+ return NULL;
+}
+
+NMIP4Config *
+nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test)
+{
+ NMDHCPClientPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (test && !state_is_bound (priv->state)) {
+ g_warning ("%s: dhcp client didn't bind to a lease.", priv->iface);
+ return NULL;
+ }
+
+ return ip4_options_to_config (self);
+}
+
+/********************************************/
+
+static void
+ip6_add_domain_search (gpointer data, gpointer user_data)
+{
+ nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
+}
+
+/* Given a table of DHCP options from the client, convert into an IP6Config */
+static NMIP6Config *
+ip6_options_to_config (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv;
+ NMIP6Config *ip6_config = NULL;
+ struct in6_addr tmp_addr;
+ NMIP6Address *addr = NULL;
+ char *str = NULL;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+ g_return_val_if_fail (priv->options != NULL, NULL);
+
+ ip6_config = nm_ip6_config_new ();
+ if (!ip6_config) {
+ g_warning ("%s: couldn't allocate memory for an IP6Config!", priv->iface);
+ return NULL;
+ }
+
+ addr = nm_ip6_address_new ();
+ if (!addr) {
+ g_warning ("%s: couldn't allocate memory for an IP6 Address!", priv->iface);
+ goto error;
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_ip6_address");
+ if (str && (inet_pton (AF_INET6, str, &tmp_addr) > 0)) {
+ nm_ip6_address_set_address (addr, &tmp_addr);
+ g_message (" address %s", str);
+ } else
+ goto error;
+
+ str = g_hash_table_lookup (priv->options, "new_ip6_prefixlen");
+ if (str) {
+ long unsigned int prefix;
+
+ errno = 0;
+ prefix = strtoul (str, NULL, 10);
+ if (errno != 0 || prefix > 128)
+ goto error;
+
+ nm_ip6_address_set_prefix (addr, (guint32) prefix);
+ g_message (" prefix %lu", prefix);
+ }
+
+ nm_ip6_config_take_address (ip6_config, addr);
+ addr = NULL;
+
+ str = g_hash_table_lookup (priv->options, "new_host_name");
+ if (str)
+ g_message (" hostname '%s'", str);
+
+ str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
+ nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
+ g_message (" nameserver '%s'", *s);
+ } else
+ g_warning ("Ignoring invalid nameserver '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search");
+ if (str)
+ process_domain_search (str, ip6_add_domain_search, ip6_config);
+
+ return ip6_config;
+
+error:
+ if (addr)
+ nm_ip6_address_unref (addr);
+ g_object_unref (ip6_config);
+ return NULL;
+}
+
+NMIP6Config *
+nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test)
+{
+ NMDHCPClientPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (test && !state_is_bound (priv->state)) {
+ g_warning ("%s: dhcp client didn't bind to a lease.", priv->iface);
+ return NULL;
+ }
+
+ return ip6_options_to_config (self);
+}
+
+/********************************************/
+
+static void
+nm_dhcp_client_init (NMDHCPClient *self)
+{
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->pid = -1;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_IFACE:
+ g_value_set_string (value, priv->iface);
+ break;
+ case PROP_IPV6:
+ g_value_set_boolean (value, priv->ipv6);
+ break;
+ case PROP_UUID:
+ g_value_set_string (value, priv->uuid);
+ break;
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, priv->timeout);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_IFACE:
+ /* construct-only */
+ priv->iface = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_IPV6:
+ /* construct-only */
+ priv->ipv6 = g_value_get_boolean (value);
+ break;
+ case PROP_UUID:
+ /* construct-only */
+ priv->uuid = g_value_dup_string (value);
+ break;
+ case PROP_TIMEOUT:
+ priv->timeout = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDHCPClient *self = NM_DHCP_CLIENT (object);
+ NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (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_destroy (priv->options);
+ g_free (priv->iface);
+
+ G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_client_class_init (NMDHCPClientClass *client_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (client_class);
+
+ g_type_class_add_private (client_class, sizeof (NMDHCPClientPrivate));
+
+ /* virtual methods */
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ client_class->stop = real_stop;
+
+ g_object_class_install_property
+ (object_class, PROP_IFACE,
+ g_param_spec_string (NM_DHCP_CLIENT_INTERFACE,
+ "iface",
+ "Interface",
+ NULL,
+ 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",
+ "IPv6",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_UUID,
+ g_param_spec_string (NM_DHCP_CLIENT_UUID,
+ "uuid",
+ "UUID",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_TIMEOUT,
+ g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT,
+ "timeout",
+ "Timeout",
+ 0, G_MAXUINT, 45,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /* signals */
+ signals[STATE_CHANGED] =
+ g_signal_new ("state-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDHCPClientClass, state_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+
+ signals[TIMEOUT] =
+ g_signal_new ("timeout",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDHCPClientClass, timeout),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
new file mode 100644
index 000000000..2ebb5a745
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_CLIENT_H
+#define NM_DHCP_CLIENT_H
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type ())
+#define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClient))
+#define NM_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass))
+#define NM_IS_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_CLIENT))
+#define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_CLIENT))
+#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_IPV6 "ipv6"
+#define NM_DHCP_CLIENT_UUID "uuid"
+#define NM_DHCP_CLIENT_TIMEOUT "timeout"
+
+typedef enum {
+ DHC_NBI = 0, /* no broadcast interfaces found */
+ DHC_PREINIT, /* configuration started */
+ DHC_PREINIT6, /* configuration started */
+ DHC_BOUND4, /* IPv4 lease obtained */
+ DHC_BOUND6, /* IPv6 lease obtained */
+ DHC_IPV4LL, /* IPv4LL address obtained */
+ DHC_RENEW4, /* IPv4 lease renewed */
+ DHC_RENEW6, /* IPv6 lease renewed */
+ DHC_REBOOT, /* have valid lease, but now obtained a different one */
+ DHC_REBIND4, /* IPv4 new/different lease */
+ DHC_REBIND6, /* IPv6 new/different lease */
+ DHC_DEPREF6, /* IPv6 lease depreferred */
+ DHC_STOP, /* remove old lease */
+ DHC_MEDIUM, /* media selection begun */
+ DHC_TIMEOUT, /* timed out contacting DHCP server */
+ DHC_FAIL, /* all attempts to contact server timed out, sleeping */
+ DHC_EXPIRE, /* lease has expired, renewing */
+ DHC_RELEASE, /* releasing lease */
+ DHC_START, /* sent when dhclient started OK */
+ DHC_ABEND, /* dhclient exited abnormally */
+ DHC_END, /* dhclient exited normally */
+ DHC_END_OPTIONS, /* last option in subscription sent */
+} NMDHCPState;
+
+typedef struct {
+ GObject parent;
+} NMDHCPClient;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Methods */
+
+ /* Given the options table, extract any classless routes, add them to
+ * the IP4 config and return TRUE if any existed. If a gateway was sent
+ * as a classless route return that in out_gwaddr.
+ */
+ gboolean (*ip4_process_classless_routes) (NMDHCPClient *self,
+ GHashTable *options,
+ NMIP4Config *ip4_config,
+ guint32 *out_gwaddr);
+
+ GPid (*ip4_start) (NMDHCPClient *self,
+ NMSettingIP4Config *s_ip4,
+ guint8 *anycast_addr);
+
+ GPid (*ip6_start) (NMDHCPClient *self,
+ NMSettingIP6Config *s_ip6,
+ guint8 *anycast_addr,
+ gboolean info_only);
+
+ void (*stop) (NMDHCPClient *self);
+
+ /* Signals */
+ void (*state_changed) (NMDHCPClient *self, NMDHCPState state);
+ void (*timeout) (NMDHCPClient *self);
+} NMDHCPClientClass;
+
+GType nm_dhcp_client_get_type (void);
+
+GPid nm_dhcp_client_get_pid (NMDHCPClient *self);
+
+const char *nm_dhcp_client_get_iface (NMDHCPClient *self);
+
+gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self);
+
+const char *nm_dhcp_client_get_uuid (NMDHCPClient *self);
+
+gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self,
+ NMSettingIP4Config *s_ip4,
+ guint8 *dhcp_anycast_addr);
+
+gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self,
+ NMSettingIP6Config *s_ip6,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only);
+
+void nm_dhcp_client_stop (NMDHCPClient *self);
+
+void nm_dhcp_client_new_options (NMDHCPClient *self,
+ GHashTable *options,
+ const char *reason);
+
+gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self,
+ GHFunc func,
+ gpointer user_data);
+
+NMIP4Config *nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test);
+
+NMIP6Config *nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test);
+
+/* Backend helpers */
+void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
+
+#endif /* NM_DHCP_CLIENT_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index 478ac298f..1c5625798 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -15,7 +15,7 @@
* 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
*/
#define _XOPEN_SOURCE
@@ -36,46 +36,38 @@
#include
-#include "nm-dhcp-manager.h"
+#include "nm-dhcp-dhclient.h"
#include "nm-utils.h"
+G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
-#define NM_DHCP_MANAGER_PID_FILENAME "dhclient"
-#define NM_DHCP_MANAGER_PID_FILE_EXT "pid"
+#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate))
#if defined(TARGET_DEBIAN)
-#define NM_DHCP_MANAGER_LEASE_DIR LOCALSTATEDIR "/lib/dhcp3"
+#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp3"
#elif defined(TARGET_SUSE) || defined(TARGET_MANDRIVA)
-#define NM_DHCP_MANAGER_LEASE_DIR LOCALSTATEDIR "/lib/dhcp"
+#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp"
#else
-#define NM_DHCP_MANAGER_LEASE_DIR LOCALSTATEDIR "/lib/dhclient"
+#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhclient"
#endif
-#define NM_DHCP_MANAGER_LEASE_FILENAME "dhclient"
-#define NM_DHCP_MANAGER_LEASE_FILE_EXT "lease"
#define ACTION_SCRIPT_PATH LIBEXECDIR "/nm-dhcp-client.action"
-
-static char *
-get_pidfile_for_iface (const char * iface)
-{
- return g_strdup_printf ("%s/%s-%s.%s",
- NM_DHCP_MANAGER_RUN_DIR,
- NM_DHCP_MANAGER_PID_FILENAME,
- iface,
- NM_DHCP_MANAGER_PID_FILE_EXT);
-}
+typedef struct {
+ char *conf_file;
+ char *lease_file;
+ char *pid_file;
+} NMDHCPDhclientPrivate;
static char *
-get_leasefile_for_iface (const char * iface, const char *uuid)
+get_leasefile_for_iface (const char * iface, const char *uuid, gboolean ipv6)
{
- return g_strdup_printf ("%s/%s-%s-%s.%s",
- NM_DHCP_MANAGER_LEASE_DIR,
- NM_DHCP_MANAGER_LEASE_FILENAME,
+ return g_strdup_printf ("%s/dhclient%s-%s-%s.lease",
+ NM_DHCLIENT_LEASE_DIR,
+ ipv6 ? "6" : "",
uuid,
- iface,
- NM_DHCP_MANAGER_LEASE_FILE_EXT);
+ iface);
}
static void
@@ -119,7 +111,7 @@ add_lease_option (GHashTable *hash, char *line)
}
GSList *
-nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid)
+nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid)
{
GSList *parsed = NULL, *iter, *leases = NULL;
char *contents = NULL;
@@ -127,7 +119,7 @@ nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid)
char **line, **split = NULL;
GHashTable *hash = NULL;
- leasefile = get_leasefile_for_iface (iface, uuid);
+ leasefile = get_leasefile_for_iface (iface, uuid, FALSE);
if (!leasefile)
return NULL;
@@ -288,28 +280,39 @@ out:
#define DHCP_HOSTNAME_FORMAT DHCP_HOSTNAME_TAG " \"%s\"; # added by NetworkManager"
static gboolean
-merge_dhclient_config (NMDHCPDevice *device,
+merge_dhclient_config (const char *iface,
+ const char *conf_file,
NMSettingIP4Config *s_ip4,
guint8 *anycast_addr,
- const char *contents,
- const char *orig,
+ const char *orig_path,
GError **error)
{
GString *new_contents;
+ char *orig_contents = NULL;
gboolean success = FALSE;
- g_return_val_if_fail (device != NULL, FALSE);
- g_return_val_if_fail (device->iface != NULL, FALSE);
-
+ g_return_val_if_fail (iface != NULL, FALSE);
+ g_return_val_if_fail (conf_file != NULL, FALSE);
+
new_contents = g_string_new (_("# Created by NetworkManager\n"));
+ if (g_file_test (orig_path, G_FILE_TEST_EXISTS)) {
+ GError *read_error = NULL;
+
+ if (!g_file_get_contents (orig_path, &orig_contents, NULL, &read_error)) {
+ nm_warning ("%s: error reading dhclient configuration %s: %s",
+ iface, orig_path, read_error->message);
+ g_error_free (read_error);
+ }
+ }
+
/* Add existing options, if any, but ignore stuff NM will replace. */
- if (contents) {
+ if (orig_contents) {
char **lines = NULL, **line;
- g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig);
+ g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path);
- lines = g_strsplit_set (contents, "\n\r", 0);
+ lines = g_strsplit_set (orig_contents, "\n\r", 0);
for (line = lines; lines && *line; line++) {
gboolean ignore = FALSE;
@@ -334,6 +337,7 @@ merge_dhclient_config (NMDHCPDevice *device,
if (lines)
g_strfreev (lines);
+ g_free (orig_contents);
} else
g_string_append_c (new_contents, '\n');
@@ -374,14 +378,13 @@ merge_dhclient_config (NMDHCPDevice *device,
" initial-interval 1; \n"
" anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n"
"}\n",
- device->iface,
+ iface,
anycast_addr[0], anycast_addr[1],
anycast_addr[2], anycast_addr[3],
anycast_addr[4], anycast_addr[5]);
}
- if (g_file_set_contents (device->conf_file, new_contents->str, -1, error))
- success = TRUE;
+ success = g_file_set_contents (conf_file, new_contents->str, -1, error);
g_string_free (new_contents, TRUE);
return success;
@@ -393,17 +396,16 @@ merge_dhclient_config (NMDHCPDevice *device,
* read their single config file and merge that into a custom per-interface
* config file along with the NM options.
*/
-static gboolean
-create_dhclient_config (NMDHCPDevice *device,
+static char *
+create_dhclient_config (const char *iface,
NMSettingIP4Config *s_ip4,
guint8 *dhcp_anycast_addr)
{
- char *orig = NULL, *contents = NULL;
+ char *orig = NULL, *tmp, *conf_file = NULL;
GError *error = NULL;
gboolean success = FALSE;
- char *tmp;
- g_return_val_if_fail (device != NULL, FALSE);
+ g_return_val_if_fail (iface != NULL, FALSE);
#if defined(TARGET_SUSE)
orig = g_strdup (SYSCONFDIR "/dhclient.conf");
@@ -412,41 +414,28 @@ create_dhclient_config (NMDHCPDevice *device,
#elif defined(TARGET_GENTOO)
orig = g_strdup (SYSCONFDIR "/dhcp/dhclient.conf");
#else
- orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", device->iface);
+ orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", iface);
#endif
if (!orig) {
- nm_warning ("%s: not enough memory for dhclient options.", device->iface);
+ nm_warning ("%s: not enough memory for dhclient options.", iface);
return FALSE;
}
- tmp = g_strdup_printf ("nm-dhclient-%s.conf", device->iface);
- device->conf_file = g_build_filename ("/var", "run", tmp, NULL);
+ tmp = g_strdup_printf ("nm-dhclient-%s.conf", iface);
+ conf_file = g_build_filename ("/var", "run", tmp, NULL);
g_free (tmp);
- if (!g_file_test (orig, G_FILE_TEST_EXISTS))
- goto out;
-
- if (!g_file_get_contents (orig, &contents, NULL, &error)) {
- nm_warning ("%s: error reading dhclient configuration %s: %s",
- device->iface, orig, error->message);
- g_error_free (error);
- goto out;
- }
-
-out:
error = NULL;
- if (merge_dhclient_config (device, s_ip4, dhcp_anycast_addr, contents, orig, &error))
- success = TRUE;
- else {
+ success = merge_dhclient_config (iface, conf_file, s_ip4, dhcp_anycast_addr, orig, &error);
+ if (!success) {
nm_warning ("%s: error creating dhclient configuration: %s",
- device->iface, error->message);
+ iface, error->message);
g_error_free (error);
}
- g_free (contents);
g_free (orig);
- return success;
+ return conf_file;
}
@@ -458,80 +447,129 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED)
setpgid (pid, pid);
}
-
-GPid
-nm_dhcp_client_start (NMDHCPDevice *device,
- const char *uuid,
- NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr)
+static GPid
+dhclient_start (NMDHCPClient *client,
+ const char *ip_opt,
+ const char *mode_opt)
{
- GPtrArray *dhclient_argv = NULL;
+ NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+ GPtrArray *argv = NULL;
GPid pid = 0;
GError *error = NULL;
- char *pid_contents = NULL;
+ const char *iface, *uuid;
+ char *binary_name;
+ gboolean ipv6;
- if (!g_file_test (DHCP_CLIENT_PATH, G_FILE_TEST_EXISTS)) {
- nm_warning (DHCP_CLIENT_PATH " does not exist.");
- goto out;
+ g_return_val_if_fail (priv->pid_file == NULL, -1);
+ g_return_val_if_fail (ip_opt != NULL, -1);
+
+ iface = nm_dhcp_client_get_iface (client);
+ uuid = nm_dhcp_client_get_uuid (client);
+ ipv6 = nm_dhcp_client_get_ipv6 (client);
+
+ priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
+ ipv6 ? "6" : "",
+ iface);
+ if (!priv->pid_file) {
+ nm_warning ("%s: not enough memory for dhcpcd options.", iface);
+ return -1;
}
- device->pid_file = get_pidfile_for_iface (device->iface);
- if (!device->pid_file) {
- nm_warning ("%s: not enough memory for dhclient options.", device->iface);
- goto out;
+ if (!g_file_test (DHCLIENT_PATH, G_FILE_TEST_EXISTS)) {
+ nm_warning (DHCLIENT_PATH " does not exist.");
+ return -1;
}
- device->lease_file = get_leasefile_for_iface (device->iface, uuid);
- if (!device->lease_file) {
- nm_warning ("%s: not enough memory for dhclient options.", device->iface);
- goto out;
+ /* Kill any existing dhclient from the pidfile */
+ binary_name = g_path_get_basename (DHCLIENT_PATH);
+ nm_dhcp_client_stop_existing (priv->pid_file, binary_name);
+ g_free (binary_name);
+
+ priv->lease_file = get_leasefile_for_iface (iface, uuid, ipv6);
+ if (!priv->lease_file) {
+ nm_warning ("%s: not enough memory for dhclient options.", iface);
+ return -1;
}
- if (!create_dhclient_config (device, s_ip4, dhcp_anycast_addr))
- goto out;
+ argv = g_ptr_array_new ();
+ g_ptr_array_add (argv, (gpointer) DHCLIENT_PATH);
- /* Kill any existing dhclient bound to this interface */
- if (g_file_get_contents (device->pid_file, &pid_contents, NULL, NULL)) {
- unsigned long int tmp = strtoul (pid_contents, NULL, 10);
+ g_ptr_array_add (argv, (gpointer) "-d");
- if (!((tmp == ULONG_MAX) && (errno == ERANGE)))
- nm_dhcp_client_stop (device, (pid_t) tmp);
- remove (device->pid_file);
+ g_ptr_array_add (argv, (gpointer) ip_opt);
+
+ if (mode_opt)
+ 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) "-pf"); /* Set pid file */
+ g_ptr_array_add (argv, (gpointer) priv->pid_file);
+
+ g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */
+ g_ptr_array_add (argv, (gpointer) priv->lease_file);
+
+ if (priv->conf_file) {
+ g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */
+ g_ptr_array_add (argv, (gpointer) priv->conf_file);
}
- dhclient_argv = g_ptr_array_new ();
- g_ptr_array_add (dhclient_argv, (gpointer) DHCP_CLIENT_PATH);
+ g_ptr_array_add (argv, (gpointer) iface);
+ g_ptr_array_add (argv, NULL);
- g_ptr_array_add (dhclient_argv, (gpointer) "-d");
-
- g_ptr_array_add (dhclient_argv, (gpointer) "-sf"); /* Set script file */
- g_ptr_array_add (dhclient_argv, (gpointer) ACTION_SCRIPT_PATH );
-
- g_ptr_array_add (dhclient_argv, (gpointer) "-pf"); /* Set pid file */
- g_ptr_array_add (dhclient_argv, (gpointer) device->pid_file);
-
- g_ptr_array_add (dhclient_argv, (gpointer) "-lf"); /* Set lease file */
- g_ptr_array_add (dhclient_argv, (gpointer) device->lease_file);
-
- g_ptr_array_add (dhclient_argv, (gpointer) "-cf"); /* Set interface config file */
- g_ptr_array_add (dhclient_argv, (gpointer) device->conf_file);
-
- g_ptr_array_add (dhclient_argv, (gpointer) device->iface);
- g_ptr_array_add (dhclient_argv, NULL);
-
- if (!g_spawn_async (NULL, (char **) dhclient_argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+ if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
&dhclient_child_setup, NULL, &pid, &error)) {
nm_warning ("dhclient failed to start. error: '%s'", error->message);
g_error_free (error);
- goto out;
+ pid = -1;
+ } else
+ nm_info ("dhclient started with pid %d", pid);
+
+ g_ptr_array_free (argv, TRUE);
+ return pid;
+}
+
+static GPid
+real_ip4_start (NMDHCPClient *client,
+ NMSettingIP4Config *s_ip4,
+ guint8 *dhcp_anycast_addr)
+{
+ NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+ const char *iface;
+
+ iface = nm_dhcp_client_get_iface (client);
+
+ priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr);
+ if (!priv->conf_file) {
+ nm_warning ("%s: error creating dhclient configuration file.", iface);
+ return -1;
}
- nm_info ("dhclient started with pid %d", pid);
+ return dhclient_start (client, "-4", NULL);
+}
-out:
- g_free (pid_contents);
- g_ptr_array_free (dhclient_argv, TRUE);
- return pid;
+static GPid
+real_ip6_start (NMDHCPClient *client,
+ NMSettingIP6Config *s_ip6,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only)
+{
+ return dhclient_start (client, "-6", info_only ? "-S" : "-N");
+}
+
+static void
+real_stop (NMDHCPClient *client)
+{
+ NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+
+ /* Chain up to parent */
+ NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client);
+
+ if (priv->conf_file)
+ remove (priv->conf_file);
+ if (priv->pid_file)
+ remove (priv->pid_file);
}
static const char **
@@ -595,10 +633,11 @@ error:
return o;
}
-gboolean
-nm_dhcp_client_process_classless_routes (GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
+static gboolean
+real_ip4_process_classless_routes (NMDHCPClient *client,
+ GHashTable *options,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
{
const char *str;
char **octets, **o;
@@ -663,3 +702,39 @@ out:
return have_routes;
}
+/***************************************************/
+
+static void
+nm_dhcp_dhclient_init (NMDHCPDhclient *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+
+ g_free (priv->pid_file);
+ g_free (priv->conf_file);
+ g_free (priv->lease_file);
+
+ G_OBJECT_CLASS (nm_dhcp_dhclient_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
+{
+ NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (dhclient_class);
+
+ g_type_class_add_private (dhclient_class, sizeof (NMDHCPDhclientPrivate));
+
+ /* virtual methods */
+ object_class->dispose = dispose;
+
+ client_class->ip4_start = real_ip4_start;
+ client_class->ip6_start = real_ip6_start;
+ client_class->stop = real_stop;
+ client_class->ip4_process_classless_routes = real_ip4_process_classless_routes;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h
new file mode 100644
index 000000000..db9f73d25
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhclient.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_DHCLIENT_H
+#define NM_DHCP_DHCLIENT_H
+
+#include
+#include
+
+#include "nm-dhcp-client.h"
+
+#define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type ())
+#define NM_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclient))
+#define NM_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass))
+#define NM_IS_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT))
+#define NM_IS_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT))
+#define NM_DHCP_DHCLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass))
+
+typedef struct {
+ NMDHCPClient parent;
+} NMDHCPDhclient;
+
+typedef struct {
+ NMDHCPClientClass parent;
+} NMDHCPDhclientClass;
+
+GType nm_dhcp_dhclient_get_type (void);
+
+GSList *nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid);
+
+#endif /* NM_DHCP_DHCLIENT_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index a8d929a52..27fb31bcc 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -2,6 +2,7 @@
/* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager
*
* Copyright (C) 2008 Roy Marples
+ * Copyright (C) 2010 Dan Williams
*
* 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
@@ -31,26 +32,22 @@
#include
#include
-#include "nm-dhcp-manager.h"
+#include "nm-dhcp-dhcpcd.h"
#include "nm-utils.h"
-#define NM_DHCP_MANAGER_PID_FILENAME "dhcpcd"
-#define NM_DHCP_MANAGER_PID_FILE_EXT "pid"
+G_DEFINE_TYPE (NMDHCPDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_DHCPCD)
+
+#define NM_DHCP_DHCPCD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdPrivate))
#define ACTION_SCRIPT_PATH LIBEXECDIR "/nm-dhcp-client.action"
+typedef struct {
+ char *pid_file;
+} NMDHCPDhcpcdPrivate;
-static char *
-get_pidfile_for_iface (const char * iface)
-{
- return g_strdup_printf ("/var/run/%s-%s.%s",
- NM_DHCP_MANAGER_PID_FILENAME,
- iface,
- NM_DHCP_MANAGER_PID_FILE_EXT);
-}
GSList *
-nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid)
+nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid)
{
return NULL;
}
@@ -63,40 +60,41 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
setpgid (pid, pid);
}
-
-GPid
-nm_dhcp_client_start (NMDHCPDevice *device,
- const char *uuid,
- NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr)
+static GPid
+real_ip4_start (NMDHCPClient *client,
+ NMSettingIP4Config *s_ip4,
+ guint8 *dhcp_anycast_addr)
{
+ NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
GPtrArray *argv = NULL;
GPid pid = 0;
GError *error = NULL;
- char *pid_contents = NULL;
+ char *pid_contents = NULL, *binary_name;
+ const char *iface, *uuid;
- if (!g_file_test (DHCP_CLIENT_PATH, G_FILE_TEST_EXISTS)) {
- nm_warning (DHCP_CLIENT_PATH " does not exist.");
- goto out;
+ g_return_val_if_fail (priv->pid_file == NULL, -1);
+
+ iface = nm_dhcp_client_get_iface (client);
+ uuid = nm_dhcp_client_get_uuid (client);
+
+ priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhcpcd-%s.pid", iface);
+ if (!priv->pid_file) {
+ nm_warning ("%s: not enough memory for dhcpcd options.", iface);
+ return -1;
}
- device->pid_file = get_pidfile_for_iface (device->iface);
- if (!device->pid_file) {
- nm_warning ("%s: not enough memory for dhcpcd options.", device->iface);
- goto out;
+ if (!g_file_test (DHCPCD_PATH, G_FILE_TEST_EXISTS)) {
+ nm_warning (DHCPCD_PATH " does not exist.");
+ return -1;
}
- /* Kill any existing dhcpcd bound to this interface */
- if (g_file_get_contents (device->pid_file, &pid_contents, NULL, NULL)) {
- unsigned long int tmp = strtoul (pid_contents, NULL, 10);
-
- if (!((tmp == ULONG_MAX) && (errno == ERANGE)))
- nm_dhcp_client_stop (device, (pid_t) tmp);
- remove (device->pid_file);
- }
+ /* Kill any existing dhclient from the pidfile */
+ binary_name = g_path_get_basename (DHCPCD_PATH);
+ nm_dhcp_client_stop_existing (priv->pid_file, binary_name);
+ g_free (binary_name);
argv = g_ptr_array_new ();
- g_ptr_array_add (argv, (gpointer) DHCP_CLIENT_PATH);
+ g_ptr_array_add (argv, (gpointer) DHCPCD_PATH);
g_ptr_array_add (argv, (gpointer) "-B"); /* Don't background on lease (disable fork()) */
@@ -107,28 +105,48 @@ nm_dhcp_client_start (NMDHCPDevice *device,
g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */
g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH );
- g_ptr_array_add (argv, (gpointer) device->iface);
+ g_ptr_array_add (argv, (gpointer) iface);
g_ptr_array_add (argv, NULL);
if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
&dhcpcd_child_setup, NULL, &pid, &error)) {
nm_warning ("dhcpcd failed to start. error: '%s'", error->message);
g_error_free (error);
- goto out;
- }
+ } else
+ nm_info ("dhcpcd started with pid %d", pid);
- nm_info ("dhcpcd started with pid %d", pid);
-
-out:
g_free (pid_contents);
g_ptr_array_free (argv, TRUE);
return pid;
}
-gboolean
-nm_dhcp_client_process_classless_routes (GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
+static GPid
+real_ip6_start (NMDHCPClient *client,
+ NMSettingIP6Config *s_ip6,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only)
+{
+ g_warning ("The dhcpcd backend does not support IPv6.");
+ return -1;
+}
+
+static void
+real_stop (NMDHCPClient *client)
+{
+ NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+
+ /* Chain up to parent */
+ NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client);
+
+ if (priv->pid_file)
+ remove (priv->pid_file);
+}
+
+static gboolean
+real_ip4_process_classless_routes (NMDHCPClient *client,
+ GHashTable *options,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
{
const char *str;
char **routes, **r;
@@ -201,3 +219,37 @@ out:
return have_routes;
}
+/***************************************************/
+
+static void
+nm_dhcp_dhcpcd_init (NMDHCPDhcpcd *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object);
+
+ g_free (priv->pid_file);
+
+ G_OBJECT_CLASS (nm_dhcp_dhcpcd_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class)
+{
+ NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcd_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (dhcpcd_class);
+
+ g_type_class_add_private (dhcpcd_class, sizeof (NMDHCPDhcpcdPrivate));
+
+ /* virtual methods */
+ object_class->dispose = dispose;
+
+ client_class->ip4_start = real_ip4_start;
+ client_class->ip6_start = real_ip6_start;
+ client_class->stop = real_stop;
+ client_class->ip4_process_classless_routes = real_ip4_process_classless_routes;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h
new file mode 100644
index 000000000..9a9812941
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_DHCPCD_H
+#define NM_DHCP_DHCPCD_H
+
+#include
+#include
+
+#include "nm-dhcp-client.h"
+
+#define NM_TYPE_DHCP_DHCPCD (nm_dhcp_dhcpcd_get_type ())
+#define NM_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcd))
+#define NM_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass))
+#define NM_IS_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCPCD))
+#define NM_IS_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCPCD))
+#define NM_DHCP_DHCPCD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass))
+
+typedef struct {
+ NMDHCPClient parent;
+} NMDHCPDhcpcd;
+
+typedef struct {
+ NMDHCPClientClass parent;
+} NMDHCPDhcpcdClass;
+
+GType nm_dhcp_dhcpcd_get_type (void);
+
+GSList *nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid);
+
+#endif /* NM_DHCP_DHCPCD_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index c3ca358dc..241199f40 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -15,7 +15,7 @@
* 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*
*/
@@ -25,8 +25,6 @@
#include
#include
#include
-#include
-#include
#include
#include
#include
@@ -37,6 +35,8 @@
#include
#include "nm-dhcp-manager.h"
+#include "nm-dhcp-dhclient.h"
+#include "nm-dhcp-dhcpcd.h"
#include "nm-marshal.h"
#include "nm-utils.h"
#include "nm-dbus-manager.h"
@@ -47,12 +47,19 @@
#define NM_DHCP_CLIENT_DBUS_SERVICE "org.freedesktop.nm_dhcp_client"
#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
-#define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */
+#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
+
+static NMDHCPManager *singleton = NULL;
+
+typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid);
typedef struct {
- NMDBusManager * dbus_mgr;
- GHashTable * devices;
- DBusGProxy * proxy;
+ GType client_type;
+ GetLeaseConfigFunc get_lease_config_func;
+
+ NMDBusManager * dbus_mgr;
+ GHashTable * clients;
+ DBusGProxy * proxy;
NMHostnameProvider *hostname_provider;
} NMDHCPManagerPrivate;
@@ -61,224 +68,6 @@ typedef struct {
G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT)
-enum {
- STATE_CHANGED,
- TIMEOUT,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-static NMDHCPManager *nm_dhcp_manager_new (void);
-
-static void nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device);
-
-static void hostname_provider_destroyed (gpointer data, GObject *destroyed_object);
-
-NMDHCPManager *
-nm_dhcp_manager_get (void)
-{
- static NMDHCPManager *singleton = NULL;
-
- if (!singleton)
- singleton = nm_dhcp_manager_new ();
- else
- g_object_ref (singleton);
-
- g_assert (singleton);
- return singleton;
-}
-
-static void
-nm_dhcp_manager_init (NMDHCPManager *manager)
-{
-}
-
-static void
-finalize (GObject *object)
-{
- NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
-
- if (priv->hostname_provider) {
- g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object);
- priv->hostname_provider = NULL;
- }
-
- g_hash_table_destroy (priv->devices);
- g_object_unref (priv->proxy);
- g_object_unref (priv->dbus_mgr);
-
- 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);
-
- g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate));
-
- /* virtual methods */
- object_class->finalize = finalize;
-
- /* signals */
- signals[STATE_CHANGED] =
- g_signal_new ("state-changed",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMDHCPManagerClass, state_changed),
- NULL, NULL,
- _nm_marshal_VOID__STRING_UCHAR,
- G_TYPE_NONE, 2,
- G_TYPE_STRING,
- G_TYPE_UCHAR);
-
- signals[TIMEOUT] =
- g_signal_new ("timeout",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMDHCPManagerClass, timeout),
- NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1,
- G_TYPE_STRING);
-}
-
-static gboolean state_is_bound (guint8 state)
-{
- if ((state == DHC_BOUND)
- || (state == DHC_RENEW)
- || (state == DHC_REBOOT)
- || (state == DHC_REBIND)
- || (state == DHC_IPV4LL))
- return TRUE;
-
- return FALSE;
-}
-
-
-static void
-nm_dhcp_device_timeout_cleanup (NMDHCPDevice * device)
-{
- if (device->timeout_id) {
- g_source_remove (device->timeout_id);
- device->timeout_id = 0;
- }
-}
-
-static void
-nm_dhcp_device_watch_cleanup (NMDHCPDevice * device)
-{
- if (device->watch_id) {
- g_source_remove (device->watch_id);
- device->watch_id = 0;
- }
-}
-
-static void
-nm_dhcp_device_destroy (NMDHCPDevice *device)
-{
- int ignored;
-
- nm_dhcp_device_timeout_cleanup (device);
-
- if (device->pid)
- nm_dhcp_client_stop (device, device->pid);
-
- if (device->options)
- g_hash_table_destroy (device->options);
-
- if (device->conf_file) {
- ignored = unlink (device->conf_file);
- g_free (device->conf_file);
- }
-
- g_free (device->pid_file);
- g_free (device->lease_file);
- g_free (device->iface);
-
- g_slice_free (NMDHCPDevice, device);
-}
-
-
-static inline const char *
-state_to_string (guint32 state)
-{
- switch (state)
- {
- case DHC_PREINIT:
- return "preinit";
- case DHC_BOUND:
- return "bound";
- case DHC_IPV4LL:
- return "bound (ipv4ll)";
- case DHC_RENEW:
- return "renew";
- case DHC_REBOOT:
- return "reboot";
- case DHC_REBIND:
- return "rebind";
- case DHC_STOP:
- return "stop";
- case DHC_MEDIUM:
- return "medium";
- case DHC_TIMEOUT:
- return "timeout";
- case DHC_FAIL:
- return "fail";
- case DHC_EXPIRE:
- return "expire";
- case DHC_RELEASE:
- return "release";
- case DHC_START:
- return "successfully started";
- case DHC_ABEND:
- return "abnormal exit";
- case DHC_END:
- return "normal exit";
- default:
- break;
- }
- return NULL;
-}
-
-static inline guint32
-string_to_state (const char *state)
-{
- if (strcmp("PREINIT", state) == 0)
- return DHC_PREINIT;
- else if (strcmp("BOUND", state) == 0)
- return DHC_BOUND;
- else if (strcmp("IPV4LL", state) == 0)
- return DHC_IPV4LL;
- else if (strcmp("RENEW", state) == 0)
- return DHC_RENEW;
- else if (strcmp("REBOOT", state) == 0)
- return DHC_REBOOT;
- else if (strcmp("REBIND", state) == 0)
- return DHC_REBIND;
- else if (strcmp("STOP", state) == 0)
- return DHC_STOP;
- else if (strcmp("MEDIUM", state) == 0)
- return DHC_MEDIUM;
- else if (strcmp("TIMEOUT", state) == 0)
- return DHC_TIMEOUT;
- else if (strcmp("FAIL", state) == 0)
- return DHC_FAIL;
- else if (strcmp("EXPIRE", state) == 0)
- return DHC_EXPIRE;
- else if (strcmp("RELEASE", state) == 0)
- return DHC_RELEASE;
- else if (strcmp("START", state) == 0)
- return DHC_START;
- else if (strcmp("ABEND", state) == 0)
- return DHC_ABEND;
- else if (strcmp("END", state) == 0)
- return DHC_END;
- else
- return 255;
-}
-
static char *
garray_to_string (GArray *array, const char *key)
{
@@ -312,6 +101,56 @@ garray_to_string (GArray *array, const char *key)
return converted;
}
+static NMDHCPClient *
+get_client_for_pid (NMDHCPManager *manager, GPid pid)
+{
+ NMDHCPManagerPrivate *priv;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_val_if_fail (manager != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), 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_pid (candidate) == pid)
+ return candidate;
+ }
+
+ return NULL;
+}
+
+static NMDHCPClient *
+get_client_for_iface (NMDHCPManager *manager,
+ const char *iface,
+ gboolean ip6)
+{
+ NMDHCPManagerPrivate *priv;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_val_if_fail (manager != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
+ g_return_val_if_fail (iface, 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 ( !strcmp (iface, nm_dhcp_client_get_iface (candidate))
+ && (nm_dhcp_client_get_ipv6 (candidate) == ip6))
+ return candidate;
+ }
+
+ return NULL;
+}
+
static char *
get_option (GHashTable *hash, const char *key)
{
@@ -322,84 +161,27 @@ get_option (GHashTable *hash, const char *key)
return NULL;
if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
- nm_warning ("Unexpected key %s value type was not "
- "DBUS_TYPE_G_UCHAR_ARRAY",
- (char *) key);
+ g_warning ("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
-copy_option (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- NMDHCPDevice * device = (NMDHCPDevice *) user_data;
- const char *str_key = (const char *) key;
- char *str_value = NULL;
-
- if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
- nm_warning ("Unexpected key %s value type was not "
- "DBUS_TYPE_G_UCHAR_ARRAY",
- str_key);
- return;
- }
-
- str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key);
- if (str_value)
- g_hash_table_insert (device->options, g_strdup (str_key), str_value);
-}
-
-static void
-handle_options (NMDHCPManager * manager,
- NMDHCPDevice * device,
- GHashTable * options,
- const char * reason)
-{
- guint32 old_state = device->state;
- guint32 new_state = string_to_state (reason);
-
- /* Clear old and save new DHCP options */
- g_hash_table_remove_all (device->options);
- g_hash_table_foreach (options, copy_option, device);
-
- if (old_state == new_state)
- return;
-
- /* Handle changed device state */
- if (state_is_bound (new_state)) {
- /* Cancel the timeout if the DHCP client is now bound */
- nm_dhcp_device_timeout_cleanup (device);
- }
-
- device->state = new_state;
- nm_info ("DHCP: device %s state changed %s -> %s",
- device->iface,
- state_to_string (old_state),
- state_to_string (device->state));
-
- g_signal_emit (G_OBJECT (device->manager),
- signals[STATE_CHANGED],
- 0,
- device->iface,
- device->state);
-}
-
static void
nm_dhcp_manager_handle_event (DBusGProxy *proxy,
GHashTable *options,
gpointer user_data)
{
- NMDHCPManager * manager;
- NMDHCPManagerPrivate * priv;
- NMDHCPDevice * device;
- char * iface = NULL;
- char * pid_str = NULL;
- char * reason = NULL;
+ NMDHCPManager *manager;
+ NMDHCPManagerPrivate *priv;
+ NMDHCPClient *client;
+ char *iface = NULL;
+ char *pid_str = NULL;
+ char *reason = NULL;
unsigned long temp;
- pid_t pid;
manager = NM_DHCP_MANAGER (user_data);
priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
@@ -410,29 +192,27 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy,
goto out;
}
- device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface);
- if (device == NULL) {
- nm_warning ("Unhandled DHCP event for interface %s", iface);
- goto out;
- }
-
pid_str = get_option (options, "pid");
if (pid_str == NULL) {
nm_warning ("DHCP event didn't have associated PID.");
goto out;
}
- temp = strtoul(pid_str, NULL, 10);
+ temp = strtoul (pid_str, NULL, 10);
if ((temp == ULONG_MAX) && (errno == ERANGE)) {
nm_warning ("Couldn't convert PID");
goto out;
}
- pid = (pid_t) temp;
- if (pid != device->pid) {
- nm_warning ("Received DHCP event from unexpected PID %u (expected %u)",
- pid,
- device->pid);
+ client = get_client_for_pid (manager, (GPid) temp);
+ if (client == NULL) {
+ nm_warning ("Unhandled DHCP event for interface %s", iface);
+ goto out;
+ }
+
+ if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
+ nm_warning ("Received DHCP event from unexpected interface '%s' (expected '%s')",
+ iface, nm_dhcp_client_get_iface (client));
goto out;
}
@@ -442,7 +222,7 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy,
goto out;
}
- handle_options (manager, device, options, reason);
+ nm_dhcp_client_new_options (client, options, reason);
out:
g_free (iface);
@@ -450,25 +230,51 @@ out:
g_free (reason);
}
-static NMDHCPManager *
-nm_dhcp_manager_new (void)
+static GType
+get_client_type (const char *client, GError **error)
+{
+ g_return_val_if_fail (client != NULL, 0);
+
+ if (!strcmp (client, "dhclient") && strlen (DHCLIENT_PATH))
+ return NM_TYPE_DHCP_DHCLIENT;
+ else if (!strcmp (client, "dhcpcd") && strlen (DHCPCD_PATH))
+ return NM_TYPE_DHCP_DHCPCD;
+ else
+ g_set_error (error, 0, 0, "unknown or missing DHCP client '%s'", client);
+
+ return 0;
+}
+
+NMDHCPManager *
+nm_dhcp_manager_new (const char *client, GError **error)
{
- NMDHCPManager *manager;
NMDHCPManagerPrivate *priv;
- DBusGConnection * g_connection;
+ DBusGConnection *g_connection;
- manager = g_object_new (NM_TYPE_DHCP_MANAGER, NULL);
- priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+ g_warn_if_fail (singleton == NULL);
+ g_return_val_if_fail (client != NULL, NULL);
- priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
- NULL,
- (GDestroyNotify) nm_dhcp_device_destroy);
- if (!priv->devices) {
- nm_warning ("Error: not enough memory to initialize DHCP manager "
- "tables");
- g_object_unref (manager);
- manager = NULL;
- goto out;
+ singleton = g_object_new (NM_TYPE_DHCP_MANAGER, NULL);
+ priv = NM_DHCP_MANAGER_GET_PRIVATE (singleton);
+
+ /* Figure out which DHCP client to use */
+ priv->client_type = get_client_type (client, error);
+ if (!priv->client_type)
+ goto error;
+
+ if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
+ priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config;
+ else if (priv->client_type == NM_TYPE_DHCP_DHCPCD)
+ priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config;
+ else
+ g_assert_not_reached ();
+
+ priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+ if (!priv->clients) {
+ g_set_error_literal (error, 0, 0, "not enough memory to initialize DHCP manager");
+ goto error;
}
priv->dbus_mgr = nm_dbus_manager_get ();
@@ -478,8 +284,8 @@ nm_dhcp_manager_new (void)
"/",
NM_DHCP_CLIENT_DBUS_IFACE);
if (!priv->proxy) {
- nm_warning ("Error: could not init DHCP manager proxy");
- g_object_unref (manager);
+ g_set_error_literal (error, 0, 0, "not enough memory to initialize DHCP manager proxy");
+ goto error;
}
dbus_g_proxy_add_signal (priv->proxy,
@@ -488,611 +294,178 @@ nm_dhcp_manager_new (void)
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->proxy, "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- manager,
- NULL);
+ G_CALLBACK (nm_dhcp_manager_handle_event),
+ singleton,
+ NULL);
-out:
- return manager;
-}
-
-
-/*
- * nm_dhcp_manager_handle_timeout
- *
- * Called after timeout of a DHCP transaction to notify device of the failure.
- *
- */
-static gboolean
-nm_dhcp_manager_handle_timeout (gpointer user_data)
-{
- NMDHCPDevice *device = (NMDHCPDevice *) user_data;
-
- nm_info ("(%s): DHCP transaction took too long, stopping it.", device->iface);
-
- nm_dhcp_manager_cancel_transaction (device->manager, device->iface);
-
- g_signal_emit (G_OBJECT (device->manager), signals[TIMEOUT], 0, device->iface);
-
- return FALSE;
-}
-
-static NMDHCPDevice *
-nm_dhcp_device_new (NMDHCPManager *manager, const char *iface)
-{
- NMDHCPDevice *device;
- GHashTable * hash = NM_DHCP_MANAGER_GET_PRIVATE (manager)->devices;
-
- device = g_slice_new0 (NMDHCPDevice);
- if (!device) {
- nm_warning ("%s: Out of memory creating DHCP transaction object.", iface);
- return NULL;
- }
-
- device->iface = g_strdup (iface);
- if (!device) {
- nm_warning ("%s: Out of memory creating DHCP transaction object "
- "property 'iface'.",
- iface);
- goto error;
- }
-
- device->manager = manager;
-
- /* Do this after the transaction cancel since that clears options out */
- device->options = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_free);
- if (!device->options) {
- nm_warning ("%s: Out of memory creating DHCP transaction object "
- "property 'options'.",
- iface);
- goto error;
- }
-
- g_hash_table_insert (hash, device->iface, device);
- return device;
+ return singleton;
error:
- nm_dhcp_device_destroy (device);
- return NULL;
+ g_object_unref (singleton);
+ singleton = NULL;
+ return singleton;
}
+#define STATE_ID_TAG "state-id"
+#define TIMEOUT_ID_TAG "timeout-id"
-/*
- * dhcp_watch_cb
- *
- * Watch our child dhclient process and get notified of events from it.
- *
- */
-static void dhcp_watch_cb (GPid pid, gint status, gpointer user_data)
+static void
+remove_client (NMDHCPManager *self, NMDHCPClient *client)
{
- NMDHCPDevice *device = (NMDHCPDevice *)user_data;
+ NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ guint id;
- if (!WIFEXITED (status)) {
- device->state = DHC_ABEND;
- nm_warning ("dhcp client died abnormally");
- }
- device->pid = 0;
+ id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), STATE_ID_TAG));
+ if (id)
+ g_signal_handler_disconnect (client, id);
- nm_dhcp_device_watch_cleanup (device);
- nm_dhcp_device_timeout_cleanup (device);
+ id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG));
+ if (id)
+ g_signal_handler_disconnect (client, id);
- g_signal_emit (G_OBJECT (device->manager), signals[STATE_CHANGED], 0, device->iface, device->state);
-}
-
-gboolean
-nm_dhcp_manager_begin_transaction (NMDHCPManager *manager,
- const char *iface,
- const char *uuid,
- NMSettingIP4Config *s_ip4,
- guint32 timeout,
- guint8 *dhcp_anycast_addr)
-{
- NMDHCPManagerPrivate *priv;
- NMDHCPDevice *device;
- NMSettingIP4Config *setting;
-
- g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), FALSE);
- g_return_val_if_fail (iface != NULL, FALSE);
-
- priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
-
- device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface);
- if (!device)
- device = nm_dhcp_device_new (manager, iface);
-
- if (device->pid && (device->state < DHC_ABEND)) {
- /* Cancel any DHCP transaction already in progress */
- nm_dhcp_manager_cancel_transaction_real (device);
- }
-
- if (s_ip4 &&
- nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) &&
- nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL &&
- priv->hostname_provider != NULL) {
- /* We're asked to send the hostname to DHCP server,
- the hostname isn't specified,
- and a hostname provider is registered: use that */
-
- setting = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4)));
- g_object_set (G_OBJECT (setting),
- NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME,
- nm_hostname_provider_get_hostname (priv->hostname_provider),
- NULL);
- } else {
- setting = s_ip4 ? g_object_ref (s_ip4) : NULL;
- }
-
- if (timeout == 0)
- timeout = NM_DHCP_TIMEOUT;
-
- nm_info ("Activation (%s) Beginning DHCP transaction (timeout in %d seconds)",
- iface, timeout);
- device->pid = nm_dhcp_client_start (device, uuid, setting, dhcp_anycast_addr);
-
- if (setting)
- g_object_unref (setting);
-
- if (device->pid == 0)
- return FALSE;
-
- /* Set up a timeout on the transaction to kill it after the timeout */
- device->timeout_id = g_timeout_add_seconds (timeout,
- nm_dhcp_manager_handle_timeout,
- device);
- device->watch_id = g_child_watch_add (device->pid,
- (GChildWatchFunc) dhcp_watch_cb,
- device);
- return TRUE;
-}
-
-void
-nm_dhcp_client_stop (NMDHCPDevice *device, pid_t pid)
-{
- int i = 15; /* 3 seconds */
-
- g_return_if_fail (pid > 0);
-
- /* Clean up the watch handler since we're explicitly killing
- * the daemon
+ /* Stopping the client is left up to the controlling device
+ * explicitly since we may want to quit NetworkManager but not terminate
+ * the DHCP client.
*/
- nm_dhcp_device_watch_cleanup (device);
- /* Tell it to quit; maybe it wants to send out a RELEASE message */
- kill (pid, SIGTERM);
-
- while (i-- > 0) {
- gint child_status;
- int ret;
-
- ret = waitpid (pid, &child_status, WNOHANG);
- if (ret > 0)
- break;
-
- if (ret == -1) {
- /* Child already exited */
- if (errno == ECHILD)
- break;
- /* Took too long; shoot it in the head */
- i = 0;
- break;
- }
- g_usleep (G_USEC_PER_SEC / 5);
- }
-
- if (i <= 0) {
- nm_warning ("%s: dhcp client pid %d didn't exit, will kill it.", device->iface, pid);
- kill (pid, SIGKILL);
-
- nm_debug ("waiting for dhcp client pid %d to exit", pid);
- waitpid (pid, NULL, 0);
- nm_debug ("dhcp client pid %d cleaned up", pid);
- }
+ g_hash_table_remove (priv->clients, client);
}
static void
-nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device)
+client_state_changed (NMDHCPClient *client, NMDHCPState new_state, gpointer user_data)
{
- g_return_if_fail (device->pid > 0);
-
- nm_dhcp_client_stop (device, device->pid);
-
- nm_info ("(%s): canceled DHCP transaction, dhcp client pid %d",
- device->iface,
- device->pid);
-
- device->pid = 0;
- device->state = DHC_END;
-
- /* Clean up the pidfile if it got left around */
- if (device->pid_file) {
- remove (device->pid_file);
- g_free (device->pid_file);
- device->pid_file = NULL;
- }
-
- /* Free leasefile (but don't delete) */
- if (device->lease_file) {
- g_free (device->lease_file);
- device->lease_file = NULL;
- }
-
- /* Clean up config file if it got left around */
- if (device->conf_file) {
- remove (device->conf_file);
- g_free (device->conf_file);
- device->conf_file = NULL;
- }
-
- nm_dhcp_device_timeout_cleanup (device);
- g_hash_table_remove_all (device->options);
+ if (new_state == DHC_ABEND || new_state == DHC_END)
+ remove_client (NM_DHCP_MANAGER (user_data), client);
}
-
-/*
- * nm_dhcp_manager_cancel_transaction
- *
- * Stop any in-progress DHCP transaction on a particular device.
- *
- */
-void
-nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager,
- const char *iface)
+static void
+client_timeout (NMDHCPClient *client, gpointer user_data)
+{
+ remove_client (NM_DHCP_MANAGER (user_data), client);
+}
+
+static void
+add_client (NMDHCPManager *self, NMDHCPClient *client)
+{
+ NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ guint id;
+
+ id = g_signal_connect (client, "state-changed", G_CALLBACK (client_state_changed), self);
+ g_object_set_data (G_OBJECT (client), STATE_ID_TAG, GUINT_TO_POINTER (id));
+
+ id = g_signal_connect (client, "timeout", G_CALLBACK (client_timeout), self);
+ g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id));
+
+ g_hash_table_insert (priv->clients, client, g_object_ref (client));
+}
+
+static NMDHCPClient *
+client_start (NMDHCPManager *self,
+ const char *iface,
+ const char *uuid,
+ gboolean ipv6,
+ NMSettingIP4Config *s_ip4,
+ NMSettingIP6Config *s_ip6,
+ guint32 timeout,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only)
{
- NMDHCPDevice *device;
NMDHCPManagerPrivate *priv;
+ NMDHCPClient *client;
+ gboolean success = FALSE;
- g_return_if_fail (NM_IS_DHCP_MANAGER (manager));
- g_return_if_fail (iface != NULL);
-
- priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
-
- device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface);
-
- if (!device || !device->pid)
- return;
-
- nm_dhcp_manager_cancel_transaction_real (device);
-}
-
-static void
-process_classful_routes (GHashTable *options, NMIP4Config *ip4_config)
-{
- const char *str;
- char **searches, **s;
-
- str = g_hash_table_lookup (options, "new_static_routes");
- if (!str)
- return;
-
- searches = g_strsplit (str, " ", 0);
- if ((g_strv_length (searches) % 2)) {
- nm_info (" static routes provided, but invalid");
- goto out;
- }
-
- for (s = searches; *s; s += 2) {
- NMIP4Route *route;
- struct in_addr rt_addr;
- struct in_addr rt_route;
-
- if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
- nm_warning ("DHCP provided invalid static route address: '%s'", *s);
- continue;
- }
- if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
- nm_warning ("DHCP provided invalid static route gateway: '%s'", *(s + 1));
- continue;
- }
-
- // FIXME: ensure the IP addresse and route are sane
-
- route = nm_ip4_route_new ();
- nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr);
- nm_ip4_route_set_prefix (route, 32); /* 255.255.255.255 */
- nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr);
-
- nm_ip4_config_take_route (ip4_config, route);
- nm_info (" static route %s gw %s", *s, *(s + 1));
- }
-
-out:
- g_strfreev (searches);
-}
-
-static void
-process_domain_search (NMIP4Config *ip4_config, const char *str)
-{
- char **searches, **s;
- char *unescaped, *p;
- int i;
-
- g_return_if_fail (str != NULL);
- g_return_if_fail (ip4_config != NULL);
-
- p = unescaped = g_strdup (str);
- do {
- p = strstr (p, "\\032");
- if (!p)
- break;
-
- /* Clear the escaped space with real spaces */
- for (i = 0; i < 4; i++)
- *p++ = ' ';
- } while (*p++);
-
- if (strchr (unescaped, '\\')) {
- nm_info (" invalid domain search: '%s'", unescaped);
- goto out;
- }
-
- searches = g_strsplit (unescaped, " ", 0);
- for (s = searches; *s; s++) {
- if (strlen (*s)) {
- nm_info (" domain search '%s'", *s);
- nm_ip4_config_add_search (ip4_config, *s);
- }
- }
- g_strfreev (searches);
-
-out:
- g_free (unescaped);
-}
-
-/* Given a table of DHCP options from the client, convert into an IP4Config */
-NMIP4Config *
-nm_dhcp_manager_options_to_ip4_config (const char *iface, GHashTable *options)
-{
- NMIP4Config *ip4_config = NULL;
- struct in_addr tmp_addr;
- NMIP4Address *addr = NULL;
- char *str = NULL;
- guint32 gwaddr = 0;
- gboolean have_classless = FALSE;
-
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
g_return_val_if_fail (iface != NULL, NULL);
- g_return_val_if_fail (options != NULL, NULL);
-
- ip4_config = nm_ip4_config_new ();
- if (!ip4_config) {
- nm_warning ("%s: couldn't allocate memory for an IP4Config!", iface);
- return NULL;
- }
-
- addr = nm_ip4_address_new ();
- if (!addr) {
- nm_warning ("%s: couldn't allocate memory for an IP4 Address!", iface);
- goto error;
- }
-
- str = g_hash_table_lookup (options, "new_ip_address");
- if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
- nm_ip4_address_set_address (addr, tmp_addr.s_addr);
- nm_info (" address %s", str);
- } else
- goto error;
-
- str = g_hash_table_lookup (options, "new_subnet_mask");
- if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
- nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr));
- nm_info (" prefix %d (%s)", nm_ip4_address_get_prefix (addr), str);
- }
-
- /* Routes: if the server returns classless static routes, we MUST ignore
- * the 'static_routes' option.
- */
- have_classless = nm_dhcp_client_process_classless_routes (options, ip4_config, &gwaddr);
- if (!have_classless) {
- gwaddr = 0; /* Ensure client code doesn't lie */
- process_classful_routes (options, ip4_config);
- }
-
- if (gwaddr) {
- char buf[INET_ADDRSTRLEN + 1];
-
- inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf));
- nm_info (" gateway %s", buf);
- nm_ip4_address_set_gateway (addr, gwaddr);
- } else {
- /* If the gateway wasn't provided as a classless static route with a
- * subnet length of 0, try to find it using the old-style 'routers' option.
- */
- str = g_hash_table_lookup (options, "new_routers");
- if (str) {
- char **routers = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = routers; *s; s++) {
- /* FIXME: how to handle multiple routers? */
- if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
- nm_ip4_address_set_gateway (addr, tmp_addr.s_addr);
- nm_info (" gateway %s", *s);
- break;
- } else
- nm_warning ("Ignoring invalid gateway '%s'", *s);
- }
- g_strfreev (routers);
- }
- }
-
- nm_ip4_config_take_address (ip4_config, addr);
- addr = NULL;
-
- str = g_hash_table_lookup (options, "new_host_name");
- if (str)
- nm_info (" hostname '%s'", str);
-
- str = g_hash_table_lookup (options, "new_domain_name_servers");
- if (str) {
- char **searches = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = searches; *s; s++) {
- if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
- nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr);
- nm_info (" nameserver '%s'", *s);
- } else
- nm_warning ("Ignoring invalid nameserver '%s'", *s);
- }
- g_strfreev (searches);
- }
-
- str = g_hash_table_lookup (options, "new_domain_name");
- if (str) {
- char **domains = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = domains; *s; s++) {
- nm_info (" domain name '%s'", *s);
- nm_ip4_config_add_domain (ip4_config, *s);
- }
- g_strfreev (domains);
- }
-
- str = g_hash_table_lookup (options, "new_domain_search");
- if (str)
- process_domain_search (ip4_config, str);
-
- str = g_hash_table_lookup (options, "new_netbios_name_servers");
- if (str) {
- char **searches = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = searches; *s; s++) {
- if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
- nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr);
- nm_info (" wins '%s'", *s);
- } else
- nm_warning ("Ignoring invalid WINS server '%s'", *s);
- }
- g_strfreev (searches);
- }
-
- str = g_hash_table_lookup (options, "new_interface_mtu");
- if (str) {
- int int_mtu;
-
- errno = 0;
- int_mtu = strtol (str, NULL, 10);
- if ((errno == EINVAL) || (errno == ERANGE))
- goto error;
-
- if (int_mtu > 576)
- nm_ip4_config_set_mtu (ip4_config, int_mtu);
- }
-
- return ip4_config;
-
-error:
- if (addr)
- nm_ip4_address_unref (addr);
- g_object_unref (ip4_config);
- return NULL;
-}
-
-/*
- * nm_dhcp_manager_get_ip4_config
- *
- * Get IP4 configuration values from the DHCP daemon
- *
- */
-NMIP4Config *
-nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager,
- const char *iface)
-{
- NMDHCPManagerPrivate *priv;
- NMDHCPDevice *device;
-
- g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
- g_return_val_if_fail (iface != NULL, NULL);
-
- priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
-
- device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface);
- if (!device) {
- nm_warning ("Device '%s' transaction not started.", iface);
- return NULL;
- }
-
- if (!state_is_bound (device->state)) {
- nm_warning ("%s: dhcp client didn't bind to a lease.", device->iface);
- return NULL;
- }
-
- return nm_dhcp_manager_options_to_ip4_config (iface, device->options);
-}
-
-#define NEW_TAG "new_"
-#define OLD_TAG "old_"
-
-typedef struct {
- GHFunc func;
- gpointer user_data;
-} Dhcp4ForeachInfo;
-
-static void
-iterate_dhcp4_config_option (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- Dhcp4ForeachInfo *info = (Dhcp4ForeachInfo *) user_data;
- char *tmp_key = NULL;
- const char **p;
- static const char *filter_options[] = {
- "interface", "pid", "reason", "dhcp_message_type", NULL
- };
-
- /* Filter out stuff that's not actually new DHCP options */
- for (p = filter_options; *p; p++) {
- if (!strcmp (*p, (const char *) key))
- return;
- if (!strncmp ((const char *) key, OLD_TAG, strlen (OLD_TAG)))
- return;
- }
-
- /* Remove the "new_" prefix that dhclient passes back */
- if (!strncmp ((const char *) key, NEW_TAG, strlen (NEW_TAG)))
- tmp_key = g_strdup ((const char *) (key + strlen (NEW_TAG)));
- else
- tmp_key = g_strdup ((const char *) key);
-
- (*info->func) ((gpointer) tmp_key, value, info->user_data);
- g_free (tmp_key);
-}
-
-gboolean
-nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self,
- const char *iface,
- GHFunc func,
- gpointer user_data)
-{
- NMDHCPManagerPrivate *priv;
- NMDHCPDevice *device;
- Dhcp4ForeachInfo info = { NULL, NULL };
-
- g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), FALSE);
- g_return_val_if_fail (iface != NULL, FALSE);
- g_return_val_if_fail (func != NULL, FALSE);
+ g_return_val_if_fail (uuid != NULL, NULL);
priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface);
- if (!device) {
- nm_warning ("Device '%s' transaction not started.", iface);
- return FALSE;
+ /* Kill any old client instance */
+ client = get_client_for_iface (self, iface, ipv6);
+ if (client) {
+ nm_dhcp_client_stop (client);
+ remove_client (self, client);
}
- if (!state_is_bound (device->state)) {
- nm_warning ("%s: dhclient didn't bind to a lease.", device->iface);
- return FALSE;
+ /* And make a new one */
+ client = g_object_new (priv->client_type,
+ NM_DHCP_CLIENT_INTERFACE, iface,
+ NM_DHCP_CLIENT_IPV6, ipv6,
+ NM_DHCP_CLIENT_UUID, uuid,
+ NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
+ NULL);
+ g_return_val_if_fail (client != NULL, NULL);
+ add_client (self, client);
+
+ if (ipv6)
+ success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, info_only);
+ else
+ success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr);
+
+ if (!success) {
+ remove_client (self, client);
+ g_object_unref (client);
+ client = NULL;
}
- info.func = func;
- info.user_data = user_data;
- g_hash_table_foreach (device->options, iterate_dhcp4_config_option, &info);
- return TRUE;
+ return client;
+}
+
+/* Caller owns a reference to the NMDHCPClient on return */
+NMDHCPClient *
+nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
+ const char *iface,
+ const char *uuid,
+ NMSettingIP4Config *s_ip4,
+ guint32 timeout,
+ guint8 *dhcp_anycast_addr)
+{
+ NMDHCPManagerPrivate *priv;
+ NMDHCPClient *client = NULL;
+
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+
+ priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+
+ if (s_ip4) {
+ if ( nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4)
+ && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL)
+ && priv->hostname_provider != NULL) {
+
+ s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4)));
+
+ /* We're asked to send the hostname to DHCP server, the hostname
+ * isn't specified, and a hostname provider is registered: use that
+ */
+ g_object_set (G_OBJECT (s_ip4),
+ NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME,
+ nm_hostname_provider_get_hostname (priv->hostname_provider),
+ NULL);
+ } else
+ g_object_ref (s_ip4);
+ }
+
+ client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, FALSE);
+
+ if (s_ip4)
+ g_object_unref (s_ip4);
+
+ return client;
+}
+
+/* Caller owns a reference to the NMDHCPClient on return */
+NMDHCPClient *
+nm_dhcp_manager_start_ip6 (NMDHCPManager *self,
+ const char *iface,
+ const char *uuid,
+ NMSettingIP6Config *s_ip6,
+ guint32 timeout,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only)
+{
+ return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, info_only);
}
static void
@@ -1123,14 +496,106 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager,
}
GSList *
-nm_dhcp_manager_get_lease_ip4_config (NMDHCPManager *self,
- const char *iface,
- const char *uuid)
+nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
+ const char *iface,
+ const char *uuid)
{
+ g_return_val_if_fail (self != NULL, NULL);
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);
- return nm_dhcp_client_get_lease_ip4_config (iface, uuid);
+ return NM_DHCP_MANAGER_GET_PRIVATE (self)->get_lease_config_func (iface, uuid);
+}
+
+NMIP4Config *
+nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
+ const char *iface,
+ GHashTable *options,
+ const char *reason)
+{
+ NMDHCPClient *client;
+ NMIP4Config *config;
+ GType client_type;
+ GError *error = NULL;
+
+ client_type = get_client_type (dhcp_client, &error);
+ if (!client_type) {
+ g_warning ("Error: %s", error ? error->message : "(unknown)");
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ client = (NMDHCPClient *) g_object_new (client_type,
+ NM_DHCP_CLIENT_INTERFACE, iface,
+ NULL);
+ g_return_val_if_fail (client != NULL, NULL);
+ nm_dhcp_client_new_options (client, options, reason);
+ config = nm_dhcp_client_get_ip4_config (client, TRUE);
+ g_object_unref (client);
+
+ return config;
+}
+
+/***************************************************/
+
+NMDHCPManager *
+nm_dhcp_manager_get (void)
+{
+ g_warn_if_fail (singleton != NULL);
+ return g_object_ref (singleton);
+}
+
+static void
+nm_dhcp_manager_init (NMDHCPManager *manager)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (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 (object);
+
+ if (priv->hostname_provider) {
+ g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object);
+ priv->hostname_provider = NULL;
+ }
+
+ if (priv->clients)
+ g_hash_table_destroy (priv->clients);
+ if (priv->proxy)
+ g_object_unref (priv->proxy);
+ if (priv->dbus_mgr)
+ g_object_unref (priv->dbus_mgr);
+
+ 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);
+
+ g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate));
+
+ /* virtual methods */
+ object_class->finalize = finalize;
+ object_class->dispose = dispose;
}
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
index 124008493..648f4cd0d 100644
--- a/src/dhcp-manager/nm-dhcp-manager.h
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -15,7 +15,7 @@
* 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -26,13 +26,13 @@
#include
#include
+#include
+#include "nm-dhcp-client.h"
#include "nm-ip4-config.h"
#include "nm-dhcp4-config.h"
#include "nm-hostname-provider.h"
-#define NM_DHCP_MANAGER_RUN_DIR LOCALSTATEDIR "/run"
-
#define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type ())
#define NM_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManager))
#define NM_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass))
@@ -40,93 +40,47 @@
#define NM_IS_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_MANAGER))
#define NM_DHCP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass))
-typedef enum {
- DHC_NBI=0, /* no broadcast interfaces found */
- DHC_PREINIT, /* configuration started */
- DHC_BOUND, /* lease obtained */
- DHC_IPV4LL, /* IPv4LL address obtained */
- DHC_RENEW, /* lease renewed */
- DHC_REBOOT, /* have valid lease, but now obtained a different one */
- DHC_REBIND, /* new, different lease */
- DHC_STOP, /* remove old lease */
- DHC_MEDIUM, /* media selection begun */
- DHC_TIMEOUT, /* timed out contacting DHCP server */
- DHC_FAIL, /* all attempts to contact server timed out, sleeping */
- DHC_EXPIRE, /* lease has expired, renewing */
- DHC_RELEASE, /* releasing lease */
- DHC_START, /* sent when dhclient started OK */
- DHC_ABEND, /* dhclient exited abnormally */
- DHC_END, /* dhclient exited normally */
- DHC_END_OPTIONS, /* last option in subscription sent */
-} NMDHCPState;
-
typedef struct {
GObject parent;
} NMDHCPManager;
typedef struct {
GObjectClass parent;
-
- /* Signals */
- void (*state_changed) (NMDHCPManager *manager, char *iface, NMDHCPState state);
- void (*timeout) (NMDHCPManager *manager, char *iface);
} NMDHCPManagerClass;
-typedef struct {
- char * iface;
- guchar state;
- GPid pid;
- char * pid_file;
- char * conf_file;
- char * lease_file;
- guint timeout_id;
- guint watch_id;
- NMDHCPManager * manager;
- GHashTable * options;
-} NMDHCPDevice;
-
GType nm_dhcp_manager_get_type (void);
NMDHCPManager *nm_dhcp_manager_get (void);
+
void nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager,
NMHostnameProvider *provider);
-gboolean nm_dhcp_manager_begin_transaction (NMDHCPManager *manager,
- const char *iface,
- const char *uuid,
- NMSettingIP4Config *s_ip4,
- guint32 timeout,
- guint8 *dhcp_anycast_addr);
-void nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager,
- const char *iface);
-NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, const char *iface);
-NMDHCPState nm_dhcp_manager_get_state_for_device (NMDHCPManager *manager, const char *iface);
+NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *manager,
+ const char *iface,
+ const char *uuid,
+ NMSettingIP4Config *s_ip4,
+ guint32 timeout,
+ guint8 *dhcp_anycast_addr);
-gboolean nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self,
- const char *iface,
- GHFunc func,
- gpointer user_data);
+NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager,
+ const char *iface,
+ const char *uuid,
+ NMSettingIP6Config *s_ip6,
+ guint32 timeout,
+ guint8 *dhcp_anycast_addr,
+ gboolean info_only);
-GSList * nm_dhcp_manager_get_lease_ip4_config (NMDHCPManager *self,
- const char *iface,
- const char *uuid);
+GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
+ const char *iface,
+ const char *uuid);
-/* The following are implemented by the DHCP client backends */
-GPid nm_dhcp_client_start (NMDHCPDevice *device,
- const char *uuid,
- NMSettingIP4Config *s_ip4,
- guint8 *anycast_addr);
-void nm_dhcp_client_stop (NMDHCPDevice *device, pid_t pid);
+/* For testing only */
+NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
+ const char *iface,
+ GHashTable *options,
+ const char *reason);
-gboolean nm_dhcp_client_process_classless_routes (GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *gwaddr);
-
-GSList * nm_dhcp_client_get_lease_ip4_config (const char *iface,
- const char *uuid);
-
-/* Test functions */
-NMIP4Config *nm_dhcp_manager_options_to_ip4_config (const char *iface,
- GHashTable *options);
+/* Only for NetworkManager.c */
+NMDHCPManager *nm_dhcp_manager_new (const char *client, GError **error);
#endif /* NM_DHCP_MANAGER_H */
diff --git a/src/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c
index f41169cb1..20e1a7f93 100644
--- a/src/ip6-manager/nm-ip6-manager.c
+++ b/src/ip6-manager/nm-ip6-manager.c
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include
@@ -165,10 +165,8 @@ nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete),
NULL, NULL,
- _nm_marshal_VOID__STRING_BOOLEAN,
- G_TYPE_NONE, 2,
- G_TYPE_STRING,
- G_TYPE_BOOLEAN);
+ _nm_marshal_VOID__STRING_UINT_BOOLEAN,
+ G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_BOOLEAN);
signals[CONFIG_CHANGED] =
g_signal_new ("config-changed",
@@ -176,9 +174,8 @@ nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed),
NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1,
- G_TYPE_STRING);
+ _nm_marshal_VOID__STRING_UINT,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT);
}
static void
@@ -233,10 +230,16 @@ nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
GINT_TO_POINTER (ifindex));
}
+typedef struct {
+ NMIP6Device *device;
+ guint dhcp_opts;
+} CallbackInfo;
+
static gboolean
finish_addrconf (gpointer user_data)
{
- NMIP6Device *device = user_data;
+ CallbackInfo *info = user_data;
+ NMIP6Device *device = info->device;
NMIP6Manager *manager = device->manager;
char *iface_copy;
@@ -245,7 +248,7 @@ finish_addrconf (gpointer user_data)
if (device->state >= device->target_state) {
g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
- device->iface, TRUE);
+ device->iface, info->dhcp_opts, TRUE);
} else {
nm_info ("Device '%s' IP6 addrconf timed out or failed.",
device->iface);
@@ -254,7 +257,7 @@ finish_addrconf (gpointer user_data)
nm_ip6_manager_cancel_addrconf (manager, device->iface);
g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
- iface_copy, FALSE);
+ iface_copy, info->dhcp_opts, FALSE);
g_free (iface_copy);
}
@@ -265,11 +268,12 @@ finish_addrconf (gpointer user_data)
static gboolean
emit_config_changed (gpointer user_data)
{
- NMIP6Device *device = user_data;
+ CallbackInfo *info = user_data;
+ NMIP6Device *device = info->device;
NMIP6Manager *manager = device->manager;
device->config_changed_id = 0;
- g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface);
+ g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface, info->dhcp_opts);
return FALSE;
}
@@ -279,9 +283,10 @@ static gboolean
rdnss_expired (gpointer user_data)
{
NMIP6Device *device = user_data;
+ CallbackInfo info = { device, IP6_DHCP_OPT_NONE };
set_rdnss_timeout (device);
- emit_config_changed (device);
+ emit_config_changed (&info);
return FALSE;
}
@@ -323,6 +328,18 @@ set_rdnss_timeout (NMIP6Device *device)
}
}
+static CallbackInfo *
+callback_info_new (NMIP6Device *device, guint dhcp_opts)
+{
+ CallbackInfo *info;
+
+ info = g_malloc0 (sizeof (CallbackInfo));
+ info->device = device;
+ info->dhcp_opts = dhcp_opts;
+
+ return info;
+}
+
static void
nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
{
@@ -333,6 +350,8 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
struct in6_addr *addr;
struct rtnl_link *link;
guint flags;
+ CallbackInfo *info;
+ guint dhcp_opts = IP6_DHCP_OPT_NONE;
for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache);
rtnladdr;
@@ -365,8 +384,10 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
if ((flags & IF_RA_RCVD) && device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT;
-// if (flags & (IF_RA_MANAGED | IF_RA_OTHERCONF))
-// device->need_dhcp = TRUE;
+ if (flags & IF_RA_MANAGED)
+ dhcp_opts = IP6_DHCP_OPT_MANAGED;
+ else if (flags & IF_RA_OTHERCONF)
+ dhcp_opts = IP6_DHCP_OPT_OTHERCONF;
if (!device->addrconf_complete) {
if (device->state >= device->target_state ||
@@ -376,13 +397,20 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
*/
if (device->finish_addrconf_id)
g_source_remove (device->finish_addrconf_id);
- device->finish_addrconf_id = g_idle_add (finish_addrconf,
- device);
+
+ info = callback_info_new (device, dhcp_opts);
+ device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ finish_addrconf,
+ info,
+ (GDestroyNotify) g_free);
}
} else if (config_changed) {
if (!device->config_changed_id) {
- device->config_changed_id = g_idle_add (emit_config_changed,
- device);
+ info = callback_info_new (device, dhcp_opts);
+ device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ emit_config_changed,
+ info,
+ (GDestroyNotify) g_free);
}
}
}
@@ -717,6 +745,7 @@ nm_ip6_manager_begin_addrconf (NMIP6Manager *manager,
{
NMIP6ManagerPrivate *priv;
NMIP6Device *device;
+ CallbackInfo *info;
g_return_if_fail (NM_IS_IP6_MANAGER (manager));
g_return_if_fail (iface != NULL);
@@ -731,9 +760,12 @@ nm_ip6_manager_begin_addrconf (NMIP6Manager *manager,
device->addrconf_complete = FALSE;
/* Set up a timeout on the transaction to kill it after the timeout */
- device->finish_addrconf_id = g_timeout_add_seconds (NM_IP6_TIMEOUT,
- finish_addrconf,
- device);
+ info = callback_info_new (device, 0);
+ device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ NM_IP6_TIMEOUT,
+ finish_addrconf,
+ info,
+ (GDestroyNotify) g_free);
/* Sync flags, etc, from netlink; this will also notice if the
* device is already fully configured and schedule the
diff --git a/src/ip6-manager/nm-ip6-manager.h b/src/ip6-manager/nm-ip6-manager.h
index 33b2b9893..d0cf4b08a 100644
--- a/src/ip6-manager/nm-ip6-manager.h
+++ b/src/ip6-manager/nm-ip6-manager.h
@@ -35,6 +35,12 @@
#define NM_IS_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_MANAGER))
#define NM_IP6_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass))
+enum {
+ IP6_DHCP_OPT_NONE = 0,
+ IP6_DHCP_OPT_OTHERCONF,
+ IP6_DHCP_OPT_MANAGED
+};
+
typedef struct {
GObject parent;
} NMIP6Manager;
@@ -47,13 +53,18 @@ typedef struct {
/* addrconf_complete is emitted only during initial configuration to indicate
* that the initial configuration is complete.
*/
- void (*addrconf_complete) (NMIP6Manager *manager, char *iface, gboolean success);
+ void (*addrconf_complete) (NMIP6Manager *manager,
+ char *iface,
+ guint dhcp_opts,
+ gboolean success);
/* config_changed gets emitted only *after* initial configuration is
* complete; it's like DHCP renew and indicates that the existing config
* of the interface has changed.
*/
- void (*config_changed) (NMIP6Manager *manager, char *iface);
+ void (*config_changed) (NMIP6Manager *manager,
+ char *iface,
+ guint dhcp_opts);
} NMIP6ManagerClass;
GType nm_ip6_manager_get_type (void);
diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c
index 037d60338..f20afcc45 100644
--- a/src/nm-device-ethernet.c
+++ b/src/nm-device-ethernet.c
@@ -15,7 +15,7 @@
* 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -1608,9 +1608,9 @@ ip4_match_config (NMDevice *self, NMConnection *connection)
/* Get any saved leases that apply to this connection */
dhcp_mgr = nm_dhcp_manager_get ();
- leases = nm_dhcp_manager_get_lease_ip4_config (dhcp_mgr,
- nm_device_get_iface (self),
- nm_setting_connection_get_uuid (s_con));
+ leases = nm_dhcp_manager_get_lease_config (dhcp_mgr,
+ nm_device_get_iface (self),
+ nm_setting_connection_get_uuid (s_con));
g_object_unref (dhcp_mgr);
method = nm_setting_ip4_config_get_method (s_ip4);
diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c
index 70cce8b62..f0a4fd1c6 100644
--- a/src/nm-device-interface.c
+++ b/src/nm-device-interface.c
@@ -137,6 +137,14 @@ nm_device_interface_init (gpointer g_iface)
DBUS_TYPE_G_OBJECT_PATH,
G_PARAM_READWRITE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boxed (NM_DEVICE_INTERFACE_DHCP6_CONFIG,
+ "DHCP6 Config",
+ "DHCP6 Config",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_PARAM_READWRITE));
+
g_object_interface_install_property
(g_iface,
g_param_spec_uint (NM_DEVICE_INTERFACE_STATE,
diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h
index 0ec5e3da1..306f2dbe6 100644
--- a/src/nm-device-interface.h
+++ b/src/nm-device-interface.h
@@ -53,6 +53,7 @@ typedef enum
#define NM_DEVICE_INTERFACE_IP4_CONFIG "ip4-config"
#define NM_DEVICE_INTERFACE_DHCP4_CONFIG "dhcp4-config"
#define NM_DEVICE_INTERFACE_IP6_CONFIG "ip6-config"
+#define NM_DEVICE_INTERFACE_DHCP6_CONFIG "dhcp6-config"
#define NM_DEVICE_INTERFACE_STATE "state"
#define NM_DEVICE_INTERFACE_DEVICE_TYPE "device-type" /* ugh */
#define NM_DEVICE_INTERFACE_MANAGED "managed"
@@ -69,6 +70,7 @@ typedef enum {
NM_DEVICE_INTERFACE_PROP_IP4_CONFIG,
NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG,
NM_DEVICE_INTERFACE_PROP_IP6_CONFIG,
+ NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG,
NM_DEVICE_INTERFACE_PROP_STATE,
NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE,
NM_DEVICE_INTERFACE_PROP_MANAGED,
diff --git a/src/nm-device.c b/src/nm-device.c
index 6c0f99ca4..7a2f83d96 100644
--- a/src/nm-device.c
+++ b/src/nm-device.c
@@ -15,7 +15,7 @@
* 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -102,13 +102,16 @@ typedef struct {
gboolean ip4_ready;
gboolean ip6_ready;
- /* IP4 configuration info */
- NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */
+ /* Generic DHCP stuff */
NMDHCPManager * dhcp_manager;
guint32 dhcp_timeout;
- gulong dhcp_state_sigid;
- gulong dhcp_timeout_sigid;
GByteArray * dhcp_anycast_address;
+
+ /* IP4 configuration info */
+ NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */
+ NMDHCPClient * dhcp4_client;
+ gulong dhcp4_state_sigid;
+ gulong dhcp4_timeout_sigid;
NMDHCP4Config * dhcp4_config;
/* dnsmasq stuff for shared connections */
@@ -127,6 +130,12 @@ typedef struct {
gulong ip6_addrconf_sigid;
gulong ip6_config_changed_sigid;
gboolean ip6_waiting_for_config;
+ guint32 ip6_dhcp_opt;
+
+ NMDHCPClient * dhcp6_client;
+ gulong dhcp6_state_sigid;
+ gulong dhcp6_timeout_sigid;
+ NMDHCP6Config * dhcp6_config;
/* inhibit autoconnect feature */
gboolean autoconnect_inhibit;
@@ -159,6 +168,11 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev,
gboolean assumed,
NMDeviceStateReason *reason);
+static NMActStageReturn dhcp6_start (NMDevice *self,
+ NMConnection *connection,
+ guint32 dhcp_opt,
+ NMDeviceStateReason *reason);
+
static void
device_interface_init (NMDeviceInterface *device_interface_class)
{
@@ -220,6 +234,8 @@ constructor (GType type,
if (NM_DEVICE_GET_CLASS (dev)->update_hw_address)
NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev);
+ priv->dhcp_manager = nm_dhcp_manager_get ();
+
priv->initialized = TRUE;
return object;
@@ -525,31 +541,77 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
static void
ip6_addrconf_complete (NMIP6Manager *ip6_manager,
- const char *iface,
- gboolean success,
- gpointer user_data)
+ const char *iface,
+ guint dhcp_opts,
+ gboolean success,
+ gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMActRequest *req;
+ NMConnection *connection;
+ NMActStageReturn ret;
+ NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+ NMDeviceState state;
if (strcmp (nm_device_get_ip_iface (self), iface) != 0)
return;
- if (!nm_device_get_act_request (self))
+ req = nm_device_get_act_request (self);
+ if (!req)
+ return;
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+
+ if (!priv->ip6_waiting_for_config)
return;
- if (priv->ip6_waiting_for_config) {
- priv->ip6_waiting_for_config = FALSE;
- if (success)
- nm_device_activate_schedule_stage4_ip6_config_get (self);
- else
- nm_device_activate_schedule_stage4_ip6_config_timeout (self);
+ priv->ip6_waiting_for_config = FALSE;
+
+ if (!success) {
+ nm_device_activate_schedule_stage4_ip6_config_timeout (self);
+ return;
+ }
+
+ priv->ip6_dhcp_opt = dhcp_opts;
+
+ /* If addrconf is all that's required, we're done */
+ if (dhcp_opts == IP6_DHCP_OPT_NONE) {
+ nm_device_activate_schedule_stage4_ip6_config_get (self);
+ return;
+ }
+
+ /* If the router said to use DHCP for managed or otherconf, do it */
+
+ /* Don't re-start DHCPv6 if it's already in progress */
+ state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+ if ((state != NM_DEVICE_STATE_IP_CONFIG) || priv->dhcp6_client)
+ return;
+
+ nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6"
+ " as requested by IPv6 router...",
+ priv->iface);
+
+ ret = dhcp6_start (self, connection, dhcp_opts, &reason);
+ switch (ret) {
+ case NM_ACT_STAGE_RETURN_SUCCESS:
+ /* Shouldn't get this, but handle it anyway */
+ g_warn_if_reached ();
+ nm_device_activate_schedule_stage4_ip6_config_get (self);
+ break;
+ case NM_ACT_STAGE_RETURN_POSTPONE:
+ /* Success; wait for DHCPv6 to complete */
+ break;
+ default:
+ nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+ break;
}
}
static void
ip6_config_changed (NMIP6Manager *ip6_manager,
- const char *iface,
- gpointer user_data)
+ const char *iface,
+ guint dhcp_opts,
+ gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
@@ -561,49 +623,58 @@ ip6_config_changed (NMIP6Manager *ip6_manager,
nm_device_activate_schedule_stage4_ip6_config_get (self);
}
-static void
-nm_device_setup_ip6 (NMDevice *self)
+static gboolean
+ip6_method_matches (NMConnection *connection, const char *match)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- NMActRequest *req;
- NMConnection *connection;
- const char *ip_iface, *method = NULL;
NMSettingIP6Config *s_ip6;
-
- req = nm_device_get_act_request (self);
- if (!req)
- return;
- connection = nm_act_request_get_connection (req);
- if (!connection)
- return;
+ const char *method = NULL;
s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
if (s_ip6)
method = nm_setting_ip6_config_get_method (s_ip6);
- if (!method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
+ return method && !strcmp (method, match);
+}
+
+static void
+addrconf6_setup (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMActRequest *req;
+ NMConnection *connection;
+ const char *ip_iface;
+ NMSettingIP6Config *s_ip6;
+
+ priv->ip6_waiting_for_config = FALSE;
+ priv->ip6_dhcp_opt = IP6_DHCP_OPT_NONE;
+
+ req = nm_device_get_act_request (self);
+ g_assert (req);
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+
+ if (!ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO))
return;
if (!priv->ip6_manager) {
priv->ip6_manager = nm_ip6_manager_get ();
priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager,
- "addrconf-complete",
- G_CALLBACK (ip6_addrconf_complete),
- self);
+ "addrconf-complete",
+ G_CALLBACK (ip6_addrconf_complete),
+ self);
priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager,
- "config-changed",
- G_CALLBACK (ip6_config_changed),
- self);
+ "config-changed",
+ G_CALLBACK (ip6_config_changed),
+ self);
}
- priv->ip6_waiting_for_config = FALSE;
-
ip_iface = nm_device_get_ip_iface (self);
+ s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
nm_ip6_manager_prepare_interface (priv->ip6_manager, ip_iface, s_ip6);
}
static void
-nm_device_cleanup_ip6 (NMDevice *self)
+addrconf6_cleanup (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -625,6 +696,13 @@ nm_device_cleanup_ip6 (NMDevice *self)
priv->ip6_manager = NULL;
}
+static NMActStageReturn
+real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
+{
+ addrconf6_setup (self);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
/*
* nm_device_activate_stage1_device_prepare
*
@@ -635,6 +713,7 @@ static gboolean
nm_device_activate_stage1_device_prepare (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
const char *iface;
NMActStageReturn ret;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -642,12 +721,12 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
+ priv->ip4_ready = priv->ip6_ready = FALSE;
+
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
- nm_device_setup_ip6 (self);
-
ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
goto out;
@@ -687,13 +766,6 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
nm_device_get_iface (self));
}
-static NMActStageReturn
-real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
-{
- /* Nothing to do */
- return NM_ACT_STAGE_RETURN_SUCCESS;
-}
-
static NMActStageReturn
real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
{
@@ -1057,6 +1129,165 @@ aipd_exec (NMDevice *self, GError **error)
return TRUE;
}
+static void
+dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
+{
+ nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data),
+ (const char *) key,
+ (const char *) value);
+}
+
+static void
+dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
+{
+ nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
+ (const char *) key,
+ (const char *) value);
+}
+
+static void
+handle_dhcp_lease_change (NMDevice *device, gboolean ipv6)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ NMIP4Config *ip4_config;
+ NMSettingIP4Config *s_ip4;
+ NMIP6Config *ip6_config;
+ NMSettingIP6Config *s_ip6;
+ NMConnection *connection;
+ NMActRequest *req;
+ NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+ gboolean assumed;
+
+ req = nm_device_get_act_request (device);
+ g_assert (req);
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+ assumed = nm_act_request_get_assumed (req);
+
+ if (ipv6) {
+ ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
+ if (!ip6_config) {
+ nm_warning ("failed to get DHCPv6 config for rebind");
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+ return;
+ }
+
+ s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG));
+ nm_utils_merge_ip6_config (ip6_config, s_ip6);
+
+ g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
+
+ if (nm_device_set_ip6_config (device, ip6_config, assumed, &reason)) {
+ nm_dhcp6_config_reset (priv->dhcp6_config);
+ nm_dhcp_client_foreach_option (priv->dhcp6_client,
+ dhcp6_add_option_cb,
+ priv->dhcp6_config);
+ } else {
+ nm_warning ("Failed to update IPv6 config in response to DHCP event.");
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+ }
+ } else {
+ ip4_config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
+ if (!ip4_config) {
+ nm_warning ("failed to get DHCPv4 config for rebind");
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+ return;
+ }
+
+ s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG));
+ nm_utils_merge_ip4_config (ip4_config, s_ip4);
+
+ g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
+
+ if (nm_device_set_ip4_config (device, ip4_config, assumed, &reason)) {
+ nm_dhcp4_config_reset (priv->dhcp4_config);
+ nm_dhcp_client_foreach_option (priv->dhcp4_client,
+ dhcp4_add_option_cb,
+ priv->dhcp4_config);
+ } else {
+ nm_warning ("Failed to update IPv4 config in response to DHCP event.");
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+ }
+ }
+}
+
+static void
+dhcp_state_changed (NMDHCPClient *client,
+ NMDHCPState state,
+ gpointer user_data)
+{
+ NMDevice *device = NM_DEVICE (user_data);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ NMDeviceState dev_state;
+ gboolean ipv6;
+
+ ipv6 = nm_dhcp_client_get_ipv6 (client);
+ dev_state = nm_device_get_state (device);
+
+ switch (state) {
+ case DHC_BOUND4: /* lease obtained */
+ case DHC_BOUND6:
+ case DHC_RENEW4: /* lease renewed */
+ case DHC_RENEW6: /* lease renewed */
+ case DHC_REBOOT: /* have valid lease, but now obtained a different one */
+ case DHC_REBIND4: /* new, different lease */
+ case DHC_REBIND6: /* new, different lease */
+ if (dev_state == NM_DEVICE_STATE_IP_CONFIG) {
+ if (ipv6)
+ nm_device_activate_schedule_stage4_ip6_config_get (device);
+ else
+ nm_device_activate_schedule_stage4_ip4_config_get (device);
+ } else if (dev_state == NM_DEVICE_STATE_ACTIVATED)
+ handle_dhcp_lease_change (device, ipv6);
+ break;
+ case DHC_TIMEOUT: /* timed out contacting DHCP server */
+ if (ipv6) {
+ nm_dhcp6_config_reset (priv->dhcp6_config);
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
+ nm_device_activate_schedule_stage4_ip6_config_timeout (device);
+ } else {
+ nm_dhcp4_config_reset (priv->dhcp4_config);
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
+ nm_device_activate_schedule_stage4_ip4_config_timeout (device);
+ }
+ break;
+ case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
+ case DHC_ABEND: /* dhclient exited abnormally */
+ case DHC_END: /* dhclient exited normally */
+ if (ipv6)
+ nm_dhcp6_config_reset (priv->dhcp6_config);
+ else
+ nm_dhcp4_config_reset (priv->dhcp4_config);
+
+ /* dhclient quit and can't get/renew a lease; so kill the connection */
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED);
+ else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+dhcp_timeout (NMDHCPClient *client, gpointer user_data)
+{
+ NMDevice *device = NM_DEVICE (user_data);
+
+ if (!nm_device_get_act_request (device))
+ return;
+
+ nm_dhcp_client_stop (client);
+
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
+ if (nm_dhcp_client_get_ipv6 (client))
+ nm_device_activate_schedule_stage4_ip6_config_timeout (device);
+ else
+ nm_device_activate_schedule_stage4_ip4_config_timeout (device);
+ }
+}
+
static NMActStageReturn
real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason)
{
@@ -1090,22 +1321,34 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason)
if (!s_ip4 || !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- gboolean success;
guint8 *anycast = NULL;
+ /* Begin a DHCP transaction on the interface */
+
if (priv->dhcp_anycast_address)
anycast = priv->dhcp_anycast_address->data;
- /* Begin a DHCP transaction on the interface */
- nm_device_set_use_dhcp (self, TRUE);
+ /* Clear old exported DHCP options */
+ if (priv->dhcp4_config)
+ g_object_unref (priv->dhcp4_config);
+ priv->dhcp4_config = nm_dhcp4_config_new ();
- /* DHCP manager will cancel any transaction already in progress and we do not
- want to cancel this activation if we get "down" state from that. */
- g_signal_handler_block (priv->dhcp_manager, priv->dhcp_state_sigid);
- success = nm_dhcp_manager_begin_transaction (priv->dhcp_manager, ip_iface, uuid, s_ip4, priv->dhcp_timeout, anycast);
- g_signal_handler_unblock (priv->dhcp_manager, priv->dhcp_state_sigid);
+ priv->dhcp4_client = nm_dhcp_manager_start_ip4 (priv->dhcp_manager,
+ ip_iface,
+ uuid,
+ s_ip4,
+ priv->dhcp_timeout,
+ anycast);
+ if (priv->dhcp4_client) {
+ priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client,
+ "state-changed",
+ G_CALLBACK (dhcp_state_changed),
+ self);
+ priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client,
+ "timeout",
+ G_CALLBACK (dhcp_timeout),
+ self);
- if (success) {
/* DHCP devices will be notified by the DHCP manager when
* stuff happens.
*/
@@ -1136,24 +1379,99 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason)
return ret;
}
+static NMActStageReturn
+dhcp6_start (NMDevice *self,
+ NMConnection *connection,
+ guint32 dhcp_opt,
+ NMDeviceStateReason *reason)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+ guint8 *anycast = NULL;
+ NMSettingIP6Config *s_ip6;
+ NMSettingConnection *s_con;
+ const char *uuid;
+ const char *ip_iface;
+
+ if (!connection) {
+ NMActRequest *req;
+
+ req = nm_device_get_act_request (self);
+ g_assert (req);
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+ }
+
+ /* Begin a DHCP transaction on the interface */
+
+ if (priv->dhcp_anycast_address)
+ anycast = priv->dhcp_anycast_address->data;
+
+ /* Clear old exported DHCP options */
+ if (priv->dhcp6_config)
+ g_object_unref (priv->dhcp6_config);
+ priv->dhcp6_config = nm_dhcp6_config_new ();
+
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ uuid = nm_setting_connection_get_uuid (s_con);
+
+ s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+
+ ip_iface = nm_device_get_ip_iface (self);
+ priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager,
+ ip_iface,
+ uuid,
+ s_ip6,
+ priv->dhcp_timeout,
+ anycast,
+ (dhcp_opt == IP6_DHCP_OPT_OTHERCONF) ? TRUE : FALSE);
+ if (priv->dhcp6_client) {
+ priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client,
+ "state-changed",
+ G_CALLBACK (dhcp_state_changed),
+ self);
+ priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client,
+ "timeout",
+ G_CALLBACK (dhcp_timeout),
+ self);
+
+ /* DHCP devices will be notified by the DHCP manager when stuff happens */
+ ret = NM_ACT_STAGE_RETURN_POSTPONE;
+ } else {
+ *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED;
+ ret = NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ return ret;
+}
+
static NMActStageReturn
real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- const char *ip_iface = nm_device_get_ip_iface (self);
+ const char *ip_iface;
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+ NMActRequest *req;
+ NMConnection *connection;
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
- /* If we are ignoring IPv6 on this interface then we can go right
- * to stage 4.
- */
- if (!priv->ip6_manager)
- return NM_ACT_STAGE_RETURN_SUCCESS;
+ req = nm_device_get_act_request (self);
+ g_assert (req);
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
- priv->ip6_waiting_for_config = TRUE;
- nm_ip6_manager_begin_addrconf (priv->ip6_manager, ip_iface);
+ ip_iface = nm_device_get_ip_iface (self);
- return NM_ACT_STAGE_RETURN_POSTPONE;
+ if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+ priv->ip6_waiting_for_config = TRUE;
+ nm_ip6_manager_begin_addrconf (priv->ip6_manager, ip_iface);
+ ret = NM_ACT_STAGE_RETURN_POSTPONE;
+ } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP))
+ ret = dhcp6_start (self, connection, IP6_DHCP_OPT_MANAGED, reason);
+
+ return ret;
}
@@ -1167,7 +1485,6 @@ static gboolean
nm_device_activate_stage3_ip_config_start (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
const char *iface;
NMActStageReturn ret;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -1175,8 +1492,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
- priv->ip4_ready = priv->ip6_ready = FALSE;
-
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
@@ -1283,14 +1598,6 @@ nm_device_new_ip4_shared_config (NMDevice *self, NMDeviceStateReason *reason)
return config;
}
-static void
-dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
-{
- nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data),
- (const char *) key,
- (const char *) value);
-}
-
static NMActStageReturn
real_act_stage4_get_ip4_config (NMDevice *self,
NMIP4Config **config,
@@ -1314,23 +1621,24 @@ real_act_stage4_get_ip4_config (NMDevice *self,
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
- if (nm_device_get_use_dhcp (self)) {
- *config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, ip_iface);
+ if (priv->dhcp4_client) {
+ /* DHCP */
+ *config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
if (*config) {
/* Merge user-defined overrides into the IP4Config to be applied */
nm_utils_merge_ip4_config (*config, s_ip4);
nm_dhcp4_config_reset (priv->dhcp4_config);
- nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager,
- ip_iface,
- dhcp4_add_option_cb,
- priv->dhcp4_config);
+ nm_dhcp_client_foreach_option (priv->dhcp4_client,
+ dhcp4_add_option_cb,
+ priv->dhcp4_config);
/* Notify of new DHCP4 config */
g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
} else
*reason = NM_DEVICE_STATE_REASON_DHCP_ERROR;
} else {
+ /* Not DHCP */
const char *method;
g_assert (s_ip4);
@@ -1514,11 +1822,11 @@ real_act_stage4_get_ip6_config (NMDevice *self,
NMIP6Config **config,
NMDeviceStateReason *reason)
{
- NMDevicePrivate *priv;
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
NMConnection *connection;
NMSettingIP6Config *s_ip6;
const char *ip_iface;
- const char *method = NULL;
g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -1531,25 +1839,45 @@ real_act_stage4_get_ip6_config (NMDevice *self,
g_assert (connection);
s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
- if (s_ip6)
- method = nm_setting_ip6_config_get_method (s_ip6);
- if (!method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
+ if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+ *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, ip_iface);
+ if (*config) {
+ /* Merge user-defined overrides into the IP6Config to be applied */
+ nm_utils_merge_ip6_config (*config, s_ip6);
+ ret = NM_ACT_STAGE_RETURN_SUCCESS;
+ } else {
+ *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+ goto out;
+ }
+ } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP))
+ g_assert (priv->dhcp6_client); /* sanity check */
+
+ /* Autoconf might have triggered DHCPv6 too */
+ if (priv->dhcp6_client) {
+ *config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
+ if (*config) {
+ /* Merge user-defined overrides into the IP4Config to be applied */
+ nm_utils_merge_ip6_config (*config, s_ip6);
+
+ nm_dhcp6_config_reset (priv->dhcp6_config);
+ nm_dhcp_client_foreach_option (priv->dhcp6_client,
+ dhcp6_add_option_cb,
+ priv->dhcp6_config);
+
+ /* Notify of new DHCP4 config */
+ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+ ret = NM_ACT_STAGE_RETURN_SUCCESS;
+ } else {
+ *reason = NM_DEVICE_STATE_REASON_DHCP_ERROR;
+ }
+ } else {
*config = NULL;
- return NM_ACT_STAGE_RETURN_SUCCESS;
+ ret = NM_ACT_STAGE_RETURN_SUCCESS;
}
- priv = NM_DEVICE_GET_PRIVATE (self);
- *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, ip_iface);
- if (!*config) {
- *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
- return NM_ACT_STAGE_RETURN_FAILURE;
- }
-
- /* Merge user-defined overrides into the IP6Config to be applied */
- nm_utils_merge_ip6_config (*config, s_ip6);
-
- return NM_ACT_STAGE_RETURN_SUCCESS;
+out:
+ return ret;
}
/*
@@ -1625,6 +1953,9 @@ real_act_stage4_ip6_config_timeout (NMDevice *self,
g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+ /* Notify of invalid DHCP6 config object */
+ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+
*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
return NM_ACT_STAGE_RETURN_FAILURE;
}
@@ -1986,6 +2317,85 @@ delayed_transitions_clear (NMDevice *self)
}
}
+static void
+dhcp4_cleanup (NMDevice *self, gboolean stop)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->dhcp4_config) {
+ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
+ g_object_unref (priv->dhcp4_config);
+ priv->dhcp4_config = NULL;
+ }
+
+ if (priv->dhcp4_client) {
+ /* Stop any ongoing DHCP transaction on this device */
+ if (priv->dhcp4_state_sigid) {
+ g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid);
+ priv->dhcp4_state_sigid = 0;
+ }
+
+ if (priv->dhcp4_timeout_sigid) {
+ g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid);
+ priv->dhcp4_timeout_sigid = 0;
+ }
+
+ if (stop)
+ nm_dhcp_client_stop (priv->dhcp4_client);
+
+ g_object_unref (priv->dhcp4_client);
+ priv->dhcp4_client = NULL;
+ }
+}
+
+static void
+dhcp6_cleanup (NMDevice *self, gboolean stop)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->dhcp6_config) {
+ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+ g_object_unref (priv->dhcp6_config);
+ priv->dhcp6_config = NULL;
+ }
+
+ if (priv->dhcp6_client) {
+ if (priv->dhcp6_state_sigid) {
+ g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid);
+ priv->dhcp6_state_sigid = 0;
+ }
+
+ if (priv->dhcp6_timeout_sigid) {
+ g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid);
+ priv->dhcp6_timeout_sigid = 0;
+ }
+
+ if (stop)
+ nm_dhcp_client_stop (priv->dhcp6_client);
+
+ g_object_unref (priv->dhcp6_client);
+ priv->dhcp6_client = NULL;
+ }
+}
+
+static void
+dnsmasq_cleanup (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (!priv->dnsmasq_manager)
+ return;
+
+ if (priv->dnsmasq_state_id) {
+ g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id);
+ priv->dnsmasq_state_id = 0;
+ }
+
+ nm_dnsmasq_manager_stop (priv->dnsmasq_manager);
+ g_object_unref (priv->dnsmasq_manager);
+ priv->dnsmasq_manager = NULL;
+}
+
/*
* nm_device_deactivate_quickly
*
@@ -2010,27 +2420,11 @@ nm_device_deactivate_quickly (NMDevice *self)
/* Clear any delayed transitions */
delayed_transitions_clear (self);
- /* Stop any ongoing DHCP transaction on this device */
- if (nm_device_get_act_request (self)) {
- if (nm_device_get_use_dhcp (self)) {
- nm_dhcp_manager_cancel_transaction (priv->dhcp_manager, nm_device_get_ip_iface (self));
- nm_device_set_use_dhcp (self, FALSE);
- /* Notify of invalid DHCP4 config */
- g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
- } else if (priv->dnsmasq_manager) {
- if (priv->dnsmasq_state_id) {
- g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id);
- priv->dnsmasq_state_id = 0;
- }
-
- nm_dnsmasq_manager_stop (priv->dnsmasq_manager);
- g_object_unref (priv->dnsmasq_manager);
- priv->dnsmasq_manager = NULL;
- }
- }
-
+ dhcp4_cleanup (self, TRUE);
+ dhcp6_cleanup (self, TRUE);
+ addrconf6_cleanup (self);
+ dnsmasq_cleanup (self);
aipd_cleanup (self);
- nm_device_cleanup_ip6 (self);
/* Call device type-specific deactivation */
if (NM_DEVICE_GET_CLASS (self)->deactivate_quickly)
@@ -2246,193 +2640,20 @@ nm_device_can_interrupt_activation (NMDevice *self)
/* IP Configuration stuff */
-static void
-handle_dhcp_lease_change (NMDevice *device)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- NMIP4Config *config;
- NMSettingIP4Config *s_ip4;
- NMConnection *connection;
- NMActRequest *req;
- NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
- const char *ip_iface;
- gboolean assumed;
-
- if (!nm_device_get_use_dhcp (device)) {
- nm_warning ("got DHCP rebind for device that wasn't using DHCP.");
- return;
- }
-
- ip_iface = nm_device_get_ip_iface (device);
-
- config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, ip_iface);
- if (!config) {
- nm_warning ("failed to get DHCP config for rebind");
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
- return;
- }
-
- req = nm_device_get_act_request (device);
- g_assert (req);
- connection = nm_act_request_get_connection (req);
- g_assert (connection);
-
- s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG));
- nm_utils_merge_ip4_config (config, s_ip4);
-
- g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config);
-
- assumed = nm_act_request_get_assumed (req);
- if (nm_device_set_ip4_config (device, config, assumed, &reason)) {
- nm_dhcp4_config_reset (priv->dhcp4_config);
- nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager,
- ip_iface,
- dhcp4_add_option_cb,
- priv->dhcp4_config);
- } else {
- nm_warning ("Failed to update IP4 config in response to DHCP event.");
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
- }
-}
-
-static void
-dhcp_state_changed (NMDHCPManager *dhcp_manager,
- const char *iface,
- NMDHCPState state,
- gpointer user_data)
-{
- NMDevice *device = NM_DEVICE (user_data);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- NMDeviceState dev_state;
-
- if (strcmp (nm_device_get_ip_iface (device), iface) != 0)
- return;
-
- if (!nm_device_get_act_request (device))
- return;
-
- dev_state = nm_device_get_state (device);
-
- switch (state) {
- case DHC_BOUND: /* lease obtained */
- case DHC_RENEW: /* lease renewed */
- case DHC_REBOOT: /* have valid lease, but now obtained a different one */
- case DHC_REBIND: /* new, different lease */
- if (dev_state == NM_DEVICE_STATE_IP_CONFIG)
- nm_device_activate_schedule_stage4_ip4_config_get (device);
- else if (dev_state == NM_DEVICE_STATE_ACTIVATED)
- handle_dhcp_lease_change (device);
- break;
- case DHC_TIMEOUT: /* timed out contacting DHCP server */
- nm_dhcp4_config_reset (priv->dhcp4_config);
-
- if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
- nm_device_activate_schedule_stage4_ip4_config_timeout (device);
- break;
- case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
- case DHC_ABEND: /* dhclient exited abnormally */
- case DHC_END: /* dhclient exited normally */
- nm_dhcp4_config_reset (priv->dhcp4_config);
-
- if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED);
- } else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
- if (nm_device_get_use_dhcp (device)) {
- /* dhclient quit and therefore can't renew our lease, kill the conneciton */
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
- }
- }
- break;
- default:
- break;
- }
-}
-
-static void
-dhcp_timeout (NMDHCPManager *dhcp_manager,
- const char *iface,
- gpointer user_data)
-{
- NMDevice * device = NM_DEVICE (user_data);
-
- if (strcmp (nm_device_get_ip_iface (device), iface) != 0)
- return;
-
- if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
- nm_device_activate_schedule_stage4_ip4_config_timeout (device);
-}
-
-gboolean
-nm_device_get_use_dhcp (NMDevice *self)
-{
- g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-
- return NM_DEVICE_GET_PRIVATE (self)->dhcp_manager ? TRUE : FALSE;
-}
-
-void
-nm_device_set_use_dhcp (NMDevice *self,
- gboolean use_dhcp)
-{
- NMDevicePrivate *priv;
-
- g_return_if_fail (NM_IS_DEVICE (self));
-
- priv = NM_DEVICE_GET_PRIVATE (self);
-
- if (use_dhcp) {
- /* New exported DHCP4 config */
- if (priv->dhcp4_config)
- g_object_unref (priv->dhcp4_config);
- priv->dhcp4_config = nm_dhcp4_config_new ();
-
- if (!priv->dhcp_manager) {
- priv->dhcp_manager = nm_dhcp_manager_get ();
- priv->dhcp_state_sigid = g_signal_connect (priv->dhcp_manager,
- "state-changed",
- G_CALLBACK (dhcp_state_changed),
- self);
- priv->dhcp_timeout_sigid = g_signal_connect (priv->dhcp_manager,
- "timeout",
- G_CALLBACK (dhcp_timeout),
- self);
- }
- } else {
- if (priv->dhcp4_config) {
- g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
- g_object_unref (priv->dhcp4_config);
- priv->dhcp4_config = NULL;
- }
-
- if (priv->dhcp_manager) {
- g_signal_handler_disconnect (priv->dhcp_manager, priv->dhcp_state_sigid);
- priv->dhcp_state_sigid = 0;
- g_signal_handler_disconnect (priv->dhcp_manager, priv->dhcp_timeout_sigid);
- priv->dhcp_timeout_sigid = 0;
- g_object_unref (priv->dhcp_manager);
- priv->dhcp_manager = NULL;
- }
- }
-}
-
NMDHCP4Config *
nm_device_get_dhcp4_config (NMDevice *self)
{
- NMDevicePrivate *priv;
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
- g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-
- priv = NM_DEVICE_GET_PRIVATE (self);
-
- if (priv->dhcp_manager)
- return priv->dhcp4_config;
- return NULL;
+ return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config;
}
NMIP4Config *
nm_device_get_ip4_config (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
return NM_DEVICE_GET_PRIVATE (self)->ip4_config;
}
@@ -2602,10 +2823,20 @@ nm_device_set_ip6_config (NMDevice *self,
return success;
}
+NMDHCP6Config *
+nm_device_get_dhcp6_config (NMDevice *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+ return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config;
+}
+
NMIP6Config *
nm_device_get_ip6_config (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
return NM_DEVICE_GET_PRIVATE (self)->ip6_config;
}
@@ -2761,6 +2992,13 @@ dispose (GObject *object)
/* Clear any delayed transitions */
delayed_transitions_clear (self);
+ /* Clean up and stop DHCP */
+ dhcp4_cleanup (self, take_down);
+ dhcp6_cleanup (self, take_down);
+ addrconf6_cleanup (self);
+ dnsmasq_cleanup (self);
+
+ /* Take the device itself down and clear its IPv4 configuration */
if (priv->managed && take_down) {
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
@@ -2768,26 +3006,10 @@ dispose (GObject *object)
nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
}
- clear_act_request (self);
-
activation_source_clear (self, TRUE, AF_INET);
activation_source_clear (self, TRUE, AF_INET6);
- if (!take_down) {
- nm_device_set_use_dhcp (self, FALSE);
- nm_device_cleanup_ip6 (self);
- }
-
- if (priv->dnsmasq_manager) {
- if (priv->dnsmasq_state_id) {
- g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id);
- priv->dnsmasq_state_id = 0;
- }
-
- nm_dnsmasq_manager_stop (priv->dnsmasq_manager);
- g_object_unref (priv->dnsmasq_manager);
- priv->dnsmasq_manager = NULL;
- }
+ clear_act_request (self);
out:
G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
@@ -2799,6 +3021,8 @@ finalize (GObject *object)
NMDevice *self = NM_DEVICE (object);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ g_object_unref (priv->dhcp_manager);
+
g_free (priv->udi);
g_free (priv->iface);
g_free (priv->ip_iface);
@@ -2889,7 +3113,7 @@ get_property (GObject *object, guint prop_id,
break;
case NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG:
if ( ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG))
- && nm_device_get_use_dhcp (self))
+ && priv->dhcp4_client)
g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config));
else
g_value_set_boxed (value, "/");
@@ -2903,6 +3127,13 @@ get_property (GObject *object, guint prop_id,
}
g_value_set_boxed (value, "/");
break;
+ case NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG:
+ if ( ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG))
+ && priv->dhcp6_client)
+ g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config));
+ else
+ g_value_set_boxed (value, "/");
+ break;
case NM_DEVICE_INTERFACE_PROP_STATE:
g_value_set_uint (value, priv->state);
break;
@@ -2981,6 +3212,10 @@ nm_device_class_init (NMDeviceClass *klass)
NM_DEVICE_INTERFACE_PROP_IP6_CONFIG,
NM_DEVICE_INTERFACE_IP6_CONFIG);
+ g_object_class_override_property (object_class,
+ NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG,
+ NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_STATE,
NM_DEVICE_INTERFACE_STATE);
diff --git a/src/nm-device.h b/src/nm-device.h
index b81c460d6..5fcde5c33 100644
--- a/src/nm-device.h
+++ b/src/nm-device.h
@@ -31,6 +31,7 @@
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
#include "nm-dhcp4-config.h"
+#include "nm-dhcp6-config.h"
#include "nm-connection.h"
typedef enum NMActStageReturn
@@ -140,10 +141,8 @@ int nm_device_get_priority (NMDevice *dev);
guint32 nm_device_get_ip4_address (NMDevice *dev);
void nm_device_update_ip4_address (NMDevice *dev);
-gboolean nm_device_get_use_dhcp (NMDevice *dev);
-void nm_device_set_use_dhcp (NMDevice *dev,
- gboolean use_dhcp);
NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev);
+NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *dev);
NMIP4Config * nm_device_get_ip4_config (NMDevice *dev);
NMIP6Config * nm_device_get_ip6_config (NMDevice *dev);
diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c
new file mode 100644
index 000000000..fb6ccce50
--- /dev/null
+++ b/src/nm-dhcp6-config.c
@@ -0,0 +1,192 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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 of the License, 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) 2008 Red Hat, Inc.
+ */
+
+#include
+#include
+
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-dhcp6-config.h"
+#include "nm-dhcp6-config-glue.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-utils.h"
+
+
+G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, G_TYPE_OBJECT)
+
+#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate))
+
+typedef struct {
+ char *dbus_path;
+ GHashTable *options;
+} NMDHCP6ConfigPrivate;
+
+
+enum {
+ PROP_0,
+ PROP_OPTIONS,
+
+ LAST_PROP
+};
+
+enum {
+ PROPERTIES_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+NMDHCP6Config *
+nm_dhcp6_config_new (void)
+{
+ return NM_DHCP6_CONFIG (g_object_new (NM_TYPE_DHCP6_CONFIG, NULL));
+}
+
+void
+nm_dhcp6_config_add_option (NMDHCP6Config *self,
+ const char *key,
+ const char *option)
+{
+ GValue *svalue;
+
+ g_return_if_fail (NM_IS_DHCP6_CONFIG (self));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (option != NULL);
+
+ svalue = g_slice_new0 (GValue);
+ g_value_init (svalue, G_TYPE_STRING);
+ g_value_set_string (svalue, option);
+ g_hash_table_insert (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, g_strdup (key), svalue);
+ g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS);
+}
+
+void
+nm_dhcp6_config_reset (NMDHCP6Config *self)
+{
+ g_return_if_fail (NM_IS_DHCP6_CONFIG (self));
+
+ g_hash_table_remove_all (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options);
+ g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS);
+}
+
+const char *
+nm_dhcp6_config_get_option (NMDHCP6Config *self, const char *key)
+{
+ GValue *value;
+
+ g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ value = g_hash_table_lookup (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, key);
+ return value ? g_value_get_string (value) : NULL;
+}
+
+const char *
+nm_dhcp6_config_get_dbus_path (NMDHCP6Config *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL);
+
+ return NM_DHCP6_CONFIG_GET_PRIVATE (self)->dbus_path;
+}
+
+static void
+nm_gvalue_destroy (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static void
+nm_dhcp6_config_init (NMDHCP6Config *self)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (self);
+ static guint32 counter = 0;
+ DBusGConnection *connection;
+ NMDBusManager *dbus_mgr;
+
+ dbus_mgr = nm_dbus_manager_get ();
+ connection = nm_dbus_manager_get_connection (dbus_mgr);
+ priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/DHCP6Config/%d", counter++);
+ dbus_g_connection_register_g_object (connection, priv->dbus_path, G_OBJECT (self));
+ g_object_unref (dbus_mgr);
+
+ priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+ g_free (priv->dbus_path);
+ g_hash_table_destroy (priv->options);
+
+ G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_OPTIONS:
+ g_value_set_boxed (value, priv->options);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+ g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate));
+
+ /* virtual methods */
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+ g_object_class_install_property
+ (object_class, PROP_OPTIONS,
+ g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS,
+ "Options",
+ "DHCP configuration options returned by the server",
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_PARAM_READABLE));
+
+ /* Signals */
+ signals[PROPERTIES_CHANGED] =
+ nm_properties_changed_signal_new (object_class,
+ G_STRUCT_OFFSET (NMDHCP6ConfigClass, properties_changed));
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class),
+ &dbus_glib_nm_dhcp6_config_object_info);
+}
diff --git a/src/nm-dhcp6-config.h b/src/nm-dhcp6-config.h
new file mode 100644
index 000000000..90eb10ffb
--- /dev/null
+++ b/src/nm-dhcp6-config.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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 of the License, 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) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP6_CONFIG_H
+#define NM_DHCP6_CONFIG_H
+
+#include
+#include
+
+#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type ())
+#define NM_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config))
+#define NM_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass))
+#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+#define NM_DHCP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass))
+
+typedef struct {
+ GObject parent;
+} NMDHCP6Config;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Signals */
+ void (*properties_changed) (NMDHCP6Config *config, GHashTable *properties);
+} NMDHCP6ConfigClass;
+
+#define NM_DHCP6_CONFIG_OPTIONS "options"
+
+GType nm_dhcp6_config_get_type (void);
+
+NMDHCP6Config *nm_dhcp6_config_new (void);
+
+const char *nm_dhcp6_config_get_dbus_path (NMDHCP6Config *config);
+
+void nm_dhcp6_config_add_option (NMDHCP6Config *config,
+ const char *key,
+ const char *option);
+
+void nm_dhcp6_config_reset (NMDHCP6Config *config);
+
+const char *nm_dhcp6_config_get_option (NMDHCP6Config *config, const char *option);
+
+#endif /* NM_DHCP6_CONFIG_H */
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index 398042ccb..75f9cffd5 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -13,7 +13,9 @@ test_dhcp_options_SOURCES = \
test_dhcp_options_CPPFLAGS = \
$(GLIB_CFLAGS) \
- $(DBUS_CFLAGS)
+ $(DBUS_CFLAGS) \
+ -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+ -DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
test_dhcp_options_LDADD = \
$(top_builddir)/libnm-util/libnm-util.la \
diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c
index 90828d9dd..6211f1d9c 100644
--- a/src/tests/test-dhcp-options.c
+++ b/src/tests/test-dhcp-options.c
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
*
*/
@@ -35,15 +35,43 @@ typedef struct {
const char *value;
} Option;
+static void
+destroy_gvalue (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static GValue *
+string_to_byte_array_gvalue (const char *str)
+{
+ GByteArray *array;
+ GValue *val;
+
+ array = g_byte_array_sized_new (strlen (str));
+ g_byte_array_append (array, (const guint8 *) str, strlen (str));
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
+ g_value_take_boxed (val, array);
+
+ return val;
+}
+
static GHashTable *
fill_table (Option *test_options, GHashTable *table)
{
Option *opt;
if (!table)
- table = g_hash_table_new (g_str_hash, g_str_equal);
- for (opt = test_options; opt->name; opt++)
- g_hash_table_insert (table, (gpointer) opt->name, (gpointer) opt->value);
+ table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue);
+ for (opt = test_options; opt->name; opt++) {
+ g_hash_table_insert (table,
+ (gpointer) opt->name,
+ string_to_byte_array_gvalue (opt->value));
+ }
return table;
}
@@ -69,7 +97,7 @@ static Option generic_options[] = {
};
static void
-test_generic_options (void)
+test_generic_options (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -88,7 +116,7 @@ test_generic_options (void)
const char *expected_route2_gw = "10.1.1.1";
options = fill_table (generic_options, NULL);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-generic", "failed to parse DHCP4 options");
@@ -187,7 +215,7 @@ static Option wins_options[] = {
};
static void
-test_wins_options (void)
+test_wins_options (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -199,7 +227,7 @@ test_wins_options (void)
options = fill_table (generic_options, NULL);
options = fill_table (wins_options, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-wins", "failed to parse DHCP4 options");
@@ -231,7 +259,7 @@ static Option classless_routes_options[] = {
};
static void
-test_classless_static_routes (void)
+test_classless_static_routes (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -245,7 +273,7 @@ test_classless_static_routes (void)
options = fill_table (generic_options, NULL);
options = fill_table (classless_routes_options, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-rfc3442", "failed to parse DHCP4 options");
@@ -299,7 +327,7 @@ static Option invalid_classless_routes1[] = {
};
static void
-test_invalid_classless_routes1 (void)
+test_invalid_classless_routes1 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -311,7 +339,7 @@ test_invalid_classless_routes1 (void)
options = fill_table (generic_options, NULL);
options = fill_table (invalid_classless_routes1, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options");
@@ -348,7 +376,7 @@ static Option invalid_classless_routes2[] = {
};
static void
-test_invalid_classless_routes2 (void)
+test_invalid_classless_routes2 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -362,7 +390,7 @@ test_invalid_classless_routes2 (void)
options = fill_table (generic_options, NULL);
options = fill_table (invalid_classless_routes2, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options");
@@ -420,7 +448,7 @@ static Option invalid_classless_routes3[] = {
};
static void
-test_invalid_classless_routes3 (void)
+test_invalid_classless_routes3 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -432,7 +460,7 @@ test_invalid_classless_routes3 (void)
options = fill_table (generic_options, NULL);
options = fill_table (invalid_classless_routes3, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options");
@@ -469,7 +497,7 @@ static Option gw_in_classless_routes[] = {
};
static void
-test_gateway_in_classless_routes (void)
+test_gateway_in_classless_routes (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -483,7 +511,7 @@ test_gateway_in_classless_routes (void)
options = fill_table (generic_options, NULL);
options = fill_table (gw_in_classless_routes, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-rfc3442-gateway", "failed to parse DHCP4 options");
@@ -526,7 +554,7 @@ static Option escaped_searches_options[] = {
};
static void
-test_escaped_domain_searches (void)
+test_escaped_domain_searches (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -537,7 +565,7 @@ test_escaped_domain_searches (void)
options = fill_table (generic_options, NULL);
options = fill_table (escaped_searches_options, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-escaped-domain-searches", "failed to parse DHCP4 options");
@@ -560,7 +588,7 @@ static Option invalid_escaped_searches_options[] = {
};
static void
-test_invalid_escaped_domain_searches (void)
+test_invalid_escaped_domain_searches (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
@@ -568,7 +596,7 @@ test_invalid_escaped_domain_searches (void)
options = fill_table (generic_options, NULL);
options = fill_table (invalid_escaped_searches_options, options);
- ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options);
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
"dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options");
@@ -579,6 +607,9 @@ test_invalid_escaped_domain_searches (void)
g_hash_table_destroy (options);
}
+#define DHCLIENT "dhclient"
+#define DHCPCD "dhcpcd"
+
int main (int argc, char **argv)
{
GError *error = NULL;
@@ -592,15 +623,29 @@ int main (int argc, char **argv)
FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
/* The tests */
- test_generic_options ();
- test_wins_options ();
- test_classless_static_routes ();
- test_invalid_classless_routes1 ();
- test_invalid_classless_routes2 ();
- test_invalid_classless_routes3 ();
- test_gateway_in_classless_routes ();
- test_escaped_domain_searches ();
- test_invalid_escaped_domain_searches ();
+ if (strlen (DHCLIENT_PATH)) {
+ test_generic_options (DHCLIENT);
+ test_wins_options (DHCLIENT);
+ test_classless_static_routes (DHCLIENT);
+ test_invalid_classless_routes1 (DHCLIENT);
+ test_invalid_classless_routes2 (DHCLIENT);
+ test_invalid_classless_routes3 (DHCLIENT);
+ test_gateway_in_classless_routes (DHCLIENT);
+ test_escaped_domain_searches (DHCLIENT);
+ test_invalid_escaped_domain_searches (DHCLIENT);
+ }
+
+ if (strlen (DHCPCD_PATH)) {
+ test_generic_options (DHCPCD);
+ test_wins_options (DHCPCD);
+ test_classless_static_routes (DHCPCD);
+ test_invalid_classless_routes1 (DHCPCD);
+ test_invalid_classless_routes2 (DHCPCD);
+ test_invalid_classless_routes3 (DHCPCD);
+ test_gateway_in_classless_routes (DHCPCD);
+ test_escaped_domain_searches (DHCPCD);
+ test_invalid_escaped_domain_searches (DHCPCD);
+ }
base = g_path_get_basename (argv[0]);
fprintf (stdout, "%s: SUCCESS\n", base);
diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c
index afe35bdf8..1b8ae9392 100644
--- a/system-settings/plugins/ifcfg-rh/reader.c
+++ b/system-settings/plugins/ifcfg-rh/reader.c
@@ -1382,7 +1382,7 @@ make_ip6_setting (shvarFile *ifcfg,
char *value = NULL;
char *str_value;
char *route6_path = NULL;
- gboolean bool_value, ipv6forwarding, ipv6_autoconf;
+ gboolean bool_value, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE;
char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
guint32 i;
shvarFile *network_ifcfg;
@@ -1459,9 +1459,12 @@ make_ip6_setting (shvarFile *ifcfg,
/* Find out method property */
ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE);
ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding);
+ dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE);
if (ipv6_autoconf)
method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+ else if (dhcp6)
+ method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
else {
/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE);
diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c
index 4ded5c681..330bbf044 100644
--- a/system-settings/plugins/ifcfg-rh/writer.c
+++ b/system-settings/plugins/ifcfg-rh/writer.c
@@ -1205,18 +1205,27 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
g_assert (value);
if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
svSetValue (ifcfg, "IPV6INIT", "no", FALSE);
+ svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
return TRUE;
} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE);
+ svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
+ } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
+ svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
+ svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
+ svSetValue (ifcfg, "DHCPV6C", "yes", FALSE);
} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
+ svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
+ svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
+ svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
/* TODO */
}