From 19ddcb8ad78bf043bf0892a26e7ef24b5b793a24 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 Nov 2012 09:48:43 -0600 Subject: bridge: allow independent IPv4 and IPv6 configuration with no slaves When no slave is present, dynamic IP configuration (DHCPv4, DHCPv6, IPv6 autoconf) cannot proceed. But static and link-local configuration can. So if IPv4 requires DHCP but IPv6 is static, it makes no sense to block IPv6 configuration from proceeding just because DHCPv4 cannot. --- src/nm-device-bridge.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/src/nm-device-bridge.c b/src/nm-device-bridge.c index 35ff2af26b..6f3d70e4b3 100644 --- a/src/nm-device-bridge.c +++ b/src/nm-device-bridge.c @@ -46,7 +46,8 @@ G_DEFINE_TYPE (NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE_WIRED) #define NM_BRIDGE_ERROR (nm_bridge_error_quark ()) typedef struct { - gboolean unused; + gboolean ip4_waiting; + gboolean ip6_waiting; } NMDeviceBridgePrivate; enum { @@ -85,10 +86,17 @@ device_state_changed (NMDevice *device, NMDeviceState old_state, NMDeviceStateReason reason) { + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (device); + if (new_state == NM_DEVICE_STATE_UNAVAILABLE) { /* Use NM_DEVICE_STATE_REASON_CARRIER to make sure num retries is reset */ nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER); } + + if (new_state <= NM_DEVICE_STATE_DISCONNECTED || new_state > NM_DEVICE_STATE_ACTIVATED) { + priv->ip4_waiting = FALSE; + priv->ip6_waiting = FALSE; + } } static void @@ -363,6 +371,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + NM_DEVICE_BRIDGE_GET_PRIVATE (dev)->ip4_waiting = FALSE; + NM_DEVICE_BRIDGE_GET_PRIVATE (dev)->ip6_waiting = FALSE; + ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (dev, reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { connection = nm_device_get_connection (dev); @@ -387,6 +398,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) static gboolean enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) { + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (device); gboolean success; NMSettingBridgePort *s_port; const char *iface = nm_device_get_ip_iface (device); @@ -410,6 +422,20 @@ enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) nm_log_info (LOGD_DEVICE, "(%s): attached bridge port %s", iface, slave_iface); g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES); + + /* If waiting for a slave to continue with IP config, start now */ + if (priv->ip4_waiting) { + nm_log_info (LOGD_DEVICE | LOGD_IP4, "(%s): retrying IPv4 config with first slave", iface); + priv->ip4_waiting = FALSE; + nm_device_activate_stage3_ip4_start (device); + } + + if (priv->ip6_waiting) { + nm_log_info (LOGD_DEVICE | LOGD_IP6, "(%s): retrying IPv6 config with first slave", iface); + priv->ip6_waiting = FALSE; + nm_device_activate_stage3_ip6_start (device); + } + return TRUE; } @@ -430,6 +456,92 @@ release_slave (NMDevice *device, NMDevice *slave) return success; } +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *reason) +{ + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (device); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMConnection *connection; + NMSettingIP4Config *s_ip4; + const char *method = NULL; + GSList *slaves; + + priv->ip4_waiting = FALSE; + + slaves = nm_device_master_get_slaves (device); + if (slaves == NULL) { + connection = nm_device_get_connection (device); + g_assert (connection); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) + priv->ip4_waiting = TRUE; + } + g_slist_free (slaves); + + if (priv->ip4_waiting) { + ret = NM_ACT_STAGE_RETURN_WAIT; + nm_log_info (LOGD_DEVICE | LOGD_IP4, "(%s): IPv4 config waiting until slaves are present", + nm_device_get_ip_iface (device)); + } else { + /* We have slaves; proceed with normal IPv4 configuration */ + ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage3_ip4_config_start (device, out_config, reason); + } + + return ret; +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *reason) +{ + NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (device); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMConnection *connection; + NMSettingIP6Config *s_ip6; + const char *method = NULL; + GSList *slaves; + + priv->ip6_waiting = FALSE; + + slaves = nm_device_master_get_slaves (device); + if (slaves == NULL) { + connection = nm_device_get_connection (device); + g_assert (connection); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6) + method = nm_setting_ip6_config_get_method (s_ip6); + + /* SLAAC, DHCP, and Link-Local depend on connectivity (and thus slaves) + * to complete addressing. SLAAC and DHCP obviously need a peer to + * provide a prefix, while Link-Local must perform DAD on the local link. + */ + if ( !g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || !g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) + || !g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + priv->ip6_waiting = TRUE; + } + g_slist_free (slaves); + + if (priv->ip6_waiting) { + ret = NM_ACT_STAGE_RETURN_WAIT; + nm_log_info (LOGD_DEVICE | LOGD_IP6, "(%s): IPv6 config waiting until slaves are present", + nm_device_get_ip_iface (device)); + } else { + /* We have slaves; proceed with normal IPv6 configuration */ + ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage3_ip6_config_start (device, out_config, reason); + } + + return ret; +} + /******************************************************************/ NMDevice * @@ -527,6 +639,8 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) parent_class->connection_match_config = connection_match_config; parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; parent_class->enslave_slave = enslave_slave; parent_class->release_slave = release_slave; -- cgit v1.2.1