nm-iface-helper.c 22.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* -*- 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.
 */

21
#include "nm-default.h"
22

23 24 25 26 27 28 29 30 31
#include <glib-unix.h>
#include <getopt.h>
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <signal.h>
32
#include <linux/rtnetlink.h>
33

34
#include "nm-glib-aux/nm-c-list.h"
35

36
#include "main-utils.h"
37
#include "NetworkManagerUtils.h"
38
#include "platform/nm-linux-platform.h"
39
#include "platform/nm-platform-utils.h"
40 41 42
#include "dhcp/nm-dhcp-manager.h"
#include "ndisc/nm-ndisc.h"
#include "ndisc/nm-lndp-ndisc.h"
43
#include "nm-utils.h"
44
#include "nm-setting-ip6-config.h"
45
#include "systemd/nm-sd.h"
46 47 48 49 50 51 52

#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
#endif

#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid"

53 54 55 56 57
/*****************************************************************************/

static struct {
	GMainLoop *main_loop;
	int ifindex;
58 59 60

	guint dad_failed_id;
	CList dad_failed_lst_head;
61 62 63
} gl/*obal*/ = {
	.ifindex = -1,
};
64

65 66 67 68 69 70 71 72 73 74 75
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;
76
	char *stable_id;
77 78 79
	char *dhcp4_address;
	char *dhcp4_clientid;
	char *dhcp4_hostname;
80
	char *dhcp4_fqdn;
81
	char *iid_str;
82
	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
83
	char *logging_backend;
84 85
	char *opt_log_level;
	char *opt_log_domains;
86 87
	guint32 priority_v4;
	guint32 priority_v6;
88 89
} global_opt = {
	.tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
90 91
	.priority_v4 = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4,
	.priority_v6 = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
92 93
};

94 95
/*****************************************************************************/

96 97
#define _NMLOG_PREFIX_NAME      "nm-iface-helper"
#define _NMLOG(level, domain, ...) \
98
    nm_log ((level), (domain), global_opt.ifname, NULL, \
99
            "iface-helper: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) \
100 101 102 103
            _NM_UTILS_MACRO_REST (__VA_ARGS__))

/*****************************************************************************/

104 105 106 107 108
static void
dhcp4_state_changed (NMDhcpClient *client,
                     NMDhcpState state,
                     NMIP4Config *ip4_config,
                     GHashTable *options,
109
                     const char *event_id,
110 111 112 113
                     gpointer user_data)
{
	static NMIP4Config *last_config = NULL;
	NMIP4Config *existing;
114
	gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
115 116 117

	g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));

118
	_LOGD (LOGD_DHCP4, "new DHCPv4 client state %d", state);
119 120 121 122

	switch (state) {
	case NM_DHCP_STATE_BOUND:
		g_assert (ip4_config);
123 124
		g_assert (nm_ip4_config_get_ifindex (ip4_config) == gl.ifindex);

125
		existing = nm_ip4_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET),
126
		                                  NM_PLATFORM_GET, gl.ifindex);
127
		if (last_config)
128
			nm_ip4_config_subtract (existing, last_config, 0);
129

130 131 132 133 134
		nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0);
		nm_ip4_config_add_dependent_routes (existing,
		                                    RT_TABLE_MAIN,
		                                    global_opt.priority_v4,
		                                    &ip4_dev_route_blacklist);
135
		if (!nm_ip4_config_commit (existing,
136 137
		                           NM_PLATFORM_GET,
		                           NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN))
138
			_LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config");
139

140 141 142 143
		nm_platform_ip4_dev_route_blacklist_set (NM_PLATFORM_GET,
		                                         gl.ifindex,
		                                         ip4_dev_route_blacklist);

144
		if (last_config)
145
			g_object_unref (last_config);
146 147
		last_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
		                                 nm_dhcp_client_get_ifindex (client));
148
		nm_ip4_config_replace (last_config, ip4_config, NULL);
149 150 151 152
		break;
	case NM_DHCP_STATE_TIMEOUT:
	case NM_DHCP_STATE_DONE:
	case NM_DHCP_STATE_FAIL:
153
		if (global_opt.dhcp4_required) {
154
			_LOGW (LOGD_DHCP4, "DHCPv4 timed out or failed, quitting...");
155
			g_main_loop_quit (gl.main_loop);
156
		} else
157
			_LOGW (LOGD_DHCP4, "DHCPv4 timed out or failed");
158 159 160 161 162 163 164
		break;
	default:
		break;
	}
}

