nm-dhcp-client.c 30.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* 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, 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) 2005 - 2010 Red Hat, Inc.
 *
 */

20
#include "nm-default.h"
21

22 23
#include "nm-dhcp-client.h"

24 25 26 27 28
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
29
#include <linux/rtnetlink.h>
30

31
#include "nm-utils/nm-dedup-multi.h"
32
#include "nm-utils/nm-random-utils.h"
33

34
#include "NetworkManagerUtils.h"
35
#include "nm-utils.h"
36
#include "nm-dhcp-utils.h"
37
#include "platform/nm-platform.h"
38

39 40
#include "nm-dhcp-client-logging.h"

41 42 43 44
/*****************************************************************************/

enum {
	SIGNAL_STATE_CHANGED,
45
	SIGNAL_PREFIX_DELEGATED,
46 47 48 49 50
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

51
NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient,
52
	PROP_ADDR_FAMILY,
53 54
	PROP_FLAGS,
	PROP_HWADDR,
55 56
	PROP_IFACE,
	PROP_IFINDEX,
57
	PROP_MULTI_IDX,
58
	PROP_ROUTE_METRIC,
59
	PROP_ROUTE_TABLE,
60
	PROP_TIMEOUT,
61
	PROP_UUID,
62
	PROP_HOSTNAME,
63
);
64 65

typedef struct _NMDhcpClientPrivate {
66
	NMDedupMultiIndex *multi_idx;
67
	char *       iface;
68
	GBytes *     hwaddr;
69
	char *       uuid;
70
	GBytes *     client_id;
71
	char *       hostname;
72
	pid_t        pid;
73 74
	guint        timeout_id;
	guint        watch_id;
75 76
	int          addr_family;
	int          ifindex;
77
	guint32      route_table;
78
	guint32      route_metric;
79 80 81 82
	guint32      timeout;
	NMDhcpState  state;
	bool         info_only:1;
	bool         use_fqdn:1;
83
} NMDhcpClientPrivate;
84

85
G_DEFINE_TYPE_EXTENDED (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
86

87
#define NM_DHCP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMDhcpClient, NM_IS_DHCP_CLIENT)
88

89
/*****************************************************************************/
90

91
pid_t
92
nm_dhcp_client_get_pid (NMDhcpClient *self)
93 94 95 96 97 98
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid;
}

99 100 101 102 103 104 105 106
NMDedupMultiIndex *
nm_dhcp_client_get_multi_idx (NMDhcpClient *self)
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->multi_idx;
}

107
const char *
108
nm_dhcp_client_get_iface (NMDhcpClient *self)
109 110 111 112 113 114
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface;
}

115
int
116
nm_dhcp_client_get_ifindex (NMDhcpClient *self)
117 118 119 120 121 122
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->ifindex;
}

123 124
int
nm_dhcp_client_get_addr_family (NMDhcpClient *self)
125
{
126
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), AF_UNSPEC);
127

128
	return NM_DHCP_CLIENT_GET_PRIVATE (self)->addr_family;
129 130 131
}

const char *
132
nm_dhcp_client_get_uuid (NMDhcpClient *self)
133 134 135 136 137 138
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid;
}

139
GBytes *
140
nm_dhcp_client_get_hw_addr (NMDhcpClient *self)
141 142 143 144 145 146
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr;
}

147 148 149 150 151 152 153 154
guint32
nm_dhcp_client_get_route_table (NMDhcpClient *self)
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), RT_TABLE_MAIN);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->route_table;
}

155 156 157 158 159 160 161 162 163 164 165
void
nm_dhcp_client_set_route_table (NMDhcpClient *self, guint32 route_table)
{
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	if (route_table != priv->route_table) {
		priv->route_table = route_table;
		_notify (self, PROP_ROUTE_TABLE);
	}
}

166
guint32
167
nm_dhcp_client_get_route_metric (NMDhcpClient *self)
168 169 170
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), G_MAXUINT32);

171
	return NM_DHCP_CLIENT_GET_PRIVATE (self)->route_metric;
172 173
}

174 175 176 177 178 179 180 181 182 183 184
void
nm_dhcp_client_set_route_metric (NMDhcpClient *self, guint32 route_metric)
{
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	if (route_metric != priv->route_metric) {
		priv->route_metric = route_metric;
		_notify (self, PROP_ROUTE_METRIC);
	}
}

185 186 187 188 189 190 191 192
guint32
nm_dhcp_client_get_timeout (NMDhcpClient *self)
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), 0);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->timeout;
}

