summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-07-22 14:39:32 -0500
committerDan Williams <dcbw@redhat.com>2014-07-22 14:39:32 -0500
commitafebbb6ef8db81270beb8d55fa03000890ca8875 (patch)
treef621459bd1a42a360cab70ecb0a4819126522de3
parent5de81735842a6e774b46c7f03e3a5fdba1556dd4 (diff)
parent7ef4e3fd01ea5c61c408e0bb8418395badae8846 (diff)
merge: DHCP client code cleanups and simplification (bgo #732965)
In preparation for library-based DHCP clients, remove some assupmtions that all DHCP clients are subprocesses, and that all DHCP options get passed back NM via D-Bus. https://bugzilla.gnome.org/show_bug.cgi?id=732965
-rw-r--r--src/Makefile.am2
-rw-r--r--src/devices/nm-device.c204
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c1254
-rw-r--r--src/dhcp-manager/nm-dhcp-client.h99
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c36
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c27
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c132
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.h19
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.c685
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.h39
-rw-r--r--src/dhcp-manager/tests/Makefile.am19
-rw-r--r--src/dhcp-manager/tests/test-dhcp-options.c683
-rw-r--r--src/tests/Makefile.am14
-rw-r--r--src/tests/test-dhcp-options.c937
14 files changed, 1892 insertions, 2258 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ebbee17935..367b4c85fa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -97,6 +97,8 @@ nm_sources = \
\
dhcp-manager/nm-dhcp-client.c \
dhcp-manager/nm-dhcp-client.h \
+ dhcp-manager/nm-dhcp-utils.c \
+ dhcp-manager/nm-dhcp-utils.h \
dhcp-manager/nm-dhcp-dhclient.c \
dhcp-manager/nm-dhcp-dhclient.h \
dhcp-manager/nm-dhcp-dhclient-utils.c \
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 4750d0b3a8..ba9a675eae 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -241,7 +241,6 @@ typedef struct {
/* DHCPv4 tracking */
NMDHCPClient * dhcp4_client;
gulong dhcp4_state_sigid;
- gulong dhcp4_timeout_sigid;
NMDHCP4Config * dhcp4_config;
NMIP4Config * vpn4_config; /* routes added by a VPN which uses this device */
@@ -279,7 +278,6 @@ typedef struct {
NMDHCPClient * dhcp6_client;
NMRDiscDHCPLevel dhcp6_mode;
gulong dhcp6_state_sigid;
- gulong dhcp6_timeout_sigid;
NMDHCP6Config * dhcp6_config;
/* IP6 config from DHCP */
NMIP6Config * dhcp6_ip6_config;
@@ -2591,11 +2589,6 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
priv->dhcp4_state_sigid = 0;
}
- if (priv->dhcp4_timeout_sigid) {
- g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid);
- priv->dhcp4_timeout_sigid = 0;
- }
-
nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
if (stop)
@@ -2610,14 +2603,6 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
}
}
-static void
-dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
-{
- nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data),
- (const char *) key,
- (const char *) value);
-}
-
static gboolean
ip4_config_merge_and_apply (NMDevice *self,
NMIP4Config *config,
@@ -2689,8 +2674,7 @@ dhcp4_fail (NMDevice *device, gboolean timeout)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- nm_dhcp4_config_reset (priv->dhcp4_config);
-
+ dhcp4_cleanup (device, TRUE, FALSE);
if (timeout || (priv->ip4_state == IP_CONF))
nm_device_activate_schedule_ip4_config_timeout (device);
else if (priv->ip4_state == IP_FAIL)
@@ -2698,26 +2682,40 @@ dhcp4_fail (NMDevice *device, gboolean timeout)
}
static void
+dhcp4_update_config (NMDevice *self, NMDHCP4Config *config, GHashTable *options)
+{
+ GHashTableIter iter;
+ const char *key, *value;
+
+ /* Update the DHCP4 config object with new DHCP options */
+ nm_dhcp4_config_reset (config);
+
+ g_hash_table_iter_init (&iter, options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
+ nm_dhcp4_config_add_option (config, key, value);
+
+ g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
+}
+
+static void
dhcp4_state_changed (NMDHCPClient *client,
- NMDHCPState state,
+ NMDhcpState state,
+ NMIP4Config *ip4_config,
+ GHashTable *options,
gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- NMIP4Config *config;
g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
+ g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d",
nm_device_get_iface (device), state);
switch (state) {
- case DHC_BOUND4: /* lease obtained */
- case DHC_RENEW4: /* lease renewed */
- case DHC_REBOOT: /* have valid lease, but now obtained a different one */
- case DHC_REBIND4: /* new, different lease */
- config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
- if (!config) {
+ case NM_DHCP_STATE_BOUND:
+ if (!ip4_config) {
nm_log_warn (LOGD_DHCP4, "(%s): failed to get IPv4 config in response to DHCP event.",
nm_device_get_ip_iface (device));
nm_device_state_changed (device,
@@ -2726,26 +2724,18 @@ dhcp4_state_changed (NMDHCPClient *client,
break;
}
- /* Update the DHCP4 config object with new DHCP options */
- nm_dhcp4_config_reset (priv->dhcp4_config);
- nm_dhcp_client_foreach_option (priv->dhcp4_client,
- dhcp4_add_option_cb,
- priv->dhcp4_config);
- g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG);
+ dhcp4_update_config (device, priv->dhcp4_config, options);
if (priv->ip4_state == IP_CONF)
- nm_device_activate_schedule_ip4_config_result (device, config);
+ nm_device_activate_schedule_ip4_config_result (device, ip4_config);
else if (priv->ip4_state == IP_DONE)
- dhcp4_lease_change (device, config);
- g_object_unref (config);
-
+ dhcp4_lease_change (device, ip4_config);
break;
- case DHC_TIMEOUT: /* timed out contacting DHCP server */
+ case NM_DHCP_STATE_TIMEOUT:
dhcp4_fail (device, TRUE);
break;
- case DHC_END: /* dhclient exited normally */
- case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
- case DHC_ABEND: /* dhclient exited abnormally */
+ case NM_DHCP_STATE_DONE:
+ case NM_DHCP_STATE_FAIL:
/* dhclient quit and can't get/renew a lease; so kill the connection */
dhcp4_fail (device, FALSE);
break;
@@ -2754,18 +2744,6 @@ dhcp4_state_changed (NMDHCPClient *client,
}
}
-static void
-dhcp4_timeout (NMDHCPClient *client, gpointer user_data)
-{
- NMDevice *device = NM_DEVICE (user_data);
-
- g_return_if_fail (nm_device_get_act_request (device) != NULL);
- g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
-
- nm_dhcp_client_stop (client, FALSE);
- dhcp4_fail (device, TRUE);
-}
-
static NMActStageReturn
dhcp4_start (NMDevice *self,
NMConnection *connection,
@@ -2791,10 +2769,13 @@ dhcp4_start (NMDevice *self,
g_warn_if_fail (priv->dhcp4_client == NULL);
priv->dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
nm_device_get_ip_iface (self),
+ nm_device_get_ip_ifindex (self),
tmp,
nm_connection_get_uuid (connection),
nm_device_get_priority (self),
- s_ip4,
+ nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4),
+ nm_setting_ip4_config_get_dhcp_hostname (s_ip4),
+ nm_setting_ip4_config_get_dhcp_client_id (s_ip4),
priv->dhcp_timeout,
priv->dhcp_anycast_address);
@@ -2810,10 +2791,6 @@ dhcp4_start (NMDevice *self,
NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
G_CALLBACK (dhcp4_state_changed),
self);
- priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client,
- NM_DHCP_CLIENT_SIGNAL_TIMEOUT,
- G_CALLBACK (dhcp4_timeout),
- self);
nm_device_add_pending_action (self, PENDING_ACTION_DHCP4, TRUE);
@@ -3037,11 +3014,6 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
priv->dhcp6_state_sigid = 0;
}
- if (priv->dhcp6_timeout_sigid) {
- g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid);
- priv->dhcp6_timeout_sigid = 0;
- }
-
nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
if (stop)
@@ -3056,14 +3028,6 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
}
}
-static void
-dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
-{
- nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
- (const char *) key,
- (const char *) value);
-}
-
static gboolean
ip6_config_merge_and_apply (NMDevice *self,
gboolean commit,
@@ -3139,8 +3103,7 @@ dhcp6_fail (NMDevice *device, gboolean timeout)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
- nm_dhcp6_config_reset (priv->dhcp6_config);
-
+ dhcp6_cleanup (device, TRUE, FALSE);
if (timeout || (priv->ip6_state == IP_CONF))
nm_device_activate_schedule_ip6_config_timeout (device);
else if (priv->ip6_state == IP_FAIL)
@@ -3148,34 +3111,59 @@ dhcp6_fail (NMDevice *device, gboolean timeout)
}
static void
+dhcp6_timeout (NMDevice *self, NMDHCPClient *client)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED)
+ dhcp6_fail (self, TRUE);
+ else {
+ /* not a hard failure; just live with the RA info */
+ dhcp6_cleanup (self, TRUE, FALSE);
+ if (priv->ip6_state == IP_CONF)
+ nm_device_activate_schedule_ip6_config_result (self);
+ }
+}
+
+static void
+dhcp6_update_config (NMDevice *self, NMDHCP6Config *config, GHashTable *options)
+{
+ GHashTableIter iter;
+ const char *key, *value;
+
+ /* Update the DHCP6 config object with new DHCP options */
+ nm_dhcp6_config_reset (config);
+
+ g_hash_table_iter_init (&iter, options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
+ nm_dhcp6_config_add_option (config, key, value);
+
+ g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+}
+
+static void
dhcp6_state_changed (NMDHCPClient *client,
- NMDHCPState state,
+ NMDhcpState state,
+ NMIP6Config *ip6_config,
+ GHashTable *options,
gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
+ g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config));
nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d",
nm_device_get_iface (device), state);
switch (state) {
- case DHC_BOUND6:
- case DHC_RENEW6: /* lease renewed */
- case DHC_REBOOT: /* have valid lease, but now obtained a different one */
- case DHC_REBIND6: /* new, different lease */
+ case NM_DHCP_STATE_BOUND:
g_clear_object (&priv->dhcp6_ip6_config);
- priv->dhcp6_ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
-
- /* Update the DHCP6 config object with new DHCP options */
- nm_dhcp6_config_reset (priv->dhcp6_config);
- if (priv->dhcp6_ip6_config) {
- nm_dhcp_client_foreach_option (priv->dhcp6_client,
- dhcp6_add_option_cb,
- priv->dhcp6_config);
+ if (ip6_config) {
+ priv->dhcp6_ip6_config = g_object_ref (ip6_config);
+ dhcp6_update_config (device, priv->dhcp6_config, options);
}
- g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG);
if (priv->ip6_state == IP_CONF) {
if (priv->dhcp6_ip6_config == NULL) {
@@ -3187,10 +3175,10 @@ dhcp6_state_changed (NMDHCPClient *client,
} else if (priv->ip6_state == IP_DONE)
dhcp6_lease_change (device);
break;
- case DHC_TIMEOUT: /* timed out contacting DHCP server */
- dhcp6_fail (device, TRUE);
+ case NM_DHCP_STATE_TIMEOUT:
+ dhcp6_timeout (device, client);
break;
- case DHC_END: /* dhclient exited normally */
+ case NM_DHCP_STATE_DONE:
/* In IPv6 info-only mode, the client doesn't handle leases so it
* may exit right after getting a response from the server. That's
* normal. In that case we just ignore the exit.
@@ -3198,8 +3186,7 @@ dhcp6_state_changed (NMDHCPClient *client,
if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF)
break;
/* Otherwise, fall through */
- case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
- case DHC_ABEND: /* dhclient exited abnormally */
+ case NM_DHCP_STATE_FAIL:
/* dhclient quit and can't get/renew a lease; so kill the connection */
dhcp6_fail (device, FALSE);
break;
@@ -3208,30 +3195,6 @@ dhcp6_state_changed (NMDHCPClient *client,
}
}
-static void
-dhcp6_timeout (NMDHCPClient *client, gpointer user_data)
-{
- NMDevice *device = NM_DEVICE (user_data);
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-
- g_return_if_fail (nm_device_get_act_request (device) != NULL);
- g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
-
- nm_dhcp_client_stop (client, FALSE);
- if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED)
- dhcp6_fail (device, TRUE);
- else {
- /* not a hard failure; just live with the RA info */
- nm_dhcp6_config_reset (priv->dhcp6_config);
- if (priv->dhcp6_ip6_config)
- g_object_unref (priv->dhcp6_ip6_config);
- priv->dhcp6_ip6_config = NULL;
-
- if (priv->ip6_state == IP_CONF)
- nm_device_activate_schedule_ip6_config_result (device);
- }
-}
-
static NMActStageReturn
dhcp6_start (NMDevice *self,
NMConnection *connection,
@@ -3248,7 +3211,8 @@ dhcp6_start (NMDevice *self,
g_assert (connection);
}
- /* Begin a DHCP transaction on the interface */
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ g_assert (s_ip6);
/* Clear old exported DHCP options */
if (priv->dhcp6_config)
@@ -3268,13 +3232,15 @@ dhcp6_start (NMDevice *self,
priv->dhcp6_client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
nm_device_get_ip_iface (self),
+ nm_device_get_ip_ifindex (self),
tmp,
nm_connection_get_uuid (connection),
nm_device_get_priority (self),
- nm_connection_get_setting_ip6_config (connection),
+ nm_setting_ip6_config_get_dhcp_hostname (s_ip6),
priv->dhcp_timeout,
priv->dhcp_anycast_address,
- (dhcp_opt == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE);
+ (dhcp_opt == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
+ nm_setting_ip6_config_get_ip6_privacy (s_ip6));
if (tmp)
g_byte_array_free (tmp, TRUE);
@@ -3283,10 +3249,6 @@ dhcp6_start (NMDevice *self,
NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
G_CALLBACK (dhcp6_state_changed),
self);
- priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client,
- NM_DHCP_CLIENT_SIGNAL_TIMEOUT,
- G_CALLBACK (dhcp6_timeout),
- self);
s_ip6 = nm_connection_get_setting_ip6_config (connection);
if (!nm_setting_ip6_config_get_may_fail (s_ip6) ||
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index 5c39fafa68..7fda8646f5 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -33,9 +33,11 @@
#include "nm-logging.h"
#include "nm-dbus-glib-types.h"
#include "nm-dhcp-client.h"
+#include "nm-dhcp-utils.h"
typedef struct {
char * iface;
+ int ifindex;
GByteArray * hwaddr;
gboolean ipv6;
char * uuid;
@@ -43,13 +45,10 @@ typedef struct {
guint32 timeout;
GByteArray * duid;
- guchar state;
- GPid pid;
- gboolean dead;
+ NMDhcpState state;
+ pid_t pid;
guint timeout_id;
guint watch_id;
- guint32 remove_id;
- GHashTable * options;
gboolean info_only;
} NMDHCPClientPrivate;
@@ -60,8 +59,6 @@ G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG
enum {
SIGNAL_STATE_CHANGED,
- SIGNAL_TIMEOUT,
- SIGNAL_REMOVE,
LAST_SIGNAL
};
@@ -70,6 +67,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
PROP_IFACE,
+ PROP_IFINDEX,
PROP_HWADDR,
PROP_IPV6,
PROP_UUID,
@@ -80,7 +78,7 @@ enum {
/********************************************/
-GPid
+pid_t
nm_dhcp_client_get_pid (NMDHCPClient *self)
{
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);
@@ -96,6 +94,14 @@ nm_dhcp_client_get_iface (NMDHCPClient *self)
return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface;
}
+int
+nm_dhcp_client_get_ifindex (NMDHCPClient *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->ifindex;
+}
+
gboolean
nm_dhcp_client_get_ipv6 (NMDHCPClient *self)
{
@@ -112,6 +118,72 @@ nm_dhcp_client_get_uuid (NMDHCPClient *self)
return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid;
}
+const GByteArray *
+nm_dhcp_client_get_duid (NMDHCPClient *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid;
+}
+
+const GByteArray *
+nm_dhcp_client_get_hw_addr (NMDHCPClient *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr;
+}
+
+guint32
+nm_dhcp_client_get_priority (NMDHCPClient *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), G_MAXUINT32);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority;
+}
+
+/********************************************/
+
+static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
+ [NM_DHCP_STATE_UNKNOWN] = "unknown",
+ [NM_DHCP_STATE_BOUND] = "bound",
+ [NM_DHCP_STATE_TIMEOUT] = "timeout",
+ [NM_DHCP_STATE_DONE] = "done",
+ [NM_DHCP_STATE_FAIL] = "fail",
+};
+
+static const char *
+state_to_string (NMDhcpState state)
+{
+ if (state >= 0 && state < G_N_ELEMENTS (state_table))
+ return state_table[state];
+ return NULL;
+}
+
+static NMDhcpState
+reason_to_state (const char *iface, const char *reason)
+{
+ 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;
+ else if (g_ascii_strcasecmp (reason, "end") == 0)
+ return NM_DHCP_STATE_DONE;
+ else if (g_ascii_strcasecmp (reason, "fail") == 0 ||
+ g_ascii_strcasecmp (reason, "abend") == 0 ||
+ g_ascii_strcasecmp (reason, "nak") == 0)
+ return NM_DHCP_STATE_FAIL;
+
+ nm_log_dbg (LOGD_DHCP, "(%s): unmapped DHCP state '%s'", iface, reason);
+ return NM_DHCP_STATE_UNKNOWN;
+}
+
/********************************************/
static void
@@ -137,7 +209,7 @@ watch_cleanup (NMDHCPClient *self)
}
void
-nm_dhcp_client_stop_pid (GPid pid, const char *iface)
+nm_dhcp_client_stop_pid (pid_t pid, const char *iface)
{
char *name = iface ? g_strdup_printf ("dhcp-client-%s", iface) : NULL;
@@ -154,63 +226,76 @@ stop (NMDHCPClient *self, gboolean release, const GByteArray *duid)
g_return_if_fail (NM_IS_DHCP_CLIENT (self));
priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- g_return_if_fail (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);
+ 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);
+ priv->pid = -1;
+ }
priv->info_only = FALSE;
}
-static gboolean
-daemon_timeout (gpointer user_data)
+void
+nm_dhcp_client_set_state (NMDHCPClient *self,
+ NMDhcpState new_state,
+ GObject *ip_config,
+ GHashTable *options)
{
- NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- if (priv->ipv6) {
- nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 request timed out.", priv->iface);
+ if (new_state >= NM_DHCP_STATE_BOUND)
+ timeout_cleanup (self);
+ if (new_state >= NM_DHCP_STATE_TIMEOUT)
+ watch_cleanup (self);
+
+ if (new_state == NM_DHCP_STATE_BOUND) {
+ g_assert ( (priv->ipv6 && NM_IS_IP6_CONFIG (ip_config))
+ || (!priv->ipv6 && NM_IS_IP4_CONFIG (ip_config)));
+ g_assert (options);
+ g_assert_cmpint (g_hash_table_size (options), >, 0);
} else {
- nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 request timed out.", priv->iface);
+ g_assert (ip_config == NULL);
+ g_assert (options == NULL);
}
- g_signal_emit (G_OBJECT (self), signals[SIGNAL_TIMEOUT], 0);
- return FALSE;
-}
-static gboolean
-signal_remove (gpointer user_data)
-{
- NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+ /* 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;
+
+ nm_log_info (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
+ "(%s): DHCPv%c state changed %s -> %s",
+ priv->iface,
+ priv->ipv6 ? '6' : '4',
+ state_to_string (priv->state),
+ state_to_string (new_state));
- NM_DHCP_CLIENT_GET_PRIVATE (self)->remove_id = 0;
- g_signal_emit (G_OBJECT (self), signals[SIGNAL_REMOVE], 0);
- return FALSE;
+ priv->state = new_state;
+ g_signal_emit (G_OBJECT (self),
+ signals[SIGNAL_STATE_CHANGED], 0,
+ new_state,
+ ip_config,
+ options);
}
-static void
-dhcp_client_set_state (NMDHCPClient *self,
- NMDHCPState state,
- gboolean emit_state,
- gboolean remove_now)
+static gboolean
+daemon_timeout (gpointer user_data)
{
+ NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- priv->state = state;
-
- if (emit_state)
- g_signal_emit (G_OBJECT (self), signals[SIGNAL_STATE_CHANGED], 0, priv->state);
-
- if (state == DHC_END || state == DHC_ABEND) {
- /* Start the remove signal timer */
- if (remove_now) {
- g_signal_emit (G_OBJECT (self), signals[SIGNAL_REMOVE], 0);
- } else {
- if (!priv->remove_id)
- priv->remove_id = g_timeout_add_seconds (5, signal_remove, self);
- }
- }
+ priv->timeout_id = 0;
+ nm_log_warn (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
+ "(%s): DHCPv%c request timed out.",
+ priv->iface,
+ priv->ipv6 ? '6' : '4');
+ nm_dhcp_client_set_state (self, NM_DHCP_STATE_TIMEOUT, NULL, NULL);
+ return G_SOURCE_REMOVE;
}
static void
@@ -218,7 +303,7 @@ daemon_watch_cb (GPid pid, gint status, gpointer user_data)
{
NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- NMDHCPState new_state;
+ NMDhcpState new_state;
if (priv->ipv6) {
nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 client pid %d exited with status %d",
@@ -231,32 +316,31 @@ daemon_watch_cb (GPid pid, gint status, gpointer user_data)
}
if (!WIFEXITED (status)) {
- new_state = DHC_ABEND;
+ new_state = NM_DHCP_STATE_FAIL;
nm_log_warn (LOGD_DHCP, "DHCP client died abnormally");
} else
- new_state = DHC_END;
+ new_state = NM_DHCP_STATE_DONE;
- watch_cleanup (self);
- timeout_cleanup (self);
- priv->dead = TRUE;
+ priv->pid = -1;
- dhcp_client_set_state (self, new_state, TRUE, FALSE);
+ nm_dhcp_client_set_state (self, new_state, NULL, NULL);
}
-static void
-start_monitor (NMDHCPClient *self)
+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 > 0);
+ g_return_if_fail (priv->pid == -1);
+ priv->pid = pid;
/* Set up a timeout on the transaction to kill it after the timeout */
+ g_assert (priv->timeout_id == 0);
priv->timeout_id = g_timeout_add_seconds (priv->timeout,
daemon_timeout,
self);
- priv->watch_id = g_child_watch_add (priv->pid,
- (GChildWatchFunc) daemon_watch_cb,
- self);
+ g_assert (priv->watch_id == 0);
+ priv->watch_id = g_child_watch_add (pid, daemon_watch_cb, self);
}
gboolean
@@ -277,11 +361,7 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self,
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
priv->iface, priv->timeout);
- priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_client_id, dhcp_anycast_addr, hostname);
- if (priv->pid)
- start_monitor (self);
-
- return priv->pid ? TRUE : FALSE;
+ return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_client_id, dhcp_anycast_addr, hostname);
}
/* uuid_parse does not work for machine-id, so we use our own converter */
@@ -370,38 +450,21 @@ generate_duid_from_machine_id (void)
return duid;
}
-static char *
-escape_duid (const GByteArray *duid)
-{
- guint32 i = 0;
- GString *s;
-
- g_return_val_if_fail (duid != NULL, NULL);
-
- s = g_string_sized_new (40);
- while (i < duid->len) {
- if (s->len)
- g_string_append_c (s, ':');
- g_string_append_printf (s, "%02x", duid->data[i++]);
- }
- return g_string_free (s, FALSE);
-}
-
static GByteArray *
get_duid (NMDHCPClient *self)
{
static GByteArray *duid = NULL;
GByteArray *copy = NULL;
- char *escaped;
+ char *str;
if (G_UNLIKELY (duid == NULL)) {
duid = generate_duid_from_machine_id ();
g_assert (duid);
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
- escaped = escape_duid (duid);
- nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", escaped);
- g_free (escaped);
+ str = nm_dhcp_utils_duid_to_string (duid);
+ nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", str);
+ g_free (str);
}
}
@@ -417,10 +480,11 @@ gboolean
nm_dhcp_client_start_ip6 (NMDHCPClient *self,
GByteArray *dhcp_anycast_addr,
const char *hostname,
- gboolean info_only)
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy)
{
NMDHCPClientPrivate *priv;
- char *escaped;
+ char *str;
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
@@ -436,9 +500,9 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self,
priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP)) {
- escaped = escape_duid (priv->duid);
- nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped);
- g_free (escaped);
+ str = nm_dhcp_utils_duid_to_string (priv->duid);
+ nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, str);
+ g_free (str);
}
priv->info_only = info_only;
@@ -446,15 +510,12 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self,
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
priv->iface, priv->timeout);
- priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
- dhcp_anycast_addr,
- hostname,
- info_only,
- priv->duid);
- if (priv->pid > 0)
- start_monitor (self);
-
- return priv->pid ? TRUE : FALSE;
+ return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
+ dhcp_anycast_addr,
+ hostname,
+ info_only,
+ privacy,
+ priv->duid);
}
void
@@ -482,7 +543,7 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
exe = proc_contents;
if (!strcmp (exe, binary_name))
- nm_dhcp_client_stop_pid ((GPid) tmp, NULL);
+ nm_dhcp_client_stop_pid ((pid_t) tmp, NULL);
}
}
@@ -498,100 +559,27 @@ void
nm_dhcp_client_stop (NMDHCPClient *self, gboolean release)
{
NMDHCPClientPrivate *priv;
+ pid_t old_pid = 0;
g_return_if_fail (NM_IS_DHCP_CLIENT (self));
priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
/* Kill the DHCP client */
- if (!priv->dead) {
- NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid);
- priv->dead = TRUE;
-
+ old_pid = priv->pid;
+ NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid);
+ if (old_pid > 0) {
nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d",
- priv->iface, priv->pid);
- }
-
- /* And clean stuff up */
-
- priv->pid = -1;
- dhcp_client_set_state (self, DHC_END, FALSE, TRUE);
-
- g_hash_table_remove_all (priv->options);
+ priv->iface, old_pid);
+ } else
+ nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction", priv->iface);
+ g_assert (priv->pid == -1);
- timeout_cleanup (self);
- watch_cleanup (self);
+ nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL);
}
/********************************************/
-static gboolean
-state_is_bound (guint32 state)
-{
- if ( (state == DHC_BOUND4)
- || (state == DHC_BOUND6)
- || (state == DHC_RENEW4)
- || (state == DHC_RENEW6)
- || (state == DHC_REBOOT)
- || (state == DHC_REBIND4)
- || (state == DHC_REBIND6)
- || (state == DHC_IPV4LL))
- return TRUE;
-
- return FALSE;
-}
-
-static const char *state_table[] = {
- [DHC_NBI] = "nbi",
- [DHC_PREINIT] = "preinit",
- [DHC_PREINIT6] = "preinit6",
- [DHC_BOUND4] = "bound",
- [DHC_BOUND6] = "bound6",
- [DHC_IPV4LL] = "ipv4ll",
- [DHC_RENEW4] = "renew",
- [DHC_RENEW6] = "renew6",
- [DHC_REBOOT] = "reboot",
- [DHC_REBIND4] = "rebind",
- [DHC_REBIND6] = "rebind6",
- [DHC_DEPREF6] = "depref6",
- [DHC_STOP] = "stop",
- [DHC_STOP6] = "stop6",
- [DHC_MEDIUM] = "medium",
- [DHC_TIMEOUT] = "timeout",
- [DHC_FAIL] = "fail",
- [DHC_EXPIRE] = "expire",
- [DHC_EXPIRE6] = "expire6",
- [DHC_RELEASE] = "release",
- [DHC_RELEASE6] = "release6",
- [DHC_START] = "start",
- [DHC_ABEND] = "abend",
- [DHC_END] = "end",
-};
-
-static const char *
-state_to_string (NMDHCPState state)
-{
- if (state >= 0 && state < G_N_ELEMENTS (state_table))
- return state_table[state];
- return NULL;
-}
-
-static NMDHCPState
-string_to_state (const char *name)
-{
- int i;
-
- if (name) {
- for (i = 0; i < G_N_ELEMENTS (state_table); i++) {
- const char *n = state_table[i];
-
- if (n && !strcasecmp (name, n))
- return i;
- }
- }
- return 255;
-}
-
static char *
garray_to_string (GArray *array, const char *key)
{
@@ -625,25 +613,47 @@ garray_to_string (GArray *array, const char *key)
return converted;
}
+#define OLD_TAG "old_"
+#define NEW_TAG "new_"
+
static void
-copy_option (gpointer key,
- gpointer value,
+copy_option (const char * key,
+ GValue *value,
gpointer user_data)
{
GHashTable *hash = user_data;
- const char *str_key = (const char *) key;
char *str_value = NULL;
+ const char **p;
+ static const char *ignored_keys[] = {
+ "interface",
+ "pid",
+ "reason",
+ "dhcp_message_type",
+ NULL
+ };
+
+ if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY)) {
+ nm_log_warn (LOGD_DHCP, "key %s value type was not DBUS_TYPE_G_UCHAR_ARRAY", key);
+ return;
+ }
- if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
- nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
- "DBUS_TYPE_G_UCHAR_ARRAY",
- str_key);
+ if (g_str_has_prefix (key, OLD_TAG))
return;
+
+ /* Filter out stuff that's not actually new DHCP options */
+ for (p = ignored_keys; *p; p++) {
+ if (!strcmp (*p, key))
+ return;
}
- str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key);
+ if (g_str_has_prefix (key, NEW_TAG))
+ key += STRLEN (NEW_TAG);
+ if (!key[0])
+ return;
+
+ str_value = garray_to_string ((GArray *) g_value_get_boxed (value), key);
if (str_value)
- g_hash_table_insert (hash, g_strdup (str_key), str_value);
+ g_hash_table_insert (hash, g_strdup (key), str_value);
}
void
@@ -654,6 +664,8 @@ nm_dhcp_client_new_options (NMDHCPClient *self,
NMDHCPClientPrivate *priv;
guint32 old_state;
guint32 new_state;
+ GHashTable *str_options = NULL;
+ GObject *ip_config = NULL;
g_return_if_fail (NM_IS_DHCP_CLIENT (self));
g_return_if_fail (options != NULL);
@@ -661,789 +673,35 @@ nm_dhcp_client_new_options (NMDHCPClient *self,
priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
old_state = priv->state;
- new_state = string_to_state (reason);
-
- /* Clear old and save new DHCP options */
- g_hash_table_remove_all (priv->options);
- g_hash_table_foreach (options, copy_option, priv->options);
-
- if (old_state == new_state) {
- /* dhclient will stay in the same state (or, really, provide the same
- * reason) for operations like RENEW and REBIND. We need to ensure
- * that triggers various DHCP lease change code, so we need to pass
- * along same-state transitions for these states.
- */
- if ( new_state != DHC_BOUND4
- && new_state != DHC_RENEW4
- && new_state != DHC_REBIND4
- && new_state != DHC_BOUND6
- && new_state != DHC_RENEW6
- && new_state != DHC_REBIND6)
- return;
- }
-
- /* Handle changed device state */
- if (state_is_bound (new_state)) {
- /* Cancel the timeout if the DHCP client is now bound */
- timeout_cleanup (self);
- }
-
- if (priv->ipv6) {
- nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s",
- priv->iface,
- state_to_string (old_state),
- state_to_string (new_state));
- } else {
- nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s",
- priv->iface,
- state_to_string (old_state),
- state_to_string (new_state));
- }
-
- dhcp_client_set_state (self, new_state, TRUE, FALSE);
-}
-
-#define NEW_TAG "new_"
-#define OLD_TAG "old_"
-
-gboolean
-nm_dhcp_client_foreach_option (NMDHCPClient *self,
- GHFunc func,
- gpointer user_data)
-{
- NMDHCPClientPrivate *priv;
- GHashTableIter iter;
- gpointer iterkey, itervalue;
-
- g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
- g_return_val_if_fail (func != NULL, FALSE);
-
- priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
- if (!state_is_bound (priv->state)) {
- if (priv->ipv6) {
- nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
- } else {
- nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
- }
- }
-
- g_hash_table_iter_init (&iter, priv->options);
- while (g_hash_table_iter_next (&iter, &iterkey, &itervalue)) {
- const char *key = iterkey, *value = itervalue;
- const char **p;
- static const char *filter_options[] = {
- "interface", "pid", "reason", "dhcp_message_type", NULL
- };
- gboolean ignore = FALSE;
-
- /* Filter out stuff that's not actually new DHCP options */
- for (p = filter_options; *p; p++) {
- if (!strcmp (*p, key) || !strncmp (key, OLD_TAG, strlen (OLD_TAG))) {
- ignore = TRUE;
- break;
- }
- }
-
- if (!ignore) {
- const char *tmp_key = key;
-
- /* Remove the "new_" prefix that dhclient passes back */
- if (!strncmp (key, NEW_TAG, strlen (NEW_TAG)))
- tmp_key = key + strlen (NEW_TAG);
-
- func ((gpointer) tmp_key, (gpointer) value, user_data);
- }
- }
- return TRUE;
-}
-
-/********************************************/
-
-static gboolean
-ip4_process_dhcpcd_rfc3442_routes (NMDHCPClient *self,
- const char *str,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
-{
- NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- char **routes, **r;
- gboolean have_routes = FALSE;
-
- routes = g_strsplit (str, " ", 0);
- if (g_strv_length (routes) == 0)
- goto out;
-
- if ((g_strv_length (routes) % 2) != 0) {
- nm_log_warn (LOGD_DHCP4, " classless static routes provided, but invalid");
- goto out;
- }
-
- for (r = routes; *r; r += 2) {
- char *slash;
- NMPlatformIP4Route route;
- int rt_cidr = 32;
- guint32 rt_addr, rt_route;
-
- slash = strchr(*r, '/');
- if (slash) {
- *slash = '\0';
- errno = 0;
- rt_cidr = strtol (slash + 1, NULL, 10);
- if ((errno == EINVAL) || (errno == ERANGE)) {
- nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
- continue;
- }
- }
- if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
- nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
- continue;
- }
- if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
- nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
- continue;
- }
-
- have_routes = TRUE;
- if (rt_cidr == 0 && rt_addr == 0) {
- /* FIXME: how to handle multiple routers? */
- *gwaddr = rt_route;
- } else {
- nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
- memset (&route, 0, sizeof (route));
- route.network = rt_addr;
- route.plen = rt_cidr;
- route.gateway = rt_route;
- route.source = NM_PLATFORM_SOURCE_DHCP;
- route.metric = priv->priority;
- nm_ip4_config_add_route (ip4_config, &route);
- }
- }
-
-out:
- g_strfreev (routes);
- return have_routes;
-}
-
-static const char **
-process_dhclient_rfc3442_route (const char **octets, NMPlatformIP4Route *route, gboolean *success)
-{
- const char **o = octets;
- int addr_len = 0, i = 0;
- long int tmp;
- char *next_hop;
- guint32 tmp_addr;
-
- *success = FALSE;
-
- if (!*o)
- return o; /* no prefix */
-
- tmp = strtol (*o, NULL, 10);
- if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */
- return o;
-
- memset (route, 0, sizeof (*route));
- route->plen = tmp;
- o++;
-
- if (tmp > 0)
- addr_len = ((tmp - 1) / 8) + 1;
-
- /* ensure there's at least the address + next hop left */
- if (g_strv_length ((char **) o) < addr_len + 4)
- goto error;
-
- if (tmp) {
- const char *addr[4] = { "0", "0", "0", "0" };
- char *str_addr;
-
- for (i = 0; i < addr_len; i++)
- addr[i] = *o++;
-
- str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
- if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
- g_free (str_addr);
- goto error;
- }
- tmp_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
- route->network = tmp_addr;
- }
-
- /* Handle next hop */
- next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
- if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
- g_free (next_hop);
- goto error;
- }
- route->gateway = tmp_addr;
- g_free (next_hop);
-
- *success = TRUE;
- return o + 4; /* advance to past the next hop */
-
-error:
- return o;
-}
-
-static gboolean
-ip4_process_dhclient_rfc3442_routes (NMDHCPClient *self,
- const char *str,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
-{
- NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- char **octets, **o;
- gboolean have_routes = FALSE;
- NMPlatformIP4Route route;
- gboolean success;
-
- o = octets = g_strsplit_set (str, " .", 0);
- if (g_strv_length (octets) < 5) {
- nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
- goto out;
- }
-
- while (*o) {
- memset (&route, 0, sizeof (route));
- o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success);
- if (!success) {
- nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
- break;
- }
-
- have_routes = TRUE;
- if (!route.plen) {
- /* gateway passed as classless static route */
- *gwaddr = route.gateway;
- } else {
- char addr[INET_ADDRSTRLEN];
-
- /* normal route */
- route.source = NM_PLATFORM_SOURCE_DHCP;
- route.metric = priv->priority;
- nm_ip4_config_add_route (ip4_config, &route);
-
- nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s",
- nm_utils_inet4_ntop (route.network, addr), route.plen,
- nm_utils_inet4_ntop (route.gateway, NULL));
- }
- }
-
-out:
- g_strfreev (octets);
- return have_routes;
-}
-
-static gboolean
-ip4_process_classless_routes (NMDHCPClient *self,
- GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
-{
- const char *str, *p;
-
- g_return_val_if_fail (options != NULL, FALSE);
- g_return_val_if_fail (ip4_config != NULL, FALSE);
-
- *gwaddr = 0;
-
- /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
- * slightly different format:
- *
- * option classless-static-routes = array of (destination-descriptor ip-address);
- *
- * which results in:
- *
- * 0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6
- *
- * dhcpcd supports classless static routes natively and uses this same
- * option identifier with the following format:
- *
- * 192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41
- */
- str = g_hash_table_lookup (options, "new_classless_static_routes");
-
- /* dhclient doesn't have actual support for rfc3442 classless static routes
- * upstream. Thus, people resort to defining the option in dhclient.conf
- * and using arbitrary formats like so:
- *
- * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
- *
- * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
- */
- if (!str)
- str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes");
-
- /* Microsoft version; same as rfc3442 but with a different option # (249) */
- if (!str)
- str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
-
- if (!str || !strlen (str))
- return FALSE;
-
- p = str;
- while (*p) {
- if (!g_ascii_isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) {
- nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
- return FALSE;
- }
- p++;
- };
-
- if (strchr (str, '/')) {
- /* dhcpcd format */
- return ip4_process_dhcpcd_rfc3442_routes (self, str, ip4_config, gwaddr);
- }
-
- return ip4_process_dhclient_rfc3442_routes (self, str, ip4_config, gwaddr);
-}
-
-static void
-process_classful_routes (NMDHCPClient *self, GHashTable *options, NMIP4Config *ip4_config)
-{
- NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- const char *str;
- char **searches, **s;
-
- str = g_hash_table_lookup (options, "new_static_routes");
- if (!str)
- return;
-
- searches = g_strsplit (str, " ", 0);
- if ((g_strv_length (searches) % 2)) {
- nm_log_info (LOGD_DHCP, " static routes provided, but invalid");
- goto out;
- }
-
- for (s = searches; *s; s += 2) {
- NMPlatformIP4Route route;
- guint32 rt_addr, rt_route;
-
- if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
- nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s);
- continue;
- }
- if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
- nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
- continue;
- }
-
- // FIXME: ensure the IP address and route are sane
-
- memset (&route, 0, sizeof (route));
- route.network = rt_addr;
- /* RFC 2132, updated by RFC 3442:
- The Static Routes option (option 33) does not provide a subnet mask
- for each route - it is assumed that the subnet mask is implicit in
- whatever network number is specified in each route entry */
- route.plen = nm_utils_ip4_get_default_prefix (rt_addr);
- if (rt_addr & ~nm_utils_ip4_prefix_to_netmask (route.plen)) {
- /* RFC 943: target not "this network"; using host routing */
- route.plen = 32;
- }
- route.gateway = rt_route;
- route.source = NM_PLATFORM_SOURCE_DHCP;
- route.metric = priv->priority;
-
- nm_ip4_config_add_route (ip4_config, &route);
- nm_log_info (LOGD_DHCP, " static route %s",
- nm_platform_ip4_route_to_string (&route));
- }
-
-out:
- g_strfreev (searches);
-}
-
-static void
-process_domain_search (const char *str, GFunc add_func, gpointer user_data)
-{
- char **searches, **s;
- char *unescaped, *p;
- int i;
-
- g_return_if_fail (str != NULL);
- g_return_if_fail (add_func != NULL);
-
- p = unescaped = g_strdup (str);
- do {
- p = strstr (p, "\\032");
- if (!p)
- break;
-
- /* Clear the escaped space with real spaces */
- for (i = 0; i < 4; i++)
- *p++ = ' ';
- } while (*p++);
-
- if (strchr (unescaped, '\\')) {
- nm_log_warn (LOGD_DHCP, " invalid domain search: '%s'", unescaped);
- goto out;
- }
-
- searches = g_strsplit (unescaped, " ", 0);
- for (s = searches; *s; s++) {
- if (strlen (*s)) {
- nm_log_info (LOGD_DHCP, " domain search '%s'", *s);
- add_func (*s, user_data);
- }
- }
- g_strfreev (searches);
-
-out:
- g_free (unescaped);
-}
-
-static void
-ip4_add_domain_search (gpointer data, gpointer user_data)
-{
- nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data);
-}
-
-/* Given a table of DHCP options from the client, convert into an IP4Config */
-static NMIP4Config *
-ip4_options_to_config (NMDHCPClient *self)
-{
- NMDHCPClientPrivate *priv;
- NMIP4Config *ip4_config = NULL;
- guint32 tmp_addr;
- NMPlatformIP4Address address;
- char *str = NULL;
- guint32 gwaddr = 0, plen = 0;
-
- g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
- priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- g_return_val_if_fail (priv->options != NULL, NULL);
-
- ip4_config = nm_ip4_config_new ();
- memset (&address, 0, sizeof (address));
- address.timestamp = nm_utils_get_monotonic_timestamp_s ();
-
- str = g_hash_table_lookup (priv->options, "new_ip_address");
- if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
- address.address = tmp_addr;
- nm_log_info (LOGD_DHCP4, " address %s", str);
- } else
- goto error;
-
- str = g_hash_table_lookup (priv->options, "new_subnet_mask");
- if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
- plen = nm_utils_ip4_netmask_to_prefix (tmp_addr);
- nm_log_info (LOGD_DHCP4, " plen %d (%s)", plen, str);
- } else {
- /* Get default netmask for the IP according to appropriate class. */
- plen = nm_utils_ip4_get_default_prefix (address.address);
- nm_log_info (LOGD_DHCP4, " plen %d (default)", plen);
- }
- address.plen = plen;
-
- /* Routes: if the server returns classless static routes, we MUST ignore
- * the 'static_routes' option.
- */
- if (!ip4_process_classless_routes (self, priv->options, ip4_config, &gwaddr))
- process_classful_routes (self, priv->options, ip4_config);
-
- if (gwaddr) {
- nm_log_info (LOGD_DHCP4, " gateway %s", nm_utils_inet4_ntop (gwaddr, NULL));
- nm_ip4_config_set_gateway (ip4_config, gwaddr);
- } else {
- /* If the gateway wasn't provided as a classless static route with a
- * subnet length of 0, try to find it using the old-style 'routers' option.
- */
- str = g_hash_table_lookup (priv->options, "new_routers");
- if (str) {
- char **routers = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = routers; *s; s++) {
- /* FIXME: how to handle multiple routers? */
- if (inet_pton (AF_INET, *s, &gwaddr) > 0) {
- nm_ip4_config_set_gateway (ip4_config, gwaddr);
- nm_log_info (LOGD_DHCP4, " gateway %s", *s);
- break;
- } else
- nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s);
- }
- g_strfreev (routers);
- }
- }
-
- /*
- * RFC 2132, section 9.7
- * DHCP clients use the contents of the 'server identifier' field
- * as the destination address for any DHCP messages unicast to
- * the DHCP server.
- *
- * Some ISP's provide leases from central servers that are on
- * different subnets that the address offered. If the host
- * does not configure the interface as the default route, the
- * dhcp server may not be reachable via unicast, and a host
- * specific route is needed.
- **/
- str = g_hash_table_lookup (priv->options, "new_dhcp_server_identifier");
- if (str) {
- if (inet_pton (AF_INET, str, &tmp_addr) > 0) {
- NMPlatformIP4Route route;
- guint32 mask = nm_utils_ip4_prefix_to_netmask (address.plen);
-
- nm_log_info (LOGD_DHCP4, " server identifier %s", str);
- if ((tmp_addr & mask) != (address.address & mask)) {
- /* DHCP server not on assigned subnet, route needed */
- memset (&route, 0, sizeof (route));
- route.network = tmp_addr;
- route.plen = 32;
- /* this will be a device route if gwaddr is 0 */
- route.gateway = gwaddr;
- route.source = NM_PLATFORM_SOURCE_DHCP;
- route.metric = priv->priority;
- nm_ip4_config_add_route (ip4_config, &route);
- nm_log_dbg (LOGD_IP, "adding route for server identifier: %s",
- nm_platform_ip4_route_to_string (&route));
+ new_state = reason_to_state (priv->iface, reason);
+
+ if (new_state == NM_DHCP_STATE_BOUND) {
+ /* Copy options */
+ str_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ g_hash_table_foreach (options, (GHFunc) copy_option, str_options);
+
+ /* Create the IP config */
+ g_warn_if_fail (g_hash_table_size (str_options));
+ if (g_hash_table_size (str_options)) {
+ if (priv->ipv6) {
+ ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (priv->iface,
+ str_options,
+ priv->priority,
+ priv->info_only);
+ } else {
+ ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (priv->iface,
+ str_options,
+ priv->priority);
}
+ g_warn_if_fail (ip_config != NULL);
}
- else
- nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str);
- }
-
- str = g_hash_table_lookup (priv->options, "new_dhcp_lease_time");
- if (str) {
- address.lifetime = address.preferred = strtoul (str, NULL, 10);
- nm_log_info (LOGD_DHCP4, " lease time %d", address.lifetime);
- }
-
- address.source = NM_PLATFORM_SOURCE_DHCP;
- nm_ip4_config_add_address (ip4_config, &address);
-
- str = g_hash_table_lookup (priv->options, "new_host_name");
- if (str)
- nm_log_info (LOGD_DHCP4, " hostname '%s'", str);
-
- str = g_hash_table_lookup (priv->options, "new_domain_name_servers");
- if (str) {
- char **searches = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = searches; *s; s++) {
- if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
- nm_ip4_config_add_nameserver (ip4_config, tmp_addr);
- nm_log_info (LOGD_DHCP4, " nameserver '%s'", *s);
- } else
- nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s);
- }
- g_strfreev (searches);
- }
-
- str = g_hash_table_lookup (priv->options, "new_domain_name");
- if (str) {
- char **domains = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = domains; *s; s++) {
- nm_log_info (LOGD_DHCP4, " domain name '%s'", *s);
- nm_ip4_config_add_domain (ip4_config, *s);
- }
- g_strfreev (domains);
- }
-
- str = g_hash_table_lookup (priv->options, "new_domain_search");
- if (str)
- process_domain_search (str, ip4_add_domain_search, ip4_config);
-
- str = g_hash_table_lookup (priv->options, "new_netbios_name_servers");
- if (str) {
- char **searches = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = searches; *s; s++) {
- if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
- nm_ip4_config_add_wins (ip4_config, tmp_addr);
- nm_log_info (LOGD_DHCP4, " wins '%s'", *s);
- } else
- nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s);
- }
- g_strfreev (searches);
- }
-
- str = g_hash_table_lookup (priv->options, "new_interface_mtu");
- if (str) {
- int int_mtu;
-
- errno = 0;
- int_mtu = strtol (str, NULL, 10);
- if ((errno == EINVAL) || (errno == ERANGE))
- goto error;
-
- if (int_mtu > 576)
- nm_ip4_config_set_mtu (ip4_config, int_mtu);
- }
-
- str = g_hash_table_lookup (priv->options, "new_nis_domain");
- if (str) {
- nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str);
- nm_ip4_config_set_nis_domain (ip4_config, str);
- }
-
- str = g_hash_table_lookup (priv->options, "new_nis_servers");
- if (str) {
- char **searches = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = searches; *s; s++) {
- if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
- nm_ip4_config_add_nis_server (ip4_config, tmp_addr);
- nm_log_info (LOGD_DHCP4, " nis '%s'", *s);
- } else
- nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
- }
- g_strfreev (searches);
- }
-
- return ip4_config;
-
-error:
- g_object_unref (ip4_config);
- return NULL;
-}
-
-NMIP4Config *
-nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test)
-{
- NMDHCPClientPrivate *priv;
-
- g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
- priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
- if (test && !state_is_bound (priv->state)) {
- nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
- return NULL;
- }
-
- if (!g_hash_table_size (priv->options)) {
- /* We never got a response from the DHCP client */
- return NULL;
- }
-
- return ip4_options_to_config (self);
-}
-
-/********************************************/
-
-static void
-ip6_add_domain_search (gpointer data, gpointer user_data)
-{
- nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
-}
-
-/* Given a table of DHCP options from the client, convert into an IP6Config */
-static NMIP6Config *
-ip6_options_to_config (NMDHCPClient *self)
-{
- NMDHCPClientPrivate *priv;
- NMIP6Config *ip6_config = NULL;
- struct in6_addr tmp_addr;
- NMPlatformIP6Address address;
- char *str = NULL;
- GHashTableIter iter;
- gpointer key, value;
-
- g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
- memset (&address, 0, sizeof (address));
- address.plen = 128;
- address.timestamp = nm_utils_get_monotonic_timestamp_s ();
-
- priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
- g_return_val_if_fail (priv->options != NULL, NULL);
-
- g_hash_table_iter_init (&iter, priv->options);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'",
- priv->iface, (const char *) key, (const char *) value);
- }
-
- ip6_config = nm_ip6_config_new ();
-
- str = g_hash_table_lookup (priv->options, "new_max_life");
- if (str) {
- address.lifetime = strtoul (str, NULL, 10);
- nm_log_info (LOGD_DHCP6, " valid_lft %d", address.lifetime);
- }
-
- str = g_hash_table_lookup (priv->options, "new_preferred_life");
- if (str) {
- address.preferred = strtoul (str, NULL, 10);
- nm_log_info (LOGD_DHCP6, " preferred_lft %d", address.preferred);
- }
-
- str = g_hash_table_lookup (priv->options, "new_ip6_address");
- if (str) {
- if (!inet_pton (AF_INET6, str, &tmp_addr)) {
- nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'",
- priv->iface, str);
- goto error;
- }
-
- address.address = tmp_addr;
- address.source = NM_PLATFORM_SOURCE_DHCP;
- nm_ip6_config_add_address (ip6_config, &address);
- nm_log_info (LOGD_DHCP6, " address %s", str);
- } else if (priv->info_only == FALSE) {
- /* No address in Managed mode is a hard error */
- goto error;
- }
-
- str = g_hash_table_lookup (priv->options, "new_host_name");
- if (str)
- nm_log_info (LOGD_DHCP6, " hostname '%s'", str);
-
- str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers");
- if (str) {
- char **searches = g_strsplit (str, " ", 0);
- char **s;
-
- for (s = searches; *s; s++) {
- if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
- nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
- nm_log_info (LOGD_DHCP6, " nameserver '%s'", *s);
- } else
- nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s);
- }
- g_strfreev (searches);
- }
-
- str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search");
- if (str)
- process_domain_search (str, ip6_add_domain_search, ip6_config);
-
- return ip6_config;
-
-error:
- g_object_unref (ip6_config);
- return NULL;
-}
-
-NMIP6Config *
-nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test)
-{
- NMDHCPClientPrivate *priv;
-
- g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
- priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
- if (test && !state_is_bound (priv->state)) {
- nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
- return NULL;
}
- if (!g_hash_table_size (priv->options)) {
- /* We never got a response from the DHCP client */
- return NULL;
- }
+ nm_dhcp_client_set_state (self, new_state, ip_config, str_options);
- return ip6_options_to_config (self);
+ if (str_options)
+ g_hash_table_destroy (str_options);
+ g_clear_object (&ip_config);
}
/********************************************/
@@ -1451,10 +709,7 @@ nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test)
static void
nm_dhcp_client_init (NMDHCPClient *self)
{
- NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
- priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- priv->pid = -1;
+ NM_DHCP_CLIENT_GET_PRIVATE (self)->pid = -1;
}
static void
@@ -1467,6 +722,9 @@ get_property (GObject *object, guint prop_id,
case PROP_IFACE:
g_value_set_string (value, priv->iface);
break;
+ case PROP_IFINDEX:
+ g_value_set_int (value, priv->ifindex);
+ break;
case PROP_HWADDR:
g_value_set_boxed (value, priv->hwaddr);
break;
@@ -1499,6 +757,11 @@ set_property (GObject *object, guint prop_id,
/* construct-only */
priv->iface = g_strdup (g_value_get_string (value));
break;
+ case PROP_IFINDEX:
+ /* construct-only */
+ priv->ifindex = g_value_get_int (value);
+ g_warn_if_fail (priv->ifindex > 0);
+ break;
case PROP_HWADDR:
/* construct only */
priv->hwaddr = g_value_dup_boxed (value);
@@ -1535,15 +798,9 @@ dispose (GObject *object)
* the DHCP client.
*/
- if (priv->remove_id) {
- g_source_remove (priv->remove_id);
- priv->remove_id = 0;
- }
+ watch_cleanup (self);
+ timeout_cleanup (self);
- if (priv->options) {
- g_hash_table_destroy (priv->options);
- priv->options = NULL;
- }
g_clear_pointer (&priv->iface, g_free);
if (priv->hwaddr) {
@@ -1582,6 +839,12 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class)
G_PARAM_STATIC_STRINGS));
g_object_class_install_property
+ (object_class, PROP_IFINDEX,
+ g_param_spec_int (NM_DHCP_CLIENT_IFINDEX, "", "",
+ -1, G_MAXINT, -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
(object_class, PROP_HWADDR,
g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, "", "",
G_TYPE_BYTE_ARRAY,
@@ -1622,26 +885,7 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class)
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMDHCPClientClass, state_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
-
- signals[SIGNAL_TIMEOUT] =
- g_signal_new (NM_DHCP_CLIENT_SIGNAL_TIMEOUT,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMDHCPClientClass, timeout),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[SIGNAL_REMOVE] =
- g_signal_new (NM_DHCP_CLIENT_SIGNAL_REMOVE,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMDHCPClientClass, remove),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE);
}
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
index ad18926af9..d01d938a2d 100644
--- a/src/dhcp-manager/nm-dhcp-client.h
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -35,43 +35,24 @@
#define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass))
#define NM_DHCP_CLIENT_INTERFACE "iface"
+#define NM_DHCP_CLIENT_IFINDEX "ifindex"
#define NM_DHCP_CLIENT_HWADDR "hwaddr"
#define NM_DHCP_CLIENT_IPV6 "ipv6"
#define NM_DHCP_CLIENT_UUID "uuid"
#define NM_DHCP_CLIENT_PRIORITY "priority"
#define NM_DHCP_CLIENT_TIMEOUT "timeout"
-#define NM_DHCP_CLIENT_SIGNAL_TIMEOUT "timeout"
#define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed"
-#define NM_DHCP_CLIENT_SIGNAL_REMOVE "remove"
typedef enum {
- DHC_NBI = 0, /* no broadcast interfaces found */
- DHC_PREINIT, /* configuration started */
- DHC_PREINIT6, /* configuration started */
- DHC_BOUND4, /* IPv4 lease obtained */
- DHC_BOUND6, /* IPv6 lease obtained */
- DHC_IPV4LL, /* IPv4LL address obtained */
- DHC_RENEW4, /* IPv4 lease renewed */
- DHC_RENEW6, /* IPv6 lease renewed */
- DHC_REBOOT, /* have valid lease, but now obtained a different one */
- DHC_REBIND4, /* IPv4 new/different lease */
- DHC_REBIND6, /* IPv6 new/different lease */
- DHC_DEPREF6, /* IPv6 lease depreferred */
- DHC_STOP, /* remove old lease */
- DHC_STOP6, /* remove old lease */
- DHC_MEDIUM, /* media selection begun */
- DHC_TIMEOUT, /* timed out contacting DHCP server */
- DHC_FAIL, /* all attempts to contact server timed out, sleeping */
- DHC_EXPIRE, /* lease has expired, renewing */
- DHC_EXPIRE6, /* lease has expired, renewing */
- DHC_RELEASE, /* releasing lease */
- DHC_RELEASE6, /* releasing lease */
- DHC_START, /* sent when dhclient started OK */
- DHC_ABEND, /* dhclient exited abnormally */
- DHC_END, /* dhclient exited normally */
- DHC_END_OPTIONS, /* last option in subscription sent */
-} NMDHCPState;
+ NM_DHCP_STATE_UNKNOWN = 0,
+ NM_DHCP_STATE_BOUND, /* lease changed (state_is_bound) */
+ NM_DHCP_STATE_TIMEOUT, /* TIMEOUT */
+ NM_DHCP_STATE_DONE, /* END */
+ NM_DHCP_STATE_FAIL, /* failed or quit unexpectedly */
+ __NM_DHCP_STATE_MAX,
+ NM_DHCP_STATE_MAX = __NM_DHCP_STATE_MAX - 1,
+} NMDhcpState;
typedef struct {
GObject parent;
@@ -82,20 +63,21 @@ typedef struct {
/* Methods */
- GPid (*ip4_start) (NMDHCPClient *self,
- const char *dhcp_client_id,
- GByteArray *anycast_addr,
- const char *hostname);
+ gboolean (*ip4_start) (NMDHCPClient *self,
+ const char *dhcp_client_id,
+ GByteArray *anycast_addr,
+ const char *hostname);
- GPid (*ip6_start) (NMDHCPClient *self,
- GByteArray *anycast_addr,
- const char *hostname,
- gboolean info_only,
- const GByteArray *duid);
+ gboolean (*ip6_start) (NMDHCPClient *self,
+ GByteArray *anycast_addr,
+ const char *hostname,
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy,
+ const GByteArray *duid);
- void (*stop) (NMDHCPClient *self,
- gboolean release,
- const GByteArray *duid);
+ void (*stop) (NMDHCPClient *self,
+ gboolean release,
+ const GByteArray *duid);
/**
* get_duid:
@@ -109,21 +91,30 @@ typedef struct {
GByteArray * (*get_duid) (NMDHCPClient *self);
/* Signals */
- void (*state_changed) (NMDHCPClient *self, NMDHCPState state);
- void (*timeout) (NMDHCPClient *self);
- void (*remove) (NMDHCPClient *self);
+ void (*state_changed) (NMDHCPClient *self,
+ NMDhcpState state,
+ GObject *ip_config,
+ GHashTable *options);
} NMDHCPClientClass;
GType nm_dhcp_client_get_type (void);
-GPid nm_dhcp_client_get_pid (NMDHCPClient *self);
+pid_t nm_dhcp_client_get_pid (NMDHCPClient *self);
const char *nm_dhcp_client_get_iface (NMDHCPClient *self);
+int nm_dhcp_client_get_ifindex (NMDHCPClient *self);
+
gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self);
const char *nm_dhcp_client_get_uuid (NMDHCPClient *self);
+const GByteArray *nm_dhcp_client_get_duid (NMDHCPClient *self);
+
+const GByteArray *nm_dhcp_client_get_hw_addr (NMDHCPClient *self);
+
+guint32 nm_dhcp_client_get_priority (NMDHCPClient *self);
+
gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self,
const char *dhcp_client_id,
GByteArray *dhcp_anycast_addr,
@@ -132,7 +123,8 @@ gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self,
gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self,
GByteArray *dhcp_anycast_addr,
const char *hostname,
- gboolean info_only);
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy);
void nm_dhcp_client_stop (NMDHCPClient *self, gboolean release);
@@ -140,18 +132,17 @@ void nm_dhcp_client_new_options (NMDHCPClient *self,
GHashTable *options,
const char *reason);
-gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self,
- GHFunc func,
- gpointer user_data);
-
-NMIP4Config *nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test);
+/* Backend helpers for subclasses */
+void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
-NMIP6Config *nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test);
+void nm_dhcp_client_stop_pid (pid_t pid, const char *iface);
-/* Backend helpers */
-void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
+void nm_dhcp_client_watch_child (NMDHCPClient *self, pid_t pid);
-void nm_dhcp_client_stop_pid (GPid pid, const char *iface);
+void nm_dhcp_client_set_state (NMDHCPClient *self,
+ NMDhcpState new_state,
+ GObject *ip_config, /* NMIP4Config or NMIP6Config */
+ GHashTable *options); /* str:str hash */
#endif /* NM_DHCP_CLIENT_H */
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index 4232ee0cce..2f1e11deba 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -324,7 +324,7 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED)
nm_unblock_posix_signals (NULL);
}
-static GPid
+static gboolean
dhclient_start (NMDHCPClient *client,
const char *mode_opt,
const GByteArray *duid,
@@ -332,7 +332,7 @@ dhclient_start (NMDHCPClient *client,
{
NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
GPtrArray *argv = NULL;
- GPid pid = -1;
+ pid_t pid;
GError *error = NULL;
const char *iface, *uuid, *system_bus_address;
char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL;
@@ -340,7 +340,7 @@ dhclient_start (NMDHCPClient *client,
guint log_domain;
char *escaped, *preferred_leasefile_path = NULL;
- g_return_val_if_fail (priv->pid_file == NULL, -1);
+ g_return_val_if_fail (priv->pid_file == NULL, FALSE);
iface = nm_dhcp_client_get_iface (client);
uuid = nm_dhcp_client_get_uuid (client);
@@ -350,7 +350,7 @@ dhclient_start (NMDHCPClient *client,
if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
nm_log_warn (log_domain, "%s does not exist.", priv->path);
- return -1;
+ return FALSE;
}
pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
@@ -404,7 +404,8 @@ dhclient_start (NMDHCPClient *client,
iface, priv->lease_file,
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
- return -1;
+ g_free (pid_file);
+ return FALSE;
}
}
@@ -456,22 +457,24 @@ dhclient_start (NMDHCPClient *client,
nm_log_dbg (log_domain, "running: %s", cmd_str);
g_free (cmd_str);
- if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+ if (g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
&dhclient_child_setup, NULL, &pid, &error)) {
- nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
- g_error_free (error);
- pid = -1;
- } else {
+ g_assert (pid > 0);
nm_log_info (log_domain, "dhclient started with pid %d", pid);
+ nm_dhcp_client_watch_child (client, pid);
priv->pid_file = pid_file;
+ } else {
+ nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
+ g_error_free (error);
+ g_free (pid_file);
}
g_ptr_array_free (argv, TRUE);
g_free (system_bus_address_env);
- return pid;
+ return pid > 0 ? TRUE : FALSE;
}
-static GPid
+static gboolean
ip4_start (NMDHCPClient *client,
const char *dhcp_client_id,
GByteArray *dhcp_anycast_addr,
@@ -486,17 +489,18 @@ ip4_start (NMDHCPClient *client,
priv->conf_file = create_dhclient_config (iface, FALSE, uuid, dhcp_client_id, dhcp_anycast_addr, hostname);
if (!priv->conf_file) {
nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
- return -1;
+ return FALSE;
}
return dhclient_start (client, NULL, NULL, FALSE);
}
-static GPid
+static gboolean
ip6_start (NMDHCPClient *client,
GByteArray *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
{
NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
@@ -508,7 +512,7 @@ ip6_start (NMDHCPClient *client,
priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname);
if (!priv->conf_file) {
nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
- return -1;
+ return FALSE;
}
return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE);
@@ -533,7 +537,7 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
}
if (release) {
- GPid rpid;
+ pid_t rpid;
rpid = dhclient_start (client, NULL, duid, TRUE);
if (rpid > 0) {
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index acfbee2692..5221f4362a 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -86,7 +86,7 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
nm_unblock_posix_signals (NULL);
}
-static GPid
+static gboolean
ip4_start (NMDHCPClient *client,
const char *dhcp_client_id,
GByteArray *dhcp_anycast_addr,
@@ -94,12 +94,12 @@ ip4_start (NMDHCPClient *client,
{
NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
GPtrArray *argv = NULL;
- GPid pid = -1;
+ pid_t pid = -1;
GError *error = NULL;
char *pid_contents = NULL, *binary_name, *cmd_str;
const char *iface;
- g_return_val_if_fail (priv->pid_file == NULL, -1);
+ g_return_val_if_fail (priv->pid_file == NULL, FALSE);
iface = nm_dhcp_client_get_iface (client);
@@ -110,7 +110,7 @@ ip4_start (NMDHCPClient *client,
if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
nm_log_warn (LOGD_DHCP4, "%s does not exist.", priv->path);
- return -1;
+ return FALSE;
}
/* Kill any existing dhcpcd from the pidfile */
@@ -152,28 +152,31 @@ ip4_start (NMDHCPClient *client,
nm_log_dbg (LOGD_DHCP4, "running: %s", cmd_str);
g_free (cmd_str);
- if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
- &dhcpcd_child_setup, NULL, &pid, &error)) {
+ if (g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+ &dhcpcd_child_setup, NULL, &pid, &error)) {
+ g_assert (pid > 0);
+ nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid);
+ nm_dhcp_client_watch_child (client, pid);
+ } else {
nm_log_warn (LOGD_DHCP4, "dhcpcd failed to start. error: '%s'", error->message);
g_error_free (error);
- pid = -1;
- } else
- nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid);
+ }
g_free (pid_contents);
g_ptr_array_free (argv, TRUE);
- return pid;
+ return pid > 0 ? TRUE : FALSE;
}
-static GPid
+static gboolean
ip6_start (NMDHCPClient *client,
GByteArray *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
{
nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6.");
- return -1;
+ return FALSE;
}
static void
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 195d50b074..c4de434976 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -142,16 +142,14 @@ get_client_for_pid (NMDHCPManager *manager, GPid pid)
}
static NMDHCPClient *
-get_client_for_iface (NMDHCPManager *manager,
- const char *iface,
- gboolean ip6)
+get_client_for_ifindex (NMDHCPManager *manager, int ifindex, gboolean ip6)
{
NMDHCPManagerPrivate *priv;
GHashTableIter iter;
gpointer value;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
- g_return_val_if_fail (iface, NULL);
+ g_return_val_if_fail (ifindex > 0, NULL);
priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
@@ -159,8 +157,8 @@ get_client_for_iface (NMDHCPManager *manager,
while (g_hash_table_iter_next (&iter, NULL, &value)) {
NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
- if ( !strcmp (iface, nm_dhcp_client_get_iface (candidate))
- && (nm_dhcp_client_get_ipv6 (candidate) == ip6))
+ if ( nm_dhcp_client_get_ifindex (candidate) == ifindex
+ && nm_dhcp_client_get_ipv6 (candidate) == ip6)
return candidate;
}
@@ -339,49 +337,40 @@ get_client_type (const char *client, GError **error)
return G_TYPE_INVALID;
}
-#define REMOVE_ID_TAG "remove-id"
-#define TIMEOUT_ID_TAG "timeout-id"
+static void client_state_changed (NMDHCPClient *client,
+ NMDhcpState state,
+ GObject *ip_config,
+ GHashTable *options,
+ NMDHCPManager *self);
static void
remove_client (NMDHCPManager *self, NMDHCPClient *client)
{
- NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- guint id;
-
- id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), REMOVE_ID_TAG));
- if (id)
- g_signal_handler_disconnect (client, id);
-
- id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG));
- if (id)
- g_signal_handler_disconnect (client, id);
+ g_signal_handlers_disconnect_by_func (client, client_state_changed, self);
/* Stopping the client is left up to the controlling device
* explicitly since we may want to quit NetworkManager but not terminate
* the DHCP client.
*/
- g_hash_table_remove (priv->clients, client);
+ g_hash_table_remove (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client);
}
static void
-add_client (NMDHCPManager *self, NMDHCPClient *client)
+client_state_changed (NMDHCPClient *client,
+ NMDhcpState state,
+ GObject *ip_config,
+ GHashTable *options,
+ NMDHCPManager *self)
{
- NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- guint id;
-
- id = g_signal_connect_swapped (client, NM_DHCP_CLIENT_SIGNAL_REMOVE, G_CALLBACK (remove_client), self);
- g_object_set_data (G_OBJECT (client), REMOVE_ID_TAG, GUINT_TO_POINTER (id));
-
- id = g_signal_connect_swapped (client, NM_DHCP_CLIENT_SIGNAL_TIMEOUT, G_CALLBACK (remove_client), self);
- g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id));
-
- g_hash_table_insert (priv->clients, client, g_object_ref (client));
+ if (state >= NM_DHCP_STATE_TIMEOUT)
+ remove_client (self, client);
}
static NMDHCPClient *
client_start (NMDHCPManager *self,
const char *iface,
+ int ifindex,
const GByteArray *hwaddr,
const char *uuid,
guint priority,
@@ -390,7 +379,8 @@ client_start (NMDHCPManager *self,
guint32 timeout,
GByteArray *dhcp_anycast_addr,
const char *hostname,
- gboolean info_only)
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy)
{
NMDHCPManagerPrivate *priv;
NMDHCPClient *client;
@@ -398,7 +388,7 @@ client_start (NMDHCPManager *self,
g_return_val_if_fail (self, NULL);
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
- g_return_val_if_fail (iface != NULL, NULL);
+ g_return_val_if_fail (ifindex > 0, NULL);
g_return_val_if_fail (uuid != NULL, NULL);
priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
@@ -407,32 +397,34 @@ client_start (NMDHCPManager *self,
g_return_val_if_fail (priv->client_type != 0, NULL);
/* Kill any old client instance */
- client = get_client_for_iface (self, iface, ipv6);
+ client = get_client_for_ifindex (self, ifindex, ipv6);
if (client) {
- nm_dhcp_client_stop (client, FALSE);
+ g_object_ref (client);
remove_client (self, client);
+ nm_dhcp_client_stop (client, FALSE);
+ g_object_unref (client);
}
/* And make a new one */
client = g_object_new (priv->client_type,
NM_DHCP_CLIENT_INTERFACE, iface,
+ NM_DHCP_CLIENT_IFINDEX, ifindex,
NM_DHCP_CLIENT_HWADDR, hwaddr,
NM_DHCP_CLIENT_IPV6, ipv6,
NM_DHCP_CLIENT_UUID, uuid,
NM_DHCP_CLIENT_PRIORITY, priority,
NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
NULL);
- g_return_val_if_fail (client != NULL, NULL);
- add_client (self, client);
+ g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client));
+ g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self);
if (ipv6)
- success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only);
+ success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only, privacy);
else
success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname);
if (!success) {
remove_client (self, client);
- g_object_unref (client);
client = NULL;
}
@@ -452,51 +444,50 @@ get_send_hostname (NMDHCPManager *self, const char *setting_hostname)
NMDHCPClient *
nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
const char *iface,
+ int ifindex,
const GByteArray *hwaddr,
const char *uuid,
guint priority,
- NMSettingIP4Config *s_ip4,
+ gboolean send_hostname,
+ const char *dhcp_hostname,
+ const char *dhcp_client_id,
guint32 timeout,
GByteArray *dhcp_anycast_addr)
{
- const char *hostname = NULL, *method;
- gboolean send_hostname;
+ const char *hostname = NULL;
- g_return_val_if_fail (self, NULL);
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
- method = nm_setting_ip4_config_get_method (s_ip4);
- g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL);
-
- send_hostname = nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4);
if (send_hostname)
- hostname = get_send_hostname (self, nm_setting_ip4_config_get_dhcp_hostname (s_ip4));
-
- return client_start (self, iface, hwaddr, uuid, priority, FALSE,
- nm_setting_ip4_config_get_dhcp_client_id (s_ip4),
- timeout, dhcp_anycast_addr, hostname, FALSE);
+ hostname = get_send_hostname (self, dhcp_hostname);
+ return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE,
+ dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
+ FALSE, 0);
}
/* Caller owns a reference to the NMDHCPClient on return */
NMDHCPClient *
nm_dhcp_manager_start_ip6 (NMDHCPManager *self,
const char *iface,
+ int ifindex,
const GByteArray *hwaddr,
const char *uuid,
guint priority,
- NMSettingIP6Config *s_ip6,
+ const char *dhcp_hostname,
guint32 timeout,
GByteArray *dhcp_anycast_addr,
- gboolean info_only)
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy)
{
const char *hostname;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
- hostname = get_send_hostname (self, nm_setting_ip6_config_get_dhcp_hostname (s_ip6));
+ hostname = dhcp_hostname ? get_send_hostname (self, dhcp_hostname) : NULL;
- return client_start (self, iface, hwaddr, uuid, priority, TRUE,
- NULL, timeout, dhcp_anycast_addr, hostname, info_only);
+ return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
+ NULL, timeout, dhcp_anycast_addr, hostname, info_only,
+ privacy);
}
void
@@ -532,35 +523,6 @@ nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
return NULL;
}
-NMIP4Config *
-nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
- const char *iface,
- GHashTable *options,
- const char *reason)
-{
- NMDHCPClient *client;
- NMIP4Config *config;
- GType client_type;
- GError *error = NULL;
-
- client_type = get_client_type (dhcp_client, &error);
- if (!client_type) {
- nm_log_err (LOGD_DHCP4, "error: %s", error ? error->message : "(unknown)");
- g_clear_error (&error);
- return NULL;
- }
-
- client = (NMDHCPClient *) g_object_new (client_type,
- NM_DHCP_CLIENT_INTERFACE, iface,
- NULL);
- g_return_val_if_fail (client != NULL, NULL);
- nm_dhcp_client_new_options (client, options, reason);
- config = nm_dhcp_client_get_ip4_config (client, TRUE);
- g_object_unref (client);
-
- return config;
-}
-
/***************************************************/
NMDHCPManager *
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
index efdd43788b..de162abab3 100644
--- a/src/dhcp-manager/nm-dhcp-manager.h
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -25,9 +25,6 @@
#include <glib.h>
#include <glib-object.h>
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-ip6-config.h>
-
#include "nm-dhcp-client.h"
#include "nm-ip4-config.h"
#include "nm-dhcp4-config.h"
@@ -66,22 +63,27 @@ void nm_dhcp_manager_set_default_hostname (NMDHCPManager *manager,
NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *manager,
const char *iface,
+ int ifindex,
const GByteArray *hwaddr,
const char *uuid,
guint priority,
- NMSettingIP4Config *s_ip4,
+ gboolean send_hostname,
+ const char *dhcp_hostname,
+ const char *dhcp_client_id,
guint32 timeout,
GByteArray *dhcp_anycast_addr);
NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager,
const char *iface,
+ int ifindex,
const GByteArray *hwaddr,
const char *uuid,
guint priority,
- NMSettingIP6Config *s_ip6,
+ const char *dhcp_hostname,
guint32 timeout,
GByteArray *dhcp_anycast_addr,
- gboolean info_only);
+ gboolean info_only,
+ NMSettingIP6ConfigPrivacy privacy);
GSList * nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
const char *iface,
@@ -89,11 +91,6 @@ GSList * nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
gboolean ipv6);
/* For testing only */
-NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
- const char *iface,
- GHashTable *options,
- const char *reason);
-
extern const char* nm_dhcp_helper_path;
#endif /* NM_DHCP_MANAGER_H */
diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c
new file mode 100644
index 0000000000..48330e5113
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-utils.c
@@ -0,0 +1,685 @@
+/* -*- 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.
+ *
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "nm-logging.h"
+#include "nm-dhcp-utils.h"
+#include "nm-utils.h"
+#include "NetworkManagerUtils.h"
+
+/********************************************/
+
+static gboolean
+ip4_process_dhcpcd_rfc3442_routes (const char *str,
+ guint priority,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
+{
+ char **routes, **r;
+ gboolean have_routes = FALSE;
+
+ routes = g_strsplit (str, " ", 0);
+ if (g_strv_length (routes) == 0)
+ goto out;
+
+ if ((g_strv_length (routes) % 2) != 0) {
+ nm_log_warn (LOGD_DHCP4, " classless static routes provided, but invalid");
+ goto out;
+ }
+
+ for (r = routes; *r; r += 2) {
+ char *slash;
+ NMPlatformIP4Route route;
+ int rt_cidr = 32;
+ guint32 rt_addr, rt_route;
+
+ slash = strchr(*r, '/');
+ if (slash) {
+ *slash = '\0';
+ errno = 0;
+ rt_cidr = strtol (slash + 1, NULL, 10);
+ if ((errno == EINVAL) || (errno == ERANGE)) {
+ nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
+ continue;
+ }
+ }
+ if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
+ nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
+ continue;
+ }
+ if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
+ nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
+ continue;
+ }
+
+ have_routes = TRUE;
+ if (rt_cidr == 0 && rt_addr == 0) {
+ /* FIXME: how to handle multiple routers? */
+ *gwaddr = rt_route;
+ } else {
+ nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
+ memset (&route, 0, sizeof (route));
+ route.network = rt_addr;
+ route.plen = rt_cidr;
+ route.gateway = rt_route;
+ route.source = NM_PLATFORM_SOURCE_DHCP;
+ route.metric = priority;
+ nm_ip4_config_add_route (ip4_config, &route);
+ }
+ }
+
+out:
+ g_strfreev (routes);
+ return have_routes;
+}
+
+static const char **
+process_dhclient_rfc3442_route (const char **octets,
+ NMPlatformIP4Route *route,
+ gboolean *success)
+{
+ const char **o = octets;
+ int addr_len = 0, i = 0;
+ long int tmp;
+ char *next_hop;
+ guint32 tmp_addr;
+
+ *success = FALSE;
+
+ if (!*o)
+ return o; /* no prefix */
+
+ tmp = strtol (*o, NULL, 10);
+ if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */
+ return o;
+
+ memset (route, 0, sizeof (*route));
+ route->plen = tmp;
+ o++;
+
+ if (tmp > 0)
+ addr_len = ((tmp - 1) / 8) + 1;
+
+ /* ensure there's at least the address + next hop left */
+ if (g_strv_length ((char **) o) < addr_len + 4)
+ goto error;
+
+ if (tmp) {
+ const char *addr[4] = { "0", "0", "0", "0" };
+ char *str_addr;
+
+ for (i = 0; i < addr_len; i++)
+ addr[i] = *o++;
+
+ str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
+ if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
+ g_free (str_addr);
+ goto error;
+ }
+ tmp_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
+ route->network = tmp_addr;
+ }
+
+ /* Handle next hop */
+ next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
+ if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
+ g_free (next_hop);
+ goto error;
+ }
+ route->gateway = tmp_addr;
+ g_free (next_hop);
+
+ *success = TRUE;
+ return o + 4; /* advance to past the next hop */
+
+error:
+ return o;
+}
+
+static gboolean
+ip4_process_dhclient_rfc3442_routes (const char *str,
+ guint priority,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
+{
+ char **octets, **o;
+ gboolean have_routes = FALSE;
+ NMPlatformIP4Route route;
+ gboolean success;
+
+ o = octets = g_strsplit_set (str, " .", 0);
+ if (g_strv_length (octets) < 5) {
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+ goto out;
+ }
+
+ while (*o) {
+ memset (&route, 0, sizeof (route));
+ o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success);
+ if (!success) {
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
+ break;
+ }
+
+ have_routes = TRUE;
+ if (!route.plen) {
+ /* gateway passed as classless static route */
+ *gwaddr = route.gateway;
+ } else {
+ char addr[INET_ADDRSTRLEN];
+
+ /* normal route */
+ route.source = NM_PLATFORM_SOURCE_DHCP;
+ route.metric = priority;
+ nm_ip4_config_add_route (ip4_config, &route);
+
+ nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s",
+ nm_utils_inet4_ntop (route.network, addr), route.plen,
+ nm_utils_inet4_ntop (route.gateway, NULL));
+ }
+ }
+
+out:
+ g_strfreev (octets);
+ return have_routes;
+}
+
+static gboolean
+ip4_process_classless_routes (GHashTable *options,
+ guint priority,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
+{
+ const char *str, *p;
+
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (ip4_config != NULL, FALSE);
+
+ *gwaddr = 0;
+
+ /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
+ * slightly different format:
+ *
+ * option classless-static-routes = array of (destination-descriptor ip-address);
+ *
+ * which results in:
+ *
+ * 0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6
+ *
+ * dhcpcd supports classless static routes natively and uses this same
+ * option identifier with the following format:
+ *
+ * 192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41
+ */
+ str = g_hash_table_lookup (options, "classless_static_routes");
+
+ /* dhclient doesn't have actual support for rfc3442 classless static routes
+ * upstream. Thus, people resort to defining the option in dhclient.conf
+ * and using arbitrary formats like so:
+ *
+ * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
+ *
+ * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
+ */
+ if (!str)
+ str = g_hash_table_lookup (options, "rfc3442_classless_static_routes");
+
+ /* Microsoft version; same as rfc3442 but with a different option # (249) */
+ if (!str)
+ str = g_hash_table_lookup (options, "ms_classless_static_routes");
+
+ if (!str || !strlen (str))
+ return FALSE;
+
+ p = str;
+ while (*p) {
+ if (!g_ascii_isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) {
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+ return FALSE;
+ }
+ p++;
+ };
+
+ if (strchr (str, '/')) {
+ /* dhcpcd format */
+ return ip4_process_dhcpcd_rfc3442_routes (str, priority, ip4_config, gwaddr);
+ }
+
+ return ip4_process_dhclient_rfc3442_routes (str, priority, ip4_config, gwaddr);
+}
+
+static void
+process_classful_routes (GHashTable *options, guint priority, NMIP4Config *ip4_config)
+{
+ const char *str;
+ char **searches, **s;
+
+ str = g_hash_table_lookup (options, "static_routes");
+ if (!str)
+ return;
+
+ searches = g_strsplit (str, " ", 0);
+ if ((g_strv_length (searches) % 2)) {
+ nm_log_info (LOGD_DHCP, " static routes provided, but invalid");
+ goto out;
+ }
+
+ for (s = searches; *s; s += 2) {
+ NMPlatformIP4Route route;
+ guint32 rt_addr, rt_route;
+
+ if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
+ nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s);
+ continue;
+ }
+ if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
+ nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
+ continue;
+ }
+
+ // FIXME: ensure the IP address and route are sane
+
+ memset (&route, 0, sizeof (route));
+ route.network = rt_addr;
+ /* RFC 2132, updated by RFC 3442:
+ The Static Routes option (option 33) does not provide a subnet mask
+ for each route - it is assumed that the subnet mask is implicit in
+ whatever network number is specified in each route entry */
+ route.plen = nm_utils_ip4_get_default_prefix (rt_addr);
+ if (rt_addr & ~nm_utils_ip4_prefix_to_netmask (route.plen)) {
+ /* RFC 943: target not "this network"; using host routing */
+ route.plen = 32;
+ }
+ route.gateway = rt_route;
+ route.source = NM_PLATFORM_SOURCE_DHCP;
+ route.metric = priority;
+
+ nm_ip4_config_add_route (ip4_config, &route);
+ nm_log_info (LOGD_DHCP, " static route %s",
+ nm_platform_ip4_route_to_string (&route));
+ }
+
+out:
+ g_strfreev (searches);
+}
+
+static void
+process_domain_search (const char *str, GFunc add_func, gpointer user_data)
+{
+ char **searches, **s;
+ char *unescaped, *p;
+ int i;
+
+ g_return_if_fail (str != NULL);
+ g_return_if_fail (add_func != NULL);
+
+ p = unescaped = g_strdup (str);
+ do {
+ p = strstr (p, "\\032");
+ if (!p)
+ break;
+
+ /* Clear the escaped space with real spaces */
+ for (i = 0; i < 4; i++)
+ *p++ = ' ';
+ } while (*p++);
+
+ if (strchr (unescaped, '\\')) {
+ nm_log_warn (LOGD_DHCP, " invalid domain search: '%s'", unescaped);
+ goto out;
+ }
+
+ searches = g_strsplit (unescaped, " ", 0);
+ for (s = searches; *s; s++) {
+ if (strlen (*s)) {
+ nm_log_info (LOGD_DHCP, " domain search '%s'", *s);
+ add_func (*s, user_data);
+ }
+ }
+ g_strfreev (searches);
+
+out:
+ g_free (unescaped);
+}
+
+static void
+ip4_add_domain_search (gpointer data, gpointer user_data)
+{
+ nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data);
+}
+
+NMIP4Config *
+nm_dhcp_utils_ip4_config_from_options (const char *iface,
+ GHashTable *options,
+ guint priority)
+{
+ NMIP4Config *ip4_config = NULL;
+ guint32 tmp_addr;
+ NMPlatformIP4Address address;
+ char *str = NULL;
+ guint32 gwaddr = 0, plen = 0;
+
+ g_return_val_if_fail (options != NULL, NULL);
+
+ ip4_config = nm_ip4_config_new ();
+ memset (&address, 0, sizeof (address));
+ address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+
+ str = g_hash_table_lookup (options, "ip_address");
+ if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+ address.address = tmp_addr;
+ nm_log_info (LOGD_DHCP4, " address %s", str);
+ } else
+ goto error;
+
+ str = g_hash_table_lookup (options, "subnet_mask");
+ if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+ plen = nm_utils_ip4_netmask_to_prefix (tmp_addr);
+ nm_log_info (LOGD_DHCP4, " plen %d (%s)", plen, str);
+ } else {
+ /* Get default netmask for the IP according to appropriate class. */
+ plen = nm_utils_ip4_get_default_prefix (address.address);
+ nm_log_info (LOGD_DHCP4, " plen %d (default)", plen);
+ }
+ address.plen = plen;
+
+ /* Routes: if the server returns classless static routes, we MUST ignore
+ * the 'static_routes' option.
+ */
+ if (!ip4_process_classless_routes (options, priority, ip4_config, &gwaddr))
+ process_classful_routes (options, priority, ip4_config);
+
+ if (gwaddr) {
+ nm_log_info (LOGD_DHCP4, " gateway %s", nm_utils_inet4_ntop (gwaddr, NULL));
+ nm_ip4_config_set_gateway (ip4_config, gwaddr);
+ } else {
+ /* If the gateway wasn't provided as a classless static route with a
+ * subnet length of 0, try to find it using the old-style 'routers' option.
+ */
+ str = g_hash_table_lookup (options, "routers");
+ if (str) {
+ char **routers = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = routers; *s; s++) {
+ /* FIXME: how to handle multiple routers? */
+ if (inet_pton (AF_INET, *s, &gwaddr) > 0) {
+ nm_ip4_config_set_gateway (ip4_config, gwaddr);
+ nm_log_info (LOGD_DHCP4, " gateway %s", *s);
+ break;
+ } else
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s);
+ }
+ g_strfreev (routers);
+ }
+ }
+
+ /*
+ * RFC 2132, section 9.7
+ * DHCP clients use the contents of the 'server identifier' field
+ * as the destination address for any DHCP messages unicast to
+ * the DHCP server.
+ *
+ * Some ISP's provide leases from central servers that are on
+ * different subnets that the address offered. If the host
+ * does not configure the interface as the default route, the
+ * dhcp server may not be reachable via unicast, and a host
+ * specific route is needed.
+ **/
+ str = g_hash_table_lookup (options, "dhcp_server_identifier");
+ if (str) {
+ if (inet_pton (AF_INET, str, &tmp_addr) > 0) {
+ NMPlatformIP4Route route;
+ guint32 mask = nm_utils_ip4_prefix_to_netmask (address.plen);
+
+ nm_log_info (LOGD_DHCP4, " server identifier %s", str);
+ if ((tmp_addr & mask) != (address.address & mask)) {
+ /* DHCP server not on assigned subnet, route needed */
+ memset (&route, 0, sizeof (route));
+ route.network = tmp_addr;
+ route.plen = 32;
+ /* this will be a device route if gwaddr is 0 */
+ route.gateway = gwaddr;
+ route.source = NM_PLATFORM_SOURCE_DHCP;
+ route.metric = priority;
+ nm_ip4_config_add_route (ip4_config, &route);
+ nm_log_dbg (LOGD_IP, "adding route for server identifier: %s",
+ nm_platform_ip4_route_to_string (&route));
+ }
+ }
+ else
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str);
+ }
+
+ str = g_hash_table_lookup (options, "dhcp_lease_time");
+ if (str) {
+ address.lifetime = address.preferred = strtoul (str, NULL, 10);
+ nm_log_info (LOGD_DHCP4, " lease time %d", address.lifetime);
+ }
+
+ address.source = NM_PLATFORM_SOURCE_DHCP;
+ nm_ip4_config_add_address (ip4_config, &address);
+
+ str = g_hash_table_lookup (options, "host_name");
+ if (str)
+ nm_log_info (LOGD_DHCP4, " hostname '%s'", str);
+
+ str = g_hash_table_lookup (options, "domain_name_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_config_add_nameserver (ip4_config, tmp_addr);
+ nm_log_info (LOGD_DHCP4, " nameserver '%s'", *s);
+ } else
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ str = g_hash_table_lookup (options, "domain_name");
+ if (str) {
+ char **domains = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = domains; *s; s++) {
+ nm_log_info (LOGD_DHCP4, " domain name '%s'", *s);
+ nm_ip4_config_add_domain (ip4_config, *s);
+ }
+ g_strfreev (domains);
+ }
+
+ str = g_hash_table_lookup (options, "domain_search");
+ if (str)
+ process_domain_search (str, ip4_add_domain_search, ip4_config);
+
+ str = g_hash_table_lookup (options, "netbios_name_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_config_add_wins (ip4_config, tmp_addr);
+ nm_log_info (LOGD_DHCP4, " wins '%s'", *s);
+ } else
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ str = g_hash_table_lookup (options, "interface_mtu");
+ if (str) {
+ int int_mtu;
+
+ errno = 0;
+ int_mtu = strtol (str, NULL, 10);
+ if ((errno == EINVAL) || (errno == ERANGE))
+ goto error;
+
+ if (int_mtu > 576)
+ nm_ip4_config_set_mtu (ip4_config, int_mtu);
+ }
+
+ str = g_hash_table_lookup (options, "nis_domain");
+ if (str) {
+ nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str);
+ nm_ip4_config_set_nis_domain (ip4_config, str);
+ }
+
+ str = g_hash_table_lookup (options, "nis_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_config_add_nis_server (ip4_config, tmp_addr);
+ nm_log_info (LOGD_DHCP4, " nis '%s'", *s);
+ } else
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ return ip4_config;
+
+error:
+ g_object_unref (ip4_config);
+ return NULL;
+}
+
+/********************************************/
+
+static void
+ip6_add_domain_search (gpointer data, gpointer user_data)
+{
+ nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
+}
+
+NMIP6Config *
+nm_dhcp_utils_ip6_config_from_options (const char *iface,
+ GHashTable *options,
+ guint priority,
+ gboolean info_only)
+{
+ NMIP6Config *ip6_config = NULL;
+ struct in6_addr tmp_addr;
+ NMPlatformIP6Address address;
+ char *str = NULL;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_val_if_fail (options != NULL, NULL);
+
+ memset (&address, 0, sizeof (address));
+ address.plen = 128;
+ address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+
+ g_hash_table_iter_init (&iter, options);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'",
+ iface, (const char *) key, (const char *) value);
+ }
+
+ ip6_config = nm_ip6_config_new ();
+
+ str = g_hash_table_lookup (options, "max_life");
+ if (str) {
+ address.lifetime = strtoul (str, NULL, 10);
+ nm_log_info (LOGD_DHCP6, " valid_lft %d", address.lifetime);
+ }
+
+ str = g_hash_table_lookup (options, "preferred_life");
+ if (str) {
+ address.preferred = strtoul (str, NULL, 10);
+ nm_log_info (LOGD_DHCP6, " preferred_lft %d", address.preferred);
+ }
+
+ str = g_hash_table_lookup (options, "ip6_address");
+ if (str) {
+ if (!inet_pton (AF_INET6, str, &tmp_addr)) {
+ nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'",
+ iface, str);
+ goto error;
+ }
+
+ address.address = tmp_addr;
+ address.source = NM_PLATFORM_SOURCE_DHCP;
+ nm_ip6_config_add_address (ip6_config, &address);
+ nm_log_info (LOGD_DHCP6, " address %s", str);
+ } else if (info_only == FALSE) {
+ /* No address in Managed mode is a hard error */
+ goto error;
+ }
+
+ str = g_hash_table_lookup (options, "host_name");
+ if (str)
+ nm_log_info (LOGD_DHCP6, " hostname '%s'", str);
+
+ str = g_hash_table_lookup (options, "dhcp6_name_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
+ nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
+ nm_log_info (LOGD_DHCP6, " nameserver '%s'", *s);
+ } else
+ nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
+ str = g_hash_table_lookup (options, "dhcp6_domain_search");
+ if (str)
+ process_domain_search (str, ip6_add_domain_search, ip6_config);
+
+ return ip6_config;
+
+error:
+ g_object_unref (ip6_config);
+ return NULL;
+}
+
+char *
+nm_dhcp_utils_duid_to_string (const GByteArray *duid)
+{
+ guint32 i = 0;
+ GString *s;
+
+ g_return_val_if_fail (duid != NULL, NULL);
+
+ s = g_string_sized_new (MIN (duid->len * 3, 50));
+ while (i < duid->len) {
+ if (s->len)
+ g_string_append_c (s, ':');
+ g_string_append_printf (s, "%02x", duid->data[i++]);
+ }
+ return g_string_free (s, FALSE);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-utils.h b/src/dhcp-manager/nm-dhcp-utils.h
new file mode 100644
index 0000000000..be6b28106a
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-utils.h
@@ -0,0 +1,39 @@
+/* -*- 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) 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_UTILS_H
+#define NM_DHCP_UTILS_H
+
+#include <stdlib.h>
+#include <glib.h>
+#include <nm-ip4-config.h>
+#include <nm-ip6-config.h>
+
+NMIP4Config *nm_dhcp_utils_ip4_config_from_options (const char *iface,
+ GHashTable *options,
+ guint priority);
+
+NMIP6Config *nm_dhcp_utils_ip6_config_from_options (const char *iface,
+ GHashTable *options,
+ guint priority,
+ gboolean info_only);
+
+char * nm_dhcp_utils_duid_to_string (const GByteArray *duid);
+
+#endif /* NM_DHCP_UTILS_H */
+
diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am
index 4701b3f1b9..e9f9a61013 100644
--- a/src/dhcp-manager/tests/Makefile.am
+++ b/src/dhcp-manager/tests/Makefile.am
@@ -3,6 +3,7 @@ AM_CPPFLAGS = \
-I${top_srcdir}/libnm-util \
-I${top_builddir}/libnm-util \
-I$(top_srcdir)/src/dhcp-manager \
+ -I$(top_srcdir)/src/logging \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/platform \
-DG_LOG_DOMAIN=\""NetworkManager"\" \
@@ -10,9 +11,11 @@ AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
-DTESTDIR="\"$(abs_srcdir)\""
-noinst_PROGRAMS = test-dhcp-dhclient
+noinst_PROGRAMS = \
+ test-dhcp-dhclient \
+ test-dhcp-options
-####### policy /etc/hosts test #######
+####### dhclient leases test #######
test_dhcp_dhclient_SOURCES = \
test-dhcp-dhclient.c
@@ -20,7 +23,17 @@ test_dhcp_dhclient_SOURCES = \
test_dhcp_dhclient_LDADD = \
$(top_builddir)/src/libNetworkManager.la
-TESTS = test-dhcp-dhclient
+####### DHCP options test #######
+
+test_dhcp_options_SOURCES = \
+ test-dhcp-options.c
+
+test_dhcp_options_LDADD = \
+ $(top_builddir)/src/libNetworkManager.la
+
+#################################
+
+TESTS = test-dhcp-dhclient test-dhcp-options
EXTRA_DIST = \
test-dhclient-duid.leases \
diff --git a/src/dhcp-manager/tests/test-dhcp-options.c b/src/dhcp-manager/tests/test-dhcp-options.c
new file mode 100644
index 0000000000..ac6c22a6c3
--- /dev/null
+++ b/src/dhcp-manager/tests/test-dhcp-options.c
@@ -0,0 +1,683 @@
+/* -*- 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) 2008 - 2014 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <nm-utils.h>
+
+#include "nm-dhcp-utils.h"
+#include "nm-logging.h"
+
+#include "nm-test-utils.h"
+
+typedef struct {
+ const char *name;
+ const char *value;
+} Option;
+
+static GHashTable *
+fill_table (const Option *test_options, GHashTable *table)
+{
+ const Option *opt;
+
+ if (!table)
+ table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ for (opt = test_options; opt->name; opt++)
+ g_hash_table_insert (table, (gpointer) opt->name, (gpointer) opt->value);
+ return table;
+}
+
+static const Option generic_options[] = {
+ { "subnet_mask", "255.255.255.0" },
+ { "ip_address", "192.168.1.106" },
+ { "network_number", "192.168.1.0" },
+ { "expiry", "1232324877" },
+ { "dhcp_lease_time", "3600" },
+ { "dhcp_server_identifier", "192.168.1.1" },
+ { "routers", "192.168.1.1" },
+ { "domain_name_servers", "216.254.95.2 216.231.41.2" },
+ { "dhcp_message_type", "5" },
+ { "broadcast_address", "192.168.1.255" },
+ { "domain_search", "foobar.com blah.foobar.com" },
+ { "host_name", "nmreallywhipsthe" },
+ { "domain_name", "lamasass.com" },
+ { "interface_mtu", "987" },
+ { "static_routes", "10.1.1.5 10.1.1.1 100.99.88.56 10.1.1.1" },
+ { NULL, NULL }
+};
+
+static void
+test_generic_options (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const NMPlatformIP4Address *address;
+ const NMPlatformIP4Route *route;
+ guint32 tmp;
+ const char *expected_addr = "192.168.1.106";
+ const char *expected_gw = "192.168.1.1";
+ const char *expected_dns1 = "216.254.95.2";
+ const char *expected_dns2 = "216.231.41.2";
+ const char *expected_search1 = "foobar.com";
+ const char *expected_search2 = "blah.foobar.com";
+ const char *expected_route1_dest = "10.1.1.5";
+ const char *expected_route1_gw = "10.1.1.1";
+ const char *expected_route2_dest = "100.99.88.56";
+ const char *expected_route2_gw = "10.1.1.1";
+
+ options = fill_table (generic_options, NULL);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 address */
+ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+ address = nm_ip4_config_get_address (ip4_config, 0);
+ g_assert (inet_pton (AF_INET, expected_addr, &tmp) > 0);
+ g_assert (address->address == tmp);
+ g_assert (address->peer_address == 0);
+ g_assert_cmpint (address->plen, ==, 24);
+
+ /* Gateway */
+ g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0);
+ g_assert (nm_ip4_config_get_gateway (ip4_config) == tmp);
+
+ g_assert_cmpint (nm_ip4_config_get_num_wins (ip4_config), ==, 0);
+
+ g_assert_cmpint (nm_ip4_config_get_mtu (ip4_config), ==, 987);
+
+ /* Domain searches */
+ g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 2);
+ g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 0), ==, expected_search1);
+ g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 1), ==, expected_search2);
+
+ /* DNS servers */
+ g_assert_cmpint (nm_ip4_config_get_num_nameservers (ip4_config), ==, 2);
+ g_assert (inet_pton (AF_INET, expected_dns1, &tmp) > 0);
+ g_assert (nm_ip4_config_get_nameserver (ip4_config, 0) == tmp);
+ g_assert (inet_pton (AF_INET, expected_dns2, &tmp) > 0);
+ g_assert (nm_ip4_config_get_nameserver (ip4_config, 1) == tmp);
+
+ /* Routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+
+ /* Route #1 */
+ route = nm_ip4_config_get_route (ip4_config, 0);
+ g_assert (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0);
+ g_assert (route->network == tmp);
+ g_assert (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0);
+ g_assert (route->gateway == tmp);
+ g_assert_cmpint (route->plen, ==, 32);
+ g_assert_cmpint (route->metric, ==, 0);
+
+ /* Route #2 */
+ route = nm_ip4_config_get_route (ip4_config, 1);
+ g_assert (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0);
+ g_assert (route->network == tmp);
+ g_assert (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0);
+ g_assert (route->gateway == tmp);
+ g_assert_cmpint (route->plen, ==, 32);
+ g_assert_cmpint (route->metric, ==, 0);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_wins_options (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const NMPlatformIP4Address *address;
+ guint32 tmp;
+ const char *expected_wins1 = "63.12.199.5";
+ const char *expected_wins2 = "150.4.88.120";
+ static const Option data[] = {
+ { "netbios_name_servers", "63.12.199.5 150.4.88.120" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 address */
+ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+ address = nm_ip4_config_get_address (ip4_config, 0);
+ g_assert (address);
+ g_assert_cmpint (nm_ip4_config_get_num_wins (ip4_config), ==, 2);
+ g_assert (inet_pton (AF_INET, expected_wins1, &tmp) > 0);
+ g_assert (nm_ip4_config_get_wins (ip4_config, 0) == tmp);
+ g_assert (inet_pton (AF_INET, expected_wins2, &tmp) > 0);
+ g_assert (nm_ip4_config_get_wins (ip4_config, 1) == tmp);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+ip4_test_route (NMIP4Config *ip4_config,
+ guint route_num,
+ const char *expected_dest,
+ const char *expected_gw,
+ guint expected_prefix)
+{
+ const NMPlatformIP4Route *route;
+ guint32 tmp;
+
+ route = nm_ip4_config_get_route (ip4_config, route_num);
+ g_assert (inet_pton (AF_INET, expected_dest, &tmp) > 0);
+ g_assert (route->network == tmp);
+ g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0);
+ g_assert (route->gateway == tmp);
+ g_assert_cmpint (route->plen, ==, expected_prefix);
+ g_assert_cmpint (route->metric, ==, 0);
+}
+
+static void
+ip4_test_gateway (NMIP4Config *ip4_config, const char *expected_gw)
+{
+ guint32 tmp;
+
+ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+ g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0);
+ g_assert (nm_ip4_config_get_gateway (ip4_config) == tmp);
+}
+
+static void
+test_classless_static_routes_1 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ const char *expected_route2_dest = "10.0.0.0";
+ const char *expected_route2_gw = "10.17.66.41";
+ static const Option data[] = {
+ /* dhclient custom format */
+ { "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+ ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 8);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_classless_static_routes_2 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ const char *expected_route2_dest = "10.0.0.0";
+ const char *expected_route2_gw = "10.17.66.41";
+ static const Option data[] = {
+ /* dhcpcd format */
+ { "classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+ ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 8);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_fedora_dhclient_classless_static_routes (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "129.210.177.128";
+ const char *expected_route1_gw = "192.168.0.113";
+ const char *expected_route2_dest = "2.0.0.0";
+ const char *expected_route2_gw = "10.34.255.6";
+ const char *expected_gateway = "192.168.0.113";
+ static const Option data[] = {
+ /* Fedora dhclient format */
+ { "classless_static_routes", "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 25);
+ ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 7);
+
+ /* Gateway */
+ ip4_test_gateway (ip4_config, expected_gateway);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_invalid_classless_routes_1 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ static const Option data[] = {
+ /* dhclient format */
+ { "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*ignoring invalid classless static routes*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_invalid_classless_routes_1 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "10.1.1.5";
+ const char *expected_route1_gw = "10.1.1.1";
+ const char *expected_route2_dest = "100.99.88.56";
+ const char *expected_route2_gw = "10.1.1.1";
+ static const Option data[] = {
+ /* dhcpcd format */
+ { "classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*ignoring invalid classless static routes*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* Test falling back to old-style static routes if the classless static
+ * routes are invalid.
+ */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32);
+ ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_invalid_classless_routes_2 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "10.1.1.5";
+ const char *expected_route1_gw = "10.1.1.1";
+ const char *expected_route2_dest = "100.99.88.56";
+ const char *expected_route2_gw = "10.1.1.1";
+ static const Option data[] = {
+ { "rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*ignoring invalid classless static routes*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* Test falling back to old-style static routes if the classless static
+ * routes are invalid.
+ */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32);
+ ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_invalid_classless_routes_2 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "10.1.1.5";
+ const char *expected_route1_gw = "10.1.1.1";
+ const char *expected_route2_dest = "100.99.88.56";
+ const char *expected_route2_gw = "10.1.1.1";
+ static const Option data[] = {
+ { "classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*ignoring invalid classless static routes*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* Test falling back to old-style static routes if the classless static
+ * routes are invalid.
+ */
+
+ /* Routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32);
+ ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_invalid_classless_routes_3 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ static const Option data[] = {
+ { "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*ignoring invalid classless static routes*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_invalid_classless_routes_3 (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ static Option data[] = {
+ { "classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*DHCP provided invalid classless static route*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_gw_in_classless_routes (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ const char *expected_gateway = "192.2.3.4";
+ static Option data[] = {
+ { "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+ /* Gateway */
+ ip4_test_gateway (ip4_config, expected_gateway);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_gw_in_classless_routes (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ const char *expected_gateway = "192.2.3.4";
+ static Option data[] = {
+ { "classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* IP4 routes */
+ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+ ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+ /* Gateway */
+ ip4_test_gateway (ip4_config, expected_gateway);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_escaped_domain_searches (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_search0 = "host1";
+ const char *expected_search1 = "host2";
+ const char *expected_search2 = "host3";
+ static const Option data[] = {
+ { "domain_search", "host1\\032host2\\032host3" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ /* domain searches */
+ g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 3);
+ g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 0), ==, expected_search0);
+ g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 1), ==, expected_search1);
+ g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 2), ==, expected_search2);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_invalid_escaped_domain_searches (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ static const Option data[] = {
+ { "domain_search", "host1\\aahost2\\032host3" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*invalid domain search*");
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+ g_test_assert_expected_messages ();
+
+ /* domain searches */
+ g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 0);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_ip4_missing_prefix (const char *ip, guint32 expected_prefix)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const NMPlatformIP4Address *address;
+
+ options = fill_table (generic_options, NULL);
+ g_hash_table_insert (options, "ip_address", (gpointer) ip);
+ g_hash_table_remove (options, "subnet_mask");
+
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+ address = nm_ip4_config_get_address (ip4_config, 0);
+ g_assert (address);
+ g_assert_cmpint (address->plen, ==, expected_prefix);
+
+ g_hash_table_destroy (options);
+}
+
+static void
+test_ip4_missing_prefix_24 (void)
+{
+ test_ip4_missing_prefix ("192.168.1.10", 24);
+}
+
+static void
+test_ip4_missing_prefix_16 (void)
+{
+ test_ip4_missing_prefix ("172.16.54.50", 16);
+}
+
+static void
+test_ip4_missing_prefix_8 (void)
+{
+ test_ip4_missing_prefix ("10.1.2.3", 8);
+}
+
+static void
+test_ip4_prefix_classless (void)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const NMPlatformIP4Address *address;
+
+ /* Ensure that the missing-subnet-mask handler doesn't mangle classless
+ * subnet masks at all. The handler should trigger only if the server
+ * doesn't send the subnet mask.
+ */
+
+ options = fill_table (generic_options, NULL);
+ g_hash_table_insert (options, "ip_address", "172.16.54.22");
+ g_hash_table_insert (options, "subnet_mask", "255.255.252.0");
+
+ ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+ g_assert (ip4_config);
+
+ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+ address = nm_ip4_config_get_address (ip4_config, 0);
+ g_assert (address);
+ g_assert_cmpint (address->plen, ==, 22);
+
+ g_hash_table_destroy (options);
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+ nmtst_init_assert_logging (&argc, &argv);
+ nm_logging_setup ("WARN", "DEFAULT", NULL, NULL);
+
+ g_test_add_func ("/dhcp/generic-options", test_generic_options);
+ g_test_add_func ("/dhcp/wins-options", test_wins_options);
+ g_test_add_func ("/dhcp/classless-static-routes-1", test_classless_static_routes_1);
+ g_test_add_func ("/dhcp/classless-static-routes-2", test_classless_static_routes_2);
+ g_test_add_func ("/dhcp/fedora-dhclient-classless-static-routes", test_fedora_dhclient_classless_static_routes);
+ g_test_add_func ("/dhcp/dhclient-invalid-classless-routes-1", test_dhclient_invalid_classless_routes_1);
+ g_test_add_func ("/dhcp/dhcpcd-invalid-classless-routes-1", test_dhcpcd_invalid_classless_routes_1);
+ g_test_add_func ("/dhcp/dhclient-invalid-classless-routes-2", test_dhclient_invalid_classless_routes_2);
+ g_test_add_func ("/dhcp/dhcpcd-invalid-classless-routes-2", test_dhcpcd_invalid_classless_routes_2);
+ g_test_add_func ("/dhcp/dhclient-invalid-classless-routes-3", test_dhclient_invalid_classless_routes_3);
+ g_test_add_func ("/dhcp/dhcpcd-invalid-classless-routes-3", test_dhcpcd_invalid_classless_routes_3);
+ g_test_add_func ("/dhcp/dhclient-gw-in-classless-routes", test_dhclient_gw_in_classless_routes);
+ g_test_add_func ("/dhcp/dhcpcd-gw-in-classless-routes", test_dhcpcd_gw_in_classless_routes);
+ g_test_add_func ("/dhcp/escaped-domain-searches", test_escaped_domain_searches);
+ g_test_add_func ("/dhcp/invalid-escaped-domain-searches", test_invalid_escaped_domain_searches);
+ g_test_add_func ("/dhcp/ip4-missing-prefix-24", test_ip4_missing_prefix_24);
+ g_test_add_func ("/dhcp/ip4-missing-prefix-16", test_ip4_missing_prefix_16);
+ g_test_add_func ("/dhcp/ip4-missing-prefix-8", test_ip4_missing_prefix_8);
+ g_test_add_func ("/dhcp/ip4-prefix-classless", test_ip4_prefix_classless);
+
+ return g_test_run ();
+}
+
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index 2370d57110..75e93f20ee 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -14,7 +14,6 @@ AM_CPPFLAGS = \
$(DBUS_CFLAGS)
noinst_PROGRAMS = \
- test-dhcp-options \
test-general \
test-general-with-expect \
test-ip4-config \
@@ -22,19 +21,6 @@ noinst_PROGRAMS = \
test-dcb \
test-resolvconf-capture
-####### DHCP options test #######
-
-test_dhcp_options_SOURCES = \
- test-dhcp-options.c
-
-test_dhcp_options_CPPFLAGS = \
- $(AM_CPPFLAGS) \
- -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
- -DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
-
-test_dhcp_options_LDADD = \
- $(top_builddir)/src/libNetworkManager.la
-
####### ip4 config test #######
test_ip4_config_SOURCES = \
diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c
deleted file mode 100644
index 5df17e587c..0000000000
--- a/src/tests/test-dhcp-options.c
+++ /dev/null
@@ -1,937 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager
- *
- * 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) 2008 - 2011 Red Hat, Inc.
- *
- */
-
-#include <glib.h>
-#include <dbus/dbus-glib.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-
-#include <nm-utils.h>
-
-#include "nm-dhcp-manager.h"
-#include "nm-logging.h"
-
-#include "nm-test-utils.h"
-
-typedef struct {
- const char *name;
- const char *value;
-} Option;
-
-static void
-destroy_gvalue (gpointer data)
-{
- GValue *value = (GValue *) data;
-
- g_value_unset (value);
- g_slice_free (GValue, value);
-}
-
-static GValue *
-string_to_byte_array_gvalue (const char *str)
-{
- GByteArray *array;
- GValue *val;
-
- array = g_byte_array_sized_new (strlen (str));
- g_byte_array_append (array, (const guint8 *) str, strlen (str));
-
- val = g_slice_new0 (GValue);
- g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
- g_value_take_boxed (val, array);
-
- return val;
-}
-
-static GHashTable *
-fill_table (Option *test_options, GHashTable *table)
-{
- Option *opt;
-
- if (!table)
- table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue);
- for (opt = test_options; opt->name; opt++) {
- g_hash_table_insert (table,
- (gpointer) opt->name,
- string_to_byte_array_gvalue (opt->value));
- }
- return table;
-}
-
-static Option generic_options[] = {
- { "new_subnet_mask", "255.255.255.0" },
- { "new_ip_address", "192.168.1.106" },
- { "new_network_number", "192.168.1.0" },
- { "interface", "eth0" },
- { "reason", "BOUND" },
- { "new_expiry", "1232324877" },
- { "new_dhcp_lease_time", "3600" },
- { "new_dhcp_server_identifier", "192.168.1.1" },
- { "new_routers", "192.168.1.1" },
- { "new_domain_name_servers", "216.254.95.2 216.231.41.2" },
- { "new_dhcp_message_type", "5" },
- { "new_broadcast_address", "192.168.1.255" },
- { "new_domain_search", "foobar.com blah.foobar.com" },
- { "new_host_name", "nmreallywhipsthe" },
- { "new_domain_name", "lamasass.com" },
- { "new_interface_mtu", "987" },
- { "new_static_routes", "10.1.1.5 10.1.1.1 100.99.88.56 10.1.1.1" },
- { NULL, NULL }
-};
-
-static void
-test_generic_options (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const NMPlatformIP4Address *address;
- const NMPlatformIP4Route *route;
- guint32 tmp;
- const char *expected_addr = "192.168.1.106";
- const char *expected_gw = "192.168.1.1";
- const char *expected_dns1 = "216.254.95.2";
- const char *expected_dns2 = "216.231.41.2";
- const char *expected_search1 = "foobar.com";
- const char *expected_search2 = "blah.foobar.com";
- const char *expected_route1_dest = "10.1.1.5";
- const char *expected_route1_gw = "10.1.1.1";
- const char *expected_route2_dest = "100.99.88.56";
- const char *expected_route2_gw = "10.1.1.1";
-
- options = fill_table (generic_options, NULL);
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-generic", "failed to parse DHCP4 options");
-
- /* IP4 address */
- ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
- "dhcp-generic", "unexpected number of IP addresses");
- address = nm_ip4_config_get_address (ip4_config, 0);
-
- ASSERT (inet_pton (AF_INET, expected_addr, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected IP address");
- ASSERT (address->address == tmp,
- "dhcp-generic", "unexpected IP address");
- ASSERT (address->peer_address == 0,
- "dhcp-generic", "unexpected PTP address");
-
- ASSERT (address->plen == 24,
- "dhcp-generic", "unexpected IP address prefix length");
-
- /* Gateway */
- ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected IP gateway");
- ASSERT (nm_ip4_config_get_gateway (ip4_config) == tmp,
- "dhcp-generic", "unexpected IP gateway");
-
- ASSERT (nm_ip4_config_get_num_wins (ip4_config) == 0,
- "dhcp-generic", "unexpected number of WINS servers");
-
- ASSERT (nm_ip4_config_get_mtu (ip4_config) == 987,
- "dhcp-generic", "unexpected MTU");
-
- /* Domain searches */
- ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 2,
- "dhcp-generic", "unexpected number of domain searches");
- ASSERT (strcmp (nm_ip4_config_get_search (ip4_config, 0), expected_search1) == 0,
- "dhcp-generic", "unexpected domain search #1");
- ASSERT (strcmp (nm_ip4_config_get_search (ip4_config, 1), expected_search2) == 0,
- "dhcp-generic", "unexpected domain search #2");
-
- /* DNS servers */
- ASSERT (nm_ip4_config_get_num_nameservers (ip4_config) == 2,
- "dhcp-generic", "unexpected number of domain name servers");
- ASSERT (inet_pton (AF_INET, expected_dns1, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected DNS server address #1");
- ASSERT (nm_ip4_config_get_nameserver (ip4_config, 0) == tmp,
- "dhcp-generic", "unexpected domain name server #1");
- ASSERT (inet_pton (AF_INET, expected_dns2, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected DNS server address #2");
- ASSERT (nm_ip4_config_get_nameserver (ip4_config, 1) == tmp,
- "dhcp-generic", "unexpected domain name server #2");
-
- /* Routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-generic", "unexpected number of routes");
-
- /* Route #1 */
- route = nm_ip4_config_get_route (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected route destination #1");
- ASSERT (route->network == tmp,
- "dhcp-generic", "unexpected route #1 destination");
-
- ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected route next hop #1");
- ASSERT (route->gateway == tmp,
- "dhcp-generic", "unexpected route #1 next hop");
-
- ASSERT (route->plen == 32,
- "dhcp-generic", "unexpected route #1 prefix");
- ASSERT (route->metric == 0,
- "dhcp-generic", "unexpected route #1 metric");
-
- /* Route #2 */
- route = nm_ip4_config_get_route (ip4_config, 1);
- ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected route destination #2");
- ASSERT (route->network == tmp,
- "dhcp-generic", "unexpected route #2 destination");
-
- ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0,
- "dhcp-generic", "couldn't convert expected route next hop #2");
- ASSERT (route->gateway == tmp,
- "dhcp-generic", "unexpected route #2 next hop");
-
- ASSERT (route->plen == 32,
- "dhcp-generic", "unexpected route #2 prefix");
- ASSERT (route->metric == 0,
- "dhcp-generic", "unexpected route #2 metric");
-
- g_hash_table_destroy (options);
-}
-
-static Option wins_options[] = {
- { "new_netbios_name_servers", "63.12.199.5 150.4.88.120" },
- { NULL, NULL }
-};
-
-static void
-test_wins_options (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const NMPlatformIP4Address *address;
- guint32 tmp;
- const char *expected_wins1 = "63.12.199.5";
- const char *expected_wins2 = "150.4.88.120";
-
- options = fill_table (generic_options, NULL);
- options = fill_table (wins_options, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-wins", "failed to parse DHCP4 options");
-
- /* IP4 address */
- ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
- "dhcp-wins", "unexpected number of IP addresses");
- address = nm_ip4_config_get_address (ip4_config, 0);
- ASSERT (address != NULL, "dhcp-wins", "unexpectedly did not get address #0");
-
- ASSERT (nm_ip4_config_get_num_wins (ip4_config) == 2,
- "dhcp-wins", "unexpected number of WINS servers");
- ASSERT (inet_pton (AF_INET, expected_wins1, &tmp) > 0,
- "dhcp-wins", "couldn't convert expected WINS server address #1");
- ASSERT (nm_ip4_config_get_wins (ip4_config, 0) == tmp,
- "dhcp-wins", "unexpected WINS server #1");
- ASSERT (inet_pton (AF_INET, expected_wins2, &tmp) > 0,
- "dhcp-wins", "couldn't convert expected WINS server address #1");
- ASSERT (nm_ip4_config_get_wins (ip4_config, 1) == tmp,
- "dhcp-wins", "unexpected WINS server #1");
-
- g_hash_table_destroy (options);
-}
-
-static void
-ip4_test_route (const char *test,
- NMIP4Config *ip4_config,
- guint route_num,
- const char *expected_dest,
- const char *expected_gw,
- guint expected_prefix)
-{
- const NMPlatformIP4Route *route;
- guint32 tmp;
-
- route = nm_ip4_config_get_route (ip4_config, route_num);
- ASSERT (inet_pton (AF_INET, expected_dest, &tmp) > 0,
- test, "couldn't convert expected route destination #1");
- ASSERT (route->network == tmp,
- test, "unexpected route %d destination", route_num + 1);
-
- ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0,
- test, "couldn't convert expected route next hop %d",
- route_num + 1);
- ASSERT (route->gateway == tmp,
- test, "unexpected route %d next hop", route_num + 1);
-
- ASSERT (route->plen == expected_prefix,
- test, "unexpected route %d prefix", route_num + 1);
- ASSERT (route->metric == 0,
- test, "unexpected route %d metric", route_num + 1);
-}
-
-static void
-ip4_test_gateway (const char *test,
- NMIP4Config *ip4_config,
- const char *expected_gw)
-{
- guint32 tmp;
-
- ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
- test, "unexpected number of IP addresses");
- ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0,
- test, "couldn't convert expected IP gateway");
- ASSERT (nm_ip4_config_get_gateway (ip4_config) == tmp,
- test, "unexpected IP gateway");
-}
-
-static void
-test_classless_static_routes_1 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- const char *expected_route2_dest = "10.0.0.0";
- const char *expected_route2_gw = "10.17.66.41";
- static Option data[] = {
- /* dhclient custom format */
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-classless-1", "failed to parse DHCP4 options");
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-classless-1", "unexpected number of IP routes");
- ip4_test_route ("dhcp-classless-1", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
- ip4_test_route ("dhcp-classless-1", ip4_config, 1,
- expected_route2_dest, expected_route2_gw, 8);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_classless_static_routes_2 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- const char *expected_route2_dest = "10.0.0.0";
- const char *expected_route2_gw = "10.17.66.41";
- static Option data[] = {
- /* dhcpcd format */
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-classless-2", "failed to parse DHCP4 options");
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-classless-2", "unexpected number of IP routes");
- ip4_test_route ("dhcp-classless-2", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
- ip4_test_route ("dhcp-classless-2", ip4_config, 1,
- expected_route2_dest, expected_route2_gw, 8);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_fedora_dhclient_classless_static_routes (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "129.210.177.128";
- const char *expected_route1_gw = "192.168.0.113";
- const char *expected_route2_dest = "2.0.0.0";
- const char *expected_route2_gw = "10.34.255.6";
- const char *expected_gateway = "192.168.0.113";
- static Option data[] = {
- /* Fedora dhclient format */
- { "new_classless_static_routes", "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-fedora-dhclient-classless", "failed to parse DHCP4 options");
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-fedora-dhclient-classless", "unexpected number of IP routes");
- ip4_test_route ("dhcp-fedora-dhclient-classless", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 25);
- ip4_test_route ("dhcp-fedora-dhclient-classless", ip4_config, 1,
- expected_route2_dest, expected_route2_gw, 7);
-
- /* Gateway */
- ip4_test_gateway ("dhcp-fedora-dhclient-classless", ip4_config, expected_gateway);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhclient_invalid_classless_routes_1 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- static Option data[] = {
- /* dhclient format */
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*ignoring invalid classless static routes*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhclient-classless-invalid-1", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-dhclient-classless-invalid-1", "unexpected number of IP routes");
-
- ip4_test_route ("dhcp-dhclient-classless-invalid-1", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhcpcd_invalid_classless_routes_1 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "10.1.1.5";
- const char *expected_route1_gw = "10.1.1.1";
- const char *expected_route2_dest = "100.99.88.56";
- const char *expected_route2_gw = "10.1.1.1";
- static Option data[] = {
- /* dhcpcd format */
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*ignoring invalid classless static routes*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhcpcd-classless-invalid-1", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* Test falling back to old-style static routes if the classless static
- * routes are invalid.
- */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-dhcpcdp-classless-invalid-1", "unexpected number of routes");
- ip4_test_route ("dhcp-dhcpcdp-classless-invalid-1", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 32);
- ip4_test_route ("dhcp-dhcpcdp-classless-invalid-1", ip4_config, 1,
- expected_route2_dest, expected_route2_gw, 32);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhclient_invalid_classless_routes_2 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "10.1.1.5";
- const char *expected_route1_gw = "10.1.1.1";
- const char *expected_route2_dest = "100.99.88.56";
- const char *expected_route2_gw = "10.1.1.1";
- static Option data[] = {
- { "new_rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*ignoring invalid classless static routes*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhclient-classless-invalid-2", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* Test falling back to old-style static routes if the classless static
- * routes are invalid.
- */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-dhclient-classless-invalid-2", "unexpected number of routes");
- ip4_test_route ("dhcp-dhclient-classless-invalid-2", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 32);
- ip4_test_route ("dhcp-dhclient-classless-invalid-2", ip4_config, 1,
- expected_route2_dest, expected_route2_gw, 32);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhcpcd_invalid_classless_routes_2 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "10.1.1.5";
- const char *expected_route1_gw = "10.1.1.1";
- const char *expected_route2_dest = "100.99.88.56";
- const char *expected_route2_gw = "10.1.1.1";
- static Option data[] = {
- { "new_classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*ignoring invalid classless static routes*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhcpcd-classless-invalid-2", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* Test falling back to old-style static routes if the classless static
- * routes are invalid.
- */
-
- /* Routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-dhcpcd-classless-invalid-2", "unexpected number of routes");
- ip4_test_route ("dhcp-dhcpcd-classless-invalid-2", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 32);
- ip4_test_route ("dhcp-dhcpcd-classless-invalid-2", ip4_config, 1,
- expected_route2_dest, expected_route2_gw, 32);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhclient_invalid_classless_routes_3 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- static Option data[] = {
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*ignoring invalid classless static routes*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhclient-classless-invalid-3", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-dhclient-classless-invalid-3", "unexpected number of IP routes");
- ip4_test_route ("dhcp-dhclient-classless-invalid-3", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhcpcd_invalid_classless_routes_3 (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- static Option data[] = {
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*DHCP provided invalid classless static route*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhcpcd-classless-invalid-3", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-dhcpcd-classless-invalid-3", "unexpected number of IP routes");
- ip4_test_route ("dhcp-dhcpcd-classless-invalid-3", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhclient_gw_in_classless_routes (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- const char *expected_gateway = "192.2.3.4";
- static Option data[] = {
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhclient-classless-gateway", "failed to parse DHCP4 options");
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-dhclient-classless-gateway", "unexpected number of IP routes");
- ip4_test_route ("dhcp-dhclient-classless-gateway", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
-
- /* Gateway */
- ip4_test_gateway ("dhcp-dhclient-classless-gateway", ip4_config, expected_gateway);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_dhcpcd_gw_in_classless_routes (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_route1_dest = "192.168.10.0";
- const char *expected_route1_gw = "192.168.1.1";
- const char *expected_gateway = "192.2.3.4";
- static Option data[] = {
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" },
- { NULL, NULL }
- };
-
- options = fill_table (generic_options, NULL);
- options = fill_table (data, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-dhcpcd-classless-gateway", "failed to parse DHCP4 options");
-
- /* IP4 routes */
- ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-dhcpcd-classless-gateway", "unexpected number of IP routes");
- ip4_test_route ("dhcp-dhcpcd-classless-gateway", ip4_config, 0,
- expected_route1_dest, expected_route1_gw, 24);
-
- /* Gateway */
- ip4_test_gateway ("dhcp-dhcpcd-classless-gateway", ip4_config, expected_gateway);
-
- g_hash_table_destroy (options);
-}
-
-static Option escaped_searches_options[] = {
- { "new_domain_search", "host1\\032host2\\032host3" },
- { NULL, NULL }
-};
-
-static void
-test_escaped_domain_searches (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const char *expected_search0 = "host1";
- const char *expected_search1 = "host2";
- const char *expected_search2 = "host3";
-
- options = fill_table (generic_options, NULL);
- options = fill_table (escaped_searches_options, options);
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-escaped-domain-searches", "failed to parse DHCP4 options");
-
- /* domain searches */
- ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 3,
- "dhcp-escaped-domain-searches", "unexpected number of searches");
- ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 0), expected_search0),
- "dhcp-escaped-domain-searches", "unexpected domain search #1");
- ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 1), expected_search1),
- "dhcp-escaped-domain-searches", "unexpected domain search #1");
- ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 2), expected_search2),
- "dhcp-escaped-domain-searches", "unexpected domain search #1");
-
- g_hash_table_destroy (options);
-}
-
-static Option invalid_escaped_searches_options[] = {
- { "new_domain_search", "host1\\aahost2\\032host3" },
- { NULL, NULL }
-};
-
-static void
-test_invalid_escaped_domain_searches (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
-
- options = fill_table (generic_options, NULL);
- options = fill_table (invalid_escaped_searches_options, options);
-
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*invalid domain search*");
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options");
- g_test_assert_expected_messages ();
-
- /* domain searches */
- ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 0,
- "dhcp-invalid-escaped-domain-searches", "unexpected domain searches");
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_ip4_missing_prefix (const char *client, const char *ip, guint32 expected_prefix)
-{
- GHashTable *options;
- NMIP4Config *ip4_config;
- const NMPlatformIP4Address *address;
-
- options = fill_table (generic_options, NULL);
- g_hash_table_insert (options, "new_ip_address", string_to_byte_array_gvalue (ip));
- g_hash_table_remove (options, "new_subnet_mask");
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-ip4-missing-prefix", "failed to parse DHCP4 options");
-
- ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
- "dhcp-ip4-missing-prefix", "unexpected number of IP4 addresses (not 1)");
-
- address = nm_ip4_config_get_address (ip4_config, 0);
- ASSERT (address,
- "dhcp-ip4-missing-prefix", "missing IP4 address #1");
-
- ASSERT (address->plen == expected_prefix,
- "dhcp-ip4-missing-prefix", "unexpected IP4 address prefix %d (expected %d)",
- address->plen, expected_prefix);
-
- g_hash_table_destroy (options);
-}
-
-static void
-test_ip4_missing_prefix_24 (gconstpointer test_data)
-{
- const char *client = test_data;
-
- test_ip4_missing_prefix (client, "192.168.1.10", 24);
-}
-
-static void
-test_ip4_missing_prefix_16 (gconstpointer test_data)
-{
- const char *client = test_data;
-
- test_ip4_missing_prefix (client, "172.16.54.50", 16);
-}
-
-static void
-test_ip4_missing_prefix_8 (gconstpointer test_data)
-{
- const char *client = test_data;
-
- test_ip4_missing_prefix (client, "10.1.2.3", 8);
-}
-
-static void
-test_ip4_prefix_classless (gconstpointer test_data)
-{
- const char *client = test_data;
- GHashTable *options;
- NMIP4Config *ip4_config;
- const NMPlatformIP4Address *address;
-
- /* Ensure that the missing-subnet-mask handler doesn't mangle classless
- * subnet masks at all. The handler should trigger only if the server
- * doesn't send the subnet mask.
- */
-
- options = fill_table (generic_options, NULL);
- g_hash_table_insert (options, "new_ip_address", string_to_byte_array_gvalue ("172.16.54.22"));
- g_hash_table_insert (options, "new_subnet_mask", string_to_byte_array_gvalue ("255.255.252.0"));
-
- ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
- ASSERT (ip4_config != NULL,
- "dhcp-ip4-prefix-classless", "failed to parse DHCP4 options");
-
- ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
- "dhcp-ip4-prefix-classless", "unexpected number of IP4 addresses (not 1)");
-
- address = nm_ip4_config_get_address (ip4_config, 0);
- ASSERT (address,
- "dhcp-ip4-prefix-classless", "missing IP4 address #1");
-
- ASSERT (address->plen == 22,
- "dhcp-ip4-prefix-classless", "unexpected IP4 address prefix %d (expected 22)",
- address->plen);
-
- g_hash_table_destroy (options);
-}
-
-NMTST_DEFINE ();
-
-int main (int argc, char **argv)
-{
- char *path;
- const char *clients[2][2] = { {DHCLIENT_PATH, "dhclient"}, {DHCPCD_PATH, "dhcpcd"} };
- guint32 i;
-
- nmtst_init_assert_logging (&argc, &argv);
- nm_logging_setup ("WARN", "DEFAULT", NULL, NULL);
-
- for (i = 0; i < 2; i++) {
- const char *client_path = clients[i][0];
- const char *client = clients[i][1];
-
- if (!client_path || !strlen (client_path))
- continue;
-
- path = g_strdup_printf ("/dhcp/%s/generic-options", client);
- g_test_add_data_func (path, client, test_generic_options);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/wins-options", client);
- g_test_add_data_func (path, client, test_wins_options);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/classless-static-routes-1", client);
- g_test_add_data_func (path, client, test_classless_static_routes_1);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/classless-static-routes-2", client);
- g_test_add_data_func (path, client, test_classless_static_routes_2);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/fedora-dhclient-classless-static-routes", client);
- g_test_add_data_func (path, client, test_fedora_dhclient_classless_static_routes);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhclient-invalid-classless-routes-1", client);
- g_test_add_data_func (path, client, test_dhclient_invalid_classless_routes_1);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhcpcd-invalid-classless-routes-1", client);
- g_test_add_data_func (path, client, test_dhcpcd_invalid_classless_routes_1);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhclient-invalid-classless-routes-2", client);
- g_test_add_data_func (path, client, test_dhclient_invalid_classless_routes_2);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhcpcd-invalid-classless-routes-2", client);
- g_test_add_data_func (path, client, test_dhcpcd_invalid_classless_routes_2);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhclient-invalid-classless-routes-3", client);
- g_test_add_data_func (path, client, test_dhclient_invalid_classless_routes_3);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhcpcd-invalid-classless-routes-3", client);
- g_test_add_data_func (path, client, test_dhcpcd_invalid_classless_routes_3);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhclient-gw-in-classless-routes", client);
- g_test_add_data_func (path, client, test_dhclient_gw_in_classless_routes);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/dhcpcd-gw-in-classless-routes", client);
- g_test_add_data_func (path, client, test_dhcpcd_gw_in_classless_routes);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/escaped-domain-searches", client);
- g_test_add_data_func (path, client, test_escaped_domain_searches);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/invalid-escaped-domain-searches", client);
- g_test_add_data_func (path, client, test_invalid_escaped_domain_searches);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/ip4-missing-prefix-24", client);
- g_test_add_data_func (path, client, test_ip4_missing_prefix_24);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/ip4-missing-prefix-16", client);
- g_test_add_data_func (path, client, test_ip4_missing_prefix_16);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/ip4-missing-prefix-8", client);
- g_test_add_data_func (path, client, test_ip4_missing_prefix_8);
- g_free (path);
-
- path = g_strdup_printf ("/dhcp/%s/ip4-prefix-classless", client);
- g_test_add_data_func (path, client, test_ip4_prefix_classless);
- g_free (path);
- }
-
- return g_test_run ();
-}
-