NetworkManager/src/nm-iface-helper.c

608 lines
20 KiB
C
Raw Normal View History

/* -*- 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 "nm-default.h"
#include <glib-unix.h>
#include <getopt.h>
#include <locale.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <signal.h>
/* Cannot include <net/if.h> due to conflict with <linux/if.h>.
* Forward declare if_nametoindex. */
extern unsigned int if_nametoindex (const char *__ifname);
#include "main-utils.h"
#include "NetworkManagerUtils.h"
#include "nm-linux-platform.h"
#include "nm-dhcp-manager.h"
#include "nm-rdisc.h"
#include "nm-lndp-rdisc.h"
#include "nm-utils.h"
core: add support for RFC7217 stable privacy addressing RFC7217 introduces an alternative mechanism for creating addresses during stateless IPv6 address configuration. It's supposed to create addresses whose host part stays stable in a particular network but changes when the hosts enters another network to mitigate possibility of tracking the host movement. It can be used alongside RFC 4941 privacy extensions (temporary addresses) and replaces the use of RFC 4862 interface identifiers. The address creation mode is controlld by ip6.addr_gen_mode property (ADDR_GEN_MODE in ifcfg-rh), with values of "stable-privacy" and "eui-64", defaulting to "eui-64" if unspecified. The host part of an address is computed by hashing a system-specific secret salted with various stable values that identify the connection with a secure hash algorithm: RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) For NetworkManager we use these parameters: * F() SHA256 hash function. * Prefix This is a network part of the /64 address * Net_Iface We use the interface name (e.g. "eth0"). This ensures the address won't change with the change of interface hardware. * Network_ID We use the connection UUID here. This ensures the salt is different for wireless networks with a different SSID as suggested by RFC7217. * DAD_Counter A per-address counter that increases with each DAD failure. * secret_key We store the secret key in /var/lib/NetworkManager/secret_key. If it's shorter than 128 bits then it's rejected. If the file is not present we initialize it by fetching 256 pseudo-random bits from /dev/urandom on first use. Duplicate address detection uses IDGEN_RETRIES = 3 and does not utilize the IDGEN_DELAY delay (despite it SHOULD). This is for ease of implementation and may change in future. Neither parameter is currently configurable.
2015-10-03 19:44:27 +02:00
#include "nm-setting-ip6-config.h"
#include "systemd/nm-sd.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
#endif
#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid"
/*****************************************************************************/
static struct {
GMainLoop *main_loop;
int ifindex;
} gl/*obal*/ = {
.ifindex = -1,
};
static struct {
gboolean slaac;
gboolean show_version;
gboolean become_daemon;
gboolean debug;
gboolean g_fatal_warnings;
gboolean slaac_required;
gboolean dhcp4_required;
int tempaddr;
char *ifname;
char *uuid;
char *stable_id;
char *dhcp4_address;
char *dhcp4_clientid;
char *dhcp4_hostname;
char *dhcp4_fqdn;
char *iid_str;
core: add support for RFC7217 stable privacy addressing RFC7217 introduces an alternative mechanism for creating addresses during stateless IPv6 address configuration. It's supposed to create addresses whose host part stays stable in a particular network but changes when the hosts enters another network to mitigate possibility of tracking the host movement. It can be used alongside RFC 4941 privacy extensions (temporary addresses) and replaces the use of RFC 4862 interface identifiers. The address creation mode is controlld by ip6.addr_gen_mode property (ADDR_GEN_MODE in ifcfg-rh), with values of "stable-privacy" and "eui-64", defaulting to "eui-64" if unspecified. The host part of an address is computed by hashing a system-specific secret salted with various stable values that identify the connection with a secure hash algorithm: RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) For NetworkManager we use these parameters: * F() SHA256 hash function. * Prefix This is a network part of the /64 address * Net_Iface We use the interface name (e.g. "eth0"). This ensures the address won't change with the change of interface hardware. * Network_ID We use the connection UUID here. This ensures the salt is different for wireless networks with a different SSID as suggested by RFC7217. * DAD_Counter A per-address counter that increases with each DAD failure. * secret_key We store the secret key in /var/lib/NetworkManager/secret_key. If it's shorter than 128 bits then it's rejected. If the file is not present we initialize it by fetching 256 pseudo-random bits from /dev/urandom on first use. Duplicate address detection uses IDGEN_RETRIES = 3 and does not utilize the IDGEN_DELAY delay (despite it SHOULD). This is for ease of implementation and may change in future. Neither parameter is currently configurable.
2015-10-03 19:44:27 +02:00
NMSettingIP6ConfigAddrGenMode addr_gen_mode;
char *logging_backend;
char *opt_log_level;
char *opt_log_domains;
guint32 priority_v4;
guint32 priority_v6;
} global_opt = {
.tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
.priority_v4 = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4,
.priority_v6 = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
};
/*****************************************************************************/
2016-10-05 12:04:46 +02:00
#define _NMLOG_PREFIX_NAME "nm-iface-helper"
#define _NMLOG(level, domain, ...) \
nm_log ((level), (domain), \
"%s[%ld] (%s): " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
_NMLOG_PREFIX_NAME, \
(long) getpid (), \
global_opt.ifname \
_NM_UTILS_MACRO_REST (__VA_ARGS__))
/*****************************************************************************/
static void
dhcp4_state_changed (NMDhcpClient *client,
NMDhcpState state,
NMIP4Config *ip4_config,
GHashTable *options,
const char *event_id,
gpointer user_data)
{
static NMIP4Config *last_config = NULL;
NMIP4Config *existing;
g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
2016-10-05 12:04:46 +02:00
_LOGD (LOGD_DHCP4, "new DHCPv4 client state %d", state);
switch (state) {
case NM_DHCP_STATE_BOUND:
g_assert (ip4_config);
existing = nm_ip4_config_capture (gl.ifindex, FALSE);
if (last_config)
nm_ip4_config_subtract (existing, last_config);
nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
if (!nm_ip4_config_commit (existing, gl.ifindex, TRUE, global_opt.priority_v4))
2016-10-05 12:04:46 +02:00
_LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config");
if (last_config)
g_object_unref (last_config);
last_config = nm_ip4_config_new (nm_dhcp_client_get_ifindex (client));
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 (global_opt.dhcp4_required) {
2016-10-05 12:04:46 +02:00
_LOGW (LOGD_DHCP4, "DHCPv4 timed out or failed, quitting...");
g_main_loop_quit (gl.main_loop);
} else
2016-10-05 12:04:46 +02:00
_LOGW (LOGD_DHCP4, "DHCPv4 timed out or failed");
break;
default:
break;
}
}
static void
rdisc_config_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, gpointer user_data)
{
NMRDiscConfigMap changed = changed_int;
static NMIP6Config *rdisc_config = NULL;
NMIP6Config *existing;
static int system_support = -1;
guint32 ifa_flags = 0x00;
int i;
if (system_support == -1) {
/*
platform: create netlink messages directly without libnl-route-3 Instead of using libnl-route-3 library to serialize netlink messages, construct the netlink messages ourselves. This has several advantages: - Creating the netlink message ourself is actually more straight forward then having an intermediate layer between NM and the kernel. Now it is immediately clear, how a platform request translates to a netlink/kernel request. You can look at the kernel sources how a certain netlink attribute behaves, and then it's immediately clear how to set that (and vice versa). - Older libnl versions might have bugs or missing features for which we needed to workaround (often by offering a reduced/broken/untested functionality). Now we can get rid or workaround like _nl_has_capability(), check_support_libnl_extended_ifa_flags(), HAVE_LIBNL_INET6_TOKEN. Another example is a libnl bug when setting vlan ingress map which isn't even yet fixed in libnl upstream. - We no longer need libnl-route-3 at all and can drop that runtime requirement, saving some 400k. Constructing the messages ourselves also gives better performance because we don't have to create the intermediate libnl object. - In the future we will add more link-type support which is easier to support by basing directly on the plain kernel/netlink API, instead of requiring also libnl3 to expose this functionality. E.g. adding macvtap support: we already parsed macvtap properties ourselves because of missing libnl support. To *add* macvtap support, we also would have to do it ourself (or extend libnl).
2015-10-20 09:27:16 +02:00
* Check, whether kernel is 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.
**/
platform: create netlink messages directly without libnl-route-3 Instead of using libnl-route-3 library to serialize netlink messages, construct the netlink messages ourselves. This has several advantages: - Creating the netlink message ourself is actually more straight forward then having an intermediate layer between NM and the kernel. Now it is immediately clear, how a platform request translates to a netlink/kernel request. You can look at the kernel sources how a certain netlink attribute behaves, and then it's immediately clear how to set that (and vice versa). - Older libnl versions might have bugs or missing features for which we needed to workaround (often by offering a reduced/broken/untested functionality). Now we can get rid or workaround like _nl_has_capability(), check_support_libnl_extended_ifa_flags(), HAVE_LIBNL_INET6_TOKEN. Another example is a libnl bug when setting vlan ingress map which isn't even yet fixed in libnl upstream. - We no longer need libnl-route-3 at all and can drop that runtime requirement, saving some 400k. Constructing the messages ourselves also gives better performance because we don't have to create the intermediate libnl object. - In the future we will add more link-type support which is easier to support by basing directly on the plain kernel/netlink API, instead of requiring also libnl3 to expose this functionality. E.g. adding macvtap support: we already parsed macvtap properties ourselves because of missing libnl support. To *add* macvtap support, we also would have to do it ourself (or extend libnl).
2015-10-20 09:27:16 +02:00
system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
}
if (system_support)
ifa_flags = IFA_F_NOPREFIXROUTE;
if (global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
|| global_opt.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;
}
existing = nm_ip6_config_capture (gl.ifindex, FALSE, global_opt.tempaddr);
if (rdisc_config)
nm_ip6_config_subtract (existing, rdisc_config);
else
rdisc_config = nm_ip6_config_new (gl.ifindex);
if (changed & NM_RDISC_CONFIG_GATEWAYS) {
/* Use the first gateway as ordered in router discovery cache. */
if (rdata->gateways_n)
nm_ip6_config_set_gateway (rdisc_config, &rdata->gateways[0].address);
else
nm_ip6_config_set_gateway (rdisc_config, NULL);
}
if (changed & NM_RDISC_CONFIG_ADDRESSES) {
/* Rebuild address list from router discovery cache. */
nm_ip6_config_reset_addresses (rdisc_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 < rdata->addresses_n; i++) {
const NMRDiscAddress *discovered_address = &rdata->addresses[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.addr_source = NM_IP_CONFIG_SOURCE_RDISC;
address.n_ifa_flags = ifa_flags;
nm_ip6_config_add_address (rdisc_config, &address);
}
}
if (changed & NM_RDISC_CONFIG_ROUTES) {
/* Rebuild route list from router discovery cache. */
nm_ip6_config_reset_routes (rdisc_config);
for (i = 0; i < rdata->routes_n; i++) {
const NMRDiscRoute *discovered_route = &rdata->routes[i];
const NMPlatformIP6Route route = {
.network = discovered_route->network,
.plen = discovered_route->plen,
.gateway = discovered_route->gateway,
.rt_source = NM_IP_CONFIG_SOURCE_RDISC,
.metric = global_opt.priority_v6,
};
nm_ip6_config_add_route (rdisc_config, &route);
}
}
if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
/* Unsupported until systemd DHCPv6 is ready */
}
if (changed & NM_RDISC_CONFIG_HOP_LIMIT)
nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit);
if (changed & NM_RDISC_CONFIG_MTU) {
char val[16];
g_snprintf (val, sizeof (val), "%d", rdata->mtu);
platform: add self argument to platform functions Most nm_platform_*() functions operate on the platform singleton nm_platform_get(). That made sense because the NMPlatform instance was mainly to hook fake platform for testing. While the implicit argument saved some typing, I think explicit is better. Especially, because NMPlatform could become a more usable object then just a hook for testing. With this change, NMPlatform instances can be used individually, not only as a singleton instance. Before this change, the constructor of NMLinuxPlatform could not call any nm_platform_*() functions because the singleton was not yet initialized. We could only instantiate an incomplete instance, register it via nm_platform_setup(), and then complete initialization via singleton->setup(). With this change, we can create and fully initialize NMPlatform instances before/without setting them up them as singleton. Also, currently there is no clear distinction between functions that operate on the NMPlatform instance, and functions that can be used stand-alone (e.g. nm_platform_ip4_address_to_string()). The latter can not be mocked for testing. With this change, the distinction becomes obvious. That is also useful because it becomes clearer which functions make use of the platform cache and which not. Inside nm-linux-platform.c, continue the pattern that the self instance is named @platform. That makes sense because its type is NMPlatform, and not NMLinuxPlatform what we would expect from a paramter named @self. This is a major diff that causes some pain when rebasing. Try to rebase to the parent commit of this commit as a first step. Then rebase on top of this commit using merge-strategy "ours".
2015-04-18 12:36:09 +02:00
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val);
}
nm_ip6_config_merge (existing, rdisc_config, NM_IP_CONFIG_MERGE_DEFAULT);
if (!nm_ip6_config_commit (existing, gl.ifindex, TRUE))
2016-10-05 12:04:46 +02:00
_LOGW (LOGD_IP6, "failed to apply IPv6 config");
}
static void
rdisc_ra_timeout (NMRDisc *rdisc, gpointer user_data)
{
if (global_opt.slaac_required) {
2016-10-05 12:04:46 +02:00
_LOGW (LOGD_IP6, "IPv6 timed out or failed, quitting...");
g_main_loop_quit (gl.main_loop);
} else
2016-10-05 12:04:46 +02:00
_LOGW (LOGD_IP6, "IPv6 timed out or failed");
}
static gboolean
quit_handler (gpointer user_data)
{
g_main_loop_quit (gl.main_loop);
return G_SOURCE_REMOVE;
}
static void
setup_signals (void)
{
signal (SIGPIPE, SIG_IGN);
g_unix_signal_add (SIGINT, quit_handler, NULL);
g_unix_signal_add (SIGTERM, quit_handler, NULL);
}
static gboolean
do_early_setup (int *argc, char **argv[])
{
gint64 priority64_v4 = -1;
gint64 priority64_v6 = -1;
GOptionEntry options[] = {
/* Interface/IP config */
{ "ifname", 'i', 0, G_OPTION_ARG_STRING, &global_opt.ifname, N_("The interface to manage"), "eth0" },
{ "uuid", 'u', 0, G_OPTION_ARG_STRING, &global_opt.uuid, N_("Connection UUID"), "661e8cd0-b618-46b8-9dc9-31a52baaa16b" },
{ "stable-id", '\0', 0, G_OPTION_ARG_STRING, &global_opt.stable_id, N_("Connection Token for Stable IDs"), "eth" },
{ "slaac", 's', 0, G_OPTION_ARG_NONE, &global_opt.slaac, N_("Whether to manage IPv6 SLAAC"), NULL },
{ "slaac-required", '6', 0, G_OPTION_ARG_NONE, &global_opt.slaac_required, N_("Whether SLAAC must be successful"), NULL },
{ "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &global_opt.tempaddr, N_("Use an IPv6 temporary privacy address"), NULL },
{ "dhcp4", 'd', 0, G_OPTION_ARG_STRING, &global_opt.dhcp4_address, N_("Current DHCPv4 address"), NULL },
{ "dhcp4-required", '4', 0, G_OPTION_ARG_NONE, &global_opt.dhcp4_required, N_("Whether DHCPv4 must be successful"), NULL },
{ "dhcp4-clientid", 'c', 0, G_OPTION_ARG_STRING, &global_opt.dhcp4_clientid, N_("Hex-encoded DHCPv4 client ID"), NULL },
{ "dhcp4-hostname", 'h', 0, G_OPTION_ARG_STRING, &global_opt.dhcp4_hostname, N_("Hostname to send to DHCP server"), N_("barbar") },
{ "dhcp4-fqdn", 'F', 0, G_OPTION_ARG_STRING, &global_opt.dhcp4_fqdn, N_("FQDN to send to DHCP server"), N_("host.domain.org") },
{ "priority4", '\0', 0, G_OPTION_ARG_INT64, &priority64_v4, N_("Route priority for IPv4"), N_("0") },
{ "priority6", '\0', 0, G_OPTION_ARG_INT64, &priority64_v6, N_("Route priority for IPv6"), N_("1024") },
{ "iid", 'e', 0, G_OPTION_ARG_STRING, &global_opt.iid_str, N_("Hex-encoded Interface Identifier"), "" },
core: add support for RFC7217 stable privacy addressing RFC7217 introduces an alternative mechanism for creating addresses during stateless IPv6 address configuration. It's supposed to create addresses whose host part stays stable in a particular network but changes when the hosts enters another network to mitigate possibility of tracking the host movement. It can be used alongside RFC 4941 privacy extensions (temporary addresses) and replaces the use of RFC 4862 interface identifiers. The address creation mode is controlld by ip6.addr_gen_mode property (ADDR_GEN_MODE in ifcfg-rh), with values of "stable-privacy" and "eui-64", defaulting to "eui-64" if unspecified. The host part of an address is computed by hashing a system-specific secret salted with various stable values that identify the connection with a secure hash algorithm: RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) For NetworkManager we use these parameters: * F() SHA256 hash function. * Prefix This is a network part of the /64 address * Net_Iface We use the interface name (e.g. "eth0"). This ensures the address won't change with the change of interface hardware. * Network_ID We use the connection UUID here. This ensures the salt is different for wireless networks with a different SSID as suggested by RFC7217. * DAD_Counter A per-address counter that increases with each DAD failure. * secret_key We store the secret key in /var/lib/NetworkManager/secret_key. If it's shorter than 128 bits then it's rejected. If the file is not present we initialize it by fetching 256 pseudo-random bits from /dev/urandom on first use. Duplicate address detection uses IDGEN_RETRIES = 3 and does not utilize the IDGEN_DELAY delay (despite it SHOULD). This is for ease of implementation and may change in future. Neither parameter is currently configurable.
2015-10-03 19:44:27 +02:00
{ "addr-gen-mode", 'e', 0, G_OPTION_ARG_INT, &global_opt.addr_gen_mode, N_("IPv6 SLAAC address generation mode"), "eui64" },
{ "logging-backend", '\0', 0, G_OPTION_ARG_STRING, &global_opt.logging_backend, N_("The logging backend configuration value. See logging.backend in NetworkManager.conf"), NULL },
/* Logging/debugging */
{ "version", 'V', 0, G_OPTION_ARG_NONE, &global_opt.show_version, N_("Print NetworkManager version and exit"), NULL },
{ "no-daemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &global_opt.become_daemon, N_("Don't become a daemon"), NULL },
{ "debug", 'b', 0, G_OPTION_ARG_NONE, &global_opt.debug, N_("Don't become a daemon, and log to stderr"), NULL },
{ "log-level", 0, 0, G_OPTION_ARG_STRING, &global_opt.opt_log_level, N_("Log level: one of [%s]"), "INFO" },
{ "log-domains", 0, 0, G_OPTION_ARG_STRING, &global_opt.opt_log_domains,
N_("Log domains separated by ',': any combination of [%s]"),
"PLATFORM,RFKILL,WIFI" },
{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
{NULL}
};
if (!nm_main_utils_early_setup ("nm-iface-helper",
argc,
argv,
options,
NULL,
NULL,
_("nm-iface-helper is a small, standalone process that manages a single network interface.")))
return FALSE;
if (priority64_v4 >= 0 && priority64_v4 <= G_MAXUINT32)
global_opt.priority_v4 = (guint32) priority64_v4;
if (priority64_v6 >= 0 && priority64_v6 <= G_MAXUINT32)
global_opt.priority_v6 = (guint32) priority64_v6;
return TRUE;
}
static void
ip6_address_changed (NMPlatform *platform,
NMPObjectType obj_type,
int iface,
NMPlatformIP6Address *addr,
NMPlatformSignalChangeType change_type,
NMRDisc *rdisc)
{
if ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
|| (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))
nm_rdisc_dad_failed (rdisc, &addr->address);
}
int
main (int argc, char *argv[])
{
char *bad_domains = NULL;
GError *error = NULL;
gboolean wrote_pidfile = FALSE;
gs_free char *pidfile = NULL;
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;
guint sd_id;
nm_g_type_init ();
setpgid (getpid (), getpid ());
if (!do_early_setup (&argc, &argv))
return 1;
if (global_opt.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);
}
if (global_opt.show_version) {
fprintf (stdout, NM_DIST_VERSION "\n");
return 0;
}
nm_main_utils_ensure_root ();
if (!global_opt.ifname || !global_opt.uuid) {
fprintf (stderr, _("An interface name and UUID are required\n"));
return 1;
}
gl.ifindex = if_nametoindex (global_opt.ifname);
if (gl.ifindex <= 0) {
fprintf (stderr, _("Failed to find interface index for %s (%s)\n"), global_opt.ifname, strerror (errno));
return 1;
}
pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, gl.ifindex);
nm_main_utils_ensure_not_running_pidfile (pidfile);
nm_main_utils_ensure_rundir ();
if (!nm_logging_setup (global_opt.opt_log_level,
global_opt.opt_log_domains,
&bad_domains,
&error)) {
fprintf (stderr,
_("%s. Please use --help to see a list of valid options.\n"),
error->message);
return 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);
}
if (global_opt.become_daemon && !global_opt.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);
return 1;
}
if (nm_main_utils_write_pidfile (pidfile))
wrote_pidfile = TRUE;
}
/* Set up unix signal handling - before creating threads, but after daemonizing! */
gl.main_loop = g_main_loop_new (NULL, FALSE);
setup_signals ();
nm_logging_syslog_openlog (global_opt.logging_backend
? global_opt.logging_backend
: (global_opt.debug ? "debug" : NULL));
2016-10-05 12:04:46 +02:00
_LOGI (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
/* Set up platform interaction layer */
nm_linux_platform_setup ();
tmp = nm_platform_link_get_address (NM_PLATFORM_GET, gl.ifindex, &hwaddr_len);
if (tmp) {
hwaddr = g_byte_array_sized_new (hwaddr_len);
g_byte_array_append (hwaddr, tmp, hwaddr_len);
}
if (global_opt.iid_str) {
GBytes *bytes;
gsize ignored = 0;
bytes = nm_utils_hexstr2bin (global_opt.iid_str);
if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) {
fprintf (stderr, _("(%s): Invalid IID %s\n"), global_opt.ifname, global_opt.iid_str);
return 1;
}
iid = g_bytes_unref_to_data (bytes, &ignored);
}
if (global_opt.dhcp4_address) {
platform: add self argument to platform functions Most nm_platform_*() functions operate on the platform singleton nm_platform_get(). That made sense because the NMPlatform instance was mainly to hook fake platform for testing. While the implicit argument saved some typing, I think explicit is better. Especially, because NMPlatform could become a more usable object then just a hook for testing. With this change, NMPlatform instances can be used individually, not only as a singleton instance. Before this change, the constructor of NMLinuxPlatform could not call any nm_platform_*() functions because the singleton was not yet initialized. We could only instantiate an incomplete instance, register it via nm_platform_setup(), and then complete initialization via singleton->setup(). With this change, we can create and fully initialize NMPlatform instances before/without setting them up them as singleton. Also, currently there is no clear distinction between functions that operate on the NMPlatform instance, and functions that can be used stand-alone (e.g. nm_platform_ip4_address_to_string()). The latter can not be mocked for testing. With this change, the distinction becomes obvious. That is also useful because it becomes clearer which functions make use of the platform cache and which not. Inside nm-linux-platform.c, continue the pattern that the self instance is named @platform. That makes sense because its type is NMPlatform, and not NMLinuxPlatform what we would expect from a paramter named @self. This is a major diff that causes some pain when rebasing. Try to rebase to the parent commit of this commit as a first step. Then rebase on top of this commit using merge-strategy "ours".
2015-04-18 12:36:09 +02:00
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries"), "1");
dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
global_opt.ifname,
gl.ifindex,
hwaddr,
global_opt.uuid,
global_opt.priority_v4,
!!global_opt.dhcp4_hostname,
global_opt.dhcp4_hostname,
global_opt.dhcp4_fqdn,
global_opt.dhcp4_clientid,
45,
NULL,
global_opt.dhcp4_address);
g_assert (dhcp4_client);
g_signal_connect (dhcp4_client,
NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
G_CALLBACK (dhcp4_state_changed),
NULL);
}
if (global_opt.slaac) {
NMUtilsStableType stable_type = NM_UTILS_STABLE_TYPE_UUID;
const char *stable_id = global_opt.uuid;
nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, gl.ifindex, TRUE);
if ( global_opt.stable_id
&& (global_opt.stable_id[0] >= '0' && global_opt.stable_id[0] <= '9')
&& global_opt.stable_id[1] == ' ') {
/* strict parsing of --stable-id, which is the numeric stable-type
* and the ID, joined with one space. For now, only support stable-types
* from 0 to 9. */
stable_type = (global_opt.stable_id[0] - '0');
stable_id = &global_opt.stable_id[2];
}
rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, gl.ifindex, global_opt.ifname,
stable_type, stable_id,
global_opt.addr_gen_mode, NULL);
g_assert (rdisc);
if (iid)
nm_rdisc_set_iid (rdisc, *iid);
platform: add self argument to platform functions Most nm_platform_*() functions operate on the platform singleton nm_platform_get(). That made sense because the NMPlatform instance was mainly to hook fake platform for testing. While the implicit argument saved some typing, I think explicit is better. Especially, because NMPlatform could become a more usable object then just a hook for testing. With this change, NMPlatform instances can be used individually, not only as a singleton instance. Before this change, the constructor of NMLinuxPlatform could not call any nm_platform_*() functions because the singleton was not yet initialized. We could only instantiate an incomplete instance, register it via nm_platform_setup(), and then complete initialization via singleton->setup(). With this change, we can create and fully initialize NMPlatform instances before/without setting them up them as singleton. Also, currently there is no clear distinction between functions that operate on the NMPlatform instance, and functions that can be used stand-alone (e.g. nm_platform_ip4_address_to_string()). The latter can not be mocked for testing. With this change, the distinction becomes obvious. That is also useful because it becomes clearer which functions make use of the platform cache and which not. Inside nm-linux-platform.c, continue the pattern that the self instance is named @platform. That makes sense because its type is NMPlatform, and not NMLinuxPlatform what we would expect from a paramter named @self. This is a major diff that causes some pain when rebasing. Try to rebase to the parent commit of this commit as a first step. Then rebase on top of this commit using merge-strategy "ours".
2015-04-18 12:36:09 +02:00
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra"), "1");
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr"), "0");
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo"), "0");
nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref"), "0");
g_signal_connect (NM_PLATFORM_GET,
NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
G_CALLBACK (ip6_address_changed),
rdisc);
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);
}
sd_id = nm_sd_event_attach_default ();
g_main_loop_run (gl.main_loop);
g_clear_pointer (&hwaddr, g_byte_array_unref);
if (pidfile && wrote_pidfile)
unlink (pidfile);
2016-10-05 12:04:46 +02:00
_LOGI (LOGD_CORE, "exiting");
nm_clear_g_source (&sd_id);
g_clear_pointer (&gl.main_loop, g_main_loop_unref);
return 0;
}
/*****************************************************************************/
/* Stub functions */
void
nm_main_config_reload (int signal)
{
2016-10-05 12:04:46 +02:00
_LOGI (LOGD_CORE, "reloading configuration not supported");
}
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_bus_manager_get (void);
void nm_bus_manager_register_object (gpointer unused, gpointer object);
void nm_bus_manager_unregister_object (gpointer unused, gpointer object);
dbus: fix emitting D-Bus NetworkManager's old-style PropertiesChange signal Before switching to gdbus (before 1.2.0), NetworkManager used dbus-glib. Most objects in the D-Bus API with properties had a signal NetworkManager-specific "PropertiesChanged" signal. Nowadays, this way of handling of property changes is deprecated for the common "PropertiesChanged" signal on the "org.freedesktop.DBus.Properties" interface. There were a few pecularities in 1.0.0 and earlier: (1) Due to the implementation with dbus-glib, a property-changed signal was emitted on *all* interfaces. For example: - a change on a NMDeviceVeth of "NMDeviceEthernet.HwAddress" would be emitted both for the interfaces "fdo.NM.Device.Ethernet" and "fdo.NM.Device.Veth". Note that NMDeviceVeth is derived from NMDeviceEthernet and there is no "HwAddress" on veth device. - a change of "NMVpnConnection.VpnState" was emitted on both interfaces "fdo.NM.VPN.Connection" and "fdo.NM.Connecion.Active". Note that NMActiveConnection is the parent type of NMVpnConnection and only the latter has a property "VpnState". (2) NMDevice's "fdo.NM.Device" interface doesn't have a "PropertiesChanged" signal. From (1) follows that all property-changes for this type were instead invoked with an interface like "fdo.NM.Device.Ethernet" (or multiple interfaces in case of NMDeviceVeth). 1.2.0 introduced gdbus, which gives us the standard "fdo.DBus.Properties" signal. However, it made the mistake of not realizing (1), thus instead of emitting the signal once for each interface, it would pick the first one in the inheritance tree. With 1.4.0, a bug from merge commit 844345e caused signals for devices to be only emitted for the interface "fdo.NM.Device.Statistics", instead of "fdo.NM.Device.Ethernet" or "fdo.NM.Device.Veth" (or both). The latter is what bgo#770629 is about and what commit 82e9439 tried to fix. However, the fix was wrong because it tried to do the theoretically correct thing of emitting the property-changed signal exactly once for the interface that actually ontains the property. In addition, it missed that NMDevice doesn't have a PropertiesChanged signal, which caused signals for "fdo.NM.Device" to get lost *sigh*. Now, restore the (broken) behavior of 1.0.0. These old-style property changed signals are anyway considered deprecated and exist solely to satisfy old clients and preserve the old API. Fixes: 63fbfad3705db5901e6a2a6a2fc332da0f0ae4be https://bugzilla.gnome.org/show_bug.cgi?id=770629 https://bugzilla.redhat.com/show_bug.cgi?id=1371920
2016-09-01 13:00:21 +02:00
GType nm_device_get_type (void);
GType nm_active_connection_get_type (void);
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_bus_manager_get (void)
{
return GUINT_TO_POINTER (1);
}
void
nm_bus_manager_register_object (gpointer unused, gpointer object)
{
}
void
nm_bus_manager_unregister_object (gpointer unused, gpointer object)
{
}
dbus: fix emitting D-Bus NetworkManager's old-style PropertiesChange signal Before switching to gdbus (before 1.2.0), NetworkManager used dbus-glib. Most objects in the D-Bus API with properties had a signal NetworkManager-specific "PropertiesChanged" signal. Nowadays, this way of handling of property changes is deprecated for the common "PropertiesChanged" signal on the "org.freedesktop.DBus.Properties" interface. There were a few pecularities in 1.0.0 and earlier: (1) Due to the implementation with dbus-glib, a property-changed signal was emitted on *all* interfaces. For example: - a change on a NMDeviceVeth of "NMDeviceEthernet.HwAddress" would be emitted both for the interfaces "fdo.NM.Device.Ethernet" and "fdo.NM.Device.Veth". Note that NMDeviceVeth is derived from NMDeviceEthernet and there is no "HwAddress" on veth device. - a change of "NMVpnConnection.VpnState" was emitted on both interfaces "fdo.NM.VPN.Connection" and "fdo.NM.Connecion.Active". Note that NMActiveConnection is the parent type of NMVpnConnection and only the latter has a property "VpnState". (2) NMDevice's "fdo.NM.Device" interface doesn't have a "PropertiesChanged" signal. From (1) follows that all property-changes for this type were instead invoked with an interface like "fdo.NM.Device.Ethernet" (or multiple interfaces in case of NMDeviceVeth). 1.2.0 introduced gdbus, which gives us the standard "fdo.DBus.Properties" signal. However, it made the mistake of not realizing (1), thus instead of emitting the signal once for each interface, it would pick the first one in the inheritance tree. With 1.4.0, a bug from merge commit 844345e caused signals for devices to be only emitted for the interface "fdo.NM.Device.Statistics", instead of "fdo.NM.Device.Ethernet" or "fdo.NM.Device.Veth" (or both). The latter is what bgo#770629 is about and what commit 82e9439 tried to fix. However, the fix was wrong because it tried to do the theoretically correct thing of emitting the property-changed signal exactly once for the interface that actually ontains the property. In addition, it missed that NMDevice doesn't have a PropertiesChanged signal, which caused signals for "fdo.NM.Device" to get lost *sigh*. Now, restore the (broken) behavior of 1.0.0. These old-style property changed signals are anyway considered deprecated and exist solely to satisfy old clients and preserve the old API. Fixes: 63fbfad3705db5901e6a2a6a2fc332da0f0ae4be https://bugzilla.gnome.org/show_bug.cgi?id=770629 https://bugzilla.redhat.com/show_bug.cgi?id=1371920
2016-09-01 13:00:21 +02:00
GType
nm_device_get_type (void)
{
g_return_val_if_reached (0);
}
GType
nm_active_connection_get_type (void)
{
g_return_val_if_reached (0);
}