Files
NetworkManager/src/core/devices/wifi/nm-device-olpc-mesh.c
Thomas Haller ac1a9e03e4 all: move "src/" directory to "src/core/"
Currently "src/" mostly contains the source code of the daemon.
I say mostly, because that is not true, there are also the device,
settings, wwan, ppp plugins, the initrd generator, the pppd and dhcp
helper, and probably more.

Also we have source code under libnm-core/, libnm/, clients/, and
shared/ directories. That is all confusing.

We should have one "src" directory, that contains subdirectories. Those
subdirectories should contain individual parts (libraries or
applications), that possibly have dependencies on other subdirectories.
There should be a flat hierarchy of directories under src/, which
contains individual modules.

As the name "src/" is already taken, that prevents any sensible
restructuring of the code.

As a first step, move "src/" to "src/core/". This gives space to
reorganize the code better by moving individual components into "src/".

For inspiration, look at systemd's "src/" directory.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/743
2021-02-04 09:45:55 +01:00

552 lines
19 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Dan Williams <dcbw@redhat.com>
* Sjoerd Simons <sjoerd.simons@collabora.co.uk>
* Daniel Drake <dsd@laptop.org>
* Copyright (C) 2005 - 2014 Red Hat, Inc.
* Copyright (C) 2008 Collabora Ltd.
* Copyright (C) 2009 One Laptop per Child
*/
#include "nm-default.h"
#include "nm-device-olpc-mesh.h"
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "devices/nm-device.h"
#include "nm-device-wifi.h"
#include "devices/nm-device-private.h"
#include "nm-utils.h"
#include "NetworkManagerUtils.h"
#include "nm-act-request.h"
#include "nm-setting-connection.h"
#include "nm-setting-olpc-mesh.h"
#include "nm-manager.h"
#include "platform/nm-platform.h"
#define _NMLOG_DEVICE_TYPE NMDeviceOlpcMesh
#include "devices/nm-device-logging.h"
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceOlpcMesh, PROP_COMPANION, PROP_ACTIVE_CHANNEL, );
typedef struct {
NMDevice * companion;
NMManager *manager;
bool stage1_waiting : 1;
} NMDeviceOlpcMeshPrivate;
struct _NMDeviceOlpcMesh {
NMDevice parent;
NMDeviceOlpcMeshPrivate _priv;
};
struct _NMDeviceOlpcMeshClass {
NMDeviceClass parent;
};
G_DEFINE_TYPE(NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(self) \
_NM_GET_PRIVATE(self, NMDeviceOlpcMesh, NM_IS_DEVICE_OLPC_MESH, NMDevice)
/*****************************************************************************/
static gboolean
get_autoconnect_allowed(NMDevice *device)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(device);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
/* We can't even connect if we don't have a companion yet. */
if (!priv->companion)
return FALSE;
/* We must not attempt to autoconnect when the companion is connected or
* connecting, * because we'd tear down its connection. */
if (nm_device_get_state(priv->companion) > NM_DEVICE_STATE_DISCONNECTED)
return FALSE;
return TRUE;
}
#define DEFAULT_SSID "olpc-mesh"
static gboolean
complete_connection(NMDevice * device,
NMConnection * connection,
const char * specific_object,
NMConnection *const *existing_connections,
GError ** error)
{
NMSettingOlpcMesh *s_mesh;
s_mesh = nm_connection_get_setting_olpc_mesh(connection);
if (!s_mesh) {
s_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new();
nm_connection_add_setting(connection, NM_SETTING(s_mesh));
}
if (!nm_setting_olpc_mesh_get_ssid(s_mesh)) {
gs_unref_bytes GBytes *ssid = NULL;
ssid = g_bytes_new_static(DEFAULT_SSID, NM_STRLEN(DEFAULT_SSID));
g_object_set(G_OBJECT(s_mesh), NM_SETTING_OLPC_MESH_SSID, ssid, NULL);
}
if (!nm_setting_olpc_mesh_get_dhcp_anycast_address(s_mesh)) {
const char *anycast = "c0:27:c0:27:c0:27";
g_object_set(G_OBJECT(s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, anycast, NULL);
}
nm_utils_complete_generic(nm_device_get_platform(device),
connection,
NM_SETTING_OLPC_MESH_SETTING_NAME,
existing_connections,
NULL,
_("Mesh"),
NULL,
NULL,
FALSE); /* No IPv6 by default */
return TRUE;
}
/*****************************************************************************/
static NMActStageReturn
act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(device);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
/* disconnect companion device, if it is connected */
if (nm_device_get_act_request(NM_DEVICE(priv->companion))) {
_LOGI(LOGD_OLPC, "disconnecting companion device %s", nm_device_get_iface(priv->companion));
/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
nm_device_state_changed(NM_DEVICE(priv->companion),
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_USER_REQUESTED);
_LOGI(LOGD_OLPC, "companion %s disconnected", nm_device_get_iface(priv->companion));
}
/* wait with continuing configuration until the companion device is done scanning */
if (nm_device_wifi_get_scanning(NM_DEVICE_WIFI(priv->companion))) {
priv->stage1_waiting = TRUE;
return NM_ACT_STAGE_RETURN_POSTPONE;
}
priv->stage1_waiting = FALSE;
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static gboolean
_mesh_set_channel(NMDeviceOlpcMesh *self, guint32 channel)
{
NMPlatform *platform;
int ifindex = nm_device_get_ifindex(NM_DEVICE(self));
guint32 old_channel;
platform = nm_device_get_platform(NM_DEVICE(self));
old_channel = nm_platform_mesh_get_channel(platform, ifindex);
if (channel == 0)
channel = old_channel;
/* We want to call this even if the channel number is the same,
* because that actually starts the mesh with the configured mesh ID. */
if (!nm_platform_mesh_set_channel(platform, ifindex, channel))
return FALSE;
if (old_channel != channel)
_notify(self, PROP_ACTIVE_CHANNEL);
return TRUE;
}
static NMActStageReturn
act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(device);
NMSettingOlpcMesh *s_mesh;
GBytes * ssid;
const char * anycast_addr;
gboolean success;
s_mesh = nm_device_get_applied_setting(device, NM_TYPE_SETTING_OLPC_MESH);
g_return_val_if_fail(s_mesh, NM_ACT_STAGE_RETURN_FAILURE);
ssid = nm_setting_olpc_mesh_get_ssid(s_mesh);
nm_device_take_down(NM_DEVICE(self), TRUE);
success = nm_platform_mesh_set_ssid(nm_device_get_platform(device),
nm_device_get_ifindex(device),
g_bytes_get_data(ssid, NULL),
g_bytes_get_size(ssid));
nm_device_bring_up(NM_DEVICE(self), TRUE, NULL);
if (!success) {
_LOGW(LOGD_WIFI, "Unable to set the mesh ID");
return NM_ACT_STAGE_RETURN_FAILURE;
}
anycast_addr = nm_setting_olpc_mesh_get_dhcp_anycast_address(s_mesh);
nm_device_set_dhcp_anycast_address(device, anycast_addr);
if (!_mesh_set_channel(self, nm_setting_olpc_mesh_get_channel(s_mesh))) {
_LOGW(LOGD_WIFI, "Unable to set the mesh channel");
return NM_ACT_STAGE_RETURN_FAILURE;
}
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static gboolean
is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
{
NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH(device);
if (!NM_DEVICE_OLPC_MESH_GET_PRIVATE(self)->companion) {
_LOGD(LOGD_WIFI, "not available because companion not found");
return FALSE;
}
return TRUE;
}
/*****************************************************************************/
static void
companion_cleanup(NMDeviceOlpcMesh *self)
{
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
if (priv->companion) {
nm_device_wifi_scanning_prohibited_track(NM_DEVICE_WIFI(priv->companion), self, FALSE);
g_signal_handlers_disconnect_by_data(priv->companion, self);
g_clear_object(&priv->companion);
}
_notify(self, PROP_COMPANION);
}
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);
nm_assert(NM_IS_DEVICE_WIFI(companion));
nm_assert(priv->companion == (gpointer) companion);
if (!priv->stage1_waiting)
return;
if (!nm_device_wifi_get_scanning(NM_DEVICE_WIFI(companion))) {
priv->stage1_waiting = FALSE;
nm_device_activate_schedule_stage1_device_prepare(NM_DEVICE(self), FALSE);
}
}
/* 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 (old_state > NM_DEVICE_STATE_DISCONNECTED && state <= NM_DEVICE_STATE_DISCONNECTED) {
nm_device_emit_recheck_auto_activate(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;
_LOGD(LOGD_OLPC, "disconnecting mesh due to companion connectivity");
/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
nm_device_state_changed(NM_DEVICE(self),
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_USER_REQUESTED);
}
static gboolean
companion_autoconnect_allowed_cb(NMDeviceWifi *companion, gpointer user_data)
{
NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH(user_data);
NMDeviceState state = nm_device_get_state(NM_DEVICE(self));
/* 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
check_companion(NMDeviceOlpcMesh *self, NMDevice *other)
{
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
const char * my_addr, *their_addr;
if (!NM_IS_DEVICE_WIFI(other))
return FALSE;
my_addr = nm_device_get_hw_address(NM_DEVICE(self));
their_addr = nm_device_get_hw_address(other);
if (!nm_utils_hwaddr_matches(my_addr, -1, their_addr, -1))
return FALSE;
nm_assert(priv->companion == NULL);
priv->companion = g_object_ref(other);
_LOGI(LOGD_OLPC, "found companion Wi-Fi device %s", nm_device_get_iface(other));
g_signal_connect(G_OBJECT(other),
NM_DEVICE_STATE_CHANGED,
G_CALLBACK(companion_state_changed_cb),
self);
g_signal_connect(G_OBJECT(other),
"notify::" NM_DEVICE_WIFI_SCANNING,
G_CALLBACK(companion_notify_cb),
self);
g_signal_connect(G_OBJECT(other),
NM_DEVICE_AUTOCONNECT_ALLOWED,
G_CALLBACK(companion_autoconnect_allowed_cb),
self);
_notify(self, PROP_COMPANION);
return TRUE;
}
static void
device_added_cb(NMManager *manager, NMDevice *other, gpointer user_data)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(user_data);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
if (!priv->companion && check_companion(self, other)) {
nm_device_queue_recheck_available(NM_DEVICE(self),
NM_DEVICE_STATE_REASON_NONE,
NM_DEVICE_STATE_REASON_NONE);
nm_device_remove_pending_action(NM_DEVICE(self),
NM_PENDING_ACTION_WAITING_FOR_COMPANION,
FALSE);
}
}
static void
device_removed_cb(NMManager *manager, NMDevice *other, gpointer user_data)
{
NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH(user_data);
if (other == NM_DEVICE_OLPC_MESH_GET_PRIVATE(self)->companion)
companion_cleanup(self);
}
static void
find_companion(NMDeviceOlpcMesh *self)
{
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
const CList * tmp_lst;
NMDevice * candidate;
if (priv->companion)
return;
nm_device_add_pending_action(NM_DEVICE(self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, TRUE);
/* Try to find the companion if it's already known to the NMManager */
nm_manager_for_each_device (priv->manager, candidate, tmp_lst) {
if (check_companion(self, candidate)) {
nm_device_queue_recheck_available(NM_DEVICE(self),
NM_DEVICE_STATE_REASON_NONE,
NM_DEVICE_STATE_REASON_NONE);
nm_device_remove_pending_action(NM_DEVICE(self),
NM_PENDING_ACTION_WAITING_FOR_COMPANION,
TRUE);
break;
}
}
}
static void
state_changed(NMDevice * device,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(device);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
if (new_state == NM_DEVICE_STATE_UNAVAILABLE)
find_companion(self);
if (priv->companion) {
gboolean temporarily_prohibited = FALSE;
if (new_state >= NM_DEVICE_STATE_PREPARE && new_state <= NM_DEVICE_STATE_IP_CONFIG) {
/* Don't allow the companion to scan while configuring the mesh interface */
temporarily_prohibited = TRUE;
}
nm_device_wifi_scanning_prohibited_track(NM_DEVICE_WIFI(priv->companion),
self,
temporarily_prohibited);
}
}
static guint32
get_dhcp_timeout_for_device(NMDevice *device, int addr_family)
{
/* shorter timeout for mesh connectivity */
return 20;
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(object);
NMDevice * device = NM_DEVICE(self);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
switch (prop_id) {
case PROP_COMPANION:
nm_dbus_utils_g_value_set_object_path(value, priv->companion);
break;
case PROP_ACTIVE_CHANNEL:
g_value_set_uint(value,
nm_platform_mesh_get_channel(nm_device_get_platform(device),
nm_device_get_ifindex(device)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_device_olpc_mesh_init(NMDeviceOlpcMesh *self)
{}
static void
constructed(GObject *object)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(object);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
G_OBJECT_CLASS(nm_device_olpc_mesh_parent_class)->constructed(object);
priv->manager = g_object_ref(NM_MANAGER_GET);
g_signal_connect(priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK(device_added_cb), self);
g_signal_connect(priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK(device_removed_cb), self);
}
NMDevice *
nm_device_olpc_mesh_new(const char *iface)
{
return g_object_new(NM_TYPE_DEVICE_OLPC_MESH,
NM_DEVICE_IFACE,
iface,
NM_DEVICE_TYPE_DESC,
"802.11 OLPC Mesh",
NM_DEVICE_DEVICE_TYPE,
NM_DEVICE_TYPE_OLPC_MESH,
NM_DEVICE_LINK_TYPE,
NM_LINK_TYPE_OLPC_MESH,
NULL);
}
static void
dispose(GObject *object)
{
NMDeviceOlpcMesh * self = NM_DEVICE_OLPC_MESH(object);
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE(self);
companion_cleanup(self);
if (priv->manager) {
g_signal_handlers_disconnect_by_func(priv->manager, G_CALLBACK(device_added_cb), self);
g_signal_handlers_disconnect_by_func(priv->manager, G_CALLBACK(device_removed_cb), self);
g_clear_object(&priv->manager);
}
G_OBJECT_CLASS(nm_device_olpc_mesh_parent_class)->dispose(object);
}
static const NMDBusInterfaceInfoExtended interface_info_device_olpc_mesh = {
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
NM_DBUS_INTERFACE_DEVICE_OLPC_MESH,
.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
"s",
NM_DEVICE_HW_ADDRESS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Companion",
"o",
NM_DEVICE_OLPC_MESH_COMPANION),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(
"ActiveChannel",
"u",
NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL), ), ),
.legacy_property_changed = TRUE,
};
static void
nm_device_olpc_mesh_class_init(NMDeviceOlpcMeshClass *klass)
{
GObjectClass * object_class = G_OBJECT_CLASS(klass);
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
NMDeviceClass * device_class = NM_DEVICE_CLASS(klass);
object_class->constructed = constructed;
object_class->get_property = get_property;
object_class->dispose = dispose;
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_olpc_mesh);
device_class->connection_type_supported = NM_SETTING_OLPC_MESH_SETTING_NAME;
device_class->connection_type_check_compatible = NM_SETTING_OLPC_MESH_SETTING_NAME;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_OLPC_MESH);
device_class->get_autoconnect_allowed = get_autoconnect_allowed;
device_class->complete_connection = complete_connection;
device_class->is_available = is_available;
device_class->act_stage1_prepare = act_stage1_prepare;
device_class->act_stage2_config = act_stage2_config;
device_class->state_changed = state_changed;
device_class->get_dhcp_timeout_for_device = get_dhcp_timeout_for_device;
obj_properties[PROP_COMPANION] = g_param_spec_string(NM_DEVICE_OLPC_MESH_COMPANION,
"",
"",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_ACTIVE_CHANNEL] =
g_param_spec_uint(NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL,
"",
"",
0,
G_MAXUINT32,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}