[PATCH] Connman : Initial P2P Group Managemen t API Support (CreateGroup, GetGroups, Disc onnectGroup)
Shailesh Rathod/LGSI Connectivity Team <[email protected]> Mon, 7 Jul 2025 07:06:58 +0000
| Newsgroups | dev.linux.lists.connman |
|---|---|
| Message-ID | <SE1P216MB2647C4352BF93E35C79680A8F04FA@SE1P216MB2647.KORP216.PROD.OUTLOOK.COM> |
From f8e016205a0220ae93979b6413db97d6d55236d7 Mon Sep 17 00:00:00 2001 From: shailesh <[email protected]> Date: Mon, 16 Jun 2025 11:39:54 +0000 Subject: [PATCH] Initial P2P Group Management API Support (CreateGroup, GetGroups, DisconnectGroup) - Add new D-Bus APIs to ConnMan for Wi-Fi Direct (P2P) group management: - CreateGroup: Create a P2P group with identifier and passphrase. - GetGroups: List all current P2P groups and their properties. - DisconnectGroup: Disconnect a running P2P group. - Implement group tracking, D-Bus signals for group add/remove, and group property reporting. - Integrate with wpa_supplicant for group creation, persistence, and teardown. - Add utility functions for MAC address formatting and DHCP pool management. - Update Makefile and headers to register new APIs and structures. - Extend plugins/wifi, manager, and technology logic to support P2P group lifecycle. - Add new files: include/group.h, src/group.c, src/p2pgo.c. - Update documentation and comments for new APIs and structures. --- Makefile.am | 5 +- gdhcp/gdhcp.h | 16 + gdhcp/server.c | 6 - gsupplicant/gsupplicant.h | 61 +++ gsupplicant/supplicant.c | 796 +++++++++++++++++++++++++++++- include/dbus.h | 2 +- include/group.h | 175 +++++++ include/peer.h | 3 + include/technology.h | 6 +- plugins/wifi.c | 252 +++++++++- src/connman.h | 33 ++ src/group.c | 993 ++++++++++++++++++++++++++++++++++++++ src/ippool.c | 40 ++ src/manager.c | 114 +++++ src/p2pgo.c | 477 ++++++++++++++++++ src/peer.c | 18 + src/technology.c | 77 +++ src/util.c | 39 ++ 18 files changed, 3090 insertions(+), 23 deletions(-) create mode 100644 include/group.h create mode 100644 src/group.c create mode 100644 src/p2pgo.c diff --git a/Makefile.am b/Makefile.am index b5881a8..eb0efbe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,8 @@ include_HEADERS = include/log.h include/plugin.h \ include/storage.h include/provision.h \ include/session.h include/ipaddress.h include/agent.h \ include/inotify.h include/peer.h include/machine.h \ - include/acd.h include/tethering.h + include/acd.h include/tethering.h \ + include/group.h nodist_include_HEADERS = include/version.h @@ -130,7 +131,7 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) $(stats_sources) \ src/6to4.c src/ippool.c src/bridge.c src/nat.c \ src/ipaddress.c src/inotify.c src/ipv6pd.c src/peer.c \ src/peer_service.c src/machine.c src/util.c \ - src/acd.c + src/acd.c src/group.c src/p2pgo.c if INTERNAL_DNS_BACKEND src_connmand_SOURCES += src/dnsproxy.c diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h index e3b0131..0475e7b 100644 --- a/gdhcp/gdhcp.h +++ b/gdhcp/gdhcp.h @@ -25,6 +25,7 @@ #include <stdbool.h> #include <stdint.h> #include <arpa/inet.h> +#include <netinet/if_ether.h> #include <glib.h> @@ -37,6 +38,21 @@ struct _GDHCPClient; typedef struct _GDHCPClient GDHCPClient; +/** + * Represents a DHCP lease. + * + * This structure contains information about a DHCP lease, including: + * - `expire`: The expiration time of the lease. + * - `lease_nip`: The leased IP address in network byte order. + * - `lease_mac`: The MAC address associated with the lease. + */ +struct dhcp_lease { + time_t expire; // Expiration time of the lease. + uint32_t lease_nip; // Leased IP address (network byte order). + uint8_t lease_mac[ETH_ALEN]; // MAC address associated with the lease. +}; + + typedef enum { G_DHCP_CLIENT_ERROR_NONE, G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE, diff --git a/gdhcp/server.c b/gdhcp/server.c index 52ea2a5..970f293 100644 --- a/gdhcp/server.c +++ b/gdhcp/server.c @@ -70,12 +70,6 @@ struct _GDHCPServer { gpointer debug_data; }; -struct dhcp_lease { - time_t expire; - uint32_t lease_nip; - uint8_t lease_mac[ETH_ALEN]; -}; - static inline void debug(GDHCPServer *server, const char *format, ...) { char str[256]; diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h index 77c2d46..26d5f73 100644 --- a/gsupplicant/gsupplicant.h +++ b/gsupplicant/gsupplicant.h @@ -205,6 +205,21 @@ struct _GSupplicantP2PServiceParams { typedef struct _GSupplicantP2PServiceParams GSupplicantP2PServiceParams; + +/* + * Parameters used to add a new P2P (Wi-Fi Direct) group: + * - 'persistent': whether the group should be persistent and remembered across sessions. + * - 'persistent_group_object': D-Bus object path referring to an existing persistent group (if reusing). + * - 'frequency': the desired frequency (in MHz) to create the group on; 0 means auto-selection. + */ +struct _GSupplicantP2PGroupAddParams { + dbus_bool_t persistent; + const char *persistent_group_object; + dbus_int32_t frequency; +}; + +typedef struct _GSupplicantP2PGroupAddParams GSupplicantP2PGroupAddParams; + /* global API */ typedef void (*GSupplicantCountryCallback) (int result, const char *alpha2, @@ -253,6 +268,22 @@ int g_supplicant_interface_p2p_connect(GSupplicantInterface *interface, int g_supplicant_interface_p2p_disconnect(GSupplicantInterface *interface, GSupplicantPeerParams *peer_params); +/* Disconnects a running P2P group on the given interface. */ +int g_supplicant_interface_p2p_group_disconnect(GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, + void *user_data); + +/* Adds a persistent P2P group using the specified SSID configuration. */ +int g_supplicant_interface_p2p_persistent_group_add(GSupplicantInterface *interface, + GSupplicantSSID *ssid, GSupplicantInterfaceCallback callback, + void *user_data); + +/* Creates a new P2P group with optional parameters like persistence and frequency. */ +int g_supplicant_interface_p2p_group_add(GSupplicantInterface *interface, + GSupplicantP2PGroupAddParams *group_data, + GSupplicantInterfaceCallback callback, + void *user_data); + int g_supplicant_interface_p2p_listen(GSupplicantInterface *interface, int period, int interval); @@ -264,6 +295,10 @@ int g_supplicant_interface_p2p_add_service(GSupplicantInterface *interface, int g_supplicant_interface_p2p_del_service(GSupplicantInterface *interface, GSupplicantP2PServiceParams *p2p_service_params); +/* Flushes all P2P peer information and cached data from the given interface. */ +int g_supplicant_interface_p2p_flush(GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, void *user_data); + int g_supplicant_set_widi_ies(GSupplicantP2PServiceParams *p2p_service_params, GSupplicantInterfaceCallback callback, void *user_data); @@ -362,6 +397,30 @@ bool g_supplicant_peer_is_client(GSupplicantPeer *peer); bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer); unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network); +/* Returns the interface associated with the given P2P group. */ +GSupplicantInterface *g_supplicant_group_get_interface(GSupplicantGroup *group); + +/* Returns the original interface that initiated the given P2P group. */ +GSupplicantInterface *g_supplicant_group_get_orig_interface(GSupplicantGroup *group); + +/* Returns the D-Bus object path of the given P2P group. */ +char *g_supplicant_group_get_object_path(GSupplicantGroup *group); + +/* Returns the role (e.g., GO or client) of the given P2P group. */ +int g_supplicant_group_get_role(GSupplicantGroup *group); + +/* Returns the SSID of the given P2P group. */ +char *g_supplicant_group_get_ssid(GSupplicantGroup *group); + +/* Returns the passphrase used by the P2P group. */ +char *g_supplicant_group_get_passphrase(GSupplicantGroup *group); + +/* Returns the operating frequency of the P2P group. */ +int g_supplicant_group_get_frequency(GSupplicantGroup *group); + +/* Returns whether the P2P group is persistent or not. */ +bool g_supplicant_group_get_persistent(GSupplicantGroup *group); + struct _GSupplicantCallbacks { void (*system_ready) (void); void (*system_killed) (void); @@ -391,6 +450,8 @@ struct _GSupplicantCallbacks { int reasoncode); void (*assoc_status_code)(GSupplicantInterface *interface, int reasoncode); + void (*p2p_group_started)(GSupplicantGroup *group); + void (*p2p_group_finished)(GSupplicantInterface *interface); }; typedef struct _GSupplicantCallbacks GSupplicantCallbacks; diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c index 7facfe8..36159e6 100644 --- a/gsupplicant/supplicant.c +++ b/gsupplicant/supplicant.c @@ -44,6 +44,7 @@ #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 +#define MAX_P2P_SSID_LEN 32 #define BSS_UNKNOWN_STRENGTH -90 static DBusConnection *connection; @@ -253,12 +254,21 @@ struct _GSupplicantPeer { bool connection_requested; }; +/* + * Structure representing a Wi-Fi P2P group, including its associated interfaces, * D-Bus path, role (GO/client), list of peer members, SSID, passphrase, + * persistence status, and operating frequency. + */ struct _GSupplicantGroup { GSupplicantInterface *interface; GSupplicantInterface *orig_interface; char *path; int role; GSList *members; + char *ssid; + char *passphrase; + bool persistent; + int frequency; + char * psk; }; struct interface_data { @@ -302,6 +312,8 @@ struct interface_scan_data { static int network_remove(struct interface_data *data); +void __connman_util_byte_to_string(unsigned char *src, char *dest, int len); + static inline void debug(const char *format, ...) { char str[256]; @@ -674,6 +686,152 @@ static void callback_peer_changed(GSupplicantPeer *peer, callbacks_pointer->peer_changed(peer, state); } +/** + * Invokes the registered P2P group started callback, if set, + * passing the newly created GSupplicantGroup as the argument. + * + * @param group Pointer to the newly created GSupplicantGroup. + */ +static void callback_p2p_group_started(GSupplicantGroup *group) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->p2p_group_started) + return; + + callbacks_pointer->p2p_group_started(group); +} + +/** + * Calls the registered P2P group finished callback, if available, + * passing the GSupplicantInterface that finished the P2P group. + * + * @param interface Pointer to the GSupplicantInterface that finished the P2P group. + */ +static void callback_p2p_group_finished(GSupplicantInterface *interface) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->p2p_group_finished) + return; + + callbacks_pointer->p2p_group_finished(interface); +} + +/** + * Returns the GSupplicantInterface associated with the given GSupplicantGroup. + * + * @param group The pointer to the GSupplicantGroup instance. + * @return The GSupplicantInterface associated with the group, or NULL if the group is NULL. + */ +GSupplicantInterface *g_supplicant_group_get_interface(GSupplicantGroup *group) +{ + if (!group) + return NULL; + + return group->interface; +} + +/** + * Returns the original/physical interface associated with the given P2P group. + * + * @param group Pointer to the GSupplicantGroup. + * @return Pointer to the original GSupplicantInterface, or NULL if group is NULL. + */ +GSupplicantInterface *g_supplicant_group_get_orig_interface(GSupplicantGroup *group) +{ + if (!group) + return NULL; + + return group->orig_interface; +} + +/** + * Returns the D-Bus object path associated with the given P2P group. + * + * @param group Pointer to the GSupplicantGroup. + * @return The object path string, or NULL if the group is NULL. + */ +char *g_supplicant_group_get_object_path(GSupplicantGroup *group) +{ + if (!group) + return NULL; + + return group->path; +} + +/** + * Retrieves the SSID string of the specified P2P group. + * + * @param group Pointer to the GSupplicantGroup. + * @return The SSID string, or NULL if the group is NULL. + */ +char *g_supplicant_group_get_ssid(GSupplicantGroup *group) +{ + if (!group) + return NULL; + + return group->ssid; +} + +/** + * Retrieves the passphrase of the specified P2P group. + * + * @param group Pointer to the GSupplicantGroup. + * @return The passphrase string, or NULL if the group is NULL. + */ +char *g_supplicant_group_get_passphrase(GSupplicantGroup *group) +{ + if (!group) + return NULL; + + return group->passphrase; +} + +/** + * Retrieves the operating frequency (in MHz) of the specified P2P group. + * + * @param group Pointer to the GSupplicantGroup. + * @return The frequency value, or 0 if the group is NULL. + */ +int g_supplicant_group_get_frequency(GSupplicantGroup *group) +{ + if (!group) + return 0; + + return group->frequency; +} + +/** + * Retrieves the role of the specified P2P group. + * + * @param group Pointer to the GSupplicantGroup. + * @return The role value, or 0 if the group is NULL. + */ +int g_supplicant_group_get_role(GSupplicantGroup *group) +{ + if (!group) + return 0; + + return group->role; +} + +/** + * Checks whether the specified P2P group is persistent. + * + * @param group Pointer to the GSupplicantGroup. + * @return true if the group is persistent, false if not or if group is NULL. + */ +bool g_supplicant_group_get_persistent(GSupplicantGroup *group) +{ + if (!group) + return false; + + return group->persistent; +} + static void callback_peer_request(GSupplicantPeer *peer) { if (!callbacks_pointer) @@ -3321,14 +3479,40 @@ static void signal_peer_changed(const char *path, DBusMessageIter *iter) peer->connection_requested = false; } +/** + * Holds P2P group signal data: + * peer_obj_path - object path of the peer device, + * iface_address - MAC address of the interface, + * interface_obj_path - object path of the local interface, + * group_obj_path - object path of the group, + * role - role of the device in the group, + * persistent - whether the group is persistent or not. + */ struct group_sig_data { const char *peer_obj_path; unsigned char iface_address[ETH_ALEN]; const char *interface_obj_path; const char *group_obj_path; int role; + bool persistent; }; +/** + * Parses and assigns P2P group signal properties from DBus message iterator to the provided group_sig_data struct. + * + * @param key Property name string. + * @param iter DBus message iterator pointing to the property value. + * @param user_data Pointer to group_sig_data struct where parsed data will be stored. + * + * Handles the following keys: + * - "peer_interface_addr": copies MAC address into iface_address. + * - "role": sets role enum based on string ("GO" or client). + * - "peer_object": stores peer device object path. + * - "interface_object": stores local interface object path. + * - "group_object": stores group object path. + * - "persistent": sets persistence flag. + */ + static void group_sig_property(const char *key, DBusMessageIter *iter, void *user_data) { @@ -3361,6 +3545,8 @@ static void group_sig_property(const char *key, DBusMessageIter *iter, dbus_message_iter_get_basic(iter, &data->interface_obj_path); else if (g_strcmp0(key, "group_object") == 0) dbus_message_iter_get_basic(iter, &data->group_obj_path); + else if (g_strcmp0(key, "persistent") == 0) + dbus_message_iter_get_basic(iter, &data->persistent); } @@ -3412,12 +3598,169 @@ static void signal_group_failure(const char *path, DBusMessageIter *iter) peer->connection_requested = false; } +/** + * Processes P2P group properties received via DBus and updates the GSupplicantGroup struct accordingly. + * + * @param key Property name string. + * @param iter DBus message iterator pointing to the property value. + * @param user_data Pointer to GSupplicantGroup struct to update with property values. + * + * Supported keys: + * - "SSID": copies SSID byte array to group's ssid string. + * - "Passphrase": copies passphrase string to group's passphrase. + * - "Frequency": sets group's frequency value. + * + * If key is NULL, triggers the p2p_group_started callback for the group. + */ +static void p2p_group_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + GSupplicantGroup *group = user_data; + + SUPPLICANT_DBG("key %s", key); + + if (!key) { + callback_p2p_group_started(group); + return; + } + + if (!iter) + return; + + if (g_strcmp0(key, "SSID") == 0) { + DBusMessageIter array; + char *ssid; + int ssid_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len); + + if (ssid_len > 0 && ssid_len < 33) { + group->ssid = g_strndup(ssid, ssid_len); + } + } else if (g_strcmp0(key, "Passphrase") == 0) { + char *passphrase; + + dbus_message_iter_get_basic(iter, &passphrase); + group->passphrase = g_strdup(passphrase); + } else if (g_strcmp0(key, "Frequency") == 0) { + dbus_uint16_t frequency = 0; + + dbus_message_iter_get_basic(iter, &frequency); + group->frequency = frequency; + } + + callback_p2p_group_started(group); +} + +static void p2p_group_psk_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + GSupplicantGroup *group = user_data; + int len = 0; + unsigned char *psk; + char psk_s[65]; + DBusMessageIter iter_array; + + if(iter == NULL) + return; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + { + SUPPLICANT_DBG("not array %d\n", dbus_message_iter_get_arg_type(iter)); + return; + } + + dbus_message_iter_recurse(iter, &iter_array); + + dbus_message_iter_get_fixed_array(&iter_array, &psk, &len); + __connman_util_byte_to_string(psk, psk_s, len); + group->psk = g_strdup(psk_s); + SUPPLICANT_DBG("psk : %s\n", group->psk); +} + +static void p2p_group_passphrase_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + GSupplicantGroup *group = user_data; + char *passphrase; + + if(iter == NULL) + return; + + dbus_message_iter_get_basic(iter, &passphrase); + group->passphrase = g_strdup(passphrase); + + SUPPLICANT_DBG("passphrase : %s\n", group->passphrase); + +} + +static void p2p_group_ssid_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + GSupplicantGroup *group = user_data; + char *ssid; + int len = 0; + DBusMessageIter iter_array; + GSupplicantPeer *peer = NULL; + + if(iter == NULL) + return; + + dbus_message_iter_recurse(iter, &iter_array); + + dbus_message_iter_get_fixed_array(&iter_array, &ssid, &len); + + if(len >= MAX_P2P_SSID_LEN) + len = MAX_P2P_SSID_LEN - 1; + + group->ssid = g_strndup(ssid, len); + callback_p2p_group_started(group); +} + + +/** + * Called when a P2P group is started; initiates retrieval of all group properties + * over DBus for the given group path, updating the group's properties via callback. + * + * @param group Pointer to the GSupplicantGroup representing the started P2P group. + * Must have a valid 'path' to the DBus group object. + */ +static void interface_p2p_group_started(GSupplicantGroup *group) { + + if (!group->path) + return; + + supplicant_dbus_property_get(group->path, SUPPLICANT_INTERFACE ".Group", + "PSK", p2p_group_psk_property, group, NULL); + + supplicant_dbus_property_get(group->path, SUPPLICANT_INTERFACE ".Group", + "Passphrase", p2p_group_passphrase_property, group, NULL); + + supplicant_dbus_property_get(group->path, SUPPLICANT_INTERFACE ".Group", + "SSID", p2p_group_ssid_property, group, NULL); +} + + + +/** + * Handles the DBus signal indicating a P2P group has started. + * + * Looks up the interface from the provided path, extracts group-related properties, + * creates a new GSupplicantGroup if it doesn't exist, initializes it, and inserts it + * into relevant hash tables. If a pending peer is associated, updates its state. + * Finally, triggers retrieval of group properties asynchronously. + * + * @param path DBus object path of the interface sending the signal. + * @param iter Iterator over DBus message arguments containing group properties. + */ + static void signal_group_started(const char *path, DBusMessageIter *iter) { GSupplicantInterface *interface, *g_interface; struct group_sig_data data = {}; GSupplicantGroup *group; - GSupplicantPeer *peer; + GSupplicantPeer *peer = NULL; SUPPLICANT_DBG(""); @@ -3429,11 +3772,13 @@ static void signal_group_started(const char *path, DBusMessageIter *iter) if (!data.interface_obj_path || !data.group_obj_path) return; - peer = g_hash_table_lookup(interface->peer_table, - interface->pending_peer_path); - interface->pending_peer_path = NULL; - if (!peer) - return; + if (interface->pending_peer_path) { + peer = g_hash_table_lookup(interface->peer_table, + interface->pending_peer_path); + interface->pending_peer_path = NULL; + if (!peer) + return; + } g_interface = g_hash_table_lookup(interface_table, data.interface_obj_path); @@ -3453,12 +3798,16 @@ static void signal_group_started(const char *path, DBusMessageIter *iter) group->orig_interface = interface; group->path = g_strdup(data.group_obj_path); group->role = data.role; - + group->persistent = data.persistent; g_hash_table_insert(interface->group_table, group->path, group); g_hash_table_replace(group_mapping, group->path, group); - peer->current_group_iface = g_interface; - callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_STARTED); + if (peer) { + peer->current_group_iface = g_interface; + callback_peer_changed(peer, G_SUPPLICANT_PEER_GROUP_STARTED); + } + + interface_p2p_group_started(group); } static void remove_peer_group_interface(GHashTable *group_table, @@ -3506,6 +3855,7 @@ static void signal_group_finished(const char *path, DBusMessageIter *iter) g_hash_table_remove(group_mapping, data.group_obj_path); g_hash_table_remove(interface->group_table, data.group_obj_path); + callback_p2p_group_finished(interface); } static void signal_group_request(const char *path, DBusMessageIter *iter) @@ -3683,6 +4033,9 @@ static DBusHandlerResult g_supplicant_filter(DBusConnection *conn, void g_supplicant_interface_cancel(GSupplicantInterface *interface) { + if (!interface) + return; + SUPPLICANT_DBG("Cancelling any pending DBus calls"); supplicant_dbus_method_call_cancel_all(interface); supplicant_dbus_property_call_cancel_all(interface); @@ -5630,6 +5983,82 @@ int g_supplicant_interface_p2p_disconnect(GSupplicantInterface *interface, return -EINPROGRESS; } +/** + * Handles the result of a P2P group disconnect operation. + * + * Checks if an error was reported during the disconnect request, + * logs it if present, and invokes the user callback with the status. + * Finally, frees the allocated interface_data structure. + * + * @param error Error string returned by the DBus call, or NULL on success. + * @param iter DBus message iterator for additional response data (unused). + * @param user_data Pointer to interface_data containing callback info and user data. + */ +static void interface_p2p_disconnect_result(const char *error, + DBusMessageIter *iter, void *user_data) +{ + struct interface_data *data = user_data; + int err = 0; + + if (error) { + SUPPLICANT_DBG("error %s", error); + err = -EIO; + } + + if (data->callback) + data->callback(err, data->interface, data->user_data); + + dbus_free(data); +} + +/** + * Initiates a disconnect from the current P2P group on the specified interface. + * + * Allocates and sets up user callback data, then calls the + * "Disconnect" DBus method on the P2PDevice interface. + * + * @param interface Pointer to the GSupplicantInterface to disconnect. + * @param callback Function to call when disconnect completes or fails. + * @param user_data User data to pass to the callback function. + * + * @return -EINVAL if interface is NULL, + * -ENOMEM if memory allocation fails, + * -EINPROGRESS if the disconnect request is sent successfully, + * or a negative error code if the DBus call fails immediately. + */ +int g_supplicant_interface_p2p_group_disconnect(GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, + void *user_data) +{ + struct interface_data *data; + int ret; + + if (!interface) + return -EINVAL; + + data = dbus_malloc0(sizeof(*data)); + if (!data) + return -ENOMEM; + + data->interface = interface; + data->callback = callback; + data->user_data = user_data; + + SUPPLICANT_DBG("interface->path : %s\n", interface->path); + + ret = supplicant_dbus_method_call(interface->path, + SUPPLICANT_INTERFACE ".Interface.P2PDevice", + "Disconnect", NULL, + interface_p2p_disconnect_result, data, NULL); + + if (ret < 0) { + dbus_free(data); + return ret; + } + + return -EINPROGRESS; +} + struct p2p_service_data { bool registration; GSupplicantInterface *interface; @@ -5763,6 +6192,355 @@ int g_supplicant_interface_p2p_del_service(GSupplicantInterface *interface, return -EINPROGRESS; } +/** + * Structure holding context information for initiating a P2P group addition. + * + * @interface The interface on which to add the P2P group. + * @callback Function to call when the group addition completes or fails. + * @p2p_group_add_params Parameters for the group to be added (e.g., frequency, persistence). + * @user_data User-defined data to pass to the callback. + */ +struct interface_p2p_group_add_data { + GSupplicantInterface *interface; + GSupplicantInterfaceCallback callback; + GSupplicantP2PGroupAddParams *p2p_group_add_params; + void *user_data; +}; + +/** + * Constructs a D-Bus dictionary of parameters for a P2P group addition request. + * + * This function populates a D-Bus message iterator with key-value pairs + * based on the values in the provided GSupplicantP2PGroupAddParams structure. + * + * @iter The D-Bus message iterator to write the dictionary into. + * @user_data Pointer to a struct interface_p2p_group_add_data containing + * the group addition parameters and context. + * + * Fields added to the dictionary (if valid): + * - "persistent": boolean flag indicating whether the group is persistent. + * - "persistent_group_object": object path of a previously saved persistent group. + * - "frequency": operating frequency (channel) for the group. + */ + +static void interface_p2p_group_add_params(DBusMessageIter *iter, void *user_data) +{ + DBusMessageIter dict; + struct interface_p2p_group_add_data *data = user_data; + + supplicant_dbus_dict_open(iter, &dict); + + + if (data && data->p2p_group_add_params) { + GSupplicantP2PGroupAddParams* params = data->p2p_group_add_params; + + if(params->persistent == TRUE) { + supplicant_dbus_dict_append_basic(&dict, "persistent", + DBUS_TYPE_BOOLEAN, ¶ms->persistent); + } + + if(params->persistent_group_object != NULL) { + supplicant_dbus_dict_append_basic(&dict, "persistent_group_object", + DBUS_TYPE_OBJECT_PATH, ¶ms->persistent_group_object); + } + + if(params->frequency >= 0) { + supplicant_dbus_dict_append_basic(&dict, "frequency", + DBUS_TYPE_INT32, ¶ms->frequency); + } + } + + supplicant_dbus_dict_close(iter, &dict); +} + +/** + * Handles the result of a P2P group addition operation and invokes the registered callback. + * + * This function is called when a response is received for a P2P group add request, + * either successfully or with an error. It extracts the error status, invokes the + * callback provided in the request context, and frees the context memory. + * + * @error A string describing the error if the operation failed, or NULL on success. + * @iter D-Bus message iterator containing any returned data (unused here). + * @user_data Pointer to a struct interface_p2p_group_add_data holding the original request context. + */ + +static void interface_p2p_group_add_result(const char *error, + DBusMessageIter *iter, void *user_data) +{ + struct interface_p2p_group_add_data *data = user_data; + int err = 0; + + if (error != NULL) { + SUPPLICANT_DBG("error %s", error); + err = -EIO; + } + + if (data->callback != NULL) + data->callback(err, data->interface, data->user_data); + + dbus_free(data); +} + +/** + * Initiates the creation of a P2P group via wpa_supplicant's GroupAdd D-Bus method. + * + * This function sends a D-Bus method call to request the creation of a new + * Wi-Fi Direct (P2P) group, using optional parameters like frequency or a + * persistent group object. Upon completion, the provided callback is invoked. + * + * @interface Pointer to the GSupplicantInterface representing the P2P device. + * @group_data Pointer to GSupplicantP2PGroupAddParams containing optional group settings. + * @callback Function to be called upon completion of the operation. + * @user_data User-specific data passed to the callback. + * + * @return 0 on success, negative errno-style error code on failure: + * -EINVAL if the interface is NULL, + * -ENOTSUP if the interface doesn't support P2P, + * -ENOMEM if memory allocation fails, + * or a negative value from supplicant_dbus_method_call. + */ + +int g_supplicant_interface_p2p_group_add(GSupplicantInterface *interface, + GSupplicantP2PGroupAddParams *group_data, + GSupplicantInterfaceCallback callback, + void *user_data) +{ + struct interface_p2p_group_add_data *data; + int ret; + + if (!interface) + return -EINVAL; + + if (!interface->p2p_support) + return -ENOTSUP; + + data = dbus_malloc0(sizeof(*data)); + if (data == NULL) + return -ENOMEM; + + data->interface = interface; + data->callback = callback; + data->user_data = user_data; + data->p2p_group_add_params = group_data; + + ret = supplicant_dbus_method_call(interface->path, + SUPPLICANT_INTERFACE ".Interface.P2PDevice", + "GroupAdd", + interface_p2p_group_add_params, interface_p2p_group_add_result, + data, NULL); + + if (ret < 0) + dbus_free(data); + + return ret; +} + +/** + * Appends parameters for adding a persistent P2P group to a D-Bus message. + * + * This function populates a D-Bus dictionary with the "persistent_group_object" + * key, setting it to the object path of the previously saved persistent group + * (network) on the specified interface. + * + * @iter D-Bus message iterator used to construct the method call. + * @user_data Pointer to an interface_connect_data struct, providing access + * to the GSupplicantInterface and its persistent group path. + */ + +static void interface_p2p_persistent_group_add_params(DBusMessageIter *iter, + void *user_data) +{ + DBusMessageIter dict; + struct interface_connect_data *data = user_data; + GSupplicantInterface *interface = data->interface; + + supplicant_dbus_dict_open(iter, &dict); + + supplicant_dbus_dict_append_basic(&dict, "persistent_group_object", + DBUS_TYPE_OBJECT_PATH, &interface->network_path); + + supplicant_dbus_dict_close(iter, &dict); +} + +/** + * Handles the result of a persistent P2P group add operation. + * + * Parses the result of a "GroupAdd" D-Bus method call for a persistent group. + * If an error occurred, it logs the error and parses it using supplicant error helpers. + * Finally, it invokes the registered callback with the resulting status and frees + * the interface_connect_data structure. + * + * @error String representing the D-Bus error name (NULL if no error). + * @iter D-Bus message iterator pointing to error details (if any). + * @user_data Pointer to interface_connect_data containing the interface and + * callback to invoke upon completion. + */ + +static void interface_p2p_persistent_group_add_result(const char *error, + DBusMessageIter *iter, void *user_data) +{ + struct interface_connect_data *data = user_data; + int err; + + err = 0; + if (error) { + SUPPLICANT_DBG("Group add error %s", error); + err = parse_supplicant_error(iter); + } + + if (data->callback) + data->callback(err, data->interface, data->user_data); + + dbus_free(data); +} + +/** + * Handles the result of a persistent group creation request and triggers GroupAdd. + * + * This function processes the result of a persistent group creation D-Bus call. + * On success, it extracts the object path of the newly created persistent group, + * stores it in the interface struct, and then issues a "GroupAdd" request using + * that object. On failure, it logs the error, parses it using supplicant helpers, + * and invokes the registered callback with the error code. Cleans up resources accordingly. + * + * @error D-Bus error string, or NULL if operation was successful. + * @iter D-Bus message iterator pointing to the result or error detail. + * @user_data Pointer to interface_connect_data, holding interface and callback context. + */ + +static void interface_p2p_persistent_group_result(const char *error, + DBusMessageIter *iter, void *user_data) +{ + struct interface_connect_data *data = user_data; + GSupplicantInterface *interface = data->interface; + const char *path = NULL; + int err; + + if (error) + goto error; + + dbus_message_iter_get_basic(iter, &path); + if (!path) + goto error; + + g_free(interface->network_path); + interface->network_path = g_strdup(path); + + SUPPLICANT_DBG("data->interface->path : %s\n", data->interface->path); + + supplicant_dbus_method_call(data->interface->path, + SUPPLICANT_INTERFACE ".Interface.P2PDevice", + "GroupAdd", + interface_p2p_persistent_group_add_params, + interface_p2p_persistent_group_add_result, + data, NULL); + return; + + error: + SUPPLICANT_DBG("GroupAdd error %s", error); + err = parse_supplicant_error(iter); + if (data->callback) + data->callback(err, data->interface, data->user_data); + + g_free(interface->network_path); + interface->network_path = NULL; + g_free(data); +} + +/** + * Appends parameters required to create a persistent P2P group into a D-Bus message. + * + * This function sets up the D-Bus dictionary with appropriate configuration values + * for persistent P2P group creation. It includes operational mode (`mode` = 3), + * disabled state (`disabled` = 2), security settings (via `add_network_security()`), + * and the SSID (as a null-terminated string). These values are written into the + * D-Bus message iterator passed by the caller. + * + * @iter Pointer to the main D-Bus message iterator to append dictionary to. + * @user_data Pointer to interface_connect_data containing the SSID configuration. + */ + +static void interface_p2p_persistent_group_params(DBusMessageIter *iter, void *user_data) +{ + DBusMessageIter dict; + struct interface_connect_data *data = user_data; + GSupplicantSSID *ssid = data->ssid; + dbus_uint32_t mode = 3; + dbus_uint32_t disabled = 2; + + supplicant_dbus_dict_open(iter, &dict); + + supplicant_dbus_dict_append_basic(&dict, "mode", DBUS_TYPE_UINT32, &mode); + supplicant_dbus_dict_append_basic(&dict, "disabled", + DBUS_TYPE_UINT32, &disabled); + + add_network_security(&dict, ssid); + + //The data structure is set up as a byte buffer, however + //SSID->SSID is created by ssid_ap_init - it is a null terminated string. + supplicant_dbus_dict_append_basic(&dict, "ssid", + DBUS_TYPE_STRING, + &(ssid->ssid)); + + supplicant_dbus_dict_close(iter, &dict); +} + + +/** + * Initiates the creation of a persistent P2P group via the supplicant. + * + * This function checks if the interface supports P2P and allocates memory for the operation context. + * It sets up the parameters required to create a persistent group and calls the D-Bus method + * "AddPersistentGroup" on the supplicant's P2PDevice interface. On success, it returns -EINPROGRESS + * to indicate asynchronous processing; otherwise, returns an appropriate error code. + * + * @interface Pointer to the GSupplicantInterface to use for the operation. + * @ssid Pointer to GSupplicantSSID containing the SSID for the persistent group. + * @callback Callback function to be invoked when the operation completes. + * @user_data User data to be passed to the callback. + * + * @return -EINPROGRESS on success (asynchronous), + * negative errno on failure. + */ + +int g_supplicant_interface_p2p_persistent_group_add(GSupplicantInterface *interface, + GSupplicantSSID *ssid, + GSupplicantInterfaceCallback callback, + void *user_data) +{ + struct interface_connect_data *data; + int ret; + + if (!interface) + return -EINVAL; + + if (!interface->p2p_support) + return -ENOTSUP; + + data = dbus_malloc0(sizeof(*data)); + if (!data) + return -ENOMEM; + + data->interface = interface; + data->callback = callback; + data->ssid = ssid; + data->user_data = user_data; + + ret = supplicant_dbus_method_call(interface->path, + SUPPLICANT_INTERFACE ".Interface.P2PDevice", + "AddPersistentGroup", + interface_p2p_persistent_group_params, + interface_p2p_persistent_group_result, + data, NULL); + + if (ret < 0) { + dbus_free(data); + return ret; + } + + return -EINPROGRESS; +} struct p2p_listen_data { int period; int interval; diff --git a/include/dbus.h b/include/dbus.h index bcab418..7be36c2 100644 --- a/include/dbus.h +++ b/include/dbus.h @@ -45,7 +45,7 @@ extern "C" { #define CONNMAN_SESSION_INTERFACE CONNMAN_SERVICE ".Session" #define CONNMAN_NOTIFICATION_INTERFACE CONNMAN_SERVICE ".Notification" #define CONNMAN_PEER_INTERFACE CONNMAN_SERVICE ".Peer" - +#define CONNMAN_GROUP_INTERFACE CONNMAN_SERVICE ".Group" #define CONNMAN_PRIVILEGE_MODIFY 1 #define CONNMAN_PRIVILEGE_SECRET 2 diff --git a/include/group.h b/include/group.h new file mode 100644 index 0000000..d009194 --- /dev/null +++ b/include/group.h @@ -0,0 +1,175 @@ +#ifndef __CONNMAN_GROUP_H +#define __CONNMAN_GROUP_H + +#include <gsupplicant/gsupplicant.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Represents a P2P group within ConnMan, tracking its configuration and peer state. + * + * This structure holds all relevant information for a Wi-Fi Direct (P2P) group + * managed by ConnMan, including D-Bus path, network parameters, ownership flags, + * peer metadata, and interface handles. + * + * @refcount Reference count for memory management. + * @identifier Unique identifier for the group. + * @path D-Bus object path for the group. + * @interface Pointer to the GSupplicantInterface used for this group. + * @orig_interface Pointer to the original interface that initiated the group. + * @name SSID or friendly name of the group. + * @passphrase Passphrase used for group security. + * @peer_ip Optional static IP address of the peer. + * @is_group_owner Whether the local device is the group owner. + * @is_persistent Indicates if the group is persistent across sessions. + * @tethering Whether the group is in tethering mode. + * @autonomous True if group was created without negotiation. + * @freq Frequency (channel) used for the group. + * @is_static_ip True if a static IP is assigned to this group. + * @group_owner MAC address of the group owner peer (if not local). + * @peer_list GSList of connected peer devices. + * @peer_hash GHashTable mapping peer object paths to peer structs. + * @peer_intf GHashTable mapping peer interface addresses to objects. + */ + +struct connman_group { + int refcount; + char *identifier; + char *path; + GSupplicantInterface *interface; + GSupplicantInterface *orig_interface; + + char *name; + char *passphrase; + char *peer_ip; + bool is_group_owner; + bool is_persistent; + bool tethering; + bool autonomous; + int freq; + bool is_static_ip; + + const char *group_owner; + GSList *peer_list; + GHashTable *peer_hash; + GHashTable *peer_intf; +}; + +/** + * Increase the reference count of a ConnMan group object, with debug info. + * @param group Pointer to the connman_group to be referenced. + */ +#define connman_group_ref(group) \ + connman_group_ref_debug(group, __FILE__, __LINE__, __func__) + +/** + * Decrease the reference count of a ConnMan group object, with debug info. + * Frees the group when the count reaches zero. + * @param group Pointer to the connman_group to be unreferenced. + */ +#define connman_group_unref(group) \ + connman_group_unref_debug(group, __FILE__, __LINE__, __func__) + +/** + * Prefix used for wildcard SSIDs in P2P groups. + */ +#define P2P_WILDCARD_SSID "DIRECT-" + +/** + * Length of the P2P wildcard SSID prefix. + */ +#define P2P_WILDCARD_SSID_LEN 7 + +/** + * Maximum allowed length for a P2P SSID. + */ +#define P2P_MAX_SSID 32 + +/** + * Returns the current active connman_group instance. + * @return Pointer to the current connman_group. + */ +struct connman_group *__connman_group(); + +/** + * Retrieves the D-Bus object path of the specified group. + * @param group Pointer to the connman_group. + * @return Const string of the group's D-Bus object path. + */ +const char* __connman_group_get_path(struct connman_group *group); + +/** + * Retrieves the unique identifier of the specified group. + * @param group Pointer to the connman_group. + * @return Const string of the group's identifier. + */ +const char* __connman_group_get_identifier(struct connman_group *group); + +/** + * Accepts an incoming connection request on the group using WPS parameters. + * @param group Pointer to the connman_group. + * @param wps_params Pointer to the WPS parameters for connection. + * @return 0 on success, negative error code on failure. + */ +int __connman_group_accept_connection(struct connman_group *group, GSupplicantPeerParams *wps_params); + +/** + * Checks whether any connman_group currently exists. + * @return true if a group exists, false otherwise. + */ +bool __connman_group_exist(void); + +/** + * Looks up a connman_group by its identifier. + * @param identifier Unique identifier string of the group. + * @return Pointer to the connman_group if found, NULL otherwise. + */ +struct connman_group *__connman_group_lookup_from_ident(const char *identifier); + +/** + * Appends the list of connman groups into a D-Bus message iterator. + * @param iter Pointer to the DBusMessageIter to append group list information. + */ +void __connman_group_list_struct(DBusMessageIter *iter); + +/** + * Creates a new connman_group with the given parameters. + * @param iface Pointer to the GSupplicantInterface for this group. + * @param ifname Network interface name string. + * @param ssid SSID string for the group. + * @param passphrase Passphrase string for securing the group. + * @param go True if the local device is the Group Owner. + * @param persistent True if the group is persistent. + * @param go_path D-Bus object path of the Group Owner interface. + * @param autonomous True if the group was autonomously created. + * @param freq Operating frequency (channel) for the group. + * @return Pointer to the newly created connman_group. + */ +struct connman_group* __connman_group_create(GSupplicantInterface *iface, const char *ifname, const char *ssid, const char *passphrase, + bool go, bool persistent, const char *go_path, bool autonomous, int freq); + +/** + * Removes and cleans up the connman_group associated with the given interface. + * @param interface Pointer to the GSupplicantInterface whose group is to be removed. + */ +void __connman_group_remove(GSupplicantInterface *interface); + +/** + * Initializes the connman_group subsystem, preparing internal structures. + */ +void __connman_group_init(void); + +/** + * Cleans up the connman_group subsystem, freeing resources. + */ +void __connman_group_cleanup(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __CONNMAN_GROUP_H */ + diff --git a/include/peer.h b/include/peer.h index 8066393..6fe5e64 100644 --- a/include/peer.h +++ b/include/peer.h @@ -109,6 +109,9 @@ struct connman_peer_driver { int connman_peer_driver_register(struct connman_peer_driver *driver); void connman_peer_driver_unregister(struct connman_peer_driver *driver); +struct connman_peer *connman_peer_get_by_path(const char *path); +void __connman_peer_get_properties_struct(DBusMessageIter *iter, gpointer user_data); + bool connman_peer_service_is_master(void); #ifdef __cplusplus diff --git a/include/technology.h b/include/technology.h index fd7b87a..391b446 100644 --- a/include/technology.h +++ b/include/technology.h @@ -23,6 +23,7 @@ #define __CONNMAN_TECHNOLOGY_H #include <connman/service.h> +#include <gdbus.h> #ifdef __cplusplus extern "C" { @@ -66,11 +67,14 @@ struct connman_technology_driver { int (*set_regdom) (struct connman_technology *technology, const char *alpha2); int (*set_p2p_listen) (struct connman_technology *technology, bool enable); + int (*set_p2p_go) (DBusMessage *msg, struct connman_technology *technology, + const char *identifier, const char *passphrase); }; int connman_technology_driver_register(struct connman_technology_driver *driver); void connman_technology_driver_unregister(struct connman_technology_driver *driver); - +void connman_technology_set_p2p_listen(struct connman_technology *technology,bool enabled); +bool connman_technology_get_p2p_listen(struct connman_technology *technology); #ifdef __cplusplus } #endif diff --git a/plugins/wifi.c b/plugins/wifi.c index cbfcf5d..9fa22dd 100644 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -55,8 +55,9 @@ #include <connman/utsname.h> #include <connman/machine.h> #include <connman/tethering.h> - +#include "include/group.h" #include <gsupplicant/gsupplicant.h> +#include "include/dbus.h" #include "src/shared/util.h" @@ -73,6 +74,16 @@ #define P2P_CONNECTION_TIMEOUT 100 #define P2P_LISTEN_PERIOD 500 #define P2P_LISTEN_INTERVAL 2000 +// Prefix string used for naming P2P group interfaces. +static char *p2p_group_if_prefix = "p2p-"; + +// Stores the current P2P group interface name, if assigned. +static char *p2p_group_ifname = NULL; + +// Stores the interface index of the current P2P group interface; -1 if uninitialized. +static int p2p_group_ifindex = -1; + +static char *p2p_go_identifier = NULL; #define ASSOC_STATUS_AUTH_TIMEOUT 16 #define ASSOC_STATUS_NO_CLIENT 17 @@ -81,6 +92,8 @@ static struct connman_technology *wifi_technology = NULL; static struct connman_technology *p2p_technology = NULL; +static DBusConnection *connection; + enum wifi_ap_capability{ WIFI_AP_UNKNOWN = 0, WIFI_AP_SUPPORTED = 1, @@ -181,11 +194,28 @@ static GList *pending_wifi_device = NULL; static GList *p2p_iface_list = NULL; static bool wfd_service_registered = false; +// Holds the current DBus message related to group operations. +static DBusMessage *group_msg; + +// Flag indicating whether a P2P group creation is in progress. +static bool create_group_flag = false; + +// Stops an ongoing P2P device discovery or find operation. +// 'data' is user-defined data passed to the function. +static gboolean p2p_find_stop(gpointer data); + +// Initializes a GSupplicantSSID structure with the given SSID and passphrase. +// Returns a pointer to the initialized GSupplicantSSID. +static GSupplicantSSID *ssid_ap_init_p2p(const char *ssid, const char *passphrase); + + static void start_autoscan(struct connman_device *device); static int tech_set_tethering(struct connman_technology *technology, const char *bridge, bool enabled); static int tech_set_p2p_listen(struct connman_technology *technology, bool enable); +void __connman_p2p_go_set_bridge(char *bridge); + static int p2p_tech_probe(struct connman_technology *technology) { p2p_technology = technology; @@ -198,11 +228,88 @@ static void p2p_tech_remove(struct connman_technology *technology) p2p_technology = NULL; } +/** + * Sets up a P2P Group Owner (GO) on the specified technology. + * + * @param msg The DBus message initiating the request. + * @param technology The ConnMan technology on which to set the P2P GO. + * @param identifier Optional identifier for a persistent group. + * @param passphrase Optional passphrase for the P2P group. + * + * @return 0 on success, negative error code on failure. + * + * This function iterates over all interfaces supporting P2P, stops + * any ongoing P2P discovery or listen mode if necessary, and then + * creates a P2P group on each interface. If identifier or passphrase + * is provided, it attempts to add a persistent group with those credentials. + * Otherwise, it creates a new temporary group. The DBus message is + * referenced and a flag is set to indicate group creation is in progress. + */ + +static int tech_set_p2p_go(DBusMessage *msg, struct connman_technology *technology, + const char *identifier, const char *passphrase) { + GList *list; + GSupplicantInterface *interface; + struct wifi_data *wifi; + struct wifi_tethering_info *info; + char p2p_ssid[P2P_MAX_SSID] = {0x00,}; + int err; + + if (!p2p_technology) + return -EOPNOTSUPP; + + for (list = iface_list; list; list = list->next) { + struct wifi_data *wifi = list->data; + GSupplicantInterface *iface = wifi->interface; + + if (!iface || !g_supplicant_interface_has_p2p(iface)) + continue; + + if (connman_technology_get_p2p_listen(technology) == TRUE) { + wifi->servicing--; + if (!wifi->servicing || wifi->servicing < 0) { + g_supplicant_interface_p2p_listen(iface, 0, 0); + wifi->servicing = 0; + connman_technology_set_p2p_listen(technology, false); + } + } else { + if (wifi->device) + p2p_find_stop(wifi->device); + } + + info = g_try_malloc0(sizeof(struct wifi_tethering_info)); + if (info == NULL ) + return -ENOMEM; + + info->wifi = wifi; + info->technology = technology; + + if (identifier || passphrase) { + snprintf(p2p_ssid, P2P_MAX_SSID, "%s%s", P2P_WILDCARD_SSID, identifier); + info->ssid = ssid_ap_init_p2p(p2p_ssid, passphrase); + + err = g_supplicant_interface_p2p_persistent_group_add(iface, + info->ssid, NULL, info); + + } else { + err = g_supplicant_interface_p2p_group_add(iface, NULL, + NULL, info); + } + + group_msg = dbus_message_ref(msg); + create_group_flag = true; + } + + return -err; +} + + static struct connman_technology_driver p2p_tech_driver = { .name = "p2p", .type = CONNMAN_SERVICE_TYPE_P2P, .probe = p2p_tech_probe, .remove = p2p_tech_remove, + .set_p2p_go = tech_set_p2p_go, }; static bool is_p2p_connecting(void) @@ -1536,6 +1643,8 @@ static void finalize_interface_creation(struct wifi_data *wifi) if (wifi->p2p_device) return; + __connman_group_init(); + if (!wifi->autoscan) setup_autoscan(wifi); @@ -1601,6 +1710,8 @@ static int wifi_enable(struct connman_device *device) if (is_p2p_connecting()) return -EINPROGRESS; + __connman_group_init(); + interface = connman_inet_ifname(index); ret = g_supplicant_interface_create(interface, driver, NULL, interface_create_callback, @@ -1647,7 +1758,7 @@ static int wifi_disable(struct connman_device *device) remove_networks(device, wifi); remove_peers(wifi); - + __connman_group_cleanup(); ret = g_supplicant_interface_remove(wifi->interface, NULL, NULL); if (ret < 0) return ret; @@ -3336,6 +3447,80 @@ static void assoc_status_code(GSupplicantInterface *interface, int status_code) } } +/** + * Handles the event when a P2P group has started. + * + * @param group The GSupplicantGroup representing the started P2P group. + * + * This function retrieves the associated wifi data and group properties + * (SSID, passphrase, frequency, role, persistence), creates or updates + * a corresponding ConnMan group structure, and if the device is the group + * owner and the group was just created, sends a DBus reply with the group's + * object path. It also resets the creation flag and releases the DBus message. + */ + +static void p2p_group_started(GSupplicantGroup *group) +{ + + struct wifi_data *wifi; + struct connman_group *connman_group = NULL; + GSupplicantInterface *iface = g_supplicant_group_get_interface(group); + + wifi = g_supplicant_interface_get_data(iface); + + if(!wifi) + return; + + const char* go_path = g_supplicant_group_get_object_path(group); + + bool is_group_owner = false; + if (g_supplicant_group_get_role(group) == G_SUPPLICANT_GROUP_ROLE_GO){ + is_group_owner = true; + __connman_p2p_go_set_bridge(p2p_group_ifname); + } + const char *ssid = g_supplicant_group_get_ssid(group); + const char *passphrase = g_supplicant_group_get_passphrase(group); + int freq = g_supplicant_group_get_frequency(group); + bool persistent = g_supplicant_group_get_persistent(group); + + connman_group = __connman_group_create(iface, p2p_group_ifname, ssid, passphrase, + is_group_owner, persistent, go_path, create_group_flag, freq); + + const char *connman_group_path = __connman_group_get_path(connman_group); + + if (is_group_owner) + p2p_go_identifier = g_strdup(__connman_group_get_identifier(connman_group)); + if (is_group_owner && create_group_flag) { + g_dbus_send_reply(connection, group_msg, + DBUS_TYPE_OBJECT_PATH, &connman_group_path, + DBUS_TYPE_INVALID); + + create_group_flag = FALSE; + dbus_message_unref(group_msg); + } +} + +/** + * Handles the event when a P2P group has finished. + * + * @param interface The GSupplicantInterface for the P2P group that finished. + * + * This function logs the event and removes the associated ConnMan group + * corresponding to the given interface. + */ + +static void p2p_group_finished(GSupplicantInterface *interface) +{ + DBG(""); + + if (p2p_go_identifier) { + g_free(p2p_go_identifier); + p2p_go_identifier = NULL; + } + + __connman_group_remove(interface); +} + static const GSupplicantCallbacks callbacks = { .system_ready = system_ready, .system_killed = system_killed, @@ -3359,9 +3544,10 @@ static const GSupplicantCallbacks callbacks = { .debug = debug, .disconnect_reasoncode = disconnect_reasoncode, .assoc_status_code = assoc_status_code, + .p2p_group_started = p2p_group_started, + .p2p_group_finished = p2p_group_finished, }; - static int tech_probe(struct connman_technology *technology) { wifi_technology = technology; @@ -3374,6 +3560,35 @@ static void tech_remove(struct connman_technology *technology) wifi_technology = NULL; } + +static GSupplicantSSID *ssid_ap_init_p2p(const char *ssid, const char *passphrase) +{ + GSupplicantSSID *ap; + + ap = g_try_malloc0(sizeof(GSupplicantSSID)); + if (!ap) + return NULL; + + ap->mode = G_SUPPLICANT_MODE_MASTER; + ap->ssid = ssid; + ap->ssid_len = strlen(ssid); + ap->scan_ssid = 0; + ap->freq = 2412; + + if (!passphrase || strlen(passphrase) == 0) { + ap->security = G_SUPPLICANT_SECURITY_NONE; + ap->passphrase = NULL; + } else { + ap->security = G_SUPPLICANT_SECURITY_PSK; + ap->protocol = G_SUPPLICANT_PROTO_RSN; + ap->pairwise_cipher = G_SUPPLICANT_PAIRWISE_CCMP; + ap->group_cipher = G_SUPPLICANT_GROUP_CCMP; + ap->passphrase = passphrase; + } + + return ap; +} + static GSupplicantSSID *ssid_ap_init(const struct connman_technology *technology) { GSupplicantSSID *ap; @@ -3657,6 +3872,34 @@ static int tech_set_regdom(struct connman_technology *technology, const char *al return g_supplicant_set_country(alpha2, regdom_callback, NULL); } +/** + * Adds a network interface to the ConnMan technology. + * + * @param technology Pointer to the ConnMan technology structure. + * @param index Index of the interface. + * @param name Name of the network interface. + * @param ident Identifier string for the interface. + * + * This function checks if the interface name has the P2P group prefix + * ("p2p-") and, if so, stores the interface name and index in the + * corresponding global variables for later use. + */ + +static void tech_add_interface(struct connman_technology *technology, + int index, const char *name, const char *ident) +{ + DBG("index %d name %s ident %s", index, name, ident); + + if(p2p_group_if_prefix && g_str_has_prefix(name, p2p_group_if_prefix)) { + if (p2p_group_ifname) { + g_free(p2p_group_ifname); + p2p_group_ifname = NULL; + } + p2p_group_ifname = g_strdup(name); + p2p_group_ifindex = index; + } +} + static struct connman_technology_driver tech_driver = { .name = "wifi", .type = CONNMAN_SERVICE_TYPE_WIFI, @@ -3665,12 +3908,13 @@ static struct connman_technology_driver tech_driver = { .set_tethering = tech_set_tethering, .set_regdom = tech_set_regdom, .set_p2p_listen = tech_set_p2p_listen, + .add_interface = tech_add_interface, }; static int wifi_init(void) { int err; - + connection = connman_dbus_get_connection(); err = connman_network_driver_register(&network_driver); if (err < 0) return err; diff --git a/src/connman.h b/src/connman.h index f2b1228..034a990 100644 --- a/src/connman.h +++ b/src/connman.h @@ -596,8 +596,27 @@ void __connman_technology_remove_interface(enum connman_service_type type, int index, const char *ident); void __connman_technology_notify_regdom_by_device(struct connman_device *device, int result, const char *alpha2); +/** + * Sets up a P2P Group Owner (GO) for the ConnMan technology. + * + * @param msg The D-Bus message requesting the P2P GO setup. + * @param ident Identifier (SSID suffix) to be used for the P2P group. + * @param passphrase Passphrase for the P2P group. + * + * @return 0 on success or a negative error code on failure. + * + * This function configures and starts a P2P group owner with the given + * identifier and passphrase using the ConnMan and supplicant interfaces. + */ +int __connman_technology_set_p2p_go(DBusMessage *msg, const char *ident, + const char *passphrase); const char *__connman_technology_get_regdom(enum connman_service_type type); +int __connman_tethering_set_enabled(void); +void __connman_tethering_set_disabled(void); + + + #include <connman/device.h> int __connman_device_init(const char *device, const char *nodevice); @@ -1052,6 +1071,17 @@ typedef void (*ippool_collision_cb_t) (struct connman_ippool *pool, int __connman_ippool_init(void); void __connman_ippool_cleanup(void); + +#define __connman_ippool_ref(ipconfig) \ + __connman_ippool_ref_debug(ipconfig, __FILE__, __LINE__, __func__) +#define __connman_ippool_unref(ipconfig) \ + __connman_ippool_unref_debug(ipconfig, __FILE__, __LINE__, __func__) + +struct connman_ippool *__connman_ippool_ref_debug(struct connman_ippool *pool, + const char *file, int line, const char *caller); +void __connman_ippool_unref_debug(struct connman_ippool *pool, + const char *file, int line, const char *caller); + void __connman_ippool_free(struct connman_ippool *pool); struct connman_ippool *__connman_ippool_create(int index, @@ -1066,6 +1096,8 @@ const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool); const char *__connman_ippool_get_start_ip(struct connman_ippool *pool); const char *__connman_ippool_get_end_ip(struct connman_ippool *pool); +char *__connman_util_insert_colon_to_mac_addr(const char *mac_addr); + void __connman_ippool_newaddr(int index, const char *address, unsigned char prefixlen); void __connman_ippool_deladdr(int index, const char *address, @@ -1146,3 +1178,4 @@ int __connman_util_get_random(uint64_t *val); unsigned int __connman_util_random_delay_ms(unsigned int secs); int __connman_util_init(void); void __connman_util_cleanup(void); +void __connman_p2p_set_dhcp_pool(struct connman_ippool *ippool); diff --git a/src/group.c b/src/group.c new file mode 100644 index 0000000..8715d65 --- /dev/null +++ b/src/group.c @@ -0,0 +1,993 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2025 LG Electronics, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <gdbus.h> +#include <ctype.h> + +#include <connman/storage.h> +#include <connman/setting.h> +#include <connman/agent.h> +#include "include/group.h" +#include <gsupplicant/gsupplicant.h> + +#include "connman.h" + +/* + * Global static variables used for P2P group management: + * + * connection - The active D-Bus connection used to communicate with external services. + * group_list - List of currently tracked P2P groups. + * group_hash - Hash table for quick lookup of groups by their object path or identifier. + */ +static DBusConnection *connection = NULL; +static GList *group_list = NULL; +static GHashTable *group_hash = NULL; + + +void __connman_p2p_go_tethering_set_enabled(void); +void __connman_p2p_go_tethering_set_disabled(void); +int __connman_p2p_go_set_enabled(void); +int __connman_p2p_go_set_disabled(void); + +/* + * Structure used to pass peer-related callback data: + * + * iter - Pointer to the D-Bus message iterator used for constructing responses. + * group - Pointer to the associated connman_group structure representing the current P2P group. + */ +struct peer_cb_data { + DBusMessageIter *iter; + struct connman_group *group; +}; + +/* + * Retrieves an existing connman_group by identifier or creates a new one if not found. + * + * identifier - The unique identifier for the P2P group. + * + * Returns a pointer to the connman_group structure associated with the given identifier, + * initializing internal group tracking structures (hash tables and list) if needed. + */ + + +static struct connman_group *group_get(const char *identifier) +{ + struct connman_group *group; + + group = g_hash_table_lookup(group_hash, identifier); + if (group) { + return group; + } + + group = g_try_new0(struct connman_group, 1); + if (!group) + return NULL; + + DBG("group %p", group); + + group->identifier = g_strdup(identifier); + group->peer_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + group->peer_intf = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + + group_list = g_list_prepend(group_list, group); + + g_hash_table_insert(group_hash, group->identifier, group); + + return group; +} + +static int set_tethering(struct connman_group *group, + bool enabled) +{ + group->tethering = enabled; + dbus_bool_t val = enabled; + + connman_dbus_property_changed_basic(group->path, + CONNMAN_GROUP_INTERFACE, "Tethering", + DBUS_TYPE_BOOLEAN, + &val); + + if (enabled == TRUE) { + __connman_p2p_go_set_enabled(); + __connman_p2p_go_tethering_set_enabled(); + } else { + __connman_p2p_go_tethering_set_disabled(); + __connman_p2p_go_set_disabled(); + + } + return 0; +} + +/** + * Handles a D-Bus SetProperty method call for a connman_group object. + * + * @param conn The D-Bus connection. + * @param msg The incoming D-Bus message containing the property name and value. + * @param user_data Pointer to the associated connman_group structure. + * + * @return A D-Bus message representing the reply or error. + * + * This method currently supports setting the "Tethering" boolean property. + * It validates the argument, applies the change, and sends a reply or error + * based on the operation outcome. + */ + + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_group *group = user_data; + DBusMessageIter iter, value; + const char *name; + int type; + + DBG("group %p", group); + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __connman_error_invalid_arguments(msg); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &name); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_recurse(&iter, &value); + + type = dbus_message_iter_get_arg_type(&value); + + if (g_str_equal(name, "Tethering") == TRUE) { + int err; + bool tethering; + + if (type != DBUS_TYPE_BOOLEAN) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&value, &tethering); + + if (group->tethering == tethering) { + if (tethering == FALSE) + return __connman_error_already_disabled(msg); + else + return __connman_error_already_enabled(msg); + } + + err = set_tethering(group, tethering); + if (err < 0) + return __connman_error_failed(msg, -err); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +/* + * Appends relevant group properties to the D-Bus message dictionary. + * + * @param dict - D-Bus dictionary iterator to append properties into. + * @param group - Pointer to the connman_group structure whose properties are appended. + * + * Appends the following properties if available: + * - "Name": group's SSID name (if present) + * - "Owner": boolean indicating if this device is the group owner + * - "Passphrase": group's passphrase (only if this device is GO) + * - "OwnerPath": D-Bus object path of the group owner (only if not GO) + * - "Persistent": boolean indicating persistent group status + * - "Tethering": boolean indicating tethering state + */ + +static void append_properties(DBusMessageIter *dict, struct connman_group *group) +{ + dbus_bool_t val; + + if(group->name) + connman_dbus_dict_append_basic(dict, "Name", DBUS_TYPE_STRING, &group->name); + + val = group->is_group_owner; + connman_dbus_dict_append_basic(dict, "Owner", DBUS_TYPE_BOOLEAN, &val); + + if(group->is_group_owner) + connman_dbus_dict_append_basic(dict, "Passphrase", DBUS_TYPE_STRING, &group->passphrase); + else + connman_dbus_dict_append_basic(dict, "OwnerPath", DBUS_TYPE_OBJECT_PATH, &group->group_owner); + + val = group->is_persistent; + connman_dbus_dict_append_basic(dict, "Persistent", DBUS_TYPE_BOOLEAN, &val); + + val = group->tethering; + connman_dbus_dict_append_basic(dict, "Tethering", DBUS_TYPE_BOOLEAN, &val); +} + +/* + * Handles the D-Bus GetProperties method call for a connman_group object. + * + * @param conn - D-Bus connection (unused in this function). + * @param msg - Incoming D-Bus message requesting properties. + * @param user_data - Pointer to the connman_group whose properties are requested. + * + * @return A D-Bus reply message containing a dictionary of the group's properties, + * such as Name, Owner, Passphrase, OwnerPath, Persistent, and Tethering. + * + * The function builds a D-Bus reply message, appends all relevant properties of + * the group using append_properties(), and returns the message for sending back + * to the client. + */ + + static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_group *group = user_data; + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("group %p", group); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + connman_dbus_dict_open(&array, &dict); + append_properties(&dict, group); + connman_dbus_dict_close(&array, &dict); + + return reply; +} + +/** + * Sends a D-Bus signal to notify that a group has been added. + * + * @param group Pointer to the connman_group structure representing the added group. + * + * This function constructs a D-Bus signal message with the "GroupAdded" signal name, + * appends the group's object path and properties, and sends the signal over the active + * D-Bus connection. The signal includes details such as the group's name, owner status, + * passphrase, and tethering state. + */ +static void group_added_signal(struct connman_group *group) +{ + DBusMessage *signal; + DBusMessageIter iter; + DBusMessageIter dict; + + signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "GroupAdded"); + if (!signal) + return; + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &group->path); + + connman_dbus_dict_open(&iter, &dict); + append_properties(&dict, group); + connman_dbus_dict_close(&iter, &dict); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); +} + +/** + * Sends a D-Bus signal to notify that a group has been removed. + * + * @param group Pointer to the connman_group structure representing the removed group. + * + * This function emits a D-Bus signal with the "GroupRemoved" signal name, appending + * the group's object path to indicate which group has been removed. The signal is sent + * over the active D-Bus connection to notify clients about the removal of the group. + */ +static void group_removed_signal(struct connman_group *group) +{ + g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "GroupRemoved", + DBUS_TYPE_OBJECT_PATH, &group->path, + DBUS_TYPE_INVALID); +} + +/** + * Callback function for handling P2P disconnection events. + * + * @param result Integer indicating the result of the disconnection operation. + * @param interface Pointer to the GSupplicantInterface representing the P2P interface. + * @param user_data Pointer to the connman_group structure associated with the disconnection. + * + * This function is invoked when a P2P disconnection operation completes. It logs the + * group information for debugging purposes and can be extended to handle additional + * cleanup or notification logic. + */ +static void p2p_disconnect_callback(int result, + GSupplicantInterface *interface, + void *user_data) +{ + struct connman_group *group = user_data; + + DBG("group %p\n", group); +} + +/** + * Handles a D-Bus method call to disconnect a P2P group. + * + * @param conn Pointer to the D-Bus connection. + * @param msg Pointer to the incoming D-Bus message requesting the disconnection. + * @param user_data Pointer to the connman_group structure representing the P2P group. + * + * @return A D-Bus reply message indicating the success or failure of the operation. + * + * This function initiates the disconnection of a P2P group using the supplicant interface. + * If the operation fails, it returns an appropriate error message. If the disconnection + * is in progress, it waits for the callback (`p2p_disconnect_callback`) to handle the result. + */ +static DBusMessage *p2p_disconnect(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_group *group = user_data; + int err = 0; + + DBG("group %p", group); + + // Initiate the P2P group disconnection using the supplicant interface. + err = g_supplicant_interface_p2p_group_disconnect(group->interface, + p2p_disconnect_callback, + group); + + if (err < 0) { + // If the error is not EINPROGRESS, return a failure message. + if (err != -EINPROGRESS) + return __connman_error_failed(msg, -err); + } + + // Return a generic D-Bus reply indicating the operation was initiated. + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +/** + * Handles a D-Bus method call to invite a peer to a P2P group. + * + * @param conn Pointer to the D-Bus connection. + * @param msg Pointer to the incoming D-Bus message requesting the invitation. + * @param user_data Pointer to the connman_group structure representing the P2P group. + * + * @return A D-Bus reply message indicating the success or failure of the operation. + * + * This function is a placeholder for inviting a peer to a P2P group. It currently + * returns a generic D-Bus reply indicating that the operation was initiated. + * The actual implementation needs to be added. + */ +static DBusMessage *p2p_invite(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + // TODO: Implement the logic for inviting a peer to a P2P group. + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +/** + * Appends peer information to a D-Bus message structure. + * + * @param value Pointer to the peer identifier (value from the hash table). + * @param user_data Pointer to the peer_cb_data structure containing the D-Bus iterator + * and the associated connman_group. + * + * This function retrieves peer-related information from the connman_group structure + * and appends it to the D-Bus message as a structured entry. It includes the peer's + * object path and an empty dictionary for additional properties. + */ +static void append_peer_struct(gpointer value, gpointer user_data) +{ + struct peer_cb_data *cbd = user_data; + const char *peer_ident = value; + const char *service_path; + DBusMessageIter entry, dict; + + DBG("peer_ident %s", peer_ident); + + // Open a new container in the D-Bus message for the peer structure. + dbus_message_iter_open_container(cbd->iter, DBUS_TYPE_STRUCT, NULL, &entry); + + // Retrieve the service path associated with the peer identifier. + service_path = g_hash_table_lookup(cbd->group->peer_hash, peer_ident); + + // Append the service path to the D-Bus message. + dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &service_path); + + // Open and close an empty dictionary for additional peer properties. + connman_dbus_dict_open(&entry, &dict); + connman_dbus_dict_close(&entry, &dict); + + // Close the container for the peer structure. + dbus_message_iter_close_container(cbd->iter, &entry); +} + +/** + * Appends all peer structures to a D-Bus message iterator. + * + * @param iter Pointer to the D-Bus message iterator where peer structures will be appended. + * @param user_data Pointer to the connman_group structure containing the list of peers. + * + * This function iterates through the peer list in the connman_group structure and appends + * each peer's information to the D-Bus message using the `append_peer_struct` function. + * It uses a helper structure (`peer_cb_data`) to pass the iterator and group information + * to the callback function. + */ +static void append_peer_structs(DBusMessageIter *iter, void *user_data) +{ + struct connman_group *group = user_data; + struct peer_cb_data cbd; + + cbd.iter = iter; + cbd.group = group; + + DBG("iter %p group %p", iter, group); + + // Iterate through the peer list and append each peer's structure. + g_slist_foreach(group->peer_list, append_peer_struct, &cbd); +} + +/** + * Appends group owner (GO) information to a D-Bus message structure. + * + * @param iter Pointer to the D-Bus message iterator where the GO information will be appended. + * @param user_data Pointer to the connman_group structure representing the P2P group. + * + * This function appends the group owner's object path and an empty dictionary for additional + * properties to the D-Bus message. If the group or group owner is NULL, the function returns + * without performing any operations. + */ +static void append_peer_go(DBusMessageIter *iter, void *user_data) +{ + struct connman_group *group = user_data; + const char *peer_ident, *peer_dev_addr = NULL; + DBusMessageIter entry, dict; + struct connman_peer *connman_peer; + + // Return early if the group or group owner is NULL. + if (group == NULL || g_list_find(group_list, group) == NULL || group->group_owner == NULL) + return; + + // Open a new container in the D-Bus message for the group owner structure. + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); + + // Append the group owner's object path to the D-Bus message. + dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &group->group_owner); + + connman_peer = connman_peer_get_by_path(group->group_owner); + if(connman_peer) { + peer_ident = connman_peer_get_identifier(connman_peer); + __connman_peer_get_properties_struct(&entry, connman_peer); + } else { + connman_dbus_dict_open(&entry, &dict); + peer_ident = strrchr(group->group_owner, '_') + 1; + peer_dev_addr = __connman_util_insert_colon_to_mac_addr(peer_ident); + connman_dbus_dict_append_basic(&dict, "DeviceAddress", DBUS_TYPE_STRING, &peer_dev_addr); + connman_dbus_dict_close(&entry, &dict); + } + + // Open and close an empty dictionary for additional GO properties. + + + // Close the container for the group owner structure. + dbus_message_iter_close_container(iter, &entry); + g_free(peer_dev_addr); +} + +/** + * Handles a D-Bus method call to retrieve peer information for a P2P group. + * + * @param conn Pointer to the D-Bus connection. + * @param msg Pointer to the incoming D-Bus message requesting peer information. + * @param user_data Pointer to the connman_group structure representing the P2P group. + * + * @return A D-Bus reply message containing peer information. + * + * This function constructs a D-Bus reply message based on the group's ownership status. + * If the group is owned by this device, it appends all peer structures using the + * `append_peer_structs` function. Otherwise, it appends the group owner information + * using the `append_peer_go` function. + */ +static DBusMessage *get_peers(DBusConnection *conn, DBusMessage *msg, void *user_data) +{ + struct connman_group *group = user_data; + DBusMessage *reply; + + // Create a reply message for the incoming D-Bus method call. + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + // Append peer information based on the group's ownership status. + if (group->is_group_owner) + __connman_dbus_append_objpath_dict_array(reply, append_peer_structs, group); + else + __connman_dbus_append_objpath_dict_array(reply, append_peer_go, group); + + return reply; +} + +/** + * Defines the D-Bus methods for the connman_group interface. + * + * This table specifies the methods supported by the connman_group interface, including: + * - `GetProperties`: Retrieves the group's properties. + * - `SetProperty`: Sets a specific property of the group. + * - `Disconnect`: Disconnects the P2P group. + * - `Invite`: Invites a peer to the P2P group. + * - `GetPeers`: Retrieves information about peers in the P2P group. + * + * Each entry includes the method name, input/output arguments, and the corresponding + * handler function. + */ +static const GDBusMethodTable group_methods[] = { + { GDBUS_DEPRECATED_METHOD("GetProperties", + NULL, GDBUS_ARGS({ "properties", "a{sv}" }), + get_properties) }, + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({ "name", "s" }, { "value", "v" }), + NULL, set_property) }, + { GDBUS_METHOD("Disconnect", + NULL, NULL, p2p_disconnect) }, + { GDBUS_METHOD("Invite", + GDBUS_ARGS({ "service_path", "s" }), + NULL, p2p_invite) }, + { GDBUS_METHOD("GetPeers", + NULL, GDBUS_ARGS({ "peers", "a(oa{sv})" }), + get_peers) }, + {}, +}; + +/** + * Defines the D-Bus signals for the connman_group interface. + * + * This table specifies the signals emitted by the connman_group interface, including: + * - `PropertyChanged`: Emitted when a property of the group changes. + * - `PeerAdded`: Emitted when a new peer is added to the group. + * - `PeerRemoved`: Emitted when a peer is removed from the group. + * + * Each entry includes the signal name and its arguments. + */ +static const GDBusSignalTable group_signals[] = { + { GDBUS_SIGNAL("PropertyChanged", + GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, + { GDBUS_SIGNAL("PeerAdded", + GDBUS_ARGS({ "path", "o" })) }, + { GDBUS_SIGNAL("PeerRemoved", + GDBUS_ARGS({ "path", "o" })) }, + { }, +}; + +/** + * Retrieves the D-Bus object path of a connman_group. + * + * @param group Pointer to the connman_group structure. + * + * @return The D-Bus object path of the group if the group is valid, otherwise NULL. + * + * This function checks if the provided connman_group pointer is valid and returns + * its associated D-Bus object path. If the group is NULL, it returns NULL. + */ +const char* __connman_group_get_path(struct connman_group *group) +{ + if (group) + return group->path; + + return NULL; +} + +/** + * Retrieves the identifier of a connman_group. + * + * @param group Pointer to the connman_group structure. + * + * @return The identifier of the group. + * + * This function returns the unique identifier associated with the connman_group structure. + * It assumes that the group pointer is valid and does not perform null checks. + */ +const char* __connman_group_get_identifier(struct connman_group *group) +{ + return group->identifier; +} + +/** + * Checks if any connman_group exists. + * + * @return `true` if the group list is not empty, otherwise `false`. + * + * This function verifies whether there are any groups in the global `group_list`. + * It returns `false` if the list is NULL or contains no data, indicating no groups exist. + */ +bool __connman_group_exist(void) +{ + if (!group_list || !group_list->data) + return false; + + return true; +} + +/** + * Looks up a connman_group by its identifier. + * + * @param identifier Pointer to the unique identifier of the group. + * + * @return A pointer to the connman_group structure if found, or creates a new group + * if it does not exist. + * + * This function uses the `group_get` function to retrieve an existing connman_group + * by its identifier or create a new one if no matching group is found. + */ +struct connman_group *__connman_group_lookup_from_ident(const char *identifier) +{ + return group_get(identifier); +} + +/** + * Appends group properties to a D-Bus dictionary iterator. + * + * @param dict Pointer to the D-Bus dictionary iterator where properties will be appended. + * @param user_data Pointer to the connman_group structure containing the properties. + * + * This function uses the `append_properties` function to add relevant properties + * of the connman_group to the provided D-Bus dictionary iterator. + */ +static void append_dict_properties(DBusMessageIter *dict, void *user_data) +{ + struct connman_group *group = user_data; + + append_properties(dict, group); +} + +/** + * Appends a connman_group structure to a D-Bus message iterator. + * + * @param iter Pointer to the D-Bus message iterator where the group structure will be appended. + * @param function Callback function to append additional properties to the dictionary. + * @param group Pointer to the connman_group structure representing the group. + * + * This function appends the group's object path and a dictionary of properties to the + * provided D-Bus message iterator. If a callback function is provided, it is used to + * append additional properties to the dictionary. + */ +static void append_struct_group(DBusMessageIter *iter, + connman_dbus_append_cb_t function, + struct connman_group *group) + { + DBusMessageIter entry, dict; + + // Open a new container in the D-Bus message for the group structure. + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); + + // Append the group's object path to the D-Bus message. + dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &group->path); + + // Open the dictionary for appending group properties. + connman_dbus_dict_open(&entry, &dict); + + // If a callback function is provided, use it to append additional properties. + if (function) + function(&dict, group); + + // Close the dictionary after appending properties. + connman_dbus_dict_close(&entry, &dict); + + // Close the container for the group structure. + dbus_message_iter_close_container(iter, &entry); +} + +/** + * Appends a connman_group structure to a D-Bus message iterator. + * + * @param value Pointer to the connman_group structure to be appended. + * @param user_data Pointer to the D-Bus message iterator where the group structure will be appended. + * + * This function checks if the group's object path is valid before appending the group + * structure to the D-Bus message. It uses the `append_struct_group` function to append + * the group's object path and properties. + */ +static void append_struct(gpointer value, gpointer user_data) +{ + struct connman_group *group = value; + DBusMessageIter *iter = user_data; + + // Skip if the group's object path is invalid. + if (!group->path) + return; + + // Append the group structure to the D-Bus message. + append_struct_group(iter, append_dict_properties, group); +} + +/** + * Appends all connman_group structures to a D-Bus message iterator. + * + * @param iter Pointer to the D-Bus message iterator where the group structures will be appended. + * + * This function iterates through the global `group_list` and appends each group's + * structure to the provided D-Bus message iterator using the `append_struct` function. + */ +void __connman_group_list_struct(DBusMessageIter *iter) +{ + g_list_foreach(group_list, append_struct, iter); +} + +/** + * Registers a connman_group with the D-Bus system. + * + * @param group Pointer to the connman_group structure to be registered. + * + * @return 0 on success, or a negative error code if the group is already registered. + * + * This function assigns a unique D-Bus object path to the group, registers the group + * interface with the D-Bus system, and emits a "GroupAdded" signal to notify clients + * about the new group. If the group is already registered (i.e., has a valid path), + * it returns `-EALREADY`. + */ +static int group_register(struct connman_group *group) +{ + DBG("group %p", group); + + // Check if the group is already registered. + if (group->path != NULL) + return -EALREADY; + + // Assign a unique D-Bus object path to the group. + group->path = g_strdup_printf("%s/group/%s", CONNMAN_PATH, group->identifier); + + DBG("path %s", group->path); + + // Register the group interface with the D-Bus system. + g_dbus_register_interface(connection, group->path, + CONNMAN_GROUP_INTERFACE, + group_methods, group_signals, + NULL, group, NULL); + + if (!group->autonomous) + __connman_p2p_set_dhcp_pool(NULL); + + // Emit a "GroupAdded" signal to notify clients about the new group. + group_added_signal(group); + + return 0; +} + +/** + * Callback function for handling the creation of a P2P interface. + * + * @param result Integer indicating the result of the interface creation operation. + * @param interface Pointer to the GSupplicantInterface representing the created interface. + * @param user_data Pointer to the connman_group structure associated with the interface. + * + * This function is invoked when a P2P interface creation operation completes. It logs + * the result and interface name for debugging purposes and assigns the created interface + * to the connman_group structure. + */ +static void interface_create_callback(int result, + GSupplicantInterface *interface, + void *user_data) +{ + struct connman_group *group = user_data; + + DBG("result %d ifname %s", result, + g_supplicant_interface_get_ifname(interface)); + + // Assign the created interface to the group. + group->interface = interface; +} + +/** + * Creates and initializes a connman_group structure. + * + * @param iface Pointer to the GSupplicantInterface representing the P2P interface. + * @param ifname Name of the network interface. + * @param ssid SSID of the group. + * @param passphrase Passphrase for the group (used if the device is the group owner). + * @param go Boolean indicating if the device is the group owner. + * @param persistent Boolean indicating if the group is persistent. + * @param go_path D-Bus object path of the group owner (used if the device is not the group owner). + * @param autonomous Boolean indicating if the group is autonomous. + * @param freq Frequency of the group. + * + * @return Pointer to the created connman_group structure, or NULL if creation fails. + * + * This function initializes a connman_group structure with the provided parameters, + * registers the group with the D-Bus system, and initiates the creation of the P2P interface. + * It generates a unique identifier for the group based on the SSID and uses it to retrieve + * or create the group. + */ +struct connman_group* __connman_group_create(GSupplicantInterface *iface, const char *ifname, const char *ssid, const char *passphrase, + bool go, bool persistent, const char *go_path, bool autonomous, int freq) +{ + struct connman_group *group; + char *ident; + int ssid_len = strlen(ssid); + GString *name = g_string_sized_new(ssid_len * 2 + 1); + int i = 0; + int ret = 0; + + DBG("ssid : %s, len : %d\n", ssid, ssid_len); + + // Generate a unique identifier for the group based on the SSID. + for (i = 0; i < ssid_len; i++) { + g_string_append_printf(name, "%02x", ssid[i]); + } + + ident = g_string_free(name, FALSE); + + DBG("ident : %s\n", ident); + + // Retrieve or create the group using the identifier. + group = group_get(ident); + + if (!group) + return NULL; + + // Initialize the group properties. + group->name = g_strdup(ssid); + group->is_group_owner = go; + if (go) { + group->passphrase = g_strdup(passphrase); + } + group->is_persistent = persistent; + group->group_owner = go_path; + group->autonomous = autonomous; + group->freq = freq; + group->is_static_ip = false; + group->orig_interface = iface; + + DBG("go path : %s\n", go_path); + + // Register the group with the D-Bus system. + ret = group_register(group); + + // If registration succeeds, initiate the creation of the P2P interface. + if (ret == 0) { + g_supplicant_interface_create(ifname, "nl80211", NULL, interface_create_callback, group); + } + + return group; +} + +/** + * Removes a connman_group associated with a given P2P interface. + * + * @param interface Pointer to the GSupplicantInterface representing the P2P interface. + * + * This function searches for a connman_group associated with the provided interface + * in the global `group_list`. If found, it unregisters the group from the D-Bus system, + * emits a "GroupRemoved" signal, and cleans up all resources associated with the group. + */ +void __connman_group_remove(GSupplicantInterface *interface) +{ + GList *list; + struct connman_group *group = NULL; + + // Return early if there are no groups in the list. + if (!group_list) + return; + + // Iterate through the group list to find the group associated with the interface. + for (list = group_list; list != NULL; list = list->next) { + group = list->data; + + if (group->interface == interface) { + break; + } + } + + // If the group is found, proceed with removal. + if (group) { + DBG("group removed\n"); + + // Ensure the D-Bus connection is initialized. + if (!connection) + connection = connman_dbus_get_connection(); + + DBG("group path : %s\n", group->path); + + // Emit a "GroupRemoved" signal to notify clients. + group_removed_signal(group); + + // Unregister the group interface from the D-Bus system. + g_dbus_unregister_interface(connection, group->path, CONNMAN_GROUP_INTERFACE); + + // Remove the group from the hash table and list. + g_hash_table_remove(group_hash, group->identifier); + group_list = g_list_remove(group_list, group); + + // Destroy the group's hash tables and free allocated memory. + g_hash_table_destroy(group->peer_hash); + g_hash_table_destroy(group->peer_intf); + + g_free(group->path); + g_free(group); + } +} + +/** + * Initializes the connman_group system. + * + * This function sets up the global D-Bus connection and initializes the hash table + * used for managing connman_group objects. It must be called before any group-related + * operations are performed. + */ +void __connman_group_init(void) +{ + // Establish the global D-Bus connection. + connection = connman_dbus_get_connection(); + + // Initialize the hash table for managing connman_group objects. + group_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); +} + +/** + * Cleans up all connman_group objects and releases associated resources. + * + * This function iterates through the global `group_list`, removes each group, + * and releases its resources. It also destroys the global `group_hash` table. + * Special handling is included to prevent crashes by checking for null paths + * and ensuring proper cleanup of D-Bus interfaces and supplicant connections. + */ +void __connman_group_cleanup(void) +{ + GList *list; + + // Return early if there are no groups to clean up. + if (!group_list) + return; + + // Iterate through the group list and clean up each group. + for (list = group_list; list != NULL; list = list->next) { + struct connman_group *group = list->data; + + /* + * Checking if group->path == null is added to prevent connman crash [NCVTDEFFECT-2085]. + * For further investigation, PLAT-16133 is created to find a better solution + * other than checking null. + */ + if (group && group->path && strstr(group->path, "group") != NULL) { + // Emit a "GroupRemoved" signal to notify clients. + group_removed_signal(group); + + // Disconnect the P2P group and unregister the D-Bus interface. + if (group->interface) { + g_supplicant_interface_p2p_group_disconnect(group->interface, NULL, NULL); + g_dbus_unregister_interface(connection, group->path, CONNMAN_GROUP_INTERFACE); + } + } + } + + // Free the group list and reset the global pointer. + list = group_list; + group_list = NULL; + g_list_free(list); + + // Destroy the global hash table and reset the pointer. + g_hash_table_destroy(group_hash); + group_hash = NULL; +} \ No newline at end of file diff --git a/src/ippool.c b/src/ippool.c index f2e9b00..235d08c 100644 --- a/src/ippool.c +++ b/src/ippool.c @@ -42,6 +42,8 @@ struct address_info { }; struct connman_ippool { + unsigned int refcount; + struct address_info *info; char *gateway; @@ -81,6 +83,44 @@ void __connman_ippool_free(struct connman_ippool *pool) g_free(pool); } +struct connman_ippool * +__connman_ippool_ref_debug(struct connman_ippool *pool, + const char *file, int line, const char *caller) +{ + DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1, + file, line, caller); + + __sync_fetch_and_add(&pool->refcount, 1); + + return pool; +} + +void __connman_ippool_unref_debug(struct connman_ippool *pool, + const char *file, int line, const char *caller) +{ + if (!pool) + return; + + DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1, + file, line, caller); + + if (__sync_fetch_and_sub(&pool->refcount, 1) != 1) + return; + + if (pool->info) { + allocated_blocks = g_slist_remove(allocated_blocks, pool->info); + g_free(pool->info); + } + + g_free(pool->gateway); + g_free(pool->broadcast); + g_free(pool->start_ip); + g_free(pool->end_ip); + g_free(pool->subnet_mask); + + g_free(pool); +} + static char *get_ip(uint32_t ip) { struct in_addr addr; diff --git a/src/manager.c b/src/manager.c index 892d3a4..b1992dd 100644 --- a/src/manager.c +++ b/src/manager.c @@ -30,6 +30,7 @@ #include <connman/agent.h> #include "connman.h" +#include "include/group.h" static bool connman_state_idle; static dbus_bool_t sessionmode; @@ -521,6 +522,107 @@ error: } +/** + * Handles a D-Bus method call to create a P2P group. + * + * @param conn Pointer to the D-Bus connection. + * @param msg Pointer to the incoming D-Bus message requesting group creation. + * @param data Additional user data (unused in this function). + * + * @return NULL on success, or a D-Bus error message on failure. + * + * This function validates the input arguments from the D-Bus message, including the + * group identifier and passphrase. It ensures the identifier length and passphrase + * meet the required constraints. If valid, it invokes the P2P group creation logic + * using `__connman_technology_set_p2p_go`. On failure, it returns an appropriate + * D-Bus error message. + */ +static DBusMessage *create_group(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessageIter iter; + const char *identifier, *passphrase; + int err; + + DBG(""); + + // Validate the D-Bus message and initialize the iterator. + if (dbus_message_iter_init(msg, &iter) == FALSE) + return __connman_error_invalid_arguments(msg); + + // Validate the group identifier argument. + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &identifier); + + if (strlen(identifier) > (P2P_MAX_SSID - P2P_WILDCARD_SSID_LEN)) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_next(&iter); + + // Validate the passphrase argument. + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&iter, &passphrase); + + if ((strlen(passphrase) > 1 && strlen(passphrase) < 8) || strlen(passphrase) > 63) + return __connman_error_passphrase_required(msg); + + // Attempt to create the P2P group. + err = __connman_technology_set_p2p_go(msg, identifier, passphrase); + + if (err < 0) + return __connman_error_failed(msg, -err); + + return NULL; +} + +/** + * Appends all connman_group structures to a D-Bus message iterator. + * + * @param iter Pointer to the D-Bus message iterator where group structures will be appended. + * @param user_data Additional user data (unused in this function). + * + * This function calls `__connman_group_list_struct` to append all group structures + * from the global group list to the provided D-Bus message iterator. + */ +static void append_group_structs(DBusMessageIter *iter, void *user_data) +{ + __connman_group_list_struct(iter); +} + +/** + * Handles a D-Bus method call to retrieve all P2P groups. + * + * @param conn Pointer to the D-Bus connection. + * @param msg Pointer to the incoming D-Bus message requesting group information. + * @param data Additional user data (unused in this function). + * + * @return A D-Bus reply message containing all P2P groups, or NULL if the reply could not be created. + * + * This function constructs a D-Bus reply message and appends all P2P group structures + * using the `append_group_structs` function. The group information is retrieved from + * the global group list. + */ +static DBusMessage *get_groups(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + + // Create a reply message for the incoming D-Bus method call. + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + // Append all P2P group structures to the reply message. + __connman_dbus_append_objpath_dict_array(reply, append_group_structs, NULL); + + return reply; +} + + static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), @@ -583,6 +685,13 @@ static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("UnregisterPeerService", GDBUS_ARGS({ "specification", "a{sv}" }), NULL, unregister_peer_service) }, + { GDBUS_ASYNC_METHOD("CreateGroup", + GDBUS_ARGS({ "identifier", "s" },{ "passphrase", "s" }), + GDBUS_ARGS({ "path", "o" }), + create_group) }, + { GDBUS_METHOD("GetGroups", + NULL, GDBUS_ARGS({ "groups", "a(oa{sv})" }), + get_groups) }, { }, }; @@ -603,6 +712,11 @@ static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("TetheringClientsChanged", GDBUS_ARGS({ "registered", "as" }, { "removed", "as" })) }, + { GDBUS_SIGNAL("GroupAdded", + GDBUS_ARGS({ "path", "o" }, + { "properties", "a{sv}" })) }, + { GDBUS_SIGNAL("GroupRemoved", + GDBUS_ARGS({ "path", "o"})) }, { }, }; diff --git a/src/p2pgo.c b/src/p2pgo.c new file mode 100644 index 0000000..f58caa6 --- /dev/null +++ b/src/p2pgo.c @@ -0,0 +1,477 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. + * Copyright (C) 2011 ProFUSION embedded systems + * Copyright (C) 2013 LG Electronics, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/sockios.h> +#include <string.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if_tun.h> +#include <linux/if_bridge.h> + +#include "connman.h" + +#include <gdhcp/gdhcp.h> + +#include <gdbus.h> + +#ifndef DBUS_TYPE_UNIX_FD +#define DBUS_TYPE_UNIX_FD -1 +#endif + +/** + * Default DNS server for the bridge network. + * + * This DNS server is used for name resolution in the private network + * created for P2P connections. + */ +#define BRIDGE_DNS "8.8.8.8" + +/** + * Default Maximum Transmission Unit (MTU) size. + * + * This value defines the maximum size of packets that can be transmitted + * over the network interface. + */ +#define DEFAULT_MTU 1500 + +/** + * Primary DNS server for the private network. + * + * This is an alias for the bridge DNS server used in the private network. + */ +#define PRIVATE_NETWORK_PRIMARY_DNS BRIDGE_DNS + +/** + * Secondary DNS server for the private network. + * + * This DNS server is used as a fallback for name resolution in the private network. + */ +#define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4" + +/** + * Default IP block for P2P connections. + * + * This IP block is used to assign IP addresses in the private network + * created for P2P connections. The block corresponds to 192.168.49.x. + */ +#define P2P_DEFAULT_BLOCK 0xc0a83100 // 192.168.49.x + +/** + * Pointer to the DHCP server used for tethering. + * + * This global variable represents the DHCP server instance responsible + * for managing IP address allocation in the tethering network. + */ +static GDHCPServer *tethering_dhcp_server = NULL; + +/** + * Pointer to the IP pool used for DHCP. + * + * This global variable represents the IP pool used by the DHCP server + * to allocate IP addresses to devices in the network. + */ +static struct connman_ippool *dhcp_ippool = NULL; + +/** + * Name of the bridge interface. + * + * This global variable holds the name of the bridge interface used + * for managing the tethering network. + */ +static char *bridge_name; + +/** + * Sets the name of the bridge interface for P2P Group Owner (GO). + * + * @param bridge Pointer to the string representing the bridge interface name. + * + * This function assigns the provided bridge name to the global `bridge_name` variable. + * The name is duplicated using `g_strdup` to ensure proper memory management. + */ +void __connman_p2p_go_set_bridge(char *bridge) +{ + bridge_name = g_strdup(bridge); +} + +/** + * Retrieves the name of the bridge interface for P2P Group Owner (GO). + * + * @return Pointer to the string representing the bridge interface name. + * + * This function returns the global `bridge_name` variable, which holds + * the name of the bridge interface used for managing the tethering network. + */ +const char *__connman_p2p_go_get_bridge(void) +{ + return bridge_name; +} + +/** + * Logs debug information for the DHCP server. + * + * @param str Pointer to the string containing the debug message. + * @param data Pointer to additional data (typically the context or identifier). + * + * This function logs the provided debug message along with the associated data + * using the `connman_info` logging mechanism. + */ +static void dhcp_server_debug(const char *str, void *data) +{ + connman_info("%s: %s\n", (const char *) data, str); +} + +/** + * Logs error messages for the DHCP server. + * + * @param error Enum value representing the DHCP server error. + * + * This function maps the provided `GDHCPServerError` enum value to a corresponding + * error message and logs it using the `connman_error` logging mechanism. + */ +static void dhcp_server_error(GDHCPServerError error) +{ + switch (error) { + case G_DHCP_SERVER_ERROR_NONE: + connman_error("OK"); + break; + case G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE: + connman_error("Interface unavailable"); + break; + case G_DHCP_SERVER_ERROR_INTERFACE_IN_USE: + connman_error("Interface in use"); + break; + case G_DHCP_SERVER_ERROR_INTERFACE_DOWN: + connman_error("Interface down"); + break; + case G_DHCP_SERVER_ERROR_NOMEM: + connman_error("No memory"); + break; + case G_DHCP_SERVER_ERROR_INVALID_INDEX: + connman_error("Invalid index"); + break; + case G_DHCP_SERVER_ERROR_INVALID_OPTION: + connman_error("Invalid option"); + break; + case G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID: + connman_error("Invalid address"); + break; + } +} + +/** + * Starts a DHCP server for the specified bridge interface. + * + * @param bridge Name of the bridge interface. + * @param router IP address of the router. + * @param subnet Subnet mask for the network. + * @param start_ip Starting IP address for the DHCP range. + * @param end_ip Ending IP address for the DHCP range. + * @param lease_time Lease time for DHCP clients. + * @param dns DNS server address. + * + * @return Pointer to the created `GDHCPServer` instance, or NULL on failure. + * + * This function initializes and starts a DHCP server for the specified bridge interface. + * It configures the server with the provided network settings, including IP range, subnet, + * router, DNS server, and lease time. Debugging and error handling are also set up. + */ +static GDHCPServer *dhcp_server_start(const char *bridge, + const char *router, const char *subnet, + const char *start_ip, const char *end_ip, + unsigned int lease_time, const char *dns) +{ + GDHCPServerError error; + GDHCPServer *dhcp_server; + int index; + + DBG(""); + + // Get the interface index for the bridge. + index = connman_inet_ifindex(bridge); + if (index < 0) + return NULL; + + // Create a new DHCP server instance. + dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &error); + if (!dhcp_server) { + dhcp_server_error(error); + return NULL; + } + + // Set debugging information for the DHCP server. + g_dhcp_server_set_debug(dhcp_server, dhcp_server_debug, "DHCP server"); + + // Configure the DHCP server with the provided settings. + g_dhcp_server_set_lease_time(dhcp_server, lease_time); + g_dhcp_server_set_option(dhcp_server, G_DHCP_SUBNET, subnet); + g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER, router); + g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, dns); + g_dhcp_server_set_ip_range(dhcp_server, start_ip, end_ip); + + // Start the DHCP server. + g_dhcp_server_start(dhcp_server); + + return dhcp_server; +} + +/** + * Stops and releases the DHCP server instance. + * + * @param server Pointer to the `GDHCPServer` instance to be stopped. + * + * This function checks if the provided DHCP server instance is valid. + * If valid, it releases the reference to the server using `g_dhcp_server_unref`. + */ +static void dhcp_server_stop(GDHCPServer *server) +{ + if (!server) + return; + + // Release the reference to the DHCP server. + g_dhcp_server_unref(server); +} + +/** + * Enables the P2P Group Owner (GO) functionality. + * + * @return 0 on success, or a negative error code on failure. + * + * This function sets up the P2P Group Owner (GO) by creating an IP pool, + * enabling the bridge interface, configuring DNS proxy, and starting a DHCP server. + * It handles errors at each step and ensures proper cleanup if any operation fails. + */ +int __connman_p2p_go_set_enabled(void) +{ + int index; + int err; + const char *gateway; + const char *broadcast; + const char *subnet_mask; + const char *start_ip; + const char *end_ip; + const char *dns; + + // Get the interface index for the bridge. + index = connman_inet_ifindex(bridge_name); + connman_info("p2pgo.c : index = %d", index); + + // Create an IP pool for the bridge interface. + dhcp_ippool = __connman_ippool_create(index, 2, 252, NULL, NULL); + if (!dhcp_ippool) { + connman_error("Fail to create IP pool"); + __connman_bridge_remove(bridge_name); + } + + // Retrieve IP configuration details from the IP pool. + gateway = __connman_ippool_get_gateway(dhcp_ippool); + broadcast = __connman_ippool_get_broadcast(dhcp_ippool); + subnet_mask = __connman_ippool_get_subnet_mask(dhcp_ippool); + start_ip = __connman_ippool_get_start_ip(dhcp_ippool); + end_ip = __connman_ippool_get_end_ip(dhcp_ippool); + + // Enable the bridge interface with the retrieved configuration. + err = __connman_bridge_enable(bridge_name, gateway, + connman_ipaddress_calc_netmask_len(subnet_mask), broadcast); + if (err < 0 && err != -EALREADY) { + __connman_ippool_unref(dhcp_ippool); + __connman_bridge_remove(bridge_name); + } + + // Set up DNS proxy for the bridge interface. + dns = gateway; + if (__connman_dnsproxy_add_listener(index) < 0) { + connman_error("Can't add listener %s to DNS proxy", bridge_name); + dns = BRIDGE_DNS; + } + + // Start the DHCP server for the bridge interface. + tethering_dhcp_server = dhcp_server_start(bridge_name, + gateway, subnet_mask, + start_ip, end_ip, + 24 * 3600, dns); + if (tethering_dhcp_server == NULL) { + __connman_bridge_disable(bridge_name); + __connman_ippool_unref(dhcp_ippool); + __connman_bridge_remove(bridge_name); + } + + DBG("p2p go dhcp started"); + return 0; +} + +/** + * Disables the P2P Group Owner (GO) functionality. + * + * This function stops the DHCP server, removes DNS proxy listeners, disables NAT, + * and cleans up the bridge interface and IP pool associated with the P2P Group Owner. + * It ensures proper cleanup of resources to disable the P2P GO functionality. + */ +void __connman_p2p_go_set_disabled(void) +{ + int index; + + // Get the interface index for the bridge. + index = connman_inet_ifindex(bridge_name); + if (index < 0) + return; + + // Remove DNS proxy listener for the bridge interface. + __connman_dnsproxy_remove_listener(index); + + // Disable NAT for the bridge interface. + __connman_nat_disable(bridge_name); + + // Stop the DHCP server and release its resources. + dhcp_server_stop(tethering_dhcp_server); + tethering_dhcp_server = NULL; + + // Disable the bridge interface. + __connman_bridge_disable(bridge_name); + + // Release the IP pool associated with the bridge. + __connman_ippool_unref(dhcp_ippool); + + // Remove the bridge interface. + __connman_bridge_remove(bridge_name); + + DBG("p2p go stopped"); +} + +/** + * Enables NAT (Network Address Translation) for P2P Group Owner (GO) tethering. + * + * This function retrieves the subnet mask and starting IP address from the IP pool, + * calculates the prefix length, and enables NAT for the bridge interface to allow + * tethering functionality. + */ +void __connman_p2p_go_tethering_set_enabled(void) +{ + unsigned char prefixlen; + const char *subnet_mask; + const char *start_ip; + + // Retrieve the subnet mask and starting IP address from the IP pool. + subnet_mask = __connman_ippool_get_subnet_mask(dhcp_ippool); + start_ip = __connman_ippool_get_start_ip(dhcp_ippool); + + // Calculate the prefix length from the subnet mask. + prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask); + + // Enable NAT for the bridge interface using the starting IP and prefix length. + __connman_nat_enable(bridge_name, start_ip, prefixlen); +} + +/** + * Disables NAT (Network Address Translation) for P2P Group Owner (GO) tethering. + * + * This function disables NAT for the bridge interface associated with the P2P Group Owner, + * ensuring that tethering functionality is properly stopped. + */ +void __connman_p2p_go_tethering_set_disabled(void) +{ + __connman_nat_disable(bridge_name); +} + +/** + * Retrieves the local IP address of the P2P Group Owner (GO). + * + * @return Pointer to the string representing the local IP address, or NULL if unavailable. + * + * This function checks if the DHCP IP pool is initialized and retrieves the gateway + * IP address from the pool, which represents the local IP address of the P2P Group Owner. + */ +const char* __connman_p2p_group_get_local_ip(void) +{ + if (dhcp_ippool) { + const char *gateway = __connman_ippool_get_gateway(dhcp_ippool); + return gateway; + } + + return NULL; +} + +/** + * Appends the local gateway address to a D-Bus message iterator. + * + * @param iter Pointer to the D-Bus message iterator where the gateway address will be appended. + * + * This function retrieves the local IP address of the P2P Group Owner (GO) using + * `__connman_p2p_group_get_local_ip` and appends it to the D-Bus message iterator + * as the "LocalAddress" property, if available. + */ +void __connman_dhcpserver_append_gateway(DBusMessageIter* iter) +{ + const char* local_address = __connman_p2p_group_get_local_ip(); + if (local_address != NULL) + connman_dbus_dict_append_basic(iter, "LocalAddress", DBUS_TYPE_STRING, &local_address); +} + +/** + * Initializes the P2P Group Owner (GO) functionality. + * + * @return 0 on success. + * + * This function serves as the initialization routine for the P2P Group Owner (GO). + * Currently, it only logs a debug message and returns success. + */ +int __connman_p2p_go_init(void) +{ + DBG("Initializing P2P Group Owner (GO)"); + return 0; +} + +/** + * Sets the DHCP IP pool for P2P Group Owner (GO). + * + * @param ippool Pointer to the `connman_ippool` structure representing the DHCP IP pool. + * + * This function assigns the provided IP pool to the global `dhcp_ippool` variable, + * which is used for managing IP address allocation in the P2P Group Owner network. + */ +void __connman_p2p_set_dhcp_pool(struct connman_ippool *ippool) +{ + dhcp_ippool = ippool; +} + +/** + * Cleans up the P2P Group Owner (GO) functionality. + * + * This function serves as the cleanup routine for the P2P Group Owner (GO). + * Currently, it only logs a debug message. + */ +void __connman_p2p_go_cleanup(void) +{ + DBG("Cleaning up P2P Group Owner (GO)"); +} \ No newline at end of file diff --git a/src/peer.c b/src/peer.c index 2b2c636..03c96c0 100644 --- a/src/peer.c +++ b/src/peer.c @@ -185,6 +185,8 @@ error: return err; } + + static void reply_pending(struct connman_peer *peer, int error) { if (!peer->pending) @@ -1200,6 +1202,22 @@ static void disconnect_peer_hash_table(gpointer key, peer_disconnect(peer); } +struct connman_peer *connman_peer_get_by_path(const char *path) +{ + struct connman_peer *peer; + + peer = g_hash_table_lookup(peers_table, path); + + return peer; +} + +void __connman_peer_get_properties_struct(DBusMessageIter *iter, gpointer user_data) +{ + struct connman_peer *peer = user_data; + + append_properties(iter, peer); +} + void __connman_peer_disconnect_all(void) { g_hash_table_foreach(peers_table, disconnect_peer_hash_table, NULL); diff --git a/src/technology.c b/src/technology.c index fc90663..10041a1 100644 --- a/src/technology.c +++ b/src/technology.c @@ -1343,6 +1343,63 @@ void __connman_technology_notify_regdom_by_device(struct connman_device *device, connman_technology_regdom_notify(technology, alpha2); } +/** + * Sets up a P2P group owner (GO) for the P2P technology. + * + * @param msg Pointer to the D-Bus message requesting the operation. + * @param ident Identifier for the P2P group. + * @param passphrase Passphrase for the P2P group. + * + * @return 0 on success, or a negative error code on failure. + * + * This function finds the P2P technology and iterates through its drivers to + * set up a P2P group owner (GO). If the identifier or passphrase is invalid, + * they are set to NULL. The function invokes the `set_p2p_go` method of each + * driver and handles errors appropriately. + */ +int __connman_technology_set_p2p_go(DBusMessage *msg, const char *ident, const char *passphrase) +{ + struct connman_technology *technology; + GSList *tech_drivers; + int result = 0; + int err; + + // Find the P2P technology. + technology = technology_find(CONNMAN_SERVICE_TYPE_P2P); + + DBG("technology %p", technology); + + if (!technology) + return -EINVAL; + + // Validate the identifier and passphrase. + if (strlen(ident) < 1 || strlen(passphrase) < 1) { + ident = NULL; + passphrase = NULL; + } + + // Iterate through the technology's drivers to set up the P2P GO. + for (tech_drivers = technology->driver_list; tech_drivers; + tech_drivers = g_slist_next(tech_drivers)) { + struct connman_technology_driver *driver = tech_drivers->data; + + if (!driver || !driver->set_p2p_go) + continue; + + err = driver->set_p2p_go(msg, technology, ident, passphrase); + + if (result == -EINPROGRESS) + continue; + + if (err == -EINPROGRESS || err == 0) { + result = err; + continue; + } + } + + return 0; +} + static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data) { struct connman_technology *technology = data; @@ -2146,6 +2203,26 @@ void __connman_technology_sta_count_changed(enum connman_service_type type, int connman_dbus_property_changed_basic(technology->path, CONNMAN_TECHNOLOGY_INTERFACE, "StaCount", DBUS_TYPE_INT32, &stacount); +} +void connman_technology_set_p2p_listen(struct connman_technology *technology, bool enabled) +{ + dbus_bool_t listen_enabled; + + if (enabled == technology->p2p_listen) + return; + technology->p2p_listen = enabled; + listen_enabled = enabled; + + connman_dbus_property_changed_basic(technology->path, + CONNMAN_TECHNOLOGY_INTERFACE, + "P2PListen", + DBUS_TYPE_BOOLEAN, + &listen_enabled); +} + +bool connman_technology_get_p2p_listen(struct connman_technology *technology) +{ + return technology->p2p_listen; } \ No newline at end of file diff --git a/src/util.c b/src/util.c index 03b14cd..991677c 100644 --- a/src/util.c +++ b/src/util.c @@ -30,6 +30,7 @@ #include <stdint.h> #include <unistd.h> #include <errno.h> +#include <stdio.h> #include <stdlib.h> #include "connman.h" @@ -59,6 +60,17 @@ int __connman_util_get_random(uint64_t *val) return r; } +void __connman_util_byte_to_string(unsigned char *src, char *dest, int len) +{ + int i=0; + + for(i=0; i<len; i++) { + snprintf(&dest[i*2], 3, "%02x", src[i]); + } + + dest[len*2] = '\0'; +} + int __connman_util_init(void) { int r = 0; @@ -102,3 +114,30 @@ unsigned int __connman_util_random_delay_ms(unsigned int secs) __connman_util_get_random(&rand); return rand % (secs * 1000); } + +char *__connman_util_insert_colon_to_mac_addr(const char *mac_addr) +{ + char *result = g_try_malloc(18); + int i; + + if (!mac_addr || strlen(mac_addr) < 12) { + g_free(result); + return NULL; + } + + for (i=0; i<6; i++) { + result[i*3] = mac_addr[i*2]; + result[i*3+1] = mac_addr[i*2+1]; + } + + result[2] = ':'; + result[5] = ':'; + result[8] = ':'; + result[11] = ':'; + result[14] = ':'; + result[17] = '\0'; + + DBG("before: %s, after: %s", mac_addr, result); + + return result; +}