diff options
author | Daniele Palmas <dnlplm@gmail.com> | 2022-06-21 10:00:07 +0200 |
---|---|---|
committer | Daniele Palmas <dnlplm@gmail.com> | 2022-08-05 18:00:12 +0200 |
commit | 85a0849cb87912000afda852a2566dbea3cb623e (patch) | |
tree | cedc248c2962da5b3f7458b9efacbd2247a0b8a5 | |
parent | dc6901b625679a8fab72c2df11fd55d4fafef64d (diff) | |
download | libmbim-85a0849cb87912000afda852a2566dbea3cb623e.tar.gz |
libmbim-glib,net-port-manager: new subclass for wwan
-rw-r--r-- | docs/reference/libmbim-glib/meson.build | 1 | ||||
-rw-r--r-- | meson.build | 4 | ||||
-rw-r--r-- | src/libmbim-glib/kernel/wwan.h | 34 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-device.c | 16 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-helpers-netlink.c | 13 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-helpers-netlink.h | 5 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-helpers.c | 67 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-helpers.h | 8 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-net-port-manager-wwan.c | 244 | ||||
-rw-r--r-- | src/libmbim-glib/mbim-net-port-manager-wwan.h | 43 | ||||
-rw-r--r-- | src/libmbim-glib/meson.build | 1 |
11 files changed, 427 insertions, 9 deletions
diff --git a/docs/reference/libmbim-glib/meson.build b/docs/reference/libmbim-glib/meson.build index 803b939..335d860 100644 --- a/docs/reference/libmbim-glib/meson.build +++ b/docs/reference/libmbim-glib/meson.build @@ -16,6 +16,7 @@ private_headers = [ 'mbim-message-private.h', 'mbim-net-port-manager.h', 'mbim-net-port-manager-wdm.h', + 'mbim-net-port-manager-wwan.h', 'wwan.h', ] diff --git a/meson.build b/meson.build index bf3428a..00ad600 100644 --- a/meson.build +++ b/meson.build @@ -118,6 +118,10 @@ endif add_project_arguments(common_flags + cc_flags, language: 'c') +if cc.check_header('linux/wwan.h') + add_project_arguments(['-DHAVE_WWAN_KERNEL_HEADER'], language: 'c') +endif + glib_version = '2.56' glib_dep = dependency('glib-2.0', version: '>= ' + glib_version) diff --git a/src/libmbim-glib/kernel/wwan.h b/src/libmbim-glib/kernel/wwan.h new file mode 100644 index 0000000..61ab8b1 --- /dev/null +++ b/src/libmbim-glib/kernel/wwan.h @@ -0,0 +1,34 @@ + +#ifndef _MBIM_WWAN_H +#define _MBIM_WWAN_H + +/* safe guard to avoid redefining WWAN symbols if + * the system provided kernel or libc headers already + * define them: HAVE_WWAN_KERNEL_HEADER is set by + * meson build system according to linux/wwan.h presence */ +#if !defined HAVE_WWAN_KERNEL_HEADER + +enum { + IFLA_WWAN_UNSPEC, + IFLA_WWAN_LINK_ID, /* u32 */ + + __IFLA_WWAN_MAX +}; +#define IFLA_WWAN_MAX (__IFLA_WWAN_MAX - 1) + +/* Both IFLA_WWAN_MAX and enum entry IFLA_PARENT_DEV_NAME got included + * in the same kernel version 5.14-rc1 */ +enum { + /* device (sysfs) name as parent, used instead + * of IFLA_LINK where there's no parent netdev + */ + IFLA_PARENT_DEV_NAME = 56, +}; + +#else + +#include <linux/wwan.h> + +#endif /* HAVE_WWAN_KERNEL_HEADER */ + +#endif /* _MBIM_WWAN_H */ diff --git a/src/libmbim-glib/mbim-device.c b/src/libmbim-glib/mbim-device.c index bfc4084..2177b0b 100644 --- a/src/libmbim-glib/mbim-device.c +++ b/src/libmbim-glib/mbim-device.c @@ -40,6 +40,7 @@ #include "mbim-proxy-control.h" #include "mbim-net-port-manager.h" #include "mbim-net-port-manager-wdm.h" +#include "mbim-net-port-manager-wwan.h" #include "mbim-basic-connect.h" #include "mbim-ms-basic-connect-extensions.h" @@ -586,15 +587,12 @@ setup_net_port_manager (MbimDevice *self, if (self->priv->net_port_manager) return TRUE; - /* For now we only support link management with cdc-mbim */ reload_wwan_iface_name (self); - if (!self->priv->wwan_iface) { - g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED, - "Link management is unsupported"); - return FALSE; - } - - self->priv->net_port_manager = MBIM_NET_PORT_MANAGER (mbim_net_port_manager_wdm_new (self->priv->wwan_iface, error)); + if (!self->priv->wwan_iface) + /* If wwan_iface is not set wwan subsystem is probably in use */ + self->priv->net_port_manager = MBIM_NET_PORT_MANAGER (mbim_net_port_manager_wwan_new (error)); + else + self->priv->net_port_manager = MBIM_NET_PORT_MANAGER (mbim_net_port_manager_wdm_new (self->priv->wwan_iface, error)); return !!self->priv->net_port_manager; } @@ -736,7 +734,7 @@ mbim_device_delete_link (MbimDevice *self, g_assert (self->priv->net_port_manager); mbim_net_port_manager_del_link (self->priv->net_port_manager, - ifname, + ifname, 5, /* timeout */ cancellable, (GAsyncReadyCallback) device_del_link_ready, diff --git a/src/libmbim-glib/mbim-helpers-netlink.c b/src/libmbim-glib/mbim-helpers-netlink.c index 9a14086..a0e69a7 100644 --- a/src/libmbim-glib/mbim-helpers-netlink.c +++ b/src/libmbim-glib/mbim-helpers-netlink.c @@ -14,6 +14,11 @@ #include <linux/if_link.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> + +/* This is a built-in file, not provided by the kernel headers, + * used to add wwan symbols if not available */ +#include <kernel/wwan.h> + #include <sys/socket.h> #include "mbim-helpers-netlink.h" @@ -84,6 +89,14 @@ mbim_helpers_netlink_append_attribute_string (NetlinkMessage *msg, } void +mbim_helpers_netlink_append_attribute_string_null (NetlinkMessage *msg, + gushort type, + const gchar *value) +{ + append_netlink_attribute (msg, type, value, strlen (value) + 1); +} + +void mbim_helpers_netlink_append_attribute_uint16 (NetlinkMessage *msg, gushort type, guint16 value) diff --git a/src/libmbim-glib/mbim-helpers-netlink.h b/src/libmbim-glib/mbim-helpers-netlink.h index 3c73b32..eff18e3 100644 --- a/src/libmbim-glib/mbim-helpers-netlink.h +++ b/src/libmbim-glib/mbim-helpers-netlink.h @@ -46,6 +46,11 @@ void mbim_helpers_netlink_append_attribute_string (NetlinkMessage *msg, const gchar *value); G_GNUC_INTERNAL +void mbim_helpers_netlink_append_attribute_string_null (NetlinkMessage *msg, + gushort type, + const gchar *value); + +G_GNUC_INTERNAL void mbim_helpers_netlink_append_attribute_uint16 (NetlinkMessage *msg, gushort type, guint16 value); diff --git a/src/libmbim-glib/mbim-helpers.c b/src/libmbim-glib/mbim-helpers.c index 3a37556..bec5302 100644 --- a/src/libmbim-glib/mbim-helpers.c +++ b/src/libmbim-glib/mbim-helpers.c @@ -163,6 +163,73 @@ mbim_helpers_list_links_wdm (GFile *sysfs_file, return TRUE; } +/*****************************************************************************/ + +gboolean +mbim_helpers_list_links_wwan (const gchar *base_ifname, + GFile *sysfs_file, + GCancellable *cancellable, + GPtrArray *previous_links, + GPtrArray **out_links, + GError **error) +{ + g_autofree gchar *sysfs_path = NULL; + g_autoptr(GFileEnumerator) direnum = NULL; + g_autoptr(GPtrArray) links = NULL; + + direnum = g_file_enumerate_children (sysfs_file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + cancellable, + error); + if (!direnum) + return FALSE; + + sysfs_path = g_file_get_path (sysfs_file); + links = g_ptr_array_new_with_free_func (g_free); + + while (TRUE) { + GFileInfo *info; + g_autofree gchar *filename = NULL; + g_autofree gchar *link_path = NULL; + g_autofree gchar *real_path = NULL; + g_autofree gchar *basename = NULL; + + if (!g_file_enumerator_iterate (direnum, &info, NULL, cancellable, error)) + return FALSE; + if (!info) + break; + + filename = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME); + if (!g_strcmp0(base_ifname, filename)) + continue; + + link_path = g_strdup_printf ("%s/%s", sysfs_path, filename); + real_path = realpath (link_path, NULL); + if (!real_path) + continue; + + basename = g_path_get_basename (real_path); + + /* skip interface if it was already known */ + if (previous_links && g_ptr_array_find_with_equal_func (previous_links, basename, g_str_equal, NULL)) + continue; + + g_ptr_array_add (links, g_steal_pointer (&basename)); + } + + if (!links || !links->len) { + *out_links = NULL; + return TRUE; + } + + g_ptr_array_sort (links, (GCompareFunc) g_ascii_strcasecmp); + *out_links = g_steal_pointer (&links); + return TRUE; +} + +/*****************************************************************************/ + #if !GLIB_CHECK_VERSION(2,54,0) gboolean diff --git a/src/libmbim-glib/mbim-helpers.h b/src/libmbim-glib/mbim-helpers.h index b314e0e..8059dfe 100644 --- a/src/libmbim-glib/mbim-helpers.h +++ b/src/libmbim-glib/mbim-helpers.h @@ -37,6 +37,14 @@ gboolean mbim_helpers_list_links_wdm (GFile *sysfs_file, GPtrArray **out_links, GError **error); +G_GNUC_INTERNAL +gboolean mbim_helpers_list_links_wwan (const gchar *base_ifname, + GFile *sysfs_file, + GCancellable *cancellable, + GPtrArray *previous_links, + GPtrArray **out_links, + GError **error); + #if !GLIB_CHECK_VERSION(2,54,0) /* Pointer Array lookup with a GEqualFunc, imported from GLib 2.54 */ diff --git a/src/libmbim-glib/mbim-net-port-manager-wwan.c b/src/libmbim-glib/mbim-net-port-manager-wwan.c new file mode 100644 index 0000000..2b00d37 --- /dev/null +++ b/src/libmbim-glib/mbim-net-port-manager-wwan.c @@ -0,0 +1,244 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * libmbim-glib -- GLib/GIO based library to control MBIM devices + * + * Copyright (C) 2022 Daniele Palmas <dnlplm@gmail.com> + * + * Based on previous work: + * Copyright (C) 2020-2021 Eric Caruso <ejcaruso@chromium.org> + * Copyright (C) 2020-2021 Andrew Lassalle <andrewlassalle@chromium.org> + * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include <linux/if_link.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +/* This is a built-in file, not provided by the kernel headers, + * used to add wwan symbols if not available */ +#include <kernel/wwan.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <string.h> + +#include "mbim-device.h" +#include "mbim-helpers.h" +#include "mbim-error-types.h" +#include "mbim-net-port-manager.h" +#include "mbim-net-port-manager-wwan.h" +#include "mbim-helpers-netlink.h" + +G_DEFINE_TYPE (MbimNetPortManagerWwan, mbim_net_port_manager_wwan, MBIM_TYPE_NET_PORT_MANAGER) + +#define WWAN_DATA_TYPE "wwan" + +/*****************************************************************************/ + +static NetlinkMessage * +netlink_message_new_link (guint link_id, + gchar *ifname, + const gchar *base_if_name) +{ + NetlinkMessage *msg; + guint linkinfo_pos, datainfo_pos; + struct rtattr info; + + msg = mbim_helpers_netlink_message_new (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL); + /* IFLA_PARENT_DEV_NAME has type NLA_NUL_STRING */ + mbim_helpers_netlink_append_attribute_string_null (msg, IFLA_PARENT_DEV_NAME, base_if_name); + mbim_helpers_netlink_append_attribute_string (msg, IFLA_IFNAME, ifname); + + /* Store the position of the next attribute to adjust its length later. */ + linkinfo_pos = mbim_helpers_netlink_get_pos_of_next_attr (msg); + mbim_helpers_netlink_append_attribute_nested (msg, IFLA_LINKINFO); + mbim_helpers_netlink_append_attribute_string (msg, IFLA_INFO_KIND, WWAN_DATA_TYPE); + + /* Store the position of the next attribute to adjust its length later. */ + datainfo_pos = mbim_helpers_netlink_get_pos_of_next_attr (msg); + mbim_helpers_netlink_append_attribute_nested (msg, IFLA_INFO_DATA); + mbim_helpers_netlink_append_attribute_uint32 (msg, IFLA_WWAN_LINK_ID, link_id); + + /* Use memcpy to preserve byte alignment */ + memcpy (&info, (char *) msg->data + datainfo_pos, sizeof (struct rtattr)); + info.rta_len = msg->len - datainfo_pos; + memcpy ((char *) msg->data + datainfo_pos, &info, sizeof (struct rtattr)); + + memcpy (&info, (char *) msg->data + linkinfo_pos, sizeof (struct rtattr)); + info.rta_len = msg->len - linkinfo_pos; + memcpy ((char *) msg->data + linkinfo_pos, &info, sizeof (struct rtattr)); + + return msg; +} + +/*****************************************************************************/ + +static gboolean +mbim_net_port_manager_wwan_list_links (MbimNetPortManager *self, + const gchar *base_ifname, + GPtrArray **out_links, + GError **error) +{ + g_autoptr(GFile) sysfs_file = NULL; + g_autofree gchar *sysfs_path = NULL; + + sysfs_path = g_strdup_printf ("/sys/class/net/%s/device/net", base_ifname); + sysfs_file = g_file_new_for_path (sysfs_path); + + return mbim_helpers_list_links_wwan (base_ifname, sysfs_file, NULL, NULL, out_links, error); +} + +/*****************************************************************************/ + +typedef struct { + guint session_id; + guint link_id; + gchar *ifname; +} AddLinkContext; + +static void +add_link_context_free (AddLinkContext *ctx) +{ + g_free (ctx->ifname); + g_free (ctx); +} + +static gchar * +mbim_net_port_manager_wwan_add_link_finish (MbimNetPortManager *self, + guint *session_id, + GAsyncResult *res, + GError **error) +{ + AddLinkContext *ctx; + + ctx = g_task_get_task_data (G_TASK (res)); + + if (!g_task_propagate_boolean (G_TASK (res), error)) { + g_prefix_error (error, "Failed to add link with session id %d: ", + ctx->session_id); + return NULL; + } + + *session_id = ctx->session_id; + return g_steal_pointer (&ctx->ifname); +} + +static void +mbim_net_port_manager_wwan_add_link (MbimNetPortManager *self, + guint session_id, + const gchar *base_ifname, + const gchar *ifname_prefix, + guint timeout, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NetlinkMessage *msg; + NetlinkTransaction *tr; + GTask *task; + GError *error = NULL; + gssize bytes_sent; + guint base_if_index; + AddLinkContext *ctx; + + task = g_task_new (self, cancellable, callback, user_data); + + ctx = g_new0 (AddLinkContext, 1); + ctx->session_id = session_id; + g_task_set_task_data (task, ctx, (GDestroyNotify) add_link_context_free); + + if (ctx->session_id == MBIM_DEVICE_SESSION_ID_AUTOMATIC) { + if (!mbim_net_port_manager_util_get_first_free_session_id (ifname_prefix, &ctx->session_id)) { + g_task_return_new_error (task, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED, + "Failed to find an available session ID"); + g_object_unref (task); + return; + } + g_debug ("Using dynamic session ID %u", ctx->session_id); + } else + g_debug ("Using static session ID %u", ctx->session_id); + + base_if_index = if_nametoindex (base_ifname); + if (!base_if_index) { + g_task_return_new_error (task, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED, + "%s interface is not available", + base_ifname); + g_object_unref (task); + return; + } + + ctx->ifname = mbim_net_port_manager_util_session_id_to_ifname (ifname_prefix, ctx->session_id); + ctx->link_id = ctx->session_id; + g_debug ("Using ifname '%s' and link id %u", ctx->ifname, ctx->link_id); + + msg = netlink_message_new_link (ctx->link_id, ctx->ifname, base_ifname); + + /* The task ownership is transferred to the transaction. */ + tr = mbim_helpers_netlink_transaction_new (mbim_net_port_manager_peek_current_sequence_id (MBIM_NET_PORT_MANAGER (self)), + mbim_net_port_manager_peek_transactions (MBIM_NET_PORT_MANAGER (self)), + msg, timeout, task); + + bytes_sent = g_socket_send (mbim_net_port_manager_peek_socket (MBIM_NET_PORT_MANAGER (self)), + (const gchar *) msg->data, + msg->len, + cancellable, + &error); + mbim_helpers_netlink_message_free (msg); + + if (bytes_sent < 0) + mbim_helpers_netlink_transaction_complete_with_error (tr, + mbim_net_port_manager_peek_transactions (MBIM_NET_PORT_MANAGER (self)), + error); + + g_object_unref (task); +} + +/*****************************************************************************/ + +MbimNetPortManagerWwan * +mbim_net_port_manager_wwan_new (GError **error) +{ + MbimNetPortManagerWwan *self; + gint socket_fd; + GSocket *gsocket; + GError *inner_error = NULL; + + socket_fd = socket (AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (socket_fd < 0) { + g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED, + "Failed to create netlink socket"); + return NULL; + } + + gsocket = g_socket_new_from_fd (socket_fd, &inner_error); + if (inner_error) { + g_debug ("Could not create socket: %s", inner_error->message); + close (socket_fd); + g_propagate_error (error, inner_error); + return NULL; + } + + self = g_object_new (MBIM_TYPE_NET_PORT_MANAGER_WWAN, NULL); + + mbim_net_port_manager_common_setup (MBIM_NET_PORT_MANAGER (self), NULL, gsocket); + + return self; +} + +static void +mbim_net_port_manager_wwan_init (MbimNetPortManagerWwan *self) +{ +} + +static void +mbim_net_port_manager_wwan_class_init (MbimNetPortManagerWwanClass *klass) +{ + MbimNetPortManagerClass *net_port_manager_class = MBIM_NET_PORT_MANAGER_CLASS (klass); + + net_port_manager_class->list_links = mbim_net_port_manager_wwan_list_links; + net_port_manager_class->add_link = mbim_net_port_manager_wwan_add_link; + net_port_manager_class->add_link_finish = mbim_net_port_manager_wwan_add_link_finish; +} diff --git a/src/libmbim-glib/mbim-net-port-manager-wwan.h b/src/libmbim-glib/mbim-net-port-manager-wwan.h new file mode 100644 index 0000000..38f1bb3 --- /dev/null +++ b/src/libmbim-glib/mbim-net-port-manager-wwan.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * libmbim-glib -- GLib/GIO based library to control MBIM devices + * + * Copyright (C) 2022 Daniele Palmas <dnlplm@gmail.com> + * + * Based on previous work: + * Copyright (C) 2020-2021 Eric Caruso <ejcaruso@chromium.org> + * Copyright (C) 2020-2021 Andrew Lassalle <andrewlassalle@chromium.org> + * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es> + */ + +#ifndef _LIBMBIM_GLIB_MBIM_NET_PORT_MANAGER_WWAN_H_ +#define _LIBMBIM_GLIB_MBIM_NET_PORT_MANAGER_WWAN_H_ + +#include <gio/gio.h> +#include <glib-object.h> + +#define MBIM_TYPE_NET_PORT_MANAGER_WWAN (mbim_net_port_manager_wwan_get_type ()) +#define MBIM_NET_PORT_MANAGER_WWAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MBIM_TYPE_NET_PORT_MANAGER_WWAN, MbimNetPortManagerWwan)) +#define MBIM_NET_PORT_MANAGER_WWAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MBIM_TYPE_NET_PORT_MANAGER_WWAN, MbimNetPortManagerWwanClass)) +#define MBIM_IS_NET_PORT_MANAGER_WWAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MBIM_TYPE_NET_PORT_MANAGER_WWAN)) +#define MBIM_IS_NET_PORT_MANAGER_WWAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MBIM_TYPE_NET_PORT_MANAGER_WWAN)) +#define MBIM_NET_PORT_MANAGER_WWAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MBIM_TYPE_NET_PORT_MANAGER_WWAN, MbimNetPortManagerWwanClass)) + +typedef struct _MbimNetPortManagerWwan MbimNetPortManagerWwan; +typedef struct _MbimNetPortManagerWwanClass MbimNetPortManagerWwanClass; + +struct _MbimNetPortManagerWwan { + MbimNetPortManager parent; +}; + +struct _MbimNetPortManagerWwanClass { + MbimNetPortManagerClass parent; +}; + +GType mbim_net_port_manager_wwan_get_type (void); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MbimNetPortManagerWwan, g_object_unref) + +MbimNetPortManagerWwan *mbim_net_port_manager_wwan_new (GError **error); + +#endif /* _LIBMBIM_GLIB_MBIM_NET_PORT_MANAGER_WWAN_H_ */ diff --git a/src/libmbim-glib/meson.build b/src/libmbim-glib/meson.build index 6c3774e..148935c 100644 --- a/src/libmbim-glib/meson.build +++ b/src/libmbim-glib/meson.build @@ -43,6 +43,7 @@ sources = files( 'mbim-message.c', 'mbim-net-port-manager.c', 'mbim-net-port-manager-wdm.c', + 'mbim-net-port-manager-wwan.c', 'mbim-proxy.c', 'mbim-proxy-helpers.c', 'mbim-utils.c', |