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.
This commit is contained in:
Dan Williams
2014-10-29 09:12:18 -05:00
parent 9ff8c01d4a
commit 5149fd120d
14 changed files with 862 additions and 69 deletions

1
.gitignore vendored
View File

@@ -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_*

View File

@@ -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/*

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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.",

View File

@@ -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);

View File

@@ -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;
gboolean value = default_value;
char *str;
g_return_val_if_fail (str, FALSE);
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);
while (g_ascii_isspace (*str))
str++;
str = g_key_file_get_value (keyfile, section, key, NULL);
if (!str)
return default_value;
if (!*str)
return FALSE;
len = strlen (str);
if (g_ascii_isspace (str[len-1])) {
str = s = g_strdup (str);
g_strchomp (s);
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 (!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;
}
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;
}

529
src/nm-iface-helper.c Normal file
View File

@@ -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)
{
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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);