static void
Lubomir Rintel's avatar
Lubomir Rintel committed
165
ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, gpointer user_data)
166
{
Lubomir Rintel's avatar
Lubomir Rintel committed
167 168
	NMNDiscConfigMap changed = changed_int;
	static NMIP6Config *ndisc_config = NULL;
169 170
	NMIP6Config *existing;

171
	existing = nm_ip6_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET),
172
	                                  NM_PLATFORM_GET, gl.ifindex, global_opt.tempaddr);
Lubomir Rintel's avatar
Lubomir Rintel committed
173
	if (ndisc_config)
174
		nm_ip6_config_subtract (existing, ndisc_config, 0);
175 176 177 178
	else {
		ndisc_config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
		                                  gl.ifindex);
	}
179

Lubomir Rintel's avatar
Lubomir Rintel committed
180
	if (changed & NM_NDISC_CONFIG_ADDRESSES) {
181 182 183 184 185 186 187 188
		guint8 plen;
		guint32 ifa_flags;

		/* 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. */
		ifa_flags = 0;
189
		if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) {
190 191 192 193 194 195 196 197
			ifa_flags |= IFA_F_NOPREFIXROUTE;
			if (NM_IN_SET (global_opt.tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
			                                    NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR))
				ifa_flags |= IFA_F_MANAGETEMPADDR;
			plen = 64;
		} else
			plen = 128;

198 199 200
		nm_ip6_config_reset_addresses_ndisc (ndisc_config,
		                                     rdata->addresses,
		                                     rdata->addresses_n,
201
		                                     plen,
202
		                                     ifa_flags);
203 204
	}

205 206
	if (NM_FLAGS_ANY (changed,  NM_NDISC_CONFIG_ROUTES
	                          | NM_NDISC_CONFIG_GATEWAYS)) {
207
		nm_ip6_config_reset_routes_ndisc (ndisc_config,
208 209
		                                  rdata->gateways,
		                                  rdata->gateways_n,
210 211
		                                  rdata->routes,
		                                  rdata->routes_n,
212
		                                  RT_TABLE_MAIN,
213
		                                  global_opt.priority_v6,
214
		                                  nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF));
215 216
	}

Lubomir Rintel's avatar
Lubomir Rintel committed
217
	if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) {
218 219 220
		/* Unsupported until systemd DHCPv6 is ready */
	}

Lubomir Rintel's avatar
Lubomir Rintel committed
221
	if (changed & NM_NDISC_CONFIG_HOP_LIMIT)
222
		nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit);
223

Lubomir Rintel's avatar
Lubomir Rintel committed
224
	if (changed & NM_NDISC_CONFIG_MTU) {
225 226 227 228 229
		nm_platform_sysctl_ip_conf_set_int64 (NM_PLATFORM_GET,
		                                      AF_INET6,
		                                      global_opt.ifname,
		                                      "mtu",
		                                      rdata->mtu);
230 231
	}

232 233 234 235
	nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT, 0);
	nm_ip6_config_add_dependent_routes (existing,
	                                    RT_TABLE_MAIN,
	                                    global_opt.priority_v6);
236 237 238 239
	if (!nm_ip6_config_commit (existing,
	                           NM_PLATFORM_GET,
	                           NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
	                           NULL))
240
		_LOGW (LOGD_IP6, "failed to apply IPv6 config");
241 242 243
}

static void
Lubomir Rintel's avatar
Lubomir Rintel committed
244
ndisc_ra_timeout (NMNDisc *ndisc, gpointer user_data)
245
{
246
	if (global_opt.slaac_required) {
247
		_LOGW (LOGD_IP6, "IPv6 timed out or failed, quitting...");
248
		g_main_loop_quit (gl.main_loop);
249
	} else
250
		_LOGW (LOGD_IP6, "IPv6 timed out or failed");
251 252 253 254 255
}

static gboolean
quit_handler (gpointer user_data)
{
256
	g_main_loop_quit (gl.main_loop);
257
	return G_SOURCE_REMOVE;
258 259 260
}

static void
261
setup_signals (void)
262 263
{
	signal (SIGPIPE, SIG_IGN);
264 265
	g_unix_signal_add (SIGINT, quit_handler, NULL);
	g_unix_signal_add (SIGTERM, quit_handler, NULL);
266 267
}

268
static gboolean
269
do_early_setup (int *argc, char **argv[])
270
{
271 272
	gint64 priority64_v4 = -1;
	gint64 priority64_v6 = -1;
273 274
	GOptionEntry options[] = {
		/* Interface/IP config */
275 276 277
		{ "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" },
278 279 280 281 282 283 284
		{ "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") },
285
		{ "dhcp4-fqdn",     'F', 0, G_OPTION_ARG_STRING, &global_opt.dhcp4_fqdn, N_("FQDN to send to DHCP server"), N_("host.domain.org") },
286 287
		{ "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") },
288
		{ "iid", 'e', 0, G_OPTION_ARG_STRING, &global_opt.iid_str, N_("Hex-encoded Interface Identifier"), "" },
289
		{ "addr-gen-mode", 'e', 0, G_OPTION_ARG_INT, &global_opt.addr_gen_mode, N_("IPv6 SLAAC address generation mode"), "eui64" },
290
		{ "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 },
291 292

		/* Logging/debugging */
293 294 295 296 297
		{ "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,
298 299
		  N_("Log domains separated by ',': any combination of [%s]"),
		  "PLATFORM,RFKILL,WIFI" },
300
		{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
301 302 303 304
		{NULL}
	};

	if (!nm_main_utils_early_setup ("nm-iface-helper",
305 306
	                                argc,
	                                argv,
307 308
	                                options,
	                                NULL,
309
	                                NULL,
310
	                                _("nm-iface-helper is a small, standalone process that manages a single network interface.")))
311
		return FALSE;
312

313 314 315 316
	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;
317
	return TRUE;
318 319
}

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
typedef struct {
	NMPlatform *platform;
	NMNDisc *ndisc;
} DadFailedHandleData;

static gboolean
dad_failed_handle_idle (gpointer user_data)
{
	DadFailedHandleData *data = user_data;
	NMCListElem *elem;

	while ((elem = c_list_first_entry (&gl.dad_failed_lst_head, NMCListElem, lst))) {
		nm_auto_nmpobj const NMPObject *obj = elem->data;

		nm_c_list_elem_free (elem);

		if (nm_ndisc_dad_addr_is_fail_candidate (data->platform, obj)) {
			nm_ndisc_dad_failed (data->ndisc,
338 339
			                     &NMP_OBJECT_CAST_IP6_ADDRESS (obj)->address,
			                     TRUE);
340 341 342 343 344 345 346
		}
	}

	gl.dad_failed_id = 0;
	return G_SOURCE_REMOVE;
}

347 348
static void
ip6_address_changed (NMPlatform *platform,
349
                     int obj_type_i,
350
                     int iface,
351
                     const NMPlatformIP6Address *addr,
352
                     int change_type_i,
Lubomir Rintel's avatar
Lubomir Rintel committed
353
                     NMNDisc *ndisc)
354
{
355
	const NMPlatformSignalChangeType change_type = change_type_i;
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
	DadFailedHandleData *data;

	if (!nm_ndisc_dad_addr_is_fail_candidate_event (change_type, addr))
		return;

	c_list_link_tail (&gl.dad_failed_lst_head,
	                  &nm_c_list_elem_new_stale ((gpointer) nmp_object_ref (NMP_OBJECT_UP_CAST (addr)))->lst);
	if (gl.dad_failed_id)
		return;

	data = g_slice_new (DadFailedHandleData);
	data->platform = platform;
	data->ndisc = ndisc;
	gl.dad_failed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
	                                    dad_failed_handle_idle,
	                                    data,
	                                    nm_g_slice_free_fcn (DadFailedHandleData));
373 374
}

375 376 377 378
int
main (int argc, char *argv[])
{
	char *bad_domains = NULL;
379
	gs_free_error GError *error = NULL;
380 381 382
	gboolean wrote_pidfile = FALSE;
	gs_free char *pidfile = NULL;
	gs_unref_object NMDhcpClient *dhcp4_client = NULL;
Lubomir Rintel's avatar
Lubomir Rintel committed
383
	gs_unref_object NMNDisc *ndisc = NULL;
384
	gs_unref_bytes GBytes *hwaddr = NULL;
385
	gs_unref_bytes GBytes *client_id = NULL;
386
	gs_free NMUtilsIPv6IfaceId *iid = NULL;
387
	guint sd_id;
388
	int errsv;
389

390 391
	c_list_init (&gl.dad_failed_lst_head);

392 393
	setpgid (getpid (), getpid ());

394 395
	if (!do_early_setup (&argc, &argv))
		return 1;
396

397 398 399 400 401
	nm_logging_init_pre ("nm-iface-helper",
	                     g_strdup_printf ("%s[%ld] (%s): ",
	                                      _NMLOG_PREFIX_NAME,
	                                      (long) getpid (),
	                                      global_opt.ifname ?: "???"));
402

403 404 405 406 407 408 409 410
	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);
	}

411
	if (global_opt.show_version) {
412
		fprintf (stdout, NM_DIST_VERSION "\n");
413
		return 0;
414 415
	}

416 417
	nm_main_utils_ensure_root ();

418
	if (!global_opt.ifname || !global_opt.uuid) {
419
		fprintf (stderr, _("An interface name and UUID are required\n"));
420
		return 1;
421 422
	}

423
	gl.ifindex = nmp_utils_if_nametoindex (global_opt.ifname);
424
	if (gl.ifindex <= 0) {
425
		errsv = errno;
426
		fprintf (stderr, _("Failed to find interface index for %s (%s)\n"), global_opt.ifname, nm_strerror_native (errsv));
427
		return 1;
428
	}
429
	pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, gl.ifindex);
430 431
	nm_main_utils_ensure_not_running_pidfile (pidfile);

432 433
	nm_main_utils_ensure_rundir ();

434 435
	if (!nm_logging_setup (global_opt.opt_log_level,
	                       global_opt.opt_log_domains,
436 437 438 439 440
	                       &bad_domains,
	                       &error)) {
		fprintf (stderr,
		         _("%s.  Please use --help to see a list of valid options.\n"),
		         error->message);
441
		return 1;
442 443 444 445 446 447 448
	} 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);
	}

449
	if (global_opt.become_daemon && !global_opt.debug) {
450
		if (daemon (0, 0) < 0) {
451
			errsv = errno;
452
			fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
453
			         nm_strerror_native (errsv),
454
			         errsv);
455
			return 1;
456 457 458 459 460 461
		}
		if (nm_main_utils_write_pidfile (pidfile))
			wrote_pidfile = TRUE;
	}

	/* Set up unix signal handling - before creating threads, but after daemonizing! */
462
	gl.main_loop = g_main_loop_new (NULL, FALSE);
463
	setup_signals ();
464

465 466
	nm_logging_init (global_opt.logging_backend,
	                 global_opt.debug);
467

468
	_LOGI (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
469 470 471 472

	/* Set up platform interaction layer */
	nm_linux_platform_setup ();

473
	hwaddr = nm_platform_link_get_address_as_bytes (NM_PLATFORM_GET, gl.ifindex);
474

475
	if (global_opt.iid_str) {
476 477 478
		GBytes *bytes;
		gsize ignored = 0;

479
		bytes = nm_utils_hexstr2bin (global_opt.iid_str);
480
		if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) {
481
			fprintf (stderr, _("(%s): Invalid IID %s\n"), global_opt.ifname, global_opt.iid_str);
482
			return 1;
483 484 485 486
		}
		iid = g_bytes_unref_to_data (bytes, &ignored);
	}

487 488 489 490 491 492 493 494 495 496
	if (global_opt.dhcp4_clientid) {
		/* this string is just a plain hex-string. Unlike ipv4.dhcp-client-id, which
		 * is parsed via nm_dhcp_utils_client_id_string_to_bytes(). */
		client_id = nm_utils_hexstr2bin (global_opt.dhcp4_clientid);
		if (!client_id || g_bytes_get_size (client_id) < 2) {
			fprintf (stderr, _("(%s): Invalid DHCP client-id %s\n"), global_opt.ifname, global_opt.dhcp4_clientid);
			return 1;
		}
	}

497
	if (global_opt.dhcp4_address) {
498 499 500 501 502
		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET,
		                                AF_INET,
		                                global_opt.ifname,
		                                "promote_secondaries",
		                                "1");
503

504
		dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
505
		                                          nm_platform_get_multi_idx (NM_PLATFORM_GET),
506
		                                          global_opt.ifname,
507
		                                          gl.ifindex,
508
		                                          hwaddr,
509
		                                          global_opt.uuid,
510
		                                          RT_TABLE_MAIN,
511
		                                          global_opt.priority_v4,
512 513
		                                          !!global_opt.dhcp4_hostname,
		                                          global_opt.dhcp4_hostname,
514
		                                          global_opt.dhcp4_fqdn,
515
		                                          client_id,
516
		                                          NM_DHCP_TIMEOUT_DEFAULT,
517
		                                          NULL,
518 519 520 521 522
		                                          global_opt.dhcp4_address,
		                                          &error);
		if (!dhcp4_client)
			g_error ("failure to start DHCP: %s", error->message);

523 524 525 526 527 528
		g_signal_connect (dhcp4_client,
		                  NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
		                  G_CALLBACK (dhcp4_state_changed),
		                  NULL);
	}

529
	if (global_opt.slaac) {
530 531 532
		NMUtilsStableType stable_type = NM_UTILS_STABLE_TYPE_UUID;
		const char *stable_id = global_opt.uuid;

533
		nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, gl.ifindex, TRUE);
