Commit 5149fd12 authored by Dan Williams's avatar Dan Williams

iface-helper: add nm-iface-helper for dynamic configure-then-quit support

When quitting, the Manager asks each device to spawn the interface helper,
which persists and manages dynamic address on the interface after NetworkManager
is gone.  If the dynamic address cannot be maintaned, the helper quits and
the interface's address may be removed when their lifetime runs out.

To keep the helper as simple as possible, NetworkManager passes most of the
configuration on the command-line, including some properties of the device's
current state, which are necessary for the helper to maintain DHCP leases
or IPv6 SLAAC addresses.
parent 9ff8c01d
......@@ -232,6 +232,7 @@ valgrind-*.log
/src/dhcp-manager/tests/test-dhcp-options
/src/dhcp-manager/tests/test-dhcp-utils
/src/dnsmasq-manager/tests/test-dnsmasq-utils
/src/nm-iface-helper
/src/settings/plugins/ibft/tests/test-ibft
/src/settings/plugins/ifcfg-rh/tests/network-scripts/*-Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/network-scripts/Test_Write_*
......
......@@ -518,6 +518,7 @@ fi
%{_libexecdir}/nm-dhcp-helper
%{_libexecdir}/nm-avahi-autoipd.action
%{_libexecdir}/nm-dispatcher
%{_libexecdir}/nm-iface-helper
%dir %{_libdir}/NetworkManager
%{_libdir}/NetworkManager/libnm-settings-plugin*.so
%{_mandir}/man1/*
......
......@@ -147,6 +147,7 @@ src/devices/wifi/nm-wifi-ap-utils.c
src/devices/wimax/nm-device-wimax.c
src/devices/wwan/nm-modem-broadband.c
src/nm-config.c
src/nm-iface-helper.c
src/nm-logging.c
src/nm-manager.c
src/nm-sleep-monitor-systemd.c
......
......@@ -48,7 +48,10 @@ AM_CPPFLAGS = \
# primarily for its side effect of removing duplicates.
AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
noinst_LTLIBRARIES = libNetworkManager.la libsystemd-dhcp.la
noinst_LTLIBRARIES = \
libNetworkManager.la \
libnm-iface-helper.la \
libsystemd-dhcp.la
######################
# libsystemd-dhcp
......@@ -458,6 +461,86 @@ NetworkManager_LDFLAGS = -rdynamic
######################
libnm_iface_helper_la_SOURCES = \
dhcp-manager/nm-dhcp-client.c \
dhcp-manager/nm-dhcp-client.h \
dhcp-manager/nm-dhcp-utils.c \
dhcp-manager/nm-dhcp-utils.h \
dhcp-manager/nm-dhcp-manager.c \
dhcp-manager/nm-dhcp-manager.h \
\
platform/nm-linux-platform.c \
platform/nm-linux-platform.h \
platform/nm-platform.c \
platform/nm-platform.h \
platform/wifi/wifi-utils-nl80211.c \
platform/wifi/wifi-utils-nl80211.h \
platform/wifi/wifi-utils-private.h \
platform/wifi/wifi-utils.c \
platform/wifi/wifi-utils.h \
\
rdisc/nm-fake-rdisc.c \
rdisc/nm-fake-rdisc.h \
rdisc/nm-lndp-rdisc.c \
rdisc/nm-lndp-rdisc.h \
rdisc/nm-rdisc.c \
rdisc/nm-rdisc.h \
\
nm-ip4-config.c \
nm-ip4-config.h \
nm-ip6-config.c \
nm-ip6-config.h \
\
nm-enum-types.c \
nm-enum-types.h \
nm-logging.c \
nm-logging.h \
nm-posix-signals.c \
nm-posix-signals.h \
NetworkManagerUtils.c \
NetworkManagerUtils.h
if WITH_WEXT
libnm_iface_helper_la_SOURCES += \
platform/wifi/wifi-utils-wext.c \
platform/wifi/wifi-utils-wext.h
endif
libnm_iface_helper_la_LIBADD = \
$(top_builddir)/libnm-core/libnm-core.la \
libsystemd-dhcp.la \
$(DBUS_LIBS) \
$(GLIB_LIBS) \
$(GUDEV_LIBS) \
$(LIBNL_LIBS) \
$(LIBNDP_LIBS) \
$(LIBDL) \
$(LIBM)
libexec_PROGRAMS = nm-iface-helper
nm_iface_helper_SOURCES = \
dhcp-manager/nm-dhcp-systemd.h \
dhcp-manager/nm-dhcp-systemd.c \
nm-iface-helper.c \
main-utils.c \
main-utils.h
nm_iface_helper_LDADD = \
$(top_builddir)/libnm-core/libnm-core.la \
libsystemd-dhcp.la \
libnm-iface-helper.la \
$(DBUS_LIBS) \
$(GLIB_LIBS) \
$(GUDEV_LIBS) \
$(LIBNL_LIBS) \
$(LIBNDP_LIBS) \
$(LIBM)
nm_iface_helper_LDFLAGS = -rdynamic
######################
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.NetworkManager.conf
......
......@@ -6990,6 +6990,173 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
_cleanup_generic_post (self, TRUE);
}
static char *
bin2hexstr (const char *bytes, gsize len)
{
GString *str;
int i;
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (len > 0, NULL);
str = g_string_sized_new (len * 2 + 1);
for (i = 0; i < len; i++) {
if (str->len)
g_string_append_c (str, ':');
g_string_append_printf (str, "%02x", (guint8) bytes[i]);
}
return g_string_free (str, FALSE);
}
static char *
find_dhcp4_address (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
guint i, n;
if (!priv->ip4_config)
return NULL;
n = nm_ip4_config_get_num_addresses (priv->ip4_config);
for (i = 0; i < n; i++) {
const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i);
if (a->source == NM_IP_CONFIG_SOURCE_DHCP)
return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
}
return NULL;
}
void
nm_device_spawn_iface_helper (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean priority_set = FALSE, configured = FALSE;
NMConnection *connection;
GError *error = NULL;
const char *method;
GPtrArray *argv;
gs_free char *dhcp4_address = NULL;
if (priv->state != NM_DEVICE_STATE_ACTIVATED)
return;
if (!nm_device_can_assume_connections (self))
return;
connection = nm_device_get_connection (self);
g_assert (connection);
argv = g_ptr_array_sized_new (10);
g_ptr_array_set_free_func (argv, g_free);
g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/nm-iface-helper"));
g_ptr_array_add (argv, g_strdup ("--ifname"));
g_ptr_array_add (argv, g_strdup (nm_device_get_ip_iface (self)));
g_ptr_array_add (argv, g_strdup ("--uuid"));
g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
dhcp4_address = find_dhcp4_address (self);
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
if ( priv->ip4_config
&& priv->ip4_state == IP_DONE
&& g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
&& priv->dhcp4_client
&& dhcp4_address) {
NMSettingIPConfig *s_ip4;
GBytes *client_id;
char *hex_client_id;
const char *hostname;
s_ip4 = nm_connection_get_setting_ip4_config (connection);
g_assert (s_ip4);
g_ptr_array_add (argv, g_strdup ("--priority"));
g_ptr_array_add (argv, g_strdup_printf ("%u", nm_dhcp_client_get_priority (priv->dhcp4_client)));
priority_set = TRUE;
g_ptr_array_add (argv, g_strdup ("--dhcp4"));
g_ptr_array_add (argv, g_strdup (dhcp4_address));
if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE)
g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
if (client_id) {
g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid"));
hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL),
g_bytes_get_size (client_id));
g_ptr_array_add (argv, hex_client_id);
}
hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
if (client_id) {
g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
g_ptr_array_add (argv, g_strdup (hostname));
}
configured = TRUE;
}
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
if ( priv->ip6_config
&& priv->ip6_state == IP_DONE
&& g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
&& priv->rdisc
&& priv->ac_ip6_config) {
NMSettingIPConfig *s_ip6;
char *hex_iid;
NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
s_ip6 = nm_connection_get_setting_ip6_config (connection);
g_assert (s_ip6);
g_ptr_array_add (argv, g_strdup ("--slaac"));
if (nm_setting_ip_config_get_may_fail (s_ip6) == FALSE)
g_ptr_array_add (argv, g_strdup ("--slaac-required"));
g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr"));
g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr));
if (nm_device_get_ip_iface_identifier (self, &iid)) {
g_ptr_array_add (argv, g_strdup ("--iid"));
hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId));
g_ptr_array_add (argv, hex_iid);
}
configured = TRUE;
}
if (configured) {
GPid pid;
if (!priority_set) {
g_ptr_array_add (argv, g_strdup ("--priority"));
g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_priority (self)));
}
g_ptr_array_add (argv, NULL);
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DEVICE)) {
char *tmp;
tmp = g_strjoinv (" ", (char **) argv->pdata);
_LOGD (LOGD_DEVICE, "running '%s'", tmp);
g_free (tmp);
}
if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) {
_LOGI (LOGD_DEVICE, "spawned helper PID %u", (guint) pid);
} else {
_LOGW (LOGD_DEVICE, "failed to spawn helper: %s", error->message);
g_error_free (error);
}
}
g_ptr_array_unref (argv);
}
/***********************************************************/
static gboolean
......
......@@ -365,6 +365,8 @@ NMConnection *nm_device_new_default_connection (NMDevice *self);
const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self);
const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self);
void nm_device_spawn_iface_helper (NMDevice *self);
G_END_DECLS
/* For testing only */
......
......@@ -386,11 +386,18 @@ static void
nm_dhcp_manager_init (NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
NMConfig *config = nm_config_get ();
const char *client;
GError *error = NULL;
/* Client-specific setup */
client = nm_config_get_dhcp_client (nm_config_get ());
client = nm_config_get_dhcp_client (config);
if (nm_config_get_configure_and_quit (config)) {
if (g_strcmp0 (client, "internal") != 0)
nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
client = "internal";
}
priv->client_type = get_client_type (client, &error);
if (priv->client_type == G_TYPE_INVALID) {
nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
......
......@@ -178,17 +178,10 @@ _init_nm_debug (const char *debug)
}
static void
manager_startup_complete (NMManager *manager, GParamSpec *pspec, gpointer user_data)
manager_configure_quit (NMManager *manager, gpointer user_data)
{
NMConfig *config = NM_CONFIG (user_data);
gboolean startup = FALSE;
g_object_get (G_OBJECT (manager), NM_MANAGER_STARTUP, &startup, NULL);
if (!startup && nm_config_get_configure_and_quit (config)) {
nm_log_info (LOGD_CORE, "quitting now that startup is complete");
g_main_loop_quit (main_loop);
}
nm_log_info (LOGD_CORE, "quitting now that startup is complete");
g_main_loop_quit (main_loop);
}
/*
......@@ -462,10 +455,7 @@ main (int argc, char *argv[])
}
}
g_signal_connect (manager,
"notify::" NM_MANAGER_STARTUP,
G_CALLBACK (manager_startup_complete),
config);
g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
nm_manager_start (manager);
......@@ -485,10 +475,10 @@ main (int argc, char *argv[])
success = TRUE;
/* Told to quit before getting to the mainloop by the signal handler */
if (quit_early == TRUE)
goto done;
if (!quit_early)
g_main_loop_run (main_loop);
g_main_loop_run (main_loop);
nm_manager_stop (manager);
done:
g_clear_object (&manager);
......
......@@ -74,41 +74,36 @@ G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
/************************************************************************/
static gboolean
_parse_bool_str (const char *str, gboolean *out_value)
_get_bool_value (GKeyFile *keyfile,
const char *section,
const char *key,
gboolean default_value)
{
gboolean value;
gsize len;
char *s = NULL;
g_return_val_if_fail (str, FALSE);
while (g_ascii_isspace (*str))
str++;
if (!*str)
return FALSE;
len = strlen (str);
if (g_ascii_isspace (str[len-1])) {
str = s = g_strdup (str);
g_strchomp (s);
}
if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
value = TRUE;
else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
value = FALSE;
else {
g_free (s);
return FALSE;
gboolean value = default_value;
char *str;
g_return_val_if_fail (keyfile != NULL, default_value);
g_return_val_if_fail (section != NULL, default_value);
g_return_val_if_fail (key != NULL, default_value);
str = g_key_file_get_value (keyfile, section, key, NULL);
if (!str)
return default_value;
g_strstrip (str);
if (str[0]) {
if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
value = TRUE;
else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
value = FALSE;
else {
nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'",
section, key, str, default_value ? "true" : "false");
}
}
if (out_value)
*out_value = value;
g_free (s);
return TRUE;
g_free (str);
return value;
}
/************************************************************************/
......@@ -521,7 +516,6 @@ nm_config_new (GError **error)
GFileInfo *info;
GPtrArray *confs;
const char *name;
char *value;
int i;
GString *config_description;
......@@ -591,23 +585,9 @@ nm_config_new (GError **error)
if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1);
value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
priv->monitor_connection_files = FALSE;
if (value) {
if (!_parse_bool_str (value, &priv->monitor_connection_files))
nm_log_warn (LOGD_CORE, "Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
g_free (value);
}
priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE);
value = g_key_file_get_value (priv->keyfile, "main", "auth-polkit", NULL);
priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT;
if (value) {
if (!_parse_bool_str (value, &priv->auth_polkit)) {
nm_log_warn (LOGD_CORE, "Unrecognized value for main.auth-polkit: %s. Assuming '%s'", value,
NM_CONFIG_DEFAULT_AUTH_POLKIT ? "true" : "false");
}
g_free (value);
}
priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
......@@ -631,7 +611,7 @@ nm_config_new (GError **error)
priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
priv->configure_and_quit = g_key_file_get_boolean (priv->keyfile, "main", "configure-and-quit", NULL);
priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE);
return singleton;
}
......
This diff is collapsed.
......@@ -83,6 +83,7 @@ nm_ip4_config_new (void)
}
#ifndef NM_IFACE_HELPER
void
nm_ip4_config_export (NMIP4Config *config)
{
......@@ -94,6 +95,7 @@ nm_ip4_config_export (NMIP4Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
#endif
const char *
nm_ip4_config_get_dbus_path (const NMIP4Config *config)
......@@ -1952,7 +1954,9 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip4_config_object_info);
#endif
}
......@@ -73,6 +73,7 @@ nm_ip6_config_new (void)
return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL);
}
#ifndef NM_IFACE_HELPER
void
nm_ip6_config_export (NMIP6Config *config)
{
......@@ -84,6 +85,7 @@ nm_ip6_config_export (NMIP6Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
#endif
const char *
nm_ip6_config_get_dbus_path (const NMIP6Config *config)
......@@ -1863,7 +1865,9 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip6_config_object_info);
#endif
}
......@@ -59,6 +59,7 @@
#include "nm-session-monitor.h"
#include "nm-activation-request.h"
#include "nm-core-internal.h"
#include "nm-config.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
......@@ -214,6 +215,7 @@ enum {
USER_PERMISSIONS_CHANGED,
ACTIVE_CONNECTION_ADDED,
ACTIVE_CONNECTION_REMOVED,
CONFIGURE_QUIT,
LAST_SIGNAL
};
......@@ -706,6 +708,9 @@ check_if_startup_complete (NMManager *self)
g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
}
if (nm_config_get_configure_and_quit (nm_config_get ()))
g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
}
static void
......@@ -745,6 +750,8 @@ remove_device (NMManager *manager,
nm_device_set_unmanaged_quitting (device);
else
nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
} else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
nm_device_spawn_iface_helper (device);
}
}
......@@ -4167,6 +4174,16 @@ nm_manager_start (NMManager *self)
check_if_startup_complete (self);
}
void
nm_manager_stop (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
/* Remove all devices */
while (priv->devices)
remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
}
static gboolean
handle_firmware_changed (gpointer user_data)
{
......@@ -4990,9 +5007,7 @@ dispose (GObject *object)
G_CALLBACK (authority_changed_cb),
manager);
/* Remove all devices */
while (priv->devices)
remove_device (manager, NM_DEVICE (priv->devices->data), TRUE, TRUE);
g_assert (priv->devices == NULL);
if (priv->ac_cleanup_id) {
g_source_remove (priv->ac_cleanup_id);
......@@ -5258,6 +5273,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
signals[CONFIGURE_QUIT] =
g_signal_new (NM_MANAGER_CONFIGURE_QUIT,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (manager_class),
&dbus_glib_nm_manager_object_info);
......
......@@ -59,6 +59,7 @@
/* Internal signals */
#define NM_MANAGER_ACTIVE_CONNECTION_ADDED "active-connection-added"
#define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed"
#define NM_MANAGER_CONFIGURE_QUIT "configure-quit"
struct _NMManager {
......@@ -88,6 +89,7 @@ NMManager * nm_manager_new (NMSettings *settings,
NMManager * nm_manager_get (void);
void nm_manager_start (NMManager *manager);
void nm_manager_stop (NMManager *manager);
NMState nm_manager_get_state (NMManager *manager);
const GSList *nm_manager_get_active_connections (NMManager *manager);
GSList * nm_manager_get_activatable_connections (NMManager *manager);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment