From 3c4f70c5c2ac1342abd358b93580c2feecf75894 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 23 Sep 2019 18:37:32 +0200 Subject: [PATCH] dhcp: systemd: support DHCPv6 prefix delegation Add support to the internal DHCP client for requesting a prefix and distributing it to interfaces with 'shared' IPv6 mode. The systemd-networkd API currently allows to request only a single prefix and so there will be issues when the number of downstream interfaces is greater than the number of /64 subnets available in the returned prefix; but this is still an improvement over the previous situation when no prefix was requested at all. https://gitlab.freedesktop.org/NetworkManager/NetworkManager/issues/247 --- src/dhcp/nm-dhcp-client.c | 13 ++++++++++--- src/dhcp/nm-dhcp-client.h | 3 +++ src/dhcp/nm-dhcp-systemd.c | 32 ++++++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 6e4c28c122..6eec21a9c2 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -805,6 +805,15 @@ maybe_add_option (NMDhcpClient *self, } } +void +nm_dhcp_client_emit_ipv6_prefix_delegated (NMDhcpClient *self, + const NMPlatformIP6Address *prefix) +{ + g_signal_emit (G_OBJECT (self), + signals[SIGNAL_PREFIX_DELEGATED], 0, + prefix); +} + gboolean nm_dhcp_client_handle_event (gpointer unused, const char *iface, @@ -876,9 +885,7 @@ nm_dhcp_client_handle_event (gpointer unused, /* If we got an IPv6 prefix to delegate, we don't change the state * of the DHCP client instance. Instead, we just signal the prefix * to the device. */ - g_signal_emit (G_OBJECT (self), - signals[SIGNAL_PREFIX_DELEGATED], 0, - &prefix); + nm_dhcp_client_emit_ipv6_prefix_delegated (self, &prefix); } else { /* Fail if no valid IP config was received */ if ( new_state == NM_DHCP_STATE_BOUND diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 9263565192..ea74c8b3e3 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -189,6 +189,9 @@ void nm_dhcp_client_set_client_id_bin (NMDhcpClient *self, const guint8 *client_id, gsize len); +void nm_dhcp_client_emit_ipv6_prefix_delegated (NMDhcpClient *self, + const NMPlatformIP6Address *prefix); + /***************************************************************************** * Client data *****************************************************************************/ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index b943bead06..7f0cd3325e 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -709,6 +709,7 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, sd_dhcp6_lease *lease, gboolean info_only, GHashTable **out_options, + gint32 ts, GError **error) { gs_unref_object NMIP6Config *ip6_config = NULL; @@ -719,7 +720,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, char **domains; nm_auto_free_gstring GString *str = NULL; int num, i; - const gint32 ts = nm_utils_get_monotonic_timestamp_s (); g_return_val_if_fail (lease, NULL); @@ -794,10 +794,12 @@ static void bound6_handle (NMDhcpSystemd *self) { NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); + const gint32 ts = nm_utils_get_monotonic_timestamp_s (); const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); gs_unref_object NMIP6Config *ip6_config = NULL; gs_unref_hashtable GHashTable *options = NULL; gs_free_error GError *error = NULL; + NMPlatformIP6Address prefix = { 0 }; sd_dhcp6_lease *lease; if ( sd_dhcp6_client_get_lease (priv->client6, &lease) < 0 @@ -815,6 +817,7 @@ bound6_handle (NMDhcpSystemd *self) lease, nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)), &options, + ts, &error); if (!ip6_config) { @@ -827,6 +830,16 @@ bound6_handle (NMDhcpSystemd *self) NM_DHCP_STATE_BOUND, NM_IP_CONFIG_CAST (ip6_config), options); + + sd_dhcp6_lease_reset_pd_prefix_iter (lease); + while (!sd_dhcp6_lease_get_pd (lease, + &prefix.address, + &prefix.plen, + &prefix.preferred, + &prefix.lifetime)) { + prefix.timestamp = ts; + nm_dhcp_client_emit_ipv6_prefix_delegated (NM_DHCP_CLIENT (self), &prefix); + } } static void @@ -895,11 +908,6 @@ ip6_start (NMDhcpClient *client, return FALSE; } - if (needed_prefixes > 0) { - _LOGW ("dhcp-client6: prefix delegation not yet supported, won't supply %d prefixes", - needed_prefixes); - } - _LOGT ("dhcp-client6: set %p", sd_client); if (nm_dhcp_client_get_info_only (client)) @@ -962,6 +970,18 @@ ip6_start (NMDhcpClient *client, } } + if (needed_prefixes > 0) { + if (needed_prefixes > 1) + _LOGW ("dhcp-client6: only one prefix request is supported"); + /* FIXME: systemd-networkd API only allows to request a + * single prefix */ + r = sd_dhcp6_client_set_prefix_delegation (sd_client, TRUE); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to enable prefix delegation: %s"); + return FALSE; + } + } + r = sd_dhcp6_client_set_local_address (sd_client, ll_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set local address: %s");