193 194 195 196 197 198 199 200
GBytes *
nm_dhcp_client_get_client_id (NMDhcpClient *self)
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->client_id;
}

201 202 203 204 205 206 207 208 209
static void
_set_client_id (NMDhcpClient *self, GBytes *client_id, gboolean take)
{
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	nm_assert (!client_id || g_bytes_get_size (client_id) >= 2);

	if (   priv->client_id == client_id
	    || (   priv->client_id
210
	        && client_id
211 212 213 214 215 216 217 218 219 220 221
	        && g_bytes_equal (priv->client_id, client_id))) {
		if (take && client_id)
			g_bytes_unref (client_id);
		return;
	}

	if (priv->client_id)
		g_bytes_unref (priv->client_id);
	priv->client_id = client_id;
	if (!take && client_id)
		g_bytes_ref (client_id);
222 223 224 225

	{
		gs_free char *s = NULL;

226 227 228 229
		_LOGT ("%s: set %s",
		       nm_dhcp_client_get_addr_family (self) == AF_INET6
		         ? "duid"
		         : "client-id",
230 231 232 233
		       priv->client_id
		         ? (s = nm_dhcp_utils_duid_to_string (priv->client_id))
		         : "default");
	}
234 235
}

236 237 238
void
nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id)
{
239 240 241 242 243 244 245 246 247 248 249 250 251 252
	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
	g_return_if_fail (!client_id || g_bytes_get_size (client_id) >= 2);

	_set_client_id (self, client_id, FALSE);
}

void
nm_dhcp_client_set_client_id_bin (NMDhcpClient *self,
                                  guint8 type,
                                  const guint8 *client_id,
                                  gsize len)
{
	guint8 *buf;
	GBytes *b;
253 254

	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
255 256 257 258 259 260 261 262 263
	g_return_if_fail (client_id);
	g_return_if_fail (len > 0);

	buf = g_malloc (len + 1);
	buf[0] = type;
	memcpy (buf + 1, client_id, len);
	b = g_bytes_new_take (buf, len + 1);
	_set_client_id (self, b, TRUE);
}
264

265 266 267 268 269 270 271 272
const char *
nm_dhcp_client_get_hostname (NMDhcpClient *self)
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
}

273 274 275 276 277 278 279 280
gboolean
nm_dhcp_client_get_info_only (NMDhcpClient *self)
{
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);

	return NM_DHCP_CLIENT_GET_PRIVATE (self)->info_only;
}

281 282
gboolean
nm_dhcp_client_get_use_fqdn (NMDhcpClient *self)
283
{
284
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
285

286
	return NM_DHCP_CLIENT_GET_PRIVATE (self)->use_fqdn;
287 288
}

289
/*****************************************************************************/
290

291
static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
292 293 294 295 296 297 298
	[NM_DHCP_STATE_UNKNOWN]    = "unknown",
	[NM_DHCP_STATE_BOUND]      = "bound",
	[NM_DHCP_STATE_TIMEOUT]    = "timeout",
	[NM_DHCP_STATE_EXPIRE]     = "expire",
	[NM_DHCP_STATE_DONE]       = "done",
	[NM_DHCP_STATE_FAIL]       = "fail",
	[NM_DHCP_STATE_TERMINATED] = "terminated",
299 300 301 302 303
};

static const char *
state_to_string (NMDhcpState state)
{
304
	if ((gsize) state < G_N_ELEMENTS (state_table))
305 306 307 308 309
		return state_table[state];
	return NULL;
}

static NMDhcpState
310
reason_to_state (NMDhcpClient *self, const char *iface, const char *reason)
311 312 313 314 315 316 317 318 319 320 321
{
	if (g_ascii_strcasecmp (reason, "bound") == 0 ||
	    g_ascii_strcasecmp (reason, "bound6") == 0 ||
	    g_ascii_strcasecmp (reason, "renew") == 0 ||
	    g_ascii_strcasecmp (reason, "renew6") == 0 ||
	    g_ascii_strcasecmp (reason, "reboot") == 0 ||
	    g_ascii_strcasecmp (reason, "rebind") == 0 ||
	    g_ascii_strcasecmp (reason, "rebind6") == 0)
		return NM_DHCP_STATE_BOUND;
	else if (g_ascii_strcasecmp (reason, "timeout") == 0)
		return NM_DHCP_STATE_TIMEOUT;
322 323 324 325
	else if (g_ascii_strcasecmp (reason, "nak") == 0 ||
	         g_ascii_strcasecmp (reason, "expire") == 0 ||
	         g_ascii_strcasecmp (reason, "expire6") == 0)
		return NM_DHCP_STATE_EXPIRE;
326 327 328
	else if (g_ascii_strcasecmp (reason, "end") == 0)
		return NM_DHCP_STATE_DONE;
	else if (g_ascii_strcasecmp (reason, "fail") == 0 ||
329
	         g_ascii_strcasecmp (reason, "abend") == 0)
330 331
		return NM_DHCP_STATE_FAIL;

332
	_LOGD ("unmapped DHCP state '%s'", reason);
333 334 335
	return NM_DHCP_STATE_UNKNOWN;
}

336
/*****************************************************************************/
337

338
static void
339
timeout_cleanup (NMDhcpClient *self)
340
{
341
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
342

343
	nm_clear_g_source (&priv->timeout_id);
344 345 346
}

static void
347
watch_cleanup (NMDhcpClient *self)
348
{
349
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
350

351
	nm_clear_g_source (&priv->watch_id);
352 353
}

354
void
355
nm_dhcp_client_stop_pid (pid_t pid, const char *iface)
356
{
357
	char *name = iface ? g_strdup_printf ("dhcp-client-%s", iface) : NULL;
358

359
	g_return_if_fail (pid > 1);
360

361
	nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, name ?: "dhcp-client", NULL,
362 363
	                          1000 / 2, 1000 / 20);
	g_free (name);
364 365 366
}

static void
367
stop (NMDhcpClient *self, gboolean release)
368
{
369
	NMDhcpClientPrivate *priv;
370 371 372 373 374

	g_return_if_fail (NM_IS_DHCP_CLIENT (self));

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

375 376 377 378 379
	if (priv->pid > 0) {
		/* Clean up the watch handler since we're explicitly killing the daemon */
		watch_cleanup (self);
		nm_dhcp_client_stop_pid (priv->pid, priv->iface);
	}
380
	priv->pid = -1;
381 382
}

383
void
384
nm_dhcp_client_set_state (NMDhcpClient *self,
385
                          NMDhcpState new_state,
386
                          NMIPConfig *ip_config,
387
                          GHashTable *options)
388
{
389
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
390
	gs_free char *event_id = NULL;
391

392 393 394 395 396 397 398 399
	if (new_state == NM_DHCP_STATE_BOUND) {
		g_return_if_fail (NM_IS_IP_CONFIG (ip_config, priv->addr_family));
		g_return_if_fail (options);
	} else {
		g_return_if_fail (!ip_config);
		g_return_if_fail (!options);
	}

400
	if (new_state >= NM_DHCP_STATE_BOUND)
401
		timeout_cleanup (self);
402 403
	if (new_state >= NM_DHCP_STATE_TIMEOUT)
		watch_cleanup (self);
404

405 406 407 408 409 410 411 412
	/* The client may send same-state transitions for RENEW/REBIND events and
	 * the lease may have changed, so handle same-state transitions for the
	 * BOUND state.  Ignore same-state transitions for other events since
	 * the lease won't have changed and the state was already handled.
	 */
	if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
		return;

413 414
	if (   priv->addr_family == AF_INET6
	    && new_state == NM_DHCP_STATE_BOUND) {
415 416 417 418 419 420 421 422
		char *start, *iaid;

		iaid = g_hash_table_lookup (options, "iaid");
		start = g_hash_table_lookup (options, "life_starts");
		if (iaid && start)
			event_id = g_strdup_printf ("%s|%s", iaid, start);
	}

423 424 425 426
	_LOGI ("state changed %s -> %s%s%s%s",
	       state_to_string (priv->state),
	       state_to_string (new_state),
	       NM_PRINT_FMT_QUOTED (event_id, ", event ID=\"", event_id, "\"", ""));
427 428

	priv->state = new_state;
429 430
	g_signal_emit (G_OBJECT (self),
	               signals[SIGNAL_STATE_CHANGED], 0,
431
	               new_state,
432
	               ip_config,
433 434
	               options,
	               event_id);
435 436
}

437
static gboolean
438
transaction_timeout (gpointer user_data)
439
{
440 441
	NMDhcpClient *self = NM_DHCP_CLIENT (user_data);
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
442 443

	priv->timeout_id = 0;
444
	_LOGW ("request timed out");
445
	nm_dhcp_client_set_state (self, NM_DHCP_STATE_TIMEOUT, NULL, NULL);
446 447 448
	return G_SOURCE_REMOVE;
}

449
static void
450
daemon_watch_cb (GPid pid, int status, gpointer user_data)
451
{
452 453
	NMDhcpClient *self = NM_DHCP_CLIENT (user_data);
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
454

455 456 457
	g_return_if_fail (priv->watch_id);
	priv->watch_id = 0;

458
	if (WIFEXITED (status))
459
		_LOGI ("client pid %d exited with status %d", pid, WEXITSTATUS (status));
460
	else if (WIFSIGNALED (status))
461
		_LOGI ("client pid %d killed by signal %d", pid, WTERMSIG (status));
462
	else if (WIFSTOPPED(status))
463
		_LOGI ("client pid %d stopped by signal %d", pid, WSTOPSIG (status));
464
	else if (WIFCONTINUED (status))
465
		_LOGI ("client pid %d resumed (by SIGCONT)", pid);
466
	else
467
		_LOGW ("client died abnormally");
468

469
	priv->pid = -1;
470

471
	nm_dhcp_client_set_state (self, NM_DHCP_STATE_TERMINATED, NULL, NULL);
472 473
}

474
void
475
nm_dhcp_client_start_timeout (NMDhcpClient *self)
476
{
477
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
478 479

	/* Set up a timeout on the transaction to kill it after the timeout */
480
	g_assert (priv->timeout_id == 0);
481 482 483 484

	if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
		return;

485
	priv->timeout_id = g_timeout_add_seconds (priv->timeout,
486
	                                          transaction_timeout,
487
	                                          self);
488 489 490 491 492 493 494 495 496 497 498 499
}

void
nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid)
{
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	g_return_if_fail (priv->pid == -1);
	priv->pid = pid;

	nm_dhcp_client_start_timeout (self);

500
	g_return_if_fail (priv->watch_id == 0);
501
	priv->watch_id = g_child_watch_add (pid, daemon_watch_cb, self);
502 503
}

504
gboolean
505
nm_dhcp_client_start_ip4 (NMDhcpClient *self,
506
                          GBytes *client_id,
507
                          const char *dhcp_anycast_addr,
508 509
                          const char *last_ip4_address,
                          GError **error)
510
{
511
	NMDhcpClientPrivate *priv;
512 513 514 515 516

	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
	g_return_val_if_fail (priv->pid == -1, FALSE);
517
	g_return_val_if_fail (priv->addr_family == AF_INET, FALSE);
518
	g_return_val_if_fail (priv->uuid != NULL, FALSE);
519

520 521 522 523
	if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
		_LOGI ("activation: beginning transaction (no timeout)");
	else
		_LOGI ("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout);
524

525
	nm_dhcp_client_set_client_id (self, client_id);
526

527 528 529 530
	return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self,
	                                                   dhcp_anycast_addr,
	                                                   last_ip4_address,
	                                                   error);
531
}
532

533
static GBytes *
534
get_duid (NMDhcpClient *self)
535
{
536
	return NULL;
537 538
}

539
gboolean
540
nm_dhcp_client_start_ip6 (NMDhcpClient *self,
541
                          GBytes *client_id,
542
                          gboolean enforce_duid,
543
                          const char *dhcp_anycast_addr,
544
                          const struct in6_addr *ll_addr,
545
                          NMSettingIP6ConfigPrivacy privacy,
546 547
                          guint needed_prefixes,
                          GError **error)
548
{
549
	NMDhcpClientPrivate *priv;
550
	gs_unref_bytes GBytes *own_client_id = NULL;
551 552

	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
553
	g_return_val_if_fail (client_id, FALSE);
554 555

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
556

557
	g_return_val_if_fail (priv->pid == -1, FALSE);
558
	g_return_val_if_fail (priv->addr_family == AF_INET6, FALSE);
559
	g_return_val_if_fail (priv->uuid != NULL, FALSE);
560
	g_return_val_if_fail (!priv->client_id, FALSE);
561

562
	if (!enforce_duid)
563
		own_client_id = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
564

565 566 567
	_set_client_id (self,
	                own_client_id ?: client_id,
	                FALSE);
568

569 570 571 572
	if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
		_LOGI ("activation: beginning transaction (no timeout)");
	else
		_LOGI ("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout);
573

574 575
	return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
	                                                   dhcp_anycast_addr,
576
	                                                   ll_addr,
577
	                                                   privacy,
578 579
	                                                   needed_prefixes,
	                                                   error);
580 581 582 583 584
}

void
nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
{
585 586 587 588 589
	guint64 start_time;
	pid_t pid, ppid;
	const char *exe;
	char proc_path[NM_STRLEN ("/proc/%lu/cmdline") + 100];
	gs_free char *pid_contents = NULL, *proc_contents = NULL;
590 591 592 593 594

	/* Check for an existing instance and stop it */
	if (!g_file_get_contents (pid_file, &pid_contents, NULL, NULL))
		return;

595 596 597 598 599 600 601 602
	pid = _nm_utils_ascii_str_to_int64 (pid_contents, 10, 1, G_MAXINT64, 0);
	if (pid <= 0)
		goto out;

	start_time = nm_utils_get_start_time_for_pid (pid, NULL, &ppid);
	if (start_time == 0)
		goto out;

603
	nm_sprintf_buf (proc_path, "/proc/%lu/cmdline", (unsigned long) pid);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
	if (!g_file_get_contents (proc_path, &proc_contents, NULL, NULL))
		goto out;

	exe = strrchr (proc_contents, '/');
	if (exe)
		exe++;
	else
		exe = proc_contents;
	if (!nm_streq0 (exe, binary_name))
		goto out;

	if (ppid == getpid ()) {
		/* the process is our own child. */
		nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, "dhcp-client", NULL, 1000 / 2, 1000 / 20);
	} else {
		nm_utils_kill_process_sync (pid, start_time, SIGTERM, LOGD_DHCP,
		                            "dhcp-client", 1000 / 2, 1000 / 20, 2000);
621 622
	}

623
out:
624
	if (remove (pid_file) == -1) {
625 626 627
		int errsv = errno;

		nm_log_dbg (LOGD_DHCP, "dhcp: could not remove pid file \"%s\": %s (%d)",
628
		            pid_file, nm_strerror_native (errsv), errsv);
629
	}
630 631 632
}

void
633
nm_dhcp_client_stop (NMDhcpClient *self, gboolean release)
634
{
635
	NMDhcpClientPrivate *priv;
636
	pid_t old_pid = 0;
637 638 639 640 641 642

	g_return_if_fail (NM_IS_DHCP_CLIENT (self));

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);

	/* Kill the DHCP client */
643
	old_pid = priv->pid;
644
	NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release);
645 646 647 648
	if (old_pid > 0)
		_LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid);
	else
		_LOGI ("canceled DHCP transaction");
649
	g_assert (priv->pid == -1);
650

651
	nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL);
652 653
}

654
/*****************************************************************************/
655 656

static char *
657
bytearray_variant_to_string (NMDhcpClient *self, GVariant *value, const char *key)
658
{
Dan Winship's avatar
Dan Winship committed
659 660
	const guint8 *array;
	gsize length;
661 662 663 664 665
	GString *str;
	int i;
	unsigned char c;
	char *converted = NULL;

Dan Winship's avatar
Dan Winship committed
666 667 668
	g_return_val_if_fail (value != NULL, NULL);

	array = g_variant_get_fixed_array (value, &length, 1);
669 670 671 672

	/* Since the DHCP options come through environment variables, they should
	 * already be UTF-8 safe, but just make sure.
	 */
Dan Winship's avatar
Dan Winship committed
673 674 675
	str = g_string_sized_new (length);
	for (i = 0; i < length; i++) {
		c = array[i];
676 677 678 679 680 681 682 683 684 685 686 687

		/* Convert NULLs to spaces and non-ASCII characters to ? */
		if (c == '\0')
			c = ' ';
		else if (c > 127)
			c = '?';
		str = g_string_append_c (str, c);
	}
	str = g_string_append_c (str, '\0');

	converted = str->str;
	if (!g_utf8_validate (converted, -1, NULL))
688
		_LOGW ("option '%s' couldn't be converted to UTF-8", key);
689 690 691 692
	g_string_free (str, FALSE);
	return converted;
}

693 694 695
#define OLD_TAG "old_"
#define NEW_TAG "new_"

696
static void
697 698
maybe_add_option (NMDhcpClient *self,
                  GHashTable *hash,
Dan Winship's avatar
Dan Winship committed
699 700
                  const char *key,
                  GVariant *value)
701 702
{
	char *str_value = NULL;
703

Dan Winship's avatar
Dan Winship committed
704
	g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_BYTESTRING));
705

706
	if (g_str_has_prefix (key, OLD_TAG))
707
		return;
708 709

	/* Filter out stuff that's not actually new DHCP options */
710 711 712 713 714
	if (NM_IN_STRSET (key, "interface",
	                       "pid",
	                       "reason",
	                       "dhcp_message_type"))
		return;
715

716
	if (g_str_has_prefix (key, NEW_TAG))
717
		key += NM_STRLEN (NEW_TAG);
718 719 720
	if (!key[0])
		return;

721
	str_value = bytearray_variant_to_string (self, value, key);
722
	if (str_value)
723
		g_hash_table_insert (hash, g_strdup (key), str_value);
724 725
}

726 727 728
gboolean
nm_dhcp_client_handle_event (gpointer unused,
                             const char *iface,
729
                             int pid,
Dan Winship's avatar
Dan Winship committed
730
                             GVariant *options,
731 732
                             const char *reason,
                             NMDhcpClient *self)
733
{
734
	NMDhcpClientPrivate *priv;
735 736
	guint32 old_state;
	guint32 new_state;
737 738
	gs_unref_hashtable GHashTable *str_options = NULL;
	gs_unref_object NMIPConfig *ip_config = NULL;
739
	NMPlatformIP6Address prefix = { 0, };
740

741 742 743
	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
	g_return_val_if_fail (iface != NULL, FALSE);
	g_return_val_if_fail (pid > 0, FALSE);
Dan Winship's avatar
Dan Winship committed
744
	g_return_val_if_fail (g_variant_is_of_type (options, G_VARIANT_TYPE_VARDICT), FALSE);
745
	g_return_val_if_fail (reason != NULL, FALSE);
746 747

	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
748 749 750 751 752 753

	if (g_strcmp0 (priv->iface, iface) != 0)
		return FALSE;
	if (priv->pid != pid)
		return FALSE;

754
	old_state = priv->state;
755
	new_state = reason_to_state (self, priv->iface, reason);
756 757
	_LOGD ("DHCP state '%s' -> '%s' (reason: '%s')",
	       state_to_string (old_state), state_to_string (new_state), reason);
758

759
	if (new_state == NM_DHCP_STATE_BOUND) {
Dan Winship's avatar
Dan Winship committed
760 761 762 763
		GVariantIter iter;
		const char *name;
		GVariant *value;

764
		/* Copy options */
765
		str_options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
Dan Winship's avatar
Dan Winship committed
766 767
		g_variant_iter_init (&iter, options);
		while (g_variant_iter_next (&iter, "{&sv}", &name, &value)) {
768
			maybe_add_option (self, str_options, name, value);
Dan Winship's avatar
Dan Winship committed
769 770
			g_variant_unref (value);
		}
771

772 773 774 775 776 777 778 779 780
		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
			GHashTableIter hash_iter;
			gpointer key, val;

			g_hash_table_iter_init (&hash_iter, str_options);
			while (g_hash_table_iter_next (&hash_iter, &key, &val))
				_LOGD ("option '%s'=>'%s'", (const char *) key, (const char *) val);
		}

781
		/* Create the IP config */
782
		if (g_hash_table_size (str_options) > 0) {
783
			if (priv->addr_family == AF_INET) {
784 785 786 787 788 789
				ip_config = NM_IP_CONFIG_CAST (nm_dhcp_utils_ip4_config_from_options (nm_dhcp_client_get_multi_idx (self),
				                                                                      priv->ifindex,
				                                                                      priv->iface,
				                                                                      str_options,
				                                                                      priv->route_table,
				                                                                      priv->route_metric));
790
			} else {
791
				prefix = nm_dhcp_utils_ip6_prefix_from_options (str_options);
792 793 794 795 796
				ip_config = NM_IP_CONFIG_CAST (nm_dhcp_utils_ip6_config_from_options (nm_dhcp_client_get_multi_idx (self),
				                                                                      priv->ifindex,
				                                                                      priv->iface,
				                                                                      str_options,
				                                                                      priv->info_only));
797
			}
798 799
		} else
			g_warn_if_reached ();
800 801
	}

802 803 804 805 806 807 808 809 810
	if (!IN6_IS_ADDR_UNSPECIFIED (&prefix.address)) {
		/* If we got an IPv6 prefix to delegate, we don't change the state
		 * of the DHCP client instance. Instead, we just signal the prefix
		 * to the device. */
		g_signal_emit (G_OBJECT (self),
		               signals[SIGNAL_PREFIX_DELEGATED], 0,
		               &prefix);
	} else {
		/* Fail if no valid IP config was received */
811 812
		if (   new_state == NM_DHCP_STATE_BOUND
		    && !ip_config) {
813 814 815 816 817
			_LOGW ("client bound but IP config not received");
			new_state = NM_DHCP_STATE_FAIL;
			g_clear_pointer (&str_options, g_hash_table_unref);
		}

818
		nm_dhcp_client_set_state (self, new_state, ip_config, str_options);
819
	}