534

535 536 537 538 539 540 541 542 543
		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];
		}
Lubomir Rintel's avatar
Lubomir Rintel committed
544
		ndisc = nm_lndp_ndisc_new (NM_PLATFORM_GET, gl.ifindex, global_opt.ifname,
545
		                           stable_type, stable_id,
546 547 548
		                           global_opt.addr_gen_mode,
		                           NM_NDISC_NODE_TYPE_HOST,
		                           NULL);
Lubomir Rintel's avatar
Lubomir Rintel committed
549
		g_assert (ndisc);
550 551

		if (iid)
Lubomir Rintel's avatar
Lubomir Rintel committed
552
			nm_ndisc_set_iid (ndisc, *iid);
553

554 555 556 557
		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra",          "1");
		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_defrtr",   "0");
		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_pinfo",    "0");
		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_rtr_pref", "0");
558

559 560 561
		g_signal_connect (NM_PLATFORM_GET,
		                  NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
		                  G_CALLBACK (ip6_address_changed),
Lubomir Rintel's avatar
Lubomir Rintel committed
562 563
		                  ndisc);
		g_signal_connect (ndisc,
564
		                  NM_NDISC_CONFIG_RECEIVED,
Lubomir Rintel's avatar
Lubomir Rintel committed
565
		                  G_CALLBACK (ndisc_config_changed),
566
		                  NULL);
Lubomir Rintel's avatar
Lubomir Rintel committed
567 568 569
		g_signal_connect (ndisc,
		                  NM_NDISC_RA_TIMEOUT,
		                  G_CALLBACK (ndisc_ra_timeout),
570
		                  NULL);
Lubomir Rintel's avatar
Lubomir Rintel committed
571
		nm_ndisc_start (ndisc);
572 573
	}

574 575
	sd_id = nm_sd_event_attach_default ();

576
	g_main_loop_run (gl.main_loop);
577

578 579 580
	nm_clear_g_source (&gl.dad_failed_id);
	nm_c_list_elem_free_all (&gl.dad_failed_lst_head, (GDestroyNotify) nmp_object_unref);

581 582 583
	if (pidfile && wrote_pidfile)
		unlink (pidfile);

584
	_LOGI (LOGD_CORE, "exiting");
585 586

	nm_clear_g_source (&sd_id);
587
	g_clear_pointer (&gl.main_loop, g_main_loop_unref);
588
	return 0;
589 590
}

591 592
/*****************************************************************************/

593
const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = {
594 595 596
	&_nm_dhcp_client_factory_internal,
};

597
/*****************************************************************************/
598 599
/* Stub functions */

600 601 602
#include "nm-config.h"
#include "devices/nm-device.h"
#include "nm-active-connection.h"
603
#include "nm-dbus-manager.h"
604

605
void
606
nm_main_config_reload (int signal)
607
{
608
	_LOGI (LOGD_CORE, "reloading configuration not supported");
609 610
}

611
NMConfig *
612 613 614 615 616
nm_config_get (void)
{
	return GUINT_TO_POINTER (1);
}

617 618 619 620 621 622 623 624
NMConfigData *
nm_config_get_data_orig (NMConfig *config)
{
	return GUINT_TO_POINTER (1);
}

char *
nm_config_data_get_value (const NMConfigData *config_data, const char *group, const char *key, NMConfigGetValueFlags flags)
625
{
626
	return NULL;
627 628
}

629
NMConfigConfigureAndQuitType
630
nm_config_get_configure_and_quit (NMConfig *config)
631
{
632
	return NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED;
633 634
}

635 636 637 638 639 640
NMDBusManager *
nm_dbus_manager_get (void)
{
	return NULL;
}

641 642 643 644 645 646
gboolean
nm_dbus_manager_is_stopping (NMDBusManager *self)
{
	return FALSE;
}

647 648 649 650 651 652 653
void
_nm_dbus_manager_obj_export (NMDBusObject *obj)
{
}

void
_nm_dbus_manager_obj_unexport (NMDBusObject *obj)
654 655 656 657
{
}

void
658 659 660
_nm_dbus_manager_obj_notify (NMDBusObject *obj,
                             guint n_pspecs,
                             const GParamSpec *const*pspecs)
661 662 663 664
{
}

void
665 666 667 668
_nm_dbus_manager_obj_emit_signal (NMDBusObject *obj,
                                  const NMDBusInterfaceInfoExtended *interface_info,
                                  const GDBusSignalInfo *signal_info,
                                  GVariant *args)
669 670 671
{
}

672 673 674 675 676 677 678 679 680 681 682 683
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);
}