From 2ef7ea02b35aa47401d0cf3e97a0daa2e5267884 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Jan 2010 14:57:23 -0800 Subject: [PATCH 01/20] test: enable IPv6 setting defaults test --- libnm-util/tests/test-settings-defaults.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnm-util/tests/test-settings-defaults.c b/libnm-util/tests/test-settings-defaults.c index 7f0adc561f..9f38a14555 100644 --- a/libnm-util/tests/test-settings-defaults.c +++ b/libnm-util/tests/test-settings-defaults.c @@ -118,7 +118,7 @@ int main (int argc, char **argv) test_defaults (NM_TYPE_SETTING_CDMA, NM_SETTING_CDMA_SETTING_NAME); test_defaults (NM_TYPE_SETTING_GSM, NM_SETTING_GSM_SETTING_NAME); test_defaults (NM_TYPE_SETTING_IP4_CONFIG, NM_SETTING_IP4_CONFIG_SETTING_NAME); -// test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME); + test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME); test_defaults (NM_TYPE_SETTING_PPP, NM_SETTING_PPP_SETTING_NAME); test_defaults (NM_TYPE_SETTING_PPPOE, NM_SETTING_PPPOE_SETTING_NAME); test_defaults (NM_TYPE_SETTING_SERIAL, NM_SETTING_SERIAL_SETTING_NAME); From 69790deeddbae2a11bdc82e0b7f41a9772e6cfed Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Jan 2010 14:57:46 -0800 Subject: [PATCH 02/20] libnm-util: add IPv6 method 'dhcp' --- libnm-util/nm-setting-ip6-config.c | 94 ++++++++++++++++-------------- libnm-util/nm-setting-ip6-config.h | 3 +- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/libnm-util/nm-setting-ip6-config.c b/libnm-util/nm-setting-ip6-config.c index b1577f06f4..a62b697416 100644 --- a/libnm-util/nm-setting-ip6-config.c +++ b/libnm-util/nm-setting-ip6-config.c @@ -19,7 +19,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * (C) Copyright 2007 - 2008 Red Hat, Inc. + * (C) Copyright 2007 - 2010 Red Hat, Inc. */ #include @@ -435,26 +435,23 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) NM_SETTING_IP6_CONFIG_ADDRESSES); return FALSE; } - } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) - || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) + } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { - if (!priv->ignore_auto_dns) { - if (priv->dns && g_slist_length (priv->dns)) { - g_set_error (error, - NM_SETTING_IP6_CONFIG_ERROR, - NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, - NM_SETTING_IP6_CONFIG_DNS); - return FALSE; - } + if (g_slist_length (priv->dns)) { + g_set_error (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + NM_SETTING_IP6_CONFIG_DNS); + return FALSE; + } - if (g_slist_length (priv->dns_search)) { - g_set_error (error, - NM_SETTING_IP6_CONFIG_ERROR, - NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, - NM_SETTING_IP6_CONFIG_DNS_SEARCH); - return FALSE; - } + if (g_slist_length (priv->dns_search)) { + g_set_error (error, + NM_SETTING_IP6_CONFIG_ERROR, + NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, + NM_SETTING_IP6_CONFIG_DNS_SEARCH); + return FALSE; } if (g_slist_length (priv->addresses)) { @@ -464,6 +461,9 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) NM_SETTING_IP6_CONFIG_ADDRESSES); return FALSE; } + } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + /* nothing to do */ } else { g_set_error (error, NM_SETTING_IP6_CONFIG_ERROR, @@ -598,30 +598,33 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class) * * IPv6 configuration method. If 'auto' is specified then the appropriate * automatic method (DHCP, PPP, advertisement, etc) is used for the - * interface and most other properties can be left unset. If 'link-local' - * is specified, then an IPv6 link-local address will be assigned to the - * interface. If 'manual' is specified, static IP addressing is used and - * at least one IP address must be given in the 'addresses' property. If - * 'ignored' is specified, IPv6 configuration is not done. This property - * must be set. NOTE: DHCP configuration and the 'shared' method are not - * yet supported. + * interface and most other properties can be left unset. To force the use + * of DHCP only, specify 'dhcp'; this method is only valid for ethernet- + * based hardware. If 'link-local' is specified, then an IPv6 link-local + * address will be assigned to the interface. If 'manual' is specified, + * static IP addressing is used and at least one IP address must be given + * in the 'addresses' property. If 'ignored' is specified, IPv6 + * configuration is not done. This property must be set. NOTE: the 'shared' + * method are not yet supported. **/ g_object_class_install_property (object_class, PROP_METHOD, g_param_spec_string (NM_SETTING_IP6_CONFIG_METHOD, "Method", "IPv6 configuration method. If 'auto' is specified " - "then the appropriate automatic method (DHCP, PPP, " + "then the appropriate automatic method (PPP, router " "advertisement, etc) is used for the device and " - "most other properties can be left unset. If " + "most other properties can be left unset. To force " + "the use of DHCP only, specify 'dhcp'; this method " + "is only valid for ethernet-based hardware. If " "'link-local' is specified, then an IPv6 link-local " "address will be assigned to the interface. If " "'manual' is specified, static IP addressing is " "used and at least one IP address must be given in " " the 'addresses' property. If 'ignored' is " "specified, IPv6 configuration is not done. This " - "property must be set. NOTE: DHCP configuration " - "and the 'shared' method are not yet supported.", + "property must be set. NOTE: the 'shared' method" + "is not yet supported.", NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); @@ -740,38 +743,39 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class) /** * NMSettingIP6Config:ignore-auto-routes: * - * When the method is set to 'auto' and this property to TRUE, automatically - * configured routes are ignored and only routes specified in - * #NMSettingIP6Config:routes, if any, are used. + * When the method is set to 'auto' or 'dhcp' and this property is set to + * TRUE, automatically configured routes are ignored and only routes + * specified in #NMSettingIP6Config:routes, if any, are used. **/ g_object_class_install_property (object_class, PROP_IGNORE_AUTO_ROUTES, g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, "Ignore automatic routes", - "When the method is set to 'auto' and this property " - "to TRUE, automatically configured routes are " - "ignored and only routes specified in the 'routes' " - "property, if any, are used.", + "When the method is set to 'auto' or 'dhcp' and this " + "property is set to TRUE, automatically configured " + "routes are ignored and only routes specified in the " + "'routes' property, if any, are used.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE)); /** * NMSettingIP6Config:ignore-auto-dns: * - * When the method is set to 'auto' and this property to TRUE, automatically - * configured nameservers and search domains are ignored and only namservers - * and search domains specified in #NMSettingIP6Config:dns and - * #NMSettingIP6Config:dns-search, if any, are used. + * When the method is set to 'auto' or 'dhcp' and this property is set to + * TRUE, automatically configured nameservers and search domains are ignored + * and only namservers and search domains specified in + * #NMSettingIP6Config:dns and #NMSettingIP6Config:dns-search, if any, are + * used. **/ g_object_class_install_property (object_class, PROP_IGNORE_AUTO_DNS, g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, "Ignore DHCPv6/RDNSS DNS", - "When the method is set to 'auto' and this property " - "to TRUE, automatically configured nameservers and " - "search domains are ignored and only namservers and " - "search domains specified in the 'dns' and 'dns-search' " - "properties, if any, are used.", + "When the method is set to 'auto' or 'dhcp' and this " + "property is set to TRUE, automatically configured " + "nameservers and search domains are ignored and only " + "namservers and search domains specified in the 'dns' " + "and 'dns-search' properties, if any, are used.", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE)); diff --git a/libnm-util/nm-setting-ip6-config.h b/libnm-util/nm-setting-ip6-config.h index 18082e4eaa..b089679e4e 100644 --- a/libnm-util/nm-setting-ip6-config.h +++ b/libnm-util/nm-setting-ip6-config.h @@ -19,7 +19,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * (C) Copyright 2007 - 2008 Red Hat, Inc. + * (C) Copyright 2007 - 2010 Red Hat, Inc. */ #ifndef NM_SETTING_IP6_CONFIG_H @@ -65,6 +65,7 @@ GQuark nm_setting_ip6_config_error_quark (void); #define NM_SETTING_IP6_CONFIG_METHOD_IGNORE "ignore" #define NM_SETTING_IP6_CONFIG_METHOD_AUTO "auto" +#define NM_SETTING_IP6_CONFIG_METHOD_DHCP "dhcp" #define NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL "link-local" #define NM_SETTING_IP6_CONFIG_METHOD_MANUAL "manual" #define NM_SETTING_IP6_CONFIG_METHOD_SHARED "shared" From 3ee1eb7a6c7226ad90dadec15f4e3d51af5ba05a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Jan 2010 15:22:11 -0800 Subject: [PATCH 03/20] dhcp6: add dhclient ipv6 states --- src/dhcp-manager/nm-dhcp-manager.c | 122 ++++++++++++----------------- src/dhcp-manager/nm-dhcp-manager.h | 38 +++++---- src/nm-device.c | 8 +- 3 files changed, 76 insertions(+), 92 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index c3ca358dca..8942b01d2b 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -146,10 +146,13 @@ nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class) static gboolean state_is_bound (guint8 state) { - if ((state == DHC_BOUND) - || (state == DHC_RENEW) + if ( (state == DHC_BOUND4) + || (state == DHC_BOUND6) + || (state == DHC_RENEW4) + || (state == DHC_RENEW6) || (state == DHC_REBOOT) - || (state == DHC_REBIND) + || (state == DHC_REBIND4) + || (state == DHC_REBIND6) || (state == DHC_IPV4LL)) return TRUE; @@ -200,83 +203,60 @@ nm_dhcp_device_destroy (NMDHCPDevice *device) g_slice_free (NMDHCPDevice, device); } +typedef struct { + NMDHCPState state; + const char *name; +} DhcState; + +#define STATE_TABLE_SIZE (sizeof (state_table) / sizeof (state_table[0])) + +static DhcState state_table[] = { + { DHC_NBI, "nbi" }, + { DHC_PREINIT, "preinit" }, + { DHC_BOUND4, "bound" }, + { DHC_BOUND6, "bound6" }, + { DHC_IPV4LL, "ipv4ll" }, + { DHC_RENEW4, "renew" }, + { DHC_RENEW6, "renew6" }, + { DHC_REBOOT, "reboot" }, + { DHC_REBIND4, "rebind" }, + { DHC_REBIND6, "rebind6" }, + { DHC_STOP, "stop" }, + { DHC_MEDIUM, "medium" }, + { DHC_TIMEOUT, "timeout" }, + { DHC_FAIL, "fail" }, + { DHC_EXPIRE, "expire" }, + { DHC_RELEASE, "release" }, + { DHC_START, "start" }, + { DHC_ABEND, "abend" }, + { DHC_END, "end" }, + { DHC_DEPREF6, "depref6" }, +}; static inline const char * state_to_string (guint32 state) { - switch (state) - { - case DHC_PREINIT: - return "preinit"; - case DHC_BOUND: - return "bound"; - case DHC_IPV4LL: - return "bound (ipv4ll)"; - case DHC_RENEW: - return "renew"; - case DHC_REBOOT: - return "reboot"; - case DHC_REBIND: - return "rebind"; - case DHC_STOP: - return "stop"; - case DHC_MEDIUM: - return "medium"; - case DHC_TIMEOUT: - return "timeout"; - case DHC_FAIL: - return "fail"; - case DHC_EXPIRE: - return "expire"; - case DHC_RELEASE: - return "release"; - case DHC_START: - return "successfully started"; - case DHC_ABEND: - return "abnormal exit"; - case DHC_END: - return "normal exit"; - default: - break; + int i; + + for (i = 0; i < STATE_TABLE_SIZE; i++) { + if (state == state_table[i].state) + return state_table[i].name; } + return NULL; } -static inline guint32 -string_to_state (const char *state) +static inline NMDHCPState +string_to_state (const char *name) { - if (strcmp("PREINIT", state) == 0) - return DHC_PREINIT; - else if (strcmp("BOUND", state) == 0) - return DHC_BOUND; - else if (strcmp("IPV4LL", state) == 0) - return DHC_IPV4LL; - else if (strcmp("RENEW", state) == 0) - return DHC_RENEW; - else if (strcmp("REBOOT", state) == 0) - return DHC_REBOOT; - else if (strcmp("REBIND", state) == 0) - return DHC_REBIND; - else if (strcmp("STOP", state) == 0) - return DHC_STOP; - else if (strcmp("MEDIUM", state) == 0) - return DHC_MEDIUM; - else if (strcmp("TIMEOUT", state) == 0) - return DHC_TIMEOUT; - else if (strcmp("FAIL", state) == 0) - return DHC_FAIL; - else if (strcmp("EXPIRE", state) == 0) - return DHC_EXPIRE; - else if (strcmp("RELEASE", state) == 0) - return DHC_RELEASE; - else if (strcmp("START", state) == 0) - return DHC_START; - else if (strcmp("ABEND", state) == 0) - return DHC_ABEND; - else if (strcmp("END", state) == 0) - return DHC_END; - else - return 255; + int i; + + for (i = 0; i < STATE_TABLE_SIZE; i++) { + if (!strcasecmp (name, state_table[i].name)) + return state_table[i].state; + } + + return 255; } static char * diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 124008493a..46d0ea4bc3 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -41,23 +41,27 @@ #define NM_DHCP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) typedef enum { - DHC_NBI=0, /* no broadcast interfaces found */ - DHC_PREINIT, /* configuration started */ - DHC_BOUND, /* lease obtained */ - DHC_IPV4LL, /* IPv4LL address obtained */ - DHC_RENEW, /* lease renewed */ - DHC_REBOOT, /* have valid lease, but now obtained a different one */ - DHC_REBIND, /* new, different lease */ - DHC_STOP, /* remove old lease */ - DHC_MEDIUM, /* media selection begun */ - DHC_TIMEOUT, /* timed out contacting DHCP server */ - DHC_FAIL, /* all attempts to contact server timed out, sleeping */ - DHC_EXPIRE, /* lease has expired, renewing */ - DHC_RELEASE, /* releasing lease */ - DHC_START, /* sent when dhclient started OK */ - DHC_ABEND, /* dhclient exited abnormally */ - DHC_END, /* dhclient exited normally */ - DHC_END_OPTIONS, /* last option in subscription sent */ + DHC_NBI = 0, /* no broadcast interfaces found */ + DHC_PREINIT, /* configuration started */ + DHC_BOUND4, /* IPv4 lease obtained */ + DHC_BOUND6, /* IPv6 lease obtained */ + DHC_IPV4LL, /* IPv4LL address obtained */ + DHC_RENEW4, /* IPv4 lease renewed */ + DHC_RENEW6, /* IPv6 lease renewed */ + DHC_REBOOT, /* have valid lease, but now obtained a different one */ + DHC_REBIND4, /* IPv4 new/different lease */ + DHC_REBIND6, /* IPv6 new/different lease */ + DHC_STOP, /* remove old lease */ + DHC_MEDIUM, /* media selection begun */ + DHC_TIMEOUT, /* timed out contacting DHCP server */ + DHC_FAIL, /* all attempts to contact server timed out, sleeping */ + DHC_EXPIRE, /* lease has expired, renewing */ + DHC_RELEASE, /* releasing lease */ + DHC_START, /* sent when dhclient started OK */ + DHC_ABEND, /* dhclient exited abnormally */ + DHC_END, /* dhclient exited normally */ + DHC_DEPREF6, /* IPv6 lease depreferred */ + DHC_END_OPTIONS, /* last option in subscription sent */ } NMDHCPState; typedef struct { diff --git a/src/nm-device.c b/src/nm-device.c index 6c0f99ca47..05c9598254 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -2314,10 +2314,10 @@ dhcp_state_changed (NMDHCPManager *dhcp_manager, dev_state = nm_device_get_state (device); switch (state) { - case DHC_BOUND: /* lease obtained */ - case DHC_RENEW: /* lease renewed */ - case DHC_REBOOT: /* have valid lease, but now obtained a different one */ - case DHC_REBIND: /* new, different lease */ + case DHC_BOUND4: /* lease obtained */ + case DHC_RENEW4: /* lease renewed */ + case DHC_REBOOT: /* have valid lease, but now obtained a different one */ + case DHC_REBIND4: /* new, different lease */ if (dev_state == NM_DEVICE_STATE_IP_CONFIG) nm_device_activate_schedule_stage4_ip4_config_get (device); else if (dev_state == NM_DEVICE_STATE_ACTIVATED) From d997785db37b9b4c64c7bf41932193665dad4262 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Jan 2010 17:16:05 -0800 Subject: [PATCH 04/20] dhcp6: genericize DHCP client tracking Since the same interface could be used for both DHCPv4 and DHCPv6 we can't just use 'iface' for tracking DHCP client lease changes. Instead use a generated client ID, and track DHCP events based on the client's PID instead of interface name. --- src/dhcp-manager/nm-dhcp-dhclient.c | 99 ++--- src/dhcp-manager/nm-dhcp-dhcpcd.c | 30 +- src/dhcp-manager/nm-dhcp-manager.c | 553 +++++++++++++++------------- src/dhcp-manager/nm-dhcp-manager.h | 67 ++-- src/nm-device-ethernet.c | 8 +- src/nm-device.c | 44 ++- src/tests/test-dhcp-options.c | 20 +- 7 files changed, 438 insertions(+), 383 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 478ac298f0..e0deabb4a8 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2010 Red Hat, Inc. */ #define _XOPEN_SOURCE @@ -119,7 +119,7 @@ add_lease_option (GHashTable *hash, char *line) } GSList * -nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid) +nm_dhcp4_client_get_lease_config (const char *iface, const char *uuid) { GSList *parsed = NULL, *iter, *leases = NULL; char *contents = NULL; @@ -288,7 +288,7 @@ out: #define DHCP_HOSTNAME_FORMAT DHCP_HOSTNAME_TAG " \"%s\"; # added by NetworkManager" static gboolean -merge_dhclient_config (NMDHCPDevice *device, +merge_dhclient_config (NMDHCPClient *client, NMSettingIP4Config *s_ip4, guint8 *anycast_addr, const char *contents, @@ -298,8 +298,8 @@ merge_dhclient_config (NMDHCPDevice *device, GString *new_contents; gboolean success = FALSE; - g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (device->iface != NULL, FALSE); + g_return_val_if_fail (client != NULL, FALSE); + g_return_val_if_fail (client->iface != NULL, FALSE); new_contents = g_string_new (_("# Created by NetworkManager\n")); @@ -374,13 +374,13 @@ merge_dhclient_config (NMDHCPDevice *device, " initial-interval 1; \n" " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n" "}\n", - device->iface, + client->iface, anycast_addr[0], anycast_addr[1], anycast_addr[2], anycast_addr[3], anycast_addr[4], anycast_addr[5]); } - if (g_file_set_contents (device->conf_file, new_contents->str, -1, error)) + if (g_file_set_contents (client->conf_file, new_contents->str, -1, error)) success = TRUE; g_string_free (new_contents, TRUE); @@ -394,7 +394,7 @@ merge_dhclient_config (NMDHCPDevice *device, * config file along with the NM options. */ static gboolean -create_dhclient_config (NMDHCPDevice *device, +create_dhclient_config (NMDHCPClient *client, NMSettingIP4Config *s_ip4, guint8 *dhcp_anycast_addr) { @@ -403,7 +403,8 @@ create_dhclient_config (NMDHCPDevice *device, gboolean success = FALSE; char *tmp; - g_return_val_if_fail (device != NULL, FALSE); + g_return_val_if_fail (client != NULL, FALSE); + g_return_val_if_fail (client->iface != NULL, FALSE); #if defined(TARGET_SUSE) orig = g_strdup (SYSCONFDIR "/dhclient.conf"); @@ -412,16 +413,16 @@ create_dhclient_config (NMDHCPDevice *device, #elif defined(TARGET_GENTOO) orig = g_strdup (SYSCONFDIR "/dhcp/dhclient.conf"); #else - orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", device->iface); + orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", client->iface); #endif if (!orig) { - nm_warning ("%s: not enough memory for dhclient options.", device->iface); + nm_warning ("%s: not enough memory for dhclient options.", client->iface); return FALSE; } - tmp = g_strdup_printf ("nm-dhclient-%s.conf", device->iface); - device->conf_file = g_build_filename ("/var", "run", tmp, NULL); + tmp = g_strdup_printf ("nm-dhclient-%s.conf", client->iface); + client->conf_file = g_build_filename ("/var", "run", tmp, NULL); g_free (tmp); if (!g_file_test (orig, G_FILE_TEST_EXISTS)) @@ -429,18 +430,18 @@ create_dhclient_config (NMDHCPDevice *device, if (!g_file_get_contents (orig, &contents, NULL, &error)) { nm_warning ("%s: error reading dhclient configuration %s: %s", - device->iface, orig, error->message); + client->iface, orig, error->message); g_error_free (error); goto out; } out: error = NULL; - if (merge_dhclient_config (device, s_ip4, dhcp_anycast_addr, contents, orig, &error)) + if (merge_dhclient_config (client, s_ip4, dhcp_anycast_addr, contents, orig, &error)) success = TRUE; else { nm_warning ("%s: error creating dhclient configuration: %s", - device->iface, error->message); + client->iface, error->message); g_error_free (error); } @@ -460,12 +461,12 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) GPid -nm_dhcp_client_start (NMDHCPDevice *device, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *dhcp_anycast_addr) +nm_dhcp4_client_start (NMDHCPClient *client, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) { - GPtrArray *dhclient_argv = NULL; + GPtrArray *argv = NULL; GPid pid = 0; GError *error = NULL; char *pid_contents = NULL; @@ -475,51 +476,51 @@ nm_dhcp_client_start (NMDHCPDevice *device, goto out; } - device->pid_file = get_pidfile_for_iface (device->iface); - if (!device->pid_file) { - nm_warning ("%s: not enough memory for dhclient options.", device->iface); + client->pid_file = get_pidfile_for_iface (client->iface); + if (!client->pid_file) { + nm_warning ("%s: not enough memory for dhclient options.", client->iface); goto out; } - device->lease_file = get_leasefile_for_iface (device->iface, uuid); - if (!device->lease_file) { - nm_warning ("%s: not enough memory for dhclient options.", device->iface); + client->lease_file = get_leasefile_for_iface (client->iface, uuid); + if (!client->lease_file) { + nm_warning ("%s: not enough memory for dhclient options.", client->iface); goto out; } - if (!create_dhclient_config (device, s_ip4, dhcp_anycast_addr)) + if (!create_dhclient_config (client, s_ip4, dhcp_anycast_addr)) goto out; /* Kill any existing dhclient bound to this interface */ - if (g_file_get_contents (device->pid_file, &pid_contents, NULL, NULL)) { + if (g_file_get_contents (client->pid_file, &pid_contents, NULL, NULL)) { unsigned long int tmp = strtoul (pid_contents, NULL, 10); if (!((tmp == ULONG_MAX) && (errno == ERANGE))) - nm_dhcp_client_stop (device, (pid_t) tmp); - remove (device->pid_file); + nm_dhcp_client_stop (client, (pid_t) tmp); + remove (client->pid_file); } - dhclient_argv = g_ptr_array_new (); - g_ptr_array_add (dhclient_argv, (gpointer) DHCP_CLIENT_PATH); + argv = g_ptr_array_new (); + g_ptr_array_add (argv, (gpointer) DHCP_CLIENT_PATH); - g_ptr_array_add (dhclient_argv, (gpointer) "-d"); + g_ptr_array_add (argv, (gpointer) "-d"); - g_ptr_array_add (dhclient_argv, (gpointer) "-sf"); /* Set script file */ - g_ptr_array_add (dhclient_argv, (gpointer) ACTION_SCRIPT_PATH ); + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); - g_ptr_array_add (dhclient_argv, (gpointer) "-pf"); /* Set pid file */ - g_ptr_array_add (dhclient_argv, (gpointer) device->pid_file); + g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (argv, (gpointer) client->pid_file); - g_ptr_array_add (dhclient_argv, (gpointer) "-lf"); /* Set lease file */ - g_ptr_array_add (dhclient_argv, (gpointer) device->lease_file); + g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ + g_ptr_array_add (argv, (gpointer) client->lease_file); - g_ptr_array_add (dhclient_argv, (gpointer) "-cf"); /* Set interface config file */ - g_ptr_array_add (dhclient_argv, (gpointer) device->conf_file); + g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ + g_ptr_array_add (argv, (gpointer) client->conf_file); - g_ptr_array_add (dhclient_argv, (gpointer) device->iface); - g_ptr_array_add (dhclient_argv, NULL); + g_ptr_array_add (argv, (gpointer) client->iface); + g_ptr_array_add (argv, NULL); - if (!g_spawn_async (NULL, (char **) dhclient_argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, &dhclient_child_setup, NULL, &pid, &error)) { nm_warning ("dhclient failed to start. error: '%s'", error->message); g_error_free (error); @@ -530,7 +531,7 @@ nm_dhcp_client_start (NMDHCPDevice *device, out: g_free (pid_contents); - g_ptr_array_free (dhclient_argv, TRUE); + g_ptr_array_free (argv, TRUE); return pid; } @@ -596,9 +597,9 @@ error: } gboolean -nm_dhcp_client_process_classless_routes (GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr) +nm_dhcp4_client_process_classless_routes (GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr) { const char *str; char **octets, **o; diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index a8d929a528..6ba3f0a91c 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -50,7 +50,7 @@ get_pidfile_for_iface (const char * iface) } GSList * -nm_dhcp_client_get_lease_ip4_config (const char *iface, const char *uuid) +nm_dhcp4_client_get_lease_config (const char *iface, const char *uuid) { return NULL; } @@ -65,10 +65,10 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) GPid -nm_dhcp_client_start (NMDHCPDevice *device, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *dhcp_anycast_addr) +nm_dhcp4_client_start (NMDHCPClient *client, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) { GPtrArray *argv = NULL; GPid pid = 0; @@ -80,19 +80,19 @@ nm_dhcp_client_start (NMDHCPDevice *device, goto out; } - device->pid_file = get_pidfile_for_iface (device->iface); - if (!device->pid_file) { - nm_warning ("%s: not enough memory for dhcpcd options.", device->iface); + client->pid_file = get_pidfile_for_iface (client->iface); + if (!client->pid_file) { + nm_warning ("%s: not enough memory for dhcpcd options.", client->iface); goto out; } /* Kill any existing dhcpcd bound to this interface */ - if (g_file_get_contents (device->pid_file, &pid_contents, NULL, NULL)) { + if (g_file_get_contents (client->pid_file, &pid_contents, NULL, NULL)) { unsigned long int tmp = strtoul (pid_contents, NULL, 10); if (!((tmp == ULONG_MAX) && (errno == ERANGE))) - nm_dhcp_client_stop (device, (pid_t) tmp); - remove (device->pid_file); + nm_dhcp_client_stop (client, (pid_t) tmp); + remove (client->pid_file); } argv = g_ptr_array_new (); @@ -107,7 +107,7 @@ nm_dhcp_client_start (NMDHCPDevice *device, g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */ g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); - g_ptr_array_add (argv, (gpointer) device->iface); + g_ptr_array_add (argv, (gpointer) client->iface); g_ptr_array_add (argv, NULL); if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, @@ -126,9 +126,9 @@ out: } gboolean -nm_dhcp_client_process_classless_routes (GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr) +nm_dhcp4_client_process_classless_routes (GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr) { const char *str; char **routes, **r; diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 8942b01d2b..2b48a2651d 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2010 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. * */ @@ -50,9 +50,10 @@ #define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */ typedef struct { + guint32 next_id; NMDBusManager * dbus_mgr; - GHashTable * devices; - DBusGProxy * proxy; + GHashTable * clients; + DBusGProxy * proxy; NMHostnameProvider *hostname_provider; } NMDHCPManagerPrivate; @@ -69,82 +70,9 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static NMDHCPManager *nm_dhcp_manager_new (void); +static void nm_dhcp_manager_cancel_transaction_real (NMDHCPClient *client); -static void nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device); - -static void hostname_provider_destroyed (gpointer data, GObject *destroyed_object); - -NMDHCPManager * -nm_dhcp_manager_get (void) -{ - static NMDHCPManager *singleton = NULL; - - if (!singleton) - singleton = nm_dhcp_manager_new (); - else - g_object_ref (singleton); - - g_assert (singleton); - return singleton; -} - -static void -nm_dhcp_manager_init (NMDHCPManager *manager) -{ -} - -static void -finalize (GObject *object) -{ - NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object); - - if (priv->hostname_provider) { - g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object); - priv->hostname_provider = NULL; - } - - g_hash_table_destroy (priv->devices); - g_object_unref (priv->proxy); - g_object_unref (priv->dbus_mgr); - - G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object); -} - -static void -nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (manager_class); - - g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate)); - - /* virtual methods */ - object_class->finalize = finalize; - - /* signals */ - signals[STATE_CHANGED] = - g_signal_new ("state-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPManagerClass, state_changed), - NULL, NULL, - _nm_marshal_VOID__STRING_UCHAR, - G_TYPE_NONE, 2, - G_TYPE_STRING, - G_TYPE_UCHAR); - - signals[TIMEOUT] = - g_signal_new ("timeout", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPManagerClass, timeout), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, - G_TYPE_STRING); -} - -static gboolean state_is_bound (guint8 state) +static gboolean state_is_bound (guint32 state) { if ( (state == DHC_BOUND4) || (state == DHC_BOUND6) @@ -161,46 +89,46 @@ static gboolean state_is_bound (guint8 state) static void -nm_dhcp_device_timeout_cleanup (NMDHCPDevice * device) +nm_dhcp_client_timeout_cleanup (NMDHCPClient *client) { - if (device->timeout_id) { - g_source_remove (device->timeout_id); - device->timeout_id = 0; + if (client->timeout_id) { + g_source_remove (client->timeout_id); + client->timeout_id = 0; } } static void -nm_dhcp_device_watch_cleanup (NMDHCPDevice * device) +nm_dhcp_client_watch_cleanup (NMDHCPClient *client) { - if (device->watch_id) { - g_source_remove (device->watch_id); - device->watch_id = 0; + if (client->watch_id) { + g_source_remove (client->watch_id); + client->watch_id = 0; } } static void -nm_dhcp_device_destroy (NMDHCPDevice *device) +nm_dhcp_client_destroy (NMDHCPClient *client) { int ignored; - nm_dhcp_device_timeout_cleanup (device); + nm_dhcp_client_timeout_cleanup (client); - if (device->pid) - nm_dhcp_client_stop (device, device->pid); + if (client->pid) + nm_dhcp_client_stop (client, client->pid); - if (device->options) - g_hash_table_destroy (device->options); + if (client->options) + g_hash_table_destroy (client->options); - if (device->conf_file) { - ignored = unlink (device->conf_file); - g_free (device->conf_file); + if (client->conf_file) { + ignored = unlink (client->conf_file); + g_free (client->conf_file); } - g_free (device->pid_file); - g_free (device->lease_file); - g_free (device->iface); + g_free (client->pid_file); + g_free (client->lease_file); + g_free (client->iface); - g_slice_free (NMDHCPDevice, device); + g_slice_free (NMDHCPClient, client); } typedef struct { @@ -316,7 +244,7 @@ copy_option (gpointer key, gpointer value, gpointer user_data) { - NMDHCPDevice * device = (NMDHCPDevice *) user_data; + NMDHCPClient *client = user_data; const char *str_key = (const char *) key; char *str_value = NULL; @@ -329,21 +257,21 @@ copy_option (gpointer key, str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key); if (str_value) - g_hash_table_insert (device->options, g_strdup (str_key), str_value); + g_hash_table_insert (client->options, g_strdup (str_key), str_value); } static void -handle_options (NMDHCPManager * manager, - NMDHCPDevice * device, - GHashTable * options, - const char * reason) +handle_options (NMDHCPManager *manager, + NMDHCPClient *client, + GHashTable *options, + const char *reason) { - guint32 old_state = device->state; + guint32 old_state = client->state; guint32 new_state = string_to_state (reason); /* Clear old and save new DHCP options */ - g_hash_table_remove_all (device->options); - g_hash_table_foreach (options, copy_option, device); + g_hash_table_remove_all (client->options); + g_hash_table_foreach (options, copy_option, client); if (old_state == new_state) return; @@ -351,20 +279,78 @@ handle_options (NMDHCPManager * manager, /* Handle changed device state */ if (state_is_bound (new_state)) { /* Cancel the timeout if the DHCP client is now bound */ - nm_dhcp_device_timeout_cleanup (device); + nm_dhcp_client_timeout_cleanup (client); } - device->state = new_state; + client->state = new_state; nm_info ("DHCP: device %s state changed %s -> %s", - device->iface, + client->iface, state_to_string (old_state), - state_to_string (device->state)); + state_to_string (client->state)); - g_signal_emit (G_OBJECT (device->manager), + g_signal_emit (G_OBJECT (client->manager), signals[STATE_CHANGED], 0, - device->iface, - device->state); + client->id, + client->state); +} + +static NMDHCPClient * +get_client_for_pid (NMDHCPManager *manager, GPid pid) +{ + NMDHCPManagerPrivate *priv; + GHashTableIter iter; + gpointer value; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + g_hash_table_iter_init (&iter, priv->clients); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + NMDHCPClient *candidate = value; + + if (candidate->pid == pid) + return candidate; + } + + return NULL; +} + +static NMDHCPClient * +get_client_for_iface (NMDHCPManager *manager, const char *iface) +{ + NMDHCPManagerPrivate *priv; + GHashTableIter iter; + gpointer value; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); + g_return_val_if_fail (iface, NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + g_hash_table_iter_init (&iter, priv->clients); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + NMDHCPClient *candidate = value; + + if (!strcmp (iface, candidate->iface)) + return candidate; + } + + return NULL; +} + +static NMDHCPClient * +get_client_for_id (NMDHCPManager *manager, guint32 id) +{ + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); + g_return_val_if_fail (id > 0, NULL); + + return g_hash_table_lookup (NM_DHCP_MANAGER_GET_PRIVATE (manager)->clients, + GUINT_TO_POINTER (id)); } static void @@ -372,14 +358,13 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy, GHashTable *options, gpointer user_data) { - NMDHCPManager * manager; - NMDHCPManagerPrivate * priv; - NMDHCPDevice * device; - char * iface = NULL; - char * pid_str = NULL; - char * reason = NULL; + NMDHCPManager *manager; + NMDHCPManagerPrivate *priv; + NMDHCPClient *client; + char *iface = NULL; + char *pid_str = NULL; + char *reason = NULL; unsigned long temp; - pid_t pid; manager = NM_DHCP_MANAGER (user_data); priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); @@ -390,29 +375,27 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy, goto out; } - device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - if (device == NULL) { - nm_warning ("Unhandled DHCP event for interface %s", iface); - goto out; - } - pid_str = get_option (options, "pid"); if (pid_str == NULL) { nm_warning ("DHCP event didn't have associated PID."); goto out; } - temp = strtoul(pid_str, NULL, 10); + temp = strtoul (pid_str, NULL, 10); if ((temp == ULONG_MAX) && (errno == ERANGE)) { nm_warning ("Couldn't convert PID"); goto out; } - pid = (pid_t) temp; - if (pid != device->pid) { - nm_warning ("Received DHCP event from unexpected PID %u (expected %u)", - pid, - device->pid); + client = get_client_for_pid (manager, (GPid) temp); + if (client == NULL) { + nm_warning ("Unhandled DHCP event for interface %s", iface); + goto out; + } + + if (strcmp (iface, client->iface)) { + nm_warning ("Received DHCP event from unexpected interface '%s' (expected '%s')", + iface, client->iface); goto out; } @@ -422,7 +405,7 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy, goto out; } - handle_options (manager, device, options, reason); + handle_options (manager, client, options, reason); out: g_free (iface); @@ -435,20 +418,18 @@ nm_dhcp_manager_new (void) { NMDHCPManager *manager; NMDHCPManagerPrivate *priv; - DBusGConnection * g_connection; + DBusGConnection *g_connection; manager = g_object_new (NM_TYPE_DHCP_MANAGER, NULL); priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, - (GDestroyNotify) nm_dhcp_device_destroy); - if (!priv->devices) { - nm_warning ("Error: not enough memory to initialize DHCP manager " - "tables"); + priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) nm_dhcp_client_destroy); + if (!priv->clients) { + nm_warning ("Error: not enough memory to initialize DHCP manager tables"); g_object_unref (manager); - manager = NULL; - goto out; + return NULL; } priv->dbus_mgr = nm_dbus_manager_get (); @@ -460,6 +441,7 @@ nm_dhcp_manager_new (void) if (!priv->proxy) { nm_warning ("Error: could not init DHCP manager proxy"); g_object_unref (manager); + return NULL; } dbus_g_proxy_add_signal (priv->proxy, @@ -472,7 +454,6 @@ nm_dhcp_manager_new (void) manager, NULL); -out: return manager; } @@ -486,56 +467,54 @@ out: static gboolean nm_dhcp_manager_handle_timeout (gpointer user_data) { - NMDHCPDevice *device = (NMDHCPDevice *) user_data; + NMDHCPClient *client = user_data; - nm_info ("(%s): DHCP transaction took too long, stopping it.", device->iface); + nm_info ("(%s): DHCP transaction took too long, stopping it.", client->iface); - nm_dhcp_manager_cancel_transaction (device->manager, device->iface); + nm_dhcp_manager_cancel_transaction (client->manager, client->id); - g_signal_emit (G_OBJECT (device->manager), signals[TIMEOUT], 0, device->iface); + g_signal_emit (G_OBJECT (client->manager), signals[TIMEOUT], 0, client->id); return FALSE; } -static NMDHCPDevice * -nm_dhcp_device_new (NMDHCPManager *manager, const char *iface) +static NMDHCPClient * +nm_dhcp_client_new (NMDHCPManager *manager, const char *iface) { - NMDHCPDevice *device; - GHashTable * hash = NM_DHCP_MANAGER_GET_PRIVATE (manager)->devices; + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + NMDHCPClient *client; - device = g_slice_new0 (NMDHCPDevice); - if (!device) { + client = g_slice_new0 (NMDHCPClient); + if (!client) { nm_warning ("%s: Out of memory creating DHCP transaction object.", iface); return NULL; } - device->iface = g_strdup (iface); - if (!device) { + client->id = priv->next_id++; + client->iface = g_strdup (iface); + if (!client) { nm_warning ("%s: Out of memory creating DHCP transaction object " "property 'iface'.", iface); goto error; } - device->manager = manager; + client->manager = manager; /* Do this after the transaction cancel since that clears options out */ - device->options = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_free); - if (!device->options) { + client->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + if (!client->options) { nm_warning ("%s: Out of memory creating DHCP transaction object " "property 'options'.", iface); goto error; } - g_hash_table_insert (hash, device->iface, device); - return device; + g_hash_table_insert (priv->clients, GUINT_TO_POINTER (client->id), client); + return client; error: - nm_dhcp_device_destroy (device); + nm_dhcp_client_destroy (client); return NULL; } @@ -548,21 +527,21 @@ error: */ static void dhcp_watch_cb (GPid pid, gint status, gpointer user_data) { - NMDHCPDevice *device = (NMDHCPDevice *)user_data; + NMDHCPClient *client = user_data; if (!WIFEXITED (status)) { - device->state = DHC_ABEND; + client->state = DHC_ABEND; nm_warning ("dhcp client died abnormally"); } - device->pid = 0; + client->pid = 0; - nm_dhcp_device_watch_cleanup (device); - nm_dhcp_device_timeout_cleanup (device); + nm_dhcp_client_watch_cleanup (client); + nm_dhcp_client_timeout_cleanup (client); - g_signal_emit (G_OBJECT (device->manager), signals[STATE_CHANGED], 0, device->iface, device->state); + g_signal_emit (G_OBJECT (client->manager), signals[STATE_CHANGED], 0, client->id, client->state); } -gboolean +guint32 nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, const char *iface, const char *uuid, @@ -571,21 +550,22 @@ nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, guint8 *dhcp_anycast_addr) { NMDHCPManagerPrivate *priv; - NMDHCPDevice *device; + NMDHCPClient *client; NMSettingIP4Config *setting; - g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), FALSE); - g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (manager, 0); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), 0); + g_return_val_if_fail (iface != NULL, 0); priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - if (!device) - device = nm_dhcp_device_new (manager, iface); + client = get_client_for_iface (manager, iface); + if (!client) + client = nm_dhcp_client_new (manager, iface); - if (device->pid && (device->state < DHC_ABEND)) { + if (client->pid && (client->state < DHC_ABEND)) { /* Cancel any DHCP transaction already in progress */ - nm_dhcp_manager_cancel_transaction_real (device); + nm_dhcp_manager_cancel_transaction_real (client); } if (s_ip4 && @@ -601,44 +581,42 @@ nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, nm_hostname_provider_get_hostname (priv->hostname_provider), NULL); - } else { + } else setting = s_ip4 ? g_object_ref (s_ip4) : NULL; - } if (timeout == 0) timeout = NM_DHCP_TIMEOUT; nm_info ("Activation (%s) Beginning DHCP transaction (timeout in %d seconds)", iface, timeout); - device->pid = nm_dhcp_client_start (device, uuid, setting, dhcp_anycast_addr); + client->pid = nm_dhcp4_client_start (client, uuid, setting, dhcp_anycast_addr); if (setting) g_object_unref (setting); - if (device->pid == 0) + if (client->pid == 0) return FALSE; /* Set up a timeout on the transaction to kill it after the timeout */ - device->timeout_id = g_timeout_add_seconds (timeout, + client->timeout_id = g_timeout_add_seconds (timeout, nm_dhcp_manager_handle_timeout, - device); - device->watch_id = g_child_watch_add (device->pid, - (GChildWatchFunc) dhcp_watch_cb, - device); - return TRUE; + client); + client->watch_id = g_child_watch_add (client->pid, + (GChildWatchFunc) dhcp_watch_cb, + client); + return client->id; } void -nm_dhcp_client_stop (NMDHCPDevice *device, pid_t pid) +nm_dhcp_client_stop (NMDHCPClient *client, GPid pid) { int i = 15; /* 3 seconds */ + g_return_if_fail (client != NULL); g_return_if_fail (pid > 0); - /* Clean up the watch handler since we're explicitly killing - * the daemon - */ - nm_dhcp_device_watch_cleanup (device); + /* Clean up the watch handler since we're explicitly killing the daemon */ + nm_dhcp_client_watch_cleanup (client); /* Tell it to quit; maybe it wants to send out a RELEASE message */ kill (pid, SIGTERM); @@ -663,7 +641,7 @@ nm_dhcp_client_stop (NMDHCPDevice *device, pid_t pid) } if (i <= 0) { - nm_warning ("%s: dhcp client pid %d didn't exit, will kill it.", device->iface, pid); + nm_warning ("%s: dhcp client pid %d didn't exit, will kill it.", client->iface, pid); kill (pid, SIGKILL); nm_debug ("waiting for dhcp client pid %d to exit", pid); @@ -673,41 +651,42 @@ nm_dhcp_client_stop (NMDHCPDevice *device, pid_t pid) } static void -nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device) +nm_dhcp_manager_cancel_transaction_real (NMDHCPClient *client) { - g_return_if_fail (device->pid > 0); + g_return_if_fail (client != NULL); + g_return_if_fail (client->pid > 0); - nm_dhcp_client_stop (device, device->pid); + nm_dhcp_client_stop (client, client->pid); nm_info ("(%s): canceled DHCP transaction, dhcp client pid %d", - device->iface, - device->pid); + client->iface, + client->pid); - device->pid = 0; - device->state = DHC_END; + client->pid = 0; + client->state = DHC_END; /* Clean up the pidfile if it got left around */ - if (device->pid_file) { - remove (device->pid_file); - g_free (device->pid_file); - device->pid_file = NULL; + if (client->pid_file) { + remove (client->pid_file); + g_free (client->pid_file); + client->pid_file = NULL; } /* Free leasefile (but don't delete) */ - if (device->lease_file) { - g_free (device->lease_file); - device->lease_file = NULL; + if (client->lease_file) { + g_free (client->lease_file); + client->lease_file = NULL; } /* Clean up config file if it got left around */ - if (device->conf_file) { - remove (device->conf_file); - g_free (device->conf_file); - device->conf_file = NULL; + if (client->conf_file) { + remove (client->conf_file); + g_free (client->conf_file); + client->conf_file = NULL; } - nm_dhcp_device_timeout_cleanup (device); - g_hash_table_remove_all (device->options); + nm_dhcp_client_timeout_cleanup (client); + g_hash_table_remove_all (client->options); } @@ -718,23 +697,20 @@ nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device) * */ void -nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, - const char *iface) +nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, guint32 id) { - NMDHCPDevice *device; + NMDHCPClient *client; NMDHCPManagerPrivate *priv; + g_return_if_fail (manager != NULL); g_return_if_fail (NM_IS_DHCP_MANAGER (manager)); - g_return_if_fail (iface != NULL); + g_return_if_fail (id > 0); priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - - if (!device || !device->pid) - return; - - nm_dhcp_manager_cancel_transaction_real (device); + client = get_client_for_id (manager, id); + if (client && client->pid) + nm_dhcp_manager_cancel_transaction_real (client); } static void @@ -823,7 +799,7 @@ out: /* Given a table of DHCP options from the client, convert into an IP4Config */ NMIP4Config * -nm_dhcp_manager_options_to_ip4_config (const char *iface, GHashTable *options) +nm_dhcp4_manager_options_to_config (const char *iface, GHashTable *options) { NMIP4Config *ip4_config = NULL; struct in_addr tmp_addr; @@ -863,7 +839,7 @@ nm_dhcp_manager_options_to_ip4_config (const char *iface, GHashTable *options) /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ - have_classless = nm_dhcp_client_process_classless_routes (options, ip4_config, &gwaddr); + have_classless = nm_dhcp4_client_process_classless_routes (options, ip4_config, &gwaddr); if (!have_classless) { gwaddr = 0; /* Ensure client code doesn't lie */ process_classful_routes (options, ip4_config); @@ -979,29 +955,29 @@ error: * */ NMIP4Config * -nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, - const char *iface) +nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, guint32 id) { NMDHCPManagerPrivate *priv; - NMDHCPDevice *device; + NMDHCPClient *client; + g_return_val_if_fail (manager != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); - g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (id > 0, NULL); priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - if (!device) { - nm_warning ("Device '%s' transaction not started.", iface); + client = get_client_for_id (manager, id); + if (!client) { + nm_warning ("Device DHCP transaction %d not started.", id); return NULL; } - if (!state_is_bound (device->state)) { - nm_warning ("%s: dhcp client didn't bind to a lease.", device->iface); + if (!state_is_bound (client->state)) { + nm_warning ("%s: dhcp client didn't bind to a lease.", client->iface); return NULL; } - return nm_dhcp_manager_options_to_ip4_config (iface, device->options); + return nm_dhcp4_manager_options_to_config (client->iface, client->options); } #define NEW_TAG "new_" @@ -1044,34 +1020,35 @@ iterate_dhcp4_config_option (gpointer key, gboolean nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self, - const char *iface, + guint32 id, GHFunc func, gpointer user_data) { NMDHCPManagerPrivate *priv; - NMDHCPDevice *device; + NMDHCPClient *client; Dhcp4ForeachInfo info = { NULL, NULL }; + g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), FALSE); - g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (id > 0, FALSE); g_return_val_if_fail (func != NULL, FALSE); priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - if (!device) { - nm_warning ("Device '%s' transaction not started.", iface); + client = get_client_for_id (self, id); + if (!client) { + nm_warning ("Device DHCP transaction %d not started.", id); return FALSE; } - if (!state_is_bound (device->state)) { - nm_warning ("%s: dhclient didn't bind to a lease.", device->iface); + if (!state_is_bound (client->state)) { + nm_warning ("%s: dhclient didn't bind to a lease.", client->iface); return FALSE; } info.func = func; info.user_data = user_data; - g_hash_table_foreach (device->options, iterate_dhcp4_config_option, &info); + g_hash_table_foreach (client->options, iterate_dhcp4_config_option, &info); return TRUE; } @@ -1103,14 +1080,86 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager, } GSList * -nm_dhcp_manager_get_lease_ip4_config (NMDHCPManager *self, - const char *iface, - const char *uuid) +nm_dhcp4_manager_get_lease_config (NMDHCPManager *self, + const char *iface, + const char *uuid) { + g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (uuid != NULL, NULL); - return nm_dhcp_client_get_lease_ip4_config (iface, uuid); + return nm_dhcp4_client_get_lease_config (iface, uuid); +} + +/***************************************************/ + +NMDHCPManager * +nm_dhcp_manager_get (void) +{ + static NMDHCPManager *singleton = NULL; + + if (!singleton) + singleton = nm_dhcp_manager_new (); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +static void +nm_dhcp_manager_init (NMDHCPManager *manager) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + priv->next_id = 1; +} + +static void +finalize (GObject *object) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object); + + if (priv->hostname_provider) { + g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object); + priv->hostname_provider = NULL; + } + + g_hash_table_destroy (priv->clients); + g_object_unref (priv->proxy); + g_object_unref (priv->dbus_mgr); + + G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object); +} + +static void +nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPManagerClass, state_changed), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + + signals[TIMEOUT] = + g_signal_new ("timeout", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPManagerClass, timeout), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); } diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 46d0ea4bc3..3b8a6a410e 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2010 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -51,6 +51,7 @@ typedef enum { DHC_REBOOT, /* have valid lease, but now obtained a different one */ DHC_REBIND4, /* IPv4 new/different lease */ DHC_REBIND6, /* IPv6 new/different lease */ + DHC_DEPREF6, /* IPv6 lease depreferred */ DHC_STOP, /* remove old lease */ DHC_MEDIUM, /* media selection begun */ DHC_TIMEOUT, /* timed out contacting DHCP server */ @@ -60,7 +61,6 @@ typedef enum { DHC_START, /* sent when dhclient started OK */ DHC_ABEND, /* dhclient exited abnormally */ DHC_END, /* dhclient exited normally */ - DHC_DEPREF6, /* IPv6 lease depreferred */ DHC_END_OPTIONS, /* last option in subscription sent */ } NMDHCPState; @@ -72,22 +72,23 @@ typedef struct { GObjectClass parent; /* Signals */ - void (*state_changed) (NMDHCPManager *manager, char *iface, NMDHCPState state); - void (*timeout) (NMDHCPManager *manager, char *iface); + void (*state_changed) (NMDHCPManager *manager, guint32 id, NMDHCPState state); + void (*timeout) (NMDHCPManager *manager, guint32 id); } NMDHCPManagerClass; typedef struct { - char * iface; - guchar state; - GPid pid; - char * pid_file; - char * conf_file; - char * lease_file; - guint timeout_id; - guint watch_id; - NMDHCPManager * manager; - GHashTable * options; -} NMDHCPDevice; + GPid id; + char * iface; + guchar state; + GPid pid; + char * pid_file; + char * conf_file; + char * lease_file; + guint timeout_id; + guint watch_id; + NMDHCPManager * manager; + GHashTable * options; +} NMDHCPClient; GType nm_dhcp_manager_get_type (void); @@ -95,42 +96,42 @@ NMDHCPManager *nm_dhcp_manager_get (void); void nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager, NMHostnameProvider *provider); -gboolean nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, +guint32 nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, const char *iface, const char *uuid, NMSettingIP4Config *s_ip4, guint32 timeout, guint8 *dhcp_anycast_addr); void nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, - const char *iface); -NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, const char *iface); -NMDHCPState nm_dhcp_manager_get_state_for_device (NMDHCPManager *manager, const char *iface); + guint32 id); +NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, guint32 id); +NMDHCPState nm_dhcp_manager_get_client_state (NMDHCPManager *manager, guint32 id); gboolean nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self, - const char *iface, + guint32 id, GHFunc func, gpointer user_data); -GSList * nm_dhcp_manager_get_lease_ip4_config (NMDHCPManager *self, +GSList * nm_dhcp4_manager_get_lease_config (NMDHCPManager *self, const char *iface, const char *uuid); /* The following are implemented by the DHCP client backends */ -GPid nm_dhcp_client_start (NMDHCPDevice *device, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *anycast_addr); -void nm_dhcp_client_stop (NMDHCPDevice *device, pid_t pid); +GPid nm_dhcp4_client_start (NMDHCPClient *client, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr); +void nm_dhcp_client_stop (NMDHCPClient *client, pid_t pid); -gboolean nm_dhcp_client_process_classless_routes (GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr); +gboolean nm_dhcp4_client_process_classless_routes (GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr); -GSList * nm_dhcp_client_get_lease_ip4_config (const char *iface, - const char *uuid); +GSList * nm_dhcp4_client_get_lease_config (const char *iface, + const char *uuid); /* Test functions */ -NMIP4Config *nm_dhcp_manager_options_to_ip4_config (const char *iface, - GHashTable *options); +NMIP4Config *nm_dhcp4_manager_options_to_config (const char *iface, + GHashTable *options); #endif /* NM_DHCP_MANAGER_H */ diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 037d603380..72073e4150 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2010 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -1608,9 +1608,9 @@ ip4_match_config (NMDevice *self, NMConnection *connection) /* Get any saved leases that apply to this connection */ dhcp_mgr = nm_dhcp_manager_get (); - leases = nm_dhcp_manager_get_lease_ip4_config (dhcp_mgr, - nm_device_get_iface (self), - nm_setting_connection_get_uuid (s_con)); + leases = nm_dhcp4_manager_get_lease_config (dhcp_mgr, + nm_device_get_iface (self), + nm_setting_connection_get_uuid (s_con)); g_object_unref (dhcp_mgr); method = nm_setting_ip4_config_get_method (s_ip4); diff --git a/src/nm-device.c b/src/nm-device.c index 05c9598254..4be3fc23e3 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2008 Red Hat, Inc. + * Copyright (C) 2005 - 2010 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -105,6 +105,7 @@ typedef struct { /* IP4 configuration info */ NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ NMDHCPManager * dhcp_manager; + guint32 dhcp4_id; guint32 dhcp_timeout; gulong dhcp_state_sigid; gulong dhcp_timeout_sigid; @@ -1090,7 +1091,6 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) if (!s_ip4 || !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean success; guint8 *anycast = NULL; if (priv->dhcp_anycast_address) @@ -1102,10 +1102,15 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) /* DHCP manager will cancel any transaction already in progress and we do not want to cancel this activation if we get "down" state from that. */ g_signal_handler_block (priv->dhcp_manager, priv->dhcp_state_sigid); - success = nm_dhcp_manager_begin_transaction (priv->dhcp_manager, ip_iface, uuid, s_ip4, priv->dhcp_timeout, anycast); + priv->dhcp4_id = nm_dhcp_manager_begin_transaction (priv->dhcp_manager, + ip_iface, + uuid, + s_ip4, + priv->dhcp_timeout, + anycast); g_signal_handler_unblock (priv->dhcp_manager, priv->dhcp_state_sigid); - if (success) { + if (priv->dhcp4_id) { /* DHCP devices will be notified by the DHCP manager when * stuff happens. */ @@ -1315,14 +1320,14 @@ real_act_stage4_get_ip4_config (NMDevice *self, s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); if (nm_device_get_use_dhcp (self)) { - *config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, ip_iface); + *config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, priv->dhcp4_id); if (*config) { /* Merge user-defined overrides into the IP4Config to be applied */ nm_utils_merge_ip4_config (*config, s_ip4); nm_dhcp4_config_reset (priv->dhcp4_config); nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager, - ip_iface, + priv->dhcp4_id, dhcp4_add_option_cb, priv->dhcp4_config); @@ -2013,7 +2018,7 @@ nm_device_deactivate_quickly (NMDevice *self) /* Stop any ongoing DHCP transaction on this device */ if (nm_device_get_act_request (self)) { if (nm_device_get_use_dhcp (self)) { - nm_dhcp_manager_cancel_transaction (priv->dhcp_manager, nm_device_get_ip_iface (self)); + nm_dhcp_manager_cancel_transaction (priv->dhcp_manager, priv->dhcp4_id); nm_device_set_use_dhcp (self, FALSE); /* Notify of invalid DHCP4 config */ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); @@ -2027,6 +2032,8 @@ nm_device_deactivate_quickly (NMDevice *self) g_object_unref (priv->dnsmasq_manager); priv->dnsmasq_manager = NULL; } + + priv->dhcp4_id = 0; } aipd_cleanup (self); @@ -2255,7 +2262,6 @@ handle_dhcp_lease_change (NMDevice *device) NMConnection *connection; NMActRequest *req; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - const char *ip_iface; gboolean assumed; if (!nm_device_get_use_dhcp (device)) { @@ -2263,9 +2269,7 @@ handle_dhcp_lease_change (NMDevice *device) return; } - ip_iface = nm_device_get_ip_iface (device); - - config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, ip_iface); + config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, priv->dhcp4_id); if (!config) { nm_warning ("failed to get DHCP config for rebind"); nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); @@ -2286,7 +2290,7 @@ handle_dhcp_lease_change (NMDevice *device) if (nm_device_set_ip4_config (device, config, assumed, &reason)) { nm_dhcp4_config_reset (priv->dhcp4_config); nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager, - ip_iface, + priv->dhcp4_id, dhcp4_add_option_cb, priv->dhcp4_config); } else { @@ -2297,7 +2301,7 @@ handle_dhcp_lease_change (NMDevice *device) static void dhcp_state_changed (NMDHCPManager *dhcp_manager, - const char *iface, + guint32 id, NMDHCPState state, gpointer user_data) { @@ -2305,10 +2309,8 @@ dhcp_state_changed (NMDHCPManager *dhcp_manager, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); NMDeviceState dev_state; - if (strcmp (nm_device_get_ip_iface (device), iface) != 0) - return; - - if (!nm_device_get_act_request (device)) + if ( !nm_device_get_act_request (device) + || (priv->dhcp4_id != id)) return; dev_state = nm_device_get_state (device); @@ -2350,12 +2352,14 @@ dhcp_state_changed (NMDHCPManager *dhcp_manager, static void dhcp_timeout (NMDHCPManager *dhcp_manager, - const char *iface, + guint32 id, gpointer user_data) { - NMDevice * device = NM_DEVICE (user_data); + NMDevice *device = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - if (strcmp (nm_device_get_ip_iface (device), iface) != 0) + if ( !nm_device_get_act_request (device) + || (priv->dhcp4_id != id)) return; if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c index 90828d9dd8..a99feea9db 100644 --- a/src/tests/test-dhcp-options.c +++ b/src/tests/test-dhcp-options.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. * */ @@ -88,7 +88,7 @@ test_generic_options (void) const char *expected_route2_gw = "10.1.1.1"; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-generic", "failed to parse DHCP4 options"); @@ -199,7 +199,7 @@ test_wins_options (void) options = fill_table (generic_options, NULL); options = fill_table (wins_options, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-wins", "failed to parse DHCP4 options"); @@ -245,7 +245,7 @@ test_classless_static_routes (void) options = fill_table (generic_options, NULL); options = fill_table (classless_routes_options, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-rfc3442", "failed to parse DHCP4 options"); @@ -311,7 +311,7 @@ test_invalid_classless_routes1 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes1, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options"); @@ -362,7 +362,7 @@ test_invalid_classless_routes2 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes2, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options"); @@ -432,7 +432,7 @@ test_invalid_classless_routes3 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes3, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options"); @@ -483,7 +483,7 @@ test_gateway_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (gw_in_classless_routes, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-rfc3442-gateway", "failed to parse DHCP4 options"); @@ -537,7 +537,7 @@ test_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (escaped_searches_options, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-escaped-domain-searches", "failed to parse DHCP4 options"); @@ -568,7 +568,7 @@ test_invalid_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_escaped_searches_options, options); - ip4_config = nm_dhcp_manager_options_to_ip4_config ("eth0", options); + ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); ASSERT (ip4_config != NULL, "dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options"); From 18062350492afa7f062f3ed4338ca0186d491e9b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 12 Jan 2010 22:09:28 -0800 Subject: [PATCH 05/20] dhcp: convert dhcp backends to classes --- src/Makefile.am | 1 + src/dhcp-manager/Makefile.am | 10 +- src/dhcp-manager/nm-dhcp-client.c | 900 ++++++++++++++++++++++++++ src/dhcp-manager/nm-dhcp-client.h | 122 ++++ src/dhcp-manager/nm-dhcp-dhclient.c | 248 +++++--- src/dhcp-manager/nm-dhcp-dhclient.h | 45 ++ src/dhcp-manager/nm-dhcp-dhcpcd.c | 129 ++-- src/dhcp-manager/nm-dhcp-dhcpcd.h | 45 ++ src/dhcp-manager/nm-dhcp-manager.c | 952 +++++----------------------- src/dhcp-manager/nm-dhcp-manager.h | 94 +-- src/nm-device-ethernet.c | 6 +- src/nm-device.c | 315 +++++---- src/tests/test-dhcp-options.c | 52 +- 13 files changed, 1709 insertions(+), 1210 deletions(-) create mode 100644 src/dhcp-manager/nm-dhcp-client.c create mode 100644 src/dhcp-manager/nm-dhcp-client.h create mode 100644 src/dhcp-manager/nm-dhcp-dhclient.h create mode 100644 src/dhcp-manager/nm-dhcp-dhcpcd.h diff --git a/src/Makefile.am b/src/Makefile.am index f351c787d1..60aa93ee15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,6 +46,7 @@ libtest_dhcp_la_CPPFLAGS = \ $(LIBNL_CFLAGS) libtest_dhcp_la_LIBADD = \ + $(top_builddir)/marshallers/libmarshallers.la \ $(top_builddir)/libnm-util/libnm-util.la \ $(GLIB_LIBS) \ $(DBUS_LIBS) \ diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am index b7b0c6581c..711c2a5058 100644 --- a/src/dhcp-manager/Makefile.am +++ b/src/dhcp-manager/Makefile.am @@ -9,9 +9,14 @@ INCLUDES = \ noinst_LTLIBRARIES = libdhcp-manager.la libdhcp_manager_la_SOURCES = \ + nm-dhcp-client.c \ + nm-dhcp-client.h \ nm-dhcp-manager.c \ nm-dhcp-manager.h \ - nm-dhcp-@DHCP_CLIENT@.c + nm-dhcp-dhclient.h \ + nm-dhcp-dhclient.c \ + nm-dhcp-dhcpcd.h \ + nm-dhcp-dhcpcd.c libdhcp_manager_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ @@ -29,6 +34,3 @@ libdhcp_manager_la_LIBADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) -EXTRA_DIST = \ - nm-dhcp-dhclient.c \ - nm-dhcp-dhcpcd.c diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c new file mode 100644 index 0000000000..e39c9bf312 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -0,0 +1,900 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-dhcp-client.h" + +#define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */ + +typedef struct { + char * iface; + guchar state; + GPid pid; + guint timeout_id; + guint watch_id; + GHashTable * options; +} NMDHCPClientPrivate; + +#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDHCPClientPrivate)) + +G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {}) + +enum { + STATE_CHANGED, + TIMEOUT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_IFACE, + LAST_PROP +}; + +/********************************************/ + +GPid +nm_dhcp_client_get_pid (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, -1); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid; +} + +const char * +nm_dhcp_client_get_iface (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface; +} + +/********************************************/ + +static void +timeout_cleanup (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } +} + +static void +watch_cleanup (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (priv->watch_id) { + g_source_remove (priv->watch_id); + priv->watch_id = 0; + } +} + +static void +stop_process (GPid pid, const char *iface) +{ + int i = 15; /* 3 seconds */ + + g_return_if_fail (pid > 0); + + /* Tell it to quit; maybe it wants to send out a RELEASE message */ + kill (pid, SIGTERM); + + while (i-- > 0) { + gint child_status; + int ret; + + ret = waitpid (pid, &child_status, WNOHANG); + if (ret > 0) + break; + + if (ret == -1) { + /* Child already exited */ + if (errno == ECHILD) + break; + /* Took too long; shoot it in the head */ + i = 0; + break; + } + g_usleep (G_USEC_PER_SEC / 5); + } + + if (i <= 0) { + if (iface) { + g_warning ("%s: dhcp client pid %d didn't exit, will kill it.", + iface, pid); + } + kill (pid, SIGKILL); + + g_warning ("waiting for dhcp client pid %d to exit", pid); + waitpid (pid, NULL, 0); + g_warning ("dhcp client pid %d cleaned up", pid); + } +} + +static void +real_stop (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_if_fail (priv->pid > 0); + + /* Clean up the watch handler since we're explicitly killing the daemon */ + watch_cleanup (self); + + stop_process (priv->pid, priv->iface); +} + +static gboolean +daemon_timeout (gpointer user_data) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (user_data); + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + g_message ("(%s): DHCP transaction took too long, stopping it.", priv->iface); + g_signal_emit (G_OBJECT (self), signals[TIMEOUT], 0); + return FALSE; +} + +static void +daemon_watch_cb (GPid pid, gint status, gpointer user_data) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (user_data); + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (!WIFEXITED (status)) { + priv->state = DHC_ABEND; + g_warning ("dhcp client died abnormally"); + } + priv->pid = 0; + + watch_cleanup (self); + timeout_cleanup (self); + + g_signal_emit (G_OBJECT (self), signals[STATE_CHANGED], 0, priv->state); +} + +gboolean +nm_dhcp_client_start (NMDHCPClient *self, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout_secs, + guint8 *dhcp_anycast_addr) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->pid == -1, FALSE); + + if (timeout_secs == 0) + timeout_secs = NM_DHCP_TIMEOUT; + + g_message ("Activation (%s) Beginning DHCP transaction (timeout in %d seconds)", + priv->iface, timeout_secs); + priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, + uuid, + s_ip4, + dhcp_anycast_addr); + if (priv->pid <= 0) + return FALSE; + + /* Set up a timeout on the transaction to kill it after the timeout */ + priv->timeout_id = g_timeout_add_seconds (timeout_secs, + daemon_timeout, + self); + priv->watch_id = g_child_watch_add (priv->pid, + (GChildWatchFunc) daemon_watch_cb, + self); + + return TRUE; +} + +void +nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) +{ + char *pid_contents = NULL, *proc_contents = NULL, *proc_path = NULL; + long int tmp; + + /* Check for an existing instance and stop it */ + if (!g_file_get_contents (pid_file, &pid_contents, NULL, NULL)) + return; + + errno = 0; + tmp = strtol (pid_contents, NULL, 10); + if ((errno == 0) && (tmp > 1)) { + const char *exe; + + /* Ensure the process is a DHCP client */ + proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp); + if (g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) { + exe = strrchr (proc_contents, '/'); + if (exe) + exe++; + else + exe = proc_contents; + + if (!strcmp (exe, binary_name)) + stop_process ((GPid) tmp, NULL); + } + } + + remove (pid_file); + g_free (proc_path); + g_free (pid_contents); + g_free (proc_contents); +} + +void +nm_dhcp_client_stop (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + /* Kill the DHCP client */ + if (priv->pid > 0) { + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self); + + g_message ("(%s): canceled DHCP transaction, dhcp client pid %d", + priv->iface, + priv->pid); + } + + /* And clean stuff up */ + + priv->pid = -1; + priv->state = DHC_END; + + g_hash_table_remove_all (priv->options); + + timeout_cleanup (self); + watch_cleanup (self); +} + +/********************************************/ + +static gboolean +state_is_bound (guint32 state) +{ + if ( (state == DHC_BOUND4) + || (state == DHC_BOUND6) + || (state == DHC_RENEW4) + || (state == DHC_RENEW6) + || (state == DHC_REBOOT) + || (state == DHC_REBIND4) + || (state == DHC_REBIND6) + || (state == DHC_IPV4LL)) + return TRUE; + + return FALSE; +} + +typedef struct { + NMDHCPState state; + const char *name; +} DhcState; + +#define STATE_TABLE_SIZE (sizeof (state_table) / sizeof (state_table[0])) + +static DhcState state_table[] = { + { DHC_NBI, "nbi" }, + { DHC_PREINIT, "preinit" }, + { DHC_BOUND4, "bound" }, + { DHC_BOUND6, "bound6" }, + { DHC_IPV4LL, "ipv4ll" }, + { DHC_RENEW4, "renew" }, + { DHC_RENEW6, "renew6" }, + { DHC_REBOOT, "reboot" }, + { DHC_REBIND4, "rebind" }, + { DHC_REBIND6, "rebind6" }, + { DHC_STOP, "stop" }, + { DHC_MEDIUM, "medium" }, + { DHC_TIMEOUT, "timeout" }, + { DHC_FAIL, "fail" }, + { DHC_EXPIRE, "expire" }, + { DHC_RELEASE, "release" }, + { DHC_START, "start" }, + { DHC_ABEND, "abend" }, + { DHC_END, "end" }, + { DHC_DEPREF6, "depref6" }, +}; + +static inline const char * +state_to_string (guint32 state) +{ + int i; + + for (i = 0; i < STATE_TABLE_SIZE; i++) { + if (state == state_table[i].state) + return state_table[i].name; + } + + return NULL; +} + +static inline NMDHCPState +string_to_state (const char *name) +{ + int i; + + for (i = 0; i < STATE_TABLE_SIZE; i++) { + if (!strcasecmp (name, state_table[i].name)) + return state_table[i].state; + } + + return 255; +} + +static char * +garray_to_string (GArray *array, const char *key) +{ + GString *str; + int i; + unsigned char c; + char *converted = NULL; + + g_return_val_if_fail (array != NULL, NULL); + + /* Since the DHCP options come through environment variables, they should + * already be UTF-8 safe, but just make sure. + */ + str = g_string_sized_new (array->len); + for (i = 0; i < array->len; i++) { + c = array->data[i]; + + /* Convert NULLs to spaces and non-ASCII characters to ? */ + if (c == '\0') + c = ' '; + else if (c > 127) + c = '?'; + str = g_string_append_c (str, c); + } + str = g_string_append_c (str, '\0'); + + converted = str->str; + if (!g_utf8_validate (converted, -1, NULL)) + g_warning ("%s: DHCP option '%s' couldn't be converted to UTF-8", __func__, key); + g_string_free (str, FALSE); + return converted; +} + +static void +copy_option (gpointer key, + gpointer value, + gpointer user_data) +{ + GHashTable *hash = user_data; + const char *str_key = (const char *) key; + char *str_value = NULL; + + if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { + g_warning ("Unexpected key %s value type was not " + "DBUS_TYPE_G_UCHAR_ARRAY", + str_key); + return; + } + + str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key); + if (str_value) + g_hash_table_insert (hash, g_strdup (str_key), str_value); +} + +void +nm_dhcp_client_new_options (NMDHCPClient *self, + GHashTable *options, + const char *reason) +{ + NMDHCPClientPrivate *priv; + guint32 old_state; + guint32 new_state; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DHCP_CLIENT (self)); + g_return_if_fail (options != NULL); + g_return_if_fail (reason != NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + old_state = priv->state; + new_state = string_to_state (reason); + + /* Clear old and save new DHCP options */ + g_hash_table_remove_all (priv->options); + g_hash_table_foreach (options, copy_option, priv->options); + + if (old_state == new_state) + return; + + /* Handle changed device state */ + if (state_is_bound (new_state)) { + /* Cancel the timeout if the DHCP client is now bound */ + timeout_cleanup (self); + } + + priv->state = new_state; + g_message ("DHCP: device %s state changed %s -> %s", + priv->iface, + state_to_string (old_state), + state_to_string (priv->state)); + + g_signal_emit (G_OBJECT (self), + signals[STATE_CHANGED], + 0, + priv->state); +} + +#define NEW_TAG "new_" +#define OLD_TAG "old_" + +typedef struct { + GHFunc func; + gpointer user_data; +} Dhcp4ForeachInfo; + +static void +iterate_dhcp4_config_option (gpointer key, + gpointer value, + gpointer user_data) +{ + Dhcp4ForeachInfo *info = (Dhcp4ForeachInfo *) user_data; + char *tmp_key = NULL; + const char **p; + static const char *filter_options[] = { + "interface", "pid", "reason", "dhcp_message_type", NULL + }; + + /* Filter out stuff that's not actually new DHCP options */ + for (p = filter_options; *p; p++) { + if (!strcmp (*p, (const char *) key)) + return; + if (!strncmp ((const char *) key, OLD_TAG, strlen (OLD_TAG))) + return; + } + + /* Remove the "new_" prefix that dhclient passes back */ + if (!strncmp ((const char *) key, NEW_TAG, strlen (NEW_TAG))) + tmp_key = g_strdup ((const char *) (key + strlen (NEW_TAG))); + else + tmp_key = g_strdup ((const char *) key); + + (*info->func) ((gpointer) tmp_key, value, info->user_data); + g_free (tmp_key); +} + +gboolean +nm_dhcp_client_foreach_dhcp4_option (NMDHCPClient *self, + GHFunc func, + gpointer user_data) +{ + NMDHCPClientPrivate *priv; + Dhcp4ForeachInfo info = { NULL, NULL }; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (!state_is_bound (priv->state)) { + g_warning ("%s: dhclient didn't bind to a lease.", priv->iface); + return FALSE; + } + + info.func = func; + info.user_data = user_data; + g_hash_table_foreach (priv->options, iterate_dhcp4_config_option, &info); + return TRUE; +} + +/********************************************/ + +static void +process_classful_routes (GHashTable *options, NMIP4Config *ip4_config) +{ + const char *str; + char **searches, **s; + + str = g_hash_table_lookup (options, "new_static_routes"); + if (!str) + return; + + searches = g_strsplit (str, " ", 0); + if ((g_strv_length (searches) % 2)) { + g_message (" static routes provided, but invalid"); + goto out; + } + + for (s = searches; *s; s += 2) { + NMIP4Route *route; + struct in_addr rt_addr; + struct in_addr rt_route; + + if (inet_pton (AF_INET, *s, &rt_addr) <= 0) { + g_warning ("DHCP provided invalid static route address: '%s'", *s); + continue; + } + if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) { + g_warning ("DHCP provided invalid static route gateway: '%s'", *(s + 1)); + continue; + } + + // FIXME: ensure the IP addresse and route are sane + + route = nm_ip4_route_new (); + nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr); + nm_ip4_route_set_prefix (route, 32); /* 255.255.255.255 */ + nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr); + + nm_ip4_config_take_route (ip4_config, route); + g_message (" static route %s gw %s", *s, *(s + 1)); + } + +out: + g_strfreev (searches); +} + +static void +process_domain_search (NMIP4Config *ip4_config, const char *str) +{ + char **searches, **s; + char *unescaped, *p; + int i; + + g_return_if_fail (str != NULL); + g_return_if_fail (ip4_config != NULL); + + p = unescaped = g_strdup (str); + do { + p = strstr (p, "\\032"); + if (!p) + break; + + /* Clear the escaped space with real spaces */ + for (i = 0; i < 4; i++) + *p++ = ' '; + } while (*p++); + + if (strchr (unescaped, '\\')) { + g_message (" invalid domain search: '%s'", unescaped); + goto out; + } + + searches = g_strsplit (unescaped, " ", 0); + for (s = searches; *s; s++) { + if (strlen (*s)) { + g_message (" domain search '%s'", *s); + nm_ip4_config_add_search (ip4_config, *s); + } + } + g_strfreev (searches); + +out: + g_free (unescaped); +} + +/* Given a table of DHCP options from the client, convert into an IP4Config */ +static NMIP4Config * +ip4_options_to_config (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + NMIP4Config *ip4_config = NULL; + struct in_addr tmp_addr; + NMIP4Address *addr = NULL; + char *str = NULL; + guint32 gwaddr = 0; + gboolean have_classless = FALSE; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->options != NULL, NULL); + + ip4_config = nm_ip4_config_new (); + if (!ip4_config) { + g_warning ("%s: couldn't allocate memory for an IP4Config!", priv->iface); + return NULL; + } + + addr = nm_ip4_address_new (); + if (!addr) { + g_warning ("%s: couldn't allocate memory for an IP4 Address!", priv->iface); + goto error; + } + + str = g_hash_table_lookup (priv->options, "new_ip_address"); + if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { + nm_ip4_address_set_address (addr, tmp_addr.s_addr); + g_message (" address %s", str); + } else + goto error; + + str = g_hash_table_lookup (priv->options, "new_subnet_mask"); + if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { + nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr)); + g_message (" prefix %d (%s)", nm_ip4_address_get_prefix (addr), str); + } + + /* Routes: if the server returns classless static routes, we MUST ignore + * the 'static_routes' option. + */ + if (NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes) { + have_classless = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes (self, + priv->options, + ip4_config, + &gwaddr); + } + + if (!have_classless) { + gwaddr = 0; /* Ensure client code doesn't lie */ + process_classful_routes (priv->options, ip4_config); + } + + if (gwaddr) { + char buf[INET_ADDRSTRLEN + 1]; + + inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf)); + g_message (" gateway %s", buf); + nm_ip4_address_set_gateway (addr, gwaddr); + } else { + /* If the gateway wasn't provided as a classless static route with a + * subnet length of 0, try to find it using the old-style 'routers' option. + */ + str = g_hash_table_lookup (priv->options, "new_routers"); + if (str) { + char **routers = g_strsplit (str, " ", 0); + char **s; + + for (s = routers; *s; s++) { + /* FIXME: how to handle multiple routers? */ + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_address_set_gateway (addr, tmp_addr.s_addr); + g_message (" gateway %s", *s); + break; + } else + g_warning ("Ignoring invalid gateway '%s'", *s); + } + g_strfreev (routers); + } + } + + nm_ip4_config_take_address (ip4_config, addr); + addr = NULL; + + str = g_hash_table_lookup (priv->options, "new_host_name"); + if (str) + g_message (" hostname '%s'", str); + + str = g_hash_table_lookup (priv->options, "new_domain_name_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr); + g_message (" nameserver '%s'", *s); + } else + g_warning ("Ignoring invalid nameserver '%s'", *s); + } + g_strfreev (searches); + } + + str = g_hash_table_lookup (priv->options, "new_domain_name"); + if (str) { + char **domains = g_strsplit (str, " ", 0); + char **s; + + for (s = domains; *s; s++) { + g_message (" domain name '%s'", *s); + nm_ip4_config_add_domain (ip4_config, *s); + } + g_strfreev (domains); + } + + str = g_hash_table_lookup (priv->options, "new_domain_search"); + if (str) + process_domain_search (ip4_config, str); + + str = g_hash_table_lookup (priv->options, "new_netbios_name_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { + nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr); + g_message (" wins '%s'", *s); + } else + g_warning ("Ignoring invalid WINS server '%s'", *s); + } + g_strfreev (searches); + } + + str = g_hash_table_lookup (priv->options, "new_interface_mtu"); + if (str) { + int int_mtu; + + errno = 0; + int_mtu = strtol (str, NULL, 10); + if ((errno == EINVAL) || (errno == ERANGE)) + goto error; + + if (int_mtu > 576) + nm_ip4_config_set_mtu (ip4_config, int_mtu); + } + + return ip4_config; + +error: + if (addr) + nm_ip4_address_unref (addr); + g_object_unref (ip4_config); + return NULL; +} + +NMIP4Config * +nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (test && !state_is_bound (priv->state)) { + g_warning ("%s: dhcp client didn't bind to a lease.", priv->iface); + return NULL; + } + + return ip4_options_to_config (self); +} + +/********************************************/ + +static void +nm_dhcp_client_init (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->pid = -1; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_IFACE: + g_value_set_string (value, priv->iface); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_IFACE: + /* construct-only */ + priv->iface = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMDHCPClient *self = NM_DHCP_CLIENT (object); + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + /* Stopping the client is left up to the controlling device + * explicitly since we may want to quit NetworkManager but not terminate + * the DHCP client. + */ + + g_hash_table_destroy (priv->options); + g_free (priv->iface); + + G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); +} + +static void +nm_dhcp_client_class_init (NMDHCPClientClass *client_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (client_class); + + g_type_class_add_private (client_class, sizeof (NMDHCPClientPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + client_class->stop = real_stop; + + g_object_class_install_property + (object_class, PROP_IFACE, + g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, + "iface", + "Interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPClientClass, state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[TIMEOUT] = + g_signal_new ("timeout", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPClientClass, timeout), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h new file mode 100644 index 0000000000..0e5d81229d --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -0,0 +1,122 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_CLIENT_H +#define NM_DHCP_CLIENT_H + +#include +#include + +#include + +#define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type ()) +#define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClient)) +#define NM_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass)) +#define NM_IS_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_CLIENT)) +#define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_CLIENT)) +#define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass)) + +#define NM_DHCP_CLIENT_INTERFACE "iface" + +typedef enum { + DHC_NBI = 0, /* no broadcast interfaces found */ + DHC_PREINIT, /* configuration started */ + DHC_BOUND4, /* IPv4 lease obtained */ + DHC_BOUND6, /* IPv6 lease obtained */ + DHC_IPV4LL, /* IPv4LL address obtained */ + DHC_RENEW4, /* IPv4 lease renewed */ + DHC_RENEW6, /* IPv6 lease renewed */ + DHC_REBOOT, /* have valid lease, but now obtained a different one */ + DHC_REBIND4, /* IPv4 new/different lease */ + DHC_REBIND6, /* IPv6 new/different lease */ + DHC_DEPREF6, /* IPv6 lease depreferred */ + DHC_STOP, /* remove old lease */ + DHC_MEDIUM, /* media selection begun */ + DHC_TIMEOUT, /* timed out contacting DHCP server */ + DHC_FAIL, /* all attempts to contact server timed out, sleeping */ + DHC_EXPIRE, /* lease has expired, renewing */ + DHC_RELEASE, /* releasing lease */ + DHC_START, /* sent when dhclient started OK */ + DHC_ABEND, /* dhclient exited abnormally */ + DHC_END, /* dhclient exited normally */ + DHC_END_OPTIONS, /* last option in subscription sent */ +} NMDHCPState; + +typedef struct { + GObject parent; +} NMDHCPClient; + +typedef struct { + GObjectClass parent; + + /* Methods */ + + /* Given the options table, extract any classless routes, add them to + * the IP4 config and return TRUE if any existed. If a gateway was sent + * as a classless route return that in out_gwaddr. + */ + gboolean (*ip4_process_classless_routes) (NMDHCPClient *self, + GHashTable *options, + NMIP4Config *ip4_config, + guint32 *out_gwaddr); + + GPid (*ip4_start) (NMDHCPClient *self, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr); + + void (*stop) (NMDHCPClient *self); + + /* Signals */ + void (*state_changed) (NMDHCPClient *self, NMDHCPState state); + void (*timeout) (NMDHCPClient *self); +} NMDHCPClientClass; + +GType nm_dhcp_client_get_type (void); + +GPid nm_dhcp_client_get_pid (NMDHCPClient *self); + +const char *nm_dhcp_client_get_iface (NMDHCPClient *self); + +gboolean nm_dhcp_client_start (NMDHCPClient *self, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout_secs, + guint8 *dhcp_anycast_addr); + +void nm_dhcp_client_stop (NMDHCPClient *self); + +void nm_dhcp_client_new_options (NMDHCPClient *self, + GHashTable *options, + const char *reason); + +gboolean nm_dhcp_client_foreach_dhcp4_option (NMDHCPClient *self, + GHFunc func, + gpointer user_data); + +NMIP4Config *nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test); + +/* Backend helpers */ +void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name); + +/* Implemented by the backends */ +GSList *nm_dhcp_backend_get_lease_config (const char *iface, + const char *uuid); + +#endif /* NM_DHCP_CLIENT_H */ + diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index e0deabb4a8..c47d155b0f 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -36,46 +36,37 @@ #include -#include "nm-dhcp-manager.h" +#include "nm-dhcp-dhclient.h" #include "nm-utils.h" +G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT) -#define NM_DHCP_MANAGER_PID_FILENAME "dhclient" -#define NM_DHCP_MANAGER_PID_FILE_EXT "pid" +#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate)) #if defined(TARGET_DEBIAN) -#define NM_DHCP_MANAGER_LEASE_DIR LOCALSTATEDIR "/lib/dhcp3" +#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp3" #elif defined(TARGET_SUSE) || defined(TARGET_MANDRIVA) -#define NM_DHCP_MANAGER_LEASE_DIR LOCALSTATEDIR "/lib/dhcp" +#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp" #else -#define NM_DHCP_MANAGER_LEASE_DIR LOCALSTATEDIR "/lib/dhclient" +#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhclient" #endif -#define NM_DHCP_MANAGER_LEASE_FILENAME "dhclient" -#define NM_DHCP_MANAGER_LEASE_FILE_EXT "lease" #define ACTION_SCRIPT_PATH LIBEXECDIR "/nm-dhcp-client.action" - -static char * -get_pidfile_for_iface (const char * iface) -{ - return g_strdup_printf ("%s/%s-%s.%s", - NM_DHCP_MANAGER_RUN_DIR, - NM_DHCP_MANAGER_PID_FILENAME, - iface, - NM_DHCP_MANAGER_PID_FILE_EXT); -} +typedef struct { + char *conf_file; + char *lease_file; + char *pid_file; +} NMDHCPDhclientPrivate; static char * get_leasefile_for_iface (const char * iface, const char *uuid) { - return g_strdup_printf ("%s/%s-%s-%s.%s", - NM_DHCP_MANAGER_LEASE_DIR, - NM_DHCP_MANAGER_LEASE_FILENAME, + return g_strdup_printf ("%s/dhclient-%s-%s.lease", + NM_DHCLIENT_LEASE_DIR, uuid, - iface, - NM_DHCP_MANAGER_LEASE_FILE_EXT); + iface); } static void @@ -119,7 +110,7 @@ add_lease_option (GHashTable *hash, char *line) } GSList * -nm_dhcp4_client_get_lease_config (const char *iface, const char *uuid) +nm_dhcp_backend_get_lease_config (const char *iface, const char *uuid) { GSList *parsed = NULL, *iter, *leases = NULL; char *contents = NULL; @@ -288,28 +279,39 @@ out: #define DHCP_HOSTNAME_FORMAT DHCP_HOSTNAME_TAG " \"%s\"; # added by NetworkManager" static gboolean -merge_dhclient_config (NMDHCPClient *client, +merge_dhclient_config (const char *iface, + const char *conf_file, NMSettingIP4Config *s_ip4, guint8 *anycast_addr, - const char *contents, - const char *orig, + const char *orig_path, GError **error) { GString *new_contents; + char *orig_contents = NULL; gboolean success = FALSE; - g_return_val_if_fail (client != NULL, FALSE); - g_return_val_if_fail (client->iface != NULL, FALSE); - + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (conf_file != NULL, FALSE); + new_contents = g_string_new (_("# Created by NetworkManager\n")); + if (g_file_test (orig_path, G_FILE_TEST_EXISTS)) { + GError *read_error = NULL; + + if (!g_file_get_contents (orig_path, &orig_contents, NULL, &read_error)) { + nm_warning ("%s: error reading dhclient configuration %s: %s", + iface, orig_path, read_error->message); + g_error_free (read_error); + } + } + /* Add existing options, if any, but ignore stuff NM will replace. */ - if (contents) { + if (orig_contents) { char **lines = NULL, **line; - g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig); + g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path); - lines = g_strsplit_set (contents, "\n\r", 0); + lines = g_strsplit_set (orig_contents, "\n\r", 0); for (line = lines; lines && *line; line++) { gboolean ignore = FALSE; @@ -334,6 +336,7 @@ merge_dhclient_config (NMDHCPClient *client, if (lines) g_strfreev (lines); + g_free (orig_contents); } else g_string_append_c (new_contents, '\n'); @@ -374,14 +377,13 @@ merge_dhclient_config (NMDHCPClient *client, " initial-interval 1; \n" " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n" "}\n", - client->iface, + iface, anycast_addr[0], anycast_addr[1], anycast_addr[2], anycast_addr[3], anycast_addr[4], anycast_addr[5]); } - if (g_file_set_contents (client->conf_file, new_contents->str, -1, error)) - success = TRUE; + success = g_file_set_contents (conf_file, new_contents->str, -1, error); g_string_free (new_contents, TRUE); return success; @@ -393,18 +395,16 @@ merge_dhclient_config (NMDHCPClient *client, * read their single config file and merge that into a custom per-interface * config file along with the NM options. */ -static gboolean -create_dhclient_config (NMDHCPClient *client, +static char * +create_dhclient_config (const char *iface, NMSettingIP4Config *s_ip4, guint8 *dhcp_anycast_addr) { - char *orig = NULL, *contents = NULL; + char *orig = NULL, *tmp, *conf_file = NULL; GError *error = NULL; gboolean success = FALSE; - char *tmp; - g_return_val_if_fail (client != NULL, FALSE); - g_return_val_if_fail (client->iface != NULL, FALSE); + g_return_val_if_fail (iface != NULL, FALSE); #if defined(TARGET_SUSE) orig = g_strdup (SYSCONFDIR "/dhclient.conf"); @@ -413,41 +413,28 @@ create_dhclient_config (NMDHCPClient *client, #elif defined(TARGET_GENTOO) orig = g_strdup (SYSCONFDIR "/dhcp/dhclient.conf"); #else - orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", client->iface); + orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", iface); #endif if (!orig) { - nm_warning ("%s: not enough memory for dhclient options.", client->iface); + nm_warning ("%s: not enough memory for dhclient options.", iface); return FALSE; } - tmp = g_strdup_printf ("nm-dhclient-%s.conf", client->iface); - client->conf_file = g_build_filename ("/var", "run", tmp, NULL); + tmp = g_strdup_printf ("nm-dhclient-%s.conf", iface); + conf_file = g_build_filename ("/var", "run", tmp, NULL); g_free (tmp); - if (!g_file_test (orig, G_FILE_TEST_EXISTS)) - goto out; - - if (!g_file_get_contents (orig, &contents, NULL, &error)) { - nm_warning ("%s: error reading dhclient configuration %s: %s", - client->iface, orig, error->message); - g_error_free (error); - goto out; - } - -out: error = NULL; - if (merge_dhclient_config (client, s_ip4, dhcp_anycast_addr, contents, orig, &error)) - success = TRUE; - else { + success = merge_dhclient_config (iface, conf_file, s_ip4, dhcp_anycast_addr, orig, &error); + if (!success) { nm_warning ("%s: error creating dhclient configuration: %s", - client->iface, error->message); + iface, error->message); g_error_free (error); } - g_free (contents); g_free (orig); - return success; + return conf_file; } @@ -459,45 +446,49 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) setpgid (pid, pid); } - -GPid -nm_dhcp4_client_start (NMDHCPClient *client, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *dhcp_anycast_addr) +static GPid +real_ip4_start (NMDHCPClient *client, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) { + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); GPtrArray *argv = NULL; GPid pid = 0; GError *error = NULL; - char *pid_contents = NULL; + const char *iface; + char *binary_name; + + g_return_val_if_fail (priv->pid_file == NULL, -1); + + iface = nm_dhcp_client_get_iface (client); + + priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient-%s.pid", iface); + if (!priv->pid_file) { + nm_warning ("%s: not enough memory for dhcpcd options.", iface); + return -1; + } if (!g_file_test (DHCP_CLIENT_PATH, G_FILE_TEST_EXISTS)) { nm_warning (DHCP_CLIENT_PATH " does not exist."); - goto out; + return -1; } - client->pid_file = get_pidfile_for_iface (client->iface); - if (!client->pid_file) { - nm_warning ("%s: not enough memory for dhclient options.", client->iface); - goto out; + /* Kill any existing dhclient from the pidfile */ + binary_name = g_path_get_basename (DHCP_CLIENT_PATH); + nm_dhcp_client_stop_existing (priv->pid_file, binary_name); + g_free (binary_name); + + priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr); + if (!priv->conf_file) { + nm_warning ("%s: error creating dhclient configuration file.", iface); + return -1; } - client->lease_file = get_leasefile_for_iface (client->iface, uuid); - if (!client->lease_file) { - nm_warning ("%s: not enough memory for dhclient options.", client->iface); - goto out; - } - - if (!create_dhclient_config (client, s_ip4, dhcp_anycast_addr)) - goto out; - - /* Kill any existing dhclient bound to this interface */ - if (g_file_get_contents (client->pid_file, &pid_contents, NULL, NULL)) { - unsigned long int tmp = strtoul (pid_contents, NULL, 10); - - if (!((tmp == ULONG_MAX) && (errno == ERANGE))) - nm_dhcp_client_stop (client, (pid_t) tmp); - remove (client->pid_file); + priv->lease_file = get_leasefile_for_iface (iface, uuid); + if (!priv->lease_file) { + nm_warning ("%s: not enough memory for dhclient options.", iface); + return -1; } argv = g_ptr_array_new (); @@ -509,32 +500,43 @@ nm_dhcp4_client_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ - g_ptr_array_add (argv, (gpointer) client->pid_file); + g_ptr_array_add (argv, (gpointer) priv->pid_file); g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ - g_ptr_array_add (argv, (gpointer) client->lease_file); + g_ptr_array_add (argv, (gpointer) priv->lease_file); g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ - g_ptr_array_add (argv, (gpointer) client->conf_file); + g_ptr_array_add (argv, (gpointer) priv->conf_file); - g_ptr_array_add (argv, (gpointer) client->iface); + g_ptr_array_add (argv, (gpointer) iface); g_ptr_array_add (argv, NULL); if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, &dhclient_child_setup, NULL, &pid, &error)) { nm_warning ("dhclient failed to start. error: '%s'", error->message); g_error_free (error); - goto out; - } + pid = -1; + } else + nm_info ("dhclient started with pid %d", pid); - nm_info ("dhclient started with pid %d", pid); - -out: - g_free (pid_contents); g_ptr_array_free (argv, TRUE); return pid; } +static void +real_stop (NMDHCPClient *client) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + + /* Chain up to parent */ + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client); + + if (priv->conf_file) + remove (priv->conf_file); + if (priv->pid_file) + remove (priv->pid_file); +} + static const char ** process_rfc3442_route (const char **octets, NMIP4Route **out_route) { @@ -596,10 +598,11 @@ error: return o; } -gboolean -nm_dhcp4_client_process_classless_routes (GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr) +static gboolean +real_ip4_process_classless_routes (NMDHCPClient *client, + GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr) { const char *str; char **octets, **o; @@ -664,3 +667,38 @@ out: return have_routes; } +/***************************************************/ + +static void +nm_dhcp_dhclient_init (NMDHCPDhclient *self) +{ +} + +static void +dispose (GObject *object) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object); + + g_free (priv->pid_file); + g_free (priv->conf_file); + g_free (priv->lease_file); + + G_OBJECT_CLASS (nm_dhcp_dhclient_parent_class)->dispose (object); +} + +static void +nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class) +{ + NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class); + GObjectClass *object_class = G_OBJECT_CLASS (dhclient_class); + + g_type_class_add_private (dhclient_class, sizeof (NMDHCPDhclientPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + client_class->ip4_start = real_ip4_start; + client_class->stop = real_stop; + client_class->ip4_process_classless_routes = real_ip4_process_classless_routes; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h new file mode 100644 index 0000000000..bad6452f79 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhclient.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_DHCLIENT_H +#define NM_DHCP_DHCLIENT_H + +#include +#include + +#include "nm-dhcp-client.h" + +#define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type ()) +#define NM_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclient)) +#define NM_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass)) +#define NM_IS_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT)) +#define NM_IS_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT)) +#define NM_DHCP_DHCLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass)) + +typedef struct { + NMDHCPClient parent; +} NMDHCPDhclient; + +typedef struct { + NMDHCPClientClass parent; +} NMDHCPDhclientClass; + +GType nm_dhcp_dhclient_get_type (void); + +#endif /* NM_DHCP_DHCLIENT_H */ + diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index 6ba3f0a91c..ba210eda63 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -2,6 +2,7 @@ /* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager * * Copyright (C) 2008 Roy Marples + * Copyright (C) 2010 Dan Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,26 +32,22 @@ #include #include -#include "nm-dhcp-manager.h" +#include "nm-dhcp-dhcpcd.h" #include "nm-utils.h" -#define NM_DHCP_MANAGER_PID_FILENAME "dhcpcd" -#define NM_DHCP_MANAGER_PID_FILE_EXT "pid" +G_DEFINE_TYPE (NMDHCPDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_DHCPCD) + +#define NM_DHCP_DHCPCD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdPrivate)) #define ACTION_SCRIPT_PATH LIBEXECDIR "/nm-dhcp-client.action" +typedef struct { + char *pid_file; +} NMDHCPDhcpcdPrivate; -static char * -get_pidfile_for_iface (const char * iface) -{ - return g_strdup_printf ("/var/run/%s-%s.%s", - NM_DHCP_MANAGER_PID_FILENAME, - iface, - NM_DHCP_MANAGER_PID_FILE_EXT); -} GSList * -nm_dhcp4_client_get_lease_config (const char *iface, const char *uuid) +nm_dhcp_backend_get_lease_config (const char *iface, const char *uuid) { return NULL; } @@ -63,37 +60,38 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) setpgid (pid, pid); } - -GPid -nm_dhcp4_client_start (NMDHCPClient *client, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *dhcp_anycast_addr) +static GPid +real_ip4_start (NMDHCPClient *client, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) { + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client); GPtrArray *argv = NULL; GPid pid = 0; GError *error = NULL; - char *pid_contents = NULL; + char *pid_contents = NULL, *binary_name; + const char *iface; + + g_return_val_if_fail (priv->pid_file == NULL, -1); + + iface = nm_dhcp_client_get_iface (client); + + priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhcpcd-%s.pid", iface); + if (!priv->pid_file) { + nm_warning ("%s: not enough memory for dhcpcd options.", iface); + return -1; + } if (!g_file_test (DHCP_CLIENT_PATH, G_FILE_TEST_EXISTS)) { nm_warning (DHCP_CLIENT_PATH " does not exist."); - goto out; + return -1; } - client->pid_file = get_pidfile_for_iface (client->iface); - if (!client->pid_file) { - nm_warning ("%s: not enough memory for dhcpcd options.", client->iface); - goto out; - } - - /* Kill any existing dhcpcd bound to this interface */ - if (g_file_get_contents (client->pid_file, &pid_contents, NULL, NULL)) { - unsigned long int tmp = strtoul (pid_contents, NULL, 10); - - if (!((tmp == ULONG_MAX) && (errno == ERANGE))) - nm_dhcp_client_stop (client, (pid_t) tmp); - remove (client->pid_file); - } + /* Kill any existing dhclient from the pidfile */ + binary_name = g_path_get_basename (DHCP_CLIENT_PATH); + nm_dhcp_client_stop_existing (priv->pid_file, binary_name); + g_free (binary_name); argv = g_ptr_array_new (); g_ptr_array_add (argv, (gpointer) DHCP_CLIENT_PATH); @@ -107,28 +105,38 @@ nm_dhcp4_client_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */ g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); - g_ptr_array_add (argv, (gpointer) client->iface); + g_ptr_array_add (argv, (gpointer) iface); g_ptr_array_add (argv, NULL); if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, &dhcpcd_child_setup, NULL, &pid, &error)) { nm_warning ("dhcpcd failed to start. error: '%s'", error->message); g_error_free (error); - goto out; - } + } else + nm_info ("dhcpcd started with pid %d", pid); - nm_info ("dhcpcd started with pid %d", pid); - -out: g_free (pid_contents); g_ptr_array_free (argv, TRUE); return pid; } -gboolean -nm_dhcp4_client_process_classless_routes (GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr) +static void +real_stop (NMDHCPClient *client) +{ + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client); + + /* Chain up to parent */ + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client); + + if (priv->pid_file) + remove (priv->pid_file); +} + +static gboolean +real_ip4_process_classless_routes (NMDHCPClient *client, + GHashTable *options, + NMIP4Config *ip4_config, + guint32 *gwaddr) { const char *str; char **routes, **r; @@ -201,3 +209,36 @@ out: return have_routes; } +/***************************************************/ + +static void +nm_dhcp_dhcpcd_init (NMDHCPDhcpcd *self) +{ +} + +static void +dispose (GObject *object) +{ + NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object); + + g_free (priv->pid_file); + + G_OBJECT_CLASS (nm_dhcp_dhcpcd_parent_class)->dispose (object); +} + +static void +nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class) +{ + NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcd_class); + GObjectClass *object_class = G_OBJECT_CLASS (dhcpcd_class); + + g_type_class_add_private (dhcpcd_class, sizeof (NMDHCPDhcpcdPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + client_class->ip4_start = real_ip4_start; + client_class->stop = real_stop; + client_class->ip4_process_classless_routes = real_ip4_process_classless_routes; +} + diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h new file mode 100644 index 0000000000..f564bc7f71 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2010 Red Hat, Inc. + */ + +#ifndef NM_DHCP_DHCPCD_H +#define NM_DHCP_DHCPCD_H + +#include +#include + +#include "nm-dhcp-client.h" + +#define NM_TYPE_DHCP_DHCPCD (nm_dhcp_dhcpcd_get_type ()) +#define NM_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcd)) +#define NM_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass)) +#define NM_IS_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCPCD)) +#define NM_IS_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCPCD)) +#define NM_DHCP_DHCPCD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass)) + +typedef struct { + NMDHCPClient parent; +} NMDHCPDhcpcd; + +typedef struct { + NMDHCPClientClass parent; +} NMDHCPDhcpcdClass; + +GType nm_dhcp_dhcpcd_get_type (void); + +#endif /* NM_DHCP_DHCPCD_H */ + diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 2b48a2651d..f1a84cdd46 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -25,8 +25,6 @@ #include #include #include -#include -#include #include #include #include @@ -37,6 +35,7 @@ #include #include "nm-dhcp-manager.h" +#include "nm-dhcp-dhclient.h" #include "nm-marshal.h" #include "nm-utils.h" #include "nm-dbus-manager.h" @@ -47,13 +46,11 @@ #define NM_DHCP_CLIENT_DBUS_SERVICE "org.freedesktop.nm_dhcp_client" #define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client" -#define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */ - typedef struct { - guint32 next_id; - NMDBusManager * dbus_mgr; - GHashTable * clients; - DBusGProxy * proxy; + guint32 next_id; + NMDBusManager * dbus_mgr; + GHashTable * clients; + DBusGProxy * proxy; NMHostnameProvider *hostname_provider; } NMDHCPManagerPrivate; @@ -62,131 +59,6 @@ typedef struct { G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT) -enum { - STATE_CHANGED, - TIMEOUT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -static void nm_dhcp_manager_cancel_transaction_real (NMDHCPClient *client); - -static gboolean state_is_bound (guint32 state) -{ - if ( (state == DHC_BOUND4) - || (state == DHC_BOUND6) - || (state == DHC_RENEW4) - || (state == DHC_RENEW6) - || (state == DHC_REBOOT) - || (state == DHC_REBIND4) - || (state == DHC_REBIND6) - || (state == DHC_IPV4LL)) - return TRUE; - - return FALSE; -} - - -static void -nm_dhcp_client_timeout_cleanup (NMDHCPClient *client) -{ - if (client->timeout_id) { - g_source_remove (client->timeout_id); - client->timeout_id = 0; - } -} - -static void -nm_dhcp_client_watch_cleanup (NMDHCPClient *client) -{ - if (client->watch_id) { - g_source_remove (client->watch_id); - client->watch_id = 0; - } -} - -static void -nm_dhcp_client_destroy (NMDHCPClient *client) -{ - int ignored; - - nm_dhcp_client_timeout_cleanup (client); - - if (client->pid) - nm_dhcp_client_stop (client, client->pid); - - if (client->options) - g_hash_table_destroy (client->options); - - if (client->conf_file) { - ignored = unlink (client->conf_file); - g_free (client->conf_file); - } - - g_free (client->pid_file); - g_free (client->lease_file); - g_free (client->iface); - - g_slice_free (NMDHCPClient, client); -} - -typedef struct { - NMDHCPState state; - const char *name; -} DhcState; - -#define STATE_TABLE_SIZE (sizeof (state_table) / sizeof (state_table[0])) - -static DhcState state_table[] = { - { DHC_NBI, "nbi" }, - { DHC_PREINIT, "preinit" }, - { DHC_BOUND4, "bound" }, - { DHC_BOUND6, "bound6" }, - { DHC_IPV4LL, "ipv4ll" }, - { DHC_RENEW4, "renew" }, - { DHC_RENEW6, "renew6" }, - { DHC_REBOOT, "reboot" }, - { DHC_REBIND4, "rebind" }, - { DHC_REBIND6, "rebind6" }, - { DHC_STOP, "stop" }, - { DHC_MEDIUM, "medium" }, - { DHC_TIMEOUT, "timeout" }, - { DHC_FAIL, "fail" }, - { DHC_EXPIRE, "expire" }, - { DHC_RELEASE, "release" }, - { DHC_START, "start" }, - { DHC_ABEND, "abend" }, - { DHC_END, "end" }, - { DHC_DEPREF6, "depref6" }, -}; - -static inline const char * -state_to_string (guint32 state) -{ - int i; - - for (i = 0; i < STATE_TABLE_SIZE; i++) { - if (state == state_table[i].state) - return state_table[i].name; - } - - return NULL; -} - -static inline NMDHCPState -string_to_state (const char *name) -{ - int i; - - for (i = 0; i < STATE_TABLE_SIZE; i++) { - if (!strcasecmp (name, state_table[i].name)) - return state_table[i].state; - } - - return 255; -} - static char * garray_to_string (GArray *array, const char *key) { @@ -220,81 +92,6 @@ garray_to_string (GArray *array, const char *key) return converted; } -static char * -get_option (GHashTable *hash, const char *key) -{ - GValue *value; - - value = g_hash_table_lookup (hash, key); - if (value == NULL) - return NULL; - - if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { - nm_warning ("Unexpected key %s value type was not " - "DBUS_TYPE_G_UCHAR_ARRAY", - (char *) key); - return NULL; - } - - return garray_to_string ((GArray *) g_value_get_boxed (value), key); -} - -static void -copy_option (gpointer key, - gpointer value, - gpointer user_data) -{ - NMDHCPClient *client = user_data; - const char *str_key = (const char *) key; - char *str_value = NULL; - - if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { - nm_warning ("Unexpected key %s value type was not " - "DBUS_TYPE_G_UCHAR_ARRAY", - str_key); - return; - } - - str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key); - if (str_value) - g_hash_table_insert (client->options, g_strdup (str_key), str_value); -} - -static void -handle_options (NMDHCPManager *manager, - NMDHCPClient *client, - GHashTable *options, - const char *reason) -{ - guint32 old_state = client->state; - guint32 new_state = string_to_state (reason); - - /* Clear old and save new DHCP options */ - g_hash_table_remove_all (client->options); - g_hash_table_foreach (options, copy_option, client); - - if (old_state == new_state) - return; - - /* Handle changed device state */ - if (state_is_bound (new_state)) { - /* Cancel the timeout if the DHCP client is now bound */ - nm_dhcp_client_timeout_cleanup (client); - } - - client->state = new_state; - nm_info ("DHCP: device %s state changed %s -> %s", - client->iface, - state_to_string (old_state), - state_to_string (client->state)); - - g_signal_emit (G_OBJECT (client->manager), - signals[STATE_CHANGED], - 0, - client->id, - client->state); -} - static NMDHCPClient * get_client_for_pid (NMDHCPManager *manager, GPid pid) { @@ -309,9 +106,9 @@ get_client_for_pid (NMDHCPManager *manager, GPid pid) g_hash_table_iter_init (&iter, priv->clients); while (g_hash_table_iter_next (&iter, NULL, &value)) { - NMDHCPClient *candidate = value; + NMDHCPClient *candidate = NM_DHCP_CLIENT (value); - if (candidate->pid == pid) + if (nm_dhcp_client_get_pid (candidate) == pid) return candidate; } @@ -319,7 +116,8 @@ get_client_for_pid (NMDHCPManager *manager, GPid pid) } static NMDHCPClient * -get_client_for_iface (NMDHCPManager *manager, const char *iface) +get_client_for_iface (NMDHCPManager *manager, + const char *iface) { NMDHCPManagerPrivate *priv; GHashTableIter iter; @@ -333,24 +131,32 @@ get_client_for_iface (NMDHCPManager *manager, const char *iface) g_hash_table_iter_init (&iter, priv->clients); while (g_hash_table_iter_next (&iter, NULL, &value)) { - NMDHCPClient *candidate = value; + NMDHCPClient *candidate = NM_DHCP_CLIENT (value); - if (!strcmp (iface, candidate->iface)) + if (!strcmp (iface, nm_dhcp_client_get_iface (candidate))) return candidate; } return NULL; } -static NMDHCPClient * -get_client_for_id (NMDHCPManager *manager, guint32 id) +static char * +get_option (GHashTable *hash, const char *key) { - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); - g_return_val_if_fail (id > 0, NULL); + GValue *value; - return g_hash_table_lookup (NM_DHCP_MANAGER_GET_PRIVATE (manager)->clients, - GUINT_TO_POINTER (id)); + value = g_hash_table_lookup (hash, key); + if (value == NULL) + return NULL; + + if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) { + g_warning ("Unexpected key %s value type was not " + "DBUS_TYPE_G_UCHAR_ARRAY", + (char *) key); + return NULL; + } + + return garray_to_string ((GArray *) g_value_get_boxed (value), key); } static void @@ -393,9 +199,9 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy, goto out; } - if (strcmp (iface, client->iface)) { + if (strcmp (iface, nm_dhcp_client_get_iface (client))) { nm_warning ("Received DHCP event from unexpected interface '%s' (expected '%s')", - iface, client->iface); + iface, nm_dhcp_client_get_iface (client)); goto out; } @@ -405,7 +211,7 @@ nm_dhcp_manager_handle_event (DBusGProxy *proxy, goto out; } - handle_options (manager, client, options, reason); + nm_dhcp_client_new_options (client, options, reason); out: g_free (iface); @@ -425,7 +231,7 @@ nm_dhcp_manager_new (void) priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, - (GDestroyNotify) nm_dhcp_client_destroy); + (GDestroyNotify) g_object_unref); if (!priv->clients) { nm_warning ("Error: not enough memory to initialize DHCP manager tables"); g_object_unref (manager); @@ -457,599 +263,120 @@ nm_dhcp_manager_new (void) return manager; } +#define STATE_ID_TAG "state-id" +#define TIMEOUT_ID_TAG "timeout-id" -/* - * nm_dhcp_manager_handle_timeout - * - * Called after timeout of a DHCP transaction to notify device of the failure. - * - */ -static gboolean -nm_dhcp_manager_handle_timeout (gpointer user_data) +static void +remove_client (NMDHCPManager *self, NMDHCPClient *client) { - NMDHCPClient *client = user_data; + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + guint id; - nm_info ("(%s): DHCP transaction took too long, stopping it.", client->iface); + id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), STATE_ID_TAG)); + if (id) + g_signal_handler_disconnect (client, id); - nm_dhcp_manager_cancel_transaction (client->manager, client->id); + id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG)); + if (id) + g_signal_handler_disconnect (client, id); - g_signal_emit (G_OBJECT (client->manager), signals[TIMEOUT], 0, client->id); + /* Stopping the client is left up to the controlling device + * explicitly since we may want to quit NetworkManager but not terminate + * the DHCP client. + */ - return FALSE; + g_hash_table_remove (priv->clients, client); } -static NMDHCPClient * -nm_dhcp_client_new (NMDHCPManager *manager, const char *iface) +static void +client_state_changed (NMDHCPClient *client, NMDHCPState new_state, gpointer user_data) { - NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - NMDHCPClient *client; - - client = g_slice_new0 (NMDHCPClient); - if (!client) { - nm_warning ("%s: Out of memory creating DHCP transaction object.", iface); - return NULL; - } - - client->id = priv->next_id++; - client->iface = g_strdup (iface); - if (!client) { - nm_warning ("%s: Out of memory creating DHCP transaction object " - "property 'iface'.", - iface); - goto error; - } - - client->manager = manager; - - /* Do this after the transaction cancel since that clears options out */ - client->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - if (!client->options) { - nm_warning ("%s: Out of memory creating DHCP transaction object " - "property 'options'.", - iface); - goto error; - } - - g_hash_table_insert (priv->clients, GUINT_TO_POINTER (client->id), client); - return client; - -error: - nm_dhcp_client_destroy (client); - return NULL; + if (new_state == DHC_ABEND || new_state == DHC_END) + remove_client (NM_DHCP_MANAGER (user_data), client); } - -/* - * dhcp_watch_cb - * - * Watch our child dhclient process and get notified of events from it. - * - */ -static void dhcp_watch_cb (GPid pid, gint status, gpointer user_data) +static void +client_timeout (NMDHCPClient *client, gpointer user_data) { - NMDHCPClient *client = user_data; - - if (!WIFEXITED (status)) { - client->state = DHC_ABEND; - nm_warning ("dhcp client died abnormally"); - } - client->pid = 0; - - nm_dhcp_client_watch_cleanup (client); - nm_dhcp_client_timeout_cleanup (client); - - g_signal_emit (G_OBJECT (client->manager), signals[STATE_CHANGED], 0, client->id, client->state); + remove_client (NM_DHCP_MANAGER (user_data), client); } -guint32 -nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, - const char *iface, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint32 timeout, - guint8 *dhcp_anycast_addr) +static void +add_client (NMDHCPManager *self, NMDHCPClient *client) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + guint id; + + id = g_signal_connect (client, "state-changed", G_CALLBACK (client_state_changed), self); + g_object_set_data (G_OBJECT (client), STATE_ID_TAG, GUINT_TO_POINTER (id)); + + id = g_signal_connect (client, "timeout", G_CALLBACK (client_timeout), self); + g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id)); + + g_hash_table_insert (priv->clients, client, g_object_ref (client)); +} + +/* Caller owns a reference to the NMDHCPClient on return */ +NMDHCPClient * +nm_dhcp_manager_start_client (NMDHCPManager *self, + const char *iface, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout, + guint8 *dhcp_anycast_addr) { NMDHCPManagerPrivate *priv; NMDHCPClient *client; - NMSettingIP4Config *setting; + NMSettingIP4Config *setting = NULL; + gboolean success = FALSE; - g_return_val_if_fail (manager, 0); - g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), 0); - g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface != NULL, NULL); - priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - client = get_client_for_iface (manager, iface); - if (!client) - client = nm_dhcp_client_new (manager, iface); - - if (client->pid && (client->state < DHC_ABEND)) { - /* Cancel any DHCP transaction already in progress */ - nm_dhcp_manager_cancel_transaction_real (client); + /* Kill any old client instance */ + client = get_client_for_iface (self, iface); + if (client) { + nm_dhcp_client_stop (client); + remove_client (self, client); } - if (s_ip4 && - nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) && - nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL && - priv->hostname_provider != NULL) { - /* We're asked to send the hostname to DHCP server, - the hostname isn't specified, - and a hostname provider is registered: use that */ + /* And make a new one */ + client = g_object_new (NM_TYPE_DHCP_DHCLIENT, + NM_DHCP_CLIENT_INTERFACE, iface, + NULL); + g_return_val_if_fail (client != NULL, NULL); + add_client (self, client); + if ( s_ip4 + && nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) + && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL) + && priv->hostname_provider != NULL) { + + /* We're asked to send the hostname to DHCP server, the hostname + * isn't specified, and a hostname provider is registered: use that + */ setting = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4))); g_object_set (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, nm_hostname_provider_get_hostname (priv->hostname_provider), NULL); - } else - setting = s_ip4 ? g_object_ref (s_ip4) : NULL; + } - if (timeout == 0) - timeout = NM_DHCP_TIMEOUT; - - nm_info ("Activation (%s) Beginning DHCP transaction (timeout in %d seconds)", - iface, timeout); - client->pid = nm_dhcp4_client_start (client, uuid, setting, dhcp_anycast_addr); + success = nm_dhcp_client_start (client, uuid, setting, timeout, dhcp_anycast_addr); if (setting) g_object_unref (setting); - if (client->pid == 0) - return FALSE; - - /* Set up a timeout on the transaction to kill it after the timeout */ - client->timeout_id = g_timeout_add_seconds (timeout, - nm_dhcp_manager_handle_timeout, - client); - client->watch_id = g_child_watch_add (client->pid, - (GChildWatchFunc) dhcp_watch_cb, - client); - return client->id; -} - -void -nm_dhcp_client_stop (NMDHCPClient *client, GPid pid) -{ - int i = 15; /* 3 seconds */ - - g_return_if_fail (client != NULL); - g_return_if_fail (pid > 0); - - /* Clean up the watch handler since we're explicitly killing the daemon */ - nm_dhcp_client_watch_cleanup (client); - - /* Tell it to quit; maybe it wants to send out a RELEASE message */ - kill (pid, SIGTERM); - - while (i-- > 0) { - gint child_status; - int ret; - - ret = waitpid (pid, &child_status, WNOHANG); - if (ret > 0) - break; - - if (ret == -1) { - /* Child already exited */ - if (errno == ECHILD) - break; - /* Took too long; shoot it in the head */ - i = 0; - break; - } - g_usleep (G_USEC_PER_SEC / 5); + if (!success) { + remove_client (self, client); + g_object_unref (client); + client = NULL; } - if (i <= 0) { - nm_warning ("%s: dhcp client pid %d didn't exit, will kill it.", client->iface, pid); - kill (pid, SIGKILL); - - nm_debug ("waiting for dhcp client pid %d to exit", pid); - waitpid (pid, NULL, 0); - nm_debug ("dhcp client pid %d cleaned up", pid); - } -} - -static void -nm_dhcp_manager_cancel_transaction_real (NMDHCPClient *client) -{ - g_return_if_fail (client != NULL); - g_return_if_fail (client->pid > 0); - - nm_dhcp_client_stop (client, client->pid); - - nm_info ("(%s): canceled DHCP transaction, dhcp client pid %d", - client->iface, - client->pid); - - client->pid = 0; - client->state = DHC_END; - - /* Clean up the pidfile if it got left around */ - if (client->pid_file) { - remove (client->pid_file); - g_free (client->pid_file); - client->pid_file = NULL; - } - - /* Free leasefile (but don't delete) */ - if (client->lease_file) { - g_free (client->lease_file); - client->lease_file = NULL; - } - - /* Clean up config file if it got left around */ - if (client->conf_file) { - remove (client->conf_file); - g_free (client->conf_file); - client->conf_file = NULL; - } - - nm_dhcp_client_timeout_cleanup (client); - g_hash_table_remove_all (client->options); -} - - -/* - * nm_dhcp_manager_cancel_transaction - * - * Stop any in-progress DHCP transaction on a particular device. - * - */ -void -nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, guint32 id) -{ - NMDHCPClient *client; - NMDHCPManagerPrivate *priv; - - g_return_if_fail (manager != NULL); - g_return_if_fail (NM_IS_DHCP_MANAGER (manager)); - g_return_if_fail (id > 0); - - priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - - client = get_client_for_id (manager, id); - if (client && client->pid) - nm_dhcp_manager_cancel_transaction_real (client); -} - -static void -process_classful_routes (GHashTable *options, NMIP4Config *ip4_config) -{ - const char *str; - char **searches, **s; - - str = g_hash_table_lookup (options, "new_static_routes"); - if (!str) - return; - - searches = g_strsplit (str, " ", 0); - if ((g_strv_length (searches) % 2)) { - nm_info (" static routes provided, but invalid"); - goto out; - } - - for (s = searches; *s; s += 2) { - NMIP4Route *route; - struct in_addr rt_addr; - struct in_addr rt_route; - - if (inet_pton (AF_INET, *s, &rt_addr) <= 0) { - nm_warning ("DHCP provided invalid static route address: '%s'", *s); - continue; - } - if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) { - nm_warning ("DHCP provided invalid static route gateway: '%s'", *(s + 1)); - continue; - } - - // FIXME: ensure the IP addresse and route are sane - - route = nm_ip4_route_new (); - nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr); - nm_ip4_route_set_prefix (route, 32); /* 255.255.255.255 */ - nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr); - - nm_ip4_config_take_route (ip4_config, route); - nm_info (" static route %s gw %s", *s, *(s + 1)); - } - -out: - g_strfreev (searches); -} - -static void -process_domain_search (NMIP4Config *ip4_config, const char *str) -{ - char **searches, **s; - char *unescaped, *p; - int i; - - g_return_if_fail (str != NULL); - g_return_if_fail (ip4_config != NULL); - - p = unescaped = g_strdup (str); - do { - p = strstr (p, "\\032"); - if (!p) - break; - - /* Clear the escaped space with real spaces */ - for (i = 0; i < 4; i++) - *p++ = ' '; - } while (*p++); - - if (strchr (unescaped, '\\')) { - nm_info (" invalid domain search: '%s'", unescaped); - goto out; - } - - searches = g_strsplit (unescaped, " ", 0); - for (s = searches; *s; s++) { - if (strlen (*s)) { - nm_info (" domain search '%s'", *s); - nm_ip4_config_add_search (ip4_config, *s); - } - } - g_strfreev (searches); - -out: - g_free (unescaped); -} - -/* Given a table of DHCP options from the client, convert into an IP4Config */ -NMIP4Config * -nm_dhcp4_manager_options_to_config (const char *iface, GHashTable *options) -{ - NMIP4Config *ip4_config = NULL; - struct in_addr tmp_addr; - NMIP4Address *addr = NULL; - char *str = NULL; - guint32 gwaddr = 0; - gboolean have_classless = FALSE; - - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (options != NULL, NULL); - - ip4_config = nm_ip4_config_new (); - if (!ip4_config) { - nm_warning ("%s: couldn't allocate memory for an IP4Config!", iface); - return NULL; - } - - addr = nm_ip4_address_new (); - if (!addr) { - nm_warning ("%s: couldn't allocate memory for an IP4 Address!", iface); - goto error; - } - - str = g_hash_table_lookup (options, "new_ip_address"); - if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { - nm_ip4_address_set_address (addr, tmp_addr.s_addr); - nm_info (" address %s", str); - } else - goto error; - - str = g_hash_table_lookup (options, "new_subnet_mask"); - if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { - nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr)); - nm_info (" prefix %d (%s)", nm_ip4_address_get_prefix (addr), str); - } - - /* Routes: if the server returns classless static routes, we MUST ignore - * the 'static_routes' option. - */ - have_classless = nm_dhcp4_client_process_classless_routes (options, ip4_config, &gwaddr); - if (!have_classless) { - gwaddr = 0; /* Ensure client code doesn't lie */ - process_classful_routes (options, ip4_config); - } - - if (gwaddr) { - char buf[INET_ADDRSTRLEN + 1]; - - inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf)); - nm_info (" gateway %s", buf); - nm_ip4_address_set_gateway (addr, gwaddr); - } else { - /* If the gateway wasn't provided as a classless static route with a - * subnet length of 0, try to find it using the old-style 'routers' option. - */ - str = g_hash_table_lookup (options, "new_routers"); - if (str) { - char **routers = g_strsplit (str, " ", 0); - char **s; - - for (s = routers; *s; s++) { - /* FIXME: how to handle multiple routers? */ - if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { - nm_ip4_address_set_gateway (addr, tmp_addr.s_addr); - nm_info (" gateway %s", *s); - break; - } else - nm_warning ("Ignoring invalid gateway '%s'", *s); - } - g_strfreev (routers); - } - } - - nm_ip4_config_take_address (ip4_config, addr); - addr = NULL; - - str = g_hash_table_lookup (options, "new_host_name"); - if (str) - nm_info (" hostname '%s'", str); - - str = g_hash_table_lookup (options, "new_domain_name_servers"); - if (str) { - char **searches = g_strsplit (str, " ", 0); - char **s; - - for (s = searches; *s; s++) { - if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { - nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr); - nm_info (" nameserver '%s'", *s); - } else - nm_warning ("Ignoring invalid nameserver '%s'", *s); - } - g_strfreev (searches); - } - - str = g_hash_table_lookup (options, "new_domain_name"); - if (str) { - char **domains = g_strsplit (str, " ", 0); - char **s; - - for (s = domains; *s; s++) { - nm_info (" domain name '%s'", *s); - nm_ip4_config_add_domain (ip4_config, *s); - } - g_strfreev (domains); - } - - str = g_hash_table_lookup (options, "new_domain_search"); - if (str) - process_domain_search (ip4_config, str); - - str = g_hash_table_lookup (options, "new_netbios_name_servers"); - if (str) { - char **searches = g_strsplit (str, " ", 0); - char **s; - - for (s = searches; *s; s++) { - if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { - nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr); - nm_info (" wins '%s'", *s); - } else - nm_warning ("Ignoring invalid WINS server '%s'", *s); - } - g_strfreev (searches); - } - - str = g_hash_table_lookup (options, "new_interface_mtu"); - if (str) { - int int_mtu; - - errno = 0; - int_mtu = strtol (str, NULL, 10); - if ((errno == EINVAL) || (errno == ERANGE)) - goto error; - - if (int_mtu > 576) - nm_ip4_config_set_mtu (ip4_config, int_mtu); - } - - return ip4_config; - -error: - if (addr) - nm_ip4_address_unref (addr); - g_object_unref (ip4_config); - return NULL; -} - -/* - * nm_dhcp_manager_get_ip4_config - * - * Get IP4 configuration values from the DHCP daemon - * - */ -NMIP4Config * -nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, guint32 id) -{ - NMDHCPManagerPrivate *priv; - NMDHCPClient *client; - - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); - g_return_val_if_fail (id > 0, NULL); - - priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - - client = get_client_for_id (manager, id); - if (!client) { - nm_warning ("Device DHCP transaction %d not started.", id); - return NULL; - } - - if (!state_is_bound (client->state)) { - nm_warning ("%s: dhcp client didn't bind to a lease.", client->iface); - return NULL; - } - - return nm_dhcp4_manager_options_to_config (client->iface, client->options); -} - -#define NEW_TAG "new_" -#define OLD_TAG "old_" - -typedef struct { - GHFunc func; - gpointer user_data; -} Dhcp4ForeachInfo; - -static void -iterate_dhcp4_config_option (gpointer key, - gpointer value, - gpointer user_data) -{ - Dhcp4ForeachInfo *info = (Dhcp4ForeachInfo *) user_data; - char *tmp_key = NULL; - const char **p; - static const char *filter_options[] = { - "interface", "pid", "reason", "dhcp_message_type", NULL - }; - - /* Filter out stuff that's not actually new DHCP options */ - for (p = filter_options; *p; p++) { - if (!strcmp (*p, (const char *) key)) - return; - if (!strncmp ((const char *) key, OLD_TAG, strlen (OLD_TAG))) - return; - } - - /* Remove the "new_" prefix that dhclient passes back */ - if (!strncmp ((const char *) key, NEW_TAG, strlen (NEW_TAG))) - tmp_key = g_strdup ((const char *) (key + strlen (NEW_TAG))); - else - tmp_key = g_strdup ((const char *) key); - - (*info->func) ((gpointer) tmp_key, value, info->user_data); - g_free (tmp_key); -} - -gboolean -nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self, - guint32 id, - GHFunc func, - gpointer user_data) -{ - NMDHCPManagerPrivate *priv; - NMDHCPClient *client; - Dhcp4ForeachInfo info = { NULL, NULL }; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), FALSE); - g_return_val_if_fail (id > 0, FALSE); - g_return_val_if_fail (func != NULL, FALSE); - - priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - - client = get_client_for_id (self, id); - if (!client) { - nm_warning ("Device DHCP transaction %d not started.", id); - return FALSE; - } - - if (!state_is_bound (client->state)) { - nm_warning ("%s: dhclient didn't bind to a lease.", client->iface); - return FALSE; - } - - info.func = func; - info.user_data = user_data; - g_hash_table_foreach (client->options, iterate_dhcp4_config_option, &info); - return TRUE; + return client; } static void @@ -1080,16 +407,35 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager, } GSList * -nm_dhcp4_manager_get_lease_config (NMDHCPManager *self, - const char *iface, - const char *uuid) +nm_dhcp_manager_get_lease_config (NMDHCPManager *self, + const char *iface, + const char *uuid) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (uuid != NULL, NULL); - return nm_dhcp4_client_get_lease_config (iface, uuid); + return nm_dhcp_backend_get_lease_config (iface, uuid); +} + +NMIP4Config * +nm_dhcp_manager_test_ip4_options_to_config (const char *iface, + GHashTable *options, + const char *reason) +{ + NMDHCPClient *client; + NMIP4Config *config; + + client = (NMDHCPClient *) g_object_new (NM_TYPE_DHCP_DHCLIENT, + NM_DHCP_CLIENT_INTERFACE, iface, + NULL); + g_return_val_if_fail (client != NULL, NULL); + nm_dhcp_client_new_options (client, options, reason); + config = nm_dhcp_client_get_ip4_config (client, TRUE); + g_object_unref (client); + + return config; } /***************************************************/ @@ -1116,6 +462,20 @@ nm_dhcp_manager_init (NMDHCPManager *manager) priv->next_id = 1; } +static void +dispose (GObject *object) +{ + NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object); + GList *values, *iter; + + values = g_hash_table_get_values (priv->clients); + for (iter = values; iter; iter = g_list_next (iter)) + remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data)); + g_list_free (values); + + G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object); +} + static void finalize (GObject *object) { @@ -1142,24 +502,6 @@ nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class) /* virtual methods */ object_class->finalize = finalize; - - /* signals */ - signals[STATE_CHANGED] = - g_signal_new ("state-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPManagerClass, state_changed), - NULL, NULL, - _nm_marshal_VOID__UINT_UINT, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); - - signals[TIMEOUT] = - g_signal_new ("timeout", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDHCPManagerClass, timeout), - NULL, NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); + object_class->dispose = dispose; } diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 3b8a6a410e..7c82ca59ac 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -27,12 +27,11 @@ #include +#include "nm-dhcp-client.h" #include "nm-ip4-config.h" #include "nm-dhcp4-config.h" #include "nm-hostname-provider.h" -#define NM_DHCP_MANAGER_RUN_DIR LOCALSTATEDIR "/run" - #define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type ()) #define NM_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManager)) #define NM_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) @@ -40,98 +39,35 @@ #define NM_IS_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_MANAGER)) #define NM_DHCP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) -typedef enum { - DHC_NBI = 0, /* no broadcast interfaces found */ - DHC_PREINIT, /* configuration started */ - DHC_BOUND4, /* IPv4 lease obtained */ - DHC_BOUND6, /* IPv6 lease obtained */ - DHC_IPV4LL, /* IPv4LL address obtained */ - DHC_RENEW4, /* IPv4 lease renewed */ - DHC_RENEW6, /* IPv6 lease renewed */ - DHC_REBOOT, /* have valid lease, but now obtained a different one */ - DHC_REBIND4, /* IPv4 new/different lease */ - DHC_REBIND6, /* IPv6 new/different lease */ - DHC_DEPREF6, /* IPv6 lease depreferred */ - DHC_STOP, /* remove old lease */ - DHC_MEDIUM, /* media selection begun */ - DHC_TIMEOUT, /* timed out contacting DHCP server */ - DHC_FAIL, /* all attempts to contact server timed out, sleeping */ - DHC_EXPIRE, /* lease has expired, renewing */ - DHC_RELEASE, /* releasing lease */ - DHC_START, /* sent when dhclient started OK */ - DHC_ABEND, /* dhclient exited abnormally */ - DHC_END, /* dhclient exited normally */ - DHC_END_OPTIONS, /* last option in subscription sent */ -} NMDHCPState; - typedef struct { GObject parent; } NMDHCPManager; typedef struct { GObjectClass parent; - - /* Signals */ - void (*state_changed) (NMDHCPManager *manager, guint32 id, NMDHCPState state); - void (*timeout) (NMDHCPManager *manager, guint32 id); } NMDHCPManagerClass; -typedef struct { - GPid id; - char * iface; - guchar state; - GPid pid; - char * pid_file; - char * conf_file; - char * lease_file; - guint timeout_id; - guint watch_id; - NMDHCPManager * manager; - GHashTable * options; -} NMDHCPClient; - GType nm_dhcp_manager_get_type (void); NMDHCPManager *nm_dhcp_manager_get (void); + void nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager, NMHostnameProvider *provider); -guint32 nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, - const char *iface, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint32 timeout, - guint8 *dhcp_anycast_addr); -void nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, - guint32 id); -NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, guint32 id); -NMDHCPState nm_dhcp_manager_get_client_state (NMDHCPManager *manager, guint32 id); +NMDHCPClient * nm_dhcp_manager_start_client (NMDHCPManager *manager, + const char *iface, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout, + guint8 *dhcp_anycast_addr); -gboolean nm_dhcp_manager_foreach_dhcp4_option (NMDHCPManager *self, - guint32 id, - GHFunc func, - gpointer user_data); +GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self, + const char *iface, + const char *uuid); -GSList * nm_dhcp4_manager_get_lease_config (NMDHCPManager *self, - const char *iface, - const char *uuid); - -/* The following are implemented by the DHCP client backends */ -GPid nm_dhcp4_client_start (NMDHCPClient *client, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *anycast_addr); -void nm_dhcp_client_stop (NMDHCPClient *client, pid_t pid); - -gboolean nm_dhcp4_client_process_classless_routes (GHashTable *options, - NMIP4Config *ip4_config, - guint32 *gwaddr); - -GSList * nm_dhcp4_client_get_lease_config (const char *iface, - const char *uuid); - -/* Test functions */ -NMIP4Config *nm_dhcp4_manager_options_to_config (const char *iface, - GHashTable *options); +/* For testing only */ +NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *iface, + GHashTable *options, + const char *reason); #endif /* NM_DHCP_MANAGER_H */ diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 72073e4150..f20afcc45c 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -1608,9 +1608,9 @@ ip4_match_config (NMDevice *self, NMConnection *connection) /* Get any saved leases that apply to this connection */ dhcp_mgr = nm_dhcp_manager_get (); - leases = nm_dhcp4_manager_get_lease_config (dhcp_mgr, - nm_device_get_iface (self), - nm_setting_connection_get_uuid (s_con)); + leases = nm_dhcp_manager_get_lease_config (dhcp_mgr, + nm_device_get_iface (self), + nm_setting_connection_get_uuid (s_con)); g_object_unref (dhcp_mgr); method = nm_setting_ip4_config_get_method (s_ip4); diff --git a/src/nm-device.c b/src/nm-device.c index 4be3fc23e3..586c93949c 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -105,7 +105,7 @@ typedef struct { /* IP4 configuration info */ NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ NMDHCPManager * dhcp_manager; - guint32 dhcp4_id; + NMDHCPClient * dhcp4_client; guint32 dhcp_timeout; gulong dhcp_state_sigid; gulong dhcp_timeout_sigid; @@ -1058,6 +1058,120 @@ aipd_exec (NMDevice *self, GError **error) return TRUE; } +static void +dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) +{ + nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data), + (const char *) key, + (const char *) value); +} + +static void +handle_dhcp_lease_change (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMIP4Config *config; + NMSettingIP4Config *s_ip4; + NMConnection *connection; + NMActRequest *req; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + gboolean assumed; + + if (!nm_device_get_use_dhcp (device)) { + nm_warning ("got DHCP rebind for device that wasn't using DHCP."); + return; + } + + config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); + if (!config) { + nm_warning ("failed to get DHCP config for rebind"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + return; + } + + req = nm_device_get_act_request (device); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + nm_utils_merge_ip4_config (config, s_ip4); + + g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config); + + assumed = nm_act_request_get_assumed (req); + if (nm_device_set_ip4_config (device, config, assumed, &reason)) { + nm_dhcp4_config_reset (priv->dhcp4_config); + nm_dhcp_client_foreach_dhcp4_option (priv->dhcp4_client, + dhcp4_add_option_cb, + priv->dhcp4_config); + } else { + nm_warning ("Failed to update IP4 config in response to DHCP event."); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + } +} + +static void +dhcp_state_changed (NMDHCPClient *client, + NMDHCPState state, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDeviceState dev_state; + + if (!nm_device_get_act_request (device)) + return; + + dev_state = nm_device_get_state (device); + + switch (state) { + case DHC_BOUND4: /* lease obtained */ + case DHC_RENEW4: /* lease renewed */ + case DHC_REBOOT: /* have valid lease, but now obtained a different one */ + case DHC_REBIND4: /* new, different lease */ + if (dev_state == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip4_config_get (device); + else if (dev_state == NM_DEVICE_STATE_ACTIVATED) + handle_dhcp_lease_change (device); + break; + case DHC_TIMEOUT: /* timed out contacting DHCP server */ + nm_dhcp4_config_reset (priv->dhcp4_config); + + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip4_config_timeout (device); + break; + case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ + case DHC_ABEND: /* dhclient exited abnormally */ + case DHC_END: /* dhclient exited normally */ + nm_dhcp4_config_reset (priv->dhcp4_config); + + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); + } else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { + if (nm_device_get_use_dhcp (device)) { + /* dhclient quit and therefore can't renew our lease, kill the conneciton */ + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } + } + break; + default: + break; + } +} + +static void +dhcp_timeout (NMDHCPClient *client, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + if (!nm_device_get_act_request (device)) + return; + + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip4_config_timeout (device); +} + static NMActStageReturn real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) { @@ -1101,16 +1215,22 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) /* DHCP manager will cancel any transaction already in progress and we do not want to cancel this activation if we get "down" state from that. */ - g_signal_handler_block (priv->dhcp_manager, priv->dhcp_state_sigid); - priv->dhcp4_id = nm_dhcp_manager_begin_transaction (priv->dhcp_manager, - ip_iface, - uuid, - s_ip4, - priv->dhcp_timeout, - anycast); - g_signal_handler_unblock (priv->dhcp_manager, priv->dhcp_state_sigid); + priv->dhcp4_client = nm_dhcp_manager_start_client (priv->dhcp_manager, + ip_iface, + uuid, + s_ip4, + priv->dhcp_timeout, + anycast); + if (priv->dhcp4_client) { + priv->dhcp_state_sigid = g_signal_connect (priv->dhcp4_client, + "state-changed", + G_CALLBACK (dhcp_state_changed), + self); + priv->dhcp_timeout_sigid = g_signal_connect (priv->dhcp4_client, + "timeout", + G_CALLBACK (dhcp_timeout), + self); - if (priv->dhcp4_id) { /* DHCP devices will be notified by the DHCP manager when * stuff happens. */ @@ -1288,14 +1408,6 @@ nm_device_new_ip4_shared_config (NMDevice *self, NMDeviceStateReason *reason) return config; } -static void -dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) -{ - nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data), - (const char *) key, - (const char *) value); -} - static NMActStageReturn real_act_stage4_get_ip4_config (NMDevice *self, NMIP4Config **config, @@ -1320,16 +1432,15 @@ real_act_stage4_get_ip4_config (NMDevice *self, s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); if (nm_device_get_use_dhcp (self)) { - *config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, priv->dhcp4_id); + *config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); if (*config) { /* Merge user-defined overrides into the IP4Config to be applied */ nm_utils_merge_ip4_config (*config, s_ip4); nm_dhcp4_config_reset (priv->dhcp4_config); - nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager, - priv->dhcp4_id, - dhcp4_add_option_cb, - priv->dhcp4_config); + nm_dhcp_client_foreach_dhcp4_option (priv->dhcp4_client, + dhcp4_add_option_cb, + priv->dhcp4_config); /* Notify of new DHCP4 config */ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); @@ -2015,14 +2126,14 @@ nm_device_deactivate_quickly (NMDevice *self) /* Clear any delayed transitions */ delayed_transitions_clear (self); - /* Stop any ongoing DHCP transaction on this device */ if (nm_device_get_act_request (self)) { if (nm_device_get_use_dhcp (self)) { - nm_dhcp_manager_cancel_transaction (priv->dhcp_manager, priv->dhcp4_id); nm_device_set_use_dhcp (self, FALSE); + /* Notify of invalid DHCP4 config */ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); } else if (priv->dnsmasq_manager) { + /* Or any shared connection */ if (priv->dnsmasq_state_id) { g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); priv->dnsmasq_state_id = 0; @@ -2032,8 +2143,6 @@ nm_device_deactivate_quickly (NMDevice *self) g_object_unref (priv->dnsmasq_manager); priv->dnsmasq_manager = NULL; } - - priv->dhcp4_id = 0; } aipd_cleanup (self); @@ -2253,119 +2362,6 @@ nm_device_can_interrupt_activation (NMDevice *self) /* IP Configuration stuff */ -static void -handle_dhcp_lease_change (NMDevice *device) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMIP4Config *config; - NMSettingIP4Config *s_ip4; - NMConnection *connection; - NMActRequest *req; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - gboolean assumed; - - if (!nm_device_get_use_dhcp (device)) { - nm_warning ("got DHCP rebind for device that wasn't using DHCP."); - return; - } - - config = nm_dhcp_manager_get_ip4_config (priv->dhcp_manager, priv->dhcp4_id); - if (!config) { - nm_warning ("failed to get DHCP config for rebind"); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - return; - } - - req = nm_device_get_act_request (device); - g_assert (req); - connection = nm_act_request_get_connection (req); - g_assert (connection); - - s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); - nm_utils_merge_ip4_config (config, s_ip4); - - g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config); - - assumed = nm_act_request_get_assumed (req); - if (nm_device_set_ip4_config (device, config, assumed, &reason)) { - nm_dhcp4_config_reset (priv->dhcp4_config); - nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager, - priv->dhcp4_id, - dhcp4_add_option_cb, - priv->dhcp4_config); - } else { - nm_warning ("Failed to update IP4 config in response to DHCP event."); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); - } -} - -static void -dhcp_state_changed (NMDHCPManager *dhcp_manager, - guint32 id, - NMDHCPState state, - gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMDeviceState dev_state; - - if ( !nm_device_get_act_request (device) - || (priv->dhcp4_id != id)) - return; - - dev_state = nm_device_get_state (device); - - switch (state) { - case DHC_BOUND4: /* lease obtained */ - case DHC_RENEW4: /* lease renewed */ - case DHC_REBOOT: /* have valid lease, but now obtained a different one */ - case DHC_REBIND4: /* new, different lease */ - if (dev_state == NM_DEVICE_STATE_IP_CONFIG) - nm_device_activate_schedule_stage4_ip4_config_get (device); - else if (dev_state == NM_DEVICE_STATE_ACTIVATED) - handle_dhcp_lease_change (device); - break; - case DHC_TIMEOUT: /* timed out contacting DHCP server */ - nm_dhcp4_config_reset (priv->dhcp4_config); - - if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_activate_schedule_stage4_ip4_config_timeout (device); - break; - case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ - case DHC_ABEND: /* dhclient exited abnormally */ - case DHC_END: /* dhclient exited normally */ - nm_dhcp4_config_reset (priv->dhcp4_config); - - if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); - } else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { - if (nm_device_get_use_dhcp (device)) { - /* dhclient quit and therefore can't renew our lease, kill the conneciton */ - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - } - } - break; - default: - break; - } -} - -static void -dhcp_timeout (NMDHCPManager *dhcp_manager, - guint32 id, - gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - - if ( !nm_device_get_act_request (device) - || (priv->dhcp4_id != id)) - return; - - if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_activate_schedule_stage4_ip4_config_timeout (device); -} - gboolean nm_device_get_use_dhcp (NMDevice *self) { @@ -2390,17 +2386,8 @@ nm_device_set_use_dhcp (NMDevice *self, g_object_unref (priv->dhcp4_config); priv->dhcp4_config = nm_dhcp4_config_new (); - if (!priv->dhcp_manager) { + if (!priv->dhcp_manager) priv->dhcp_manager = nm_dhcp_manager_get (); - priv->dhcp_state_sigid = g_signal_connect (priv->dhcp_manager, - "state-changed", - G_CALLBACK (dhcp_state_changed), - self); - priv->dhcp_timeout_sigid = g_signal_connect (priv->dhcp_manager, - "timeout", - G_CALLBACK (dhcp_timeout), - self); - } } else { if (priv->dhcp4_config) { g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); @@ -2408,11 +2395,25 @@ nm_device_set_use_dhcp (NMDevice *self, priv->dhcp4_config = NULL; } + if (priv->dhcp4_client) { + /* Stop any ongoing DHCP transaction on this device */ + if (priv->dhcp_state_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_state_sigid); + priv->dhcp_state_sigid = 0; + } + + if (priv->dhcp_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_timeout_sigid); + priv->dhcp_timeout_sigid = 0; + } + + nm_dhcp_client_stop (priv->dhcp4_client); + + g_object_unref (priv->dhcp4_client); + priv->dhcp4_client = NULL; + } + if (priv->dhcp_manager) { - g_signal_handler_disconnect (priv->dhcp_manager, priv->dhcp_state_sigid); - priv->dhcp_state_sigid = 0; - g_signal_handler_disconnect (priv->dhcp_manager, priv->dhcp_timeout_sigid); - priv->dhcp_timeout_sigid = 0; g_object_unref (priv->dhcp_manager); priv->dhcp_manager = NULL; } @@ -2770,6 +2771,7 @@ dispose (GObject *object) nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); nm_device_set_ip4_config (self, NULL, FALSE, &ignored); + nm_device_set_use_dhcp (self, FALSE); } clear_act_request (self); @@ -2777,10 +2779,7 @@ dispose (GObject *object) activation_source_clear (self, TRUE, AF_INET); activation_source_clear (self, TRUE, AF_INET6); - if (!take_down) { - nm_device_set_use_dhcp (self, FALSE); - nm_device_cleanup_ip6 (self); - } + nm_device_cleanup_ip6 (self); if (priv->dnsmasq_manager) { if (priv->dnsmasq_state_id) { diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c index a99feea9db..6f5fb011f0 100644 --- a/src/tests/test-dhcp-options.c +++ b/src/tests/test-dhcp-options.c @@ -35,15 +35,43 @@ typedef struct { const char *value; } Option; +static void +destroy_gvalue (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GValue * +string_to_byte_array_gvalue (const char *str) +{ + GByteArray *array; + GValue *val; + + array = g_byte_array_sized_new (strlen (str)); + g_byte_array_append (array, (const guint8 *) str, strlen (str)); + + val = g_slice_new0 (GValue); + g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY); + g_value_take_boxed (val, array); + + return val; +} + static GHashTable * fill_table (Option *test_options, GHashTable *table) { Option *opt; if (!table) - table = g_hash_table_new (g_str_hash, g_str_equal); - for (opt = test_options; opt->name; opt++) - g_hash_table_insert (table, (gpointer) opt->name, (gpointer) opt->value); + table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue); + for (opt = test_options; opt->name; opt++) { + g_hash_table_insert (table, + (gpointer) opt->name, + string_to_byte_array_gvalue (opt->value)); + } return table; } @@ -88,7 +116,7 @@ test_generic_options (void) const char *expected_route2_gw = "10.1.1.1"; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-generic", "failed to parse DHCP4 options"); @@ -199,7 +227,7 @@ test_wins_options (void) options = fill_table (generic_options, NULL); options = fill_table (wins_options, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-wins", "failed to parse DHCP4 options"); @@ -245,7 +273,7 @@ test_classless_static_routes (void) options = fill_table (generic_options, NULL); options = fill_table (classless_routes_options, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442", "failed to parse DHCP4 options"); @@ -311,7 +339,7 @@ test_invalid_classless_routes1 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes1, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options"); @@ -362,7 +390,7 @@ test_invalid_classless_routes2 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes2, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options"); @@ -432,7 +460,7 @@ test_invalid_classless_routes3 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes3, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options"); @@ -483,7 +511,7 @@ test_gateway_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (gw_in_classless_routes, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-gateway", "failed to parse DHCP4 options"); @@ -537,7 +565,7 @@ test_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (escaped_searches_options, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-escaped-domain-searches", "failed to parse DHCP4 options"); @@ -568,7 +596,7 @@ test_invalid_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_escaped_searches_options, options); - ip4_config = nm_dhcp4_manager_options_to_config ("eth0", options); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options"); From 702836b42f28466f1d156a7585e19e659152f45b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 12 Jan 2010 22:55:24 -0800 Subject: [PATCH 06/20] dhcp: allow runtime DHCP client selection via config file --- configure.ac | 123 ++++++++++++++++------------ src/NetworkManager.c | 17 ++-- src/dhcp-manager/Makefile.am | 3 +- src/dhcp-manager/nm-dhcp-client.h | 4 - src/dhcp-manager/nm-dhcp-dhclient.c | 10 +-- src/dhcp-manager/nm-dhcp-dhclient.h | 2 + src/dhcp-manager/nm-dhcp-dhcpcd.c | 10 +-- src/dhcp-manager/nm-dhcp-dhcpcd.h | 2 + src/dhcp-manager/nm-dhcp-manager.c | 91 ++++++++++++-------- src/dhcp-manager/nm-dhcp-manager.h | 3 + 10 files changed, 156 insertions(+), 109 deletions(-) diff --git a/configure.ac b/configure.ac index 28734d6e77..5206bd07b5 100644 --- a/configure.ac +++ b/configure.ac @@ -286,67 +286,73 @@ else fi AC_SUBST(PPPD_PLUGIN_DIR) -# DHCP client -AC_ARG_WITH([dhcp-client], AS_HELP_STRING([--with-dhcp-client=dhcpcd|dhclient], [path to the chosen dhcp client])) +# dhclient support +AC_ARG_WITH([dhclient], AS_HELP_STRING([--with-dhclient=yes|no|path], [Enable dhclient 4.x support])) # If a full path is given, use that and do not test if it works or not. -case "${with_dhcp_client}" in +case "${with_dhclient}" in /*) - DHCP_CLIENT_PATH="${with_dhcp_client}" - AC_MSG_NOTICE(using the DHCP client ${DHCP_CLIENT_PATH}) + DHCLIENT_PATH="${with_dhclient}" + AC_MSG_NOTICE(using dhclient at ${DHCLIENT_PATH}) + ;; + no) AC_MSG_NOTICE(dhclient support disabled) + ;; + *) + AC_MSG_CHECKING(for dhclient) + # NM only works with ISC dhclient - other derivatives don't have + # the same userland. NM also requires dhclient 4.x since older + # versions do not have IPv6 support. + for path in /sbin /usr/sbin /usr/pkg/sbin /usr/local/sbin; do + test -x "${path}/dhclient" || continue + case `"$path/dhclient" --version 2>&1` in + "isc-dhclient-4"*) DHCLIENT_PATH="$path/dhclient"; break;; + esac + done + if test -n "${DHCLIENT_PATH}"; then + AC_MSG_RESULT($DHCLIENT_PATH) + else + AC_MSG_RESULT(no) + fi ;; esac -if test -z "$DHCP_CLIENT_PATH" -a \( -z "$with_dhcp_client" -o x`basename "$with_dhcp_client"` = "xdhclient" \); then - # We only work with ISC dhclient - the FreeBSD and OpenBSD derivatives don't have the same userland. - AC_MSG_CHECKING(for dhclient) - for client in "$with_dhcp_client" /sbin/dhclient /usr/pkg/sbin/dhclient /usr/local/sbin/dhclient; do - test -x "$client" || continue - case `"$client" --version 2>&1` in - "isc-dhclient-"*) DHCP_CLIENT_PATH="$client"; break;; - esac - done - if test -z "$DHCP_CLIENT_PATH"; then - AC_MSG_RESULT(no) - if test -n "$with_dhcp_client"; then - AC_MSG_ERROR([Could not find ISC dhclient]) + +# dhcpcd support +AC_ARG_WITH([dhcpcd], AS_HELP_STRING([--with-dhcpcd=yes|no|path], [Enable dhcpcd 4.x support])) +# If a full path is given, use that and do not test if it works or not. +case "${with_dhcpcd}" in + /*) + DHCPCD_PATH="${with_dhcpcd}" + AC_MSG_NOTICE(using dhcpcd at ${DHCPCD_PATH}) + ;; + no) AC_MSG_NOTICE(dhcpcd support disabled) + ;; + *) + AC_MSG_CHECKING(for dhcpcd) + # We fully work with upstream dhcpcd-4 + for path in /sbin /usr/sbin /usr/pkg/sbin /usr/local/sbin; do + test -x "${path}/dhclient" || continue + case `"$path/dhcpcd" --version 2>/dev/null` in + "dhcpcd "[123]*);; + "dhcpcd "*) DHCP_CLIENT_PATH="$path/dhcpcd"; break;; + esac + done + if test -n "${DHCPCD_PATH}"; then + AC_MSG_RESULT($DHCPCD_PATH) + else + AC_MSG_RESULT(no) fi - else - AC_MSG_RESULT($DHCP_CLIENT_PATH) - fi -fi -if test -z "$DHCP_CLIENT_PATH" -a \( -z "$with_dhcp_client" -o x`basename "$with_dhcp_client"` = "xdhcpcd" \); then - test -n "$DHCP_CLIENT_PATH" && echo bar - # We fully work with upstream dhcpcd-4 - AC_MSG_CHECKING([for dhcpcd]) - for client in "$with_dhcp_client" /sbin/dhcpcd /usr/pkg/sbin/dhcpcd /usr/local/sbin/dhcpcd; do - test -x "$client" || continue - case `"$client" --version 2>/dev/null` in - "dhcpcd "[123]*);; - "dhcpcd "*) DHCP_CLIENT_PATH="$client"; break;; - esac - done - if test -z "$DHCP_CLIENT_PATH"; then - AC_MSG_RESULT(no) - if test -n "$with_dhcp_client"; then - AC_MSG_ERROR([Could not find dhcpcd-4 or newer]) - fi - else - AC_MSG_RESULT($DHCP_CLIENT_PATH) - fi -fi -if test -z "$DHCP_CLIENT_PATH"; then + ;; +esac + +if test -z "$DHCPCD_PATH" -a -z "$DHCLIENT_PATH"; then # DHCP clients are not a build time dependency, only runtime. # dhclient has been the longtime default for NM and it's in /sbin # in most distros, so use it. AC_MSG_WARN([Could not find a suitable DHCP client]) - DHCP_CLIENT_PATH=/sbin/dhclient + DHCLIENT_PATH=/sbin/dhclient AC_MSG_WARN([Falling back to ISC dhclient, ${DHCP_CLIENT_PATH}]) fi -AC_SUBST(DHCP_CLIENT_PATH) -DHCP_CLIENT=`basename "$DHCP_CLIENT_PATH"` -if test "$DHCP_CLIENT" != "dhclient" -a "$DHCP_CLIENT" != "dhcpcd"; then - AC_MSG_ERROR([No backend for the DHCP client ${DHCP_CLIENT}]) -fi -AC_SUBST(DHCP_CLIENT) +AC_SUBST(DHCLIENT_PATH) +AC_SUBST(DHCPCD_PATH) # resolvconf support AC_ARG_WITH([resolvconf], @@ -492,12 +498,25 @@ NetworkManager.pc AC_OUTPUT echo -echo Distribution targeting: ${with_distro} +echo Distribution target: ${with_distro} echo 'if this is not correct, please specifiy your distro with --with-distro=DISTRO' +echo + +if test -n "${DHCLIENT_PATH}"; then + echo ISC dhclient support: ${DHCLIENT_PATH} +else + echo ISC dhclient support: no +fi + +if test -n "${DHCPCD_PATH}"; then + echo dhcpcd support: ${DHCPCD_PATH} +else + echo dhcpcd support: no +fi + echo echo Building documentation: ${with_docs} -echo echo Building tests: ${with_tests} echo diff --git a/src/NetworkManager.c b/src/NetworkManager.c index 09a4320c78..a6639b8c8e 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -302,7 +302,10 @@ done: } static gboolean -parse_config_file (const char *filename, char **plugins, GError **error) +parse_config_file (const char *filename, + char **plugins, + char **dhcp_client, + GError **error) { GKeyFile *config; @@ -321,6 +324,8 @@ parse_config_file (const char *filename, char **plugins, GError **error) if (*error) return FALSE; + *dhcp_client = g_key_file_get_value (config, "main", "dhcp", NULL); + g_key_file_free (config); return TRUE; } @@ -435,7 +440,7 @@ main (int argc, char *argv[]) gboolean become_daemon = FALSE; gboolean g_fatal_warnings = FALSE; char *pidfile = NULL, *user_pidfile = NULL; - char *config = NULL, *plugins = NULL; + char *config = NULL, *plugins = NULL, *dhcp = NULL; char *state_file = NM_DEFAULT_SYSTEM_STATE_FILE; gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE; gboolean success; @@ -498,7 +503,7 @@ main (int argc, char *argv[]) /* Parse the config file */ if (config) { - if (!parse_config_file (config, &plugins, &error)) { + if (!parse_config_file (config, &plugins, &dhcp, &error)) { g_warning ("Config file %s invalid: (%d) %s.", config, error ? error->code : -1, @@ -507,7 +512,7 @@ main (int argc, char *argv[]) } } else { config = NM_DEFAULT_SYSTEM_CONF_FILE; - if (!parse_config_file (config, &plugins, &error)) { + if (!parse_config_file (config, &plugins, &dhcp, &error)) { g_warning ("Default config file %s invalid: (%d) %s.", config, error ? error->code : -1, @@ -620,9 +625,9 @@ main (int argc, char *argv[]) goto done; } - dhcp_mgr = nm_dhcp_manager_get (); + dhcp_mgr = nm_dhcp_manager_new (dhcp ? dhcp : "dhclient", &error); if (!dhcp_mgr) { - nm_warning ("Failed to start the DHCP manager."); + nm_warning ("Failed to start the DHCP manager: %s.", error->message); goto done; } diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am index 711c2a5058..ff9041c390 100644 --- a/src/dhcp-manager/Makefile.am +++ b/src/dhcp-manager/Makefile.am @@ -27,7 +27,8 @@ libdhcp_manager_la_CPPFLAGS = \ -DSYSCONFDIR=\"$(sysconfdir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ -DLOCALSTATEDIR=\"$(localstatedir)\" \ - -DDHCP_CLIENT_PATH=\"$(DHCP_CLIENT_PATH)\" + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" libdhcp_manager_la_LIBADD = \ $(top_builddir)/marshallers/libmarshallers.la \ diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index 0e5d81229d..5cf77737e1 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -114,9 +114,5 @@ NMIP4Config *nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test) /* Backend helpers */ void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name); -/* Implemented by the backends */ -GSList *nm_dhcp_backend_get_lease_config (const char *iface, - const char *uuid); - #endif /* NM_DHCP_CLIENT_H */ diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index c47d155b0f..7eec31d312 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -110,7 +110,7 @@ add_lease_option (GHashTable *hash, char *line) } GSList * -nm_dhcp_backend_get_lease_config (const char *iface, const char *uuid) +nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid) { GSList *parsed = NULL, *iter, *leases = NULL; char *contents = NULL; @@ -469,13 +469,13 @@ real_ip4_start (NMDHCPClient *client, return -1; } - if (!g_file_test (DHCP_CLIENT_PATH, G_FILE_TEST_EXISTS)) { - nm_warning (DHCP_CLIENT_PATH " does not exist."); + if (!g_file_test (DHCLIENT_PATH, G_FILE_TEST_EXISTS)) { + nm_warning (DHCLIENT_PATH " does not exist."); return -1; } /* Kill any existing dhclient from the pidfile */ - binary_name = g_path_get_basename (DHCP_CLIENT_PATH); + binary_name = g_path_get_basename (DHCLIENT_PATH); nm_dhcp_client_stop_existing (priv->pid_file, binary_name); g_free (binary_name); @@ -492,7 +492,7 @@ real_ip4_start (NMDHCPClient *client, } argv = g_ptr_array_new (); - g_ptr_array_add (argv, (gpointer) DHCP_CLIENT_PATH); + g_ptr_array_add (argv, (gpointer) DHCLIENT_PATH); g_ptr_array_add (argv, (gpointer) "-d"); diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h index bad6452f79..db9f73d254 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.h +++ b/src/dhcp-manager/nm-dhcp-dhclient.h @@ -41,5 +41,7 @@ typedef struct { GType nm_dhcp_dhclient_get_type (void); +GSList *nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid); + #endif /* NM_DHCP_DHCLIENT_H */ diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index ba210eda63..fb1fd1bf1c 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -47,7 +47,7 @@ typedef struct { GSList * -nm_dhcp_backend_get_lease_config (const char *iface, const char *uuid) +nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid) { return NULL; } @@ -83,18 +83,18 @@ real_ip4_start (NMDHCPClient *client, return -1; } - if (!g_file_test (DHCP_CLIENT_PATH, G_FILE_TEST_EXISTS)) { - nm_warning (DHCP_CLIENT_PATH " does not exist."); + if (!g_file_test (DHCPCD_PATH, G_FILE_TEST_EXISTS)) { + nm_warning (DHCPCD_PATH " does not exist."); return -1; } /* Kill any existing dhclient from the pidfile */ - binary_name = g_path_get_basename (DHCP_CLIENT_PATH); + binary_name = g_path_get_basename (DHCPCD_PATH); nm_dhcp_client_stop_existing (priv->pid_file, binary_name); g_free (binary_name); argv = g_ptr_array_new (); - g_ptr_array_add (argv, (gpointer) DHCP_CLIENT_PATH); + g_ptr_array_add (argv, (gpointer) DHCPCD_PATH); g_ptr_array_add (argv, (gpointer) "-B"); /* Don't background on lease (disable fork()) */ diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h index f564bc7f71..9a98129419 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.h +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h @@ -41,5 +41,7 @@ typedef struct { GType nm_dhcp_dhcpcd_get_type (void); +GSList *nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid); + #endif /* NM_DHCP_DHCPCD_H */ diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index f1a84cdd46..59fc2536de 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -36,6 +36,7 @@ #include "nm-dhcp-manager.h" #include "nm-dhcp-dhclient.h" +#include "nm-dhcp-dhcpcd.h" #include "nm-marshal.h" #include "nm-utils.h" #include "nm-dbus-manager.h" @@ -46,8 +47,14 @@ #define NM_DHCP_CLIENT_DBUS_SERVICE "org.freedesktop.nm_dhcp_client" #define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client" +static NMDHCPManager *singleton = NULL; + +typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid); + typedef struct { - guint32 next_id; + GType client_type; + GetLeaseConfigFunc get_lease_config_func; + NMDBusManager * dbus_mgr; GHashTable * clients; DBusGProxy * proxy; @@ -219,23 +226,36 @@ out: g_free (reason); } -static NMDHCPManager * -nm_dhcp_manager_new (void) +NMDHCPManager * +nm_dhcp_manager_new (const char *client, GError **error) { - NMDHCPManager *manager; NMDHCPManagerPrivate *priv; DBusGConnection *g_connection; - manager = g_object_new (NM_TYPE_DHCP_MANAGER, NULL); - priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + g_warn_if_fail (singleton == NULL); + g_return_val_if_fail (client != NULL, NULL); + + singleton = g_object_new (NM_TYPE_DHCP_MANAGER, NULL); + priv = NM_DHCP_MANAGER_GET_PRIVATE (singleton); + + /* Figure out which DHCP client to use */ + if (!strcmp (client, "dhclient") && strlen (DHCLIENT_PATH)) { + priv->client_type = NM_TYPE_DHCP_DHCLIENT; + priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config; + } else if (!strcmp (client, "dhcpcd") && strlen (DHCPCD_PATH)) { + priv->client_type = NM_TYPE_DHCP_DHCPCD; + priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config; + } else { + g_set_error (error, 0, 0, "unknown or missing DHCP client '%s'", client); + goto error; + } priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); if (!priv->clients) { - nm_warning ("Error: not enough memory to initialize DHCP manager tables"); - g_object_unref (manager); - return NULL; + g_set_error_literal (error, 0, 0, "not enough memory to initialize DHCP manager"); + goto error; } priv->dbus_mgr = nm_dbus_manager_get (); @@ -245,9 +265,8 @@ nm_dhcp_manager_new (void) "/", NM_DHCP_CLIENT_DBUS_IFACE); if (!priv->proxy) { - nm_warning ("Error: could not init DHCP manager proxy"); - g_object_unref (manager); - return NULL; + g_set_error_literal (error, 0, 0, "not enough memory to initialize DHCP manager proxy"); + goto error; } dbus_g_proxy_add_signal (priv->proxy, @@ -256,11 +275,16 @@ nm_dhcp_manager_new (void) G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "Event", - G_CALLBACK (nm_dhcp_manager_handle_event), - manager, - NULL); + G_CALLBACK (nm_dhcp_manager_handle_event), + singleton, + NULL); - return manager; + return singleton; + +error: + g_object_unref (singleton); + singleton = NULL; + return singleton; } #define STATE_ID_TAG "state-id" @@ -416,7 +440,7 @@ nm_dhcp_manager_get_lease_config (NMDHCPManager *self, g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (uuid != NULL, NULL); - return nm_dhcp_backend_get_lease_config (iface, uuid); + return NM_DHCP_MANAGER_GET_PRIVATE (self)->get_lease_config_func (iface, uuid); } NMIP4Config * @@ -443,23 +467,13 @@ nm_dhcp_manager_test_ip4_options_to_config (const char *iface, NMDHCPManager * nm_dhcp_manager_get (void) { - static NMDHCPManager *singleton = NULL; - - if (!singleton) - singleton = nm_dhcp_manager_new (); - else - g_object_ref (singleton); - - g_assert (singleton); - return singleton; + g_warn_if_fail (singleton != NULL); + return g_object_ref (singleton); } static void nm_dhcp_manager_init (NMDHCPManager *manager) { - NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - - priv->next_id = 1; } static void @@ -468,10 +482,12 @@ dispose (GObject *object) NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object); GList *values, *iter; - values = g_hash_table_get_values (priv->clients); - for (iter = values; iter; iter = g_list_next (iter)) - remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data)); - g_list_free (values); + if (priv->clients) { + values = g_hash_table_get_values (priv->clients); + for (iter = values; iter; iter = g_list_next (iter)) + remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data)); + g_list_free (values); + } G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object); } @@ -486,9 +502,12 @@ finalize (GObject *object) priv->hostname_provider = NULL; } - g_hash_table_destroy (priv->clients); - g_object_unref (priv->proxy); - g_object_unref (priv->dbus_mgr); + if (priv->clients) + g_hash_table_destroy (priv->clients); + if (priv->proxy) + g_object_unref (priv->proxy); + if (priv->dbus_mgr) + g_object_unref (priv->dbus_mgr); G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object); } diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 7c82ca59ac..441d9fce14 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -70,4 +70,7 @@ NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *iface, GHashTable *options, const char *reason); +/* Only for NetworkManager.c */ +NMDHCPManager *nm_dhcp_manager_new (const char *client, GError **error); + #endif /* NM_DHCP_MANAGER_H */ From 81f23ea3836245d111306ad38345a83b4f7a6a3f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 12 Jan 2010 23:07:44 -0800 Subject: [PATCH 07/20] dhcp: actually use runtime-selected DHCP client --- src/dhcp-manager/nm-dhcp-manager.c | 49 ++++++++++++++++----- src/dhcp-manager/nm-dhcp-manager.h | 3 +- src/tests/Makefile.am | 4 +- src/tests/test-dhcp-options.c | 71 ++++++++++++++++++------------ 4 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 59fc2536de..6da14ed145 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -226,6 +226,21 @@ out: g_free (reason); } +static GType +get_client_type (const char *client, GError **error) +{ + g_return_val_if_fail (client != NULL, 0); + + if (!strcmp (client, "dhclient") && strlen (DHCLIENT_PATH)) + return NM_TYPE_DHCP_DHCLIENT; + else if (!strcmp (client, "dhcpcd") && strlen (DHCPCD_PATH)) + return NM_TYPE_DHCP_DHCPCD; + else + g_set_error (error, 0, 0, "unknown or missing DHCP client '%s'", client); + + return 0; +} + NMDHCPManager * nm_dhcp_manager_new (const char *client, GError **error) { @@ -239,16 +254,16 @@ nm_dhcp_manager_new (const char *client, GError **error) priv = NM_DHCP_MANAGER_GET_PRIVATE (singleton); /* Figure out which DHCP client to use */ - if (!strcmp (client, "dhclient") && strlen (DHCLIENT_PATH)) { - priv->client_type = NM_TYPE_DHCP_DHCLIENT; - priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config; - } else if (!strcmp (client, "dhcpcd") && strlen (DHCPCD_PATH)) { - priv->client_type = NM_TYPE_DHCP_DHCPCD; - priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config; - } else { - g_set_error (error, 0, 0, "unknown or missing DHCP client '%s'", client); + priv->client_type = get_client_type (client, error); + if (!priv->client_type) goto error; - } + + if (priv->client_type == NM_TYPE_DHCP_DHCLIENT) + priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config; + else if (priv->client_type == NM_TYPE_DHCP_DHCPCD) + priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config; + else + g_assert_not_reached (); priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, @@ -368,7 +383,7 @@ nm_dhcp_manager_start_client (NMDHCPManager *self, } /* And make a new one */ - client = g_object_new (NM_TYPE_DHCP_DHCLIENT, + client = g_object_new (priv->client_type, NM_DHCP_CLIENT_INTERFACE, iface, NULL); g_return_val_if_fail (client != NULL, NULL); @@ -444,14 +459,24 @@ nm_dhcp_manager_get_lease_config (NMDHCPManager *self, } NMIP4Config * -nm_dhcp_manager_test_ip4_options_to_config (const char *iface, +nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client, + const char *iface, GHashTable *options, const char *reason) { NMDHCPClient *client; NMIP4Config *config; + GType client_type; + GError *error = NULL; - client = (NMDHCPClient *) g_object_new (NM_TYPE_DHCP_DHCLIENT, + client_type = get_client_type (dhcp_client, &error); + if (!client_type) { + g_warning ("Error: %s", error ? error->message : "(unknown)"); + g_clear_error (&error); + return NULL; + } + + client = (NMDHCPClient *) g_object_new (client_type, NM_DHCP_CLIENT_INTERFACE, iface, NULL); g_return_val_if_fail (client != NULL, NULL); diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 441d9fce14..dab33601ae 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -66,7 +66,8 @@ GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self, const char *uuid); /* For testing only */ -NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *iface, +NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client, + const char *iface, GHashTable *options, const char *reason); diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 398042ccb5..75f9cffd59 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -13,7 +13,9 @@ test_dhcp_options_SOURCES = \ test_dhcp_options_CPPFLAGS = \ $(GLIB_CFLAGS) \ - $(DBUS_CFLAGS) + $(DBUS_CFLAGS) \ + -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ + -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" test_dhcp_options_LDADD = \ $(top_builddir)/libnm-util/libnm-util.la \ diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c index 6f5fb011f0..6211f1d9ce 100644 --- a/src/tests/test-dhcp-options.c +++ b/src/tests/test-dhcp-options.c @@ -97,7 +97,7 @@ static Option generic_options[] = { }; static void -test_generic_options (void) +test_generic_options (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -116,7 +116,7 @@ test_generic_options (void) const char *expected_route2_gw = "10.1.1.1"; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-generic", "failed to parse DHCP4 options"); @@ -215,7 +215,7 @@ static Option wins_options[] = { }; static void -test_wins_options (void) +test_wins_options (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -227,7 +227,7 @@ test_wins_options (void) options = fill_table (generic_options, NULL); options = fill_table (wins_options, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-wins", "failed to parse DHCP4 options"); @@ -259,7 +259,7 @@ static Option classless_routes_options[] = { }; static void -test_classless_static_routes (void) +test_classless_static_routes (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -273,7 +273,7 @@ test_classless_static_routes (void) options = fill_table (generic_options, NULL); options = fill_table (classless_routes_options, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442", "failed to parse DHCP4 options"); @@ -327,7 +327,7 @@ static Option invalid_classless_routes1[] = { }; static void -test_invalid_classless_routes1 (void) +test_invalid_classless_routes1 (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -339,7 +339,7 @@ test_invalid_classless_routes1 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes1, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options"); @@ -376,7 +376,7 @@ static Option invalid_classless_routes2[] = { }; static void -test_invalid_classless_routes2 (void) +test_invalid_classless_routes2 (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -390,7 +390,7 @@ test_invalid_classless_routes2 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes2, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options"); @@ -448,7 +448,7 @@ static Option invalid_classless_routes3[] = { }; static void -test_invalid_classless_routes3 (void) +test_invalid_classless_routes3 (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -460,7 +460,7 @@ test_invalid_classless_routes3 (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_classless_routes3, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options"); @@ -497,7 +497,7 @@ static Option gw_in_classless_routes[] = { }; static void -test_gateway_in_classless_routes (void) +test_gateway_in_classless_routes (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -511,7 +511,7 @@ test_gateway_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (gw_in_classless_routes, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-rfc3442-gateway", "failed to parse DHCP4 options"); @@ -554,7 +554,7 @@ static Option escaped_searches_options[] = { }; static void -test_escaped_domain_searches (void) +test_escaped_domain_searches (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -565,7 +565,7 @@ test_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (escaped_searches_options, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-escaped-domain-searches", "failed to parse DHCP4 options"); @@ -588,7 +588,7 @@ static Option invalid_escaped_searches_options[] = { }; static void -test_invalid_escaped_domain_searches (void) +test_invalid_escaped_domain_searches (const char *client) { GHashTable *options; NMIP4Config *ip4_config; @@ -596,7 +596,7 @@ test_invalid_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (invalid_escaped_searches_options, options); - ip4_config = nm_dhcp_manager_test_ip4_options_to_config ("eth0", options, "rebind"); + ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind"); ASSERT (ip4_config != NULL, "dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options"); @@ -607,6 +607,9 @@ test_invalid_escaped_domain_searches (void) g_hash_table_destroy (options); } +#define DHCLIENT "dhclient" +#define DHCPCD "dhcpcd" + int main (int argc, char **argv) { GError *error = NULL; @@ -620,15 +623,29 @@ int main (int argc, char **argv) FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); /* The tests */ - test_generic_options (); - test_wins_options (); - test_classless_static_routes (); - test_invalid_classless_routes1 (); - test_invalid_classless_routes2 (); - test_invalid_classless_routes3 (); - test_gateway_in_classless_routes (); - test_escaped_domain_searches (); - test_invalid_escaped_domain_searches (); + if (strlen (DHCLIENT_PATH)) { + test_generic_options (DHCLIENT); + test_wins_options (DHCLIENT); + test_classless_static_routes (DHCLIENT); + test_invalid_classless_routes1 (DHCLIENT); + test_invalid_classless_routes2 (DHCLIENT); + test_invalid_classless_routes3 (DHCLIENT); + test_gateway_in_classless_routes (DHCLIENT); + test_escaped_domain_searches (DHCLIENT); + test_invalid_escaped_domain_searches (DHCLIENT); + } + + if (strlen (DHCPCD_PATH)) { + test_generic_options (DHCPCD); + test_wins_options (DHCPCD); + test_classless_static_routes (DHCPCD); + test_invalid_classless_routes1 (DHCPCD); + test_invalid_classless_routes2 (DHCPCD); + test_invalid_classless_routes3 (DHCPCD); + test_gateway_in_classless_routes (DHCPCD); + test_escaped_domain_searches (DHCPCD); + test_invalid_escaped_domain_searches (DHCPCD); + } base = g_path_get_basename (argv[0]); fprintf (stdout, "%s: SUCCESS\n", base); From fe62e59c7e0ac04c05123dd18e19ccc04a289152 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Jan 2010 16:51:20 -0800 Subject: [PATCH 08/20] dhcp: add DHCPv6 functionality --- src/dhcp-manager/nm-dhcp-client.c | 145 +++++++++++++++++++++++----- src/dhcp-manager/nm-dhcp-client.h | 30 ++++-- src/dhcp-manager/nm-dhcp-dhclient.c | 46 ++++++--- src/dhcp-manager/nm-dhcp-dhcpcd.c | 14 ++- src/dhcp-manager/nm-dhcp-manager.c | 110 +++++++++++++++------ src/dhcp-manager/nm-dhcp-manager.h | 20 ++-- src/nm-device.c | 12 +-- 7 files changed, 288 insertions(+), 89 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index e39c9bf312..382b7d4a91 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -32,10 +32,12 @@ #include "nm-dbus-glib-types.h" #include "nm-dhcp-client.h" -#define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */ - typedef struct { char * iface; + gboolean ipv6; + char * uuid; + guint32 timeout; + guchar state; GPid pid; guint timeout_id; @@ -58,6 +60,9 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, PROP_IFACE, + PROP_IPV6, + PROP_UUID, + PROP_TIMEOUT, LAST_PROP }; @@ -81,6 +86,24 @@ nm_dhcp_client_get_iface (NMDHCPClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface; } +gboolean +nm_dhcp_client_get_ipv6 (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6; +} + +const char * +nm_dhcp_client_get_uuid (NMDHCPClient *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid; +} + /********************************************/ static void @@ -193,12 +216,26 @@ daemon_watch_cb (GPid pid, gint status, gpointer user_data) g_signal_emit (G_OBJECT (self), signals[STATE_CHANGED], 0, priv->state); } +static void +start_monitor (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + g_return_if_fail (priv->pid > 0); + + /* Set up a timeout on the transaction to kill it after the timeout */ + priv->timeout_id = g_timeout_add_seconds (priv->timeout, + daemon_timeout, + self); + priv->watch_id = g_child_watch_add (priv->pid, + (GChildWatchFunc) daemon_watch_cb, + self); +} + gboolean -nm_dhcp_client_start (NMDHCPClient *self, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint32 timeout_secs, - guint8 *dhcp_anycast_addr) +nm_dhcp_client_start_ip4 (NMDHCPClient *self, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) { NMDHCPClientPrivate *priv; @@ -207,28 +244,42 @@ nm_dhcp_client_start (NMDHCPClient *self, priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->pid == -1, FALSE); + g_return_val_if_fail (priv->ipv6 == FALSE, FALSE); + g_return_val_if_fail (priv->uuid != NULL, FALSE); - if (timeout_secs == 0) - timeout_secs = NM_DHCP_TIMEOUT; + g_message ("Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)", + priv->iface, priv->timeout); - g_message ("Activation (%s) Beginning DHCP transaction (timeout in %d seconds)", - priv->iface, timeout_secs); - priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, - uuid, - s_ip4, - dhcp_anycast_addr); - if (priv->pid <= 0) - return FALSE; + priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, s_ip4, dhcp_anycast_addr); + if (priv->pid) + start_monitor (self); - /* Set up a timeout on the transaction to kill it after the timeout */ - priv->timeout_id = g_timeout_add_seconds (timeout_secs, - daemon_timeout, - self); - priv->watch_id = g_child_watch_add (priv->pid, - (GChildWatchFunc) daemon_watch_cb, - self); + return priv->pid ? TRUE : FALSE; +} - return TRUE; +gboolean +nm_dhcp_client_start_ip6 (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->pid == -1, FALSE); + g_return_val_if_fail (priv->ipv6 == TRUE, FALSE); + g_return_val_if_fail (priv->uuid != NULL, FALSE); + + 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); + if (priv->pid > 0) + start_monitor (self); + + return priv->pid ? TRUE : FALSE; } void @@ -816,6 +867,15 @@ get_property (GObject *object, guint prop_id, case PROP_IFACE: g_value_set_string (value, priv->iface); break; + case PROP_IPV6: + g_value_set_boolean (value, priv->ipv6); + break; + case PROP_UUID: + g_value_set_string (value, priv->uuid); + break; + case PROP_TIMEOUT: + g_value_set_uint (value, priv->timeout); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -833,6 +893,17 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->iface = g_strdup (g_value_get_string (value)); break; + case PROP_IPV6: + /* construct-only */ + priv->ipv6 = g_value_get_boolean (value); + break; + case PROP_UUID: + /* construct-only */ + priv->uuid = g_value_dup_string (value); + break; + case PROP_TIMEOUT: + priv->timeout = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -878,6 +949,30 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class) NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property + (object_class, PROP_IPV6, + g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, + "ipv6", + "IPv6", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_UUID, + g_param_spec_string (NM_DHCP_CLIENT_UUID, + "uuid", + "UUID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_TIMEOUT, + g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, + "timeout", + "Timeout", + 0, G_MAXUINT, 45, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index 5cf77737e1..8b5804f418 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -22,6 +22,8 @@ #include #include +#include +#include #include #define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type ()) @@ -32,6 +34,9 @@ #define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass)) #define NM_DHCP_CLIENT_INTERFACE "iface" +#define NM_DHCP_CLIENT_IPV6 "ipv6" +#define NM_DHCP_CLIENT_UUID "uuid" +#define NM_DHCP_CLIENT_TIMEOUT "timeout" typedef enum { DHC_NBI = 0, /* no broadcast interfaces found */ @@ -76,9 +81,12 @@ typedef struct { guint32 *out_gwaddr); GPid (*ip4_start) (NMDHCPClient *self, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *anycast_addr); + NMSettingIP4Config *s_ip4, + guint8 *anycast_addr); + + GPid (*ip6_start) (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *anycast_addr); void (*stop) (NMDHCPClient *self); @@ -93,11 +101,17 @@ GPid nm_dhcp_client_get_pid (NMDHCPClient *self); const char *nm_dhcp_client_get_iface (NMDHCPClient *self); -gboolean nm_dhcp_client_start (NMDHCPClient *self, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint32 timeout_secs, - guint8 *dhcp_anycast_addr); +gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self); + +const char *nm_dhcp_client_get_uuid (NMDHCPClient *self); + +gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr); + +gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr); 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 7eec31d312..899410211f 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -447,21 +447,21 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) } static GPid -real_ip4_start (NMDHCPClient *client, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint8 *dhcp_anycast_addr) +dhclient_start (NMDHCPClient *client, + const char *ip_opt) { NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); GPtrArray *argv = NULL; GPid pid = 0; GError *error = NULL; - const char *iface; + const char *iface, *uuid; char *binary_name; g_return_val_if_fail (priv->pid_file == NULL, -1); + g_return_val_if_fail (ip_opt != NULL, -1); iface = nm_dhcp_client_get_iface (client); + uuid = nm_dhcp_client_get_uuid (client); priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient-%s.pid", iface); if (!priv->pid_file) { @@ -479,12 +479,6 @@ real_ip4_start (NMDHCPClient *client, nm_dhcp_client_stop_existing (priv->pid_file, binary_name); g_free (binary_name); - priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr); - if (!priv->conf_file) { - nm_warning ("%s: error creating dhclient configuration file.", iface); - return -1; - } - priv->lease_file = get_leasefile_for_iface (iface, uuid); if (!priv->lease_file) { nm_warning ("%s: not enough memory for dhclient options.", iface); @@ -496,6 +490,8 @@ real_ip4_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) "-d"); + g_ptr_array_add (argv, (gpointer) ip_opt); + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH ); @@ -523,6 +519,33 @@ real_ip4_start (NMDHCPClient *client, return pid; } +static GPid +real_ip4_start (NMDHCPClient *client, + NMSettingIP4Config *s_ip4, + guint8 *dhcp_anycast_addr) +{ + NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + const char *iface; + + iface = nm_dhcp_client_get_iface (client); + + priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr); + if (!priv->conf_file) { + nm_warning ("%s: error creating dhclient configuration file.", iface); + return -1; + } + + return dhclient_start (client, "-4"); +} + +static GPid +real_ip6_start (NMDHCPClient *client, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr) +{ + return dhclient_start (client, "-6"); +} + static void real_stop (NMDHCPClient *client) { @@ -698,6 +721,7 @@ nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class) object_class->dispose = dispose; client_class->ip4_start = real_ip4_start; + client_class->ip6_start = real_ip6_start; client_class->stop = real_stop; client_class->ip4_process_classless_routes = real_ip4_process_classless_routes; } diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index fb1fd1bf1c..741fe7f76b 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -62,7 +62,6 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) static GPid real_ip4_start (NMDHCPClient *client, - const char *uuid, NMSettingIP4Config *s_ip4, guint8 *dhcp_anycast_addr) { @@ -71,11 +70,12 @@ real_ip4_start (NMDHCPClient *client, GPid pid = 0; GError *error = NULL; char *pid_contents = NULL, *binary_name; - const char *iface; + const char *iface, *uuid; g_return_val_if_fail (priv->pid_file == NULL, -1); iface = nm_dhcp_client_get_iface (client); + uuid = nm_dhcp_client_get_uuid (client); priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhcpcd-%s.pid", iface); if (!priv->pid_file) { @@ -120,6 +120,15 @@ real_ip4_start (NMDHCPClient *client, return pid; } +static GPid +real_ip6_start (NMDHCPClient *client, + NMSettingIP6Config *s_ip6, + guint8 *dhcp_anycast_addr) +{ + g_warning ("The dhcpcd backend does not support IPv6."); + return -1; +} + static void real_stop (NMDHCPClient *client) { @@ -238,6 +247,7 @@ nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class) object_class->dispose = dispose; client_class->ip4_start = real_ip4_start; + client_class->ip6_start = real_ip6_start; client_class->stop = real_stop; client_class->ip4_process_classless_routes = real_ip4_process_classless_routes; } diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 6da14ed145..7d9cca5707 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -47,6 +47,8 @@ #define NM_DHCP_CLIENT_DBUS_SERVICE "org.freedesktop.nm_dhcp_client" #define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client" +#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */ + static NMDHCPManager *singleton = NULL; typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid); @@ -124,7 +126,8 @@ get_client_for_pid (NMDHCPManager *manager, GPid pid) static NMDHCPClient * get_client_for_iface (NMDHCPManager *manager, - const char *iface) + const char *iface, + gboolean ip6) { NMDHCPManagerPrivate *priv; GHashTableIter iter; @@ -140,7 +143,8 @@ get_client_for_iface (NMDHCPManager *manager, while (g_hash_table_iter_next (&iter, NULL, &value)) { NMDHCPClient *candidate = NM_DHCP_CLIENT (value); - if (!strcmp (iface, nm_dhcp_client_get_iface (candidate))) + if ( !strcmp (iface, nm_dhcp_client_get_iface (candidate)) + && (nm_dhcp_client_get_ipv6 (candidate) == ip6)) return candidate; } @@ -355,28 +359,29 @@ add_client (NMDHCPManager *self, NMDHCPClient *client) g_hash_table_insert (priv->clients, client, g_object_ref (client)); } -/* Caller owns a reference to the NMDHCPClient on return */ -NMDHCPClient * -nm_dhcp_manager_start_client (NMDHCPManager *self, - const char *iface, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint32 timeout, - guint8 *dhcp_anycast_addr) +static NMDHCPClient * +client_start (NMDHCPManager *self, + const char *iface, + const char *uuid, + gboolean ipv6, + NMSettingIP4Config *s_ip4, + NMSettingIP6Config *s_ip6, + guint32 timeout, + guint8 *dhcp_anycast_addr) { NMDHCPManagerPrivate *priv; NMDHCPClient *client; - NMSettingIP4Config *setting = NULL; gboolean success = FALSE; g_return_val_if_fail (self, NULL); g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (uuid != NULL, NULL); priv = NM_DHCP_MANAGER_GET_PRIVATE (self); /* Kill any old client instance */ - client = get_client_for_iface (self, iface); + client = get_client_for_iface (self, iface, ipv6); if (client) { nm_dhcp_client_stop (client); remove_client (self, client); @@ -385,29 +390,17 @@ nm_dhcp_manager_start_client (NMDHCPManager *self, /* And make a new one */ client = g_object_new (priv->client_type, NM_DHCP_CLIENT_INTERFACE, iface, + NM_DHCP_CLIENT_IPV6, ipv6, + NM_DHCP_CLIENT_UUID, uuid, + NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT, NULL); g_return_val_if_fail (client != NULL, NULL); add_client (self, client); - if ( s_ip4 - && nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) - && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL) - && priv->hostname_provider != NULL) { - - /* We're asked to send the hostname to DHCP server, the hostname - * isn't specified, and a hostname provider is registered: use that - */ - setting = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4))); - g_object_set (G_OBJECT (setting), - NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, - nm_hostname_provider_get_hostname (priv->hostname_provider), - NULL); - } - - success = nm_dhcp_client_start (client, uuid, setting, timeout, dhcp_anycast_addr); - - if (setting) - g_object_unref (setting); + if (ipv6) + success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr); + else + success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr); if (!success) { remove_client (self, client); @@ -418,6 +411,61 @@ nm_dhcp_manager_start_client (NMDHCPManager *self, return client; } +/* Caller owns a reference to the NMDHCPClient on return */ +NMDHCPClient * +nm_dhcp_manager_start_ip4 (NMDHCPManager *self, + const char *iface, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout, + guint8 *dhcp_anycast_addr) +{ + NMDHCPManagerPrivate *priv; + NMDHCPClient *client = NULL; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + + if (s_ip4) { + if ( nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4) + && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL) + && priv->hostname_provider != NULL) { + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4))); + + /* We're asked to send the hostname to DHCP server, the hostname + * isn't specified, and a hostname provider is registered: use that + */ + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, + nm_hostname_provider_get_hostname (priv->hostname_provider), + NULL); + } else + g_object_ref (s_ip4); + } + + client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr); + + if (s_ip4) + g_object_unref (s_ip4); + + return client; +} + +/* Caller owns a reference to the NMDHCPClient on return */ +NMDHCPClient * +nm_dhcp_manager_start_ip6 (NMDHCPManager *self, + const char *iface, + const char *uuid, + NMSettingIP6Config *s_ip6, + guint32 timeout, + guint8 *dhcp_anycast_addr) +{ + return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr); +} + static void hostname_provider_destroyed (gpointer data, GObject *destroyed_object) { diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index dab33601ae..690241482b 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -26,6 +26,7 @@ #include #include +#include #include "nm-dhcp-client.h" #include "nm-ip4-config.h" @@ -54,12 +55,19 @@ NMDHCPManager *nm_dhcp_manager_get (void); void nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager, NMHostnameProvider *provider); -NMDHCPClient * nm_dhcp_manager_start_client (NMDHCPManager *manager, - const char *iface, - const char *uuid, - NMSettingIP4Config *s_ip4, - guint32 timeout, - guint8 *dhcp_anycast_addr); +NMDHCPClient * nm_dhcp_manager_start_ip4 (NMDHCPManager *manager, + const char *iface, + const char *uuid, + NMSettingIP4Config *s_ip4, + guint32 timeout, + guint8 *dhcp_anycast_addr); + +NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager, + const char *iface, + const char *uuid, + NMSettingIP6Config *s_ip6, + guint32 timeout, + guint8 *dhcp_anycast_addr); 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 586c93949c..2b809470b2 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -1215,12 +1215,12 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) /* DHCP manager will cancel any transaction already in progress and we do not want to cancel this activation if we get "down" state from that. */ - priv->dhcp4_client = nm_dhcp_manager_start_client (priv->dhcp_manager, - ip_iface, - uuid, - s_ip4, - priv->dhcp_timeout, - anycast); + priv->dhcp4_client = nm_dhcp_manager_start_ip4 (priv->dhcp_manager, + ip_iface, + uuid, + s_ip4, + priv->dhcp_timeout, + anycast); if (priv->dhcp4_client) { priv->dhcp_state_sigid = g_signal_connect (priv->dhcp4_client, "state-changed", From 3568a98a61019a6b1e3ad0f82b0b8d30ce3f0403 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Jan 2010 17:59:19 -0800 Subject: [PATCH 09/20] dhcp: fix dhcp 4/6 mixup --- src/dhcp-manager/nm-dhcp-manager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 7d9cca5707..9dc90ac76b 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -398,9 +398,9 @@ client_start (NMDHCPManager *self, add_client (self, client); if (ipv6) - success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr); - else success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr); + else + success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr); if (!success) { remove_client (self, client); From 2a718bbb1b4e8a128193f3d64851a927de041071 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Jan 2010 17:59:54 -0800 Subject: [PATCH 10/20] core: simplify DHCP setup and cleanup nm_device_set_use_dhcp() and nm_device_get_use_dhcp() were somewhat confusing and don't really reflect the new DHCP architecture with NMDHCPClient. Now that timeout and state signals are specific to the NMDHCPClient it doesn't make sense to check for DHCP use in the callbacks for those signals since they'll never get called if DHCP isn't in use. We might as well just keep the DHCP manager around and check whether a DHCP client instance exists when we need to figure out whether DHCP is in use. --- src/nm-device.c | 138 ++++++++++++++++++++---------------------------- src/nm-device.h | 3 -- 2 files changed, 57 insertions(+), 84 deletions(-) diff --git a/src/nm-device.c b/src/nm-device.c index 2b809470b2..3c4b81c9cf 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -102,9 +102,10 @@ typedef struct { gboolean ip4_ready; gboolean ip6_ready; + NMDHCPManager * dhcp_manager; + /* IP4 configuration info */ NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ - NMDHCPManager * dhcp_manager; NMDHCPClient * dhcp4_client; guint32 dhcp_timeout; gulong dhcp_state_sigid; @@ -221,6 +222,8 @@ constructor (GType type, if (NM_DEVICE_GET_CLASS (dev)->update_hw_address) NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev); + priv->dhcp_manager = nm_dhcp_manager_get (); + priv->initialized = TRUE; return object; @@ -1077,11 +1080,6 @@ handle_dhcp_lease_change (NMDevice *device) NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; gboolean assumed; - if (!nm_device_get_use_dhcp (device)) { - nm_warning ("got DHCP rebind for device that wasn't using DHCP."); - return; - } - config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); if (!config) { nm_warning ("failed to get DHCP config for rebind"); @@ -1146,14 +1144,11 @@ dhcp_state_changed (NMDHCPClient *client, case DHC_END: /* dhclient exited normally */ nm_dhcp4_config_reset (priv->dhcp4_config); - if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + /* dhclient quit and can't get/renew a lease; so kill the connection */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); - } else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { - if (nm_device_get_use_dhcp (device)) { - /* dhclient quit and therefore can't renew our lease, kill the conneciton */ - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - } - } + else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); break; default: break; @@ -1207,11 +1202,15 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint8 *anycast = NULL; + /* Begin a DHCP transaction on the interface */ + if (priv->dhcp_anycast_address) anycast = priv->dhcp_anycast_address->data; - /* Begin a DHCP transaction on the interface */ - nm_device_set_use_dhcp (self, TRUE); + /* Clear old exported DHCP options */ + if (priv->dhcp4_config) + g_object_unref (priv->dhcp4_config); + priv->dhcp4_config = nm_dhcp4_config_new (); /* DHCP manager will cancel any transaction already in progress and we do not want to cancel this activation if we get "down" state from that. */ @@ -1431,7 +1430,8 @@ real_act_stage4_get_ip4_config (NMDevice *self, s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (nm_device_get_use_dhcp (self)) { + if (priv->dhcp4_client) { + /* DHCP */ *config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); if (*config) { /* Merge user-defined overrides into the IP4Config to be applied */ @@ -1447,6 +1447,7 @@ real_act_stage4_get_ip4_config (NMDevice *self, } else *reason = NM_DEVICE_STATE_REASON_DHCP_ERROR; } else { + /* Not DHCP */ const char *method; g_assert (s_ip4); @@ -2102,6 +2103,37 @@ delayed_transitions_clear (NMDevice *self) } } +static void +dhcp4_cleanup (NMDevice *self, gboolean stop) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->dhcp4_config) { + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); + g_object_unref (priv->dhcp4_config); + priv->dhcp4_config = NULL; + } + + if (priv->dhcp4_client) { + /* Stop any ongoing DHCP transaction on this device */ + if (priv->dhcp_state_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_state_sigid); + priv->dhcp_state_sigid = 0; + } + + if (priv->dhcp_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_timeout_sigid); + priv->dhcp_timeout_sigid = 0; + } + + if (stop) + nm_dhcp_client_stop (priv->dhcp4_client); + + g_object_unref (priv->dhcp4_client); + priv->dhcp4_client = NULL; + } +} + /* * nm_device_deactivate_quickly * @@ -2127,12 +2159,9 @@ nm_device_deactivate_quickly (NMDevice *self) delayed_transitions_clear (self); if (nm_device_get_act_request (self)) { - if (nm_device_get_use_dhcp (self)) { - nm_device_set_use_dhcp (self, FALSE); + dhcp4_cleanup (self, TRUE); - /* Notify of invalid DHCP4 config */ - g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); - } else if (priv->dnsmasq_manager) { + if (priv->dnsmasq_manager) { /* Or any shared connection */ if (priv->dnsmasq_state_id) { g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); @@ -2362,64 +2391,6 @@ nm_device_can_interrupt_activation (NMDevice *self) /* IP Configuration stuff */ -gboolean -nm_device_get_use_dhcp (NMDevice *self) -{ - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - return NM_DEVICE_GET_PRIVATE (self)->dhcp_manager ? TRUE : FALSE; -} - -void -nm_device_set_use_dhcp (NMDevice *self, - gboolean use_dhcp) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (use_dhcp) { - /* New exported DHCP4 config */ - if (priv->dhcp4_config) - g_object_unref (priv->dhcp4_config); - priv->dhcp4_config = nm_dhcp4_config_new (); - - if (!priv->dhcp_manager) - priv->dhcp_manager = nm_dhcp_manager_get (); - } else { - if (priv->dhcp4_config) { - g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); - g_object_unref (priv->dhcp4_config); - priv->dhcp4_config = NULL; - } - - if (priv->dhcp4_client) { - /* Stop any ongoing DHCP transaction on this device */ - if (priv->dhcp_state_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_state_sigid); - priv->dhcp_state_sigid = 0; - } - - if (priv->dhcp_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_timeout_sigid); - priv->dhcp_timeout_sigid = 0; - } - - nm_dhcp_client_stop (priv->dhcp4_client); - - g_object_unref (priv->dhcp4_client); - priv->dhcp4_client = NULL; - } - - if (priv->dhcp_manager) { - g_object_unref (priv->dhcp_manager); - priv->dhcp_manager = NULL; - } - } -} - NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *self) { @@ -2766,12 +2737,15 @@ dispose (GObject *object) /* Clear any delayed transitions */ delayed_transitions_clear (self); + /* Clean up and stop DHCP */ + dhcp4_cleanup (self, take_down); + + /* Take the device itself down and clear it's IPv4 configuration */ if (priv->managed && take_down) { NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); nm_device_set_ip4_config (self, NULL, FALSE, &ignored); - nm_device_set_use_dhcp (self, FALSE); } clear_act_request (self); @@ -2802,6 +2776,8 @@ finalize (GObject *object) NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + g_object_unref (priv->dhcp_manager); + g_free (priv->udi); g_free (priv->iface); g_free (priv->ip_iface); @@ -2892,7 +2868,7 @@ get_property (GObject *object, guint prop_id, break; case NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG: if ( ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) - && nm_device_get_use_dhcp (self)) + && priv->dhcp4_client) g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config)); else g_value_set_boxed (value, "/"); diff --git a/src/nm-device.h b/src/nm-device.h index b81c460d6e..6300be535f 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -140,9 +140,6 @@ int nm_device_get_priority (NMDevice *dev); guint32 nm_device_get_ip4_address (NMDevice *dev); void nm_device_update_ip4_address (NMDevice *dev); -gboolean nm_device_get_use_dhcp (NMDevice *dev); -void nm_device_set_use_dhcp (NMDevice *dev, - gboolean use_dhcp); NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev); NMIP4Config * nm_device_get_ip4_config (NMDevice *dev); From 9e805187cdc49acc8f675f36dc6ae1bdbf8d4389 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Jan 2010 18:06:05 -0800 Subject: [PATCH 11/20] core: rename some DHCPv4 specific variables --- src/nm-device.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/nm-device.c b/src/nm-device.c index 3c4b81c9cf..4f4a43d87c 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -102,15 +102,16 @@ typedef struct { gboolean ip4_ready; gboolean ip6_ready; + /* Generic DHCP stuff */ NMDHCPManager * dhcp_manager; + guint32 dhcp_timeout; + GByteArray * dhcp_anycast_address; /* IP4 configuration info */ NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ NMDHCPClient * dhcp4_client; - guint32 dhcp_timeout; - gulong dhcp_state_sigid; - gulong dhcp_timeout_sigid; - GByteArray * dhcp_anycast_address; + gulong dhcp4_state_sigid; + gulong dhcp4_timeout_sigid; NMDHCP4Config * dhcp4_config; /* dnsmasq stuff for shared connections */ @@ -1221,14 +1222,14 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) priv->dhcp_timeout, anycast); if (priv->dhcp4_client) { - priv->dhcp_state_sigid = g_signal_connect (priv->dhcp4_client, - "state-changed", - G_CALLBACK (dhcp_state_changed), - self); - priv->dhcp_timeout_sigid = g_signal_connect (priv->dhcp4_client, - "timeout", - G_CALLBACK (dhcp_timeout), - self); + priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client, + "state-changed", + G_CALLBACK (dhcp_state_changed), + self); + priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client, + "timeout", + G_CALLBACK (dhcp_timeout), + self); /* DHCP devices will be notified by the DHCP manager when * stuff happens. @@ -2116,14 +2117,14 @@ dhcp4_cleanup (NMDevice *self, gboolean stop) if (priv->dhcp4_client) { /* Stop any ongoing DHCP transaction on this device */ - if (priv->dhcp_state_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_state_sigid); - priv->dhcp_state_sigid = 0; + if (priv->dhcp4_state_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid); + priv->dhcp4_state_sigid = 0; } - if (priv->dhcp_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp_timeout_sigid); - priv->dhcp_timeout_sigid = 0; + if (priv->dhcp4_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid); + priv->dhcp4_timeout_sigid = 0; } if (stop) From 439768db3d5079cf0473c7af1ef0aee1c95fbe57 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Jan 2010 22:30:40 -0800 Subject: [PATCH 12/20] dhcp: add the NMDHCP6Config object --- introspection/Makefile.am | 1 + introspection/all.xml | 1 + introspection/nm-dhcp6-config.xml | 20 ++++ src/Makefile.am | 8 +- src/nm-dhcp6-config.c | 192 ++++++++++++++++++++++++++++++ src/nm-dhcp6-config.h | 61 ++++++++++ 6 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 introspection/nm-dhcp6-config.xml create mode 100644 src/nm-dhcp6-config.c create mode 100644 src/nm-dhcp6-config.h diff --git a/introspection/Makefile.am b/introspection/Makefile.am index 0dc286a9cd..a70d28511a 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -23,5 +23,6 @@ EXTRA_DIST = \ nm-vpn-connection.xml \ nm-ppp-manager.xml \ nm-active-connection.xml \ + nm-dhcp4-config.xml \ nm-dhcp4-config.xml diff --git a/introspection/all.xml b/introspection/all.xml index 67f1e54216..d6b2f23674 100644 --- a/introspection/all.xml +++ b/introspection/all.xml @@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + diff --git a/introspection/nm-dhcp6-config.xml b/introspection/nm-dhcp6-config.xml new file mode 100644 index 0000000000..93b0f1c8f0 --- /dev/null +++ b/introspection/nm-dhcp6-config.xml @@ -0,0 +1,20 @@ + + + + + + Options and configuration returned by the IPv6 DHCP server. + + + Configuration options returned by a DHCP server, if any. + + + + + + A dictionary mapping property names to variant boxed values + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 60aa93ee15..7111ac3e5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,8 @@ NetworkManager_SOURCES = \ nm-netlink.h \ nm-dhcp4-config.c \ nm-dhcp4-config.h \ + nm-dhcp6-config.c \ + nm-dhcp6-config.h \ nm-rfkill.h nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml @@ -149,6 +151,9 @@ nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xm nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $< +nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml + dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $< + BUILT_SOURCES = \ nm-access-point-glue.h \ nm-manager-glue.h \ @@ -160,7 +165,8 @@ BUILT_SOURCES = \ nm-ip4-config-glue.h \ nm-ip6-config-glue.h \ nm-active-connection-glue.h \ - nm-dhcp4-config-glue.h + nm-dhcp4-config-glue.h \ + nm-dhcp6-config-glue.h NetworkManager_CPPFLAGS = \ $(DBUS_CFLAGS) \ diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c new file mode 100644 index 0000000000..fb6ccce50a --- /dev/null +++ b/src/nm-dhcp6-config.c @@ -0,0 +1,192 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#include +#include + +#include "NetworkManager.h" +#include "nm-dbus-manager.h" +#include "nm-dhcp6-config.h" +#include "nm-dhcp6-config-glue.h" +#include "nm-dbus-glib-types.h" +#include "nm-properties-changed-signal.h" +#include "nm-utils.h" + + +G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, G_TYPE_OBJECT) + +#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate)) + +typedef struct { + char *dbus_path; + GHashTable *options; +} NMDHCP6ConfigPrivate; + + +enum { + PROP_0, + PROP_OPTIONS, + + LAST_PROP +}; + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMDHCP6Config * +nm_dhcp6_config_new (void) +{ + return NM_DHCP6_CONFIG (g_object_new (NM_TYPE_DHCP6_CONFIG, NULL)); +} + +void +nm_dhcp6_config_add_option (NMDHCP6Config *self, + const char *key, + const char *option) +{ + GValue *svalue; + + g_return_if_fail (NM_IS_DHCP6_CONFIG (self)); + g_return_if_fail (key != NULL); + g_return_if_fail (option != NULL); + + svalue = g_slice_new0 (GValue); + g_value_init (svalue, G_TYPE_STRING); + g_value_set_string (svalue, option); + g_hash_table_insert (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, g_strdup (key), svalue); + g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS); +} + +void +nm_dhcp6_config_reset (NMDHCP6Config *self) +{ + g_return_if_fail (NM_IS_DHCP6_CONFIG (self)); + + g_hash_table_remove_all (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options); + g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS); +} + +const char * +nm_dhcp6_config_get_option (NMDHCP6Config *self, const char *key) +{ + GValue *value; + + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL); + g_return_val_if_fail (key != NULL, NULL); + + value = g_hash_table_lookup (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, key); + return value ? g_value_get_string (value) : NULL; +} + +const char * +nm_dhcp6_config_get_dbus_path (NMDHCP6Config *self) +{ + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL); + + return NM_DHCP6_CONFIG_GET_PRIVATE (self)->dbus_path; +} + +static void +nm_gvalue_destroy (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static void +nm_dhcp6_config_init (NMDHCP6Config *self) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (self); + static guint32 counter = 0; + DBusGConnection *connection; + NMDBusManager *dbus_mgr; + + dbus_mgr = nm_dbus_manager_get (); + connection = nm_dbus_manager_get_connection (dbus_mgr); + priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/DHCP6Config/%d", counter++); + dbus_g_connection_register_g_object (connection, priv->dbus_path, G_OBJECT (self)); + g_object_unref (dbus_mgr); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy); +} + +static void +finalize (GObject *object) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + g_free (priv->dbus_path); + g_hash_table_destroy (priv->options); + + G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_OPTIONS: + g_value_set_boxed (value, priv->options); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_OPTIONS, + g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS, + "Options", + "DHCP configuration options returned by the server", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_PARAM_READABLE)); + + /* Signals */ + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMDHCP6ConfigClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class), + &dbus_glib_nm_dhcp6_config_object_info); +} diff --git a/src/nm-dhcp6-config.h b/src/nm-dhcp6-config.h new file mode 100644 index 0000000000..90eb10ffb5 --- /dev/null +++ b/src/nm-dhcp6-config.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef NM_DHCP6_CONFIG_H +#define NM_DHCP6_CONFIG_H + +#include +#include + +#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type ()) +#define NM_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config)) +#define NM_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass)) +#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_DHCP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass)) + +typedef struct { + GObject parent; +} NMDHCP6Config; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*properties_changed) (NMDHCP6Config *config, GHashTable *properties); +} NMDHCP6ConfigClass; + +#define NM_DHCP6_CONFIG_OPTIONS "options" + +GType nm_dhcp6_config_get_type (void); + +NMDHCP6Config *nm_dhcp6_config_new (void); + +const char *nm_dhcp6_config_get_dbus_path (NMDHCP6Config *config); + +void nm_dhcp6_config_add_option (NMDHCP6Config *config, + const char *key, + const char *option); + +void nm_dhcp6_config_reset (NMDHCP6Config *config); + +const char *nm_dhcp6_config_get_option (NMDHCP6Config *config, const char *option); + +#endif /* NM_DHCP6_CONFIG_H */ From f094e0ad9df39030cacb14371c58af4b4d4ae5ce Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 14 Jan 2010 00:39:58 -0800 Subject: [PATCH 13/20] dhcp: dhclient leasefile and pidfile fixes for IPv6 Don't use the same lease and pid files for IPv6 as for IPv4. --- src/dhcp-manager/nm-dhcp-dhclient.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 899410211f..2ab6aba825 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -61,10 +61,11 @@ typedef struct { static char * -get_leasefile_for_iface (const char * iface, const char *uuid) +get_leasefile_for_iface (const char * iface, const char *uuid, gboolean ipv6) { - return g_strdup_printf ("%s/dhclient-%s-%s.lease", + return g_strdup_printf ("%s/dhclient%s-%s-%s.lease", NM_DHCLIENT_LEASE_DIR, + ipv6 ? "6" : "", uuid, iface); } @@ -118,7 +119,7 @@ nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid) char **line, **split = NULL; GHashTable *hash = NULL; - leasefile = get_leasefile_for_iface (iface, uuid); + leasefile = get_leasefile_for_iface (iface, uuid, FALSE); if (!leasefile) return NULL; @@ -456,14 +457,18 @@ dhclient_start (NMDHCPClient *client, GError *error = NULL; const char *iface, *uuid; char *binary_name; + gboolean ipv6; g_return_val_if_fail (priv->pid_file == NULL, -1); g_return_val_if_fail (ip_opt != NULL, -1); iface = nm_dhcp_client_get_iface (client); uuid = nm_dhcp_client_get_uuid (client); + ipv6 = nm_dhcp_client_get_ipv6 (client); - priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient-%s.pid", iface); + priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid", + ipv6 ? "6" : "", + iface); if (!priv->pid_file) { nm_warning ("%s: not enough memory for dhcpcd options.", iface); return -1; @@ -479,7 +484,7 @@ dhclient_start (NMDHCPClient *client, nm_dhcp_client_stop_existing (priv->pid_file, binary_name); g_free (binary_name); - priv->lease_file = get_leasefile_for_iface (iface, uuid); + priv->lease_file = get_leasefile_for_iface (iface, uuid, ipv6); if (!priv->lease_file) { nm_warning ("%s: not enough memory for dhclient options.", iface); return -1; @@ -501,8 +506,10 @@ dhclient_start (NMDHCPClient *client, g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ g_ptr_array_add (argv, (gpointer) priv->lease_file); - g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ - g_ptr_array_add (argv, (gpointer) priv->conf_file); + if (priv->conf_file) { + g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ + g_ptr_array_add (argv, (gpointer) priv->conf_file); + } g_ptr_array_add (argv, (gpointer) iface); g_ptr_array_add (argv, NULL); From a9e32f3c3b55a0d6ab3e5ce4baa8dc4977d75301 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 14 Jan 2010 00:45:10 -0800 Subject: [PATCH 14/20] dhcp: add initial DHCPv6 support (managed mode only) --- src/Makefile.am | 1 + src/dhcp-manager/nm-dhcp-client.c | 147 +++++++++- src/dhcp-manager/nm-dhcp-client.h | 10 +- src/nm-device-interface.c | 8 + src/nm-device-interface.h | 2 + src/nm-device.c | 449 +++++++++++++++++++++--------- src/nm-device.h | 2 + 7 files changed, 462 insertions(+), 157 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 7111ac3e5d..db255ca357 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,7 @@ noinst_LTLIBRARIES = libtest-dhcp.la libtest_dhcp_la_SOURCES = \ nm-ip4-config.c \ + nm-ip6-config.c \ nm-hostname-provider.c \ nm-dbus-manager.c diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 382b7d4a91..03373d7172 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -375,6 +375,7 @@ typedef struct { static DhcState state_table[] = { { DHC_NBI, "nbi" }, { DHC_PREINIT, "preinit" }, + { DHC_PREINIT6,"preinit6" }, { DHC_BOUND4, "bound" }, { DHC_BOUND6, "bound6" }, { DHC_IPV4LL, "ipv4ll" }, @@ -507,7 +508,8 @@ nm_dhcp_client_new_options (NMDHCPClient *self, } priv->state = new_state; - g_message ("DHCP: device %s state changed %s -> %s", + g_message ("DHCPv%c: device %s state changed %s -> %s", + priv->ipv6 ? '6' : '4', priv->iface, state_to_string (old_state), state_to_string (priv->state)); @@ -524,14 +526,14 @@ nm_dhcp_client_new_options (NMDHCPClient *self, typedef struct { GHFunc func; gpointer user_data; -} Dhcp4ForeachInfo; +} DhcpForeachInfo; static void -iterate_dhcp4_config_option (gpointer key, - gpointer value, - gpointer user_data) +iterate_dhcp_config_option (gpointer key, + gpointer value, + gpointer user_data) { - Dhcp4ForeachInfo *info = (Dhcp4ForeachInfo *) user_data; + DhcpForeachInfo *info = (DhcpForeachInfo *) user_data; char *tmp_key = NULL; const char **p; static const char *filter_options[] = { @@ -557,12 +559,12 @@ iterate_dhcp4_config_option (gpointer key, } gboolean -nm_dhcp_client_foreach_dhcp4_option (NMDHCPClient *self, - GHFunc func, - gpointer user_data) +nm_dhcp_client_foreach_option (NMDHCPClient *self, + GHFunc func, + gpointer user_data) { NMDHCPClientPrivate *priv; - Dhcp4ForeachInfo info = { NULL, NULL }; + DhcpForeachInfo info = { NULL, NULL }; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); @@ -577,7 +579,7 @@ nm_dhcp_client_foreach_dhcp4_option (NMDHCPClient *self, info.func = func; info.user_data = user_data; - g_hash_table_foreach (priv->options, iterate_dhcp4_config_option, &info); + g_hash_table_foreach (priv->options, iterate_dhcp_config_option, &info); return TRUE; } @@ -629,14 +631,14 @@ out: } static void -process_domain_search (NMIP4Config *ip4_config, const char *str) +process_domain_search (const char *str, GFunc add_func, gpointer user_data) { char **searches, **s; char *unescaped, *p; int i; g_return_if_fail (str != NULL); - g_return_if_fail (ip4_config != NULL); + g_return_if_fail (add_func != NULL); p = unescaped = g_strdup (str); do { @@ -658,7 +660,7 @@ process_domain_search (NMIP4Config *ip4_config, const char *str) for (s = searches; *s; s++) { if (strlen (*s)) { g_message (" domain search '%s'", *s); - nm_ip4_config_add_search (ip4_config, *s); + add_func (*s, user_data); } } g_strfreev (searches); @@ -667,6 +669,12 @@ out: g_free (unescaped); } +static void +ip4_add_domain_search (gpointer data, gpointer user_data) +{ + nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data); +} + /* Given a table of DHCP options from the client, convert into an IP4Config */ static NMIP4Config * ip4_options_to_config (NMDHCPClient *self) @@ -789,7 +797,7 @@ ip4_options_to_config (NMDHCPClient *self) str = g_hash_table_lookup (priv->options, "new_domain_search"); if (str) - process_domain_search (ip4_config, str); + process_domain_search (str, ip4_add_domain_search, ip4_config); str = g_hash_table_lookup (priv->options, "new_netbios_name_servers"); if (str) { @@ -848,6 +856,115 @@ nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test) /********************************************/ +static void +ip6_add_domain_search (gpointer data, gpointer user_data) +{ + nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data); +} + +/* Given a table of DHCP options from the client, convert into an IP6Config */ +static NMIP6Config * +ip6_options_to_config (NMDHCPClient *self) +{ + NMDHCPClientPrivate *priv; + NMIP6Config *ip6_config = NULL; + struct in6_addr tmp_addr; + NMIP6Address *addr = NULL; + char *str = NULL; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->options != NULL, NULL); + + ip6_config = nm_ip6_config_new (); + if (!ip6_config) { + g_warning ("%s: couldn't allocate memory for an IP6Config!", priv->iface); + return NULL; + } + + addr = nm_ip6_address_new (); + if (!addr) { + g_warning ("%s: couldn't allocate memory for an IP6 Address!", priv->iface); + goto error; + } + + str = g_hash_table_lookup (priv->options, "new_ip6_address"); + if (str && (inet_pton (AF_INET6, str, &tmp_addr) > 0)) { + nm_ip6_address_set_address (addr, &tmp_addr); + g_message (" address %s", str); + } else + goto error; + + str = g_hash_table_lookup (priv->options, "new_ip6_prefixlen"); + if (str) { + long unsigned int prefix; + + errno = 0; + prefix = strtoul (str, NULL, 10); + if (errno != 0 || prefix > 128) + goto error; + + nm_ip6_address_set_prefix (addr, (guint32) prefix); + g_message (" prefix %lu", prefix); + } + + nm_ip6_config_take_address (ip6_config, addr); + addr = NULL; + + str = g_hash_table_lookup (priv->options, "new_host_name"); + if (str) + g_message (" hostname '%s'", str); + + str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers"); + if (str) { + char **searches = g_strsplit (str, " ", 0); + char **s; + + for (s = searches; *s; s++) { + if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { + nm_ip6_config_add_nameserver (ip6_config, &tmp_addr); + g_message (" nameserver '%s'", *s); + } else + g_warning ("Ignoring invalid nameserver '%s'", *s); + } + g_strfreev (searches); + } + + str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search"); + if (str) + process_domain_search (str, ip6_add_domain_search, ip6_config); + + return ip6_config; + +error: + if (addr) + nm_ip6_address_unref (addr); + g_object_unref (ip6_config); + return NULL; +} + +NMIP6Config * +nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test) +{ + NMDHCPClientPrivate *priv; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + + if (test && !state_is_bound (priv->state)) { + g_warning ("%s: dhcp client didn't bind to a lease.", priv->iface); + return NULL; + } + + return ip6_options_to_config (self); +} + +/********************************************/ + static void nm_dhcp_client_init (NMDHCPClient *self) { diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index 8b5804f418..50c49bbc8a 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -25,6 +25,7 @@ #include #include #include +#include #define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type ()) #define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClient)) @@ -41,6 +42,7 @@ typedef enum { DHC_NBI = 0, /* no broadcast interfaces found */ DHC_PREINIT, /* configuration started */ + DHC_PREINIT6, /* configuration started */ DHC_BOUND4, /* IPv4 lease obtained */ DHC_BOUND6, /* IPv6 lease obtained */ DHC_IPV4LL, /* IPv4LL address obtained */ @@ -119,12 +121,14 @@ void nm_dhcp_client_new_options (NMDHCPClient *self, GHashTable *options, const char *reason); -gboolean nm_dhcp_client_foreach_dhcp4_option (NMDHCPClient *self, - GHFunc func, - gpointer user_data); +gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self, + GHFunc func, + gpointer user_data); NMIP4Config *nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test); +NMIP6Config *nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test); + /* Backend helpers */ void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name); diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c index 70cce8b624..f0a4fd1c6b 100644 --- a/src/nm-device-interface.c +++ b/src/nm-device-interface.c @@ -137,6 +137,14 @@ nm_device_interface_init (gpointer g_iface) DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READWRITE)); + g_object_interface_install_property + (g_iface, + g_param_spec_boxed (NM_DEVICE_INTERFACE_DHCP6_CONFIG, + "DHCP6 Config", + "DHCP6 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + g_object_interface_install_property (g_iface, g_param_spec_uint (NM_DEVICE_INTERFACE_STATE, diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h index 0ec5e3da14..306f2dbe6d 100644 --- a/src/nm-device-interface.h +++ b/src/nm-device-interface.h @@ -53,6 +53,7 @@ typedef enum #define NM_DEVICE_INTERFACE_IP4_CONFIG "ip4-config" #define NM_DEVICE_INTERFACE_DHCP4_CONFIG "dhcp4-config" #define NM_DEVICE_INTERFACE_IP6_CONFIG "ip6-config" +#define NM_DEVICE_INTERFACE_DHCP6_CONFIG "dhcp6-config" #define NM_DEVICE_INTERFACE_STATE "state" #define NM_DEVICE_INTERFACE_DEVICE_TYPE "device-type" /* ugh */ #define NM_DEVICE_INTERFACE_MANAGED "managed" @@ -69,6 +70,7 @@ typedef enum { NM_DEVICE_INTERFACE_PROP_IP4_CONFIG, NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG, NM_DEVICE_INTERFACE_PROP_IP6_CONFIG, + NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG, NM_DEVICE_INTERFACE_PROP_STATE, NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE, NM_DEVICE_INTERFACE_PROP_MANAGED, diff --git a/src/nm-device.c b/src/nm-device.c index 4f4a43d87c..daf3d111cd 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -131,6 +131,11 @@ typedef struct { gulong ip6_config_changed_sigid; gboolean ip6_waiting_for_config; + NMDHCPClient * dhcp6_client; + gulong dhcp6_state_sigid; + gulong dhcp6_timeout_sigid; + NMDHCP6Config * dhcp6_config; + /* inhibit autoconnect feature */ gboolean autoconnect_inhibit; } NMDevicePrivate; @@ -566,49 +571,57 @@ ip6_config_changed (NMIP6Manager *ip6_manager, nm_device_activate_schedule_stage4_ip6_config_get (self); } -static void -nm_device_setup_ip6 (NMDevice *self) +static gboolean +ip6_method_matches (NMConnection *connection, const char *match) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActRequest *req; - NMConnection *connection; - const char *ip_iface, *method = NULL; NMSettingIP6Config *s_ip6; - - req = nm_device_get_act_request (self); - if (!req) - return; - connection = nm_act_request_get_connection (req); - if (!connection) - return; + const char *method = NULL; s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); if (s_ip6) method = nm_setting_ip6_config_get_method (s_ip6); - if (!method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) + return method && !strcmp (method, match); +} + +static void +addrconf6_setup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + const char *ip_iface; + NMSettingIP6Config *s_ip6; + + priv->ip6_waiting_for_config = FALSE; + + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if (!ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) return; if (!priv->ip6_manager) { priv->ip6_manager = nm_ip6_manager_get (); priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager, - "addrconf-complete", - G_CALLBACK (ip6_addrconf_complete), - self); + "addrconf-complete", + G_CALLBACK (ip6_addrconf_complete), + self); priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager, - "config-changed", - G_CALLBACK (ip6_config_changed), - self); + "config-changed", + G_CALLBACK (ip6_config_changed), + self); } - priv->ip6_waiting_for_config = FALSE; - ip_iface = nm_device_get_ip_iface (self); + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); nm_ip6_manager_prepare_interface (priv->ip6_manager, ip_iface, s_ip6); } static void -nm_device_cleanup_ip6 (NMDevice *self) +addrconf6_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -630,6 +643,13 @@ nm_device_cleanup_ip6 (NMDevice *self) priv->ip6_manager = NULL; } +static NMActStageReturn +real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) +{ + addrconf6_setup (self); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + /* * nm_device_activate_stage1_device_prepare * @@ -651,8 +671,6 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) 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); - nm_device_setup_ip6 (self); - ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { goto out; @@ -692,13 +710,6 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) nm_device_get_iface (self)); } -static NMActStageReturn -real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) -{ - /* Nothing to do */ - return NM_ACT_STAGE_RETURN_SUCCESS; -} - static NMActStageReturn real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) { @@ -1071,42 +1082,76 @@ dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) } static void -handle_dhcp_lease_change (NMDevice *device) +dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data) +{ + nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data), + (const char *) key, + (const char *) value); +} + +static void +handle_dhcp_lease_change (NMDevice *device, gboolean ipv6) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMIP4Config *config; + NMIP4Config *ip4_config; NMSettingIP4Config *s_ip4; + NMIP6Config *ip6_config; + NMSettingIP6Config *s_ip6; NMConnection *connection; NMActRequest *req; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; gboolean assumed; - config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); - if (!config) { - nm_warning ("failed to get DHCP config for rebind"); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - return; - } - req = nm_device_get_act_request (device); g_assert (req); connection = nm_act_request_get_connection (req); g_assert (connection); - - s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); - nm_utils_merge_ip4_config (config, s_ip4); - - g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config); - assumed = nm_act_request_get_assumed (req); - if (nm_device_set_ip4_config (device, config, assumed, &reason)) { - nm_dhcp4_config_reset (priv->dhcp4_config); - nm_dhcp_client_foreach_dhcp4_option (priv->dhcp4_client, - dhcp4_add_option_cb, - priv->dhcp4_config); + + if (ipv6) { + ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE); + if (!ip6_config) { + nm_warning ("failed to get DHCPv6 config for rebind"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + return; + } + + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG)); + nm_utils_merge_ip6_config (ip6_config, s_ip6); + + g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP6_CONFIG, ip6_config); + + if (nm_device_set_ip6_config (device, ip6_config, assumed, &reason)) { + nm_dhcp6_config_reset (priv->dhcp6_config); + nm_dhcp_client_foreach_option (priv->dhcp6_client, + dhcp6_add_option_cb, + priv->dhcp6_config); + } else { + nm_warning ("Failed to update IPv6 config in response to DHCP event."); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + } } else { - nm_warning ("Failed to update IP4 config in response to DHCP event."); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + ip4_config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); + if (!ip4_config) { + nm_warning ("failed to get DHCPv4 config for rebind"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + return; + } + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + nm_utils_merge_ip4_config (ip4_config, s_ip4); + + g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, ip4_config); + + if (nm_device_set_ip4_config (device, ip4_config, assumed, &reason)) { + nm_dhcp4_config_reset (priv->dhcp4_config); + nm_dhcp_client_foreach_option (priv->dhcp4_client, + dhcp4_add_option_cb, + priv->dhcp4_config); + } else { + nm_warning ("Failed to update IPv4 config in response to DHCP event."); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + } } } @@ -1118,32 +1163,45 @@ dhcp_state_changed (NMDHCPClient *client, NMDevice *device = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); NMDeviceState dev_state; + gboolean ipv6; - if (!nm_device_get_act_request (device)) - return; - + ipv6 = nm_dhcp_client_get_ipv6 (client); dev_state = nm_device_get_state (device); switch (state) { case DHC_BOUND4: /* lease obtained */ + case DHC_BOUND6: case DHC_RENEW4: /* lease renewed */ + case DHC_RENEW6: /* lease renewed */ case DHC_REBOOT: /* have valid lease, but now obtained a different one */ case DHC_REBIND4: /* new, different lease */ - if (dev_state == NM_DEVICE_STATE_IP_CONFIG) - nm_device_activate_schedule_stage4_ip4_config_get (device); - else if (dev_state == NM_DEVICE_STATE_ACTIVATED) - handle_dhcp_lease_change (device); + case DHC_REBIND6: /* new, different lease */ + if (dev_state == NM_DEVICE_STATE_IP_CONFIG) { + if (ipv6) + nm_device_activate_schedule_stage4_ip6_config_get (device); + else + nm_device_activate_schedule_stage4_ip4_config_get (device); + } else if (dev_state == NM_DEVICE_STATE_ACTIVATED) + handle_dhcp_lease_change (device, ipv6); break; case DHC_TIMEOUT: /* timed out contacting DHCP server */ - nm_dhcp4_config_reset (priv->dhcp4_config); - - if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_activate_schedule_stage4_ip4_config_timeout (device); + if (ipv6) { + nm_dhcp6_config_reset (priv->dhcp6_config); + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip6_config_timeout (device); + } else { + nm_dhcp4_config_reset (priv->dhcp4_config); + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage4_ip4_config_timeout (device); + } break; case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ case DHC_ABEND: /* dhclient exited abnormally */ case DHC_END: /* dhclient exited normally */ - nm_dhcp4_config_reset (priv->dhcp4_config); + if (ipv6) + nm_dhcp6_config_reset (priv->dhcp6_config); + else + nm_dhcp4_config_reset (priv->dhcp4_config); /* dhclient quit and can't get/renew a lease; so kill the connection */ if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) @@ -1164,8 +1222,12 @@ dhcp_timeout (NMDHCPClient *client, gpointer user_data) if (!nm_device_get_act_request (device)) return; - if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_activate_schedule_stage4_ip4_config_timeout (device); + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + if (nm_dhcp_client_get_ipv6 (client)) + nm_device_activate_schedule_stage4_ip6_config_timeout (device); + else + nm_device_activate_schedule_stage4_ip4_config_timeout (device); + } } static NMActStageReturn @@ -1213,8 +1275,6 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) g_object_unref (priv->dhcp4_config); priv->dhcp4_config = nm_dhcp4_config_new (); - /* DHCP manager will cancel any transaction already in progress and we do not - want to cancel this activation if we get "down" state from that. */ priv->dhcp4_client = nm_dhcp_manager_start_ip4 (priv->dhcp_manager, ip_iface, uuid, @@ -1265,20 +1325,70 @@ static NMActStageReturn real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ip_iface = nm_device_get_ip_iface (self); + const char *ip_iface, *uuid; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMActRequest *req; + NMConnection *connection; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); - /* If we are ignoring IPv6 on this interface then we can go right - * to stage 4. - */ - if (!priv->ip6_manager) - return NM_ACT_STAGE_RETURN_SUCCESS; + req = nm_device_get_act_request (self); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); - priv->ip6_waiting_for_config = TRUE; - nm_ip6_manager_begin_addrconf (priv->ip6_manager, ip_iface); + ip_iface = nm_device_get_ip_iface (self); - return NM_ACT_STAGE_RETURN_POSTPONE; + if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + 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; + } + } + + return ret; } @@ -1439,9 +1549,9 @@ real_act_stage4_get_ip4_config (NMDevice *self, nm_utils_merge_ip4_config (*config, s_ip4); nm_dhcp4_config_reset (priv->dhcp4_config); - nm_dhcp_client_foreach_dhcp4_option (priv->dhcp4_client, - dhcp4_add_option_cb, - priv->dhcp4_config); + nm_dhcp_client_foreach_option (priv->dhcp4_client, + dhcp4_add_option_cb, + priv->dhcp4_config); /* Notify of new DHCP4 config */ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG); @@ -1632,11 +1742,11 @@ real_act_stage4_get_ip6_config (NMDevice *self, NMIP6Config **config, NMDeviceStateReason *reason) { - NMDevicePrivate *priv; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMConnection *connection; NMSettingIP6Config *s_ip6; const char *ip_iface; - const char *method = NULL; g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -1649,25 +1759,41 @@ real_act_stage4_get_ip6_config (NMDevice *self, g_assert (connection); s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (s_ip6) - method = nm_setting_ip6_config_get_method (s_ip6); - if (!method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, ip_iface); + if (*config) { + /* Merge user-defined overrides into the IP6Config to be applied */ + nm_utils_merge_ip6_config (*config, s_ip6); + ret = NM_ACT_STAGE_RETURN_SUCCESS; + } else { + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + } + } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + g_assert (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 */ + nm_utils_merge_ip6_config (*config, s_ip6); + + nm_dhcp6_config_reset (priv->dhcp6_config); + nm_dhcp_client_foreach_option (priv->dhcp6_client, + dhcp6_add_option_cb, + priv->dhcp6_config); + + /* Notify of new DHCP4 config */ + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG); + ret = NM_ACT_STAGE_RETURN_SUCCESS; + } else { + *reason = NM_DEVICE_STATE_REASON_DHCP_ERROR; + } + } else { *config = NULL; - return NM_ACT_STAGE_RETURN_SUCCESS; + ret = NM_ACT_STAGE_RETURN_SUCCESS; } - priv = NM_DEVICE_GET_PRIVATE (self); - *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, ip_iface); - if (!*config) { - *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; - return NM_ACT_STAGE_RETURN_FAILURE; - } - - /* Merge user-defined overrides into the IP6Config to be applied */ - nm_utils_merge_ip6_config (*config, s_ip6); - - return NM_ACT_STAGE_RETURN_SUCCESS; + return ret; } /* @@ -1743,6 +1869,9 @@ real_act_stage4_ip6_config_timeout (NMDevice *self, g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); + /* Notify of invalid DHCP6 config object */ + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG); + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; return NM_ACT_STAGE_RETURN_FAILURE; } @@ -2135,6 +2264,54 @@ dhcp4_cleanup (NMDevice *self, gboolean stop) } } +static void +dhcp6_cleanup (NMDevice *self, gboolean stop) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->dhcp6_config) { + g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG); + g_object_unref (priv->dhcp6_config); + priv->dhcp6_config = NULL; + } + + if (priv->dhcp6_client) { + if (priv->dhcp6_state_sigid) { + g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid); + priv->dhcp6_state_sigid = 0; + } + + if (priv->dhcp6_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid); + priv->dhcp6_timeout_sigid = 0; + } + + if (stop) + nm_dhcp_client_stop (priv->dhcp6_client); + + g_object_unref (priv->dhcp6_client); + priv->dhcp6_client = NULL; + } +} + +static void +dnsmasq_cleanup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->dnsmasq_manager) + return; + + if (priv->dnsmasq_state_id) { + g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); + priv->dnsmasq_state_id = 0; + } + + nm_dnsmasq_manager_stop (priv->dnsmasq_manager); + g_object_unref (priv->dnsmasq_manager); + priv->dnsmasq_manager = NULL; +} + /* * nm_device_deactivate_quickly * @@ -2159,24 +2336,11 @@ nm_device_deactivate_quickly (NMDevice *self) /* Clear any delayed transitions */ delayed_transitions_clear (self); - if (nm_device_get_act_request (self)) { - dhcp4_cleanup (self, TRUE); - - if (priv->dnsmasq_manager) { - /* Or any shared connection */ - if (priv->dnsmasq_state_id) { - g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); - priv->dnsmasq_state_id = 0; - } - - nm_dnsmasq_manager_stop (priv->dnsmasq_manager); - g_object_unref (priv->dnsmasq_manager); - priv->dnsmasq_manager = NULL; - } - } - + dhcp4_cleanup (self, TRUE); + dhcp6_cleanup (self, TRUE); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); aipd_cleanup (self); - nm_device_cleanup_ip6 (self); /* Call device type-specific deactivation */ if (NM_DEVICE_GET_CLASS (self)->deactivate_quickly) @@ -2395,21 +2559,17 @@ nm_device_can_interrupt_activation (NMDevice *self) NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *self) { - NMDevicePrivate *priv; + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->dhcp_manager) - return priv->dhcp4_config; - return NULL; + return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config; } NMIP4Config * nm_device_get_ip4_config (NMDevice *self) { g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); return NM_DEVICE_GET_PRIVATE (self)->ip4_config; } @@ -2579,10 +2739,20 @@ nm_device_set_ip6_config (NMDevice *self, return success; } +NMDHCP6Config * +nm_device_get_dhcp6_config (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config; +} + NMIP6Config * nm_device_get_ip6_config (NMDevice *self) { g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); return NM_DEVICE_GET_PRIVATE (self)->ip6_config; } @@ -2740,8 +2910,11 @@ dispose (GObject *object) /* Clean up and stop DHCP */ dhcp4_cleanup (self, take_down); + dhcp6_cleanup (self, take_down); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); - /* Take the device itself down and clear it's IPv4 configuration */ + /* Take the device itself down and clear its IPv4 configuration */ if (priv->managed && take_down) { NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; @@ -2749,23 +2922,10 @@ dispose (GObject *object) nm_device_set_ip4_config (self, NULL, FALSE, &ignored); } - clear_act_request (self); - activation_source_clear (self, TRUE, AF_INET); activation_source_clear (self, TRUE, AF_INET6); - nm_device_cleanup_ip6 (self); - - if (priv->dnsmasq_manager) { - if (priv->dnsmasq_state_id) { - g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); - priv->dnsmasq_state_id = 0; - } - - nm_dnsmasq_manager_stop (priv->dnsmasq_manager); - g_object_unref (priv->dnsmasq_manager); - priv->dnsmasq_manager = NULL; - } + clear_act_request (self); out: G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); @@ -2883,6 +3043,13 @@ get_property (GObject *object, guint prop_id, } g_value_set_boxed (value, "/"); break; + case NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG: + if ( ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) + && priv->dhcp6_client) + g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config)); + else + g_value_set_boxed (value, "/"); + break; case NM_DEVICE_INTERFACE_PROP_STATE: g_value_set_uint (value, priv->state); break; @@ -2961,6 +3128,10 @@ nm_device_class_init (NMDeviceClass *klass) NM_DEVICE_INTERFACE_PROP_IP6_CONFIG, NM_DEVICE_INTERFACE_IP6_CONFIG); + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG, + NM_DEVICE_INTERFACE_DHCP6_CONFIG); + g_object_class_override_property (object_class, NM_DEVICE_INTERFACE_PROP_STATE, NM_DEVICE_INTERFACE_STATE); diff --git a/src/nm-device.h b/src/nm-device.h index 6300be535f..5fcde5c338 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -31,6 +31,7 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" #include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" #include "nm-connection.h" typedef enum NMActStageReturn @@ -141,6 +142,7 @@ guint32 nm_device_get_ip4_address (NMDevice *dev); void nm_device_update_ip4_address (NMDevice *dev); NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev); +NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *dev); NMIP4Config * nm_device_get_ip4_config (NMDevice *dev); NMIP6Config * nm_device_get_ip6_config (NMDevice *dev); From f9bd97020d429264cbe2b0aadd85566acf6e02f0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 14 Jan 2010 22:57:51 -0800 Subject: [PATCH 15/20] ipv6: push router advertisement flags to listeners --- marshallers/nm-marshal.list | 1 + src/ip6-manager/nm-ip6-manager.c | 78 ++++++++++++++++++++++---------- src/ip6-manager/nm-ip6-manager.h | 15 +++++- src/nm-device.c | 12 +++-- 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/marshallers/nm-marshal.list b/marshallers/nm-marshal.list index 38669dd32b..36e0fbe6d0 100644 --- a/marshallers/nm-marshal.list +++ b/marshallers/nm-marshal.list @@ -16,6 +16,7 @@ VOID:STRING,STRING,STRING,UINT VOID:OBJECT,UINT,UINT VOID:STRING,INT VOID:STRING,UINT +VOID:STRING,UINT,BOOLEAN VOID:OBJECT,OBJECT,ENUM VOID:POINTER,STRING VOID:STRING,BOXED diff --git a/src/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c index f41169cb15..20e1a7f939 100644 --- a/src/ip6-manager/nm-ip6-manager.c +++ b/src/ip6-manager/nm-ip6-manager.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #include @@ -165,10 +165,8 @@ nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete), NULL, NULL, - _nm_marshal_VOID__STRING_BOOLEAN, - G_TYPE_NONE, 2, - G_TYPE_STRING, - G_TYPE_BOOLEAN); + _nm_marshal_VOID__STRING_UINT_BOOLEAN, + G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_BOOLEAN); signals[CONFIG_CHANGED] = g_signal_new ("config-changed", @@ -176,9 +174,8 @@ nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed), NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, - G_TYPE_STRING); + _nm_marshal_VOID__STRING_UINT, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); } static void @@ -233,10 +230,16 @@ nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex) GINT_TO_POINTER (ifindex)); } +typedef struct { + NMIP6Device *device; + guint dhcp_opts; +} CallbackInfo; + static gboolean finish_addrconf (gpointer user_data) { - NMIP6Device *device = user_data; + CallbackInfo *info = user_data; + NMIP6Device *device = info->device; NMIP6Manager *manager = device->manager; char *iface_copy; @@ -245,7 +248,7 @@ finish_addrconf (gpointer user_data) if (device->state >= device->target_state) { g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0, - device->iface, TRUE); + device->iface, info->dhcp_opts, TRUE); } else { nm_info ("Device '%s' IP6 addrconf timed out or failed.", device->iface); @@ -254,7 +257,7 @@ finish_addrconf (gpointer user_data) nm_ip6_manager_cancel_addrconf (manager, device->iface); g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0, - iface_copy, FALSE); + iface_copy, info->dhcp_opts, FALSE); g_free (iface_copy); } @@ -265,11 +268,12 @@ finish_addrconf (gpointer user_data) static gboolean emit_config_changed (gpointer user_data) { - NMIP6Device *device = user_data; + CallbackInfo *info = user_data; + NMIP6Device *device = info->device; NMIP6Manager *manager = device->manager; device->config_changed_id = 0; - g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface); + g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface, info->dhcp_opts); return FALSE; } @@ -279,9 +283,10 @@ static gboolean rdnss_expired (gpointer user_data) { NMIP6Device *device = user_data; + CallbackInfo info = { device, IP6_DHCP_OPT_NONE }; set_rdnss_timeout (device); - emit_config_changed (device); + emit_config_changed (&info); return FALSE; } @@ -323,6 +328,18 @@ set_rdnss_timeout (NMIP6Device *device) } } +static CallbackInfo * +callback_info_new (NMIP6Device *device, guint dhcp_opts) +{ + CallbackInfo *info; + + info = g_malloc0 (sizeof (CallbackInfo)); + info->device = device; + info->dhcp_opts = dhcp_opts; + + return info; +} + static void nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) { @@ -333,6 +350,8 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) struct in6_addr *addr; struct rtnl_link *link; guint flags; + CallbackInfo *info; + guint dhcp_opts = IP6_DHCP_OPT_NONE; for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache); rtnladdr; @@ -365,8 +384,10 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) if ((flags & IF_RA_RCVD) && device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT) device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT; -// if (flags & (IF_RA_MANAGED | IF_RA_OTHERCONF)) -// device->need_dhcp = TRUE; + if (flags & IF_RA_MANAGED) + dhcp_opts = IP6_DHCP_OPT_MANAGED; + else if (flags & IF_RA_OTHERCONF) + dhcp_opts = IP6_DHCP_OPT_OTHERCONF; if (!device->addrconf_complete) { if (device->state >= device->target_state || @@ -376,13 +397,20 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) */ if (device->finish_addrconf_id) g_source_remove (device->finish_addrconf_id); - device->finish_addrconf_id = g_idle_add (finish_addrconf, - device); + + info = callback_info_new (device, dhcp_opts); + device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + finish_addrconf, + info, + (GDestroyNotify) g_free); } } else if (config_changed) { if (!device->config_changed_id) { - device->config_changed_id = g_idle_add (emit_config_changed, - device); + info = callback_info_new (device, dhcp_opts); + device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + emit_config_changed, + info, + (GDestroyNotify) g_free); } } } @@ -717,6 +745,7 @@ nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, { NMIP6ManagerPrivate *priv; NMIP6Device *device; + CallbackInfo *info; g_return_if_fail (NM_IS_IP6_MANAGER (manager)); g_return_if_fail (iface != NULL); @@ -731,9 +760,12 @@ nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, device->addrconf_complete = FALSE; /* Set up a timeout on the transaction to kill it after the timeout */ - device->finish_addrconf_id = g_timeout_add_seconds (NM_IP6_TIMEOUT, - finish_addrconf, - device); + info = callback_info_new (device, 0); + device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + NM_IP6_TIMEOUT, + finish_addrconf, + info, + (GDestroyNotify) g_free); /* Sync flags, etc, from netlink; this will also notice if the * device is already fully configured and schedule the diff --git a/src/ip6-manager/nm-ip6-manager.h b/src/ip6-manager/nm-ip6-manager.h index 33b2b9893a..d0cf4b08a9 100644 --- a/src/ip6-manager/nm-ip6-manager.h +++ b/src/ip6-manager/nm-ip6-manager.h @@ -35,6 +35,12 @@ #define NM_IS_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_MANAGER)) #define NM_IP6_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass)) +enum { + IP6_DHCP_OPT_NONE = 0, + IP6_DHCP_OPT_OTHERCONF, + IP6_DHCP_OPT_MANAGED +}; + typedef struct { GObject parent; } NMIP6Manager; @@ -47,13 +53,18 @@ typedef struct { /* addrconf_complete is emitted only during initial configuration to indicate * that the initial configuration is complete. */ - void (*addrconf_complete) (NMIP6Manager *manager, char *iface, gboolean success); + void (*addrconf_complete) (NMIP6Manager *manager, + char *iface, + guint dhcp_opts, + gboolean success); /* config_changed gets emitted only *after* initial configuration is * complete; it's like DHCP renew and indicates that the existing config * of the interface has changed. */ - void (*config_changed) (NMIP6Manager *manager, char *iface); + void (*config_changed) (NMIP6Manager *manager, + char *iface, + guint dhcp_opts); } NMIP6ManagerClass; GType nm_ip6_manager_get_type (void); diff --git a/src/nm-device.c b/src/nm-device.c index daf3d111cd..7700c7a10f 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -535,9 +535,10 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family) static void ip6_addrconf_complete (NMIP6Manager *ip6_manager, - const char *iface, - gboolean success, - gpointer user_data) + const char *iface, + guint dhcp_opts, + gboolean success, + gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -558,8 +559,9 @@ ip6_addrconf_complete (NMIP6Manager *ip6_manager, static void ip6_config_changed (NMIP6Manager *ip6_manager, - const char *iface, - gpointer user_data) + const char *iface, + guint dhcp_opts, + gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); From 4f537c195c88243c49f6d481d1405c63c44b721b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 15 Jan 2010 12:40:19 -0800 Subject: [PATCH 16/20] libnm-glib: add NMDHCP6Config class --- include/NetworkManager.h | 1 + libnm-glib/Makefile.am | 8 +- libnm-glib/nm-device.c | 103 ++++++++++++++- libnm-glib/nm-device.h | 3 + libnm-glib/nm-dhcp6-config.c | 248 +++++++++++++++++++++++++++++++++++ libnm-glib/nm-dhcp6-config.h | 68 ++++++++++ 6 files changed, 427 insertions(+), 4 deletions(-) create mode 100644 libnm-glib/nm-dhcp6-config.c create mode 100644 libnm-glib/nm-dhcp6-config.h diff --git a/include/NetworkManager.h b/include/NetworkManager.h index c8d5074a1e..b25cfbc2ba 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -42,6 +42,7 @@ #define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config" #define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config" #define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config" +#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config" #define NM_DBUS_SERVICE_USER_SETTINGS "org.freedesktop.NetworkManagerUserSettings" diff --git a/libnm-glib/Makefile.am b/libnm-glib/Makefile.am index 2aa6124e76..c177fa3cb7 100644 --- a/libnm-glib/Makefile.am +++ b/libnm-glib/Makefile.am @@ -20,7 +20,8 @@ BUILT_SOURCES = \ nm-active-connection-bindings.h \ nm-ip4-config-bindings.h \ nm-dhcp4-config-bindings.h \ - nm-ip6-config-bindings.h + nm-ip6-config-bindings.h \ + nm-dhcp6-config-bindings.h lib_LTLIBRARIES = libnm-glib.la libnm-glib-vpn.la @@ -52,6 +53,7 @@ libnminclude_HEADERS = \ nm-active-connection.h \ nm-dhcp4-config.h \ nm-ip6-config.h \ + nm-dhcp6-config.h \ nm-remote-connection.h \ nm-settings-interface.h \ nm-settings-system-interface.h \ @@ -86,6 +88,7 @@ libnm_glib_la_SOURCES = \ nm-active-connection.c \ nm-dhcp4-config.c \ nm-ip6-config.c \ + nm-dhcp6-config.c \ nm-remote-connection.c \ nm-remote-connection-private.h \ nm-settings-interface.c \ @@ -172,6 +175,9 @@ nm-dhcp4-config-bindings.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml nm-ip6-config-bindings.h: $(top_srcdir)/introspection/nm-ip6-config.xml dbus-binding-tool --prefix=nm_ip6_config --mode=glib-client --output=$@ $< +nm-dhcp6-config-bindings.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml + dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-client --output=$@ $< + pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libnm-glib.pc libnm-glib-vpn.pc diff --git a/libnm-glib/nm-device.c b/libnm-glib/nm-device.c index 17caa99d5d..a24eb06efa 100644 --- a/libnm-glib/nm-device.c +++ b/libnm-glib/nm-device.c @@ -59,6 +59,8 @@ typedef struct { gboolean null_dhcp4_config; NMIP6Config *ip6_config; gboolean null_ip6_config; + NMDHCP6Config *dhcp6_config; + gboolean null_dhcp6_config; NMDeviceState state; GUdevClient *client; @@ -79,6 +81,7 @@ enum { PROP_STATE, PROP_PRODUCT, PROP_VENDOR, + PROP_DHCP6_CONFIG, LAST_PROP }; @@ -220,6 +223,46 @@ demarshal_ip6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpoint return TRUE; } +static gboolean +demarshal_dhcp6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + const char *path; + NMDHCP6Config *config = NULL; + DBusGConnection *connection; + + if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) + return FALSE; + + priv->null_dhcp6_config = FALSE; + + path = g_value_get_boxed (value); + if (path) { + if (!strcmp (path, "/")) + priv->null_dhcp6_config = TRUE; + else { + config = NM_DHCP6_CONFIG (_nm_object_cache_get (path)); + if (config) + config = g_object_ref (config); + else { + connection = nm_object_get_connection (object); + config = NM_DHCP6_CONFIG (nm_dhcp6_config_new (connection, path)); + } + } + } + + if (priv->dhcp6_config) { + g_object_unref (priv->dhcp6_config); + priv->dhcp6_config = NULL; + } + + if (config) + priv->dhcp6_config = config; + + _nm_object_queue_notify (object, NM_DEVICE_DHCP6_CONFIG); + return TRUE; +} + static void register_for_property_changed (NMDevice *device) { @@ -230,9 +273,10 @@ register_for_property_changed (NMDevice *device) { NM_DEVICE_DRIVER, _nm_object_demarshal_generic, &priv->driver }, { NM_DEVICE_CAPABILITIES, _nm_object_demarshal_generic, &priv->capabilities }, { NM_DEVICE_MANAGED, _nm_object_demarshal_generic, &priv->managed }, - { NM_DEVICE_IP4_CONFIG, demarshal_ip4_config, &priv->ip4_config }, - { NM_DEVICE_DHCP4_CONFIG, demarshal_dhcp4_config, &priv->dhcp4_config }, - { NM_DEVICE_IP6_CONFIG, demarshal_ip6_config, &priv->ip6_config }, + { NM_DEVICE_IP4_CONFIG, demarshal_ip4_config, &priv->ip4_config }, + { NM_DEVICE_DHCP4_CONFIG, demarshal_dhcp4_config, &priv->dhcp4_config }, + { NM_DEVICE_IP6_CONFIG, demarshal_ip6_config, &priv->ip6_config }, + { NM_DEVICE_DHCP6_CONFIG, demarshal_dhcp6_config, &priv->dhcp6_config }, { NULL }, }; @@ -318,6 +362,8 @@ dispose (GObject *object) g_object_unref (priv->dhcp4_config); if (priv->ip6_config) g_object_unref (priv->ip6_config); + if (priv->dhcp6_config) + g_object_unref (priv->dhcp6_config); if (priv->client) g_object_unref (priv->client); @@ -371,6 +417,9 @@ get_property (GObject *object, case PROP_IP6_CONFIG: g_value_set_object (value, nm_device_get_ip6_config (device)); break; + case PROP_DHCP6_CONFIG: + g_value_set_object (value, nm_device_get_dhcp6_config (device)); + break; case PROP_STATE: g_value_set_uint (value, nm_device_get_state (device)); break; @@ -505,6 +554,19 @@ nm_device_class_init (NMDeviceClass *device_class) NM_TYPE_IP6_CONFIG, G_PARAM_READABLE)); + /** + * NMDevice:dhcp6-config: + * + * The #NMDHCP6Config of the device. + **/ + g_object_class_install_property + (object_class, PROP_DHCP6_CONFIG, + g_param_spec_object (NM_DEVICE_DHCP6_CONFIG, + "DHCP6 Config", + "DHCP6 Config", + NM_TYPE_DHCP6_CONFIG, + G_PARAM_READABLE)); + /** * NMDevice:state: * @@ -870,6 +932,41 @@ nm_device_get_ip6_config (NMDevice *device) return priv->ip6_config; } +/** + * nm_device_get_dhcp6_config: + * @device: a #NMDevice + * + * Gets the current #NMDHCP6Config associated with the #NMDevice. + * + * Returns: the #NMDHCPConfig or %NULL if the device is not activated or not + * using DHCP. + **/ +NMDHCP6Config * +nm_device_get_dhcp6_config (NMDevice *device) +{ + NMDevicePrivate *priv; + char *path; + GValue value = { 0, }; + + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + priv = NM_DEVICE_GET_PRIVATE (device); + if (priv->dhcp6_config) + return priv->dhcp6_config; + if (priv->null_dhcp6_config) + return NULL; + + path = _nm_object_get_object_path_property (NM_OBJECT (device), NM_DBUS_INTERFACE_DEVICE, "Dhcp6Config"); + if (path) { + g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH); + g_value_take_boxed (&value, path); + demarshal_dhcp6_config (NM_OBJECT (device), NULL, &value, &priv->dhcp6_config); + g_value_unset (&value); + } + + return priv->dhcp6_config; +} + /** * nm_device_get_state: * @device: a #NMDevice diff --git a/libnm-glib/nm-device.h b/libnm-glib/nm-device.h index da015d6c46..64694ec833 100644 --- a/libnm-glib/nm-device.h +++ b/libnm-glib/nm-device.h @@ -32,6 +32,7 @@ #include "nm-ip4-config.h" #include "nm-dhcp4-config.h" #include "nm-ip6-config.h" +#include "nm-dhcp6-config.h" #include "nm-connection.h" G_BEGIN_DECLS @@ -51,6 +52,7 @@ G_BEGIN_DECLS #define NM_DEVICE_IP4_CONFIG "ip4-config" #define NM_DEVICE_DHCP4_CONFIG "dhcp4-config" #define NM_DEVICE_IP6_CONFIG "ip6-config" +#define NM_DEVICE_DHCP6_CONFIG "dhcp6-config" #define NM_DEVICE_STATE "state" #define NM_DEVICE_VENDOR "vendor" #define NM_DEVICE_PRODUCT "product" @@ -89,6 +91,7 @@ gboolean nm_device_get_managed (NMDevice *device); NMIP4Config * nm_device_get_ip4_config (NMDevice *device); NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *device); NMIP6Config * nm_device_get_ip6_config (NMDevice *device); +NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *device); NMDeviceState nm_device_get_state (NMDevice *device); const char * nm_device_get_product (NMDevice *device); const char * nm_device_get_vendor (NMDevice *device); diff --git a/libnm-glib/nm-dhcp6-config.c b/libnm-glib/nm-dhcp6-config.c new file mode 100644 index 0000000000..49eeda39ec --- /dev/null +++ b/libnm-glib/nm-dhcp6-config.c @@ -0,0 +1,248 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * libnm_glib -- Access network status & information from glib applications + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#include + +#include "nm-dhcp6-config.h" +#include "NetworkManager.h" +#include "nm-types-private.h" +#include "nm-object-private.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, NM_TYPE_OBJECT) + +#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate)) + +typedef struct { + DBusGProxy *proxy; + + GHashTable *options; +} NMDHCP6ConfigPrivate; + +enum { + PROP_0, + PROP_OPTIONS, + + LAST_PROP +}; + +static void +nm_dhcp6_config_init (NMDHCP6Config *config) +{ +} + +static void +copy_options (gpointer key, gpointer data, gpointer user_data) +{ + GHashTable *options = (GHashTable *) user_data; + GValue *value = (GValue *) data; + + g_hash_table_insert (options, g_strdup (key), g_value_dup_string (value)); +} + +static gboolean +demarshal_dhcp6_options (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + GHashTable *new_options; + + g_hash_table_remove_all (priv->options); + + new_options = g_value_get_boxed (value); + if (new_options) + g_hash_table_foreach (new_options, copy_options, priv->options); + + _nm_object_queue_notify (object, NM_DHCP6_CONFIG_OPTIONS); + return TRUE; +} + +static void +register_for_property_changed (NMDHCP6Config *config) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (config); + const NMPropertiesChangedInfo property_changed_info[] = { + { NM_DHCP6_CONFIG_OPTIONS, demarshal_dhcp6_options, &priv->options }, + { NULL }, + }; + + _nm_object_handle_properties_changed (NM_OBJECT (config), + priv->proxy, + property_changed_info); +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + NMObject *object; + DBusGConnection *connection; + NMDHCP6ConfigPrivate *priv; + + object = (NMObject *) G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + connection = nm_object_get_connection (object); + + priv->proxy = dbus_g_proxy_new_for_name (connection, + NM_DBUS_SERVICE, + nm_object_get_path (object), + NM_DBUS_INTERFACE_DHCP6_CONFIG); + + register_for_property_changed (NM_DHCP6_CONFIG (object)); + + return G_OBJECT (object); +} + +static void +finalize (GObject *object) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object); + + if (priv->options) + g_hash_table_destroy (priv->options); + + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMDHCP6Config *self = NM_DHCP6_CONFIG (object); + + switch (prop_id) { + case PROP_OPTIONS: + g_value_set_boxed (value, nm_dhcp6_config_get_options (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate)); + + /* virtual methods */ + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + + /** + * NMDHCP6Config:options: + * + * The #GHashTable containing options of the configuration. + **/ + g_object_class_install_property + (object_class, PROP_OPTIONS, + g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS, + "Options", + "Options", + G_TYPE_HASH_TABLE, + G_PARAM_READABLE)); +} + +/** + * nm_dhcp6_config_new: + * @connection: the #DBusGConnection + * @object_path: the DBus object path of the device + * + * Creates a new #NMDHCP6Config. + * + * Returns: a new configuration + **/ +GObject * +nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path) +{ + return (GObject *) g_object_new (NM_TYPE_DHCP6_CONFIG, + NM_OBJECT_DBUS_CONNECTION, connection, + NM_OBJECT_DBUS_PATH, object_path, + NULL); +} + +/** + * nm_dhcp6_config_get_options: + * @config: a #NMDHCP6Config + * + * Gets all the options contained in the configuration. + * + * Returns: the #GHashTable containing strings for keys and values. + * This is the internal copy used by the configuration, and must not be modified. + **/ +GHashTable * +nm_dhcp6_config_get_options (NMDHCP6Config *config) +{ + NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (config); + GValue value = { 0, }; + + if (g_hash_table_size (priv->options)) + return priv->options; + + if (!_nm_object_get_property (NM_OBJECT (config), + "org.freedesktop.DBus.Properties", + "Options", + &value)) + goto out; + + demarshal_dhcp6_options (NM_OBJECT (config), NULL, &value, &priv->options); + g_value_unset (&value); + +out: + return priv->options; +} + +/** + * nm_dhcp6_config_get_one_option: + * @config: a #NMDHCP6Config + * @option: the option to retrieve + * + * Gets one option by option name. + * + * Returns: the configuration option's value. This is the internal string used by the + * configuration, and must not be modified. + **/ +const char * +nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option) +{ + g_return_val_if_fail (NM_IS_DHCP6_CONFIG (config), NULL); + + return g_hash_table_lookup (nm_dhcp6_config_get_options (config), option); +} + diff --git a/libnm-glib/nm-dhcp6-config.h b/libnm-glib/nm-dhcp6-config.h new file mode 100644 index 0000000000..91f32fa036 --- /dev/null +++ b/libnm-glib/nm-dhcp6-config.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * libnm_glib -- Access network status & information from glib applications + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 Novell, Inc. + */ + +#ifndef NM_DHCP6_CONFIG_H +#define NM_DHCP6_CONFIG_H + +#include +#include +#include +#include "nm-object.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type ()) +#define NM_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config)) +#define NM_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass)) +#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG)) +#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP6_CONFIG)) + +typedef struct { + NMObject parent; +} NMDHCP6Config; + +typedef struct { + NMObjectClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); + void (*_reserved5) (void); + void (*_reserved6) (void); +} NMDHCP6ConfigClass; + +#define NM_DHCP6_CONFIG_OPTIONS "options" + +GType nm_dhcp6_config_get_type (void); + +GObject *nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path); + +GHashTable * nm_dhcp6_config_get_options (NMDHCP6Config *config); + +const char * nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option); + +G_END_DECLS + +#endif /* NM_DHCP6_CONFIG_H */ From fbe413cbcd4f03246e7d8781cec4300acb4a8984 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 15 Jan 2010 12:50:38 -0800 Subject: [PATCH 17/20] dhcp: ensure DHCP client is stopped on timeout --- src/dhcp-manager/nm-dhcp-client.c | 3 ++- src/nm-device.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 03373d7172..7eb73006d7 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -193,7 +193,8 @@ daemon_timeout (gpointer user_data) NMDHCPClient *self = NM_DHCP_CLIENT (user_data); NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - g_message ("(%s): DHCP transaction took too long, stopping it.", priv->iface); + g_message ("(%s): DHCPv%c request timed out.", + priv->iface, priv->ipv6 ? '6' : '4'); g_signal_emit (G_OBJECT (self), signals[TIMEOUT], 0); return FALSE; } diff --git a/src/nm-device.c b/src/nm-device.c index 7700c7a10f..5acfb2a0dc 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -1224,6 +1224,8 @@ dhcp_timeout (NMDHCPClient *client, gpointer user_data) if (!nm_device_get_act_request (device)) return; + nm_dhcp_client_stop (client); + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { if (nm_dhcp_client_get_ipv6 (client)) nm_device_activate_schedule_stage4_ip6_config_timeout (device); From 133c86ee92135b66975aae60b3318e4d79376130 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 20 Jan 2010 17:30:48 -0800 Subject: [PATCH 18/20] introspection: fix inclusion of Dhcp6Config XML --- introspection/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introspection/Makefile.am b/introspection/Makefile.am index a70d28511a..212da3ab93 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -24,5 +24,5 @@ EXTRA_DIST = \ nm-ppp-manager.xml \ nm-active-connection.xml \ nm-dhcp4-config.xml \ - nm-dhcp4-config.xml + nm-dhcp6-config.xml From 13af29f693509ec13d2a3ceae4832f3b7d1bf7b3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 22 Jan 2010 12:04:52 -0800 Subject: [PATCH 19/20] ifcfg-rh: read and write DHCPv6 connections --- system-settings/plugins/ifcfg-rh/reader.c | 5 ++++- system-settings/plugins/ifcfg-rh/writer.c | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c index afe35bdf8c..1b8ae93925 100644 --- a/system-settings/plugins/ifcfg-rh/reader.c +++ b/system-settings/plugins/ifcfg-rh/reader.c @@ -1382,7 +1382,7 @@ make_ip6_setting (shvarFile *ifcfg, char *value = NULL; char *str_value; char *route6_path = NULL; - gboolean bool_value, ipv6forwarding, ipv6_autoconf; + gboolean bool_value, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; guint32 i; shvarFile *network_ifcfg; @@ -1459,9 +1459,12 @@ make_ip6_setting (shvarFile *ifcfg, /* Find out method property */ ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE); ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding); + dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE); if (ipv6_autoconf) method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + else if (dhcp6) + method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; else { /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */ str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE); diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c index 4ded5c6813..330bbf044e 100644 --- a/system-settings/plugins/ifcfg-rh/writer.c +++ b/system-settings/plugins/ifcfg-rh/writer.c @@ -1205,18 +1205,27 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_assert (value); if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { svSetValue (ifcfg, "IPV6INIT", "no", FALSE); + svSetValue (ifcfg, "DHCPV6C", NULL, FALSE); return TRUE; } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { svSetValue (ifcfg, "IPV6INIT", "yes", FALSE); svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE); + svSetValue (ifcfg, "DHCPV6C", NULL, FALSE); + } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + svSetValue (ifcfg, "IPV6INIT", "yes", FALSE); + svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE); + svSetValue (ifcfg, "DHCPV6C", "yes", FALSE); } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { svSetValue (ifcfg, "IPV6INIT", "yes", FALSE); svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE); + svSetValue (ifcfg, "DHCPV6C", NULL, FALSE); } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { svSetValue (ifcfg, "IPV6INIT", "yes", FALSE); svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE); + svSetValue (ifcfg, "DHCPV6C", NULL, FALSE); } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { svSetValue (ifcfg, "IPV6INIT", "yes", FALSE); + svSetValue (ifcfg, "DHCPV6C", NULL, FALSE); /* TODO */ } From 35c4b97d7dbf143f2ce39ca313c18f2e014a9d8f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 27 Jan 2010 16:20:09 -0800 Subject: [PATCH 20/20] dhcp6: first stab at autoconf otherconf/managed mode support --- src/dhcp-manager/nm-dhcp-client.c | 5 +- src/dhcp-manager/nm-dhcp-client.h | 6 +- src/dhcp-manager/nm-dhcp-dhclient.c | 13 +- src/dhcp-manager/nm-dhcp-dhcpcd.c | 3 +- src/dhcp-manager/nm-dhcp-manager.c | 12 +- src/dhcp-manager/nm-dhcp-manager.h | 3 +- src/nm-device.c | 194 ++++++++++++++++++++-------- 7 files changed, 164 insertions(+), 72 deletions(-) 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; }