From 7a839370387010fd853bc85f8e853e51c5b21536 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 31 Oct 2016 23:29:51 +0100 Subject: device: add prefix delegation machinery Two parts: contributing the prefixes delegated to the requesting (uplink) interface to the policy's pool and applying the negotiated subjects on the shared (downlink) devices. --- src/devices/nm-device.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-- src/devices/nm-device.h | 10 +++++ 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 886af3f1c8..106fa15f82 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -83,6 +83,8 @@ enum { AUTH_REQUEST, IP4_CONFIG_CHANGED, IP6_CONFIG_CHANGED, + IP6_PREFIX_DELEGATED, + IP6_SUBNET_NEEDED, REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, @@ -389,6 +391,7 @@ typedef struct _NMDevicePrivate { NMDhcpClient * client; NMNDiscDHCPLevel mode; gulong state_sigid; + gulong prefix_sigid; NMDhcp6Config * config; /* IP6 config from DHCP */ NMIP6Config * ip6_config; @@ -396,8 +399,11 @@ typedef struct _NMDevicePrivate { char * event_id; guint restart_id; guint num_tries_left; + guint needed_prefixes; } dhcp6; + gboolean needs_ip6_subnet; + /* allow autoconnect feature */ bool autoconnect; @@ -5474,6 +5480,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) if (priv->dhcp6.client) { nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.state_sigid); + nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.prefix_sigid); if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) @@ -5928,6 +5935,19 @@ dhcp6_state_changed (NMDhcpClient *client, } } +static void +dhcp6_prefix_delegated (NMDhcpClient *client, + NMPlatformIP6Address *prefix, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + /* Just re-emit. The device just contributes the prefix to the + * pool in NMPolicy, which decides about subnet allocation + * on the shared devices. */ + g_signal_emit (self, signals[IP6_PREFIX_DELEGATED], 0, prefix); +} + static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) { @@ -5965,7 +5985,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) priv->dhcp_timeout, priv->dhcp_anycast_address, (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE, - nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))); + nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), + priv->dhcp6.needed_prefixes); if (tmp) g_byte_array_free (tmp, TRUE); @@ -5974,6 +5995,10 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (dhcp6_state_changed), self); + priv->dhcp6.prefix_sigid = g_signal_connect (priv->dhcp6.client, + NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED, + G_CALLBACK (dhcp6_prefix_delegated), + self); } return !!priv->dhcp6.client; @@ -6040,6 +6065,53 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release) /*****************************************************************************/ +/* + * Called on the requesting interface when a subnet can't be obtained + * from known prefixes for a newly active shared connection. + */ +void +nm_device_request_ip6_prefixes (NMDevice *self, int needed_prefixes) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + _LOGD (LOGD_IP6, "ipv6-pd: asking DHCP6 for %d prefixes", needed_prefixes); + priv->dhcp6.needed_prefixes = needed_prefixes; + nm_device_dhcp6_renew (self, FALSE); +} + +gboolean +nm_device_needs_ip6_subnet (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->needs_ip6_subnet; +} + +/* + * Called on the ipv6.method=shared interface when a new subnet is allocated + * or the prefix from which it is allocated is renewed. + */ +void +nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMPlatformIP6Address address = *subnet; + + if (!priv->ac_ip6_config) + priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + + /* Assign a ::1 address in the subnet for us. */ + address.address.s6_addr32[3] |= htonl (1); + nm_ip6_config_add_address (priv->ac_ip6_config, &address); + + _LOGD (LOGD_IP6, "ipv6-pd: using %s address", + nm_utils_inet6_ntop (&address.address, NULL)); + + /* This also updates the ndisc if there are actual changes. */ + if (!ip6_config_merge_and_apply (self, TRUE, NULL)) + _LOGW (LOGD_IP6, "ipv6-pd: failed applying IP6 config for connection sharing"); +} + +/*****************************************************************************/ + static void linklocal6_cleanup (NMDevice *self) { @@ -6065,6 +6137,12 @@ linklocal6_timeout_cb (gpointer user_data) return G_SOURCE_REMOVE; } +static void +pd6_ask_for_prefix (NMDevice *self) +{ + g_signal_emit (self, signals[IP6_SUBNET_NEEDED], 0); +} + static void linklocal6_complete (NMDevice *self) { @@ -6084,8 +6162,7 @@ linklocal6_complete (NMDevice *self) _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { if (!addrconf6_start_with_link_ready (self)) { /* Time out IPv6 instead of failing the entire activation */ nm_device_activate_schedule_ip6_config_timeout (self); @@ -6095,6 +6172,10 @@ linklocal6_complete (NMDevice *self) /* Time out IPv6 instead of failing the entire activation */ nm_device_activate_schedule_ip6_config_timeout (self); } + } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { + priv->needs_ip6_subnet = TRUE; + pd6_ask_for_prefix (self); + nm_device_activate_schedule_ip6_config_timeout (self); } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) nm_device_activate_schedule_ip6_config_result (self); else @@ -6508,11 +6589,13 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) connection = nm_device_get_applied_connection (self); g_assert (connection); +#if 0 g_warn_if_fail (priv->ac_ip6_config == NULL); if (priv->ac_ip6_config) { g_object_unref (priv->ac_ip6_config); priv->ac_ip6_config = NULL; } +#endif s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); g_assert (s_ip6); @@ -6929,6 +7012,7 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) _set_ip_state (self, AF_INET6, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { +#if 0 if (!ip6_config) ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); /* Here we get a static IPv6 config, like for Shared where it's @@ -6936,6 +7020,10 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) */ g_warn_if_fail (priv->ac_ip6_config == NULL); priv->ac_ip6_config = ip6_config; +#else + if (!priv->ac_ip6_config) + priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); +#endif nm_device_activate_schedule_ip6_config_result (self); } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { _set_ip_state (self, AF_INET6, IP_DONE); @@ -10886,6 +10974,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_slist_free_full (priv->vpn6_configs, g_object_unref); priv->vpn6_configs = NULL; + priv->needs_ip6_subnet = FALSE; + clear_act_request (self); /* Clear legacy IPv4 address property */ @@ -13165,6 +13255,20 @@ nm_device_class_init (NMDeviceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); + signals[IP6_PREFIX_DELEGATED] = + g_signal_new (NM_DEVICE_IP6_PREFIX_DELEGATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[IP6_SUBNET_NEEDED] = + g_signal_new (NM_DEVICE_IP6_SUBNET_NEEDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[REMOVED] = g_signal_new (NM_DEVICE_REMOVED, G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 356331189c..c1035a5b6c 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -82,6 +82,8 @@ #define NM_DEVICE_AUTH_REQUEST "auth-request" #define NM_DEVICE_IP4_CONFIG_CHANGED "ip4-config-changed" #define NM_DEVICE_IP6_CONFIG_CHANGED "ip6-config-changed" +#define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" +#define NM_DEVICE_IP6_SUBNET_NEEDED "ip6-subnet-needed" #define NM_DEVICE_REMOVED "removed" #define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" #define NM_DEVICE_RECHECK_ASSUME "recheck-assume" @@ -439,6 +441,14 @@ void nm_device_set_enabled (NMDevice *device, gboolean enabled); RfKillType nm_device_get_rfkill_type (NMDevice *device); +/* IPv6 prefix delegation */ + +void nm_device_request_ip6_prefixes (NMDevice *self, int needed_prefixes); + +gboolean nm_device_needs_ip6_subnet (NMDevice *self); + +void nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet); + /** * NMUnmanagedFlags: * @NM_UNMANAGED_NONE: placeholder value -- cgit v1.2.1