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
This commit is contained in:
Dan Williams 2014-07-22 14:39:32 -05:00
commit afebbb6ef8
14 changed files with 1897 additions and 2263 deletions

View file

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

View file

@ -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,35 +2674,48 @@ 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)
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
}
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,43 +3103,67 @@ 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)
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
}
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) ||

File diff suppressed because it is too large Load diff

View file

@ -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);
NMIP6Config *nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test);
/* Backend helpers */
/* Backend helpers for subclasses */
void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
void nm_dhcp_client_stop_pid (GPid pid, const char *iface);
void nm_dhcp_client_stop_pid (pid_t pid, const char *iface);
void nm_dhcp_client_watch_child (NMDHCPClient *self, pid_t pid);
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 */

View file

@ -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)) {
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);
pid = -1;
} else {
nm_log_info (log_domain, "dhclient started with pid %d", pid);
priv->pid_file = pid_file;
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) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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