device: accept multiple addresses in a DHCPv6 lease

When the DHCPv6 lease received from the server contains multiple
addresses, dhclient generates a new BOUND event for each of
them. Instead of overwriting the previous IP6 configuration for each
BOUND event, we should try to detect if the new configuration belongs
to the same lease and merge its addresses with the existing one in
such case.

This allows NetworkManager to configure multiple addresses on an
interface via DHCPv6.

https://bugzilla.gnome.org/show_bug.cgi?id=681764
https://bugzilla.redhat.com/show_bug.cgi?id=1244293
(cherry picked from commit 1d6e8e8da7)
This commit is contained in:
Beniamino Galvani 2015-08-04 11:30:03 +02:00
parent eb1ccf90b8
commit 78fc92abe8
4 changed files with 48 additions and 8 deletions

View file

@ -332,6 +332,8 @@ typedef struct {
NMDhcp6Config * dhcp6_config;
/* IP6 config from DHCP */
NMIP6Config * dhcp6_ip6_config;
/* Event ID of the current IP6 config from DHCP */
char * dhcp6_event_id;
guint dhcp6_restart_id;
/* allow autoconnect feature */
@ -3494,6 +3496,7 @@ dhcp4_state_changed (NMDhcpClient *client,
NMDhcpState state,
NMIP4Config *ip4_config,
GHashTable *options,
const char *event_id,
gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
@ -3872,6 +3875,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
g_clear_object (&priv->dhcp6_ip6_config);
g_clear_pointer (&priv->dhcp6_event_id, g_free);
nm_clear_g_source (&priv->dhcp6_restart_id);
@ -4186,10 +4190,12 @@ dhcp6_state_changed (NMDhcpClient *client,
NMDhcpState state,
NMIP6Config *ip6_config,
GHashTable *options,
const char *event_id,
gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
guint i;
g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config));
@ -4198,10 +4204,27 @@ dhcp6_state_changed (NMDhcpClient *client,
switch (state) {
case NM_DHCP_STATE_BOUND:
g_clear_object (&priv->dhcp6_ip6_config);
if (ip6_config) {
priv->dhcp6_ip6_config = g_object_ref (ip6_config);
dhcp6_update_config (self, priv->dhcp6_config, options);
/* If the server sends multiple IPv6 addresses, we receive a state
* changed event for each of them. Use the event ID to merge IPv6
* addresses from the same transaction into a single configuration.
*/
if ( ip6_config
&& event_id
&& priv->dhcp6_event_id
&& !strcmp (event_id, priv->dhcp6_event_id)) {
for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
nm_ip6_config_add_address (priv->dhcp6_ip6_config,
nm_ip6_config_get_address (ip6_config, i));
}
} else {
g_clear_object (&priv->dhcp6_ip6_config);
g_clear_pointer (&priv->dhcp6_event_id, g_free);
if (ip6_config) {
priv->dhcp6_ip6_config = g_object_ref (ip6_config);
priv->dhcp6_event_id = g_strdup (event_id);
dhcp6_update_config (self, priv->dhcp6_config, options);
g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
}
}
if (priv->ip6_state == IP_CONF) {
@ -4294,6 +4317,7 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
g_warn_if_fail (priv->dhcp6_ip6_config == NULL);
g_clear_object (&priv->dhcp6_ip6_config);
g_clear_pointer (&priv->dhcp6_event_id, g_free);
connection = nm_device_get_connection (self);
g_assert (connection);

View file

@ -36,6 +36,7 @@
#include "nm-dhcp-client.h"
#include "nm-dhcp-utils.h"
#include "nm-platform.h"
#include "gsystem-local-alloc.h"
typedef struct {
char * iface;
@ -284,6 +285,7 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
GHashTable *options)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
gs_free char *event_id = NULL;
if (new_state >= NM_DHCP_STATE_BOUND)
timeout_cleanup (self);
@ -308,19 +310,30 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
return;
if (priv->ipv6 && new_state == NM_DHCP_STATE_BOUND) {
char *start, *iaid;
iaid = g_hash_table_lookup (options, "iaid");
start = g_hash_table_lookup (options, "life_starts");
if (iaid && start)
event_id = g_strdup_printf ("%s|%s", iaid, start);
}
nm_log_info (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
"(%s): DHCPv%c state changed %s -> %s",
"(%s): DHCPv%c state changed %s -> %s%s%s%s",
priv->iface,
priv->ipv6 ? '6' : '4',
state_to_string (priv->state),
state_to_string (new_state));
state_to_string (new_state),
NM_PRINT_FMT_QUOTED (event_id, ", event ID=\"", event_id, "\"", ""));
priv->state = new_state;
g_signal_emit (G_OBJECT (self),
signals[SIGNAL_STATE_CHANGED], 0,
new_state,
ip_config,
options);
options,
event_id);
}
static gboolean
@ -977,6 +990,6 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMDhcpClientClass, state_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE);
G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING);
}

View file

@ -184,6 +184,7 @@ static void client_state_changed (NMDhcpClient *client,
NMDhcpState state,
GObject *ip_config,
GHashTable *options,
const char *event_id,
NMDhcpManager *self);
static void
@ -204,6 +205,7 @@ client_state_changed (NMDhcpClient *client,
NMDhcpState state,
GObject *ip_config,
GHashTable *options,
const char *event_id,
NMDhcpManager *self)
{
if (state >= NM_DHCP_STATE_TIMEOUT)

View file

@ -87,6 +87,7 @@ dhcp4_state_changed (NMDhcpClient *client,
NMDhcpState state,
NMIP4Config *ip4_config,
GHashTable *options,
const char *event_id,
gpointer user_data)
{
static NMIP4Config *last_config = NULL;