diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 7eb73006d7..a47f929a53 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -261,7 +261,8 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self, gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, NMSettingIP6Config *s_ip6, - guint8 *dhcp_anycast_addr) + guint8 *dhcp_anycast_addr, + gboolean info_only) { NMDHCPClientPrivate *priv; @@ -276,7 +277,7 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self, g_message ("Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)", priv->iface, priv->timeout); - priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr); + priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, info_only); if (priv->pid > 0) start_monitor (self); diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index 50c49bbc8a..2ebb5a745c 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -88,7 +88,8 @@ typedef struct { GPid (*ip6_start) (NMDHCPClient *self, NMSettingIP6Config *s_ip6, - guint8 *anycast_addr); + guint8 *anycast_addr, + gboolean info_only); void (*stop) (NMDHCPClient *self); @@ -113,7 +114,8 @@ gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self, gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, NMSettingIP6Config *s_ip6, - guint8 *dhcp_anycast_addr); + guint8 *dhcp_anycast_addr, + gboolean info_only); void nm_dhcp_client_stop (NMDHCPClient *self); diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 2ab6aba825..1c5625798a 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -449,7 +449,8 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) static GPid dhclient_start (NMDHCPClient *client, - const char *ip_opt) + const char *ip_opt, + const char *mode_opt) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); GPtrArray *argv = NULL; @@ -497,6 +498,9 @@ dhclient_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) ip_opt); + if (mode_opt) + g_ptr_array_add (argv, (gpointer) mode_opt); + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); @@ -542,15 +546,16 @@ real_ip4_start (NMDHCPClient *client, return -1; } - return dhclient_start (client, "-4"); + return dhclient_start (client, "-4", NULL); } static GPid real_ip6_start (NMDHCPClient *client, NMSettingIP6Config *s_ip6, - guint8 *dhcp_anycast_addr) + guint8 *dhcp_anycast_addr, + gboolean info_only) { - return dhclient_start (client, "-6"); + return dhclient_start (client, "-6", info_only ? "-S" : "-N"); } static void diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index 741fe7f76b..27fb31bcc4 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -123,7 +123,8 @@ real_ip4_start (NMDHCPClient *client, static GPid real_ip6_start (NMDHCPClient *client, NMSettingIP6Config *s_ip6, - guint8 *dhcp_anycast_addr) + guint8 *dhcp_anycast_addr, + gboolean info_only) { g_warning ("The dhcpcd backend does not support IPv6."); return -1; diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 9dc90ac76b..241199f401 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -367,7 +367,8 @@ client_start (NMDHCPManager *self, NMSettingIP4Config *s_ip4, NMSettingIP6Config *s_ip6, guint32 timeout, - guint8 *dhcp_anycast_addr) + guint8 *dhcp_anycast_addr, + gboolean info_only) { NMDHCPManagerPrivate *priv; NMDHCPClient *client; @@ -398,7 +399,7 @@ client_start (NMDHCPManager *self, add_client (self, client); if (ipv6) - success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr); + success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, info_only); else success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr); @@ -446,7 +447,7 @@ nm_dhcp_manager_start_ip4 (NMDHCPManager *self, g_object_ref (s_ip4); } - client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr); + client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, FALSE); if (s_ip4) g_object_unref (s_ip4); @@ -461,9 +462,10 @@ nm_dhcp_manager_start_ip6 (NMDHCPManager *self, const char *uuid, NMSettingIP6Config *s_ip6, guint32 timeout, - guint8 *dhcp_anycast_addr) + guint8 *dhcp_anycast_addr, + gboolean info_only) { - return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr); + return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, info_only); } static void diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 690241482b..648f4cd0dd 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -67,7 +67,8 @@ NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager, const char *uuid, NMSettingIP6Config *s_ip6, guint32 timeout, - guint8 *dhcp_anycast_addr); + guint8 *dhcp_anycast_addr, + gboolean info_only); GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self, const char *iface, diff --git a/src/nm-device.c b/src/nm-device.c index 5acfb2a0dc..7a2f83d96f 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -130,6 +130,7 @@ typedef struct { gulong ip6_addrconf_sigid; gulong ip6_config_changed_sigid; gboolean ip6_waiting_for_config; + guint32 ip6_dhcp_opt; NMDHCPClient * dhcp6_client; gulong dhcp6_state_sigid; @@ -167,6 +168,11 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev, gboolean assumed, NMDeviceStateReason *reason); +static NMActStageReturn dhcp6_start (NMDevice *self, + NMConnection *connection, + guint32 dhcp_opt, + NMDeviceStateReason *reason); + static void device_interface_init (NMDeviceInterface *device_interface_class) { @@ -542,18 +548,62 @@ ip6_addrconf_complete (NMIP6Manager *ip6_manager, { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + NMActStageReturn ret; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + NMDeviceState state; if (strcmp (nm_device_get_ip_iface (self), iface) != 0) return; - if (!nm_device_get_act_request (self)) + req = nm_device_get_act_request (self); + if (!req) + return; + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if (!priv->ip6_waiting_for_config) return; - if (priv->ip6_waiting_for_config) { - priv->ip6_waiting_for_config = FALSE; - if (success) - nm_device_activate_schedule_stage4_ip6_config_get (self); - else - nm_device_activate_schedule_stage4_ip6_config_timeout (self); + priv->ip6_waiting_for_config = FALSE; + + if (!success) { + nm_device_activate_schedule_stage4_ip6_config_timeout (self); + return; + } + + priv->ip6_dhcp_opt = dhcp_opts; + + /* If addrconf is all that's required, we're done */ + if (dhcp_opts == IP6_DHCP_OPT_NONE) { + nm_device_activate_schedule_stage4_ip6_config_get (self); + return; + } + + /* If the router said to use DHCP for managed or otherconf, do it */ + + /* Don't re-start DHCPv6 if it's already in progress */ + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + if ((state != NM_DEVICE_STATE_IP_CONFIG) || priv->dhcp6_client) + return; + + nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6" + " as requested by IPv6 router...", + priv->iface); + + ret = dhcp6_start (self, connection, dhcp_opts, &reason); + switch (ret) { + case NM_ACT_STAGE_RETURN_SUCCESS: + /* Shouldn't get this, but handle it anyway */ + g_warn_if_reached (); + nm_device_activate_schedule_stage4_ip6_config_get (self); + break; + case NM_ACT_STAGE_RETURN_POSTPONE: + /* Success; wait for DHCPv6 to complete */ + break; + default: + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + break; } } @@ -596,6 +646,7 @@ addrconf6_setup (NMDevice *self) NMSettingIP6Config *s_ip6; priv->ip6_waiting_for_config = FALSE; + priv->ip6_dhcp_opt = IP6_DHCP_OPT_NONE; req = nm_device_get_act_request (self); g_assert (req); @@ -662,6 +713,7 @@ static gboolean nm_device_activate_stage1_device_prepare (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *iface; NMActStageReturn ret; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; @@ -669,6 +721,8 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); + priv->ip4_ready = priv->ip6_ready = FALSE; + iface = nm_device_get_iface (self); nm_info ("Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); @@ -1325,11 +1379,78 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) return ret; } +static NMActStageReturn +dhcp6_start (NMDevice *self, + NMConnection *connection, + guint32 dhcp_opt, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + guint8 *anycast = NULL; + NMSettingIP6Config *s_ip6; + NMSettingConnection *s_con; + const char *uuid; + const char *ip_iface; + + if (!connection) { + NMActRequest *req; + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + } + + /* Begin a DHCP transaction on the interface */ + + if (priv->dhcp_anycast_address) + anycast = priv->dhcp_anycast_address->data; + + /* Clear old exported DHCP options */ + if (priv->dhcp6_config) + g_object_unref (priv->dhcp6_config); + priv->dhcp6_config = nm_dhcp6_config_new (); + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + uuid = nm_setting_connection_get_uuid (s_con); + + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + + ip_iface = nm_device_get_ip_iface (self); + priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager, + ip_iface, + uuid, + s_ip6, + priv->dhcp_timeout, + anycast, + (dhcp_opt == IP6_DHCP_OPT_OTHERCONF) ? TRUE : FALSE); + if (priv->dhcp6_client) { + priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client, + "state-changed", + G_CALLBACK (dhcp_state_changed), + self); + priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client, + "timeout", + G_CALLBACK (dhcp_timeout), + self); + + /* DHCP devices will be notified by the DHCP manager when stuff happens */ + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + + return ret; +} + static NMActStageReturn real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ip_iface, *uuid; + const char *ip_iface; NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMActRequest *req; NMConnection *connection; @@ -1347,50 +1468,8 @@ real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason) priv->ip6_waiting_for_config = TRUE; nm_ip6_manager_begin_addrconf (priv->ip6_manager, ip_iface); ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { - guint8 *anycast = NULL; - NMSettingIP6Config *s_ip6; - NMSettingConnection *s_con; - - /* Begin a DHCP transaction on the interface */ - - if (priv->dhcp_anycast_address) - anycast = priv->dhcp_anycast_address->data; - - /* Clear old exported DHCP options */ - if (priv->dhcp6_config) - g_object_unref (priv->dhcp6_config); - priv->dhcp6_config = nm_dhcp6_config_new (); - - s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); - g_assert (s_con); - uuid = nm_setting_connection_get_uuid (s_con); - - s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); - - priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager, - ip_iface, - uuid, - s_ip6, - priv->dhcp_timeout, - anycast); - if (priv->dhcp6_client) { - priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client, - "state-changed", - G_CALLBACK (dhcp_state_changed), - self); - priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client, - "timeout", - G_CALLBACK (dhcp_timeout), - self); - - /* DHCP devices will be notified by the DHCP manager when stuff happens */ - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else { - *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - } + } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) + ret = dhcp6_start (self, connection, IP6_DHCP_OPT_MANAGED, reason); return ret; } @@ -1406,7 +1485,6 @@ static gboolean nm_device_activate_stage3_ip_config_start (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *iface; NMActStageReturn ret; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; @@ -1414,8 +1492,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); - priv->ip4_ready = priv->ip6_ready = FALSE; - iface = nm_device_get_iface (self); nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface); nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); @@ -1772,10 +1848,13 @@ real_act_stage4_get_ip6_config (NMDevice *self, ret = NM_ACT_STAGE_RETURN_SUCCESS; } else { *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + goto out; } - } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { - g_assert (priv->dhcp6_client); + } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) + g_assert (priv->dhcp6_client); /* sanity check */ + /* Autoconf might have triggered DHCPv6 too */ + if (priv->dhcp6_client) { *config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE); if (*config) { /* Merge user-defined overrides into the IP4Config to be applied */ @@ -1797,6 +1876,7 @@ real_act_stage4_get_ip6_config (NMDevice *self, ret = NM_ACT_STAGE_RETURN_SUCCESS; } +out: return ret; }