summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2015-08-04 11:30:03 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2015-08-04 14:11:08 +0200
commit0322d129848836c0500047d1a75261436ebf5b28 (patch)
tree01232d8d20f23e3260189f6ed8f8e095648b2210
parent7c38b784632ecc490f7440a286dc81b452dc6213 (diff)
downloadNetworkManager-bg/dhcp6-multiple-addrs-bgo681764.tar.gz
device: accept multiple addresses in a DHCPv6 leasebg/dhcp6-multiple-addrs-bgo681764
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
-rw-r--r--src/devices/nm-device.c33
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c21
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c2
-rw-r--r--src/nm-iface-helper.c1
4 files changed, 48 insertions, 9 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index d983d7a5ed..f6ed4c2979 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -326,6 +326,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;
/* allow autoconnect feature */
gboolean autoconnect;
@@ -3628,6 +3630,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);
@@ -4007,6 +4010,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);
if (priv->dhcp6_client) {
if (priv->dhcp6_state_sigid) {
@@ -4269,10 +4273,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));
@@ -4281,11 +4287,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);
- nm_dhcp6_config_set_options (priv->dhcp6_config, options);
- g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+ /* 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);
+ nm_dhcp6_config_set_options (priv->dhcp6_config, options);
+ g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+ }
}
if (priv->ip6_state == IP_CONF) {
@@ -4378,6 +4400,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);
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index c00782f963..caeba85b60 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -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
@@ -979,6 +992,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);
}
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 0f2b8faeb3..cb349fc418 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -183,6 +183,7 @@ static void client_state_changed (NMDhcpClient *client,
NMDhcpState state,
GObject *ip_config,
GHashTable *options,
+ const char *event_id,
NMDhcpManager *self);
static void
@@ -203,6 +204,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)
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index a35d0d7c04..fac16d105d 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -88,6 +88,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;