diff --git a/Makefile.am b/Makefile.am index a61200f2db..3e2fec99f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,7 +27,7 @@ EXTRA_DIST = \ intltool-merge.in \ intltool-update.in -DISTCHECK_CONFIGURE_FLAGS = --with-tests=yes --with-docs=yes +DISTCHECK_CONFIGURE_FLAGS = --with-tests=yes --with-docs=yes --with-udev-dir=$$dc_install_base/lib/udev DISTCLEANFILES = intltool-extract intltool-merge intltool-update diff --git a/callouts/77-nm-olpc-mesh.rules b/callouts/77-nm-olpc-mesh.rules new file mode 100644 index 0000000000..a1a1554c2b --- /dev/null +++ b/callouts/77-nm-olpc-mesh.rules @@ -0,0 +1,6 @@ +# do not edit this file, it will be overwritten on update + +# The fact that this device is driven by libertas is not currently exposed +# in the sysfs tree..? +KERNEL=="msh*", SUBSYSTEM=="net", DRIVERS=="usb", ATTRS{idVendor}=="1286", ATTRS{idProduct}=="2001", ENV{ID_NM_OLPC_MESH}="1" + diff --git a/callouts/Makefile.am b/callouts/Makefile.am index d77fea5880..385537ef62 100644 --- a/callouts/Makefile.am +++ b/callouts/Makefile.am @@ -62,6 +62,9 @@ nm_dispatcher_action_LDADD = \ nm-dispatcher-glue.h: nm-dispatcher.xml dbus-binding-tool --prefix=nm_dispatcher --mode=glib-server --output=$@ $< +udevrulesdir = $(UDEV_BASE_DIR)/rules.d +udevrules_DATA = 77-nm-olpc-mesh.rules + dbusactivationdir = $(datadir)/dbus-1/system-services dbusactivation_in_files = org.freedesktop.nm_dispatcher.service.in dbusactivation_DATA = $(dbusactivation_in_files:.service.in=.service) @@ -85,6 +88,7 @@ CLEANFILES = $(BUILT_SOURCES) $(dbusactivation_DATA) EXTRA_DIST = \ $(dbusservice_DATA) \ + $(udevrules_DATA) \ $(dbusactivation_in_files) \ nm-dispatcher.xml diff --git a/configure.ac b/configure.ac index adaf2e040f..320f51a6a6 100644 --- a/configure.ac +++ b/configure.ac @@ -193,6 +193,14 @@ PKG_CHECK_MODULES(GUDEV, gudev-1.0) AC_SUBST(GUDEV_CFLAGS) AC_SUBST(GUDEV_LIBS) +AC_ARG_WITH(udev-dir, AS_HELP_STRING([--with-udev-dir=DIR], [where the udev base directory is])) +if test -n "$with_udev_dir" ; then + UDEV_BASE_DIR="$with_udev_dir" +else + UDEV_BASE_DIR="/lib/udev" +fi +AC_SUBST(UDEV_BASE_DIR) + PKG_CHECK_EXISTS(gio-2.0,[have_gio=yes],[have_gio=no]) if test x"$have_gio" = "xno"; then AC_DEFINE([NO_GIO],[1],[Define if you don't have GIO]) diff --git a/include/NetworkManager.h b/include/NetworkManager.h index fcef15bc7f..d48552bc07 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -77,7 +77,8 @@ typedef enum NMDeviceType NM_DEVICE_TYPE_WIFI, NM_DEVICE_TYPE_GSM, NM_DEVICE_TYPE_CDMA, - NM_DEVICE_TYPE_BT /* Bluetooth */ + NM_DEVICE_TYPE_BT, /* Bluetooth */ + NM_DEVICE_TYPE_OLPC_MESH } NMDeviceType; /* DEPRECATED TYPE NAMES */ diff --git a/introspection/Makefile.am b/introspection/Makefile.am index eee9eb1891..0dc286a9cd 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -6,6 +6,7 @@ EXTRA_DIST = \ nm-access-point.xml \ nm-device-bt.xml \ nm-device-wifi.xml \ + nm-device-olpc-mesh.xml \ nm-device-ethernet.xml \ nm-device-cdma.xml \ nm-device-gsm.xml \ diff --git a/introspection/nm-device-olpc-mesh.xml b/introspection/nm-device-olpc-mesh.xml new file mode 100644 index 0000000000..7d326b6b95 --- /dev/null +++ b/introspection/nm-device-olpc-mesh.xml @@ -0,0 +1,32 @@ + + + + + + + The hardware address of the device. + + + + + The object path of the companion device. + + + + + The currently active channel. + + + + + + + A dictionary containing the FIXME: check changed parameters. + + + + Emitted when the wireless device's properties changed. + + + + diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am index 1a5a94eb3f..cbf6de36cb 100644 --- a/libnm-util/Makefile.am +++ b/libnm-util/Makefile.am @@ -23,6 +23,7 @@ libnm_util_include_HEADERS = \ nm-setting-serial.h \ nm-setting-gsm.h \ nm-setting-cdma.h \ + nm-setting-olpc-mesh.h \ nm-setting-wired.h \ nm-setting-wireless.h \ nm-setting-wireless-security.h \ @@ -46,6 +47,7 @@ libnm_util_la_SOURCES= \ nm-setting-serial.c \ nm-setting-gsm.c \ nm-setting-cdma.c \ + nm-setting-olpc-mesh.c \ nm-setting-wired.c \ nm-setting-wireless.c \ nm-setting-wireless-security.c \ diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 92228f5257..5a89a71581 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -303,6 +303,10 @@ global: nm_setting_wireless_security_remove_pairwise; nm_setting_wireless_security_remove_proto; nm_setting_wireless_security_set_wep_key; + nm_setting_olpc_mesh_get_type; + nm_setting_olpc_mesh_get_ssid; + nm_setting_olpc_mesh_get_channel; + nm_setting_olpc_mesh_get_dhcp_anycast_address; nm_utils_deinit; nm_utils_escape_ssid; nm_utils_gvalue_hash_dup; diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index 810032ae54..1d186a6e91 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -41,6 +41,7 @@ #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" #include "nm-setting-vpn.h" +#include "nm-setting-olpc-mesh.h" #include "nm-setting-serial.h" #include "nm-setting-gsm.h" @@ -216,6 +217,11 @@ register_default_settings (void) NM_SETTING_WIRELESS_ERROR, 1); + register_one_setting (NM_SETTING_OLPC_MESH_SETTING_NAME, + NM_TYPE_SETTING_OLPC_MESH, + NM_SETTING_OLPC_MESH_ERROR, + 1); + register_one_setting (NM_SETTING_GSM_SETTING_NAME, NM_TYPE_SETTING_GSM, NM_SETTING_GSM_ERROR, diff --git a/libnm-util/nm-setting-olpc-mesh.c b/libnm-util/nm-setting-olpc-mesh.c new file mode 100644 index 0000000000..dd0e1d64ce --- /dev/null +++ b/libnm-util/nm-setting-olpc-mesh.c @@ -0,0 +1,265 @@ +/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ + +/* + * Dan Williams + * Tambet Ingo + * Sjoerd Simons + * Daniel Drake + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 - 2008 Red Hat, Inc. + * (C) Copyright 2007 - 2008 Novell, Inc. + * (C) Copyright 2009 One Laptop per Child + */ + +#include +#include +#include + +#include "NetworkManager.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-utils-private.h" + +GQuark +nm_setting_olpc_mesh_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-wireless-mesh-error-quark"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +nm_setting_olpc_mesh_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Unknown error. */ + ENUM_ENTRY (NM_SETTING_OLPC_MESH_ERROR_UNKNOWN, "UnknownError"), + /* The specified property was invalid. */ + ENUM_ENTRY (NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, "InvalidProperty"), + /* The specified property was missing and is required. */ + ENUM_ENTRY (NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY, "MissingProperty"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMSettingWirelessError", values); + } + return etype; +} + +static void nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting); + +G_DEFINE_TYPE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING) + +#define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate)) + +typedef struct { + GByteArray *ssid; + guint32 channel; + GByteArray *dhcp_anycast_addr; +} NMSettingOlpcMeshPrivate; + +enum { + PROP_0, + PROP_SSID, + PROP_CHANNEL, + PROP_DHCP_ANYCAST_ADDRESS, + + LAST_PROP +}; + +static void +nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting) +{ + g_object_set (setting, NM_SETTING_NAME, NM_SETTING_OLPC_MESH_SETTING_NAME, NULL); +} + +const GByteArray * +nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL); + + return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->ssid; +} + +guint32 +nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), 0); + + return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->channel; +} + +const GByteArray * +nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL); + + return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->dhcp_anycast_addr; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (setting); + + if (!priv->ssid) { + g_set_error (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY, + NM_SETTING_OLPC_MESH_SSID); + return FALSE; + } + + if (!priv->ssid->len || priv->ssid->len > 32) { + g_set_error (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + NM_SETTING_OLPC_MESH_SSID); + return FALSE; + } + + if (priv->channel == 0 || priv->channel > 13) { + g_set_error (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + NM_SETTING_OLPC_MESH_CHANNEL); + return FALSE; + } + + if (priv->dhcp_anycast_addr && priv->dhcp_anycast_addr->len != ETH_ALEN) { + g_set_error (error, + NM_SETTING_OLPC_MESH_ERROR, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS); + return FALSE; + } + + return TRUE; +} + +static void +finalize (GObject *object) +{ + NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object); + + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + if (priv->dhcp_anycast_addr) + g_byte_array_free (priv->dhcp_anycast_addr, TRUE); + + G_OBJECT_CLASS (nm_setting_olpc_mesh_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SSID: + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = g_value_dup_boxed (value); + break; + case PROP_CHANNEL: + priv->channel = g_value_get_uint (value); + break; + case PROP_DHCP_ANYCAST_ADDRESS: + if (priv->dhcp_anycast_addr) + g_byte_array_free (priv->dhcp_anycast_addr, TRUE); + priv->dhcp_anycast_addr = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingOlpcMesh *setting = NM_SETTING_OLPC_MESH (object); + + switch (prop_id) { + case PROP_SSID: + g_value_set_boxed (value, nm_setting_olpc_mesh_get_ssid (setting)); + break; + case PROP_CHANNEL: + g_value_set_uint (value, nm_setting_olpc_mesh_get_channel (setting)); + break; + case PROP_DHCP_ANYCAST_ADDRESS: + g_value_set_boxed (value, nm_setting_olpc_mesh_get_dhcp_anycast_address (setting)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingOlpcMeshPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_SSID, + _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_SSID, + "SSID", + "SSID", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + g_object_class_install_property + (object_class, PROP_CHANNEL, + g_param_spec_uint (NM_SETTING_OLPC_MESH_CHANNEL, + "Channel", + "Channel", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE)); + + + g_object_class_install_property + (object_class, PROP_DHCP_ANYCAST_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, + "Anycast DHCP MAC address", + "Anycast DHCP MAC address", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + +} diff --git a/libnm-util/nm-setting-olpc-mesh.h b/libnm-util/nm-setting-olpc-mesh.h new file mode 100644 index 0000000000..173853d3f7 --- /dev/null +++ b/libnm-util/nm-setting-olpc-mesh.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ + +#ifndef NM_SETTING_OLPC_MESH_H +#define NM_SETTING_OLPC_MESH_H + +#include + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OLPC_MESH (nm_setting_olpc_mesh_get_type ()) +#define NM_SETTING_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMesh)) +#define NM_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass)) +#define NM_IS_SETTING_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OLPC_MESH)) +#define NM_IS_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_OLPC_MESH)) +#define NM_SETTING_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass)) + +#define NM_SETTING_OLPC_MESH_SETTING_NAME "802-11-olpc-mesh" + +typedef enum +{ + NM_SETTING_OLPC_MESH_ERROR_UNKNOWN = 0, + NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, + NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY +} NMSettingOlpcMeshError; + +#define NM_TYPE_SETTING_OLPC_MESH_ERROR (nm_setting_olpc_mesh_error_get_type ()) +GType nm_setting_olpc_mesh_error_get_type (void); + +#define NM_SETTING_OLPC_MESH_ERROR nm_setting_olpc_mesh_error_quark () +GQuark nm_setting_olpc_mesh_error_quark (void); + +#define NM_SETTING_OLPC_MESH_SSID "ssid" +#define NM_SETTING_OLPC_MESH_CHANNEL "channel" +#define NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS "dhcp-anycast-address" + +typedef struct { + NMSetting parent; +} NMSettingOlpcMesh; + +typedef struct { + NMSettingClass parent; +} NMSettingOlpcMeshClass; + +GType nm_setting_olpc_mesh_get_type (void); + +const GByteArray *nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting); +guint32 nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting); +const GByteArray *nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting); + +G_END_DECLS + +#endif /* NM_SETTING_OLPC_MESH_H */ diff --git a/src/Makefile.am b/src/Makefile.am index d8878e05df..915e972daa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,6 +68,8 @@ NetworkManager_SOURCES = \ nm-device-ethernet.h \ nm-device-wifi.c \ nm-device-wifi.h \ + nm-device-olpc-mesh.c \ + nm-device-olpc-mesh.h \ nm-device-bt.c \ nm-device-bt.h \ NetworkManagerAP.c \ @@ -128,6 +130,9 @@ nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $< +nm-device-olpc-mesh-glue.h: $(top_srcdir)/introspection/nm-device-olpc-mesh.xml + dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $< + nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $< @@ -146,6 +151,7 @@ BUILT_SOURCES = \ nm-device-interface-glue.h \ nm-device-ethernet-glue.h \ nm-device-wifi-glue.h \ + nm-device-olpc-mesh-glue.h \ nm-device-bt-glue.h \ nm-ip4-config-glue.h \ nm-ip6-config-glue.h \ diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c new file mode 100644 index 0000000000..743f47cdf7 --- /dev/null +++ b/src/nm-device-olpc-mesh.c @@ -0,0 +1,979 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * Dan Williams + * Sjoerd Simons + * Daniel Drake + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * (C) Copyright 2005 - 2008 Red Hat, Inc. + * (C) Copyright 2008 Collabora Ltd. + * (C) Copyright 2009 One Laptop per Child + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-utils.h" +#include "NetworkManagerUtils.h" +#include "NetworkManagerPolicy.h" +#include "nm-activation-request.h" +#include "nm-properties-changed-signal.h" +#include "nm-setting-connection.h" +#include "nm-setting-olpc-mesh.h" +#include "NetworkManagerSystem.h" +#include "nm-manager.h" + +#include "nm-device-olpc-mesh-glue.h" + +static void nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid); + +G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) + +#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate)) + + +enum { + PROP_0, + PROP_HW_ADDRESS, + PROP_COMPANION, + PROP_ACTIVE_CHANNEL, + PROP_IFINDEX, + + LAST_PROP +}; + +enum { + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef enum +{ + NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0, + NM_OLPC_MESH_ERROR_CONNECTION_INVALID, + NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, +} NMOlpcMeshError; + +#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ()) +#define NM_TYPE_OLPC_MESH_ERROR (nm_olpc_mesh_error_get_type ()) + + +struct _NMDeviceOlpcMeshPrivate +{ + gboolean dispose_has_run; + + struct ether_addr hw_addr; + guint32 ifindex; + + GByteArray * ssid; + + gint8 num_freqs; + guint32 freqs[IW_MAX_FREQUENCIES]; + + guint8 we_version; + gboolean up; + + NMDevice * companion; + gboolean stage1_waiting; + guint device_added_cb; +}; + +static GQuark +nm_olpc_mesh_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-mesh-error"); + return quark; +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +static GType +nm_olpc_mesh_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + /* Connection was not a wireless connection. */ + ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, "ConnectionNotMesh"), + /* Connection was not a valid wireless connection. */ + ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), + /* Connection does not apply to this device. */ + ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), + { 0, 0, 0 } + }; + etype = g_enum_register_static ("NMOlpcMeshError", values); + } + return etype; +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + int fd; + guint32 caps = NM_DEVICE_CAP_NONE; + struct iw_range range; + struct iwreq wrq; + const char *iface = nm_device_get_iface (dev); + + /* Check for Wireless Extensions support >= 16 for wireless devices */ + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_warning ("couldn't open control socket."); + goto out; + } + + memset (&wrq, 0, sizeof (struct iwreq)); + memset (&range, 0, sizeof (struct iw_range)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + wrq.u.data.pointer = (caddr_t) ⦥ + wrq.u.data.length = sizeof (struct iw_range); + + if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) { + nm_warning ("couldn't get driver range information."); + goto out; + } + + if ((wrq.u.data.length < 300) || (range.we_version_compiled < 16)) { + nm_warning ("%s: driver's Wireless Extensions version (%d) is too old.", + iface, range.we_version_compiled); + goto out; + } else { + caps |= NM_DEVICE_CAP_NM_SUPPORTED; + } + +out: + if (fd >= 0) + close (fd); + return caps; +} + +static void +nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + priv->dispose_has_run = FALSE; + priv->we_version = 0; + priv->companion = NULL; + priv->stage1_waiting = FALSE; + + memset (&(priv->hw_addr), 0, sizeof (struct ether_addr)); +} + +static guint32 iw_freq_to_uint32 (struct iw_freq *freq) +{ + if (freq->e == 0) { + /* Some drivers report channel not frequency. Convert to a + * frequency; but this assumes that the device is in b/g mode. + */ + if ((freq->m >= 1) && (freq->m <= 13)) + return 2407 + (5 * freq->m); + else if (freq->m == 14) + return 2484; + } + + return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000); +} + + +/* Until a new wireless-tools comes out that has the defs and the structure, + * need to copy them here. + */ +/* Scan capability flags - in (struct iw_range *)->scan_capa */ +#define NM_IW_SCAN_CAPA_NONE 0x00 +#define NM_IW_SCAN_CAPA_ESSID 0x01 + +struct iw_range_with_scan_capa +{ + guint32 throughput; + guint32 min_nwid; + guint32 max_nwid; + guint16 old_num_channels; + guint8 old_num_frequency; + + guint8 scan_capa; +/* don't need the rest... */ +}; + + + + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + GObjectClass *klass; + NMDeviceOlpcMesh *self; + NMDeviceOlpcMeshPrivate *priv; + const char *iface; + int fd; + struct iw_range range; + struct iwreq wrq; + int i; + + klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class); + object = klass->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + self = NM_DEVICE_OLPC_MESH (object); + priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + iface = nm_device_get_iface (NM_DEVICE (self)); + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + goto error; + + memset (&wrq, 0, sizeof (struct iwreq)); + memset (&range, 0, sizeof (struct iw_range)); + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + wrq.u.data.pointer = (caddr_t) ⦥ + wrq.u.data.length = sizeof (struct iw_range); + + if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) + goto error; + + priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); + for (i = 0; i < priv->num_freqs; i++) + priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); + + priv->we_version = range.we_version_compiled; + + close (fd); + + /* shorter timeout for mesh connectivity */ + nm_device_set_dhcp_timeout (NM_DEVICE (self), 20); + return object; + +error: + if (fd >= 0) + close (fd); + g_object_unref (object); + return NULL; +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + return nm_system_device_is_up (device); +} + +static gboolean +real_hw_bring_up (NMDevice *dev, gboolean *no_firmware) +{ + return nm_system_device_set_up_down (dev, TRUE, no_firmware); +} + +static void +real_hw_take_down (NMDevice *dev) +{ + nm_system_device_set_up_down (dev, FALSE, NULL); +} + +static gboolean +real_is_up (NMDevice *device) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + return priv->up; +} + +static gboolean +real_bring_up (NMDevice *dev) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + priv->up = TRUE; + return TRUE; +} + +static void +device_cleanup (NMDeviceOlpcMesh *self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + if (priv->ssid) { + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = NULL; + } + priv->up = FALSE; +} + +static void +real_take_down (NMDevice *dev) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + + device_cleanup (self); +} + +static gboolean +real_check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingOlpcMesh *s_mesh; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME)) { + g_set_error (error, + NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, + "The connection was not a Mesh connection."); + return FALSE; + } + + s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH)); + if (!s_mesh) { + g_set_error (error, + NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_INVALID, + "The connection was not a valid Mesh connection."); + return FALSE; + } + + return TRUE; +} + +/* + * nm_device_olpc_mesh_get_address + * + * Get a device's hardware address + * + */ +static void +nm_device_olpc_mesh_get_address (NMDeviceOlpcMesh *self, + struct ether_addr *addr) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + g_return_if_fail (self != NULL); + g_return_if_fail (addr != NULL); + + memcpy (addr, &(priv->hw_addr), sizeof (struct ether_addr)); +} + +static int +create_socket_with_request (NMDevice *self, struct iwreq *req) +{ + int sk; + const char * iface; + + g_return_val_if_fail (self != NULL, -1); + + sk = socket (AF_INET, SOCK_DGRAM, 0); + if (!sk) { + nm_error ("Couldn't create socket: %d.", errno); + return -1; + } + + memset (req, 0, sizeof (struct iwreq)); + iface = nm_device_get_iface (NM_DEVICE (self)); + strncpy (req->ifr_name, iface, IFNAMSIZ); + + return sk; +} + +static guint32 +nm_device_olpc_mesh_get_channel (NMDeviceOlpcMesh *self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + int sk; + struct iwreq req; + int ret = 0; + int i; + guint32 freq; + + sk = create_socket_with_request (NM_DEVICE (self), &req); + if (sk == -1) + return 0; + + if ((ioctl (sk, SIOCGIWFREQ, &req)) != 0) { + nm_warning ("%s: failed to get channel (errno: %d))", + nm_device_get_iface (NM_DEVICE (self)), errno); + goto out; + } + + freq = iw_freq_to_uint32 (&req.u.freq); + + for (i = 0 ; i < priv->num_freqs; i++) { + if (freq == priv->freqs[i]) + break; + } + if (i < priv->num_freqs) + ret = i + 1; + +out: + if (sk >= 0) + close (sk); + return ret; +} + +static void +nm_device_olpc_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) +{ + int sk; + struct iwreq req; + + if (nm_device_olpc_mesh_get_channel (self) == channel) + return; + + sk = create_socket_with_request (NM_DEVICE (self), &req); + if (sk < 0) + return; + + if (channel > 0) { + req.u.freq.flags = IW_FREQ_FIXED; + req.u.freq.e = 0; + req.u.freq.m = channel; + } + + if (ioctl (sk, SIOCSIWFREQ, &req) != 0) + nm_warning ("%s: failed to set to channel %d (errno: %d))", + nm_device_get_iface (NM_DEVICE (self)), channel, errno); + else + g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL); + + close (sk); +} + +static void +nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + int sk; + struct iwreq wrq; + const char * iface; + guint32 len = 0; + char buf[IW_ESSID_MAX_SIZE + 1]; + + g_return_if_fail (self != NULL); + + sk = socket (AF_INET, SOCK_DGRAM, 0); + if (!sk) { + nm_error ("Couldn't create socket: %d.", errno); + return; + } + + iface = nm_device_get_iface (NM_DEVICE (self)); + + memset (buf, 0, sizeof (buf)); + if (ssid) { + len = ssid->len; + memcpy (buf, ssid->data, MIN (sizeof (buf) - 1, len)); + } + wrq.u.essid.pointer = (caddr_t) buf; + + if (priv->we_version < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (len) + len++; + } + wrq.u.essid.length = len; + wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */ + + strncpy (wrq.ifr_name, iface, IFNAMSIZ); + + if (ioctl (sk, SIOCSIWESSID, &wrq) < 0) { + if (errno != ENODEV) { + nm_warning ("error setting SSID to '%s' for device %s: %s", + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(null)", + iface, strerror (errno)); + } + } + + 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 +real_update_hw_address (NMDevice *dev) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + struct ifreq req; + int ret, fd; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + g_warning ("could not open control socket."); + return; + } + + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); + ret = ioctl (fd, SIOCGIFHWADDR, &req); + if (ret) { + nm_warning ("%s: (%s) error getting hardware address: %d", + __func__, nm_device_get_iface (dev), errno); + goto out; + } + + if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) { + memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr)); + g_object_notify (G_OBJECT (dev), NM_DEVICE_OLPC_MESH_HW_ADDRESS); + } + +out: + close (fd); +} + + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev); + gboolean scanning; + + /* disconnect companion device, if it is connected */ + if (nm_device_get_act_request (NM_DEVICE (priv->companion))) { + nm_warning ("disconnecting companion device"); + nm_device_state_changed (NM_DEVICE (priv->companion), + NM_DEVICE_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + nm_warning ("companion disconnected"); + } + + + /* wait with continuing configuration untill the companion device is done + * scanning */ + g_object_get (priv->companion, "scanning", &scanning, NULL); + if (scanning) { + priv->stage1_waiting = TRUE; + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMConnection *connection; + NMSettingOlpcMesh *s_mesh; + NMActRequest *req; + guint32 channel; + const GByteArray *anycast_addr_array; + guint8 *anycast_addr = NULL; + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH)); + g_assert (s_mesh); + + channel = nm_setting_olpc_mesh_get_channel (s_mesh); + if (channel != 0) + nm_device_olpc_mesh_set_channel (self, channel); + nm_device_olpc_mesh_set_ssid (self, nm_setting_olpc_mesh_get_ssid (s_mesh)); + + anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh); + if (anycast_addr_array) + anycast_addr = anycast_addr_array->data; + + nm_device_set_dhcp_anycast_address (dev, anycast_addr); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static NMActStageReturn +real_act_stage4_ip4_config_timeout (NMDevice *dev, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + return NM_ACT_STAGE_RETURN_FAILURE; +} + + +static void +nm_device_olpc_mesh_dispose (GObject *object) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + if (priv->dispose_has_run) { + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); + return; + } + + priv->dispose_has_run = TRUE; + + device_cleanup (self); + + if (priv->device_added_cb != 0) + g_source_remove (priv->device_added_cb); + + priv->device_added_cb = 0; + + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device); + struct ether_addr hw_addr; + + switch (prop_id) { + case PROP_HW_ADDRESS: + nm_device_olpc_mesh_get_address (device, &hw_addr); + g_value_take_string (value, nm_ether_ntop (&hw_addr)); + break; + case PROP_COMPANION: + g_value_set_string (value, nm_device_get_path (priv->companion)); + break; + 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; + } +} + +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; + } +} + +static void +nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate)); + + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = nm_device_olpc_mesh_dispose; + + parent_class->get_type_capabilities = NULL; + parent_class->get_generic_capabilities = real_get_generic_capabilities; + parent_class->hw_is_up = real_hw_is_up; + parent_class->hw_bring_up = real_hw_bring_up; + parent_class->hw_take_down = real_hw_take_down; + parent_class->is_up = real_is_up; + parent_class->bring_up = real_bring_up; + parent_class->take_down = real_take_down; + parent_class->update_hw_address = real_update_hw_address; + parent_class->check_connection_compatible = real_check_connection_compatible; + + parent_class->act_stage1_prepare = real_act_stage1_prepare; + parent_class->act_stage2_config = real_act_stage2_config; + parent_class->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_OLPC_MESH_HW_ADDRESS, + "MAC Address", + "Hardware MAC address", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_COMPANION, + g_param_spec_string (NM_DEVICE_OLPC_MESH_COMPANION, + "Companion device", + "Companion device object path", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_ACTIVE_CHANNEL, + g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, + "Active channel", + "Active channel", + 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)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_olpc_mesh_object_info); + + dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, + NM_TYPE_OLPC_MESH_ERROR); +} + +static void +companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + gboolean scanning; + + if (!priv->stage1_waiting) + return; + + g_object_get (companion, "scanning", &scanning, NULL); + + if (!scanning) { + priv->stage1_waiting = FALSE; + nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self)); + } +} + +/* disconnect from mesh if someone starts using the companion */ +static void +companion_state_changed_cb (NMDeviceWifi *companion, NMDeviceState state, NMDeviceState old_state, NMDeviceStateReason reason, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self)); + + if ( self_state < NM_DEVICE_STATE_PREPARE + || self_state > NM_DEVICE_STATE_ACTIVATED + || state < NM_DEVICE_STATE_PREPARE + || state > NM_DEVICE_STATE_ACTIVATED) + return; + + nm_debug ("disconnecting mesh due to companion connectivity"); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); +} + +static gboolean +companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState state; + + g_object_get (G_OBJECT (self), NM_DEVICE_INTERFACE_STATE, &state, NULL); + + /* Don't allow the companion to scan while configure the mesh interface */ + return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG); +} + +static gboolean +companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState state; + + g_object_get (G_OBJECT (self), NM_DEVICE_INTERFACE_STATE, &state, NULL); + + /* Don't allow the companion to autoconnect while a mesh connection is + * active */ + return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_ACTIVATED); +} + +static gboolean +is_companion (NMDeviceOlpcMesh *self, NMDevice *other) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + struct ether_addr their_addr; + + if (!NM_IS_DEVICE_WIFI (other)) + return FALSE; + + nm_device_wifi_get_address (NM_DEVICE_WIFI (other), &their_addr); + + if (memcmp (priv->hw_addr.ether_addr_octet, + their_addr.ether_addr_octet, ETH_ALEN) != 0) { + return FALSE; + } + + /* FIXME detect when our companion leaves */ + priv->companion = other; + + g_source_remove (priv->device_added_cb); + priv->device_added_cb = 0; + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + + nm_debug ("Found companion device: %s", nm_device_get_iface (other)); + + g_signal_connect (G_OBJECT (other), "state-changed", + G_CALLBACK (companion_state_changed_cb), self); + g_signal_connect (G_OBJECT (other), "notify::scanning", + G_CALLBACK (companion_notify_cb), self); + g_signal_connect (G_OBJECT (other), "scanning-allowed", + G_CALLBACK (companion_scan_allowed_cb), self); + g_signal_connect (G_OBJECT (other), "autoconnect-allowed", + G_CALLBACK (companion_autoconnect_allowed_cb), self); + + return TRUE; +} + +static void +device_added_cb (NMDevice *other, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + + is_companion (self, other); +} + +static gboolean +check_companion_cb (gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + NMManager *manager; + GSList *list; + + if (priv->companion != NULL) { + nm_device_state_changed (NM_DEVICE (user_data), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + return FALSE; + } + + if (priv->device_added_cb != 0) + return FALSE; + + manager = nm_manager_get (NULL, NULL, NULL); + + priv->device_added_cb = g_signal_connect (manager, "device-added", + G_CALLBACK (device_added_cb), self); + + list = nm_manager_get_devices (manager); + for (; list != NULL ; list = list->next) + if (is_companion (self, NM_DEVICE (list->data))) + break; + + g_object_unref (manager); + + return FALSE; +} + +static void +state_changed_cb (NMDevice *device, NMDeviceState state, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + + switch (state) { + case NM_DEVICE_STATE_UNMANAGED: + break; + case NM_DEVICE_STATE_UNAVAILABLE: + /* If transitioning to UNAVAILBLE and the companion device is known then + * transition to DISCONNECTED otherwise wait for our companion. + */ + g_idle_add (check_companion_cb, self); + break; + case NM_DEVICE_STATE_ACTIVATED: + break; + case NM_DEVICE_STATE_FAILED: + break; + case NM_DEVICE_STATE_DISCONNECTED: + break; + default: + break; + } +} + + +NMDevice * +nm_device_olpc_mesh_new (const char *udi, + const char *iface, + const char *driver, + guint32 ifindex) +{ + GObject *obj; + + g_return_val_if_fail (udi != NULL, NULL); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + obj = g_object_new (NM_TYPE_DEVICE_OLPC_MESH, + 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); + if (obj == NULL) + return NULL; + + g_signal_connect (obj, "state-changed", G_CALLBACK (state_changed_cb), NULL); + + return NM_DEVICE (obj); +} diff --git a/src/nm-device-olpc-mesh.h b/src/nm-device-olpc-mesh.h new file mode 100644 index 0000000000..9ec051f468 --- /dev/null +++ b/src/nm-device-olpc-mesh.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* NetworkManager -- Network link manager + * + * Dan Williams + * Sjoerd Simons + * Daniel Drake + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * (C) Copyright 2005 Red Hat, Inc. + * (C) Copyright 2008 Collabora Ltd. + * (C) Copyright 2009 One Laptop per Child + */ + +#ifndef NM_DEVICE_OLPC_MESH_H +#define NM_DEVICE_OLPC_MESH_H + +#include +#include + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type ()) +#define NM_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) +#define NM_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) +#define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) + +#define NM_DEVICE_OLPC_MESH_HW_ADDRESS "hw-address" +#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 +typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh; +#endif + +typedef struct _NMDeviceOlpcMeshClass NMDeviceOlpcMeshClass; +typedef struct _NMDeviceOlpcMeshPrivate NMDeviceOlpcMeshPrivate; + +struct _NMDeviceOlpcMesh +{ + NMDevice parent; +}; + +struct _NMDeviceOlpcMeshClass +{ + NMDeviceClass parent; + + /* Signals */ + void (*properties_changed) (NMDeviceOlpcMesh *device, + GHashTable *properties); +}; + + +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); + +G_END_DECLS + +#endif /* NM_DEVICE_OLPC_MESH_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c index 07be3e3071..cdab19fb77 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -35,6 +35,7 @@ #include "nm-device-private.h" #include "nm-device-ethernet.h" #include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" #include "NetworkManagerSystem.h" #include "nm-properties-changed-signal.h" #include "nm-setting-bluetooth.h" @@ -1392,6 +1393,8 @@ find_device_by_ifindex (NMManager *self, guint32 ifindex) 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; diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c index c9a184d8f6..191ba4f145 100644 --- a/src/nm-udev-manager.c +++ b/src/nm-udev-manager.c @@ -36,6 +36,7 @@ #include "nm-utils.h" #include "NetworkManagerUtils.h" #include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" #include "nm-device-ethernet.h" typedef struct { @@ -271,6 +272,13 @@ is_wireless (GUdevDevice *device) return is_wifi; } +static gboolean +is_olpc_mesh (GUdevDevice *device) +{ + const gchar *prop = g_udev_device_get_property (device, "ID_NM_OLPC_MESH"); + return (prop != NULL); +} + static GObject * device_creator (NMUdevManager *manager, GUdevDevice *udev_device, @@ -311,7 +319,9 @@ device_creator (NMUdevManager *manager, return NULL; } - if (is_wireless (udev_device)) + if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ + device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver, ifindex); + else if (is_wireless (udev_device)) device = (GObject *) nm_device_wifi_new (path, ifname, driver, ifindex); else device = (GObject *) nm_device_ethernet_new (path, ifname, driver, ifindex);