820

821
	return TRUE;
822 823
}

824
/*****************************************************************************/
825 826 827

static void
get_property (GObject *object, guint prop_id,
828
              GValue *value, GParamSpec *pspec)
829
{
830
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE ((NMDhcpClient *) object);
831 832 833 834 835

	switch (prop_id) {
	case PROP_IFACE:
		g_value_set_string (value, priv->iface);
		break;
836 837 838
	case PROP_IFINDEX:
		g_value_set_int (value, priv->ifindex);
		break;
839 840 841
	case PROP_HWADDR:
		g_value_set_boxed (value, priv->hwaddr);
		break;
842 843
	case PROP_ADDR_FAMILY:
		g_value_set_int (value, priv->addr_family);
844 845 846 847
		break;
	case PROP_UUID:
		g_value_set_string (value, priv->uuid);
		break;
848 849 850
	case PROP_HOSTNAME:
		g_value_set_string (value, priv->hostname);
		break;
851 852
	case PROP_ROUTE_METRIC:
		g_value_set_uint (value, priv->route_metric);
853
		break;
854 855 856
	case PROP_ROUTE_TABLE:
		g_value_set_uint (value, priv->route_table);
		break;
857 858 859
	case PROP_TIMEOUT:
		g_value_set_uint (value, priv->timeout);
		break;
860 861 862 863 864 865 866 867
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
set_property (GObject *object, guint prop_id,
868
              const GValue *value, GParamSpec *pspec)
869
{
870
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE ((NMDhcpClient *) object);
871
	guint flags;
872

873
	switch (prop_id) {
874 875 876 877 878 879 880
	case PROP_FLAGS:
		/* construct-only */
		flags = g_value_get_uint (value);
		nm_assert ((flags & ~((guint) (NM_DHCP_CLIENT_FLAGS_INFO_ONLY | NM_DHCP_CLIENT_FLAGS_USE_FQDN))) == 0);
		priv->info_only = NM_FLAGS_HAS (flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY);
		priv->use_fqdn = NM_FLAGS_HAS (flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN);
		break;
881 882 883 884 885 886 887
	case PROP_MULTI_IDX:
		/* construct-only */
		priv->multi_idx = g_value_get_pointer (value);
		if (!priv->multi_idx)
			g_return_if_reached ();
		nm_dedup_multi_index_ref (priv->multi_idx);
		break;
888 889
	case PROP_IFACE:
		/* construct-only */
890
		priv->iface = g_value_dup_string (value);
891 892
		g_return_if_fail (   priv->iface
		                  && nm_utils_is_valid_iface_name (priv->iface, NULL));
893
		break;
894 895 896
	case PROP_IFINDEX:
		/* construct-only */
		priv->ifindex = g_value_get_int (value);
897
		g_return_if_fail (priv->ifindex > 0);
898
		break;
899
	case PROP_HWADDR:
900
		/* construct-only */
901 902
		priv->hwaddr = g_value_dup_boxed (value);
		break;
903
	case PROP_ADDR_FAMILY:
904
		/* construct-only */
905 906 907
		priv->addr_family = g_value_get_int (value);
		if (!NM_IN_SET (priv->addr_family, AF_INET, AF_INET6))
			g_return_if_reached ();
908 909 910 911 912
		break;
	case PROP_UUID:
		/* construct-only */
		priv->uuid = g_value_dup_string (value);
		break;
913 914 915 916
	case PROP_HOSTNAME:
		/* construct-only */
		priv->hostname = g_value_dup_string (value);
		break;
917 918 919
	case PROP_ROUTE_TABLE:
		priv->route_table = g_value_get_uint (value);
		break;
920 921
	case PROP_ROUTE_METRIC:
		priv->route_metric = g_value_get_uint (value);
922
		break;
923
	case PROP_TIMEOUT:
924
		/* construct-only */
925 926
		priv->timeout = g_value_get_uint (value);
		break;
927 928 929 930 931 932
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

933 934 935 936 937 938 939 940 941 942
/*****************************************************************************/

static void
nm_dhcp_client_init (NMDhcpClient *self)
{
	NMDhcpClientPrivate *priv;

	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate);
	self->_priv = priv;

943 944
	c_list_init (&self->dhcp_client_lst);

945 946 947
	priv->pid = -1;
}

948 949 950
static void
dispose (GObject *object)
{
951 952
	NMDhcpClient *self = NM_DHCP_CLIENT (object);
	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
953 954 955 956 957 958

	/* Stopping the client is left up to the controlling device
	 * explicitly since we may want to quit NetworkManager but not terminate
	 * the DHCP client.
	 */

959 960
	nm_assert (c_list_is_empty (&self->dhcp_client_lst));

961 962
	watch_cleanup (self);
	timeout_cleanup (self);
963 964

	g_clear_pointer (&priv->iface, g_free);
965
	g_clear_pointer (&priv->hostname, g_free);
966
	g_clear_pointer (&priv->uuid, g_free);
967
	g_clear_pointer (&priv->client_id, g_bytes_unref);
968
	g_clear_pointer (&priv->hwaddr, g_bytes_unref);
969

970
	G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object);
971 972

	priv->multi_idx = nm_dedup_multi_index_unref (priv->multi_idx);
973 974 975
}

static void
976
nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
977 978 979
{
	GObjectClass *object_class = G_OBJECT_CLASS (client_class);

980
	g_type_class_add_private (client_class, sizeof (NMDhcpClientPrivate));
981 982 983 984 985

	object_class->dispose = dispose;
	object_class->get_property = get_property;
	object_class->set_property = set_property;

986
	client_class->stop = stop;
987
	client_class->get_duid = get_duid;
988

989 990 991 992 993 994
	obj_properties[PROP_MULTI_IDX] =
	    g_param_spec_pointer (NM_DHCP_CLIENT_MULTI_IDX, "", "",
	                            G_PARAM_WRITABLE
	                          | G_PARAM_CONSTRUCT_ONLY
	                          | G_PARAM_STATIC_STRINGS);

995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
	obj_properties[PROP_IFACE] =
	    g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, "", "",
	                         NULL,
	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                         G_PARAM_STATIC_STRINGS);

	obj_properties[PROP_IFINDEX] =
	    g_param_spec_int (NM_DHCP_CLIENT_IFINDEX, "", "",
	                      -1, G_MAXINT, -1,
	                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                      G_PARAM_STATIC_STRINGS);

	obj_properties[PROP_HWADDR] =
	    g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, "", "",
1009
	                        G_TYPE_BYTES,
1010 1011 1012
	                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                        G_PARAM_STATIC_STRINGS);

1013 1014 1015 1016 1017
	obj_properties[PROP_ADDR_FAMILY] =
	    g_param_spec_int (NM_DHCP_CLIENT_ADDR_FAMILY, "", "",
	                      0, G_MAXINT, AF_UNSPEC,
	                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                      G_PARAM_STATIC_STRINGS);
1018 1019 1020 1021 1022 1023 1024

	obj_properties[PROP_UUID] =
	    g_param_spec_string (NM_DHCP_CLIENT_UUID, "", "",
	                         NULL,
	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                         G_PARAM_STATIC_STRINGS);

1025 1026 1027 1028 1029 1030
	obj_properties[PROP_HOSTNAME] =
	    g_param_spec_string (NM_DHCP_CLIENT_HOSTNAME, "", "",
	                         NULL,
	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                         G_PARAM_STATIC_STRINGS);

1031 1032 1033
	obj_properties[PROP_ROUTE_TABLE] =
	    g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "",
	                       0, G_MAXUINT32, RT_TABLE_MAIN,
1034
	                       G_PARAM_READWRITE |
1035 1036
	                       G_PARAM_STATIC_STRINGS);

1037 1038
	obj_properties[PROP_ROUTE_METRIC] =
	    g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_METRIC, "", "",
1039
	                       0, G_MAXUINT32, 0,
1040
	                       G_PARAM_READWRITE |
1041 1042 1043 1044
	                       G_PARAM_STATIC_STRINGS);

	obj_properties[PROP_TIMEOUT] =
	    g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, "", "",
1045
	                       1, G_MAXINT32, NM_DHCP_TIMEOUT_DEFAULT,
1046 1047 1048
	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
	                       G_PARAM_STATIC_STRINGS);

1049 1050 1051 1052 1053 1054
	obj_properties[PROP_FLAGS] =
	    g_param_spec_uint (NM_DHCP_CLIENT_FLAGS, "", "",
	                       0, G_MAXUINT32, 0,
	                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
	                       G_PARAM_STATIC_STRINGS);

1055 1056
	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);

1057
	signals[SIGNAL_STATE_CHANGED] =
1058 1059 1060
	    g_signal_new (NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
	                  G_OBJECT_CLASS_TYPE (object_class),
	                  G_SIGNAL_RUN_FIRST,
1061
	                  0,
1062 1063
	                  NULL, NULL, NULL,
	                  G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING);
1064