diff --git a/docs/generate-settings-spec.c b/docs/generate-settings-spec.c index d7a391f9b0..6ec8f7c5e0 100644 --- a/docs/generate-settings-spec.c +++ b/docs/generate-settings-spec.c @@ -17,7 +17,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * (C) Copyright 2009 Red Hat, Inc. + * (C) Copyright 2009 - 2010 Red Hat, Inc. */ #include @@ -86,7 +86,7 @@ static TypeNameElement name_map[] = { { "GPtrArray_GArray_guint__", "array of array of uint32" }, { "GPtrArray_GArray_guchar__", "array of byte array" }, { "GHashTable_gchararray+gchararray_", "dict of (string::string)" }, - { "GPtrArray_GValueArray_GArray_guchar_+guint__", "array of (byte array, uint32)" }, + { "GPtrArray_GValueArray_GArray_guchar_+guint+GArray_guchar___", "array of (byte array, uint32, byte array)" }, { "GPtrArray_GValueArray_GArray_guchar_+guint+GArray_guchar_+guint__", "array of (byte array, uint32, byte array, uint32)" }, { NULL, NULL } }; diff --git a/include/nm-dbus-glib-types.h b/include/nm-dbus-glib-types.h index cfb08274c6..0f8c542e82 100644 --- a/include/nm-dbus-glib-types.h +++ b/include/nm-dbus-glib-types.h @@ -34,7 +34,7 @@ #define DBUS_TYPE_G_MAP_OF_STRING (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)) #define DBUS_TYPE_G_LIST_OF_STRING (dbus_g_type_get_collection ("GSList", G_TYPE_STRING)) -#define DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) +#define DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_INVALID)) #define DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_IP6_ADDRESS)) #define DBUS_TYPE_G_IP6_ROUTE (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) #define DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_IP6_ROUTE)) diff --git a/introspection/nm-active-connection.xml b/introspection/nm-active-connection.xml index ff55f63885..765830a1dc 100644 --- a/introspection/nm-active-connection.xml +++ b/introspection/nm-active-connection.xml @@ -18,7 +18,10 @@ The state of this active connection. - Whether this active connection is the default connection, i.e. whether it currently owns the default route. + Whether this active connection is the default IPv4 connection, i.e. whether it currently owns the default IPv4 route. + + + Whether this active connection is the default IPv6 connection, i.e. whether it currently owns the default IPv6 route. Whether this active connection is also a VPN connection. diff --git a/introspection/nm-ip6-config.xml b/introspection/nm-ip6-config.xml index d1349b0c21..604781857e 100644 --- a/introspection/nm-ip6-config.xml +++ b/introspection/nm-ip6-config.xml @@ -2,8 +2,8 @@ - - Tuples of IPv6 address/prefix. + + Tuples of IPv6 address/prefix/gateway. The nameservers in use. diff --git a/libnm-glib/Makefile.am b/libnm-glib/Makefile.am index 58b5bc559e..8f03b0abda 100644 --- a/libnm-glib/Makefile.am +++ b/libnm-glib/Makefile.am @@ -132,7 +132,7 @@ libnm_glib_la_LIBADD = \ $(GUDEV_LIBS) libnm_glib_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnm-glib.ver \ - -version-info "4:1:2" + -version-info "4:2:2" noinst_PROGRAMS = libnm-glib-test diff --git a/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index dfe4fe5023..47312d01bb 100644 --- a/libnm-glib/libnm-glib.ver +++ b/libnm-glib/libnm-glib.ver @@ -18,6 +18,7 @@ global: nm_access_point_new; nm_active_connection_get_connection; nm_active_connection_get_default; + nm_active_connection_get_default6; nm_active_connection_get_devices; nm_active_connection_get_scope; nm_active_connection_get_service_name; diff --git a/libnm-glib/nm-active-connection.c b/libnm-glib/nm-active-connection.c index df8bb98271..2468da099f 100644 --- a/libnm-glib/nm-active-connection.c +++ b/libnm-glib/nm-active-connection.c @@ -17,7 +17,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. */ @@ -50,6 +50,7 @@ typedef struct { GPtrArray *devices; NMActiveConnectionState state; gboolean is_default; + gboolean is_default6; } NMActiveConnectionPrivate; enum { @@ -60,6 +61,7 @@ enum { PROP_DEVICES, PROP_STATE, PROP_DEFAULT, + PROP_DEFAULT6, LAST_PROP }; @@ -70,6 +72,7 @@ enum { #define DBUS_PROP_DEVICES "Devices" #define DBUS_PROP_STATE "State" #define DBUS_PROP_DEFAULT "Default" +#define DBUS_PROP_DEFAULT6 "Default6" /** * nm_active_connection_new: @@ -263,10 +266,10 @@ nm_active_connection_get_state (NMActiveConnection *connection) * nm_active_connection_get_default: * @connection: a #NMActiveConnection * - * Whether the active connection is the default one (that is, is used for the default route - * and DNS information). + * Whether the active connection is the default IPv4 one (that is, is used for + * the default IPv4 route and DNS information). * - * Returns: %TRUE if the active connection is the default one + * Returns: %TRUE if the active connection is the default IPv4 connection **/ gboolean nm_active_connection_get_default (NMActiveConnection *connection) @@ -278,13 +281,39 @@ nm_active_connection_get_default (NMActiveConnection *connection) priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (connection); if (!priv->is_default) { priv->is_default = _nm_object_get_boolean_property (NM_OBJECT (connection), - NM_DBUS_INTERFACE_ACTIVE_CONNECTION, - DBUS_PROP_DEFAULT); + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + DBUS_PROP_DEFAULT); } return priv->is_default; } +/** + * nm_active_connection_get_default6: + * @connection: a #NMActiveConnection + * + * Whether the active connection is the default IPv6 one (that is, is used for + * the default IPv6 route and DNS information). + * + * Returns: %TRUE if the active connection is the default IPv6 connection + **/ +gboolean +nm_active_connection_get_default6 (NMActiveConnection *connection) +{ + NMActiveConnectionPrivate *priv; + + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), FALSE); + + priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (connection); + if (!priv->is_default6) { + priv->is_default6 = _nm_object_get_boolean_property (NM_OBJECT (connection), + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + DBUS_PROP_DEFAULT6); + } + + return priv->is_default6; +} + static void nm_active_connection_init (NMActiveConnection *ap) { @@ -350,6 +379,9 @@ get_property (GObject *object, case PROP_DEFAULT: g_value_set_boolean (value, nm_active_connection_get_default (self)); break; + case PROP_DEFAULT6: + g_value_set_boolean (value, nm_active_connection_get_default6 (self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -392,6 +424,7 @@ register_for_property_changed (NMActiveConnection *connection) { NM_ACTIVE_CONNECTION_DEVICES, demarshal_devices, &priv->devices }, { NM_ACTIVE_CONNECTION_STATE, _nm_object_demarshal_generic, &priv->state }, { NM_ACTIVE_CONNECTION_DEFAULT, _nm_object_demarshal_generic, &priv->is_default }, + { NM_ACTIVE_CONNECTION_DEFAULT6, _nm_object_demarshal_generic, &priv->is_default6 }, { NULL }, }; @@ -512,13 +545,26 @@ nm_active_connection_class_init (NMActiveConnectionClass *ap_class) /** * NMActiveConnection:default: * - * Whether the active connection is the default one. + * Whether the active connection is the default IPv4 one. **/ g_object_class_install_property (object_class, PROP_DEFAULT, g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, "Default", - "Is the default active connection", + "Is the default IPv4 active connection", + FALSE, + G_PARAM_READABLE)); + + /** + * NMActiveConnection:default6: + * + * Whether the active connection is the default IPv6 one. + **/ + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, + "Default6", + "Is the default IPv6 active connection", FALSE, G_PARAM_READABLE)); } diff --git a/libnm-glib/nm-active-connection.h b/libnm-glib/nm-active-connection.h index f717c055ef..30edf047a0 100644 --- a/libnm-glib/nm-active-connection.h +++ b/libnm-glib/nm-active-connection.h @@ -17,7 +17,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. */ @@ -45,6 +45,7 @@ G_BEGIN_DECLS #define NM_ACTIVE_CONNECTION_DEVICES "devices" #define NM_ACTIVE_CONNECTION_STATE "state" #define NM_ACTIVE_CONNECTION_DEFAULT "default" +#define NM_ACTIVE_CONNECTION_DEFAULT6 "default6" typedef struct { NMObject parent; @@ -73,6 +74,7 @@ const char * nm_active_connection_get_specific_object (NMActiveConnection *c const GPtrArray *nm_active_connection_get_devices (NMActiveConnection *connection); NMActiveConnectionState nm_active_connection_get_state (NMActiveConnection *connection); gboolean nm_active_connection_get_default (NMActiveConnection *connection); +gboolean nm_active_connection_get_default6 (NMActiveConnection *connection); G_END_DECLS diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am index 8f6a0cc95d..bb4736f45b 100644 --- a/libnm-util/Makefile.am +++ b/libnm-util/Makefile.am @@ -59,7 +59,7 @@ libnm_util_la_SOURCES= \ libnm_util_la_LIBADD = $(GLIB_LIBS) $(DBUS_LIBS) $(UUID_LIBS) libnm_util_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnm-util.ver \ - -version-info "4:2:3" + -version-info "4:3:3" if WITH_GNUTLS libnm_util_la_SOURCES += crypto_gnutls.c diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 44d9ac5aec..1c8446ba2e 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -177,6 +177,8 @@ global: nm_ip6_address_compare; nm_ip6_address_get_address; nm_ip6_address_set_address; + nm_ip6_address_get_gateway; + nm_ip6_address_set_gateway; nm_ip6_address_get_prefix; nm_ip6_address_set_prefix; nm_ip6_route_new; diff --git a/libnm-util/nm-param-spec-specialized.c b/libnm-util/nm-param-spec-specialized.c index ad0febfcf6..f5a362ce41 100644 --- a/libnm-util/nm-param-spec-specialized.c +++ b/libnm-util/nm-param-spec-specialized.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. * (C) Copyright 2007 - 2008 Novell, Inc. */ @@ -377,6 +377,7 @@ nm_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2) GValue *tmp_val; GByteArray *addr1, *addr2; guint32 prefix1, prefix2; + GByteArray *gw1, *gw2; gint ret = 0; int i; @@ -387,8 +388,8 @@ nm_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2) /* Since they are NM IPv6 address structures, we expect both * to contain two elements as specified in nm-dbus-glib-types.h. */ - g_return_val_if_fail (values1->n_values == 2, 0); - g_return_val_if_fail (values2->n_values == 2, 0); + g_return_val_if_fail (values1->n_values == 3, 0); + g_return_val_if_fail (values2->n_values == 3, 0); /* First struct IPv6 address */ tmp_val = g_value_array_get_nth (values1, 0); @@ -396,6 +397,9 @@ nm_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2) /* First struct IPv6 prefix */ tmp_val = g_value_array_get_nth (values1, 1); prefix1 = g_value_get_uint (tmp_val); + /* First struct IPv6 gateway */ + tmp_val = g_value_array_get_nth (values1, 2); + gw1 = g_value_get_boxed (tmp_val); /* Second struct IPv6 address */ tmp_val = g_value_array_get_nth (values2, 0); @@ -403,6 +407,9 @@ nm_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2) /* Second struct IPv6 prefix */ tmp_val = g_value_array_get_nth (values2, 1); prefix2 = g_value_get_uint (tmp_val); + /* Second struct IPv6 gateway */ + tmp_val = g_value_array_get_nth (values2, 2); + gw2 = g_value_get_boxed (tmp_val); /* Compare IPv6 addresses */ if (prefix1 != prefix2) @@ -413,6 +420,11 @@ nm_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2) ret = addr1->data[i] < addr2->data[i] ? -1 : addr1->data[i] > addr2->data[i]; } + if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *) gw1->data, (struct in6_addr *) gw2->data)) { + for (i = 0; ret == 0 && i < gw1->len; i++) + ret = gw1->data[i] < gw2->data[i] ? -1 : gw1->data[i] > gw2->data[i]; + } + return ret; } diff --git a/libnm-util/nm-setting-ip4-config.c b/libnm-util/nm-setting-ip4-config.c index 46ebbd377f..c1d7f01c76 100644 --- a/libnm-util/nm-setting-ip4-config.c +++ b/libnm-util/nm-setting-ip4-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. * (C) Copyright 2007 - 2008 Novell, Inc. */ @@ -462,7 +462,8 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) return FALSE; } } else if ( !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) - || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) + || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { if (priv->dns && priv->dns->len) { g_set_error (error, NM_SETTING_IP4_CONFIG_ERROR, @@ -721,7 +722,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) * network access to other computers) then the interface is assigned an * address in the 10.42.x.1/24 range and a DHCP and forwarding DNS server * are started, and the interface is NAT-ed to the current default network - * connection. This property must be set. + * connection. 'disabled' means IPv4 will not be used on this connection. + * This property must be set. **/ g_object_class_install_property (object_class, PROP_METHOD, @@ -742,7 +744,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) "address in the 10.42.x.1/24 range and a DHCP and " "forwarding DNS server are started, and the " "interface is NAT-ed to the current default network " - "connection. This property must be set.", + "connection. 'disabled' means IPv4 will not be " + "used on this connection. This property must be set.", NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); @@ -751,8 +754,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) * * List of DNS servers (network byte order). For the 'auto' method, these * DNS servers are appended to those (if any) returned by automatic - * configuration. DNS servers cannot be used with the 'shared' or - * 'link-local' methods as there is no usptream network. In all other + * configuration. DNS servers cannot be used with the 'shared', 'link-local', + * or 'disabled' methods as there is no usptream network. In all other * methods, these DNS servers are used as the only DNS servers for this * connection. **/ @@ -764,10 +767,10 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) "the 'auto' method, these DNS servers are " "appended to those (if any) returned by automatic " "configuration. DNS servers cannot be used with " - "the 'shared' or 'link-local' methods as there is " - "no usptream network. In all other methods, " - "these DNS servers are used as the only DNS " - "servers for this connection.", + "the 'shared', 'link-local', or 'disabled' " + "methods as there is no usptream network. In all " + "other methods, these DNS servers are used as the " + "only DNS servers for this connection.", DBUS_TYPE_G_UINT_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); @@ -776,9 +779,9 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) * * List of DNS search domains. For the 'auto' method, these search domains * are appended to those returned by automatic configuration. Search domains - * cannot be used with the 'shared' or 'link-local' methods as there is no - * upstream network. In all other methods, these search domains are used - * as the only search domains for this connection. + * cannot be used with the 'shared', 'link-local', or 'disabled' methods as + * there is no upstream network. In all other methods, these search domains + * are used as the only search domains for this connection. **/ g_object_class_install_property (object_class, PROP_DNS_SEARCH, @@ -787,11 +790,11 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) "List of DNS search domains. For the 'auto' " "method, these search domains are appended to " "those returned by automatic configuration. " - "Search domains cannot be used with the 'shared' " - "or 'link-local' methods as there is no upstream " - "network. In all other methods, these search " - "domains are used as the only search domains for " - "this connection.", + "Search domains cannot be used with the 'shared', " + "'link-local', or 'disabled' methods as there is " + "no upstream network. In all other methods, these " + "search domains are used as the only search domains " + "for this connection.", DBUS_TYPE_G_LIST_OF_STRING, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); @@ -804,8 +807,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) * (network byte order). The gateway may be left as 0 if no gateway exists * for that subnet. For the 'auto' method, given IP addresses are appended * to those returned by automatic configuration. Addresses cannot be used - * with the 'shared' or 'link-local' methods as the interface is - * automatically assigned an address with these methods. + * with the 'shared', 'link-local', or 'disabled' methods as addressing is + * either automatic or disabled with these methods. **/ g_object_class_install_property (object_class, PROP_ADDRESSES, @@ -820,9 +823,9 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) "for that subnet. For the 'auto' method, given " "IP addresses are appended to those returned by " "automatic configuration. Addresses cannot be " - "used with the 'shared' or 'link-local' methods " - "as the interface is automatically assigned an " - "address with these methods.", + "used with the 'shared', 'link-local', or " + "'disabled' methods as addressing is either " + "automatic or disabled with these methods.", DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); @@ -835,8 +838,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) * address prefix (1 - 32), the third being the next-hop (network byte * order) if any, and the fourth being the route metric. For the 'auto' * method, given IP routes are appended to those returned by automatic - * configuration. Routes cannot be used with the 'shared' or 'link-local' - * methods because there is no upstream network. + * configuration. Routes cannot be used with the 'shared', 'link-local', + * or 'disabled' methods because there is no upstream network. **/ g_object_class_install_property (object_class, PROP_ROUTES, @@ -852,8 +855,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) "For the 'auto' method, given IP routes are " "appended to those returned by automatic " "configuration. Routes cannot be used with the " - "'shared' or 'link-local' methods as there is no " - "upstream network.", + "'shared', 'link-local', or 'disabled', methods " + "as there is no upstream network.", DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); diff --git a/libnm-util/nm-setting-ip4-config.h b/libnm-util/nm-setting-ip4-config.h index 2362982f51..777f53f126 100644 --- a/libnm-util/nm-setting-ip4-config.h +++ b/libnm-util/nm-setting-ip4-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. * (C) Copyright 2007 - 2008 Novell, Inc. */ @@ -69,6 +69,7 @@ GQuark nm_setting_ip4_config_error_quark (void); #define NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL "link-local" #define NM_SETTING_IP4_CONFIG_METHOD_MANUAL "manual" #define NM_SETTING_IP4_CONFIG_METHOD_SHARED "shared" +#define NM_SETTING_IP4_CONFIG_METHOD_DISABLED "disabled" typedef struct NMIP4Address NMIP4Address; diff --git a/libnm-util/nm-setting-ip6-config.c b/libnm-util/nm-setting-ip6-config.c index 3fbf1a2bc4..3e42216cbd 100644 --- a/libnm-util/nm-setting-ip6-config.c +++ b/libnm-util/nm-setting-ip6-config.c @@ -684,25 +684,32 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class) * NMSettingIP6Config:addresses: * * Array of IPv6 address structures. Each IPv6 address structure is - * composed of 2 members, the first being a byte array containing the IPv6 - * address (network byte order) and the second a 32-bit integer containing - * the IPv6 address prefix. For the 'auto' method, given IP addresses are - * appended to those returned by automatic configuration. Addresses cannot - * be used with the 'shared' or 'link-local' methods as the interface is - * automatically assigned an address with these methods. + * composed of 3 members, the first being a byte array containing the IPv6 + * address (network byte order), the second a 32-bit integer containing the + * IPv6 address prefix, and the third a byte array containing the IPv6 + * address (network byte order) of the gateway associated with this address, + * if any. If no gateway is given, the third element should be given as + * all zeros. For the 'auto' method, given IP addresses are appended to + * those returned by automatic configuration. Addresses cannot be used with + * the 'shared' or 'link-local' methods as the interface is automatically + * assigned an address with these methods. **/ g_object_class_install_property (object_class, PROP_ADDRESSES, _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ADDRESSES, "Addresses", "Array of IPv6 address structures. Each IPv6 " - "address structure is composed of 2 members, the " + "address structure is composed of 3 members, the " "first being a byte array containing the IPv6 " - "address (network byte order) and the second a " + "address (network byte order), the second a " "32-bit integer containing the IPv6 address " - "prefix. For the 'auto' method, given IP " - "addresses are appended to those returned by " - "automatic configuration. Addresses cannot be " + "prefix, and the third a byte array containing " + "the IPv6 address (network byte order) of the " + "gateway associated with this address, if any. " + "If no gateway is given, the third element should " + "be given as all zeros. For the 'auto' method, " + "given IP addresses are appended to those returned " + "by automatic configuration. Addresses cannot be " "used with the 'shared' or 'link-local' methods " "as the interface is automatically assigned an " "address with these methods.", @@ -797,10 +804,13 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class) } +/********************************************************************/ + struct NMIP6Address { guint32 refcount; struct in6_addr address; guint32 prefix; + struct in6_addr gateway; }; NMIP6Address * @@ -902,6 +912,27 @@ nm_ip6_address_set_prefix (NMIP6Address *address, guint32 prefix) address->prefix = prefix; } +const struct in6_addr * +nm_ip6_address_get_gateway (NMIP6Address *address) +{ + g_return_val_if_fail (address != NULL, 0); + g_return_val_if_fail (address->refcount > 0, 0); + + return &address->gateway; +} + +void +nm_ip6_address_set_gateway (NMIP6Address *address, const struct in6_addr *gw) +{ + g_return_if_fail (address != NULL); + g_return_if_fail (address->refcount > 0); + g_return_if_fail (gw != NULL); + + memcpy (&address->gateway, gw, sizeof (struct in6_addr)); +} + +/********************************************************************/ + struct NMIP6Route { guint32 refcount; diff --git a/libnm-util/nm-setting-ip6-config.h b/libnm-util/nm-setting-ip6-config.h index b089679e4e..78617c4547 100644 --- a/libnm-util/nm-setting-ip6-config.h +++ b/libnm-util/nm-setting-ip6-config.h @@ -88,6 +88,10 @@ guint32 nm_ip6_address_get_prefix (NMIP6Address *address); void nm_ip6_address_set_prefix (NMIP6Address *address, guint32 prefix); +const struct in6_addr *nm_ip6_address_get_gateway (NMIP6Address *address); +void nm_ip6_address_set_gateway (NMIP6Address *address, + const struct in6_addr *gw); + typedef struct NMIP6Route NMIP6Route; NMIP6Route * nm_ip6_route_new (void); diff --git a/libnm-util/nm-utils.c b/libnm-util/nm-utils.c index 5ca3014d79..c9c34f1ba6 100644 --- a/libnm-util/nm-utils.c +++ b/libnm-util/nm-utils.c @@ -275,6 +275,8 @@ nm_utils_init (GError **error) if (!crypto_init (error)) return FALSE; + _nm_utils_register_value_transformations (); + atexit (nm_utils_deinit); initialized = TRUE; } @@ -745,7 +747,8 @@ nm_utils_convert_string_hash_to_string (const GValue *src_value, GValue *dest_va hash = (GHashTable *) g_value_get_boxed (src_value); printable = g_string_new ("["); - g_hash_table_foreach (hash, convert_one_string_hash_entry, printable); + if (hash) + g_hash_table_foreach (hash, convert_one_string_hash_entry, printable); g_string_append (printable, " ]"); g_value_take_string (dest_value, printable->str); @@ -886,6 +889,19 @@ nm_utils_convert_ip6_addr_struct_array_to_string (const GValue *src_value, GValu continue; } g_string_append_printf (printable, "px = %u", prefix); + g_string_append (printable, ", "); + + /* IPv6 Gateway */ + tmp = g_value_array_get_nth (elements, 2); + ba_addr = g_value_get_boxed (tmp); + if (ba_addr->len != 16) { + g_string_append (printable, "invalid"); + continue; + } + addr = (struct in6_addr *) ba_addr->data; + memset (buf, 0, sizeof (buf)); + nm_utils_inet6_ntop (addr, buf); + g_string_append_printf (printable, "gw = %s", buf); g_string_append (printable, " }"); } g_string_append_c (printable, ']'); @@ -977,6 +993,58 @@ nm_utils_convert_ip6_route_struct_array_to_string (const GValue *src_value, GVal g_string_free (printable, FALSE); } +#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) +#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS)) + +static void +nm_utils_convert_old_ip6_addr_array (const GValue *src_value, GValue *dst_value) +{ + GPtrArray *src_outer_array; + GPtrArray *dst_outer_array; + guint i; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS)); + + src_outer_array = (GPtrArray *) g_value_get_boxed (src_value); + dst_outer_array = g_ptr_array_new (); + + for (i = 0; src_outer_array && (i < src_outer_array->len); i++) { + GValueArray *src_addr_array; + GValueArray *dst_addr_array; + GValue element = {0, }; + GValue *src_addr, *src_prefix; + GByteArray *ba; + + src_addr_array = (GValueArray *) g_ptr_array_index (src_outer_array, i); + + if ( (src_addr_array->n_values != 2) + || (G_VALUE_TYPE (g_value_array_get_nth (src_addr_array, 0)) != DBUS_TYPE_G_UCHAR_ARRAY) + || (G_VALUE_TYPE (g_value_array_get_nth (src_addr_array, 1)) != G_TYPE_UINT)) { + g_warning ("%s: invalid old IPv6 address type", __func__); + return; + } + + dst_addr_array = g_value_array_new (3); + + src_addr = g_value_array_get_nth (src_addr_array, 0); + g_value_array_append (dst_addr_array, src_addr); + src_prefix = g_value_array_get_nth (src_addr_array, 1); + g_value_array_append (dst_addr_array, src_prefix); + + /* Blank Gateway */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guint8 *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); + g_value_take_boxed (&element, ba); + g_value_array_append (dst_addr_array, &element); + g_value_unset (&element); + + g_ptr_array_add (dst_outer_array, dst_addr_array); + } + + g_value_take_boxed (dst_value, dst_outer_array); +} + void _nm_utils_register_value_transformations (void) { @@ -1013,6 +1081,9 @@ _nm_utils_register_value_transformations (void) g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE, G_TYPE_STRING, nm_utils_convert_ip6_route_struct_array_to_string); + g_value_register_transform_func (OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + nm_utils_convert_old_ip6_addr_array); registered = TRUE; } } @@ -1459,8 +1530,8 @@ nm_utils_ip4_get_default_prefix (guint32 ip) * @value: gvalue containing a GPtrArray of GValueArrays of (GArray of guchars) and guint32 * * Utility function to convert a #GPtrArray of #GValueArrays of (#GArray of guchars) and guint32 - * representing a list of NetworkManager IPv6 addresses (which is a pair of address - * and prefix), into a GSList of #NMIP6Address objects. The specific format of + * representing a list of NetworkManager IPv6 addresses (which is a tuple of address, + * prefix, and gateway), into a GSList of #NMIP6Address objects. The specific format of * this serialization is not guaranteed to be stable and the #GValueArray may be * extended in the future. * @@ -1479,16 +1550,28 @@ nm_utils_ip6_addresses_from_gvalue (const GValue *value) GValueArray *elements = (GValueArray *) g_ptr_array_index (addresses, i); GValue *tmp; GByteArray *ba_addr; + GByteArray *ba_gw = NULL; NMIP6Address *addr; guint32 prefix; - if ( (elements->n_values != 2) - || (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY) + if (elements->n_values < 2 || elements->n_values > 3) { + nm_warning ("%s: ignoring invalid IP6 address structure", __func__); + continue; + } + + if ( (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY) || (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)) { nm_warning ("%s: ignoring invalid IP6 address structure", __func__); continue; } + /* Check optional 3rd element (gateway) */ + if ( elements->n_values == 3 + && (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)) { + nm_warning ("%s: ignoring invalid IP6 address structure", __func__); + continue; + } + tmp = g_value_array_get_nth (elements, 0); ba_addr = g_value_get_boxed (tmp); if (ba_addr->len != 16) { @@ -1505,9 +1588,22 @@ nm_utils_ip6_addresses_from_gvalue (const GValue *value) continue; } + if (elements->n_values == 3) { + tmp = g_value_array_get_nth (elements, 2); + ba_gw = g_value_get_boxed (tmp); + if (ba_gw->len != 16) { + nm_warning ("%s: ignoring invalid IP6 gateway address of length %d", + __func__, ba_gw->len); + continue; + } + } + addr = nm_ip6_address_new (); nm_ip6_address_set_prefix (addr, prefix); nm_ip6_address_set_address (addr, (const struct in6_addr *) ba_addr->data); + if (ba_gw) + nm_ip6_address_set_gateway (addr, (const struct in6_addr *) ba_gw->data); + list = g_slist_prepend (list, addr); } @@ -1522,10 +1618,10 @@ nm_utils_ip6_addresses_from_gvalue (const GValue *value) * g_value_unset(). * * Utility function to convert a #GSList of #NMIP6Address objects into a - * GPtrArray of GValueArrays of (GArray or guchars) and guint32 representing a list - * of NetworkManager IPv6 addresses (which is a pair of address and prefix). - * The specific format of this serialization is not guaranteed to be stable and may be - * extended in the future. + * GPtrArray of GValueArrays representing a list of NetworkManager IPv6 addresses + * (which is a tuple of address, prefix, and gateway). The specific format of + * this serialization is not guaranteed to be stable and may be extended in the + * future. **/ void nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value) @@ -1541,8 +1637,9 @@ nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value) GValue element = {0, }; GByteArray *ba; - array = g_value_array_new (2); + array = g_value_array_new (3); + /* IP address */ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); ba = g_byte_array_new (); g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_address (addr), 16); @@ -1550,11 +1647,20 @@ nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value) g_value_array_append (array, &element); g_value_unset (&element); + /* Prefix */ g_value_init (&element, G_TYPE_UINT); g_value_set_uint (&element, nm_ip6_address_get_prefix (addr)); g_value_array_append (array, &element); g_value_unset (&element); + /* Gateway */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_gateway (addr), 16); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + g_ptr_array_add (addresses, array); } diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c index d00ec994e1..9530445959 100644 --- a/libnm-util/tests/test-general.c +++ b/libnm-util/tests/test-general.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 - 2009 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. * */ @@ -28,7 +28,8 @@ #include "nm-setting-connection.h" #include "nm-setting-vpn.h" - +#include "nm-setting-ip6-config.h" +#include "nm-dbus-glib-types.h" static void vpn_check_func (const char *key, const char *value, gpointer user_data) @@ -128,6 +129,99 @@ test_setting_vpn_items (void) g_object_unref (s_vpn); } +#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) +#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS)) + +/* Test that setting the IPv6 setting's 'addresses' property using the old + * IPv6 address format still works, i.e. that the GValue transformation function + * from old->new is working correctly. + */ +static void +test_setting_ip6_config_old_address_array (void) +{ + NMSettingIP6Config *s_ip6; + GPtrArray *addresses, *read_addresses; + GValueArray *array, *read_array; + GValue element = {0, }, written_value = {0, }, read_value = {0, }; + GByteArray *ba; + const guint8 addr[16] = { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, + 0x11, 0x22, 0x33, 0x44, 0x66, 0x77, 0x88, 0x99 }; + const guint8 gw[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + guint32 prefix = 56; + GValue *read_addr, *read_prefix, *read_gw; + + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + ASSERT (s_ip6 != NULL, + "ip6-old-addr", "error creating IP6 setting"); + + g_value_init (&written_value, OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS); + + addresses = g_ptr_array_new (); + array = g_value_array_new (3); + + /* IP address */ + g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_new (); + g_byte_array_append (ba, &addr[0], sizeof (addr)); + g_value_take_boxed (&element, ba); + g_value_array_append (array, &element); + g_value_unset (&element); + + /* Prefix */ + g_value_init (&element, G_TYPE_UINT); + g_value_set_uint (&element, prefix); + g_value_array_append (array, &element); + g_value_unset (&element); + + g_ptr_array_add (addresses, array); + g_value_set_boxed (&written_value, addresses); + + /* Set the address array on the object */ + g_object_set_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &written_value); + + /* Get it back so we can compare it */ + g_value_init (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS); + g_object_get_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &read_value); + + ASSERT (G_VALUE_HOLDS (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS), + "ip6-old-addr", "wrong addresses property value type '%s'", + G_VALUE_TYPE_NAME (&read_value)); + + read_addresses = (GPtrArray *) g_value_get_boxed (&read_value); + ASSERT (read_addresses != NULL, + "ip6-old-addr", "missing addresses on readback"); + ASSERT (read_addresses->len == 1, + "ip6-old-addr", "expected one address on readback"); + + read_array = (GValueArray *) g_ptr_array_index (read_addresses, 0); + + read_addr = g_value_array_get_nth (read_array, 0); + ba = g_value_get_boxed (read_addr); + ASSERT (ba->len == sizeof (addr), + "ip6-old-addr", "unexpected address item length %d", ba->len); + ASSERT (memcmp (ba->data, &addr[0], sizeof (addr)) == 0, + "ip6-old-addr", "unexpected failure comparing addresses"); + + read_prefix = g_value_array_get_nth (read_array, 1); + ASSERT (g_value_get_uint (read_prefix) == prefix, + "ip6-old-addr", "unexpected failure comparing prefix"); + + /* Ensure the gateway is all zeros, which is how the 2-item to 3-item + * conversion happens. + */ + read_gw = g_value_array_get_nth (read_array, 2); + ba = g_value_get_boxed (read_gw); + ASSERT (ba->len == sizeof (gw), + "ip6-old-addr", "unexpected gateway item length %d", ba->len); + ASSERT (memcmp (ba->data, &gw[0], sizeof (gw)) == 0, + "ip6-old-addr", "unexpected failure comparing gateways"); + + g_value_unset (&written_value); + g_value_unset (&read_value); + g_object_unref (s_ip6); +} + int main (int argc, char **argv) { GError *error = NULL; @@ -142,6 +236,7 @@ int main (int argc, char **argv) /* The tests */ test_setting_vpn_items (); + test_setting_ip6_config_old_address_array (); base = g_path_get_basename (argv[0]); fprintf (stdout, "%s: SUCCESS\n", base); diff --git a/marshallers/nm-marshal.list b/marshallers/nm-marshal.list index e19f8befcf..359fbed2d5 100644 --- a/marshallers/nm-marshal.list +++ b/marshallers/nm-marshal.list @@ -15,8 +15,8 @@ VOID:POINTER,POINTER VOID:STRING,STRING,STRING,UINT VOID:OBJECT,UINT,UINT VOID:STRING,INT -VOID:STRING,UINT -VOID:STRING,UINT,BOOLEAN +VOID:INT,UINT +VOID:INT,UINT,BOOLEAN VOID:OBJECT,OBJECT,ENUM VOID:POINTER,STRING VOID:STRING,BOXED diff --git a/po/POTFILES.in b/po/POTFILES.in index 2cf4d231c8..6cf236ad37 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -14,7 +14,6 @@ src/nm-netlink-monitor.c src/main.c src/dhcp-manager/nm-dhcp-dhclient.c src/dhcp-manager/nm-dhcp-manager.c -src/ip6-manager/nm-netlink-listener.c src/logging/nm-logging.c src/named-manager/nm-named-manager.c src/system-settings/nm-default-wired-connection.c diff --git a/src/Makefile.am b/src/Makefile.am index 214f7545c3..5d2db6e36d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,9 +68,11 @@ libtest_policy_hosts_la_SOURCES = \ nm-policy-hosts.h libtest_policy_hosts_la_CPPFLAGS = \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ $(GLIB_CFLAGS) libtest_policy_hosts_la_LIBADD = \ + ${top_builddir}/src/logging/libnm-logging.la \ $(GLIB_LIBS) @@ -123,6 +125,8 @@ NetworkManager_SOURCES = \ nm-policy.h \ nm-policy-hosts.c \ nm-policy-hosts.h \ + nm-policy-hostname.c \ + nm-policy-hostname.h \ NetworkManagerUtils.c \ NetworkManagerUtils.h \ nm-system.c \ @@ -137,8 +141,6 @@ NetworkManager_SOURCES = \ nm-properties-changed-signal.h \ wpa.c \ wpa.h \ - nm-netlink.c \ - nm-netlink.h \ nm-dhcp4-config.c \ nm-dhcp4-config.h \ nm-dhcp6-config.c \ @@ -206,9 +208,7 @@ BUILT_SOURCES = \ NetworkManager_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ - $(HAL_CFLAGS) \ $(GUDEV_CFLAGS) \ - $(OPENSSL_CFLAGS) \ $(LIBNL_CFLAGS) \ $(GMODULE_CFLAGS) \ -DG_DISABLE_DEPRECATED \ diff --git a/src/backends/NetworkManagerGeneric.c b/src/backends/NetworkManagerGeneric.c index 4f8336d9bb..8f16066fa3 100644 --- a/src/backends/NetworkManagerGeneric.c +++ b/src/backends/NetworkManagerGeneric.c @@ -1,9 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager - * - * Timothee Lecomte - * - * Heavily based on NetworkManagerRedhat.c by 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 @@ -19,7 +15,8 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2004 Red Hat, Inc. + * (C) Copyright 2004 - 2010 Red Hat, Inc. + * (C) Copyright 2006 Timothee Lecomte */ #ifdef HAVE_CONFIG_H @@ -36,8 +33,8 @@ #include "NetworkManagerGeneric.h" #include "nm-system.h" #include "NetworkManagerUtils.h" -#include "nm-netlink.h" #include "nm-logging.h" +#include "nm-netlink-monitor.h" /* Because of a bug in libnl, rtnl.h should be included before route.h */ #include diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 888e7c5402..9f9719473a 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -390,11 +390,14 @@ static DhcState state_table[] = { { DHC_REBIND4, "rebind" }, { DHC_REBIND6, "rebind6" }, { DHC_STOP, "stop" }, + { DHC_STOP6, "stop6" }, { DHC_MEDIUM, "medium" }, { DHC_TIMEOUT, "timeout" }, { DHC_FAIL, "fail" }, { DHC_EXPIRE, "expire" }, + { DHC_EXPIRE6, "expire6" }, { DHC_RELEASE, "release" }, + { DHC_RELEASE6,"release6" }, { DHC_START, "start" }, { DHC_ABEND, "abend" }, { DHC_END, "end" }, diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index 2ebb5a745c..146cf57b3f 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -53,11 +53,14 @@ typedef enum { DHC_REBIND6, /* IPv6 new/different lease */ DHC_DEPREF6, /* IPv6 lease depreferred */ DHC_STOP, /* remove old lease */ + DHC_STOP6, /* 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_EXPIRE6, /* lease has expired, renewing */ DHC_RELEASE, /* releasing lease */ + DHC_RELEASE6, /* releasing lease */ DHC_START, /* sent when dhclient started OK */ DHC_ABEND, /* dhclient exited abnormally */ DHC_END, /* dhclient exited normally */ diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 94109adcf6..635fe320b0 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -482,6 +482,13 @@ nm_dhcp_manager_start_ip4 (NMDHCPManager *self, priv = NM_DHCP_MANAGER_GET_PRIVATE (self); if (s_ip4) { + const char *method = nm_setting_ip4_config_get_method (s_ip4); + + if (method) { + /* Method must be 'auto' */ + g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL); + } + 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) { diff --git a/src/ip6-manager/Makefile.am b/src/ip6-manager/Makefile.am index 9f5229f77b..c2f5591429 100644 --- a/src/ip6-manager/Makefile.am +++ b/src/ip6-manager/Makefile.am @@ -11,14 +11,11 @@ noinst_LTLIBRARIES = libip6-manager.la libip6_manager_la_SOURCES = \ nm-ip6-manager.c \ - nm-ip6-manager.h \ - nm-netlink-listener.c \ - nm-netlink-listener.h + nm-ip6-manager.h libip6_manager_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ - $(HAL_CFLAGS) \ -DG_DISABLE_DEPRECATED libip6_manager_la_LIBADD = \ diff --git a/src/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c index be85ab0dc0..4ea9538d5b 100644 --- a/src/ip6-manager/nm-ip6-manager.c +++ b/src/ip6-manager/nm-ip6-manager.c @@ -21,14 +21,16 @@ #include #include +#include #include #include #include "nm-ip6-manager.h" -#include "nm-netlink-listener.h" +#include "nm-netlink-monitor.h" #include "NetworkManagerUtils.h" #include "nm-marshal.h" #include "nm-logging.h" +#include "nm-system.h" /* Pre-DHCP addrconf timeout, in seconds */ #define NM_IP6_TIMEOUT 10 @@ -40,8 +42,8 @@ #define IF_RS_SENT 0x10 typedef struct { - NMNetlinkListener *netlink; - GHashTable *devices_by_iface, *devices_by_index; + NMNetlinkMonitor *monitor; + GHashTable *devices; struct nl_handle *nlh; struct nl_cache *addr_cache, *route_cache; @@ -49,41 +51,6 @@ typedef struct { #define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate)) -typedef enum { - NM_IP6_DEVICE_UNCONFIGURED, - NM_IP6_DEVICE_GOT_LINK_LOCAL, - NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT, - NM_IP6_DEVICE_GOT_ADDRESS, - NM_IP6_DEVICE_WAITING_FOR_DHCP, - NM_IP6_DEVICE_GOT_DHCP, - NM_IP6_DEVICE_TIMED_OUT -} NMIP6DeviceState; - -typedef struct { - struct in6_addr addr; - time_t expires; -} NMIP6RDNSS; - -typedef struct { - NMIP6Manager *manager; - char *iface; - int index; - - char *accept_ra_path; - gboolean accept_ra_save_valid; - guint32 accept_ra_save; - - guint finish_addrconf_id; - guint config_changed_id; - - NMIP6DeviceState state; - NMIP6DeviceState target_state; - gboolean addrconf_complete; - - GArray *rdnss_servers; - guint rdnss_timeout_id; -} NMIP6Device; - G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT) enum { @@ -94,89 +61,48 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static NMIP6Manager *nm_ip6_manager_new (void); +typedef enum { + NM_IP6_DEVICE_UNCONFIGURED, + NM_IP6_DEVICE_GOT_LINK_LOCAL, + NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT, + NM_IP6_DEVICE_GOT_ADDRESS, + NM_IP6_DEVICE_TIMED_OUT +} NMIP6DeviceState; -static void netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer user_data); +typedef struct { + struct in6_addr addr; + time_t expires; +} NMIP6RDNSS; -static void nm_ip6_device_destroy (NMIP6Device *device); +/******************************************************************/ -NMIP6Manager * -nm_ip6_manager_get (void) -{ - static NMIP6Manager *singleton = NULL; +typedef struct { + NMIP6Manager *manager; + char *iface; + int ifindex; - if (!singleton) - singleton = nm_ip6_manager_new (); - g_assert (singleton); + char *accept_ra_path; + gboolean accept_ra_save_valid; + guint32 accept_ra_save; - return g_object_ref (singleton); -} + char *disable_ip6_path; + gboolean disable_ip6_save_valid; + guint32 disable_ip6_save; -static void -nm_ip6_manager_init (NMIP6Manager *manager) -{ - NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + guint finish_addrconf_id; + guint config_changed_id; - priv->devices_by_iface = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, - (GDestroyNotify) nm_ip6_device_destroy); - priv->devices_by_index = g_hash_table_new (NULL, NULL); + NMIP6DeviceState state; + NMIP6DeviceState target_state; + gboolean addrconf_complete; - priv->netlink = nm_netlink_listener_get (); - g_signal_connect (priv->netlink, "notification", - G_CALLBACK (netlink_notification), manager); - nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_IPV6_IFADDR, NULL); - nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_IPV6_PREFIX, NULL); - nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_ND_USEROPT, NULL); + GArray *rdnss_servers; + guint rdnss_timeout_id; - priv->nlh = nm_netlink_get_default_handle (); - priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh); - priv->route_cache = rtnl_route_alloc_cache (priv->nlh); -} + guint ip6flags_poll_id; -static void -finalize (GObject *object) -{ - NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object); - - g_hash_table_destroy (priv->devices_by_iface); - g_hash_table_destroy (priv->devices_by_index); - g_object_unref (priv->netlink); - nl_cache_free (priv->addr_cache); - nl_cache_free (priv->route_cache); - - G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object); -} - -static void -nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (manager_class); - - g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate)); - - /* virtual methods */ - object_class->finalize = finalize; - - /* signals */ - signals[ADDRCONF_COMPLETE] = - g_signal_new ("addrconf-complete", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete), - NULL, NULL, - _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", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed), - NULL, NULL, - _nm_marshal_VOID__STRING_UINT, - G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); -} + guint32 ra_flags; +} NMIP6Device; static void nm_ip6_device_destroy (NMIP6Device *device) @@ -189,6 +115,12 @@ nm_ip6_device_destroy (NMIP6Device *device) device->accept_ra_save ? "1\n" : "0\n"); } + /* reset the saved IPv6 value */ + if (device->disable_ip6_save_valid) { + nm_utils_do_sysctl (device->disable_ip6_path, + device->disable_ip6_save ? "1\n" : "0\n"); + } + if (device->finish_addrconf_id) g_source_remove (device->finish_addrconf_id); if (device->config_changed_id) @@ -198,27 +130,92 @@ nm_ip6_device_destroy (NMIP6Device *device) g_array_free (device->rdnss_servers, TRUE); if (device->rdnss_timeout_id) g_source_remove (device->rdnss_timeout_id); + if (device->ip6flags_poll_id) + g_source_remove (device->ip6flags_poll_id); g_free (device->accept_ra_path); g_slice_free (NMIP6Device, device); } -static NMIP6Manager * -nm_ip6_manager_new (void) +static gboolean +get_proc_sys_net_value (const char *path, const char *iface, guint32 *out_value) { - NMIP6Manager *manager; - NMIP6ManagerPrivate *priv; + GError *error; + char *contents = NULL; + gboolean success = FALSE; + long int tmp; - manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL); - priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - - if (!priv->devices_by_iface || !priv->devices_by_index) { - nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables"); - g_object_unref (manager); - manager = NULL; + if (!g_file_get_contents (path, &contents, NULL, &error)) { + nm_log_warn (LOGD_IP6, "(%s): error reading %s: (%d) %s", + iface, path, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } else { + errno = 0; + tmp = strtol (contents, NULL, 10); + if ((errno == 0) && (tmp == 0 || tmp == 1)) { + *out_value = (guint32) tmp; + success = TRUE; + } + g_free (contents); } - return manager; + return success; +} + +static NMIP6Device * +nm_ip6_device_new (NMIP6Manager *manager, int ifindex) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + NMIP6Device *device; + + g_return_val_if_fail (ifindex > 0, NULL); + + device = g_slice_new0 (NMIP6Device); + if (!device) { + nm_log_err (LOGD_IP6, "(%d): out of memory creating IP6 addrconf object.", + ifindex); + return NULL; + } + + device->ifindex = ifindex; + device->iface = g_strdup (nm_netlink_index_to_iface (ifindex)); + if (!device->iface) { + nm_log_err (LOGD_IP6, "(%d): could not find interface name from index.", + ifindex); + goto error; + } + + device->manager = manager; + + device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS)); + + g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device); + + /* Grab the original value of "accept_ra" so we can restore it when the + * device is taken down. + */ + device->accept_ra_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", + device->iface); + g_assert (device->accept_ra_path); + device->accept_ra_save_valid = get_proc_sys_net_value (device->accept_ra_path, + device->iface, + &device->accept_ra_save); + + /* and the original value of IPv6 enable/disable */ + device->disable_ip6_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6", + device->iface); + g_assert (device->disable_ip6_path); + device->disable_ip6_save_valid = get_proc_sys_net_value (device->disable_ip6_path, + device->iface, + &device->disable_ip6_save); + + return device; + +error: + nm_ip6_device_destroy (device); + return NULL; } static NMIP6Device * @@ -226,13 +223,15 @@ nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex) { NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - return g_hash_table_lookup (priv->devices_by_index, - GINT_TO_POINTER (ifindex)); + return g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); } +/******************************************************************/ + typedef struct { NMIP6Device *device; guint dhcp_opts; + gboolean success; } CallbackInfo; static gboolean @@ -241,25 +240,29 @@ finish_addrconf (gpointer user_data) CallbackInfo *info = user_data; NMIP6Device *device = info->device; NMIP6Manager *manager = device->manager; - char *iface_copy; + int ifindex; device->finish_addrconf_id = 0; device->addrconf_complete = TRUE; + ifindex = device->ifindex; - if (device->state >= device->target_state) { + /* We're done, stop polling IPv6 flags */ + if (device->ip6flags_poll_id) { + g_source_remove (device->ip6flags_poll_id); + device->ip6flags_poll_id = 0; + } + + /* And tell listeners that addrconf is complete */ + if (info->success) { g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0, - device->iface, info->dhcp_opts, TRUE); + ifindex, info->dhcp_opts, TRUE); } else { nm_log_info (LOGD_IP6, "(%s): IP6 addrconf timed out or failed.", - device->iface); + device->iface); - iface_copy = g_strdup (device->iface); - - nm_ip6_manager_cancel_addrconf (manager, device->iface); + nm_ip6_manager_cancel_addrconf (manager, ifindex); g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0, - iface_copy, info->dhcp_opts, FALSE); - - g_free (iface_copy); + ifindex, info->dhcp_opts, FALSE); } return FALSE; @@ -273,7 +276,7 @@ emit_config_changed (gpointer user_data) NMIP6Manager *manager = device->manager; device->config_changed_id = 0; - g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface, info->dhcp_opts); + g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->ifindex, info->dhcp_opts); return FALSE; } @@ -329,14 +332,14 @@ set_rdnss_timeout (NMIP6Device *device) } static CallbackInfo * -callback_info_new (NMIP6Device *device, guint dhcp_opts) +callback_info_new (NMIP6Device *device, guint dhcp_opts, gboolean success) { CallbackInfo *info; info = g_malloc0 (sizeof (CallbackInfo)); info->device = device; info->dhcp_opts = dhcp_opts; - + info->success = success; return info; } @@ -348,15 +351,14 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) struct rtnl_addr *rtnladdr; struct nl_addr *nladdr; 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); + /* Look for any IPv6 addresses the kernel may have set for the device */ + for (rtnladdr = (struct rtnl_addr *) nl_cache_get_first (priv->addr_cache); rtnladdr; - rtnladdr = (struct rtnl_addr *)nl_cache_get_next ((struct nl_object *)rtnladdr)) { - if (rtnl_addr_get_ifindex (rtnladdr) != device->index) + rtnladdr = (struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) rtnladdr)) { + if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) continue; nladdr = rtnl_addr_get_local (rtnladdr); @@ -373,32 +375,35 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) } } - /* Note: we don't want to keep a cache of links, because the - * kernel doesn't send notifications when the flags change, so the - * cached rtnl_links would have out-of-date flags. - */ - link = nm_netlink_index_to_rtnl_link (device->index); - flags = rtnl_link_get_flags (link); - rtnl_link_put (link); + /* We only care about router advertisements if we want for a real IPv6 address */ + if (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) { + if ( (device->ra_flags & IF_RA_RCVD) + && (device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)) + device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT; - 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) - dhcp_opts = IP6_DHCP_OPT_MANAGED; - else if (flags & IF_RA_OTHERCONF) - dhcp_opts = IP6_DHCP_OPT_OTHERCONF; + if (device->ra_flags & IF_RA_MANAGED) { + dhcp_opts = IP6_DHCP_OPT_MANAGED; + nm_log_dbg (LOGD_IP6, "router advertisement deferred to DHCPv6"); + } else if (device->ra_flags & IF_RA_OTHERCONF) { + dhcp_opts = IP6_DHCP_OPT_OTHERCONF; + nm_log_dbg (LOGD_IP6, "router advertisement requests parallel DHCPv6"); + } + } if (!device->addrconf_complete) { - if (device->state >= device->target_state || - device->state == NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT) { + /* Managed mode (ie DHCP only) short-circuits automatic addrconf, so + * we don't bother waiting for the device's target state to be reached + * when the RA requests managed mode. + */ + if ( (device->state >= device->target_state) + || (dhcp_opts == IP6_DHCP_OPT_MANAGED)) { /* device->finish_addrconf_id may currently be a timeout * rather than an idle, so we remove the existing source. */ if (device->finish_addrconf_id) g_source_remove (device->finish_addrconf_id); - info = callback_info_new (device, dhcp_opts); + info = callback_info_new (device, dhcp_opts, TRUE); device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, finish_addrconf, info, @@ -406,7 +411,7 @@ nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed) } } else if (config_changed) { if (!device->config_changed_id) { - info = callback_info_new (device, dhcp_opts); + info = callback_info_new (device, dhcp_opts, TRUE); device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, emit_config_changed, info, @@ -598,8 +603,59 @@ process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg) return NULL; } +static struct nla_policy link_policy[IFLA_MAX + 1] = { + [IFLA_PROTINFO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy link_prot_policy[IFLA_INET6_MAX + 1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, +}; + +static NMIP6Device * +process_newlink (NMIP6Manager *manager, struct nl_msg *msg) +{ + struct nlmsghdr *hdr = nlmsg_hdr (msg); + struct ifinfomsg *ifi; + NMIP6Device *device; + struct nlattr *tb[IFLA_MAX + 1]; + struct nlattr *pi[IFLA_INET6_MAX + 1]; + int err; + + ifi = nlmsg_data (hdr); + if (ifi->ifi_family != AF_INET6) + return NULL; + + device = nm_ip6_manager_get_device (manager, ifi->ifi_index); + if (!device || device->addrconf_complete) + return NULL; + + /* FIXME: we have to do this manually for now since libnl doesn't yet + * support the IFLA_PROTINFO attribute of NEWLINK messages. When it does, + * we can get rid of this function and just grab IFLA_PROTINFO from + * nm_ip6_device_sync_from_netlink(), then get the IFLA_INET6_FLAGS out of + * the PROTINFO. + */ + + err = nlmsg_parse (hdr, sizeof (*ifi), tb, IFLA_MAX, link_policy); + if (err < 0) + return NULL; + if (!tb[IFLA_PROTINFO]) + return NULL; + + err = nla_parse_nested (pi, IFLA_INET6_MAX, tb[IFLA_PROTINFO], link_prot_policy); + if (err < 0) + return NULL; + if (!pi[IFLA_INET6_FLAGS]) + return NULL; + + device->ra_flags = nla_get_u32 (pi[IFLA_INET6_FLAGS]); + nm_log_dbg (LOGD_IP6, "(%s): got IPv6 flags 0x%X", device->iface, device->ra_flags); + + return device; +} + static void -netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer user_data) +netlink_notification (NMNetlinkMonitor *monitor, struct nl_msg *msg, gpointer user_data) { NMIP6Manager *manager = (NMIP6Manager *) user_data; NMIP6Device *device; @@ -613,22 +669,22 @@ netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer device = process_addr (manager, msg); config_changed = TRUE; break; - case RTM_NEWROUTE: case RTM_DELROUTE: device = process_route (manager, msg); config_changed = TRUE; break; - case RTM_NEWPREFIX: device = process_prefix (manager, msg); break; - case RTM_NEWNDUSEROPT: device = process_nduseropt (manager, msg); config_changed = TRUE; break; - + case RTM_NEWLINK: + device = process_newlink (manager, msg); + config_changed = TRUE; + break; default: return; } @@ -637,75 +693,9 @@ netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer nm_ip6_device_sync_from_netlink (device, config_changed); } -static NMIP6Device * -nm_ip6_device_new (NMIP6Manager *manager, const char *iface) -{ - NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - NMIP6Device *device; - GError *error = NULL; - char *contents = NULL; - - device = g_slice_new0 (NMIP6Device); - if (!device) { - nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 addrconf object.", iface); - return NULL; - } - - device->iface = g_strdup (iface); - if (!device->iface) { - nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 addrconf object " - "property 'iface'.", - iface); - goto error; - } - device->index = nm_netlink_iface_to_index (iface); - - device->accept_ra_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", iface); - if (!device->accept_ra_path) { - nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 addrconf object " - "property 'accept_ra_path'.", - iface); - goto error; - } - - device->manager = manager; - - device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS)); - - g_hash_table_replace (priv->devices_by_iface, device->iface, device); - g_hash_table_replace (priv->devices_by_index, GINT_TO_POINTER (device->index), device); - - /* Grab the original value of "accept_ra" so we can restore it when the - * device is taken down. - */ - if (!g_file_get_contents (device->accept_ra_path, &contents, NULL, &error)) { - nm_log_warn (LOGD_IP6, "(%s): error reading %s: (%d) %s", - iface, device->accept_ra_path, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - } else { - long int tmp; - - errno = 0; - tmp = strtol (contents, NULL, 10); - if ((errno == 0) && (tmp == 0 || tmp == 1)) { - device->accept_ra_save = (guint32) tmp; - device->accept_ra_save_valid = TRUE; - } - g_free (contents); - } - - return device; - -error: - nm_ip6_device_destroy (device); - return NULL; -} - void nm_ip6_manager_prepare_interface (NMIP6Manager *manager, - const char *iface, + int ifindex, NMSettingIP6Config *s_ip6) { NMIP6ManagerPrivate *priv; @@ -713,60 +703,81 @@ nm_ip6_manager_prepare_interface (NMIP6Manager *manager, const char *method = NULL; g_return_if_fail (NM_IS_IP6_MANAGER (manager)); - g_return_if_fail (iface != NULL); + g_return_if_fail (ifindex > 0); priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - device = nm_ip6_device_new (manager, iface); + device = nm_ip6_device_new (manager, ifindex); + g_return_if_fail (device != NULL); + g_return_if_fail ( strchr (device->iface, '/') == NULL + && strcmp (device->iface, "all") != 0 + && strcmp (device->iface, "default") != 0); if (s_ip6) method = nm_setting_ip6_config_get_method (s_ip6); if (!method) method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + /* Establish target state and turn router advertisement acceptance on or off */ if ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) - || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL; - else + nm_utils_do_sysctl (device->accept_ra_path, "0\n"); + } else { device->target_state = NM_IP6_DEVICE_GOT_ADDRESS; + nm_utils_do_sysctl (device->accept_ra_path, "1\n"); + } +} - g_return_if_fail (strchr (iface, '/') == NULL && - strcmp (iface, "all") != 0 && - strcmp (iface, "default") != 0); - - /* Turn router advertisement acceptance on or off... */ - nm_utils_do_sysctl (device->accept_ra_path, - device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS ? "1\n" : "0\n"); +static gboolean +poll_ip6_flags (gpointer user_data) +{ + nm_netlink_monitor_request_ip6_info (NM_NETLINK_MONITOR (user_data), NULL); + return TRUE; } void -nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, - const char *iface) +nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex) { NMIP6ManagerPrivate *priv; NMIP6Device *device; CallbackInfo *info; g_return_if_fail (NM_IS_IP6_MANAGER (manager)); - g_return_if_fail (iface != NULL); + g_return_if_fail (ifindex > 0); priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - device = (NMIP6Device *) g_hash_table_lookup (priv->devices_by_iface, iface); + device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex)); g_return_if_fail (device != NULL); - nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", iface); + nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", device->iface); device->addrconf_complete = FALSE; + device->ra_flags = 0; /* Set up a timeout on the transaction to kill it after the timeout */ - info = callback_info_new (device, 0); + info = callback_info_new (device, 0, FALSE); device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, NM_IP6_TIMEOUT, finish_addrconf, info, (GDestroyNotify) g_free); + /* Bounce IPv6 on the interface to ensure the kernel will start looking for + * new RAs; there doesn't seem to be a better way to do this right now. + */ + if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) { + nm_utils_do_sysctl (device->disable_ip6_path, "1\n"); + g_usleep (200); + nm_utils_do_sysctl (device->disable_ip6_path, "0\n"); + } + + device->ip6flags_poll_id = g_timeout_add_seconds (1, poll_ip6_flags, priv->monitor); + + /* Kick off the initial IPv6 flags request */ + nm_netlink_monitor_request_ip6_info (priv->monitor, NULL); + /* Sync flags, etc, from netlink; this will also notice if the * device is already fully configured and schedule the * ADDRCONF_COMPLETE signal in that case. @@ -775,27 +786,23 @@ nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, } void -nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, - const char *iface) +nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex) { - NMIP6ManagerPrivate *priv; - NMIP6Device *device; - g_return_if_fail (NM_IS_IP6_MANAGER (manager)); - g_return_if_fail (iface != NULL); + g_return_if_fail (ifindex > 0); - priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - - device = g_hash_table_lookup (priv->devices_by_iface, iface); - if (device) { - g_hash_table_remove (priv->devices_by_index, GINT_TO_POINTER (device->index)); - g_hash_table_remove (priv->devices_by_iface, iface); - } + g_hash_table_remove (NM_IP6_MANAGER_GET_PRIVATE (manager)->devices, + GINT_TO_POINTER (ifindex)); } +#define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m)) +#define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m)) + +#define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m)) +#define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m)) + NMIP6Config * -nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, - const char *iface) +nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex) { NMIP6ManagerPrivate *priv; NMIP6Device *device; @@ -807,51 +814,44 @@ nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, struct rtnl_route *rtnlroute; struct nl_addr *nldest, *nlgateway; struct in6_addr *dest, *gateway; + gboolean defgw_set = FALSE; + struct in6_addr defgw; uint32_t metric; NMIP6Route *ip6route; int i; g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL); - g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (ifindex > 0, NULL); priv = NM_IP6_MANAGER_GET_PRIVATE (manager); - device = (NMIP6Device *) g_hash_table_lookup (priv->devices_by_iface, iface); + device = (NMIP6Device *) g_hash_table_lookup (priv->devices, + GINT_TO_POINTER (ifindex)); if (!device) { - nm_log_warn (LOGD_IP6, "(%s): addrconf not started.", iface); + nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex); return NULL; } config = nm_ip6_config_new (); if (!config) { nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.", - iface); + device->iface); return NULL; } - /* Add addresses */ - for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache); - rtnladdr; - rtnladdr = (struct rtnl_addr *)nl_cache_get_next ((struct nl_object *)rtnladdr)) { - if (rtnl_addr_get_ifindex (rtnladdr) != device->index) - continue; - - nladdr = rtnl_addr_get_local (rtnladdr); - if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) - continue; - - addr = nl_addr_get_binary_addr (nladdr); - ip6addr = nm_ip6_address_new (); - nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr)); - nm_ip6_address_set_address (ip6addr, addr); - nm_ip6_config_take_address (config, ip6addr); - } + /* Make sure we refill the route and address caches, otherwise we won't get + * up-to-date information here since the netlink route/addr change messages + * may be lagging a bit. + */ + nl_cache_refill (priv->nlh, priv->route_cache); + nl_cache_refill (priv->nlh, priv->addr_cache); /* Add routes */ - for (rtnlroute = (struct rtnl_route *)nl_cache_get_first (priv->route_cache); - rtnlroute; - rtnlroute = (struct rtnl_route *)nl_cache_get_next ((struct nl_object *)rtnlroute)) { - if (rtnl_route_get_oif (rtnlroute) != device->index) + for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) { + /* Make sure it's an IPv6 route for this device */ + if (rtnl_route_get_oif (rtnlroute) != device->ifindex) + continue; + if (rtnl_route_get_family (rtnlroute) != AF_INET6) continue; nldest = rtnl_route_get_dst (rtnlroute); @@ -864,6 +864,15 @@ nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, continue; gateway = nl_addr_get_binary_addr (nlgateway); + if (rtnl_route_get_dst_len (rtnlroute) == 0) { + /* Default gateway route; don't add to normal routes but to each address */ + if (!defgw_set) { + memcpy (&defgw, gateway, sizeof (defgw)); + defgw_set = TRUE; + } + continue; + } + ip6route = nm_ip6_route_new (); nm_ip6_route_set_dest (ip6route, dest); nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute)); @@ -874,6 +883,24 @@ nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, nm_ip6_config_take_route (config, ip6route); } + /* Add addresses */ + for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) { + if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex) + continue; + + nladdr = rtnl_addr_get_local (rtnladdr); + if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6) + continue; + + addr = nl_addr_get_binary_addr (nladdr); + ip6addr = nm_ip6_address_new (); + nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr)); + nm_ip6_address_set_address (ip6addr, addr); + nm_ip6_config_take_address (config, ip6addr); + if (defgw_set) + nm_ip6_address_set_gateway (ip6addr, &defgw); + } + /* Add DNS servers */ if (device->rdnss_servers) { NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data); @@ -884,3 +911,104 @@ nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, return config; } + +/******************************************************************/ + +static NMIP6Manager * +nm_ip6_manager_new (void) +{ + NMIP6Manager *manager; + NMIP6ManagerPrivate *priv; + + manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL); + priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + if (!priv->devices) { + nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables"); + g_object_unref (manager); + manager = NULL; + } + + return manager; +} + +NMIP6Manager * +nm_ip6_manager_get (void) +{ + static NMIP6Manager *singleton = NULL; + + if (!singleton) { + singleton = nm_ip6_manager_new (); + g_assert (singleton); + } else + g_object_ref (singleton); + + return singleton; +} + +static void +nm_ip6_manager_init (NMIP6Manager *manager) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager); + + priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) nm_ip6_device_destroy); + + priv->monitor = nm_netlink_monitor_get (); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_IFADDR, NULL); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_PREFIX, NULL); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_ND_USEROPT, NULL); + nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_LINK, NULL); + + g_signal_connect (priv->monitor, "notification", + G_CALLBACK (netlink_notification), manager); + + priv->nlh = nm_netlink_get_default_handle (); + priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh); + priv->route_cache = rtnl_route_alloc_cache (priv->nlh); +} + +static void +finalize (GObject *object) +{ + NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object); + + g_hash_table_destroy (priv->devices); + g_object_unref (priv->monitor); + nl_cache_free (priv->addr_cache); + nl_cache_free (priv->route_cache); + + G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object); +} + +static void +nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* signals */ + signals[ADDRCONF_COMPLETE] = + g_signal_new ("addrconf-complete", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete), + NULL, NULL, + _nm_marshal_VOID__INT_UINT_BOOLEAN, + G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN); + + signals[CONFIG_CHANGED] = + g_signal_new ("config-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed), + NULL, NULL, + _nm_marshal_VOID__INT_UINT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT); +} + diff --git a/src/ip6-manager/nm-ip6-manager.h b/src/ip6-manager/nm-ip6-manager.h index d0cf4b08a9..db920662b9 100644 --- a/src/ip6-manager/nm-ip6-manager.h +++ b/src/ip6-manager/nm-ip6-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) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #ifndef NM_IP6_MANAGER_H @@ -54,7 +54,7 @@ typedef struct { * that the initial configuration is complete. */ void (*addrconf_complete) (NMIP6Manager *manager, - char *iface, + guint32 ifindex, guint dhcp_opts, gboolean success); @@ -63,22 +63,22 @@ typedef struct { * of the interface has changed. */ void (*config_changed) (NMIP6Manager *manager, - char *iface, + guint32 ifindex, guint dhcp_opts); } NMIP6ManagerClass; GType nm_ip6_manager_get_type (void); -NMIP6Manager *nm_ip6_manager_get (void); -void nm_ip6_manager_prepare_interface (NMIP6Manager *manager, - const char *iface, - NMSettingIP6Config *s_ip6); -void nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, - const char *iface); -void nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, - const char *iface); +NMIP6Manager *nm_ip6_manager_get (void); +void nm_ip6_manager_prepare_interface (NMIP6Manager *manager, + int ifindex, + NMSettingIP6Config *s_ip6); +void nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, + int ifindex); +void nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, + int ifindex); -NMIP6Config * nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, - const char *iface); +NMIP6Config * nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, + int ifindex); #endif /* NM_IP6_MANAGER_H */ diff --git a/src/ip6-manager/nm-netlink-listener.c b/src/ip6-manager/nm-netlink-listener.c deleted file mode 100644 index 55c4d76ad4..0000000000 --- a/src/ip6-manager/nm-netlink-listener.c +++ /dev/null @@ -1,406 +0,0 @@ -/* -*- 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) 2005 - 2009 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - * Copyright (C) 2005 Ray Strode - * - * Some code borrowed from HAL: - * - * Copyright (C) 2003 David Zeuthen, - * Copyright (C) 2004 Novell, Inc. - */ - -/* FIXME: this should be merged with src/nm-netlink-monitor.c */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "NetworkManager.h" -#include "nm-system.h" -#include "nm-netlink-listener.h" -#include "nm-marshal.h" -#include "nm-netlink.h" - -#define NM_NETLINK_LISTENER_EVENT_CONDITIONS \ - ((GIOCondition) (G_IO_IN | G_IO_PRI)) - -#define NM_NETLINK_LISTENER_ERROR_CONDITIONS \ - ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) - -#define NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS \ - ((GIOCondition) (G_IO_HUP)) - -#define NM_NETLINK_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ - NM_TYPE_NETLINK_LISTENER, \ - NMNetlinkListenerPrivate)) - -typedef struct { - struct nl_handle *nlh; - struct nl_cb * nlh_cb; - struct nl_cache * nlh_link_cache; - - GIOChannel * io_channel; - guint event_id; - - guint request_status_id; -} NMNetlinkListenerPrivate; - -static gboolean nm_netlink_listener_event_handler (GIOChannel *channel, - GIOCondition io_condition, - gpointer user_data); - -static gboolean nm_netlink_listener_error_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkListener *listener); - -static gboolean nm_netlink_listener_disconnect_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkListener *listener); - -static void close_connection (NMNetlinkListener *listener); - -enum { - NOTIFICATION = 0, - ERROR, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE (NMNetlinkListener, nm_netlink_listener, G_TYPE_OBJECT); - -NMNetlinkListener * -nm_netlink_listener_get (void) -{ - static NMNetlinkListener *singleton = NULL; - - if (!singleton) - singleton = NM_NETLINK_LISTENER (g_object_new (NM_TYPE_NETLINK_LISTENER, NULL)); - else - g_object_ref (singleton); - - return singleton; -} - -static void -nm_netlink_listener_init (NMNetlinkListener *listener) -{ -} - -static void -finalize (GObject *object) -{ - NMNetlinkListenerPrivate *priv = NM_NETLINK_LISTENER_GET_PRIVATE (object); - - if (priv->request_status_id) - g_source_remove (priv->request_status_id); - - if (priv->io_channel) - close_connection (NM_NETLINK_LISTENER (object)); - - if (priv->nlh) { - nl_handle_destroy (priv->nlh); - priv->nlh = NULL; - } - - if (priv->nlh_cb) { - nl_cb_put (priv->nlh_cb); - priv->nlh_cb = NULL; - } - - G_OBJECT_CLASS (nm_netlink_listener_parent_class)->finalize (object); -} - -static void -nm_netlink_listener_class_init (NMNetlinkListenerClass *listener_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (listener_class); - - g_type_class_add_private (listener_class, sizeof (NMNetlinkListenerPrivate)); - - /* Virtual methods */ - object_class->finalize = finalize; - - /* Signals */ - signals[NOTIFICATION] = - g_signal_new ("notification", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkListenerClass, notification), - NULL, NULL, g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); - - signals[ERROR] = - g_signal_new ("error", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkListenerClass, error), - NULL, NULL, _nm_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); -} - -static int -netlink_event_input (struct nl_msg *msg, void *listener) -{ - struct nlmsghdr *hdr = nlmsg_hdr (msg); - - if (hdr->nlmsg_pid != 0) - return NL_STOP; - - g_signal_emit (listener, signals[NOTIFICATION], 0, msg); - - /* Stop processing messages */ - return NL_STOP; -} - -static gboolean -open_connection (NMNetlinkListener *listener, GError **error) -{ - NMNetlinkListenerPrivate *priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener); - int fd; - GError *channel_error = NULL; - GIOFlags channel_flags; - - g_return_val_if_fail (priv->io_channel == NULL, FALSE); - - priv->nlh_cb = nl_cb_alloc (NL_CB_DEFAULT); - priv->nlh = nl_handle_alloc_cb (priv->nlh_cb); - if (!priv->nlh) { - g_set_error (error, NM_NETLINK_LISTENER_ERROR, - NM_NETLINK_LISTENER_ERROR_NETLINK_ALLOC_HANDLE, - _("unable to allocate netlink handle: %s"), - nl_geterror ()); - goto error; - } - - nl_disable_sequence_check (priv->nlh); - nl_socket_modify_cb (priv->nlh, NL_CB_VALID, NL_CB_CUSTOM, netlink_event_input, listener); - if (nl_connect (priv->nlh, NETLINK_ROUTE) < 0) { - g_set_error (error, NM_NETLINK_LISTENER_ERROR, - NM_NETLINK_LISTENER_ERROR_NETLINK_CONNECT, - _("unable to connect to netlink: %s"), - nl_geterror ()); - goto error; - } - - fd = nl_socket_get_fd (priv->nlh); - priv->io_channel = g_io_channel_unix_new (fd); - - g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error); - /* Encoding is NULL, so no conversion error can possibly occur */ - g_assert (channel_error == NULL); - - g_io_channel_set_close_on_unref (priv->io_channel, TRUE); - channel_flags = g_io_channel_get_flags (priv->io_channel); - channel_error = NULL; - g_io_channel_set_flags (priv->io_channel, - channel_flags | G_IO_FLAG_NONBLOCK, - &channel_error); - if (channel_error != NULL) { - g_propagate_error (error, channel_error); - goto error; - } - - priv->event_id = g_io_add_watch (priv->io_channel, - (NM_NETLINK_LISTENER_EVENT_CONDITIONS | - NM_NETLINK_LISTENER_ERROR_CONDITIONS | - NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS), - nm_netlink_listener_event_handler, - listener); - return TRUE; - -error: - if (priv->io_channel) - close_connection (listener); - - if (priv->nlh) { - nl_handle_destroy (priv->nlh); - priv->nlh = NULL; - } - - if (priv->nlh_cb) { - nl_cb_put (priv->nlh_cb); - priv->nlh_cb = NULL; - } - return FALSE; -} - -static void -close_connection (NMNetlinkListener *listener) -{ - NMNetlinkListenerPrivate *priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener); - - g_return_if_fail (priv->io_channel != NULL); - - if (priv->event_id) { - g_source_remove (priv->event_id); - priv->event_id = 0; - } - - g_io_channel_shutdown (priv->io_channel, - TRUE /* flush pending data */, - NULL); - - g_io_channel_unref (priv->io_channel); - priv->io_channel = NULL; -} - -GQuark -nm_netlink_listener_error_quark (void) -{ - static GQuark error_quark = 0; - - if (error_quark == 0) - error_quark = g_quark_from_static_string ("nm-netlink-listener-error-quark"); - - return error_quark; -} - -gboolean -nm_netlink_listener_subscribe (NMNetlinkListener *listener, - int group, - GError **error) -{ - NMNetlinkListenerPrivate *priv; - - g_return_val_if_fail (NM_IS_NETLINK_LISTENER (listener), FALSE); - - priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener); - - if (!priv->nlh) { - if (!open_connection (listener, error)) - return FALSE; - } - - if (nl_socket_add_membership (priv->nlh, group) < 0) { - g_set_error (error, NM_NETLINK_LISTENER_ERROR, - NM_NETLINK_LISTENER_ERROR_NETLINK_JOIN_GROUP, - _("unable to join netlink group: %s"), - nl_geterror ()); - return FALSE; - } - - return TRUE; -} - -void -nm_netlink_listener_unsubscribe (NMNetlinkListener *listener, int group) -{ - NMNetlinkListenerPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_LISTENER (listener)); - - priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener); - g_return_if_fail (priv->nlh != NULL); - - nl_socket_drop_membership (priv->nlh, group); -} - -static gboolean -nm_netlink_listener_event_handler (GIOChannel *channel, - GIOCondition io_condition, - gpointer user_data) -{ - NMNetlinkListener *listener = (NMNetlinkListener *) user_data; - NMNetlinkListenerPrivate *priv; - GError *error = NULL; - - g_return_val_if_fail (NM_IS_NETLINK_LISTENER (listener), TRUE); - - priv = NM_NETLINK_LISTENER_GET_PRIVATE (listener); - g_return_val_if_fail (priv->event_id > 0, TRUE); - - if (io_condition & NM_NETLINK_LISTENER_ERROR_CONDITIONS) - return nm_netlink_listener_error_handler (channel, io_condition, listener); - else if (io_condition & NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS) - return nm_netlink_listener_disconnect_handler (channel, io_condition, listener); - - g_return_val_if_fail (!(io_condition & ~(NM_NETLINK_LISTENER_EVENT_CONDITIONS)), FALSE); - - if (nl_recvmsgs_default (priv->nlh) < 0) { - error = g_error_new (NM_NETLINK_LISTENER_ERROR, - NM_NETLINK_LISTENER_ERROR_PROCESSING_MESSAGE, - _("error processing netlink message: %s"), - nl_geterror ()); - - g_signal_emit (G_OBJECT (listener), - signals[ERROR], - 0, error); - g_error_free (error); - } - - return TRUE; -} - -static gboolean -nm_netlink_listener_error_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkListener *listener) -{ - GError *socket_error; - const char *err_msg; - int err_code; - socklen_t err_len; - - g_return_val_if_fail (io_condition & NM_NETLINK_LISTENER_ERROR_CONDITIONS, FALSE); - - err_code = 0; - err_len = sizeof (err_code); - if (getsockopt (g_io_channel_unix_get_fd (channel), - SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len)) - err_msg = strerror (err_code); - else - err_msg = _("error occurred while waiting for data on socket"); - - socket_error = g_error_new (NM_NETLINK_LISTENER_ERROR, - NM_NETLINK_LISTENER_ERROR_WAITING_FOR_SOCKET_DATA, - "%s", - err_msg); - - g_signal_emit (G_OBJECT (listener), - signals[ERROR], - 0, socket_error); - - g_error_free (socket_error); - - return TRUE; -} - -static gboolean -nm_netlink_listener_disconnect_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkListener *listener) -{ - - g_return_val_if_fail (!(io_condition & ~(NM_NETLINK_LISTENER_DISCONNECT_CONDITIONS)), FALSE); - return FALSE; -} - diff --git a/src/ip6-manager/nm-netlink-listener.h b/src/ip6-manager/nm-netlink-listener.h deleted file mode 100644 index 0baabac004..0000000000 --- a/src/ip6-manager/nm-netlink-listener.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Netlink socket listener - * - * 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) 2005 - 2009 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - * Copyright (C) 2005 Ray Strode - */ - -#ifndef NM_NETLINK_LISTENER_H -#define NM_NETLINK_LISTENER_H - -#include -#include - -#include "nm-netlink.h" - -G_BEGIN_DECLS - -#define NM_TYPE_NETLINK_LISTENER (nm_netlink_listener_get_type ()) -#define NM_NETLINK_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_LISTENER, NMNetlinkListener)) -#define NM_NETLINK_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETLINK_LISTENER, NMNetlinkListenerClass)) -#define NM_IS_NETLINK_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_LISTENER)) -#define NM_IS_NETLINK_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETLINK_LISTENER)) -#define NM_NETLINK_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NETLINK_LISTENER, NMNetlinkListenerClass)) -#define NM_NETLINK_LISTENER_ERROR (nm_netlink_listener_error_quark ()) - -typedef enum { - NM_NETLINK_LISTENER_ERROR_GENERIC = 0, - NM_NETLINK_LISTENER_ERROR_NETLINK_ALLOC_HANDLE, - NM_NETLINK_LISTENER_ERROR_NETLINK_CONNECT, - NM_NETLINK_LISTENER_ERROR_NETLINK_JOIN_GROUP, - NM_NETLINK_LISTENER_ERROR_NETLINK_ALLOC_LINK_CACHE, - NM_NETLINK_LISTENER_ERROR_PROCESSING_MESSAGE, - NM_NETLINK_LISTENER_ERROR_BAD_ALLOC, - NM_NETLINK_LISTENER_ERROR_WAITING_FOR_SOCKET_DATA, - NM_NETLINK_LISTENER_ERROR_LINK_CACHE_UPDATE -} NMNetlinkListenerError; - -typedef struct { - GObject parent; -} NMNetlinkListener; - -typedef struct { - GObjectClass parent_class; - - /* Signals */ - void (*notification) (NMNetlinkListener *listener, struct nl_msg *msg); - void (*error) (NMNetlinkListener *listener, GError *error); -} NMNetlinkListenerClass; - -GType nm_netlink_listener_get_type (void) G_GNUC_CONST; -GQuark nm_netlink_listener_error_quark (void) G_GNUC_CONST; - -NMNetlinkListener *nm_netlink_listener_get (void); - -gboolean nm_netlink_listener_subscribe (NMNetlinkListener *listener, - int group, - GError **error); -void nm_netlink_listener_unsubscribe (NMNetlinkListener *listener, - int group); - -G_END_DECLS - -#endif /* NM_NETLINK_LISTENER_H */ diff --git a/src/logging/Makefile.am b/src/logging/Makefile.am index d2e3e25112..51a6b25dc0 100644 --- a/src/logging/Makefile.am +++ b/src/logging/Makefile.am @@ -10,5 +10,6 @@ libnm_logging_la_CPPFLAGS = \ -DG_DISABLE_DEPRECATED libnm_logging_la_LIBADD = \ + -ldl \ $(GLIB_LIBS) diff --git a/src/named-manager/nm-named-manager.c b/src/named-manager/nm-named-manager.c index 0e3310112d..12b0df03e1 100644 --- a/src/named-manager/nm-named-manager.c +++ b/src/named-manager/nm-named-manager.c @@ -38,6 +38,7 @@ #include "nm-named-manager.h" #include "nm-ip4-config.h" +#include "nm-ip6-config.h" #include "nm-logging.h" #include "nm-system.h" #include "NetworkManagerUtils.h" @@ -60,8 +61,10 @@ G_DEFINE_TYPE(NMNamedManager, nm_named_manager, G_TYPE_OBJECT) struct NMNamedManagerPrivate { - NMIP4Config *vpn_config; - NMIP4Config *device_config; + NMIP4Config *ip4_vpn_config; + NMIP4Config *ip4_device_config; + NMIP6Config *ip6_vpn_config; + NMIP6Config *ip6_device_config; GSList *configs; }; @@ -513,25 +516,33 @@ rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error) rc.domain = NULL; rc.searches = g_ptr_array_new (); - if (priv->vpn_config) - merge_one_ip4_config (&rc, priv->vpn_config); + if (priv->ip4_vpn_config) + merge_one_ip4_config (&rc, priv->ip4_vpn_config); + if (priv->ip4_device_config) + merge_one_ip4_config (&rc, priv->ip4_device_config); - if (priv->device_config) - merge_one_ip4_config (&rc, priv->device_config); + if (priv->ip6_vpn_config) + merge_one_ip6_config (&rc, priv->ip6_vpn_config); + if (priv->ip6_device_config) + merge_one_ip6_config (&rc, priv->ip6_device_config); for (iter = priv->configs; iter; iter = g_slist_next (iter)) { + if ( (iter->data == priv->ip4_vpn_config) + || (iter->data == priv->ip4_device_config) + || (iter->data == priv->ip6_vpn_config) + || (iter->data == priv->ip6_device_config)) + continue; + if (NM_IS_IP4_CONFIG (iter->data)) { NMIP4Config *config = NM_IP4_CONFIG (iter->data); - if ((config == priv->vpn_config) || (config == priv->device_config)) - continue; - merge_one_ip4_config (&rc, config); - } else { + } else if (NM_IS_IP6_CONFIG (iter->data)) { NMIP6Config *config = NM_IP6_CONFIG (iter->data); merge_one_ip6_config (&rc, config); - } + } else + g_assert_not_reached (); } domain = rc.domain; @@ -598,10 +609,10 @@ nm_named_manager_add_ip4_config (NMNamedManager *mgr, switch (cfg_type) { case NM_NAMED_IP_CONFIG_TYPE_VPN: - priv->vpn_config = config; + priv->ip4_vpn_config = config; break; case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE: - priv->device_config = config; + priv->ip4_device_config = config; break; default: break; @@ -639,11 +650,10 @@ nm_named_manager_remove_ip4_config (NMNamedManager *mgr, priv->configs = g_slist_remove (priv->configs, config); - if (config == priv->vpn_config) - priv->vpn_config = NULL; - - if (config == priv->device_config) - priv->device_config = NULL; + if (config == priv->ip4_vpn_config) + priv->ip4_vpn_config = NULL; + if (config == priv->ip4_device_config) + priv->ip4_device_config = NULL; g_object_unref (config); @@ -669,10 +679,21 @@ nm_named_manager_add_ip6_config (NMNamedManager *mgr, g_return_val_if_fail (iface != NULL, FALSE); g_return_val_if_fail (config != NULL, FALSE); - g_return_val_if_fail (cfg_type == NM_NAMED_IP_CONFIG_TYPE_DEFAULT, FALSE); - priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr); + switch (cfg_type) { + case NM_NAMED_IP_CONFIG_TYPE_VPN: + /* FIXME: not quite yet... */ + g_return_val_if_fail (cfg_type != NM_NAMED_IP_CONFIG_TYPE_VPN, FALSE); + priv->ip6_vpn_config = config; + break; + case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE: + priv->ip6_device_config = config; + break; + default: + break; + } + /* Don't allow the same zone added twice */ if (!g_slist_find (priv->configs, config)) priv->configs = g_slist_append (priv->configs, g_object_ref (config)); @@ -705,6 +726,11 @@ nm_named_manager_remove_ip6_config (NMNamedManager *mgr, priv->configs = g_slist_remove (priv->configs, config); + if (config == priv->ip6_vpn_config) + priv->ip6_vpn_config = NULL; + if (config == priv->ip6_device_config) + priv->ip6_device_config = NULL; + g_object_unref (config); if (!rewrite_resolv_conf (mgr, iface, &error)) { diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index 1335c35e46..2529e77f65 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -72,6 +72,7 @@ typedef struct { NMActiveConnectionState state; gboolean is_default; + gboolean is_default6; gboolean shared; GSList *share_rules; @@ -88,6 +89,7 @@ enum { PROP_DEVICES, PROP_STATE, PROP_DEFAULT, + PROP_DEFAULT6, PROP_VPN, LAST_PROP @@ -104,7 +106,7 @@ device_state_changed (NMDevice *device, NMActRequest *self = NM_ACT_REQUEST (user_data); NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self); NMActiveConnectionState new_ac_state; - gboolean new_default = FALSE; + gboolean new_default = FALSE, new_default6 = FALSE; /* Set NMActiveConnection state based on the device's state */ switch (new_state) { @@ -117,6 +119,7 @@ device_state_changed (NMDevice *device, case NM_DEVICE_STATE_ACTIVATED: new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED; new_default = priv->is_default; + new_default6 = priv->is_default6; break; default: new_ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; @@ -132,6 +135,11 @@ device_state_changed (NMDevice *device, priv->is_default = new_default; g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEFAULT); } + + if (new_default6 != priv->is_default6) { + priv->is_default6 = new_default6; + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEFAULT6); + } } NMActRequest * @@ -271,6 +279,9 @@ get_property (GObject *object, guint prop_id, case PROP_DEFAULT: g_value_set_boolean (value, priv->is_default); break; + case PROP_DEFAULT6: + g_value_set_boolean (value, priv->is_default6); + break; case PROP_VPN: g_value_set_boolean (value, FALSE); break; @@ -334,7 +345,14 @@ nm_act_request_class_init (NMActRequestClass *req_class) (object_class, PROP_DEFAULT, g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, "Default", - "Is the default active connection", + "Is the default IPv4 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, + "Default6", + "Is the default IPv6 active connection", FALSE, G_PARAM_READABLE)); g_object_class_install_property @@ -548,6 +566,29 @@ nm_act_request_get_default (NMActRequest *req) return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default; } +void +nm_act_request_set_default6 (NMActRequest *req, gboolean is_default6) +{ + NMActRequestPrivate *priv; + + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv = NM_ACT_REQUEST_GET_PRIVATE (req); + if (priv->is_default6 == is_default6) + return; + + priv->is_default6 = is_default6; + g_object_notify (G_OBJECT (req), NM_ACTIVE_CONNECTION_DEFAULT6); +} + +gboolean +nm_act_request_get_default6 (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default6; +} + static void share_child_setup (gpointer user_data G_GNUC_UNUSED) { diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h index a3c0d97446..a243694521 100644 --- a/src/nm-activation-request.h +++ b/src/nm-activation-request.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. * - * (C) Copyright 2005 - 2008 Red Hat, Inc. + * (C) Copyright 2005 - 2010 Red Hat, Inc. */ #ifndef NM_ACTIVATION_REQUEST_H @@ -76,6 +76,10 @@ void nm_act_request_set_default (NMActRequest *req, gboolean is_default gboolean nm_act_request_get_default (NMActRequest *req); +void nm_act_request_set_default6 (NMActRequest *req, gboolean is_default6); + +gboolean nm_act_request_get_default6 (NMActRequest *req); + gboolean nm_act_request_get_shared (NMActRequest *req); void nm_act_request_set_shared (NMActRequest *req, gboolean shared); diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 4a1f2e1477..6a463cb257 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.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) 2008 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. */ #ifndef NM_ACTIVE_CONNECTION_H @@ -30,6 +30,7 @@ #define NM_ACTIVE_CONNECTION_DEVICES "devices" #define NM_ACTIVE_CONNECTION_STATE "state" #define NM_ACTIVE_CONNECTION_DEFAULT "default" +#define NM_ACTIVE_CONNECTION_DEFAULT6 "default6" #define NM_ACTIVE_CONNECTION_VPN "vpn" char *nm_active_connection_get_next_object_path (void); diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index f4581e9ac6..be6d4e2f3c 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -34,6 +34,8 @@ #include #include +#include + #include "nm-glib-compat.h" #include "nm-device-ethernet.h" #include "nm-device-interface.h" @@ -43,7 +45,6 @@ #include "nm-supplicant-manager.h" #include "nm-supplicant-interface.h" #include "nm-supplicant-config.h" -#include "nm-netlink.h" #include "nm-netlink-monitor.h" #include "nm-system.h" #include "nm-setting-connection.h" @@ -105,7 +106,6 @@ typedef struct { struct ether_addr hw_addr; gboolean carrier; - guint32 ifindex; NMNetlinkMonitor * monitor; gulong link_connected_id; @@ -133,7 +133,6 @@ enum { PROP_HW_ADDRESS, PROP_SPEED, PROP_CARRIER, - PROP_IFINDEX, LAST_PROP }; @@ -246,11 +245,10 @@ carrier_on (NMNetlinkMonitor *monitor, { NMDevice *device = NM_DEVICE (user_data); NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); guint32 caps; /* Make sure signal is for us */ - if (idx == priv->ifindex) { + if (idx == nm_device_get_ifindex (device)) { /* Ignore spurious netlink messages */ caps = nm_device_get_capabilities (device); if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT)) @@ -267,11 +265,10 @@ carrier_off (NMNetlinkMonitor *monitor, { NMDevice *device = NM_DEVICE (user_data); NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); guint32 caps; /* Make sure signal is for us */ - if (idx == priv->ifindex) { + if (idx == nm_device_get_ifindex (device)) { NMDeviceState state; gboolean defer = FALSE; @@ -312,7 +309,8 @@ constructor (GType type, priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); nm_log_dbg (LOGD_HW | LOGD_OLPC_MESH, "(%s): kernel ifindex %d", - nm_device_get_iface (NM_DEVICE (self)), priv->ifindex); + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); caps = nm_device_get_capabilities (self); if (caps & NM_DEVICE_CAP_CARRIER_DETECT) { @@ -331,7 +329,7 @@ constructor (GType type, /* Get initial link state */ if (!nm_netlink_monitor_get_flags_sync (priv->monitor, - priv->ifindex, + nm_device_get_ifindex (NM_DEVICE (self)), &ifflags, &error)) { nm_log_warn (LOGD_HW | LOGD_ETHER, @@ -426,8 +424,7 @@ real_hw_take_down (NMDevice *dev) NMDevice * nm_device_ethernet_new (const char *udi, const char *iface, - const char *driver, - guint32 ifindex) + const char *driver) { g_return_val_if_fail (udi != NULL, NULL); g_return_val_if_fail (iface != NULL, NULL); @@ -437,7 +434,6 @@ nm_device_ethernet_new (const char *udi, NM_DEVICE_INTERFACE_UDI, udi, NM_DEVICE_INTERFACE_IFACE, iface, NM_DEVICE_INTERFACE_DRIVER, driver, - NM_DEVICE_ETHERNET_IFINDEX, ifindex, NM_DEVICE_INTERFACE_TYPE_DESC, "Ethernet", NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET, NULL); @@ -459,14 +455,6 @@ nm_device_ethernet_get_address (NMDeviceEthernet *self, struct ether_addr *addr) memcpy (addr, &(NM_DEVICE_ETHERNET_GET_PRIVATE (self)->hw_addr), sizeof (struct ether_addr)); } -guint32 -nm_device_ethernet_get_ifindex (NMDeviceEthernet *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - - return NM_DEVICE_ETHERNET_GET_PRIVATE (self)->ifindex; -} - /* Returns speed in Mb/s */ static guint32 nm_device_ethernet_get_speed (NMDeviceEthernet *self) @@ -1618,7 +1606,7 @@ ip4_match_config (NMDevice *self, NMConnection *connection) int ifindex; AddrData check_data; - ifindex = nm_device_ethernet_get_ifindex (NM_DEVICE_ETHERNET (self)); + ifindex = nm_device_get_ifindex (self); s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); g_assert (s_con); @@ -1790,9 +1778,6 @@ get_property (GObject *object, guint prop_id, case PROP_CARRIER: g_value_set_boolean (value, priv->carrier); break; - case PROP_IFINDEX: - g_value_set_uint (value, priv->ifindex); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1803,13 +1788,7 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object); - switch (prop_id) { - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_uint (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1876,14 +1855,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) FALSE, G_PARAM_READABLE)); - g_object_class_install_property - (object_class, PROP_IFINDEX, - g_param_spec_uint (NM_DEVICE_ETHERNET_IFINDEX, - "Ifindex", - "Interface index", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); - /* Signals */ signals[PROPERTIES_CHANGED] = nm_properties_changed_signal_new (object_class, diff --git a/src/nm-device-ethernet.h b/src/nm-device-ethernet.h index 643572f700..7bb3db0086 100644 --- a/src/nm-device-ethernet.h +++ b/src/nm-device-ethernet.h @@ -39,7 +39,6 @@ G_BEGIN_DECLS #define NM_DEVICE_ETHERNET_HW_ADDRESS "hw-address" #define NM_DEVICE_ETHERNET_SPEED "speed" #define NM_DEVICE_ETHERNET_CARRIER "carrier" -#define NM_DEVICE_ETHERNET_IFINDEX "ifindex" typedef struct { NMDevice parent; @@ -58,14 +57,11 @@ GType nm_device_ethernet_get_type (void); NMDevice *nm_device_ethernet_new (const char *udi, const char *iface, - const char *driver, - guint32 ifindex); + const char *driver); void nm_device_ethernet_get_address (NMDeviceEthernet *dev, struct ether_addr *addr); -guint32 nm_device_ethernet_get_ifindex (NMDeviceEthernet *dev); - G_END_DECLS #endif /* NM_DEVICE_ETHERNET_H */ diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c index 4040626c2b..85f58d5563 100644 --- a/src/nm-device-interface.c +++ b/src/nm-device-interface.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. */ #include "nm-marshal.h" @@ -186,6 +186,14 @@ nm_device_interface_init (gpointer g_iface) RFKILL_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + g_object_interface_install_property + (g_iface, + g_param_spec_int (NM_DEVICE_INTERFACE_IFINDEX, + "Ifindex", + "Ifindex", + 0, G_MAXINT, 0, + G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + /* Signals */ g_signal_new ("state-changed", iface_type, diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h index adbafcaa25..ec27f6e820 100644 --- a/src/nm-device-interface.h +++ b/src/nm-device-interface.h @@ -57,8 +57,9 @@ typedef enum #define NM_DEVICE_INTERFACE_STATE "state" #define NM_DEVICE_INTERFACE_DEVICE_TYPE "device-type" /* ugh */ #define NM_DEVICE_INTERFACE_MANAGED "managed" -#define NM_DEVICE_INTERFACE_TYPE_DESC "type-desc" /* Internal only */ +#define NM_DEVICE_INTERFACE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_INTERFACE_RFKILL_TYPE "rfkill-type" /* Internal only */ +#define NM_DEVICE_INTERFACE_IFINDEX "ifindex" /* Internal only */ typedef enum { NM_DEVICE_INTERFACE_PROP_FIRST = 0x1000, @@ -77,6 +78,7 @@ typedef enum { NM_DEVICE_INTERFACE_PROP_MANAGED, NM_DEVICE_INTERFACE_PROP_TYPE_DESC, NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE, + NM_DEVICE_INTERFACE_PROP_IFINDEX, } NMDeviceInterfaceProp; diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c index 2093a8e887..f73108a793 100644 --- a/src/nm-device-olpc-mesh.c +++ b/src/nm-device-olpc-mesh.c @@ -69,7 +69,6 @@ enum { PROP_HW_ADDRESS, PROP_COMPANION, PROP_ACTIVE_CHANNEL, - PROP_IFINDEX, LAST_PROP }; @@ -98,7 +97,6 @@ struct _NMDeviceOlpcMeshPrivate gboolean dispose_has_run; struct ether_addr hw_addr; - guint32 ifindex; GByteArray * ssid; @@ -263,7 +261,8 @@ constructor (GType type, priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); nm_log_dbg (LOGD_HW | LOGD_OLPC_MESH, "(%s): kernel ifindex %d", - nm_device_get_iface (NM_DEVICE (self)), priv->ifindex); + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); iface = nm_device_get_iface (NM_DEVICE (self)); fd = socket (PF_INET, SOCK_DGRAM, 0); @@ -539,15 +538,6 @@ nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid) close (sk); } - -guint32 -nm_device_olpc_mesh_get_ifindex (NMDeviceOlpcMesh *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - - return NM_DEVICE_OLPC_MESH_GET_PRIVATE (self)->ifindex; -} - /****************************************************************************/ static void @@ -701,9 +691,6 @@ get_property (GObject *object, guint prop_id, case PROP_ACTIVE_CHANNEL: g_value_set_uint (value, nm_device_olpc_mesh_get_channel (device)); break; - case PROP_IFINDEX: - g_value_set_uint (value, nm_device_olpc_mesh_get_ifindex (device)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -714,13 +701,7 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object); - switch (prop_id) { - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_uint (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -780,13 +761,6 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) 0, G_MAXUINT32, 0, G_PARAM_READABLE)); - g_object_class_install_property (object_class, PROP_IFINDEX, - g_param_spec_uint (NM_DEVICE_OLPC_MESH_IFINDEX, - "Ifindex", - "Interface index", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - signals[PROPERTIES_CHANGED] = nm_properties_changed_signal_new (object_class, G_STRUCT_OFFSET (NMDeviceOlpcMeshClass, properties_changed)); @@ -986,8 +960,7 @@ state_changed_cb (NMDevice *device, NMDeviceState state, gpointer user_data) NMDevice * nm_device_olpc_mesh_new (const char *udi, const char *iface, - const char *driver, - guint32 ifindex) + const char *driver) { GObject *obj; @@ -999,7 +972,6 @@ nm_device_olpc_mesh_new (const char *udi, NM_DEVICE_INTERFACE_UDI, udi, NM_DEVICE_INTERFACE_IFACE, iface, NM_DEVICE_INTERFACE_DRIVER, driver, - NM_DEVICE_OLPC_MESH_IFINDEX, ifindex, NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 OLPC Mesh", NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH, NULL); diff --git a/src/nm-device-olpc-mesh.h b/src/nm-device-olpc-mesh.h index adf3d2d956..bcc03e57ab 100644 --- a/src/nm-device-olpc-mesh.h +++ b/src/nm-device-olpc-mesh.h @@ -46,7 +46,6 @@ G_BEGIN_DECLS #define NM_DEVICE_OLPC_MESH_COMPANION "companion" #define NM_DEVICE_OLPC_MESH_BITRATE "bitrate" #define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel" -#define NM_DEVICE_OLPC_MESH_IFINDEX "ifindex" #ifndef NM_DEVICE_OLPC_MESH_DEFINED #define NM_DEVICE_OLPC_MESH_DEFINED @@ -75,10 +74,7 @@ GType nm_device_olpc_mesh_get_type (void); NMDevice *nm_device_olpc_mesh_new (const char *udi, const char *iface, - const char *driver, - guint32 ifindex); - -guint32 nm_device_olpc_mesh_get_ifindex (NMDeviceOlpcMesh *self); + const char *driver); G_END_DECLS diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index cccaf1194e..b598f2b675 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -82,7 +82,6 @@ enum { PROP_BITRATE, PROP_ACTIVE_ACCESS_POINT, PROP_CAPABILITIES, - PROP_IFINDEX, PROP_SCANNING, PROP_IPW_RFKILL_STATE, @@ -144,7 +143,6 @@ struct _NMDeviceWifiPrivate { gboolean disposed; struct ether_addr hw_addr; - guint32 ifindex; /* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */ char * ipw_rfkill_path; @@ -579,7 +577,8 @@ constructor (GType type, priv = NM_DEVICE_WIFI_GET_PRIVATE (self); nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d", - nm_device_get_iface (NM_DEVICE (self)), priv->ifindex); + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); memset (&range, 0, sizeof (struct iw_range)); success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len); @@ -3462,14 +3461,6 @@ device_state_changed (NMDevice *device, remove_all_aps (self); } -guint32 -nm_device_wifi_get_ifindex (NMDeviceWifi *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - - return NM_DEVICE_WIFI_GET_PRIVATE (self)->ifindex; -} - NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self) { @@ -3563,8 +3554,7 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled) NMDevice * nm_device_wifi_new (const char *udi, const char *iface, - const char *driver, - guint32 ifindex) + const char *driver) { g_return_val_if_fail (udi != NULL, NULL); g_return_val_if_fail (iface != NULL, NULL); @@ -3574,7 +3564,6 @@ nm_device_wifi_new (const char *udi, NM_DEVICE_INTERFACE_UDI, udi, NM_DEVICE_INTERFACE_IFACE, iface, NM_DEVICE_INTERFACE_DRIVER, driver, - NM_DEVICE_WIFI_IFINDEX, ifindex, NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 WiFi", NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WLAN, @@ -3675,9 +3664,6 @@ get_property (GObject *object, guint prop_id, else g_value_set_boxed (value, "/"); break; - case PROP_IFINDEX: - g_value_set_uint (value, nm_device_wifi_get_ifindex (device)); - break; case PROP_SCANNING: g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->supplicant.iface)); break; @@ -3697,10 +3683,6 @@ set_property (GObject *object, guint prop_id, NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object); switch (prop_id) { - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_uint (value); - break; case PROP_IPW_RFKILL_STATE: /* construct only */ priv->ipw_rfkill_state = g_value_get_uint (value); @@ -3788,13 +3770,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, G_PARAM_READABLE)); - g_object_class_install_property (object_class, PROP_IFINDEX, - g_param_spec_uint (NM_DEVICE_WIFI_IFINDEX, - "Ifindex", - "Interface index", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); - g_object_class_install_property (object_class, PROP_SCANNING, g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, "Scanning", diff --git a/src/nm-device-wifi.h b/src/nm-device-wifi.h index 45b573a03c..11ac885735 100644 --- a/src/nm-device-wifi.h +++ b/src/nm-device-wifi.h @@ -47,7 +47,6 @@ G_BEGIN_DECLS #define NM_DEVICE_WIFI_BITRATE "bitrate" #define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point" #define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities" -#define NM_DEVICE_WIFI_IFINDEX "ifindex" #define NM_DEVICE_WIFI_SCANNING "scanning" #define NM_DEVICE_WIFI_IPW_RFKILL_STATE "ipw-rfkill-state" @@ -84,8 +83,7 @@ GType nm_device_wifi_get_type (void); NMDevice *nm_device_wifi_new (const char *udi, const char *iface, - const char *driver, - guint32 ifindex); + const char *driver); void nm_device_wifi_get_address (NMDeviceWifi *dev, struct ether_addr *addr); @@ -102,8 +100,6 @@ NM80211Mode nm_device_wifi_get_mode (NMDeviceWifi *self); NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self); -guint32 nm_device_wifi_get_ifindex (NMDeviceWifi *self); - RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self); G_END_DECLS diff --git a/src/nm-device.c b/src/nm-device.c index 280d4e4105..9d6ee5f410 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -45,7 +45,7 @@ #include "nm-named-manager.h" #include "nm-utils.h" #include "nm-logging.h" -#include "nm-netlink.h" +#include "nm-netlink-monitor.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" #include "nm-setting-connection.h" @@ -83,7 +83,9 @@ typedef struct { char * udi; char * path; char * iface; /* may change, could be renamed by user */ + int ifindex; char * ip_iface; + int ip_ifindex; NMDeviceType type; char * type_desc; guint32 capabilities; @@ -304,6 +306,13 @@ nm_device_get_iface (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->iface; } +int +nm_device_get_ifindex (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return NM_DEVICE_GET_PRIVATE (self)->ifindex; +} const char * nm_device_get_ip_iface (NMDevice *self) @@ -317,14 +326,36 @@ nm_device_get_ip_iface (NMDevice *self) return priv->ip_iface ? priv->ip_iface : priv->iface; } +int +nm_device_get_ip_ifindex (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (self != NULL, 0); + + priv = NM_DEVICE_GET_PRIVATE (self); + /* If it's not set, default to iface */ + return priv->ip_iface ? priv->ip_ifindex : priv->ifindex; +} void nm_device_set_ip_iface (NMDevice *self, const char *iface) { + NMDevicePrivate *priv; + g_return_if_fail (NM_IS_DEVICE (self)); - g_free (NM_DEVICE_GET_PRIVATE (self)->ip_iface); - NM_DEVICE_GET_PRIVATE (self)->ip_iface = iface ? g_strdup (iface) : NULL; + priv = NM_DEVICE_GET_PRIVATE (self); + g_free (priv->ip_iface); + priv->ip_ifindex = 0; + + priv->ip_iface = g_strdup (iface); + if (priv->ip_iface) { + priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface); + if (!priv->ip_ifindex) { + nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface); + } + } } @@ -545,7 +576,7 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family) static void ip6_addrconf_complete (NMIP6Manager *ip6_manager, - const char *iface, + int ifindex, guint dhcp_opts, gboolean success, gpointer user_data) @@ -558,7 +589,7 @@ ip6_addrconf_complete (NMIP6Manager *ip6_manager, NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; NMDeviceState state; - if (strcmp (nm_device_get_ip_iface (self), iface) != 0) + if (ifindex != nm_device_get_ip_ifindex (self)) return; req = nm_device_get_act_request (self); if (!req) @@ -614,13 +645,13 @@ ip6_addrconf_complete (NMIP6Manager *ip6_manager, static void ip6_config_changed (NMIP6Manager *ip6_manager, - const char *iface, + int ifindex, guint dhcp_opts, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); - if (strcmp (nm_device_get_ip_iface (self), iface) != 0) + if (ifindex != nm_device_get_ip_ifindex (self)) return; if (!nm_device_get_act_request (self)) return; @@ -675,7 +706,9 @@ addrconf6_setup (NMDevice *self) 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); + nm_ip6_manager_prepare_interface (priv->ip6_manager, + nm_device_get_ip_ifindex (self), + s_ip6); } static void @@ -697,6 +730,7 @@ addrconf6_cleanup (NMDevice *self) priv->ip6_config_changed_sigid = 0; } + nm_ip6_manager_cancel_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self)); g_object_unref (priv->ip6_manager); priv->ip6_manager = NULL; } @@ -1278,6 +1312,22 @@ dhcp_state_changed (NMDHCPClient *client, 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; + case DHC_STOP: + case DHC_STOP6: + case DHC_EXPIRE: + case DHC_EXPIRE6: + if (dev_state == NM_DEVICE_STATE_ACTIVATED) { + 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 */ + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } + break; default: break; } @@ -1304,6 +1354,7 @@ dhcp_timeout (NMDHCPClient *client, gpointer user_data) static NMActStageReturn real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; NMSettingConnection *s_con; NMSettingIP4Config *s_ip4; @@ -1333,7 +1384,6 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) method = nm_setting_ip4_config_get_method (s_ip4); if (!s_ip4 || !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint8 *anycast = NULL; /* Begin a DHCP transaction on the interface */ @@ -1389,6 +1439,10 @@ real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason) *reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED; ret = NM_ACT_STAGE_RETURN_FAILURE; } + } else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { + /* Nothing to do... */ + priv->ip4_ready = TRUE; + ret = NM_ACT_STAGE_RETURN_STOP; } return ret; @@ -1481,10 +1535,14 @@ real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason) 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); + nm_ip6_manager_begin_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self)); ret = NM_ACT_STAGE_RETURN_POSTPONE; } else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) ret = dhcp6_start (self, connection, IP6_DHCP_OPT_MANAGED, reason); + else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + priv->ip6_ready = TRUE; + ret = NM_ACT_STAGE_RETURN_STOP; + } return ret; } @@ -1517,6 +1575,8 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); goto out; + } else if (ret == NM_ACT_STAGE_RETURN_STOP) { + /* Nothing to do */ } else g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); @@ -1526,6 +1586,8 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); goto out; + } else if (ret == NM_ACT_STAGE_RETURN_STOP) { + /* Nothing to do */ } else g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); @@ -1674,7 +1736,8 @@ real_act_stage4_get_ip4_config (NMDevice *self, *config = nm_device_new_ip4_shared_config (self, reason); if (*config) priv->dnsmasq_manager = nm_dnsmasq_manager_new (ip_iface); - } + } else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + ret = NM_ACT_STAGE_RETURN_SUCCESS; } if (!*config) { @@ -1866,7 +1929,8 @@ real_act_stage4_get_ip6_config (NMDevice *self, s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, ip_iface); + *config = nm_ip6_manager_get_ip6_config (priv->ip6_manager, + nm_device_get_ip_ifindex (self)); if (*config) { /* Merge user-defined overrides into the IP6Config to be applied */ nm_utils_merge_ip6_config (*config, s_ip6); @@ -1896,10 +1960,8 @@ real_act_stage4_get_ip6_config (NMDevice *self, } else { *reason = NM_DEVICE_STATE_REASON_DHCP_ERROR; } - } else { - *config = NULL; + } else ret = NM_ACT_STAGE_RETURN_SUCCESS; - } out: return ret; @@ -2022,7 +2084,7 @@ nm_device_activate_stage4_ip6_config_timeout (gpointer user_data) goto out; } g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - /* FIXME g_assert (ip6_config); */ + g_assert (ip6_config); g_object_set_data (G_OBJECT (nm_device_get_act_request (self)), NM_ACT_REQUEST_IP6_CONFIG, ip6_config); @@ -2230,6 +2292,16 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data) assumed = nm_act_request_get_assumed (priv->act_request); + if (!ip6_config && !ip4_config) { + nm_log_info (LOGD_DEVICE, + "Activation (%s) Stage 5 of 5 (IP Configure Commit) failed (no IP configuration found)", + iface); + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + goto out; + } + if (ip4_config && !nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); goto out; @@ -2239,6 +2311,8 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data) nm_log_info (LOGD_DEVICE | LOGD_IP6, "Activation (%s) Stage 5 of 5 (IP Configure Commit) IPv6 failed", iface); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; } connection = nm_act_request_get_connection (nm_device_get_act_request (self)); @@ -2248,7 +2322,7 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data) if (s_ip4) method = nm_setting_ip4_config_get_method (s_ip4); - if (s_ip4 && !strcmp (method, "shared")) { + if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { if (!start_sharing (self)) { nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); @@ -3088,7 +3162,14 @@ set_property (GObject *object, guint prop_id, break; case NM_DEVICE_INTERFACE_PROP_IFACE: g_free (priv->iface); + priv->ifindex = 0; priv->iface = g_value_dup_string (value); + if (priv->iface) { + priv->ifindex = nm_netlink_iface_to_index (priv->iface); + if (priv->ifindex < 0) { + nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface); + } + } break; case NM_DEVICE_INTERFACE_PROP_DRIVER: priv->driver = g_strdup (g_value_get_string (value)); @@ -3136,6 +3217,9 @@ get_property (GObject *object, guint prop_id, case NM_DEVICE_INTERFACE_PROP_IFACE: g_value_set_string (value, priv->iface); break; + case NM_DEVICE_INTERFACE_PROP_IFINDEX: + g_value_set_int (value, priv->ifindex); + break; case NM_DEVICE_INTERFACE_PROP_DRIVER: g_value_set_string (value, priv->driver); break; @@ -3234,6 +3318,10 @@ nm_device_class_init (NMDeviceClass *klass) NM_DEVICE_INTERFACE_PROP_IFACE, NM_DEVICE_INTERFACE_IFACE); + g_object_class_override_property (object_class, + NM_DEVICE_INTERFACE_PROP_IFINDEX, + NM_DEVICE_INTERFACE_IFINDEX); + g_object_class_override_property (object_class, NM_DEVICE_INTERFACE_PROP_DRIVER, NM_DEVICE_INTERFACE_DRIVER); diff --git a/src/nm-device.h b/src/nm-device.h index 5fcde5c338..202f392413 100644 --- a/src/nm-device.h +++ b/src/nm-device.h @@ -38,7 +38,8 @@ typedef enum NMActStageReturn { NM_ACT_STAGE_RETURN_FAILURE = 0, NM_ACT_STAGE_RETURN_SUCCESS, - NM_ACT_STAGE_RETURN_POSTPONE + NM_ACT_STAGE_RETURN_POSTPONE, + NM_ACT_STAGE_RETURN_STOP /* This activation chain is done */ } NMActStageReturn; @@ -128,7 +129,9 @@ void nm_device_set_path (NMDevice *dev, const char *path); const char * nm_device_get_udi (NMDevice *dev); const char * nm_device_get_iface (NMDevice *dev); +int nm_device_get_ifindex (NMDevice *dev); const char * nm_device_get_ip_iface (NMDevice *dev); +int nm_device_get_ip_ifindex(NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev); const char * nm_device_get_type_desc (NMDevice *dev); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 66c328eef9..e3fe92dbd3 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.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. */ @@ -692,34 +692,33 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class) object_class->finalize = finalize; /* properties */ - g_object_class_install_property - (object_class, PROP_ADDRESSES, - g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES, - "Addresses", - "IP6 addresses", - DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, - G_PARAM_READABLE)); - g_object_class_install_property - (object_class, PROP_NAMESERVERS, - g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS, - "Nameservers", - "DNS list", - DBUS_TYPE_G_UINT_ARRAY, - G_PARAM_READABLE)); - g_object_class_install_property - (object_class, PROP_DOMAINS, - g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, - "Domains", - "Domains", - DBUS_TYPE_G_ARRAY_OF_STRING, - G_PARAM_READABLE)); - g_object_class_install_property - (object_class, PROP_ROUTES, - g_param_spec_boxed (NM_IP6_CONFIG_ROUTES, - "Routes", - "Routes", - DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, - G_PARAM_READABLE)); + g_object_class_install_property (object_class, PROP_ADDRESSES, + g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES, + "Addresses", + "IP6 addresses", + DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_NAMESERVERS, + g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS, + "Nameservers", + "DNS list", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_DOMAINS, + g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, + "Domains", + "Domains", + DBUS_TYPE_G_ARRAY_OF_STRING, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_ROUTES, + g_param_spec_boxed (NM_IP6_CONFIG_ROUTES, + "Routes", + "Routes", + DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE, + G_PARAM_READABLE)); dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class), &dbus_glib_nm_ip6_config_object_info); diff --git a/src/nm-manager.c b/src/nm-manager.c index 6eaf6a51ef..abe505bba5 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1523,7 +1523,8 @@ add_device (NMManager *self, NMDevice *device) driver = nm_device_get_driver (device); if (!driver) driver = "unknown"; - nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s')", iface, type_desc, driver); + nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)", + iface, type_desc, driver, nm_device_get_ifindex (device)); path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); nm_device_set_path (device, path); @@ -1794,20 +1795,11 @@ find_device_by_ifindex (NMManager *self, guint32 ifindex) GSList *iter; for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = NM_DEVICE (iter->data); - gint candidate_idx = 0; + NMDevice *candidate = NM_DEVICE (iter->data); - if (NM_IS_DEVICE_ETHERNET (device)) - candidate_idx = nm_device_ethernet_get_ifindex (NM_DEVICE_ETHERNET (device)); - else if (NM_IS_DEVICE_WIFI (device)) - candidate_idx = nm_device_wifi_get_ifindex (NM_DEVICE_WIFI (device)); - else if (NM_IS_DEVICE_OLPC_MESH (device)) - candidate_idx = nm_device_olpc_mesh_get_ifindex (NM_DEVICE_OLPC_MESH (device)); - - if (candidate_idx == ifindex) - return device; + if (ifindex == nm_device_get_ifindex (candidate)) + return candidate; } - return NULL; } diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c index 918d637d72..10bf239d5d 100644 --- a/src/nm-netlink-monitor.c +++ b/src/nm-netlink-monitor.c @@ -25,6 +25,9 @@ * Copyright (C) 2004 Novell, Inc. */ +/* for struct ucred and LIBNL_NEEDS_ADDR_CACHING_WORKAROUND */ +#include + #include #include #include @@ -35,153 +38,55 @@ #include #include #include -#include +#include +#include +#include #include #include -#include "NetworkManager.h" -#include "nm-system.h" #include "nm-netlink-monitor.h" #include "nm-logging.h" -#include "nm-marshal.h" -#include "nm-netlink.h" -#define NM_NETLINK_MONITOR_EVENT_CONDITIONS \ - ((GIOCondition) (G_IO_IN | G_IO_PRI)) - -#define NM_NETLINK_MONITOR_ERROR_CONDITIONS \ - ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) - -#define NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS \ - ((GIOCondition) (G_IO_HUP)) +#define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) +#define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) +#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) #define NM_NETLINK_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ NM_TYPE_NETLINK_MONITOR, \ NMNetlinkMonitorPrivate)) typedef struct { - struct nl_handle *nlh; - struct nl_cb * nlh_cb; - struct nl_cache * nlh_link_cache; - + /* Async event listener connection */ + struct nl_handle *nlh_event; GIOChannel * io_channel; guint event_id; + /* Sync/blocking request/response connection */ + struct nl_handle *nlh_sync; + struct nl_cache * link_cache; + guint request_status_id; + + GHashTable *subscriptions; } NMNetlinkMonitorPrivate; -static gboolean nm_netlink_monitor_event_handler (GIOChannel *channel, - GIOCondition io_condition, - gpointer user_data); - -static gboolean nm_netlink_monitor_error_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkMonitor *monitor); - -static gboolean nm_netlink_monitor_disconnect_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkMonitor *monitor); - enum { - CARRIER_ON = 0, - CARRIER_OFF, - ERROR, - - LAST_SIGNAL + NOTIFICATION = 0, + CARRIER_ON, + CARRIER_OFF, + ERROR, + LAST_SIGNAL }; - static guint signals[LAST_SIGNAL] = { 0 }; + G_DEFINE_TYPE (NMNetlinkMonitor, nm_netlink_monitor, G_TYPE_OBJECT); -NMNetlinkMonitor * -nm_netlink_monitor_get (void) -{ - static NMNetlinkMonitor *singleton = NULL; - - if (!singleton) - singleton = NM_NETLINK_MONITOR (g_object_new (NM_TYPE_NETLINK_MONITOR, NULL)); - else - g_object_ref (singleton); - - return singleton; -} - static void -nm_netlink_monitor_init (NMNetlinkMonitor *monitor) +link_msg_handler (struct nl_object *obj, void *arg) { -} - -static void -finalize (GObject *object) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (object); - - if (priv->request_status_id) - g_source_remove (priv->request_status_id); - - if (priv->io_channel) - nm_netlink_monitor_close_connection (NM_NETLINK_MONITOR (object)); - - if (priv->nlh_link_cache) { - nl_cache_free (priv->nlh_link_cache); - priv->nlh_link_cache = NULL; - } - - if (priv->nlh) { - nl_handle_destroy (priv->nlh); - priv->nlh = NULL; - } - - if (priv->nlh_cb) { - nl_cb_put (priv->nlh_cb); - priv->nlh_cb = NULL; - } - - G_OBJECT_CLASS (nm_netlink_monitor_parent_class)->finalize (object); -} - -static void -nm_netlink_monitor_class_init (NMNetlinkMonitorClass *monitor_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (monitor_class); - - g_type_class_add_private (monitor_class, sizeof (NMNetlinkMonitorPrivate)); - - /* Virtual methods */ - object_class->finalize = finalize; - - /* Signals */ - signals[CARRIER_ON] = - g_signal_new ("carrier-on", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_on), - NULL, NULL, g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, G_TYPE_INT); - - signals[CARRIER_OFF] = - g_signal_new ("carrier-off", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_off), - NULL, NULL, g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, G_TYPE_INT); - - signals[ERROR] = - g_signal_new ("error", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkMonitorClass, error), - NULL, NULL, _nm_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); -} - -static void -netlink_object_message_handler (struct nl_object *obj, void *arg) -{ - NMNetlinkMonitor *monitor = NM_NETLINK_MONITOR (arg); + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); GError *error; struct rtnl_link *filter; struct rtnl_link *link_obj; @@ -194,16 +99,16 @@ netlink_object_message_handler (struct nl_object *obj, void *arg) NM_NETLINK_MONITOR_ERROR_BAD_ALLOC, _("error processing netlink message: %s"), nl_geterror ()); - g_signal_emit (G_OBJECT (monitor), - signals[ERROR], - 0, error); + g_signal_emit (self, signals[ERROR], 0, error); g_error_free (error); return; } /* Ensure it's a link object */ - if (nl_object_match_filter(obj, OBJ_CAST (filter)) == 0) - goto out; + if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) { + rtnl_link_put (filter); + return; + } link_obj = (struct rtnl_link *) obj; flags = rtnl_link_get_flags (link_obj); @@ -215,45 +120,172 @@ netlink_object_message_handler (struct nl_object *obj, void *arg) * b00055aacdb172c05067612278ba27265fcd05ce in 2.6.17. */ if (flags & IFF_LOWER_UP) - g_signal_emit (G_OBJECT (monitor), signals[CARRIER_ON], 0, ifidx); + g_signal_emit (self, signals[CARRIER_ON], 0, ifidx); else - g_signal_emit (G_OBJECT (monitor), signals[CARRIER_OFF], 0, ifidx); + g_signal_emit (self, signals[CARRIER_OFF], 0, ifidx); -out: rtnl_link_put (filter); } static int -netlink_event_input (struct nl_msg *msg, void *arg) +event_msg_recv (struct nl_msg *msg, void *arg) { + struct nl_handle *nlh = arg; struct nlmsghdr *hdr = nlmsg_hdr (msg); + struct ucred *creds = nlmsg_get_creds (msg); + const struct sockaddr_nl *snl; + guint32 local_port; + gboolean accept_msg = FALSE; - if (hdr->nlmsg_pid != 0) - return NL_STOP; + /* Only messages sent from the kernel */ + if (!creds || creds->uid != 0) { + nm_log_dbg (LOGD_HW, "ignoring netlink message from UID %d", + creds ? creds->uid : -1); + return NL_SKIP; + } - nl_msg_parse (msg, &netlink_object_message_handler, arg); + snl = nlmsg_get_src (msg); + g_assert (snl); - /* Stop processing messages */ - return NL_STOP; + /* Accept any messages from the kernel */ + if (hdr->nlmsg_pid == 0 || snl->nl_pid == 0) + accept_msg = TRUE; + + /* And any multicast message directed to our netlink PID, since multicast + * currently requires CAP_ADMIN to use. + */ + local_port = nl_socket_get_local_port (nlh); + if ((hdr->nlmsg_pid == local_port) && snl->nl_groups) + accept_msg = TRUE; + + if (accept_msg == FALSE) { + nm_log_dbg (LOGD_HW, "ignoring netlink message from PID %d (local PID %d, multicast %d)", + hdr->nlmsg_pid, + local_port, + (hdr->nlmsg_flags & NLM_F_MULTI)); + return NL_SKIP; + } + + return NL_OK; } -gboolean -nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, - GError **error) +static int +event_msg_ready (struct nl_msg *msg, void *arg) { + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); + + /* By the time the message gets here we've already checked the sender + * and we're sure it's safe to parse this message. + */ + + /* Let clients handle generic messages */ + g_signal_emit (self, signals[NOTIFICATION], 0, msg); + + /* Parse carrier messages */ + nl_msg_parse (msg, &link_msg_handler, self); + + return NL_OK; +} + +static gboolean +event_handler (GIOChannel *channel, + GIOCondition io_condition, + gpointer user_data) +{ + NMNetlinkMonitor *self = (NMNetlinkMonitor *) user_data; NMNetlinkMonitorPrivate *priv; - int fd; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), TRUE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_val_if_fail (priv->event_id > 0, TRUE); + + if (io_condition & ERROR_CONDITIONS) { + const char *err_msg; + int err_code = 0; + socklen_t err_len = sizeof (err_code); + + /* Grab error information */ + if (getsockopt (g_io_channel_unix_get_fd (channel), + SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len)) + err_msg = strerror (err_code); + else + err_msg = _("error occurred while waiting for data on socket"); + + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA, + "%s", err_msg); + g_signal_emit (self, signals[ERROR], 0, error); + g_error_free (error); + return TRUE; + } else if (io_condition & DISCONNECT_CONDITIONS) + return FALSE; + + g_return_val_if_fail (!(io_condition & ~EVENT_CONDITIONS), FALSE); + + /* Process the netlink messages */ + if (nl_recvmsgs_default (priv->nlh_event) < 0) { + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE, + _("error processing netlink message: %s"), + nl_geterror ()); + g_signal_emit (self, signals[ERROR], 0, error); + g_error_free (error); + } + + return TRUE; +} + +static gboolean +nlh_setup (struct nl_handle *nlh, + nl_recvmsg_msg_cb_t valid_func, + gpointer cb_data, + GError **error) +{ + nl_socket_modify_cb (nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv, cb_data); + + if (valid_func) + nl_socket_modify_cb (nlh, NL_CB_VALID, NL_CB_CUSTOM, valid_func, cb_data); + + if (nl_connect (nlh, NETLINK_ROUTE) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, + _("unable to connect to netlink for monitoring link status: %s"), + nl_geterror ()); + return FALSE; + } + + /* Enable unix socket peer credentials which we use for verifying that the + * sender of the message is actually the kernel. + */ + if (nl_set_passcred (nlh, 1) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, + _("unable to enable netlink handle credential passing: %s"), + nl_geterror ()); + return FALSE; + } + + return TRUE; +} + +static gboolean +event_connection_setup (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); GError *channel_error = NULL; GIOFlags channel_flags; + struct nl_cb *cb; + int fd; - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (monitor), FALSE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); g_return_val_if_fail (priv->io_channel == NULL, FALSE); - priv->nlh_cb = nl_cb_alloc (NL_CB_VERBOSE); - priv->nlh = nl_handle_alloc_cb (priv->nlh_cb); - if (!priv->nlh) { + /* Set up the event listener connection */ + cb = nl_cb_alloc (NL_CB_VERBOSE); + priv->nlh_event = nl_handle_alloc_cb (cb); + nl_cb_put (cb); + if (!priv->nlh_event) { g_set_error (error, NM_NETLINK_MONITOR_ERROR, NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, _("unable to allocate netlink handle for monitoring link status: %s"), @@ -261,35 +293,16 @@ nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, goto error; } - nl_disable_sequence_check (priv->nlh); - nl_socket_modify_cb (priv->nlh, NL_CB_VALID, NL_CB_CUSTOM, netlink_event_input, monitor); - if (nl_connect (priv->nlh, NETLINK_ROUTE) < 0) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, - _("unable to connect to netlink for monitoring link status: %s"), - nl_geterror ()); + if (!nlh_setup (priv->nlh_event, event_msg_ready, self, error)) goto error; - } - if (nl_socket_add_membership (priv->nlh, RTNLGRP_LINK) < 0) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP, - _("unable to join netlink group for monitoring link status: %s"), - nl_geterror ()); + nl_disable_sequence_check (priv->nlh_event); + + /* Subscribe to the LINK group for internal carrier signals */ + if (!nm_netlink_monitor_subscribe (self, RTNLGRP_LINK, error)) goto error; - } - if ((priv->nlh_link_cache = rtnl_link_alloc_cache (priv->nlh)) == NULL) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE, - _("unable to allocate netlink link cache for monitoring link status: %s"), - nl_geterror ()); - goto error; - } - - nl_cache_mngt_provide (priv->nlh_link_cache); - - fd = nl_socket_get_fd (priv->nlh); + fd = nl_socket_get_fd (priv->nlh_event); priv->io_channel = g_io_channel_unix_new (fd); g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error); @@ -311,211 +324,270 @@ nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, error: if (priv->io_channel) - nm_netlink_monitor_close_connection (monitor); + nm_netlink_monitor_close_connection (self); - if (priv->nlh_link_cache) { - nl_cache_free (priv->nlh_link_cache); - priv->nlh_link_cache = NULL; + if (priv->nlh_event) { + nl_handle_destroy (priv->nlh_event); + priv->nlh_event = NULL; } - if (priv->nlh) { - nl_handle_destroy (priv->nlh); - priv->nlh = NULL; - } - - if (priv->nlh_cb) { - nl_cb_put (priv->nlh_cb); - priv->nlh_cb = NULL; - } return FALSE; } -void -nm_netlink_monitor_close_connection (NMNetlinkMonitor *monitor) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (monitor)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); - g_return_if_fail (priv->io_channel != NULL); - - if (priv->event_id) - nm_netlink_monitor_detach (monitor); - - g_io_channel_shutdown (priv->io_channel, - TRUE /* flush pending data */, - NULL); - - g_io_channel_unref (priv->io_channel); - priv->io_channel = NULL; -} - -GQuark -nm_netlink_monitor_error_quark (void) -{ - static GQuark error_quark = 0; - - if (error_quark == 0) - error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark"); - - return error_quark; -} - -void -nm_netlink_monitor_attach (NMNetlinkMonitor *monitor) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (monitor)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); - g_return_if_fail (priv->nlh != NULL); - g_return_if_fail (priv->event_id == 0); - - priv->event_id = g_io_add_watch (priv->io_channel, - (NM_NETLINK_MONITOR_EVENT_CONDITIONS | - NM_NETLINK_MONITOR_ERROR_CONDITIONS | - NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS), - nm_netlink_monitor_event_handler, - monitor); -} - -void -nm_netlink_monitor_detach (NMNetlinkMonitor *monitor) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (monitor)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); - g_return_if_fail (priv->event_id > 0); - - g_source_remove (priv->event_id); - priv->event_id = 0; -} - static gboolean -deferred_emit_carrier_state (gpointer user_data) +sync_connection_setup (NMNetlinkMonitor *self, GError **error) { - NMNetlinkMonitor *monitor = NM_NETLINK_MONITOR (user_data); - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + struct nl_cb *cb; +#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND + struct nl_cache *addr_cache; +#endif - priv->request_status_id = 0; + /* Set up the event listener connection */ + cb = nl_cb_alloc (NL_CB_VERBOSE); + priv->nlh_sync = nl_handle_alloc_cb (cb); + nl_cb_put (cb); + if (!priv->nlh_sync) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, + _("unable to allocate netlink handle for monitoring link status: %s"), + nl_geterror ()); + goto error; + } - /* Update the link cache with latest state, and if there are no errors - * emit the link states for all the interfaces in the cache. + if (!nlh_setup (priv->nlh_sync, NULL, self, error)) + goto error; + +#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND + /* Work around apparent libnl bug; rtnl_addr requires that all + * addresses have the "peer" attribute set in order to be compared + * for equality, but this attribute is not normally set. As a + * result, most addresses will not compare as equal even to + * themselves, busting caching. */ - if (nl_cache_refill (priv->nlh, priv->nlh_link_cache)) { - nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror ()); - } else { - nl_cache_foreach_filter (priv->nlh_link_cache, - NULL, - netlink_object_message_handler, - monitor); + addr_cache = rtnl_addr_alloc_cache (priv->nlh_sync); + nl_cache_get_ops (addr_cache)->co_obj_ops->oo_id_attrs &= ~0x80; + nl_cache_free (addr_cache); +#endif + + if ((priv->link_cache = rtnl_link_alloc_cache (priv->nlh_sync)) == NULL) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE, + _("unable to allocate netlink link cache for monitoring link status: %s"), + nl_geterror ()); + goto error; + } + nl_cache_mngt_provide (priv->link_cache); + + return TRUE; + +error: + if (priv->link_cache) { + nl_cache_free (priv->link_cache); + priv->link_cache = NULL; + } + + if (priv->nlh_sync) { + nl_handle_destroy (priv->nlh_sync); + priv->nlh_sync = NULL; } return FALSE; } gboolean -nm_netlink_monitor_request_status (NMNetlinkMonitor *monitor, - GError **error) +nm_netlink_monitor_open_connection (NMNetlinkMonitor *self, GError **error) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + if (!event_connection_setup (self, error)) + return FALSE; + + if (!sync_connection_setup (self, error)) + return FALSE; + + return TRUE; +} + +void +nm_netlink_monitor_close_connection (NMNetlinkMonitor *self) { NMNetlinkMonitorPrivate *priv; - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (monitor), FALSE); + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); - priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); - g_return_val_if_fail (priv->event_id > 0, FALSE); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->io_channel != NULL); + + if (priv->event_id) + nm_netlink_monitor_detach (self); + + g_io_channel_shutdown (priv->io_channel, + TRUE /* flush pending data */, + NULL); + g_io_channel_unref (priv->io_channel); + priv->io_channel = NULL; +} + +void +nm_netlink_monitor_attach (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->nlh_event != NULL); + g_return_if_fail (priv->event_id == 0); + + priv->event_id = g_io_add_watch (priv->io_channel, + (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), + event_handler, self); +} + +void +nm_netlink_monitor_detach (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->event_id > 0); + + g_source_remove (priv->event_id); + priv->event_id = 0; +} + +static int +get_subs (NMNetlinkMonitor *self, int group) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + return GPOINTER_TO_INT (g_hash_table_lookup (priv->subscriptions, + GINT_TO_POINTER (group))); +} + +static void +set_subs (NMNetlinkMonitor *self, int group, int new_subs) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + g_hash_table_insert (priv->subscriptions, + GINT_TO_POINTER (group), + GINT_TO_POINTER (new_subs)); +} + +gboolean +nm_netlink_monitor_subscribe (NMNetlinkMonitor *self, int group, GError **error) +{ + NMNetlinkMonitorPrivate *priv; + int subs; + + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + if (!priv->nlh_event) { + if (!nm_netlink_monitor_open_connection (self, error)) + return FALSE; + } + + subs = get_subs (self, group) + 1; + if (subs == 1) { + if (nl_socket_add_membership (priv->nlh_event, group) < 0) { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP, + _("unable to join netlink group: %s"), + nl_geterror ()); + return FALSE; + } + } + + /* Update # of subscriptions for this group */ + set_subs (self, group, subs); + + return TRUE; +} + +void +nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *self, int group) +{ + NMNetlinkMonitorPrivate *priv; + int subs; + + g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + g_return_if_fail (priv->nlh_event != NULL); + + subs = get_subs (self, group) - 1; + if (subs == 0) + nl_socket_drop_membership (priv->nlh_event, group); + + /* Update # of subscriptions for this group */ + set_subs (self, group, subs); +} + +/***************************************************************/ + +gboolean +nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + /* FIXME: nl_rtgen_request() gets the return value screwed up with + * libnl-1.1; revisit this and return a proper error when we port to + * a later libnl. + */ + nl_rtgen_request (priv->nlh_event, RTM_GETLINK, AF_INET6, NLM_F_DUMP); + + return TRUE; +} + + +static gboolean +deferred_emit_carrier_state (gpointer user_data) +{ + NMNetlinkMonitor *self = NM_NETLINK_MONITOR (user_data); + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + priv->request_status_id = 0; + + /* Update the link cache with latest state, and if there are no errors + * emit the link states for all the interfaces in the cache. + */ + if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { + nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror ()); + } else + nl_cache_foreach_filter (priv->link_cache, NULL, link_msg_handler, self); + + return FALSE; +} + +gboolean +nm_netlink_monitor_request_status (NMNetlinkMonitor *self, GError **error) +{ + NMNetlinkMonitorPrivate *priv; + + g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); + + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); /* Schedule the carrier state emission */ if (!priv->request_status_id) - priv->request_status_id = g_idle_add (deferred_emit_carrier_state, monitor); + priv->request_status_id = g_idle_add (deferred_emit_carrier_state, self); return TRUE; } -static gboolean -nm_netlink_monitor_event_handler (GIOChannel *channel, - GIOCondition io_condition, - gpointer user_data) -{ - NMNetlinkMonitor *monitor = (NMNetlinkMonitor *) user_data; - NMNetlinkMonitorPrivate *priv; - GError *error = NULL; - - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (monitor), TRUE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (monitor); - g_return_val_if_fail (priv->event_id > 0, TRUE); - - if (io_condition & NM_NETLINK_MONITOR_ERROR_CONDITIONS) - return nm_netlink_monitor_error_handler (channel, io_condition, monitor); - else if (io_condition & NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS) - return nm_netlink_monitor_disconnect_handler (channel, io_condition, monitor); - - g_return_val_if_fail (!(io_condition & ~(NM_NETLINK_MONITOR_EVENT_CONDITIONS)), FALSE); - - if (nl_recvmsgs_default (priv->nlh) < 0) { - error = g_error_new (NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE, - _("error processing netlink message: %s"), - nl_geterror ()); - - g_signal_emit (G_OBJECT (monitor), - signals[ERROR], - 0, error); - g_error_free (error); - } - - return TRUE; -} - -static gboolean -nm_netlink_monitor_error_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkMonitor *monitor) -{ - GError *socket_error; - const char *err_msg; - int err_code; - socklen_t err_len; - - g_return_val_if_fail (io_condition & NM_NETLINK_MONITOR_ERROR_CONDITIONS, FALSE); - - err_code = 0; - err_len = sizeof (err_code); - if (getsockopt (g_io_channel_unix_get_fd (channel), - SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len)) - err_msg = strerror (err_code); - else - err_msg = _("error occurred while waiting for data on socket"); - - socket_error = g_error_new (NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA, - "%s", - err_msg); - - g_signal_emit (G_OBJECT (monitor), - signals[ERROR], - 0, socket_error); - - g_error_free (socket_error); - - return TRUE; -} - -static gboolean -nm_netlink_monitor_disconnect_handler (GIOChannel *channel, - GIOCondition io_condition, - NMNetlinkMonitor *monitor) -{ - - g_return_val_if_fail (!(io_condition & ~(NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS)), FALSE); - return FALSE; -} - typedef struct { NMNetlinkMonitor *self; struct rtnl_link *filter; @@ -550,7 +622,7 @@ nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self, priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); /* Update the link cache with the latest information */ - if (nl_cache_refill (priv->nlh, priv->nlh_link_cache)) { + if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { g_set_error (error, NM_NETLINK_MONITOR_ERROR, NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE, @@ -563,7 +635,7 @@ nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self, * otherwise some kernels (or maybe libnl?) only send a few of the * interfaces in the refill request. */ - if (nl_cache_refill (priv->nlh, priv->nlh_link_cache)) { + if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { g_set_error (error, NM_NETLINK_MONITOR_ERROR, NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE, @@ -588,7 +660,7 @@ nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self, info.self = self; info.filter = filter; info.error = NULL; - nl_cache_foreach_filter (priv->nlh_link_cache, NULL, get_flags_sync_cb, &info); + nl_cache_foreach_filter (priv->link_cache, NULL, get_flags_sync_cb, &info); rtnl_link_put (filter); @@ -604,3 +676,191 @@ nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self, return TRUE; /* success */ } +/***************************************************************/ + +struct nl_handle * +nm_netlink_get_default_handle (void) +{ + NMNetlinkMonitor *self; + struct nl_handle *nlh; + + self = nm_netlink_monitor_get (); + nlh = NM_NETLINK_MONITOR_GET_PRIVATE (self)->nlh_sync; + g_object_unref (self); + + return nlh; +} + +int +nm_netlink_iface_to_index (const char *iface) +{ + NMNetlinkMonitor *self; + NMNetlinkMonitorPrivate *priv; + int idx; + + g_return_val_if_fail (iface != NULL, -1); + + self = nm_netlink_monitor_get (); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + nl_cache_refill (priv->nlh_sync, priv->link_cache); + idx = rtnl_link_name2i (priv->link_cache, iface); + g_object_unref (self); + + return idx; +} + +#define MAX_IFACE_LEN 33 +char * +nm_netlink_index_to_iface (int idx) +{ + NMNetlinkMonitor *self; + NMNetlinkMonitorPrivate *priv; + char *buf = NULL; + + g_return_val_if_fail (idx >= 0, NULL); + + self = nm_netlink_monitor_get (); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + buf = g_malloc0 (MAX_IFACE_LEN); + g_assert (buf); + + nl_cache_refill (priv->nlh_sync, priv->link_cache); + if (!rtnl_link_i2name (priv->link_cache, idx, buf, MAX_IFACE_LEN - 1)) { + g_free (buf); + buf = NULL; + } + + g_object_unref (self); + return buf; +} + +struct rtnl_link * +nm_netlink_index_to_rtnl_link (int idx) +{ + NMNetlinkMonitor *self; + NMNetlinkMonitorPrivate *priv; + struct rtnl_link *ret = NULL; + + if (idx <= 0) + return NULL; + + self = nm_netlink_monitor_get (); + priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + nl_cache_refill (priv->nlh_sync, priv->link_cache); + ret = rtnl_link_get (priv->link_cache, idx); + g_object_unref (self); + + return ret; +} + +/***************************************************************/ + +NMNetlinkMonitor * +nm_netlink_monitor_get (void) +{ + static NMNetlinkMonitor *singleton = NULL; + + if (!singleton) + singleton = NM_NETLINK_MONITOR (g_object_new (NM_TYPE_NETLINK_MONITOR, NULL)); + else + g_object_ref (singleton); + + return singleton; +} + +static void +nm_netlink_monitor_init (NMNetlinkMonitor *self) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); + + priv->subscriptions = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +finalize (GObject *object) +{ + NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (object); + + if (priv->request_status_id) + g_source_remove (priv->request_status_id); + + if (priv->io_channel) + nm_netlink_monitor_close_connection (NM_NETLINK_MONITOR (object)); + + if (priv->link_cache) { + nl_cache_free (priv->link_cache); + priv->link_cache = NULL; + } + + if (priv->nlh_event) { + nl_handle_destroy (priv->nlh_event); + priv->nlh_event = NULL; + } + + if (priv->nlh_sync) { + nl_handle_destroy (priv->nlh_sync); + priv->nlh_sync = NULL; + } + + g_hash_table_destroy (priv->subscriptions); + + G_OBJECT_CLASS (nm_netlink_monitor_parent_class)->finalize (object); +} + +static void +nm_netlink_monitor_class_init (NMNetlinkMonitorClass *monitor_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (monitor_class); + + g_type_class_add_private (monitor_class, sizeof (NMNetlinkMonitorPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + /* Signals */ + signals[NOTIFICATION] = + g_signal_new ("notification", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, notification), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[CARRIER_ON] = + g_signal_new ("carrier-on", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_on), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + signals[CARRIER_OFF] = + g_signal_new ("carrier-off", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_off), + NULL, NULL, g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + signals[ERROR] = + g_signal_new ("error", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMNetlinkMonitorClass, error), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + +GQuark +nm_netlink_monitor_error_quark (void) +{ + static GQuark error_quark = 0; + + if (G_UNLIKELY (error_quark == 0)) + error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark"); + return error_quark; +} + diff --git a/src/nm-netlink-monitor.h b/src/nm-netlink-monitor.h index 115eccaaa3..8b57a312d7 100644 --- a/src/nm-netlink-monitor.h +++ b/src/nm-netlink-monitor.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) 2005 - 2008 Novell, Inc. * Copyright (C) 2005 Ray Strode */ @@ -25,16 +25,15 @@ #include #include +#include +#include -G_BEGIN_DECLS - -#define NM_TYPE_NETLINK_MONITOR (nm_netlink_monitor_get_type ()) -#define NM_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitor)) +#define NM_TYPE_NETLINK_MONITOR (nm_netlink_monitor_get_type ()) +#define NM_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitor)) #define NM_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitorClass)) -#define NM_IS_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_MONITOR)) +#define NM_IS_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_MONITOR)) #define NM_IS_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETLINK_MONITOR)) #define NM_NETLINK_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitorClass)) -#define NM_NETLINK_MONITOR_ERROR (nm_netlink_monitor_error_quark ()) typedef enum { NM_NETLINK_MONITOR_ERROR_GENERIC = 0, @@ -56,29 +55,45 @@ typedef struct { GObjectClass parent_class; /* Signals */ - void (*carrier_on) (NMNetlinkMonitor *monitor, int index); - void (*carrier_off) (NMNetlinkMonitor *monitor, int index); - void (*error) (NMNetlinkMonitor *monitor, GError *error); + void (*notification) (NMNetlinkMonitor *monitor, struct nl_msg *msg); + void (*carrier_on) (NMNetlinkMonitor *monitor, int index); + void (*carrier_off) (NMNetlinkMonitor *monitor, int index); + void (*error) (NMNetlinkMonitor *monitor, GError *error); } NMNetlinkMonitorClass; -GType nm_netlink_monitor_get_type (void) G_GNUC_CONST; -GQuark nm_netlink_monitor_error_quark (void) G_GNUC_CONST; +#define NM_NETLINK_MONITOR_ERROR (nm_netlink_monitor_error_quark ()) +GType nm_netlink_monitor_get_type (void) G_GNUC_CONST; +GQuark nm_netlink_monitor_error_quark (void) G_GNUC_CONST; NMNetlinkMonitor *nm_netlink_monitor_get (void); -gboolean nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, - GError **error); +gboolean nm_netlink_monitor_open_connection (NMNetlinkMonitor *monitor, + GError **error); void nm_netlink_monitor_close_connection (NMNetlinkMonitor *monitor); -void nm_netlink_monitor_attach (NMNetlinkMonitor *monitor); -void nm_netlink_monitor_detach (NMNetlinkMonitor *monitor); +void nm_netlink_monitor_attach (NMNetlinkMonitor *monitor); +void nm_netlink_monitor_detach (NMNetlinkMonitor *monitor); + +gboolean nm_netlink_monitor_subscribe (NMNetlinkMonitor *monitor, + int group, + GError **error); +void nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *monitor, + int group); + +gboolean nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *monitor, + GError **error); + gboolean nm_netlink_monitor_request_status (NMNetlinkMonitor *monitor, - GError **error); + GError **error); gboolean nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *monitor, guint32 ifindex, guint32 *ifflags, GError **error); -G_END_DECLS +/* Generic utility functions */ +int nm_netlink_iface_to_index (const char *iface); +char * nm_netlink_index_to_iface (int idx); +struct rtnl_link *nm_netlink_index_to_rtnl_link (int idx); +struct nl_handle *nm_netlink_get_default_handle (void); #endif /* NM_NETLINK_MONITOR_H */ diff --git a/src/nm-netlink.c b/src/nm-netlink.c deleted file mode 100644 index c55c093ae8..0000000000 --- a/src/nm-netlink.c +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- 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) 2007 - 2008 Red Hat, Inc. - */ - -#include "config.h" - -#include "nm-netlink.h" -#include "nm-logging.h" - -#include -#include -#include -#include -#include - -static struct nl_cache * link_cache = NULL; -static struct nl_handle * def_nl_handle = NULL; - - -static struct nl_cache * -get_link_cache (void) -{ - struct nl_handle * nlh; - - nlh = nm_netlink_get_default_handle (); - if (G_UNLIKELY (!nlh)) { - nm_log_err (LOGD_HW, "couldn't allocate netlink handle."); - return NULL; - } - - if (G_UNLIKELY (!link_cache)) - link_cache = rtnl_link_alloc_cache (nlh); - - if (G_UNLIKELY (!link_cache)) { - nm_log_err (LOGD_HW, "couldn't allocate netlink link cache: %s", nl_geterror ()); - return NULL; - } - - nl_cache_update (nlh, link_cache); - return link_cache; -} - - -struct nl_handle * -nm_netlink_get_default_handle (void) -{ - struct nl_cb *cb; -#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND - struct nl_cache *addr_cache; -#endif - - if (def_nl_handle) - return def_nl_handle; - - cb = nl_cb_alloc(NL_CB_VERBOSE); - def_nl_handle = nl_handle_alloc_cb (cb); - if (!def_nl_handle) { - nm_log_err (LOGD_HW, "couldn't allocate netlink handle."); - return NULL; - } - - if (nl_connect (def_nl_handle, NETLINK_ROUTE) < 0) { - nm_log_err (LOGD_HW, "couldn't connect to netlink: %s", nl_geterror ()); - return NULL; - } - -#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND - /* Work around apparent libnl bug; rtnl_addr requires that all - * addresses have the "peer" attribute set in order to be compared - * for equality, but this attribute is not normally set. As a - * result, most addresses will not compare as equal even to - * themselves, busting caching. - */ - addr_cache = rtnl_addr_alloc_cache (def_nl_handle); - nl_cache_get_ops (addr_cache)->co_obj_ops->oo_id_attrs &= ~0x80; - nl_cache_free (addr_cache); -#endif - - return def_nl_handle; -} - -int -nm_netlink_iface_to_index (const char *iface) -{ - struct nl_cache * cache; - - g_return_val_if_fail (iface != NULL, -1); - - cache = get_link_cache (); - if (!cache) - return RTNL_LINK_NOT_FOUND; - - return rtnl_link_name2i (cache, iface); -} - - -#define MAX_IFACE_LEN 33 -char * -nm_netlink_index_to_iface (int idx) -{ - struct nl_cache * cache; - char * buf = NULL; - - cache = get_link_cache (); - if (!cache) - return NULL; - - buf = g_malloc0 (MAX_IFACE_LEN); - if (buf == NULL) { - nm_log_warn (LOGD_HW, "Not enough memory to allocate interface name buffer."); - return NULL; - } - - if (rtnl_link_i2name (cache, idx, buf, MAX_IFACE_LEN - 1) == NULL) { - g_free (buf); - buf = NULL; - } - - return buf; -} - -struct rtnl_link * -nm_netlink_index_to_rtnl_link (int idx) -{ - struct nl_cache *cache; - - cache = get_link_cache (); - if (!cache) - return NULL; - - return rtnl_link_get (cache, idx); -} - diff --git a/src/nm-netlink.h b/src/nm-netlink.h deleted file mode 100644 index 7b5e1ba573..0000000000 --- a/src/nm-netlink.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- 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) 2007 - 2008 Red Hat, Inc. - */ - -#ifndef NM_NETLINK_H -#define NM_NETLINK_H - -#include -#include -#include -#include -#include - -int nm_netlink_iface_to_index (const char *iface); -char * nm_netlink_index_to_iface (int idx); -struct rtnl_link * nm_netlink_index_to_rtnl_link (int idx); - -struct nl_handle * nm_netlink_get_default_handle (void); - -#endif /* NM_NETLINK_H */ diff --git a/src/nm-policy-hostname.c b/src/nm-policy-hostname.c new file mode 100644 index 0000000000..02eb560263 --- /dev/null +++ b/src/nm-policy-hostname.c @@ -0,0 +1,196 @@ +/* -*- 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) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#include +#include +#include +#include +#include + +#include + +#include "nm-logging.h" +#include "nm-policy-hostname.h" +#include "nm-policy-hosts.h" + +/************************************************************************/ + +struct HostnameThread { + GThread *thread; + + GMutex *lock; + gboolean dead; + int ret; + + guint32 ip4_addr; + char hostname[NI_MAXHOST + 1]; + + HostnameThreadCallback callback; + gpointer user_data; +}; + +static gboolean +hostname_thread_run_cb (gpointer user_data) +{ + HostnameThread *ht = (HostnameThread *) user_data; + + (*ht->callback) (ht, ht->ret, ht->hostname, ht->user_data); + return FALSE; +} + +static gpointer +hostname_thread_worker (gpointer data) +{ + HostnameThread *ht = (HostnameThread *) data; + struct sockaddr_in addr; + int i; + + g_mutex_lock (ht->lock); + if (ht->dead) { + g_mutex_unlock (ht->lock); + return (gpointer) NULL; + } + g_mutex_unlock (ht->lock); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ht->ip4_addr; + + ht->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in), + ht->hostname, NI_MAXHOST, NULL, 0, + NI_NAMEREQD); + if (ht->ret == 0) { + for (i = 0; i < strlen (ht->hostname); i++) + ht->hostname[i] = tolower (ht->hostname[i]); + } + + /* Don't track the idle handler ID because by the time the g_idle_add() + * returns the ID, the handler may already have run and freed the + * HostnameThread. + */ + g_idle_add (hostname_thread_run_cb, ht); + return (gpointer) TRUE; +} + +void +hostname_thread_free (HostnameThread *ht) +{ + g_return_if_fail (ht != NULL); + + g_mutex_free (ht->lock); + memset (ht, 0, sizeof (HostnameThread)); + g_free (ht); +} + +HostnameThread * +hostname_thread_new (guint32 ip4_addr, HostnameThreadCallback callback, gpointer user_data) +{ + HostnameThread *ht; + + ht = g_malloc0 (sizeof (HostnameThread)); + g_assert (ht); + + ht->lock = g_mutex_new (); + ht->callback = callback; + ht->user_data = user_data; + ht->ip4_addr = ip4_addr; + + ht->thread = g_thread_create (hostname_thread_worker, ht, FALSE, NULL); + if (!ht->thread) { + hostname_thread_free (ht); + ht = NULL; + } + + return ht; +} + +void +hostname_thread_kill (HostnameThread *ht) +{ + g_return_if_fail (ht != NULL); + + g_mutex_lock (ht->lock); + ht->dead = TRUE; + g_mutex_unlock (ht->lock); +} + +gboolean +hostname_thread_is_dead (HostnameThread *ht) +{ + g_return_val_if_fail (ht != NULL, TRUE); + + return ht->dead; +} + +/************************************************************************/ + +#define FALLBACK_HOSTNAME "localhost.localdomain" + +gboolean +nm_policy_set_system_hostname (const char *new_hostname, const char *msg) +{ + char old_hostname[HOST_NAME_MAX + 1]; + int ret = 0; + const char *name = new_hostname ? new_hostname : FALLBACK_HOSTNAME; + gboolean set_hostname = TRUE, changed = FALSE; + + old_hostname[HOST_NAME_MAX] = '\0'; + errno = 0; + ret = gethostname (old_hostname, HOST_NAME_MAX); + if (ret != 0) { + nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s", + errno, strerror (errno)); + } else { + /* Don't set the hostname if it isn't actually changing */ + if ( (new_hostname && !strcmp (old_hostname, new_hostname)) + || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME))) + set_hostname = FALSE; + } + + if (set_hostname) { + nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg); + ret = sethostname (name, strlen (name)); + if (ret != 0) { + nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s", + name, errno, strerror (errno)); + return FALSE; + } + } + + /* But even if the hostname isn't changing, always try updating /etc/hosts + * just in case the hostname changed while NM wasn't running; we need to + * make sure that /etc/hosts has valid mappings for '127.0.0.1' and the + * current system hostname. If those exist, + * nm_policy_hosts_update_etc_hosts() will just return and won't touch + * /etc/hosts at all. + */ + if (!nm_policy_hosts_update_etc_hosts (name, FALLBACK_HOSTNAME, &changed)) { + /* error updating /etc/hosts; fallback to localhost.localdomain */ + nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)"); + ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME)); + if (ret != 0) { + nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s", + FALLBACK_HOSTNAME, errno, strerror (errno)); + } + } + + return changed; +} + diff --git a/src/nm-policy-hostname.h b/src/nm-policy-hostname.h new file mode 100644 index 0000000000..c59ca41078 --- /dev/null +++ b/src/nm-policy-hostname.h @@ -0,0 +1,47 @@ +/* -*- 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) 2004 - 2010 Red Hat, Inc. + * Copyright (C) 2007 - 2008 Novell, Inc. + */ + +#ifndef NM_POLICY_HOSTNAME_H +#define NM_POLICY_HOSTNAME_H + +#include + +gboolean nm_policy_set_system_hostname (const char *new_hostname, const char *msg); + + +typedef struct HostnameThread HostnameThread; + +typedef void (*HostnameThreadCallback) (HostnameThread *ht, + int error, + const char *hostname, + gpointer user_data); + +HostnameThread * hostname_thread_new (guint32 ip4_addr, + HostnameThreadCallback callback, + gpointer user_data); + +void hostname_thread_free (HostnameThread *ht); + +gboolean hostname_thread_is_dead (HostnameThread *ht); + +void hostname_thread_kill (HostnameThread *ht); + +#endif /* NM_POLICY_HOSTNAME_H */ diff --git a/src/nm-policy-hosts.c b/src/nm-policy-hosts.c index 35a0b9d60c..7723c99759 100644 --- a/src/nm-policy-hosts.c +++ b/src/nm-policy-hosts.c @@ -25,6 +25,7 @@ #include #include "nm-policy-hosts.h" +#include "nm-logging.h" gboolean nm_policy_hosts_find_token (const char *line, const char *token) @@ -171,3 +172,64 @@ nm_policy_get_etc_hosts (const char **lines, return contents; } +gboolean +nm_policy_hosts_update_etc_hosts (const char *hostname, + const char *fallback_hostname, + gboolean *out_changed) +{ + char *contents = NULL; + char **lines = NULL; + GError *error = NULL; + GString *new_contents = NULL; + gsize contents_len = 0; + gboolean success = FALSE; + + g_return_val_if_fail (hostname != NULL, FALSE); + g_return_val_if_fail (out_changed != NULL, FALSE); + + if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) { + nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s", + error ? error->code : 0, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + return FALSE; + } + + /* Get the new /etc/hosts contents */ + lines = g_strsplit_set (contents, "\n\r", 0); + new_contents = nm_policy_get_etc_hosts ((const char **) lines, + contents_len, + hostname, + fallback_hostname, + &error); + g_strfreev (lines); + g_free (contents); + + if (new_contents) { + nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname"); + + g_clear_error (&error); + /* And actually update /etc/hosts */ + if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) { + nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s", + error ? error->code : 0, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + } else { + success = TRUE; + *out_changed = TRUE; + } + + g_string_free (new_contents, TRUE); + } else if (!error) { + /* No change required */ + success = TRUE; + } else { + nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s", + error->code, error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } + + return success; +} + diff --git a/src/nm-policy-hosts.h b/src/nm-policy-hosts.h index 4130ad0037..0a77e6678a 100644 --- a/src/nm-policy-hosts.h +++ b/src/nm-policy-hosts.h @@ -23,14 +23,18 @@ #include +gboolean nm_policy_hosts_update_etc_hosts (const char *hostname, + const char *fallback_hostname, + gboolean *out_changed); + +/* Only for testcases; don't use outside of nm-policy-hosts.c */ +gboolean nm_policy_hosts_find_token (const char *line, const char *token); + GString *nm_policy_get_etc_hosts (const char **lines, gsize existing_len, const char *hostname, const char *fallback_hostname, GError **error); -/* Only for testcases; don't use outside of nm-policy-hosts.c */ -gboolean nm_policy_hosts_find_token (const char *line, const char *token); - #endif /* NM_POLICY_HOSTS_H */ diff --git a/src/nm-policy.c b/src/nm-policy.c index 7159eafb57..a7bd96f1bb 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -42,24 +42,7 @@ #include "nm-named-manager.h" #include "nm-vpn-manager.h" #include "nm-policy-hosts.h" - -typedef struct LookupThread LookupThread; - -typedef void (*LookupCallback) (LookupThread *thread, gpointer user_data); - -struct LookupThread { - GThread *thread; - - GMutex *lock; - gboolean die; - int ret; - - guint32 ip4_addr; - char hostname[NI_MAXHOST + 1]; - - LookupCallback callback; - gpointer user_data; -}; +#include "nm-policy-hostname.h" struct NMPolicy { NMManager *manager; @@ -72,99 +55,14 @@ struct NMPolicy { gulong vpn_activated_id; gulong vpn_deactivated_id; - NMDevice *default_device; + NMDevice *default_device4; + NMDevice *default_device6; - LookupThread *lookup; + HostnameThread *lookup; char *orig_hostname; /* hostname at NM start time */ }; -static gboolean -lookup_thread_run_cb (gpointer user_data) -{ - LookupThread *thread = (LookupThread *) user_data; - - (*thread->callback) (thread, thread->user_data); - return FALSE; -} - -static gpointer -lookup_thread_worker (gpointer data) -{ - LookupThread *thread = (LookupThread *) data; - struct sockaddr_in addr; - - g_mutex_lock (thread->lock); - if (thread->die) { - g_mutex_unlock (thread->lock); - return (gpointer) NULL; - } - g_mutex_unlock (thread->lock); - - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = thread->ip4_addr; - - thread->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in), - thread->hostname, NI_MAXHOST, NULL, 0, - NI_NAMEREQD); - if (thread->ret == 0) { - int i; - - for (i = 0; i < strlen (thread->hostname); i++) - thread->hostname[i] = tolower (thread->hostname[i]); - } - - /* Don't track the idle handler ID because by the time the g_idle_add() - * returns the ID, the handler may already have run and freed the - * LookupThread. - */ - g_idle_add (lookup_thread_run_cb, thread); - return (gpointer) TRUE; -} - -static void -lookup_thread_free (LookupThread *thread) -{ - g_return_if_fail (thread != NULL); - - g_mutex_free (thread->lock); - memset (thread, 0, sizeof (LookupThread)); - g_free (thread); -} - -static LookupThread * -lookup_thread_new (guint32 ip4_addr, LookupCallback callback, gpointer user_data) -{ - LookupThread *thread; - - thread = g_malloc0 (sizeof (LookupThread)); - if (!thread) - return NULL; - - thread->lock = g_mutex_new (); - thread->callback = callback; - thread->user_data = user_data; - thread->ip4_addr = ip4_addr; - - thread->thread = g_thread_create (lookup_thread_worker, thread, FALSE, NULL); - if (!thread->thread) { - lookup_thread_free (thread); - return NULL; - } - - return thread; -} - -static void -lookup_thread_die (LookupThread *thread) -{ - g_return_if_fail (thread != NULL); - - g_mutex_lock (thread->lock); - thread->die = TRUE; - g_mutex_unlock (thread->lock); -} - #define INVALID_TAG "invalid" static const char * @@ -181,7 +79,7 @@ get_connection_id (NMConnection *connection) } static NMDevice * -get_best_device (NMManager *manager, NMActRequest **out_req) +get_best_ip4_device (NMManager *manager, NMActRequest **out_req) { GSList *devices, *iter; NMDevice *best = NULL; @@ -253,142 +151,107 @@ get_best_device (NMManager *manager, NMActRequest **out_req) return best; } -#define FALLBACK_HOSTNAME "localhost.localdomain" - -static gboolean -update_etc_hosts (const char *hostname, gboolean *out_changed) +static NMDevice * +get_best_ip6_device (NMManager *manager, NMActRequest **out_req) { - char *contents = NULL; - char **lines = NULL; - GError *error = NULL; - GString *new_contents = NULL; - gsize contents_len = 0; - gboolean success = FALSE; + GSList *devices, *iter; + NMDevice *best = NULL; + int best_prio = G_MAXINT; - g_return_val_if_fail (hostname != NULL, FALSE); - g_return_val_if_fail (out_changed != NULL, FALSE); + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (out_req != NULL, NULL); + g_return_val_if_fail (*out_req == NULL, NULL); - if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) { - nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s", - error ? error->code : 0, - (error && error->message) ? error->message : "(unknown)"); - g_clear_error (&error); - return FALSE; - } + devices = nm_manager_get_devices (manager); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *dev = NM_DEVICE (iter->data); + NMActRequest *req; + NMConnection *connection; + NMIP6Config *ip6_config; + NMSettingIP6Config *s_ip6; + int prio; + guint i; + gboolean can_default = FALSE; + const char *method = NULL; - /* Get the new /etc/hosts contents */ - lines = g_strsplit_set (contents, "\n\r", 0); - new_contents = nm_policy_get_etc_hosts ((const char **) lines, - contents_len, - hostname, - FALLBACK_HOSTNAME, - &error); - g_strfreev (lines); - g_free (contents); + if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED) + continue; - if (new_contents) { - nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname"); + ip6_config = nm_device_get_ip6_config (dev); + if (!ip6_config) + continue; - g_clear_error (&error); - /* And actually update /etc/hosts */ - if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) { - nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s", - error ? error->code : 0, - (error && error->message) ? error->message : "(unknown)"); - g_clear_error (&error); - } else { - success = TRUE; - *out_changed = TRUE; + req = nm_device_get_act_request (dev); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Never set the default route through an IPv4LL-addressed device */ + 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_LINK_LOCAL)) + continue; + + /* Make sure at least one of this device's IP addresses has a gateway */ + for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) { + NMIP6Address *addr; + + addr = nm_ip6_config_get_address (ip6_config, i); + if (nm_ip6_address_get_gateway (addr)) { + can_default = TRUE; + break; + } } - g_string_free (new_contents, TRUE); - } else if (!error) { - /* No change required */ - success = TRUE; - } else { - nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s", - error->code, error->message ? error->message : "(unknown)"); - g_clear_error (&error); + if (!can_default && !NM_IS_DEVICE_MODEM (dev)) + continue; + + /* 'never-default' devices can't ever be the default */ + if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6)) + continue; + + prio = nm_device_get_priority (dev); + if (prio > 0 && prio < best_prio) { + best = dev; + best_prio = prio; + *out_req = req; + } } - return success; + return best; } static void -set_system_hostname (const char *new_hostname, const char *msg) +_set_hostname (const char *new_hostname, const char *msg) { - char old_hostname[HOST_NAME_MAX + 1]; - int ret = 0; - const char *name = new_hostname ? new_hostname : FALLBACK_HOSTNAME; - gboolean set_hostname = TRUE, changed = FALSE; - - old_hostname[HOST_NAME_MAX] = '\0'; - errno = 0; - ret = gethostname (old_hostname, HOST_NAME_MAX); - if (ret != 0) { - nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s", - errno, strerror (errno)); - } else { - /* Don't set the hostname if it isn't actually changing */ - if ( (new_hostname && !strcmp (old_hostname, new_hostname)) - || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME))) - set_hostname = FALSE; - } - - if (set_hostname) { - nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg); - ret = sethostname (name, strlen (name)); - if (ret != 0) { - nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s", - name, errno, strerror (errno)); - return; - } - } - - /* But still always try updating /etc/hosts just in case the hostname - * changed while NM wasn't running; we need to make sure that /etc/hosts - * has valid mappings for '127.0.0.1' and the current system hostname. If - * those exist, update_etc_hosts() will just return and won't touch - * /etc/hosts at all. - */ - if (!update_etc_hosts (name, &changed)) { - /* error updating /etc/hosts; fallback to localhost.localdomain */ - nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)"); - ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME)); - if (ret != 0) { - nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s", - FALLBACK_HOSTNAME, errno, strerror (errno)); - } - } - - if (changed) + if (nm_policy_set_system_hostname (new_hostname, msg)) nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL); } static void -lookup_callback (LookupThread *thread, gpointer user_data) +lookup_callback (HostnameThread *thread, + int result, + const char *hostname, + gpointer user_data) { NMPolicy *policy = (NMPolicy *) user_data; + char *msg; - /* If the thread was told to die or it's not the current in-progress - * hostname lookup, nothing to do. - */ - if (thread->die || (thread != policy->lookup)) - goto done; - - policy->lookup = NULL; - if (!strlen (thread->hostname)) { - char *msg; - - /* No valid IP4 config (!!); fall back to localhost.localdomain */ - msg = g_strdup_printf ("address lookup failed: %d", thread->ret); - set_system_hostname (NULL, msg); - g_free (msg); - } else - set_system_hostname (thread->hostname, "from address lookup"); - -done: - lookup_thread_free (thread); + /* Update the hostname if the calling lookup thread is the in-progress one */ + if (!hostname_thread_is_dead (thread) && (thread == policy->lookup)) { + policy->lookup = NULL; + if (!hostname) { + /* No valid IP4 config (!!); fall back to localhost.localdomain */ + msg = g_strdup_printf ("address lookup failed: %d", result); + _set_hostname (NULL, msg); + g_free (msg); + } else + _set_hostname (hostname, "from address lookup"); + } + hostname_thread_free (thread); } static void @@ -403,7 +266,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best) g_return_if_fail (policy != NULL); if (policy->lookup) { - lookup_thread_die (policy->lookup); + hostname_thread_kill (policy->lookup); policy->lookup = NULL; } @@ -419,20 +282,20 @@ update_system_hostname (NMPolicy *policy, NMDevice *best) /* Try a persistent hostname first */ g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL); if (configured_hostname) { - set_system_hostname (configured_hostname, "from system configuration"); + _set_hostname (configured_hostname, "from system configuration"); g_free (configured_hostname); return; } /* Try automatically determined hostname from the best device's IP config */ if (!best) - best = get_best_device (policy->manager, &best_req); + best = get_best_ip4_device (policy->manager, &best_req); if (!best) { /* No best device; fall back to original hostname or if there wasn't * one, 'localhost.localdomain' */ - set_system_hostname (policy->orig_hostname, "no default device"); + _set_hostname (policy->orig_hostname, "no default device"); return; } @@ -446,7 +309,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best) /* Sanity check */ while (*p) { if (!isblank (*p++)) { - set_system_hostname (dhcp4_hostname, "from DHCP"); + _set_hostname (dhcp4_hostname, "from DHCP"); return; } } @@ -459,7 +322,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best) * when NM started up. */ if (policy->orig_hostname) { - set_system_hostname (policy->orig_hostname, "from system startup"); + _set_hostname (policy->orig_hostname, "from system startup"); return; } @@ -471,7 +334,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best) || (nm_ip4_config_get_num_nameservers (ip4_config) == 0) || (nm_ip4_config_get_num_addresses (ip4_config) == 0)) { /* No valid IP4 config (!!); fall back to localhost.localdomain */ - set_system_hostname (NULL, "no IPv4 config"); + _set_hostname (NULL, "no IPv4 config"); return; } @@ -479,15 +342,15 @@ update_system_hostname (NMPolicy *policy, NMDevice *best) g_assert (addr); /* checked for > 1 address above */ /* Start the hostname lookup thread */ - policy->lookup = lookup_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy); + policy->lookup = hostname_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy); if (!policy->lookup) { /* Fall back to 'localhost.localdomain' */ - set_system_hostname (NULL, "error starting hostname thread"); + _set_hostname (NULL, "error starting hostname thread"); } } static void -update_routing_and_dns (NMPolicy *policy, gboolean force_update) +update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) { NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; NMDevice *best = NULL; @@ -501,10 +364,10 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update) NMSettingConnection *s_con = NULL; const char *connection_id; - best = get_best_device (policy->manager, &best_req); + best = get_best_ip4_device (policy->manager, &best_req); if (!best) goto out; - if (!force_update && (best == policy->default_device)) + if (!force_update && (best == policy->default_device4)) goto out; /* If a VPN connection is active, it is preferred */ @@ -598,16 +461,149 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update) connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL; if (connection_id) { - nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for routing and DNS.", connection_id, ip_iface); + nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.", connection_id, ip_iface); } else { - nm_log_info (LOGD_CORE, "Policy set (%s) as default for routing and DNS.", ip_iface); + nm_log_info (LOGD_CORE, "Policy set (%s) as default for IPv4 routing and DNS.", ip_iface); } out: - /* Update the system hostname */ - update_system_hostname (policy, best); + policy->default_device4 = best; +} - policy->default_device = best; +static void +update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) +{ + NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; + NMDevice *best = NULL; + NMActRequest *best_req = NULL; + NMNamedManager *named_mgr; + GSList *devices = NULL, *iter; +#if NOT_YET + GSList *vpns; +#endif + NMIP6Config *ip6_config = NULL; + NMIP6Address *addr; + const char *ip_iface = NULL; + NMConnection *connection = NULL; + NMSettingConnection *s_con = NULL; + const char *connection_id; + + best = get_best_ip6_device (policy->manager, &best_req); + if (!best) + goto out; + if (!force_update && (best == policy->default_device6)) + goto out; + +#if NOT_YET + /* If a VPN connection is active, it is preferred */ + vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager); + for (iter = vpns; iter; iter = g_slist_next (iter)) { + NMVPNConnection *candidate = NM_VPN_CONNECTION (iter->data); + NMConnection *vpn_connection; + NMSettingIP6Config *s_ip6; + gboolean can_default = TRUE; + NMVPNConnectionState vpn_state; + + /* If it's marked 'never-default', don't make it default */ + vpn_connection = nm_vpn_connection_get_connection (candidate); + g_assert (vpn_connection); + s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (vpn_connection, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6)) + can_default = FALSE; + + vpn_state = nm_vpn_connection_get_vpn_state (candidate); + if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) { + NMIP6Config *parent_ip6; + NMDevice *parent; + + ip_iface = nm_vpn_connection_get_ip_iface (candidate); + connection = nm_vpn_connection_get_connection (candidate); + ip6_config = nm_vpn_connection_get_ip6_config (candidate); + addr = nm_ip6_config_get_address (ip6_config, 0); + + parent = nm_vpn_connection_get_parent_device (candidate); + parent_ip6 = nm_device_get_ip6_config (parent); + + nm_system_replace_default_ip6_route_vpn (ip_iface, + nm_ip6_address_get_gateway (addr), + nm_vpn_connection_get_ip4_internal_gateway (candidate), + nm_ip6_config_get_mss (ip4_config), + nm_device_get_ip_iface (parent), + nm_ip6_config_get_mss (parent_ip4)); + + dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN; + } + g_object_unref (candidate); + } + g_slist_free (vpns); +#endif + + /* The best device gets the default route if a VPN connection didn't */ + if (!ip_iface || !ip6_config) { + connection = nm_act_request_get_connection (best_req); + ip_iface = nm_device_get_ip_iface (best); + ip6_config = nm_device_get_ip6_config (best); + g_assert (ip6_config); + addr = nm_ip6_config_get_address (ip6_config, 0); + + nm_system_replace_default_ip6_route (ip_iface, nm_ip6_address_get_gateway (addr)); + + dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE; + } + + if (!ip_iface || !ip6_config) { + nm_log_warn (LOGD_CORE, "couldn't determine IP interface (%p) or IPv6 config (%p)!", + ip_iface, ip6_config); + goto out; + } + + /* Update the default active connection. Only mark the new default + * active connection after setting default = FALSE on all other connections + * first. The order is important, we don't want two connections marked + * default at the same time ever. + */ + devices = nm_manager_get_devices (policy->manager); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *dev = NM_DEVICE (iter->data); + NMActRequest *req; + + req = nm_device_get_act_request (dev); + if (req && (req != best_req)) + nm_act_request_set_default6 (req, FALSE); + } + + named_mgr = nm_named_manager_get (); + nm_named_manager_add_ip6_config (named_mgr, ip_iface, ip6_config, dns_type); + g_object_unref (named_mgr); + + /* Now set new default active connection _after_ updating DNS info, so that + * if the connection is shared dnsmasq picks up the right stuff. + */ + if (best_req) + nm_act_request_set_default6 (best_req, TRUE); + + if (connection) + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + + connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (connection_id) { + nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.", connection_id, ip_iface); + } else { + nm_log_info (LOGD_CORE, "Policy set (%s) as default for IPv6 routing and DNS.", ip_iface); + } + +out: + policy->default_device6 = best; +} + +static void +update_routing_and_dns (NMPolicy *policy, gboolean force_update) +{ + update_ip4_routing_and_dns (policy, force_update); + update_ip6_routing_and_dns (policy, force_update); + + /* Update the system hostname */ + update_system_hostname (policy, policy->default_device4); } typedef struct { @@ -796,10 +792,23 @@ device_state_changed (NMDevice *device, /* Mark the connection invalid if it failed during activation so that * it doesn't get automatically chosen over and over and over again. */ - if (connection && IS_ACTIVATING_STATE (old_state)) { - g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE)); - nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", get_connection_id (connection)); - nm_connection_clear_secrets (connection); + if (connection) { + gboolean fail = FALSE; + + if (IS_ACTIVATING_STATE (old_state)) { + nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", get_connection_id (connection)); + fail = TRUE; + } else if ( (old_state == NM_DEVICE_STATE_ACTIVATED) + && (reason == NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED)) { + nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid because IP configuration expired.", + get_connection_id (connection)); + fail = TRUE; + } + + if (fail) { + g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE)); + nm_connection_clear_secrets (connection); + } } schedule_activate_check (policy, device, 3); break; @@ -828,9 +837,9 @@ device_state_changed (NMDevice *device, } static void -device_ip4_config_changed (NMDevice *device, - GParamSpec *pspec, - gpointer user_data) +device_ip_config_changed (NMDevice *device, + GParamSpec *pspec, + gpointer user_data) { update_routing_and_dns ((NMPolicy *) user_data, TRUE); } @@ -872,7 +881,12 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data) policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG, - G_CALLBACK (device_ip4_config_changed), + G_CALLBACK (device_ip_config_changed), + policy); + policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); + + id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP6_CONFIG, + G_CALLBACK (device_ip_config_changed), policy); policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device); @@ -1083,7 +1097,7 @@ nm_policy_destroy (NMPolicy *policy) * by the lookup thread callback. */ if (policy->lookup) { - lookup_thread_die (policy->lookup); + hostname_thread_kill (policy->lookup); policy->lookup = NULL; } diff --git a/src/nm-system.c b/src/nm-system.c index bd5f57f51d..20d06b83ad 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -48,7 +48,7 @@ #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-logging.h" -#include "nm-netlink.h" +#include "nm-netlink-monitor.h" /* Because of a bug in libnl, rtnl.h should be included before route.h */ #include @@ -460,7 +460,7 @@ nm_system_device_set_ip6_route (const char *iface, g_return_val_if_fail (route != NULL, NULL); /* Destination */ - dest_addr = nl_addr_build (AF_INET6, (struct in6_addr *)ip6_dest, sizeof (*ip6_dest)); + dest_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_dest, sizeof (*ip6_dest)); g_return_val_if_fail (dest_addr != NULL, NULL); nl_addr_set_prefixlen (dest_addr, (int) ip6_prefix); @@ -469,7 +469,7 @@ nm_system_device_set_ip6_route (const char *iface, /* Gateway */ if (ip6_gateway && !IN6_IS_ADDR_UNSPECIFIED (ip6_gateway)) { - gw_addr = nl_addr_build (AF_INET6, (struct in6_addr *)ip6_gateway, sizeof (*ip6_gateway)); + gw_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_gateway, sizeof (*ip6_gateway)); if (gw_addr) { rtnl_route_set_gateway (route, gw_addr); rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE); @@ -940,6 +940,153 @@ nm_system_replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss) return success; } +static struct rtnl_route * +add_ip6_route_to_gateway (const char *iface, const struct in6_addr *gw) +{ + struct nl_handle *nlh; + struct rtnl_route *route = NULL; + struct nl_addr *gw_addr = NULL; + int iface_idx, err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, NULL); + + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx < 0) + return NULL; + + /* Gateway might be over a bridge; try adding a route to gateway first */ + route = rtnl_route_alloc (); + if (route == NULL) + return NULL; + + rtnl_route_set_family (route, AF_INET6); + rtnl_route_set_table (route, RT_TABLE_MAIN); + rtnl_route_set_oif (route, iface_idx); + rtnl_route_set_scope (route, RT_SCOPE_LINK); + + gw_addr = nl_addr_build (AF_INET, (void *) gw, sizeof (*gw)); + if (!gw_addr) + goto error; + nl_addr_set_prefixlen (gw_addr, 128); + rtnl_route_set_dst (route, gw_addr); + nl_addr_put (gw_addr); + + /* Add direct route to the gateway */ + err = rtnl_route_add (nlh, route, 0); + if (err) { + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to add IPv4 route to gateway (%d)", + iface, err); + goto error; + } + + return route; + +error: + rtnl_route_put (route); + return NULL; +} + +static int +replace_default_ip6_route (const char *iface, const struct in6_addr *gw) +{ + struct rtnl_route *route = NULL; + struct nl_handle *nlh; + struct nl_addr *gw_addr = NULL; + int iface_idx, err = -1; + + g_return_val_if_fail (iface != NULL, -ENODEV); + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, -ENOMEM); + + iface_idx = nm_netlink_iface_to_index (iface); + if (iface_idx < 0) + return -ENODEV; + + route = rtnl_route_alloc(); + g_return_val_if_fail (route != NULL, -ENOMEM); + + rtnl_route_set_family (route, AF_INET6); + rtnl_route_set_table (route, RT_TABLE_MAIN); + rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE); + rtnl_route_set_oif (route, iface_idx); + + if (gw && !IN6_IS_ADDR_UNSPECIFIED (gw)) { + /* Build up the gateway address */ + gw_addr = nl_addr_build (AF_INET6, (void *) gw, sizeof (*gw)); + if (!gw_addr) { + err = -ENOMEM; + goto out; + } + nl_addr_set_prefixlen (gw_addr, -1); + rtnl_route_set_gateway (route, gw_addr); + } + + /* Add the new default route */ + err = rtnl_route_add (nlh, route, NLM_F_REPLACE); + if (err == -EEXIST) { + /* FIXME: even though we use NLM_F_REPLACE the kernel won't replace + * the route if it's the same. Should try to remove it first, then + * add the new one again here. + */ + err = 0; + } + +out: + if (gw_addr) + nl_addr_put (gw_addr); + rtnl_route_put (route); + return err; +} + +/* + * nm_system_replace_default_ip6_route + * + * Replace default IPv6 route with one via the given gateway + * + */ +gboolean +nm_system_replace_default_ip6_route (const char *iface, const struct in6_addr *gw) +{ + struct rtnl_route *gw_route = NULL; + struct nl_handle *nlh; + gboolean success = FALSE; + int err; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, FALSE); + + err = replace_default_ip6_route (iface, gw); + if (err == 0) { + return TRUE; + } else if (err != -ESRCH) { + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to set IPv6 default route: %d", + iface, err); + return FALSE; + } + + /* Try adding a direct route to the gateway first */ + gw_route = add_ip6_route_to_gateway (iface, gw); + if (!gw_route) + return FALSE; + + /* Try adding the original route again */ + err = replace_default_ip6_route (iface, gw); + if (err != 0) { + rtnl_route_del (nlh, gw_route, 0); + nm_log_err (LOGD_DEVICE | LOGD_IP6, + "(%s): failed to set IPv6 default route (pass #2): %d", + iface, err); + } else + success = TRUE; + + rtnl_route_put (gw_route); + return success; +} + static void flush_addresses (const char *iface, gboolean ipv4_only) { int iface_idx; @@ -986,7 +1133,7 @@ foreach_route (void (*callback)(struct nl_object *, gpointer), nlh = nm_netlink_get_default_handle (); route_cache = rtnl_route_alloc_cache (nlh); - nl_cache_mngt_provide (route_cache); + g_assert (route_cache); nl_cache_foreach (route_cache, callback, user_data); nl_cache_free (route_cache); } diff --git a/src/nm-system.h b/src/nm-system.h index f2c2693ac1..e48e493c61 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -40,6 +40,9 @@ gboolean nm_system_replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss); +gboolean nm_system_replace_default_ip6_route (const char *iface, + const struct in6_addr *gw); + gboolean nm_system_replace_default_ip4_route_vpn (const char *iface, guint32 ext_gw, guint32 int_gw, diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c index 1e84c04cc6..ff0ef68c81 100644 --- a/src/nm-udev-manager.c +++ b/src/nm-udev-manager.c @@ -384,11 +384,11 @@ device_creator (NMUdevManager *manager, } if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ - device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver, ifindex); + device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver); else if (is_wireless (udev_device)) - device = (GObject *) nm_device_wifi_new (path, ifname, driver, ifindex); + device = (GObject *) nm_device_wifi_new (path, ifname, driver); else - device = (GObject *) nm_device_ethernet_new (path, ifname, driver, ifindex); + device = (GObject *) nm_device_ethernet_new (path, ifname, driver); out: if (grandparent) diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 39a994c39b..62612a0e15 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -35,6 +35,7 @@ test_policy_hosts_CPPFLAGS = \ $(GLIB_CFLAGS) test_policy_hosts_LDADD = \ + -ldl \ $(top_builddir)/src/libtest-policy-hosts.la \ $(GLIB_LIBS) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 01f639acc5..a714e991e8 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -45,7 +45,7 @@ #include "nm-dbus-glib-types.h" #include "NetworkManagerUtils.h" #include "nm-named-manager.h" -#include "nm-netlink.h" +#include "nm-netlink-monitor.h" #include "nm-glib-compat.h" #include "nm-vpn-connection-glue.h" @@ -69,6 +69,7 @@ typedef struct { gulong device_ip4; gboolean is_default; + gboolean is_default6; NMActiveConnectionState state; NMVPNConnectionState vpn_state; @@ -102,6 +103,7 @@ enum { PROP_DEVICES, PROP_STATE, PROP_DEFAULT, + PROP_DEFAULT6, PROP_VPN, PROP_VPN_STATE, PROP_BANNER, @@ -1052,6 +1054,9 @@ get_property (GObject *object, guint prop_id, case PROP_DEFAULT: g_value_set_boolean (value, priv->is_default); break; + case PROP_DEFAULT6: + g_value_set_boolean (value, priv->is_default6); + break; case PROP_VPN: g_value_set_boolean (value, TRUE); break; @@ -1122,7 +1127,14 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class) (object_class, PROP_DEFAULT, g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, "Default", - "Is the default active connection", + "Is the default IPv4 active connection", + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_DEFAULT6, + g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, + "Default6", + "Is the default IPv6 active connection", FALSE, G_PARAM_READABLE)); g_object_class_install_property diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c index 4149e6cd76..c98cf6e272 100644 --- a/system-settings/plugins/ifcfg-rh/reader.c +++ b/system-settings/plugins/ifcfg-rh/reader.c @@ -1215,7 +1215,10 @@ make_ip4_setting (shvarFile *ifcfg, if (!tmp_ip4 && !tmp_prefix && !tmp_netmask) { if (valid_ip6_config) { /* Nope, no IPv4 */ - goto done; + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL); + return NM_SETTING (s_ip4); } method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; diff --git a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index c96d13adb7..9a370be45a 100644 --- a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -2165,6 +2165,7 @@ test_read_wired_ipv6_only (void) const char *expected_dns1 = "1:2:3:4::a"; NMIP6Address *ip6_addr; struct in6_addr addr; + const char *method; connection = connection_from_file (TEST_IFCFG_WIRED_IPV6_ONLY, NULL, @@ -2217,11 +2218,18 @@ test_read_wired_ipv6_only (void) /* ===== IPv4 SETTING ===== */ s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); - ASSERT (s_ip4 == NULL, - "wired-ipv6-only-verify-ip4", "failed to verify %s: unexpected %s setting", + ASSERT (s_ip4 != NULL, + "wired-ipv6-only-verify-ip4", "failed to verify %s: missing %s setting", TEST_IFCFG_WIRED_IPV6_MANUAL, NM_SETTING_IP4_CONFIG_SETTING_NAME); + method = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0, + "wired-ipv6-only-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_IPV6_MANUAL, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + /* ===== IPv6 SETTING ===== */ s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG)); @@ -5408,6 +5416,7 @@ test_write_wired_static_ip6_only (void) NMConnection *reread; NMSettingConnection *s_con; NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; NMSettingIP6Config *s_ip6; static unsigned char tmpmac[] = { 0x31, 0x33, 0x33, 0x37, 0xbe, 0xcd }; GByteArray *mac; @@ -5461,6 +5470,17 @@ test_write_wired_static_ip6_only (void) g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); g_byte_array_free (mac, TRUE); + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wired-static-ip6-only-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL); + /* IP6 setting */ s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); ASSERT (s_ip6 != NULL, @@ -5517,9 +5537,6 @@ test_write_wired_static_ip6_only (void) ASSERT (nm_connection_verify (reread, &error), "wired-static-ip6-only-write-reread-verify", "failed to verify %s: %s", testfile, error->message); - ASSERT (nm_connection_get_setting (reread, NM_TYPE_SETTING_IP4_CONFIG) == NULL, - "wired-static-ip6-only-write-reread-verify", "unexpected IPv4 setting"); - ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, "wired-static-ip6-only-write", "written and re-read connection weren't the same."); diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c index d8df2d3fe9..d1e1458f73 100644 --- a/system-settings/plugins/ifcfg-rh/writer.c +++ b/system-settings/plugins/ifcfg-rh/writer.c @@ -934,9 +934,17 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) guint32 i, num; GString *searches; gboolean success = FALSE; + const char *method = NULL; s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (!s_ip4) { + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + /* Missing IP4 setting is assumed to be DHCP */ + if (!method) + method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { int result; /* IPv4 disabled, clear IPv4 related parameters */ diff --git a/system-settings/plugins/keyfile/io/writer.c b/system-settings/plugins/keyfile/io/writer.c index d9f36af7a6..2e194072e3 100644 --- a/system-settings/plugins/keyfile/io/writer.c +++ b/system-settings/plugins/keyfile/io/writer.c @@ -298,9 +298,9 @@ ip6_addr_writer (GKeyFile *file, GValueArray *values = g_ptr_array_index (array, i); char *key_name, *ip6_addr; - if (values->n_values % 2) { - nm_warning ("%s: error writing IP6 address %d; address array length" - " %d is not a multiple of 2.", + if (values->n_values != 3) { + nm_warning ("%s: error writing IP6 address %d (address array length " + "%d is not 3)", __func__, i, values->n_values); continue; }