summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/Makefile.am85
-rw-r--r--src/devices/nm-device.c167
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c9
-rw-r--r--src/main.c24
-rw-r--r--src/nm-config.c80
-rw-r--r--src/nm-iface-helper.c529
-rw-r--r--src/nm-ip4-config.c4
-rw-r--r--src/nm-ip6-config.c4
-rw-r--r--src/nm-manager.c28
-rw-r--r--src/nm-manager.h2
13 files changed, 864 insertions, 72 deletions
diff --git a/.gitignore b/.gitignore
index d7c47e3899..8aa06cad5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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_*
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b964b93f0e..0571f0e238 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -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
diff --git a/src/Makefile.am b/src/Makefile.am
index e186632548..eb5c64c089 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9d8932f18a..3390bf765e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -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
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index f7b91e00cc..840a19c08b 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -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 */
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 563b7ac91a..3147980b4b 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -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.",
diff --git a/src/main.c b/src/main.c
index 708545fd4c..476d2a7711 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
diff --git a/src/nm-config.c b/src/nm-config.c
index 26d5cb437a..aba4fa1a1b 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -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;
}
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
new file mode 100644
index 0000000000..62f1b2aba4
--- /dev/null
+++ b/src/nm-iface-helper.c
@@ -0,0 +1,529 @@
+/* -*- 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) 2014 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib-unix.h>
+#include <getopt.h>
+#include <locale.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "gsystem-local-alloc.h"
+#include "NetworkManagerUtils.h"
+#include "nm-linux-platform.h"
+#include "nm-dhcp-manager.h"
+#include "nm-logging.h"
+#include "main-utils.h"
+#include "nm-rdisc.h"
+#include "nm-lndp-rdisc.h"
+#include "nm-utils.h"
+
+#if !defined(NM_DIST_VERSION)
+# define NM_DIST_VERSION VERSION
+#endif
+
+#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid"
+
+static GMainLoop *main_loop = NULL;
+static char *ifname = NULL;
+static int ifindex = -1;
+static gboolean slaac_required = FALSE;
+static gboolean dhcp4_required = FALSE;
+static int tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+static int priority = -1;
+
+static void
+dhcp4_state_changed (NMDhcpClient *client,
+ NMDhcpState state,
+ NMIP4Config *ip4_config,
+ GHashTable *options,
+ gpointer user_data)
+{
+ static NMIP4Config *last_config = NULL;
+ NMIP4Config *existing;
+
+ g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
+
+ nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", ifname, state);
+
+ switch (state) {
+ case NM_DHCP_STATE_BOUND:
+ g_assert (ip4_config);
+ existing = nm_ip4_config_capture (ifindex, FALSE);
+ if (last_config)
+ nm_ip4_config_subtract (existing, last_config);
+
+ nm_ip4_config_merge (existing, ip4_config);
+ if (!nm_ip4_config_commit (existing, ifindex))
+ nm_log_warn (LOGD_DHCP4, "(%s): failed to apply DHCPv4 config", ifname);
+
+ if (last_config) {
+ g_object_unref (last_config);
+ last_config = nm_ip4_config_new ();
+ nm_ip4_config_replace (last_config, ip4_config, NULL);
+ }
+ break;
+ case NM_DHCP_STATE_TIMEOUT:
+ case NM_DHCP_STATE_DONE:
+ case NM_DHCP_STATE_FAIL:
+ if (dhcp4_required) {
+ nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed, quitting...", ifname);
+ g_main_loop_quit (main_loop);
+ } else
+ nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed", ifname);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
+{
+ static NMIP6Config *last_config = NULL;
+ NMIP6Config *existing;
+ NMIP6Config *ip6_config;
+ static int system_support = -1;
+ guint ifa_flags = 0x00;
+ int i;
+
+ if (system_support == -1) {
+ /*
+ * Check, if both libnl and the kernel are recent enough,
+ * to help user space handling RA. If it's not supported,
+ * we have no ipv6-privacy and must add autoconf addresses
+ * as /128. The reason for the /128 is to prevent the kernel
+ * from adding a prefix route for this address.
+ **/
+ system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
+ nm_platform_check_support_kernel_extended_ifa_flags ();
+ }
+
+ if (system_support)
+ ifa_flags = IFA_F_NOPREFIXROUTE;
+ if (tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
+ || tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
+ {
+ /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
+ ifa_flags |= IFA_F_MANAGETEMPADDR;
+ }
+
+ ip6_config = nm_ip6_config_new ();
+
+ if (changed & NM_RDISC_CONFIG_GATEWAYS) {
+ /* Use the first gateway as ordered in router discovery cache. */
+ if (rdisc->gateways->len) {
+ NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
+
+ nm_ip6_config_set_gateway (ip6_config, &gateway->address);
+ } else
+ nm_ip6_config_set_gateway (ip6_config, NULL);
+ }
+
+ if (changed & NM_RDISC_CONFIG_ADDRESSES) {
+ /* Rebuild address list from router discovery cache. */
+ nm_ip6_config_reset_addresses (ip6_config);
+
+ /* rdisc->addresses contains at most max_addresses entries.
+ * This is different from what the kernel does, which
+ * also counts static and temporary addresses when checking
+ * max_addresses.
+ **/
+ for (i = 0; i < rdisc->addresses->len; i++) {
+ NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+ NMPlatformIP6Address address;
+
+ memset (&address, 0, sizeof (address));
+ address.address = discovered_address->address;
+ address.plen = system_support ? 64 : 128;
+ address.timestamp = discovered_address->timestamp;
+ address.lifetime = discovered_address->lifetime;
+ address.preferred = discovered_address->preferred;
+ if (address.preferred > address.lifetime)
+ address.preferred = address.lifetime;
+ address.source = NM_IP_CONFIG_SOURCE_RDISC;
+ address.flags = ifa_flags;
+
+ nm_ip6_config_add_address (ip6_config, &address);
+ }
+ }
+
+ if (changed & NM_RDISC_CONFIG_ROUTES) {
+ /* Rebuild route list from router discovery cache. */
+ nm_ip6_config_reset_routes (ip6_config);
+
+ for (i = 0; i < rdisc->routes->len; i++) {
+ NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
+ NMPlatformIP6Route route;
+
+ /* Only accept non-default routes. The router has no idea what the
+ * local configuration or user preferences are, so sending routes
+ * with a prefix length of 0 is quite rude and thus ignored.
+ */
+ if (discovered_route->plen > 0) {
+ memset (&route, 0, sizeof (route));
+ route.network = discovered_route->network;
+ route.plen = discovered_route->plen;
+ route.gateway = discovered_route->gateway;
+ route.source = NM_IP_CONFIG_SOURCE_RDISC;
+ route.metric = priority;
+
+ nm_ip6_config_add_route (ip6_config, &route);
+ }
+ }
+ }
+
+ if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
+ /* Unsupported until systemd DHCPv6 is ready */
+ }
+
+ /* hop_limit == 0 is a special value "unspecified", so do not touch
+ * in this case */
+ if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) {
+ char val[16];
+
+ g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit);
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "hop_limit"), val);
+ }
+
+ if (changed & NM_RDISC_CONFIG_MTU) {
+ char val[16];
+
+ g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "mtu"), val);
+ }
+
+ existing = nm_ip6_config_capture (ifindex, FALSE, tempaddr);
+ if (last_config)
+ nm_ip6_config_subtract (existing, last_config);
+
+ nm_ip6_config_merge (existing, ip6_config);
+ if (!nm_ip6_config_commit (existing, ifindex))
+ nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", ifname);
+
+ if (last_config) {
+ g_object_unref (last_config);
+ last_config = nm_ip6_config_new ();
+ nm_ip6_config_replace (last_config, ip6_config, NULL);
+ }
+}
+
+static void
+rdisc_ra_timeout (NMRDisc *rdisc, gpointer user_data)
+{
+ if (slaac_required) {
+ nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed, quitting...", ifname);
+ g_main_loop_quit (main_loop);
+ } else
+ nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed", ifname);
+}
+
+static gboolean
+quit_handler (gpointer user_data)
+{
+ gboolean *quit_early_ptr = user_data;
+
+ *quit_early_ptr = TRUE;
+ g_main_loop_quit (main_loop);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+setup_signals (gboolean *quit_early_ptr)
+{
+ sigset_t sigmask;
+
+ sigemptyset (&sigmask);
+ pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
+
+ signal (SIGPIPE, SIG_IGN);
+ g_unix_signal_add (SIGINT, quit_handler, quit_early_ptr);
+ g_unix_signal_add (SIGTERM, quit_handler, quit_early_ptr);
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *opt_log_level = NULL;
+ char *opt_log_domains = NULL;
+ gboolean debug = FALSE, g_fatal_warnings = FALSE, become_daemon = FALSE;
+ gboolean show_version = FALSE, slaac = FALSE;
+ char *bad_domains = NULL, *dhcp4_hostname = NULL, *uuid = NULL;
+ char *iid_str = NULL, *dhcp4_clientid = NULL, *dhcp4_address = NULL;
+ gs_unref_object NMDhcpManager *dhcp_mgr = NULL;
+ GError *error = NULL;
+ gboolean wrote_pidfile = FALSE;
+ gs_free char *pidfile = NULL;
+ gboolean quit_early = FALSE;
+ gs_unref_object NMDhcpClient *dhcp4_client = NULL;
+ gs_unref_object NMRDisc *rdisc = NULL;
+ GByteArray *hwaddr = NULL;
+ size_t hwaddr_len = 0;
+ gconstpointer tmp;
+ gs_free NMUtilsIPv6IfaceId *iid = NULL;
+
+ GOptionEntry options[] = {
+ /* Interface/IP config */
+ { "ifname", 'i', 0, G_OPTION_ARG_STRING, &ifname, N_("The interface to manage"), N_("eth0") },
+ { "uuid", 'u', 0, G_OPTION_ARG_STRING, &uuid, N_("Connection UUID"), N_("661e8cd0-b618-46b8-9dc9-31a52baaa16b") },
+ { "slaac", 's', 0, G_OPTION_ARG_NONE, &slaac, N_("Whether to manage IPv6 SLAAC"), NULL },
+ { "slaac-required", '6', 0, G_OPTION_ARG_NONE, &slaac_required, N_("Whether SLAAC must be successful"), NULL },
+ { "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &tempaddr, N_("Use an IPv6 temporary privacy address"), NULL },
+ { "dhcp4", 'd', 0, G_OPTION_ARG_STRING, &dhcp4_address, N_("Current DHCPv4 address"), NULL },
+ { "dhcp4-required", '4', 0, G_OPTION_ARG_NONE, &dhcp4_required, N_("Whether DHCPv4 must be successful"), NULL },
+ { "dhcp4-clientid", 'c', 0, G_OPTION_ARG_STRING, &dhcp4_clientid, N_("Hex-encoded DHCPv4 client ID"), NULL },
+ { "dhcp4-hostname", 'h', 0, G_OPTION_ARG_STRING, &dhcp4_hostname, N_("Hostname to send to DHCP server"), N_("barbar") },
+ { "priority", 'p', 0, G_OPTION_ARG_INT, &priority, N_("Route priority"), N_("10") },
+ { "iid", 'e', 0, G_OPTION_ARG_STRING, &iid_str, N_("Hex-encoded Interface Identifier"), N_("") },
+
+ /* Logging/debugging */
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
+ { "no-daemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &become_daemon, N_("Don't become a daemon"), NULL },
+ { "debug", 'b', 0, G_OPTION_ARG_NONE, &debug, N_("Don't become a daemon, and log to stderr"), NULL },
+ { "log-level", 0, 0, G_OPTION_ARG_STRING, &opt_log_level, N_("Log level: one of [%s]"), "INFO" },
+ { "log-domains", 0, 0, G_OPTION_ARG_STRING, &opt_log_domains,
+ N_("Log domains separated by ',': any combination of [%s]"),
+ "PLATFORM,RFKILL,WIFI" },
+ { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, N_("Make all warnings fatal"), NULL },
+ {NULL}
+ };
+
+ setpgid (getpid (), getpid ());
+
+ if (!nm_main_utils_early_setup ("nm-iface-helper",
+ &argv,
+ &argc,
+ options,
+ NULL,
+ _("nm-iface-helper is a small, standalone process that manages a single network interface.")))
+ exit (1);
+
+ if (show_version) {
+ fprintf (stdout, NM_DIST_VERSION "\n");
+ exit (0);
+ }
+
+ if (!ifname || !uuid) {
+ fprintf (stderr, _("An interface name and UUID are required\n"));
+ exit (1);
+ }
+
+ if (!nm_logging_setup (opt_log_level,
+ opt_log_domains,
+ &bad_domains,
+ &error)) {
+ fprintf (stderr,
+ _("%s. Please use --help to see a list of valid options.\n"),
+ error->message);
+ exit (1);
+ } else if (bad_domains) {
+ fprintf (stderr,
+ _("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
+ bad_domains);
+ g_clear_pointer (&bad_domains, g_free);
+ }
+
+ pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, ifindex);
+ g_assert (pidfile);
+
+ /* check pid file */
+ if (nm_main_utils_check_pidfile (pidfile, "nm-iface-helper"))
+ exit (1);
+
+ if (become_daemon && !debug) {
+ if (daemon (0, 0) < 0) {
+ int saved_errno;
+
+ saved_errno = errno;
+ fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
+ g_strerror (saved_errno),
+ saved_errno);
+ exit (1);
+ }
+ if (nm_main_utils_write_pidfile (pidfile))
+ wrote_pidfile = TRUE;
+ }
+
+ /* Set up unix signal handling - before creating threads, but after daemonizing! */
+ main_loop = g_main_loop_new (NULL, FALSE);
+ setup_signals (&quit_early);
+
+ if (g_fatal_warnings) {
+ GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);
+ }
+
+ nm_logging_syslog_openlog (debug);
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ nm_log_info (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
+
+ /* Set up platform interaction layer */
+ nm_linux_platform_setup ();
+
+ ifindex = nm_platform_link_get_ifindex (ifname);
+ if (ifindex <= 0) {
+ fprintf (stderr, _("Failed to find interface index for %s\n"), ifname);
+ exit (1);
+ }
+
+ tmp = nm_platform_link_get_address (ifindex, &hwaddr_len);
+ if (tmp) {
+ hwaddr = g_byte_array_sized_new (hwaddr_len);
+ g_byte_array_append (hwaddr, tmp, hwaddr_len);
+ }
+
+ if (iid_str) {
+ GBytes *bytes;
+ gsize ignored = 0;
+
+ bytes = nm_utils_hexstr2bin (iid_str);
+ if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) {
+ fprintf (stderr, _("(%s): Invalid IID %s\n"), ifname, iid_str);
+ exit (1);
+ }
+ iid = g_bytes_unref_to_data (bytes, &ignored);
+ }
+
+ priority = MAX (0, priority);
+
+ if (dhcp4_address) {
+ nm_platform_sysctl_set (nm_utils_ip4_property_path (ifname, "promote_secondaries"), "1");
+
+ /* Initialize DHCP manager */
+ dhcp_mgr = nm_dhcp_manager_get ();
+ g_assert (dhcp_mgr != NULL);
+
+ dhcp4_client = nm_dhcp_manager_start_ip4 (dhcp_mgr,
+ ifname,
+ ifindex,
+ hwaddr,
+ uuid,
+ priority,
+ !!dhcp4_hostname,
+ dhcp4_hostname,
+ dhcp4_clientid,
+ 45,
+ NULL,
+ dhcp4_address);
+ g_assert (dhcp4_client);
+ g_signal_connect (dhcp4_client,
+ NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
+ G_CALLBACK (dhcp4_state_changed),
+ NULL);
+ }
+
+ if (slaac) {
+ nm_platform_link_set_user_ipv6ll_enabled (ifindex, TRUE);
+
+ rdisc = nm_lndp_rdisc_new (ifindex, ifname);
+ g_assert (rdisc);
+
+ if (iid)
+ nm_rdisc_set_iid (rdisc, *iid);
+
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra"), "1");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_defrtr"), "0");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_pinfo"), "0");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_rtr_pref"), "0");
+
+ g_signal_connect (rdisc,
+ NM_RDISC_CONFIG_CHANGED,
+ G_CALLBACK (rdisc_config_changed),
+ NULL);
+ g_signal_connect (rdisc,
+ NM_RDISC_RA_TIMEOUT,
+ G_CALLBACK (rdisc_ra_timeout),
+ NULL);
+ nm_rdisc_start (rdisc);
+ }
+
+ if (!quit_early)
+ g_main_loop_run (main_loop);
+
+ g_clear_pointer (&hwaddr, g_byte_array_unref);
+
+ nm_logging_syslog_closelog ();
+
+ if (pidfile && wrote_pidfile)
+ unlink (pidfile);
+
+ nm_log_info (LOGD_CORE, "exiting");
+ exit (0);
+}
+
+/*******************************************************/
+/* Stub functions */
+
+gconstpointer nm_config_get (void);
+const char *nm_config_get_dhcp_client (gpointer unused);
+gboolean nm_config_get_configure_and_quit (gpointer unused);
+gconstpointer nm_dbus_manager_get (void);
+void nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2);
+void nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object);
+
+gconstpointer
+nm_config_get (void)
+{
+ return GUINT_TO_POINTER (1);
+}
+
+const char *
+nm_config_get_dhcp_client (gpointer unused)
+{
+ return "internal";
+}
+
+gboolean
+nm_config_get_configure_and_quit (gpointer unused)
+{
+ return TRUE;
+}
+
+gconstpointer
+nm_dbus_manager_get (void)
+{
+ return GUINT_TO_POINTER (1);
+}
+
+void
+nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2)
+{
+}
+
+void
+nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object)
+{
+}
+
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 72c1b0cd75..7548daded0 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -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
}
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index fe45f824d8..f0930b7453 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -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
}
diff --git a/src/nm-manager.c b/src/nm-manager.c
index f936235c78..d8973b7337 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -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);
diff --git a/src/nm-manager.h b/src/nm-manager.h
index f623516159..3b00e8053a 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -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);