summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNishant Chaprana <n.chaprana@samsung.com>2018-08-10 16:41:37 +0530
committerNishant Chaprana <n.chaprana@samsung.com>2018-08-24 10:20:11 +0530
commitd04bfa0350781ebfb8cbb2e64fabdfb2f36cd302 (patch)
tree750ce9bb53579e1ee54d91aa3a6c976e55f1a8db /src
parent037a4a5e75f3fc0845a924bb94addc2549550e63 (diff)
downloadconnman-d04bfa0350781ebfb8cbb2e64fabdfb2f36cd302.tar.gz
connman-d04bfa0350781ebfb8cbb2e64fabdfb2f36cd302.tar.bz2
connman-d04bfa0350781ebfb8cbb2e64fabdfb2f36cd302.zip
[connman] Added Tizen Wi-Fi Meshsubmit/tizen/20180824.072908
Change-Id: Iec2ec99fb7d4fc6d9c0539e5a9e7c903e1969580 Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
Diffstat (limited to 'src')
-rw-r--r--[-rwxr-xr-x]src/config.c3
-rw-r--r--[-rwxr-xr-x]src/connman.h28
-rw-r--r--[-rwxr-xr-x]src/device.c110
-rw-r--r--[-rwxr-xr-x]src/dhcp.c35
-rw-r--r--[-rwxr-xr-x]src/error.c14
-rw-r--r--[-rwxr-xr-x]src/inet.c93
-rw-r--r--[-rwxr-xr-x]src/main.c6
-rw-r--r--[-rwxr-xr-x]src/manager.c123
-rw-r--r--src/mesh-netlink.c187
-rw-r--r--src/mesh.c1660
-rw-r--r--[-rwxr-xr-x]src/notifier.c6
-rw-r--r--[-rwxr-xr-x]src/rtnl.c14
-rw-r--r--[-rwxr-xr-x]src/service.c37
-rw-r--r--[-rwxr-xr-x]src/session.c3
-rw-r--r--[-rwxr-xr-x]src/technology.c531
-rw-r--r--[-rwxr-xr-x]src/wispr.c3
16 files changed, 2835 insertions, 18 deletions
diff --git a/src/config.c b/src/config.c
index 75cd717f..0126711c 100755..100644
--- a/src/config.c
+++ b/src/config.c
@@ -1257,6 +1257,9 @@ static int try_provision_service(struct connman_config_service *config,
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
return -ENOENT;
}
diff --git a/src/connman.h b/src/connman.h
index d1069b93..905467e8 100755..100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -57,6 +57,10 @@ DBusMessage *__connman_error_operation_aborted(DBusMessage *msg);
DBusMessage *__connman_error_operation_timeout(DBusMessage *msg);
DBusMessage *__connman_error_invalid_service(DBusMessage *msg);
DBusMessage *__connman_error_invalid_property(DBusMessage *msg);
+#if defined TIZEN_EXT_WIFI_MESH
+DBusMessage *__connman_error_invalid_command(DBusMessage *msg);
+DBusMessage *__connman_error_scan_abort_failed(DBusMessage *msg);
+#endif
int __connman_manager_init(void);
void __connman_manager_cleanup(void);
@@ -920,6 +924,30 @@ int __connman_peer_service_unregister(const char *owner,
const unsigned char *query,
int query_length, int version);
+#if defined TIZEN_EXT_WIFI_MESH
+#include <connman/mesh.h>
+
+int __connman_mesh_init(void);
+void __connman_mesh_cleanup(void);
+bool __connman_technology_get_connected(enum connman_service_type type);
+void __connman_technology_mesh_interface_create_finished(
+ enum connman_service_type type, bool success,
+ const char *error);
+void __connman_technology_mesh_interface_remove_finished(
+ enum connman_service_type type, bool success);
+void __connman_mesh_peer_list_struct(DBusMessageIter *array);
+void __connman_mesh_connected_peer_list_struct(DBusMessageIter *array);
+void __connman_mesh_disconnected_peer_list_struct(DBusMessageIter *array);
+int __connman_mesh_dhcp_start(struct connman_ipconfig *ipconfig,
+ dhcp_cb callback, gpointer user_data);
+int __connman_device_abort_scan(enum connman_service_type type);
+void __connman_technology_notify_abort_scan(enum connman_service_type type,
+ int result);
+int __connman_device_request_mesh_specific_scan(enum connman_service_type type,
+ const char *name, unsigned int freq);
+void __connman_mesh_auto_connect(void);
+#endif /* TIZEN_EXT_WIFI_MESH */
+
#include <connman/session.h>
typedef void (* service_iterate_cb) (struct connman_service *service,
diff --git a/src/device.c b/src/device.c
index 3ec8f715..8b77021a 100755..100644
--- a/src/device.c
+++ b/src/device.c
@@ -743,6 +743,11 @@ int connman_device_set_scanning(struct connman_device *device,
__connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+#if defined TIZEN_EXT_WIFI_MESH
+ if (type == CONNMAN_SERVICE_TYPE_MESH)
+ __connman_mesh_auto_connect();
+#endif
+
return 0;
}
@@ -1077,6 +1082,9 @@ int __connman_device_request_specific_scan(enum connman_service_type type,
return -EOPNOTSUPP;
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
@@ -1107,6 +1115,100 @@ int __connman_device_request_specific_scan(enum connman_service_type type,
return last_err;
}
+
+#if defined TIZEN_EXT_WIFI_MESH
+static int device_abort_scan(enum connman_service_type type,
+ struct connman_device *device)
+{
+ if (!device->driver || !device->driver->scan)
+ return -EOPNOTSUPP;
+
+ if (!device->powered)
+ return -ENOLINK;
+
+ return device->driver->abort_scan(type, device);
+}
+
+int __connman_device_abort_scan(enum connman_service_type type)
+{
+ GSList *list;
+ int err = -EINVAL;
+
+ if (type != CONNMAN_SERVICE_TYPE_MESH)
+ return -EINVAL;
+
+ for (list = device_list; list; list = list->next) {
+ struct connman_device *device = list->data;
+ enum connman_service_type service_type =
+ __connman_device_get_service_type(device);
+
+ if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+ if (type == CONNMAN_SERVICE_TYPE_MESH)
+ if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+ continue;
+
+ if (!device->scanning) {
+ err = -EEXIST;
+ continue;
+ }
+
+ err = device_abort_scan(type, device);
+ }
+ }
+ return err;
+}
+
+static int device_mesh_specific_scan(enum connman_service_type type,
+ struct connman_device *device, const char *name,
+ unsigned int freq)
+{
+ if (!device->driver || !device->driver->mesh_specific_scan)
+ return -EOPNOTSUPP;
+
+ if (!device->powered)
+ return -ENOLINK;
+
+ return device->driver->mesh_specific_scan(type, device, name, freq, NULL);
+}
+
+int __connman_device_request_mesh_specific_scan(enum connman_service_type type,
+ const char *name,
+ unsigned int freq)
+{
+ bool success = false;
+ int last_err = -ENOSYS;
+ GSList *list;
+ int err;
+
+ if (type != CONNMAN_SERVICE_TYPE_MESH)
+ return -EINVAL;
+
+ for (list = device_list; list; list = list->next) {
+ struct connman_device *device = list->data;
+ enum connman_service_type service_type =
+ __connman_device_get_service_type(device);
+
+ if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+ if (type == CONNMAN_SERVICE_TYPE_MESH)
+ if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+ continue;
+ }
+
+ err = device_mesh_specific_scan(type, device, name, freq);
+ if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
+ success = true;
+ } else {
+ last_err = err;
+ DBG("device %p err %d", device, err);
+ }
+ }
+
+ if (success)
+ return 0;
+
+ return last_err;
+}
+#endif /* TIZEN_EXT_WIFI_MESH */
#endif
int __connman_device_request_scan(enum connman_service_type type)
@@ -1128,6 +1230,9 @@ int __connman_device_request_scan(enum connman_service_type type)
return -EOPNOTSUPP;
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
@@ -1140,6 +1245,11 @@ int __connman_device_request_scan(enum connman_service_type type)
if (type == CONNMAN_SERVICE_TYPE_P2P) {
if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
continue;
+#if defined TIZEN_EXT_WIFI_MESH
+ } else if (type == CONNMAN_SERVICE_TYPE_MESH) {
+ if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+ continue;
+#endif
} else if (service_type != type)
continue;
}
diff --git a/src/dhcp.c b/src/dhcp.c
index c428c1d4..26a350be 100755..100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -730,6 +730,41 @@ char *__connman_dhcp_get_server_address(struct connman_ipconfig *ipconfig)
return g_dhcp_client_get_server_address(dhcp->dhcp_client);
}
+#if defined TIZEN_EXT_WIFI_MESH
+int __connman_mesh_dhcp_start(struct connman_ipconfig *ipconfig,
+ dhcp_cb callback, gpointer user_data)
+{
+ struct connman_dhcp *dhcp;
+ int err;
+
+ DBG("");
+
+ dhcp = g_hash_table_lookup(ipconfig_table, ipconfig);
+ if (!dhcp) {
+
+ dhcp = g_try_new0(struct connman_dhcp, 1);
+ if (!dhcp)
+ return -ENOMEM;
+
+ dhcp->ipconfig = ipconfig;
+ __connman_ipconfig_ref(ipconfig);
+
+ err = dhcp_initialize(dhcp);
+
+ if (err < 0) {
+ g_free(dhcp);
+ return err;
+ }
+
+ g_hash_table_insert(ipconfig_table, ipconfig, dhcp);
+ }
+
+ dhcp->callback = callback;
+ dhcp->user_data = user_data;
+ return g_dhcp_client_start(dhcp->dhcp_client, NULL);
+}
+#endif
+
int __connman_dhcp_start(struct connman_ipconfig *ipconfig,
struct connman_network *network, dhcp_cb callback,
gpointer user_data)
diff --git a/src/error.c b/src/error.c
index 4f24ae25..1a059207 100755..100644
--- a/src/error.c
+++ b/src/error.c
@@ -185,3 +185,17 @@ DBusMessage *__connman_error_invalid_property(DBusMessage *msg)
return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
".InvalidProperty", "Invalid property");
}
+
+#if defined TIZEN_EXT_WIFI_MESH
+DBusMessage *__connman_error_invalid_command(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+ ".InvalidCommand", "Invalid Mesh Command");
+}
+
+DBusMessage *__connman_error_scan_abort_failed(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+ ".ScanAbortFailed", "Scan Abort Failed");
+}
+#endif
diff --git a/src/inet.c b/src/inet.c
index 93be1e78..25e53727 100755..100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -190,6 +190,78 @@ done:
return err;
}
+#if defined TIZEN_EXT_WIFI_MESH
+char *connman_inet_ifaddr(const char *name)
+{
+ struct ifreq ifr;
+ struct ether_addr eth;
+ char *str;
+ int sk, err;
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return NULL;
+
+ strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
+
+ err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+ close(sk);
+
+ if (err < 0)
+ return NULL;
+
+ str = g_malloc(18);
+ if (!str)
+ return NULL;
+
+ memcpy(&eth, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+ snprintf(str, 13, "%02x%02x%02x%02x%02x%02x",
+ eth.ether_addr_octet[0],
+ eth.ether_addr_octet[1],
+ eth.ether_addr_octet[2],
+ eth.ether_addr_octet[3],
+ eth.ether_addr_octet[4],
+ eth.ether_addr_octet[5]);
+
+ return str;
+}
+
+char *connman_inet_ifname2addr(const char *name)
+{
+ struct ifreq ifr;
+ struct ether_addr eth;
+ char *str;
+ int sk, err;
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return NULL;
+
+ strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
+
+ err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+ close(sk);
+
+ if (err < 0)
+ return NULL;
+
+ str = g_malloc(18);
+ if (!str)
+ return NULL;
+
+ memcpy(&eth, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+ snprintf(str, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
+ eth.ether_addr_octet[0],
+ eth.ether_addr_octet[1],
+ eth.ether_addr_octet[2],
+ eth.ether_addr_octet[3],
+ eth.ether_addr_octet[4],
+ eth.ether_addr_octet[5]);
+
+ return str;
+}
+#endif
+
int connman_inet_ifindex(const char *name)
{
struct ifreq ifr;
@@ -347,29 +419,14 @@ void connman_inet_update_device_ident(struct connman_device *device)
case CONNMAN_DEVICE_TYPE_GADGET:
case CONNMAN_DEVICE_TYPE_WIFI:
addr = index2addr(index);
- break;
- case CONNMAN_DEVICE_TYPE_BLUETOOTH:
- case CONNMAN_DEVICE_TYPE_CELLULAR:
- case CONNMAN_DEVICE_TYPE_GPS:
- case CONNMAN_DEVICE_TYPE_VENDOR:
- break;
- }
-
- switch (type) {
- case CONNMAN_DEVICE_TYPE_VENDOR:
- case CONNMAN_DEVICE_TYPE_GPS:
- break;
- case CONNMAN_DEVICE_TYPE_ETHERNET:
- case CONNMAN_DEVICE_TYPE_GADGET:
ident = index2ident(index, NULL);
break;
- case CONNMAN_DEVICE_TYPE_WIFI:
+ case CONNMAN_DEVICE_TYPE_CELLULAR:
ident = index2ident(index, NULL);
break;
case CONNMAN_DEVICE_TYPE_BLUETOOTH:
- break;
- case CONNMAN_DEVICE_TYPE_CELLULAR:
- ident = index2ident(index, NULL);
+ case CONNMAN_DEVICE_TYPE_GPS:
+ case CONNMAN_DEVICE_TYPE_VENDOR:
break;
}
diff --git a/src/main.c b/src/main.c
index 4bc2266b..7a6d802c 100755..100644
--- a/src/main.c
+++ b/src/main.c
@@ -806,6 +806,9 @@ int main(int argc, char *argv[])
__connman_service_init();
__connman_peer_service_init();
__connman_peer_init();
+#if defined TIZEN_EXT_WIFI_MESH
+ __connman_mesh_init();
+#endif /* TIZEN_EXT_WIFI_MESH */
__connman_provider_init();
__connman_network_init();
__connman_config_init();
@@ -880,6 +883,9 @@ int main(int argc, char *argv[])
__connman_firewall_cleanup();
__connman_peer_service_cleanup();
__connman_peer_cleanup();
+#if defined TIZEN_EXT_WIFI_MESH
+ __connman_mesh_cleanup();
+#endif /* TIZEN_EXT_WIFI_MESH */
__connman_ippool_cleanup();
__connman_device_cleanup();
__connman_network_cleanup();
diff --git a/src/manager.c b/src/manager.c
index 2e1367ff..bd44fea4 100755..100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -537,6 +537,114 @@ error:
}
+#if defined TIZEN_EXT_WIFI_MESH
+static void append_mesh_peer_structs(DBusMessageIter *iter, void *user_data)
+{
+ __connman_mesh_peer_list_struct(iter);
+}
+
+static DBusMessage *get_mesh_peers(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ __connman_dbus_append_objpath_dict_array(reply,
+ append_mesh_peer_structs, NULL);
+ return reply;
+}
+
+static DBusMessage *get_connected_mesh_peers(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING, &array);
+
+ __connman_mesh_connected_peer_list_struct(&array);
+ dbus_message_iter_close_container(&iter, &array);
+ return reply;
+}
+
+static DBusMessage *get_disconnected_mesh_peers(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING, &array);
+
+ __connman_mesh_disconnected_peer_list_struct(&array);
+ dbus_message_iter_close_container(&iter, &array);
+ return reply;
+}
+
+static DBusMessage *mesh_add_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *addr;
+ int err;
+
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID);
+
+ DBG("Address %s", addr);
+
+ err = __connman_mesh_change_peer_status(msg, addr, CONNMAN_MESH_PEER_ADD);
+ if (err < 0)
+ return __connman_error_failed(msg, -err);
+
+ return NULL;
+}
+
+static DBusMessage *mesh_remove_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *addr;
+ int err;
+
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID);
+
+ DBG("Address %s", addr);
+
+ err = __connman_mesh_change_peer_status(msg, addr,
+ CONNMAN_MESH_PEER_REMOVE);
+ if (err < 0)
+ return __connman_error_failed(msg, -err);
+
+ return NULL;
+}
+#endif
+
static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -596,6 +704,21 @@ static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("UnregisterPeerService",
GDBUS_ARGS({ "specification", "a{sv}" }), NULL,
unregister_peer_service) },
+#if defined TIZEN_EXT_WIFI_MESH
+ { GDBUS_METHOD("GetMeshPeers",
+ NULL, GDBUS_ARGS({ "peers", "a(oa{sv})" }),
+ get_mesh_peers) },
+ { GDBUS_METHOD("GetConnectedMeshPeers",
+ NULL, GDBUS_ARGS({ "peers", "a(a{sv})" }),
+ get_connected_mesh_peers) },
+ { GDBUS_METHOD("GetDisconnectedMeshPeers",
+ NULL, GDBUS_ARGS({ "peers", "a(a{sv})" }),
+ get_disconnected_mesh_peers) },
+ { GDBUS_ASYNC_METHOD("MeshAddPeer", GDBUS_ARGS({ "address", "s" }), NULL,
+ mesh_add_peer) },
+ { GDBUS_ASYNC_METHOD("MeshRemovePeer", GDBUS_ARGS({ "address", "s" }), NULL,
+ mesh_remove_peer) },
+#endif
{ },
};
diff --git a/src/mesh-netlink.c b/src/mesh-netlink.c
new file mode 100644
index 00000000..22d69254
--- /dev/null
+++ b/src/mesh-netlink.c
@@ -0,0 +1,187 @@
+/*
+ *
+ * Connection Manager
+ *
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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 "connman.h"
+#include <connman/mesh-netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <netlink/netlink.h>
+
+static int seq_check_cb(struct nl_msg *msg, void *arg)
+{
+ DBG("");
+
+ return NL_OK;
+}
+
+static int finish_cb(struct nl_msg *msg, void *arg)
+{
+ int *ret = arg;
+
+ DBG("");
+
+ *ret = 0;
+
+ return NL_SKIP;
+}
+
+static int ack_cb(struct nl_msg *msg, void *arg)
+{
+ int *ret = arg;
+
+ DBG("");
+
+ *ret = 0;
+
+ return NL_STOP;
+}
+
+static int valid_cb(struct nl_msg *msg, void *arg)
+{
+ DBG("");
+
+ return NL_SKIP;
+}
+
+static int error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+ int *ret = arg;
+
+ *ret = err->error;
+
+ DBG("error %d", *ret);
+
+ return NL_STOP;
+}
+
+int __connman_mesh_netlink_set_gate_announce(mesh_nl80211_global *global,
+ int mesh_if_index, bool gate_announce, int hwmp_rootmode)
+{
+ struct nl_msg *msg;
+ struct nlattr *container;
+ struct nl_cb *cb;
+ int err = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ cb = nl_cb_clone(global->cb);
+ if (!cb)
+ goto out;
+
+ genlmsg_put(msg, 0, 0, global->id, 0, 0, NL80211_CMD_SET_MESH_CONFIG, 0);
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, mesh_if_index);
+
+ container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+
+ nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, hwmp_rootmode);
+
+ nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, gate_announce);
+
+ nla_nest_end(msg, container);
+
+ err = nl_send_auto_complete(global->nl_socket, msg);
+ if (err < 0) {
+ DBG("Failed to send msg");
+ goto out;
+ }
+
+ err = 1;
+
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_cb, &err);
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_cb, &err);
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_cb, &err);
+ nl_cb_err(cb, NL_CB_CUSTOM, error_cb, &err);
+
+ while (err > 0) {
+ int res = nl_recvmsgs(global->nl_socket, cb);
+ if (res < 0)
+ DBG("nl_recvmsgs failed: %d", res);
+ }
+
+out:
+ nl_cb_put(cb);
+ nlmsg_free(msg);
+ return err;
+}
+
+mesh_nl80211_global *__connman_mesh_nl80211_global_init(void)
+{
+ mesh_nl80211_global *global;
+
+ DBG("");
+
+ global = g_malloc0(sizeof(mesh_nl80211_global));
+
+ global->nl_socket = nl_socket_alloc();
+ if (!global->nl_socket) {
+ DBG("Failed to allocate netlink socket");
+ g_free(global);
+ return NULL;
+ }
+
+ if (genl_connect(global->nl_socket)) {
+ DBG("Failed to connect to generic netlink");
+ nl_socket_free(global->nl_socket);
+ g_free(global);
+ return NULL;
+ }
+
+ nl_socket_set_buffer_size(global->nl_socket, 8192, 8192);
+
+ global->id = genl_ctrl_resolve(global->nl_socket, "nl80211");
+ if (global->id < 0) {
+ DBG("nl80211 generic netlink not found");
+ nl_socket_free(global->nl_socket);
+ g_free(global);
+ return NULL;
+ }
+
+ global->cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!global->cb) {
+ DBG("Failed to allocate netwlink callbacks");
+ nl_socket_free(global->nl_socket);
+ g_free(global);
+ return NULL;
+ }
+
+ nl_cb_set(global->cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_cb, NULL);
+
+ return global;
+}
+
+void __connman_mesh_nl80211_global_deinit(mesh_nl80211_global *global)
+{
+ DBG("");
+
+ nl_cb_put(global->cb);
+ nl_socket_free(global->nl_socket);
+ g_free(global);
+}
diff --git a/src/mesh.c b/src/mesh.c
new file mode 100644
index 00000000..b9029343
--- /dev/null
+++ b/src/mesh.c
@@ -0,0 +1,1660 @@
+/*
+ *
+ * Connection Manager
+ *
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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 <gdbus.h>
+
+#include <connman/storage.h>
+#include "connman.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include <linux/if_bridge.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include "mesh-netlink.h"
+
+static DBusConnection *connection;
+
+static GHashTable *mesh_table;
+static GHashTable *connected_peer_table;
+static GHashTable *disconnected_peer_table;
+
+static struct connman_mesh_driver *mesh_driver;
+static struct connman_mesh_eth_driver *mesh_eth_driver;
+
+char *mesh_ifname;
+char *bridge_interface;
+static unsigned int mesh_autoconnect_timeout;
+static bool is_mesh_if_created;
+bool eth_if_bridged;
+mesh_nl80211_global *nl80211_global;
+
+struct connman_mesh {
+ int refcount;
+ char *identifier;
+ char *name;
+ char *path;
+ char *address;
+ char *interface_addr;
+ enum connman_mesh_security security;
+ char *passphrase;
+ enum connman_mesh_state state;
+ enum connman_mesh_peer_type peer_type;
+ enum connman_mesh_peer_disconnect_reason disconnect_reason;
+ uint16_t frequency;
+ uint8_t strength;
+ bool registered;
+ bool favorite;
+ DBusMessage *pending;
+ int index;
+ int br_index;
+ uint16_t ieee80211w;
+ struct connman_ipconfig *ipconfig;
+};
+
+struct connman_mesh_connected_peer {
+ char *peer_address;
+};
+
+struct connman_mesh_disconnected_peer {
+ char *peer_address;
+ enum connman_mesh_peer_disconnect_reason disconnect_reason;
+};
+
+struct connman_mesh_change_peer_data {
+ DBusMessage *pending;
+ char *peer_address;
+ enum connman_mesh_peer_status status;
+};
+
+static void mesh_dhcp_callback(struct connman_ipconfig *ipconfig,
+ struct connman_network *network, bool success, gpointer data);
+
+static void mesh_free(gpointer data)
+{
+ struct connman_mesh *mesh = data;
+
+ connman_mesh_unregister(mesh);
+
+ g_free(mesh->path);
+
+ if (mesh->state == CONNMAN_MESH_STATE_CONFIGURATION ||
+ mesh->state == CONNMAN_MESH_STATE_READY)
+ __connman_dhcp_stop(mesh->ipconfig);
+
+ if (mesh->ipconfig) {
+ __connman_ipconfig_set_ops(mesh->ipconfig, NULL);
+ __connman_ipconfig_set_data(mesh->ipconfig, NULL);
+ __connman_ipconfig_unref(mesh->ipconfig);
+ mesh->ipconfig = NULL;
+ }
+ g_free(mesh->identifier);
+ g_free(mesh->name);
+ g_free(mesh->passphrase);
+ g_free(mesh->interface_addr);
+ g_free(mesh->address);
+ g_free(mesh);
+}
+
+static void mesh_connected_peer_free(gpointer data)
+{
+ struct connman_mesh_connected_peer *peer = data;
+
+ g_free(peer->peer_address);
+ g_free(peer);
+}
+
+static void mesh_disconnected_peer_free(gpointer data)
+{
+ struct connman_mesh_disconnected_peer *peer = data;
+
+ g_free(peer->peer_address);
+ g_free(peer);
+}
+
+static void __mesh_load_and_create_network(char *mesh_id)
+{
+ GKeyFile *keyfile;
+ GString *str;
+ struct connman_mesh *connman_mesh;
+ gchar *name, *passphrase, *peer_type;
+ char *identifier, *group, *address;
+ const char *sec_type, *mesh_ifname;
+ int freq, i;
+
+ keyfile = connman_storage_load_service(mesh_id);
+ if (!keyfile) {
+ DBG("Mesh profile doesn't exist");
+ return;
+ }
+
+ peer_type = g_key_file_get_string(keyfile, mesh_id, "PeerType", NULL);
+ if (g_strcmp0(peer_type, "created")) {
+ DBG("Mesh Profile was not created");
+ goto done;
+ }
+
+ name = g_key_file_get_string(keyfile, mesh_id, "Name", NULL);
+ if (!name) {
+ DBG("Failed to get Mesh Profile Name");
+ goto done;
+ }
+
+ passphrase = g_key_file_get_string(keyfile, mesh_id, "Passphrase", NULL);
+ if (passphrase)
+ sec_type = "sae";
+ else
+ sec_type = "none";
+
+ freq = g_key_file_get_integer(keyfile, mesh_id, "Frequency", NULL);
+
+ mesh_ifname = connman_mesh_get_interface_name();
+
+ str = g_string_sized_new((strlen(name) * 2) + 24);
+
+ for (i = 0; name[i]; i++)
+ g_string_append_printf(str, "%02x", name[i]);
+
+ g_string_append_printf(str, "_mesh");
+
+ if (g_strcmp0(sec_type, "none") == 0)
+ g_string_append_printf(str, "_none");
+ else if (g_strcmp0(sec_type, "sae") == 0)
+ g_string_append_printf(str, "_sae");
+
+ group = g_string_free(str, FALSE);
+
+ identifier = connman_inet_ifaddr(mesh_ifname);
+ address = connman_inet_ifname2addr(mesh_ifname);
+
+ connman_mesh = connman_mesh_create(identifier, group);
+ connman_mesh_set_name(connman_mesh, name);
+ connman_mesh_set_address(connman_mesh, address);
+ connman_mesh_set_security(connman_mesh, sec_type);
+ connman_mesh_set_frequency(connman_mesh, freq);
+ connman_mesh_set_index(connman_mesh, connman_inet_ifindex(mesh_ifname));
+ connman_mesh_set_peer_type(connman_mesh, CONNMAN_MESH_PEER_TYPE_CREATED);
+
+ connman_mesh_register(connman_mesh);
+ g_free(group);
+ g_free(identifier);
+ g_free(address);
+done:
+ g_key_file_free(keyfile);
+}
+
+static bool is_connected(struct connman_mesh *mesh)
+{
+ if (mesh->state == CONNMAN_MESH_STATE_READY)
+ return true;
+
+ return false;
+}
+
+static void mesh_peer_dhcp_refresh(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct connman_mesh *mesh = value;
+
+ DBG("mesh %p state %d", mesh, mesh->state);
+
+ if (is_connected(mesh))
+ __connman_mesh_dhcp_start(mesh->ipconfig, mesh_dhcp_callback, mesh);
+}
+
+int connman_inet_set_stp(int stp)
+{
+ int sk, err = 0;
+ struct ifreq ifr;
+ unsigned long args[4];
+
+ if (!bridge_interface)
+ return -EINVAL;
+
+ sk = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = -errno;
+ goto out;
+ }
+
+ args[0] = BRCTL_SET_BRIDGE_STP_STATE;
+ args[1] = stp;
+ args[2] = args[3] = 0;
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strncpy(ifr.ifr_name, bridge_interface, sizeof(ifr.ifr_name) - 1);
+ ifr.ifr_data = (char *)args;
+
+ if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0)
+ err = -errno;
+
+ close(sk);
+
+out:
+ if (err < 0)
+ DBG("Set STP Failed error %s", strerror(-err));
+
+ return err;
+}
+
+int __connman_mesh_set_stp_gate_announce(bool gate_announce, int hwmp_rootmode,
+ int stp)
+{
+ int err;
+
+ if (!mesh_ifname)
+ return -EINVAL;
+
+ err = connman_inet_set_stp(stp);
+ if (err < 0)
+ return err;
+
+ err = __connman_mesh_netlink_set_gate_announce(nl80211_global,
+ connman_inet_ifindex(mesh_ifname), gate_announce,
+ hwmp_rootmode);
+
+ return err;
+}
+
+void __connman_mesh_add_ethernet_to_bridge(void)
+{
+ if (is_mesh_if_created) {
+ DBG("");
+ mesh_eth_driver->add_to_bridge(bridge_interface);
+ eth_if_bridged = true;
+ g_hash_table_foreach(mesh_table, mesh_peer_dhcp_refresh, NULL);
+ connman_inet_set_stp(1);
+ __connman_mesh_netlink_set_gate_announce(nl80211_global,
+ connman_inet_ifindex(mesh_ifname), true,
+ MESH_HWMP_ROOTMODE_RANN);
+ }
+}
+
+void __connman_mesh_remove_ethernet_from_bridge(void)
+{
+ if (eth_if_bridged) {
+ DBG("");
+ mesh_eth_driver->remove_from_bridge(bridge_interface);
+ eth_if_bridged = false;
+ g_hash_table_foreach(mesh_table, mesh_peer_dhcp_refresh, NULL);
+ connman_inet_set_stp(0);
+ __connman_mesh_netlink_set_gate_announce(nl80211_global,
+ connman_inet_ifindex(mesh_ifname), false,
+ MESH_HWMP_ROOTMODE_NO_ROOT);
+ }
+}
+
+int connman_mesh_notify_interface_create(bool success)
+{
+ int ret;
+ int index;
+ const char *error = NULL;
+ DIR *dir;
+ struct dirent *d;
+
+ if (!success) {
+ error = "Operation Failed";
+ goto done;
+ }
+
+ if (!bridge_interface) {
+ DBG("Don't create bridge interface");
+ goto done;
+ }
+
+ DBG("Creating bridge [%s]", bridge_interface);
+
+ /* Create bridge interface */
+ ret = __connman_bridge_create(bridge_interface);
+ if (0 != ret) {
+ DBG("Failed to create bridge [%s] : [%s]", bridge_interface,
+ strerror(-ret));
+ error = "Bridge Creation";
+ success = false;
+ goto done;
+ }
+
+ /* Get Mesh Interface Index */
+ index = connman_inet_ifindex(mesh_ifname);
+ if (index < 0) {
+ DBG("Failed to get interface index for %s", mesh_ifname);
+ error = "Operation Failed";
+ success = false;
+ goto done;
+ }
+
+ /* Add mesh interface into bridge */
+ ret = connman_inet_add_to_bridge(index, bridge_interface);
+ if (0 != ret) {
+ DBG("Failed to add interface[%s] into bridge[%s]", mesh_ifname,
+ bridge_interface);
+ error = "Add Mesh into bridge";
+ success = false;
+ goto done;
+ }
+
+ if (__connman_technology_get_connected(CONNMAN_SERVICE_TYPE_ETHERNET)) {
+ mesh_eth_driver->add_to_bridge(bridge_interface);
+ eth_if_bridged = true;
+ }
+
+ index = connman_inet_ifindex(bridge_interface);
+ if (index < 0) {
+ DBG("Failed to get interface index for %s", bridge_interface);
+ error = "Operation Failed";
+ success = false;
+ goto done;
+ }
+
+ /* Make bridge interface UP */
+ ret = connman_inet_ifup(index);
+ if (0 != ret) {
+ DBG("Failed to change bridge interface state");
+ error = "Make bridge interface UP";
+ success = false;
+ }
+
+done:
+ if (success) {
+ is_mesh_if_created = true;
+
+ /* Load previously created mesh profiles */
+ dir = opendir(STORAGEDIR);
+ if (!dir) {
+ DBG("Failed to open %s directory", STORAGEDIR);
+ __connman_technology_mesh_interface_create_finished(
+ CONNMAN_SERVICE_TYPE_MESH, success, error);
+ return 0;
+ }
+
+ while ((d = readdir(dir))) {
+ if (g_str_has_prefix(d->d_name, "mesh_")) {
+ DBG("%s is a mesh profile", d->d_name);
+ __mesh_load_and_create_network(d->d_name);
+ __connman_mesh_auto_connect();
+ }
+ }
+
+ closedir(dir);
+
+ } else {
+ if (eth_if_bridged)
+ mesh_eth_driver->remove_from_bridge(bridge_interface);
+
+ __connman_bridge_disable(bridge_interface);
+
+ __connman_bridge_remove(bridge_interface);
+
+ mesh_driver->remove_interface(mesh_ifname);
+ }
+ __connman_technology_mesh_interface_create_finished(
+ CONNMAN_SERVICE_TYPE_MESH, success, error);
+ return 0;
+}
+
+int __connman_mesh_add_virtual_interface(const char *ifname,
+ const char *parent_ifname, const char *bridge_ifname)
+{
+ int ret;
+
+ if (!ifname || !parent_ifname)
+ return -EINVAL;
+
+ ret = mesh_driver->add_interface(ifname, parent_ifname);
+ if (ret != -EINPROGRESS) {
+ DBG("Failed to add virtual mesh interface");
+ return ret;
+ }
+
+ mesh_ifname = g_strdup(ifname);
+ bridge_interface = g_strdup(bridge_ifname);
+ DBG("Success adding virtual mesh interface");
+ return 0;
+}
+
+int connman_mesh_notify_interface_remove(bool success)
+{
+ struct connman_device *device;
+ int index;
+ if (success) {
+ g_free(mesh_ifname);
+ mesh_ifname = NULL;
+ g_hash_table_remove_all(mesh_table);
+ is_mesh_if_created = false;
+
+ if (eth_if_bridged) {
+ if (bridge_interface)
+ mesh_eth_driver->remove_from_bridge(bridge_interface);
+
+ device = __connman_device_find_device(
+ CONNMAN_SERVICE_TYPE_ETHERNET);
+ if (device) {
+ index = connman_device_get_index(device);
+ connman_inet_ifup(index);
+ }
+ eth_if_bridged = false;
+ }
+
+ if (bridge_interface) {
+ __connman_bridge_disable(bridge_interface);
+ if (__connman_bridge_remove(bridge_interface))
+ DBG("Failed to remove bridge [%s]", bridge_interface);
+
+ g_free(bridge_interface);
+ bridge_interface = NULL;
+ }
+ }
+
+ __connman_technology_mesh_interface_remove_finished(
+ CONNMAN_SERVICE_TYPE_MESH, success);
+ return 0;
+}
+
+int __connman_mesh_remove_virtual_interface(const char *ifname)
+{
+ int ret;
+ int index;
+
+ if (!ifname)
+ return -EINVAL;
+
+ if (bridge_interface) {
+ index = connman_inet_ifindex(mesh_ifname);
+ if (index < 0) {
+ DBG("Failed to get interface index for %s", mesh_ifname);
+ return -EINVAL;
+ }
+
+ ret = connman_inet_remove_from_bridge(index, bridge_interface);
+ if (0 != ret) {
+ DBG("Failed to remove interface[%s] freom bridge[%s]", mesh_ifname,
+ bridge_interface);
+ return -EINVAL;
+ }
+
+ if (eth_if_bridged)
+ mesh_eth_driver->remove_from_bridge(bridge_interface);
+
+ __connman_bridge_disable(bridge_interface);
+
+ ret = __connman_bridge_remove(bridge_interface);
+ if (0 != ret) {
+ DBG("Failed to remove bridge [%s]", bridge_interface);
+ return -EINVAL;
+ }
+
+ g_free(bridge_interface);
+ bridge_interface = NULL;
+ }
+
+ ret = mesh_driver->remove_interface(ifname);
+ if (ret != -EINPROGRESS) {
+ DBG("Failed to remove virtual mesh interface");
+ return ret;
+ }
+
+ DBG("Success removing virtual mesh interface");
+ return 0;
+}
+
+const char *connman_mesh_get_interface_name(void)
+{
+ return mesh_ifname;
+}
+
+bool connman_mesh_is_interface_created(void)
+{
+ DBG("Mesh interface is %screated", is_mesh_if_created ? "" : "not ");
+ return is_mesh_if_created;
+}
+
+struct connman_mesh *connman_mesh_create(const char *interface_addr,
+ const char *identifier)
+{
+ struct connman_mesh *mesh;
+
+ mesh = g_malloc0(sizeof(struct connman_mesh));
+ mesh->identifier = g_strdup_printf("mesh_%s_%s", interface_addr,
+ identifier);
+ mesh->interface_addr = g_strdup(interface_addr);
+ mesh->state = CONNMAN_MESH_STATE_IDLE;
+
+ mesh->refcount = 1;
+
+ return mesh;
+}
+
+void connman_mesh_set_name(struct connman_mesh *mesh, const char *name)
+{
+ g_free(mesh->name);
+ mesh->name = g_strdup(name);
+}
+
+const char *connman_mesh_get_name(struct connman_mesh *mesh)
+{
+ return mesh->name;
+}
+
+void connman_mesh_set_passphrase(struct connman_mesh *mesh,
+ const char *passphrase)
+{
+ g_free(mesh->passphrase);
+ mesh->passphrase = g_strdup(passphrase);
+}
+
+const char *connman_mesh_get_passphrase(struct connman_mesh *mesh)
+{
+ return mesh->passphrase;
+}
+
+void connman_mesh_set_address(struct connman_mesh *mesh, const char *address)
+{
+ g_free(mesh->address);
+ mesh->address = g_strdup(address);
+}
+
+void connman_mesh_set_security(struct connman_mesh *mesh, const char *security)
+{
+ if (!g_strcmp0(security, "none"))
+ mesh->security = CONNMAN_MESH_SECURITY_NONE;
+ else if (!g_strcmp0(security, "sae"))
+ mesh->security = CONNMAN_MESH_SECURITY_SAE;
+ else
+ mesh->security = CONNMAN_MESH_SECURITY_UNKNOWN;
+}
+
+static const char *security2string(enum connman_mesh_security security)
+{
+ switch (security) {
+ case CONNMAN_MESH_SECURITY_UNKNOWN:
+ break;
+ case CONNMAN_MESH_SECURITY_NONE:
+ return "none";
+ case CONNMAN_MESH_SECURITY_SAE:
+ return "sae";
+ }
+
+ return NULL;
+}
+
+const char *connman_mesh_get_security(struct connman_mesh *mesh)
+{
+ return security2string(mesh->security);
+}
+
+void connman_mesh_set_frequency(struct connman_mesh *mesh, uint16_t frequency)
+{
+ mesh->frequency = frequency;
+}
+
+uint16_t connman_mesh_get_frequency(struct connman_mesh *mesh)
+{
+ return mesh->frequency;
+}
+
+void connman_mesh_set_ieee80211w(struct connman_mesh *mesh, uint16_t ieee80211w)
+{
+ mesh->ieee80211w = ieee80211w;
+}
+
+uint16_t connman_mesh_get_ieee80211w(struct connman_mesh *mesh)
+{
+ return mesh->ieee80211w;
+}
+
+void connman_mesh_set_index(struct connman_mesh *mesh, int index)
+{
+ mesh->index = index;
+
+ if (bridge_interface)
+ mesh->br_index = connman_inet_ifindex(bridge_interface);
+}
+
+void connman_mesh_set_strength(struct connman_mesh *mesh, uint8_t strength)
+{
+ mesh->strength = strength;
+}
+
+static const char *peertype2string(enum connman_mesh_peer_type type)
+{
+ switch (type) {
+ case CONNMAN_MESH_PEER_TYPE_CREATED:
+ return "created";
+ case CONNMAN_MESH_PEER_TYPE_DISCOVERED:
+ return "discovered";
+ }
+
+ return NULL;
+}
+
+void connman_mesh_set_peer_type(struct connman_mesh *mesh,
+ enum connman_mesh_peer_type type)
+{
+ mesh->peer_type = type;
+}
+
+static const char *state2string(enum connman_mesh_state state)
+{
+ switch (state) {
+ case CONNMAN_MESH_STATE_UNKNOWN:
+ break;
+ case CONNMAN_MESH_STATE_IDLE:
+ return "idle";
+ case CONNMAN_MESH_STATE_ASSOCIATION:
+ return "association";
+ case CONNMAN_MESH_STATE_CONFIGURATION:
+ return "configuration";
+ case CONNMAN_MESH_STATE_READY:
+ return "ready";
+ case CONNMAN_MESH_STATE_DISCONNECT:
+ return "disconnect";
+ case CONNMAN_MESH_STATE_FAILURE:
+ return "failure";
+ }
+
+ return NULL;
+}
+
+static enum connman_mesh_peer_disconnect_reason convert_to_disconnect_reason(
+ int reason)
+{
+ switch (reason) {
+ case 3:
+ return CONNMAN_MESH_DEAUTH_LEAVING;
+ case 52:
+ return CONNMAN_MESH_PEERING_CANCELLED;
+ case 53:
+ return CONNMAN_MESH_MAX_PEERS;
+ case 54:
+ return CONNMAN_MESH_CONFIG_POLICY_VIOLATION;
+ case 55:
+ return CONNMAN_MESH_CLOSE_RCVD;
+ case 56:
+ return CONNMAN_MESH_MAX_RETRIES;
+ case 57:
+ return CONNMAN_MESH_CONFIRM_TIMEOUT;
+ case 58:
+ return CONNMAN_MESH_INVALID_GTK;
+ case 59:
+ return CONNMAN_MESH_INCONSISTENT_PARAMS;
+ case 60:
+ return CONNMAN_MESH_INVALID_SECURITY_CAP;
+ }
+
+ return CONNMAN_MESH_REASON_UNKNOWN;
+}
+
+void connman_mesh_peer_set_disconnect_reason(struct connman_mesh *mesh,
+ int disconnect_reason)
+{
+ mesh->disconnect_reason = convert_to_disconnect_reason(disconnect_reason);
+}
+
+static bool is_connecting(struct connman_mesh *mesh)
+{
+ if (mesh->state == CONNMAN_MESH_STATE_ASSOCIATION ||
+ mesh->state == CONNMAN_MESH_STATE_CONFIGURATION)
+ return true;
+
+ return false;
+}
+
+static int mesh_load(struct connman_mesh *mesh)
+{
+ GKeyFile *keyfile;
+ bool favorite;
+ GError *error = NULL;
+ gchar *str;
+
+ keyfile = connman_storage_load_service(mesh->identifier);
+ if (!keyfile) {
+ DBG("Mesh profile is new");
+ return -EIO;
+ }
+
+ favorite = g_key_file_get_boolean(keyfile,
+ mesh->identifier, "Favorite", &error);
+
+ if (!error)
+ mesh->favorite = favorite;
+
+ g_clear_error(&error);
+
+ str = g_key_file_get_string(keyfile, mesh->identifier, "Passphrase", NULL);
+
+ if (str) {
+ g_free(mesh->passphrase);
+ mesh->passphrase = str;
+ }
+
+ return 0;
+}
+
+static int mesh_save(struct connman_mesh *mesh)
+{
+ GKeyFile *keyfile;
+
+ keyfile = __connman_storage_open_service(mesh->identifier);
+ if (!keyfile)
+ return -EIO;
+
+ g_key_file_set_string(keyfile, mesh->identifier, "Name", mesh->name);
+ g_key_file_set_integer(keyfile, mesh->identifier, "Frequency",
+ mesh->frequency);
+ g_key_file_set_boolean(keyfile, mesh->identifier, "Favorite",
+ mesh->favorite);
+
+ if (mesh->passphrase)
+ g_key_file_set_string(keyfile, mesh->identifier, "Passphrase",
+ mesh->passphrase);
+
+ g_key_file_set_string(keyfile, mesh->identifier, "PeerType",
+ peertype2string(mesh->peer_type));
+
+ __connman_storage_save_service(keyfile, mesh->identifier);
+
+ g_key_file_free(keyfile);
+
+ return 0;
+}
+
+static void reply_pending(struct connman_mesh *mesh, int error)
+{
+ if (!mesh->pending)
+ return;
+
+ connman_dbus_reply_pending(mesh->pending, error, NULL);
+ mesh->pending = NULL;
+}
+
+static void state_changed(struct connman_mesh *mesh)
+{
+ const char *state;
+
+ state = state2string(mesh->state);
+ if (!state)
+ return;
+
+ connman_dbus_property_changed_basic(mesh->path,
+ CONNMAN_MESH_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state);
+}
+
+static void mesh_dhcp_callback(struct connman_ipconfig *ipconfig,
+ struct connman_network *network, bool success, gpointer data)
+{
+ struct connman_mesh *mesh = data;
+ int err;
+
+ if (!success)
+ goto error;
+
+ err = __connman_ipconfig_address_add(ipconfig);
+ if (err < 0)
+ goto error;
+
+ return;
+
+error:
+ connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_FAILURE);
+}
+
+static int mesh_start_dhcp_client(struct connman_mesh *mesh)
+{
+ DBG("");
+
+ __connman_ipconfig_enable(mesh->ipconfig);
+
+ return __connman_mesh_dhcp_start(mesh->ipconfig, mesh_dhcp_callback, mesh);
+}
+
+static void mesh_remove_connected_peer(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct connman_mesh_connected_peer *peer = value;
+
+ DBG("Remove Peer %s", peer->peer_address);
+ g_hash_table_remove(connected_peer_table, key);
+}
+
+static void mesh_remove_disconnected_peer(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct connman_mesh_disconnected_peer *peer = value;
+
+ DBG("Remove Peer %s", peer->peer_address);
+ g_hash_table_remove(disconnected_peer_table, key);
+}
+
+int connman_mesh_peer_set_state(struct connman_mesh *mesh,
+ enum connman_mesh_state new_state)
+{
+ enum connman_mesh_state old_state = mesh->state;
+
+ DBG("mesh peer %s old state %s new state %s", mesh->name,
+ state2string(old_state), state2string(new_state));
+
+ if (old_state == new_state)
+ return -EALREADY;
+
+ switch (new_state) {
+ case CONNMAN_MESH_STATE_UNKNOWN:
+ return -EINVAL;
+ case CONNMAN_MESH_STATE_IDLE:
+ case CONNMAN_MESH_STATE_ASSOCIATION:
+ break;
+ case CONNMAN_MESH_STATE_CONFIGURATION:
+ /* Start Link Local IP Address */
+ mesh_start_dhcp_client(mesh);
+ break;
+ case CONNMAN_MESH_STATE_READY:
+ reply_pending(mesh, 0);
+ mesh->favorite = true;
+ __connman_notifier_connect(CONNMAN_SERVICE_TYPE_MESH);
+
+ /* Set Gate Announce option */
+ if (eth_if_bridged) {
+ connman_inet_set_stp(1);
+ __connman_mesh_netlink_set_gate_announce(nl80211_global,
+ connman_inet_ifindex(mesh_ifname), true,
+ MESH_HWMP_ROOTMODE_RANN);
+ }
+
+ mesh_save(mesh);
+ break;
+ case CONNMAN_MESH_STATE_DISCONNECT:
+ __connman_dhcp_stop(mesh->ipconfig);
+ g_hash_table_foreach(connected_peer_table, mesh_remove_connected_peer,
+ NULL);
+ g_hash_table_foreach(disconnected_peer_table,
+ mesh_remove_disconnected_peer, NULL);
+ __connman_notifier_disconnect(CONNMAN_SERVICE_TYPE_MESH);
+ break;
+ case CONNMAN_MESH_STATE_FAILURE:
+ reply_pending(mesh, ECONNABORTED);
+ break;
+ }
+
+ mesh->state = new_state;
+ state_changed(mesh);
+
+ return 0;
+}
+
+bool connman_mesh_peer_is_connected_state(struct connman_mesh *mesh)
+{
+ switch (mesh->state) {
+ case CONNMAN_MESH_STATE_UNKNOWN:
+ case CONNMAN_MESH_STATE_IDLE:
+ case CONNMAN_MESH_STATE_ASSOCIATION:
+ case CONNMAN_MESH_STATE_CONFIGURATION:
+ case CONNMAN_MESH_STATE_DISCONNECT:
+ case CONNMAN_MESH_STATE_FAILURE:
+ break;
+ case CONNMAN_MESH_STATE_READY:
+ return true;
+ }
+
+ return false;
+}
+
+struct connman_mesh *connman_get_connected_mesh_from_name(char *name)
+{
+ GList *list, *start;
+
+ list = g_hash_table_get_values(mesh_table);
+ start = list;
+ for (; list; list = list->next) {
+ struct connman_mesh *mesh = list->data;
+
+ if (!g_strcmp0(mesh->name, name) &&
+ mesh->state == CONNMAN_MESH_STATE_READY) {
+ g_list_free(start);
+ return mesh;
+ }
+ }
+
+ g_list_free(start);
+
+ return NULL;
+}
+
+struct connman_mesh *connman_get_connecting_mesh_from_name(char *name)
+{
+ GList *list, *start;
+
+ list = g_hash_table_get_values(mesh_table);
+ start = list;
+ for (; list; list = list->next) {
+ struct connman_mesh *mesh = list->data;
+
+ if (!g_strcmp0(mesh->name, name) && is_connecting(mesh)) {
+ g_list_free(start);
+ return mesh;
+ }
+ }
+
+ g_list_free(start);
+
+ return NULL;
+}
+
+static void mesh_append_ethernet(DBusMessageIter *iter, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+
+ if (mesh->ipconfig)
+ __connman_ipconfig_append_ethernet(mesh->ipconfig, iter);
+}
+
+static void mesh_append_ipv4(DBusMessageIter *iter, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+
+ if (!is_connected(mesh))
+ return;
+
+ if (mesh->ipconfig)
+ __connman_ipconfig_append_ipv4(mesh->ipconfig, iter);
+}
+
+static void mesh_append_ipv4config(DBusMessageIter *iter, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+
+ if (mesh->ipconfig)
+ __connman_ipconfig_append_ipv4config(mesh->ipconfig, iter);
+}
+
+static void append_properties(DBusMessageIter *iter, struct connman_mesh *mesh)
+{
+ const char *state = state2string(mesh->state);
+ const char *security = security2string(mesh->security);
+ const char *peer_type = peertype2string(mesh->peer_type);
+ const char *type = "mesh";
+ DBusMessageIter dict;
+
+ connman_dbus_dict_open(iter, &dict);
+
+ connman_dbus_dict_append_basic(&dict, "Type", DBUS_TYPE_STRING, &type);
+ connman_dbus_dict_append_basic(&dict, "Name",
+ DBUS_TYPE_STRING, &mesh->name);
+ connman_dbus_dict_append_basic(&dict, "BSSID",
+ DBUS_TYPE_STRING, &mesh->address);
+ connman_dbus_dict_append_basic(&dict, "State", DBUS_TYPE_STRING, &state);
+ if (security)
+ connman_dbus_dict_append_basic(&dict, "Security",
+ DBUS_TYPE_STRING, &security);
+ connman_dbus_dict_append_basic(&dict, "Frequency",
+ DBUS_TYPE_UINT16, &mesh->frequency);
+ connman_dbus_dict_append_basic(&dict, "Favorite",
+ DBUS_TYPE_BOOLEAN, &mesh->favorite);
+ connman_dbus_dict_append_basic(&dict, "Strength",
+ DBUS_TYPE_BYTE, &mesh->strength);
+ connman_dbus_dict_append_basic(&dict, "PeerType",
+ DBUS_TYPE_STRING, &peer_type);
+ connman_dbus_dict_append_basic(&dict, "DisconnectReason",
+ DBUS_TYPE_INT32, &mesh->disconnect_reason);
+
+ connman_dbus_dict_append_dict(&dict, "Ethernet", mesh_append_ethernet,
+ mesh);
+
+ connman_dbus_dict_append_dict(&dict, "IPv4", mesh_append_ipv4, mesh);
+
+ connman_dbus_dict_append_dict(&dict, "IPv4.Configuration",
+ mesh_append_ipv4config, mesh);
+
+ connman_dbus_dict_close(iter, &dict);
+}
+
+static void append_mesh_peer_struct(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ DBusMessageIter *array = user_data;
+ struct connman_mesh *mesh = value;
+ DBusMessageIter entry;
+
+ DBG("Mesh Peer path %s", mesh->path);
+ dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+ NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+ &mesh->path);
+ append_properties(&entry, mesh);
+ dbus_message_iter_close_container(array, &entry);
+}
+
+void __connman_mesh_peer_list_struct(DBusMessageIter *array)
+{
+ g_hash_table_foreach(mesh_table, append_mesh_peer_struct, array);
+}
+
+static DBusMessage *get_mesh_peer_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct connman_mesh *mesh = data;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &dict);
+ append_properties(&dict, mesh);
+
+ return reply;
+}
+
+static void append_mesh_disconnected_peer_struct(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ DBusMessageIter *array = user_data;
+ struct connman_mesh_disconnected_peer *peer = value;
+ DBusMessageIter entry;
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+ NULL, &entry);
+
+ connman_dbus_dict_open(&entry, &dict);
+
+ connman_dbus_dict_append_basic(&dict, "PeerAddress",
+ DBUS_TYPE_STRING, &peer->peer_address);
+
+ connman_dbus_dict_append_basic(&dict, "DisconnectReason",
+ DBUS_TYPE_INT32, &peer->disconnect_reason);
+
+ connman_dbus_dict_close(&entry, &dict);
+ dbus_message_iter_close_container(array, &entry);
+}
+
+void __connman_mesh_disconnected_peer_list_struct(DBusMessageIter *array)
+{
+ g_hash_table_foreach(disconnected_peer_table,
+ append_mesh_disconnected_peer_struct, array);
+}
+
+static void append_mesh_connected_peer_struct(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ DBusMessageIter *array = user_data;
+ struct connman_mesh_connected_peer *peer = value;
+ DBusMessageIter entry;
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+ NULL, &entry);
+
+ connman_dbus_dict_open(&entry, &dict);
+
+ connman_dbus_dict_append_basic(&dict, "PeerAddress",
+ DBUS_TYPE_STRING, &peer->peer_address);
+
+ connman_dbus_dict_close(&entry, &dict);
+ dbus_message_iter_close_container(array, &entry);
+}
+
+void __connman_mesh_connected_peer_list_struct(DBusMessageIter *array)
+{
+ g_hash_table_foreach(connected_peer_table,
+ append_mesh_connected_peer_struct, array);
+}
+
+int connman_mesh_add_connected_peer(const char *peer_address)
+{
+ struct connman_mesh_connected_peer *peer;
+ struct connman_mesh_connected_peer *temp_peer;
+ struct connman_mesh_disconnected_peer *disconn_peer;
+
+ temp_peer = g_hash_table_lookup(connected_peer_table, peer_address);
+
+ if (temp_peer) {
+ DBG("Mesh Peer %s is already connected", peer_address);
+ return 0;
+ }
+
+ peer = g_malloc0(sizeof(struct connman_mesh_connected_peer));
+ peer->peer_address = g_strdup(peer_address);
+ DBG("Peer %s", peer->peer_address);
+
+ g_hash_table_insert(connected_peer_table, peer->peer_address, peer);
+
+ /* Remove from disconnected Peer Table */
+ disconn_peer = g_hash_table_lookup(disconnected_peer_table, peer_address);
+ if (!disconn_peer) {
+ DBG("Peer %s was never disconnected", peer_address);
+ goto done;
+ }
+
+ g_hash_table_remove(disconnected_peer_table, peer_address);
+done:
+ return 0;
+}
+
+int connman_mesh_remove_connected_peer(const char *peer_address, int reason)
+{
+ struct connman_mesh_connected_peer *peer;
+ struct connman_mesh_disconnected_peer *disconn_peer;
+
+ peer = g_hash_table_lookup(connected_peer_table, peer_address);
+
+ if (!peer) {
+ DBG("Peer %s not connected", peer_address);
+ return 0;
+ }
+
+ g_hash_table_remove(connected_peer_table, peer_address);
+
+ /* Add to Disconnected Peer Table */
+ disconn_peer = g_malloc0(sizeof(struct connman_mesh_disconnected_peer));
+ disconn_peer->peer_address = g_strdup(peer_address);
+ disconn_peer->disconnect_reason = convert_to_disconnect_reason(reason);
+
+ g_hash_table_insert(disconnected_peer_table, disconn_peer->peer_address,
+ disconn_peer);
+
+ DBG("Mesh Peer %s removed due to reason %d", peer_address, reason);
+ return 0;
+}
+
+static void __mesh_change_peer_status_cb(int result, void *user_data)
+{
+ struct connman_mesh_change_peer_data *data = user_data;
+
+ DBG("Status %d Peer Address %s result %d", data->status, data->peer_address,
+ result);
+
+ connman_dbus_reply_pending(data->pending, -result, NULL);
+
+ data->pending = NULL;
+ g_free(data->peer_address);
+ g_free(data);
+}
+
+int __connman_mesh_change_peer_status(DBusMessage *msg,
+ const char *peer_address,
+ enum connman_mesh_peer_status status)
+{
+ struct connman_mesh_connected_peer *conn_peer;
+ struct connman_mesh_disconnected_peer *disconn_peer;
+ int err = -ENOTSUP;
+ struct connman_mesh_change_peer_data *data;
+
+ switch (status) {
+ case CONNMAN_MESH_PEER_ADD:
+ conn_peer = g_hash_table_lookup(connected_peer_table, peer_address);
+
+ if (conn_peer) {
+ DBG("Peer %s already connected", peer_address);
+ return -EEXIST;
+ }
+
+ break;
+
+ case CONNMAN_MESH_PEER_REMOVE:
+ disconn_peer = g_hash_table_lookup(disconnected_peer_table,
+ peer_address);
+
+ if (disconn_peer) {
+ DBG("Peer %s already disconnected", peer_address);
+ return -EEXIST;
+ }
+
+ break;
+
+ default:
+ DBG("Invalid Status type");
+ return err;
+ }
+
+ if (mesh_driver->change_peer_status) {
+ data = g_try_malloc0(sizeof(struct connman_mesh_disconnected_peer));
+ if (data == NULL) {
+ DBG("Memory allocation failed");
+ return -ENOMEM;
+ }
+
+ data->pending = dbus_message_ref(msg);
+ data->peer_address = g_strdup(peer_address);
+ data->status = status;
+
+ err = mesh_driver->change_peer_status(peer_address, status,
+ __mesh_change_peer_status_cb, data);
+
+ if (err < 0) {
+ dbus_message_unref(data->pending);
+ g_free(data->peer_address);
+ g_free(data);
+ }
+ }
+
+ return err;
+}
+
+static int mesh_peer_connect(struct connman_mesh *mesh)
+{
+ int err = -ENOTSUP;
+ if (mesh_driver->connect)
+ err = mesh_driver->connect(mesh);
+
+ /* Reset Disconnect Reason */
+ mesh->disconnect_reason = CONNMAN_MESH_REASON_UNKNOWN;
+ return err;
+}
+
+static DBusMessage *connect_mesh_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+ int err;
+
+ DBG("mesh %p", mesh);
+
+ if (mesh->state == CONNMAN_MESH_STATE_READY) {
+ DBG("mesh %s already connected", mesh->name);
+ return __connman_error_already_exists(msg);
+ }
+
+ if (mesh->pending)
+ return __connman_error_in_progress(msg);
+
+ mesh->pending = dbus_message_ref(msg);
+
+ err = mesh_peer_connect(mesh);
+ if (err == -EINPROGRESS)
+ return NULL;
+
+ if (err < 0) {
+ dbus_message_unref(mesh->pending);
+ mesh->pending = NULL;
+ return __connman_error_failed(msg, -err);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void auto_connect_mesh_peer(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ bool *conn_started = user_data;
+ struct connman_mesh *mesh = value;
+ int err;
+
+ if (*conn_started)
+ return;
+
+ if (!mesh->favorite || mesh->state != CONNMAN_MESH_STATE_IDLE)
+ return;
+
+ err = mesh_peer_connect(mesh);
+ if (err == -EINPROGRESS)
+ *conn_started = 1;
+}
+
+static gboolean run_mesh_auto_connect(gpointer data)
+{
+ bool conn_started;
+
+ mesh_autoconnect_timeout = 0;
+ DBG("");
+
+ conn_started = false;
+ g_hash_table_foreach(mesh_table, auto_connect_mesh_peer, &conn_started);
+ return FALSE;
+}
+
+void __connman_mesh_auto_connect(void)
+{
+ DBG("");
+
+ if (mesh_autoconnect_timeout != 0)
+ return;
+
+ mesh_autoconnect_timeout = g_idle_add(run_mesh_auto_connect, NULL);
+}
+
+static void mesh_peer_up(struct connman_ipconfig *ipconfig, const char *ifname)
+{
+ DBG("%s up", ifname);
+}
+
+static void mesh_peer_down(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ DBG("%s down", ifname);
+}
+
+static void mesh_peer_lower_up(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ DBG("%s lower up", ifname);
+}
+
+static void mesh_peer_lower_down(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ DBG("%s lower down", ifname);
+}
+
+static void mesh_peer_ip_bound(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ struct connman_mesh *mesh = __connman_ipconfig_get_data(ipconfig);
+
+ DBG("%s ip bound", ifname);
+
+ connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_READY);
+}
+
+static void mesh_peer_ip_release(struct connman_ipconfig *ipconfig,
+ const char *ifname)
+{
+ DBG("%s ip release", ifname);
+}
+
+static const struct connman_ipconfig_ops mesh_peer_ip_ops = {
+ .up = mesh_peer_up,
+ .down = mesh_peer_down,
+ .lower_up = mesh_peer_lower_up,
+ .lower_down = mesh_peer_lower_down,
+ .ip_bound = mesh_peer_ip_bound,
+ .ip_release = mesh_peer_ip_release,
+ .route_set = NULL,
+ .route_unset = NULL,
+};
+
+static struct connman_ipconfig *create_ipconfig(int index, void *user_data)
+{
+ struct connman_ipconfig *ipconfig;
+
+ ipconfig = __connman_ipconfig_create(index,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+ if (!ipconfig)
+ return NULL;
+
+ __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+ __connman_ipconfig_set_data(ipconfig, user_data);
+ __connman_ipconfig_set_ops(ipconfig, &mesh_peer_ip_ops);
+
+ return ipconfig;
+}
+
+static int __connman_mesh_peer_disconnect(struct connman_mesh *mesh)
+{
+ int err;
+
+ reply_pending(mesh, ECONNABORTED);
+
+ if (!is_connected(mesh) && !is_connecting(mesh))
+ return -ENOTCONN;
+
+ err = mesh_driver->disconnect(mesh);
+ if (err < 0 && err != -EINPROGRESS)
+ return err;
+
+ return err;
+}
+
+static DBusMessage *disconnect_mesh_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+ int err;
+
+ DBG("mesh %p", mesh);
+ err = __connman_mesh_peer_disconnect(mesh);
+ if (err < 0 && err != -EINPROGRESS)
+ return __connman_error_failed(msg, -err);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static bool __connman_mesh_peer_remove(struct connman_mesh *mesh)
+{
+ if (!mesh->favorite)
+ return false;
+
+ __connman_mesh_peer_disconnect(mesh);
+
+ mesh->favorite = false;
+
+ __connman_storage_remove_service(mesh->identifier);
+
+ return true;
+}
+
+static DBusMessage *remove_mesh_peer(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+
+ DBG("mesh %p", mesh);
+
+ if (!__connman_mesh_peer_remove(mesh))
+ return __connman_error_not_supported(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *set_mesh_peer_property(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_mesh *mesh = user_data;
+ DBusMessageIter iter, value;
+ const char *name;
+ int type;
+
+ DBG("mesh %p", mesh);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ 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, "Passphrase")) {
+ char *passphrase;
+
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value, &passphrase);
+
+ connman_mesh_set_passphrase(mesh, passphrase);
+ } else {
+ DBG("Invalid Property %s", name);
+ return __connman_error_invalid_property(msg);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable mesh_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_mesh_peer_properties) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_mesh_peer) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_mesh_peer) },
+ { GDBUS_METHOD("Remove", NULL, NULL, remove_mesh_peer) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, set_mesh_peer_property) },
+ { },
+};
+
+static const GDBusSignalTable mesh_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { },
+};
+
+int connman_mesh_register(struct connman_mesh *mesh)
+{
+ struct connman_mesh *temp;
+ DBG("mesh %p", mesh);
+
+ if (mesh->path)
+ return -EALREADY;
+
+ mesh->path = g_strdup_printf("%s/mesh/%s", CONNMAN_PATH,
+ mesh->identifier);
+ DBG("path %s", mesh->path);
+
+ temp = g_hash_table_lookup(mesh_table, mesh->path);
+ if (temp) {
+ DBG("mesh path %s already exists", mesh->path);
+
+ if (mesh->frequency != temp->frequency) {
+ DBG("Update frequency for mesh network %s", mesh->name);
+ connman_mesh_set_frequency(temp, mesh->frequency);
+ }
+
+ mesh_free(mesh);
+ return -EALREADY;
+ }
+
+ if (mesh->br_index > 0)
+ mesh->ipconfig = create_ipconfig(mesh->br_index, mesh);
+ else
+ mesh->ipconfig = create_ipconfig(mesh->index, mesh);
+
+ if (!mesh->ipconfig)
+ return -ENOMEM;
+
+ g_hash_table_insert(mesh_table, mesh->path, mesh);
+
+ mesh_load(mesh);
+
+ g_dbus_register_interface(connection, mesh->path,
+ CONNMAN_MESH_INTERFACE,
+ mesh_methods, mesh_signals,
+ NULL, mesh, NULL);
+ mesh->registered = true;
+ return 0;
+}
+
+void connman_mesh_unregister(struct connman_mesh *mesh)
+{
+ DBG("mesh %p", mesh);
+
+ if (!mesh->path || !mesh->registered)
+ return;
+
+ g_dbus_unregister_interface(connection, mesh->path,
+ CONNMAN_MESH_INTERFACE);
+ mesh->registered = false;
+
+ g_hash_table_remove(mesh_table, mesh->path);
+}
+
+struct connman_mesh *connman_mesh_get(const char *interface_addr,
+ const char *identifier)
+{
+ char *ident = g_strdup_printf("%s/mesh/mesh_%s_%s", CONNMAN_PATH,
+ interface_addr, identifier);
+ struct connman_mesh *mesh;
+
+ mesh = g_hash_table_lookup(mesh_table, ident);
+ g_free(ident);
+
+ return mesh;
+}
+
+int connman_mesh_driver_register(struct connman_mesh_driver *driver)
+{
+ if (mesh_driver && mesh_driver != driver)
+ return -EINVAL;
+
+ mesh_driver = driver;
+
+ return 0;
+}
+
+void connman_mesh_driver_unregister(struct connman_mesh_driver *driver)
+{
+ if (mesh_driver != driver)
+ return;
+
+ mesh_driver = NULL;
+}
+
+int connman_mesh_eth_driver_register(struct connman_mesh_eth_driver *driver)
+{
+ if (mesh_eth_driver && mesh_eth_driver != driver)
+ return -EINVAL;
+
+ mesh_eth_driver = driver;
+
+ return 0;
+}
+
+void connman_mesh_eth_driver_unregister(struct connman_mesh_eth_driver *driver)
+{
+ if (mesh_eth_driver != driver)
+ return;
+
+ mesh_eth_driver = NULL;
+}
+
+int __connman_mesh_init(void)
+{
+ DBG("");
+
+ connection = connman_dbus_get_connection();
+
+ mesh_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, mesh_free);
+
+ connected_peer_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ mesh_connected_peer_free);
+
+ disconnected_peer_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, mesh_disconnected_peer_free);
+
+ nl80211_global = __connman_mesh_nl80211_global_init();
+ return 0;
+}
+
+void __connman_mesh_cleanup(void)
+{
+ DBG("");
+
+ __connman_mesh_nl80211_global_deinit(nl80211_global);
+ g_hash_table_destroy(mesh_table);
+ g_hash_table_destroy(connected_peer_table);
+ g_hash_table_destroy(disconnected_peer_table);
+ dbus_connection_unref(connection);
+}
diff --git a/src/notifier.c b/src/notifier.c
index 5ba53242..7c3d0311 100755..100644
--- a/src/notifier.c
+++ b/src/notifier.c
@@ -153,6 +153,9 @@ void __connman_notifier_connect(enum connman_service_type type)
case CONNMAN_SERVICE_TYPE_BLUETOOTH:
case CONNMAN_SERVICE_TYPE_CELLULAR:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
@@ -200,6 +203,9 @@ void __connman_notifier_disconnect(enum connman_service_type type)
case CONNMAN_SERVICE_TYPE_BLUETOOTH:
case CONNMAN_SERVICE_TYPE_CELLULAR:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
diff --git a/src/rtnl.c b/src/rtnl.c
index ac29f313..d7332880 100755..100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -462,6 +462,20 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
if (!extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats))
return;
+#if defined TIZEN_EXT_WIFI_MESH
+ /* Do not accept Wi-Fi Mesh interface */
+ if (g_strrstr(ifname, "mesh") != NULL) {
+ DBG("Newlink event for Wi-Fi Mesh interface ignored");
+ return;
+ }
+
+ /* Do not accept Wi-Fi WLAN1 interface "dedicated for softAP */
+ if (!g_strcmp0(ifname, "wlan1")) {
+ DBG("Newlink event for Wi-Fi WLAN1 interface ignored");
+ return;
+ }
+#endif
+
#if defined TIZEN_EXT
/* Do not accept Wi-Fi P2P interface */
if (g_strrstr(ifname, "p2p") != NULL) {
diff --git a/src/service.c b/src/service.c
index d7a70395..d13aafb6 100755..100644
--- a/src/service.c
+++ b/src/service.c
@@ -298,6 +298,10 @@ const char *__connman_service_type2string(enum connman_service_type type)
return "gadget";
case CONNMAN_SERVICE_TYPE_P2P:
return "p2p";
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+ return "mesh";
+#endif
}
return NULL;
@@ -596,6 +600,9 @@ int __connman_service_load_modifiable(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
case CONNMAN_SERVICE_TYPE_VPN:
set_split_routing(service, g_key_file_get_boolean(keyfile,
@@ -671,6 +678,9 @@ static int service_load(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
case CONNMAN_SERVICE_TYPE_VPN:
set_split_routing(service, g_key_file_get_boolean(keyfile,
@@ -933,6 +943,9 @@ static int service_save(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
case CONNMAN_SERVICE_TYPE_VPN:
g_key_file_set_boolean(keyfile, service->identifier,
@@ -3427,6 +3440,9 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
case CONNMAN_SERVICE_TYPE_CELLULAR:
val = service->roaming;
@@ -5046,6 +5062,9 @@ void __connman_service_set_active_session(bool enable, GSList *list)
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
@@ -5181,6 +5200,12 @@ static bool auto_connect_service(GList *services,
ignore[CONNMAN_SERVICE_TYPE_VPN] = true;
+#if defined TIZEN_EXT_WIFI_MESH
+ /* Don't auto connect wifi if mesh interface is created */
+ if (connman_mesh_is_interface_created())
+ ignore[CONNMAN_SERVICE_TYPE_WIFI] = true;
+#endif
+
for (list = services; list; list = list->next) {
service = list->data;
@@ -8060,6 +8085,9 @@ static int service_connect(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
return -EINVAL;
case CONNMAN_SERVICE_TYPE_ETHERNET:
case CONNMAN_SERVICE_TYPE_GADGET:
@@ -8203,6 +8231,9 @@ int __connman_service_connect(struct connman_service *service,
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
return -EINVAL;
case CONNMAN_SERVICE_TYPE_ETHERNET:
@@ -9025,6 +9056,9 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_CELLULAR:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
case CONNMAN_SERVICE_TYPE_ETHERNET:
service->favorite = true;
@@ -9056,6 +9090,9 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
case CONNMAN_SERVICE_TYPE_UNKNOWN:
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
case CONNMAN_SERVICE_TYPE_GADGET:
diff --git a/src/session.c b/src/session.c
index 9e3c5594..26cbf878 100755..100644
--- a/src/session.c
+++ b/src/session.c
@@ -194,6 +194,9 @@ static char *service2bearer(enum connman_service_type type)
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
case CONNMAN_SERVICE_TYPE_UNKNOWN:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
return "";
}
diff --git a/src/technology.c b/src/technology.c
index 57ab8e14..6604599e 100755..100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -95,6 +95,9 @@ struct connman_technology {
bool softblocked;
bool hardblocked;
bool dbus_registered;
+#if defined TIZEN_EXT_WIFI_MESH
+ DBusMessage *mesh_dbus_msg;
+#endif
};
static GSList *driver_list = NULL;
@@ -165,6 +168,10 @@ static const char *get_name(enum connman_service_type type)
return "Cellular";
case CONNMAN_SERVICE_TYPE_P2P:
return "P2P";
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+ return "Mesh";
+#endif
}
return NULL;
@@ -670,6 +677,11 @@ static int technology_affect_devices(struct connman_technology *technology,
return 0;
}
+#if defined TIZEN_EXT_WIFI_MESH
+ if (technology->type == CONNMAN_SERVICE_TYPE_MESH)
+ return 0;
+#endif
+
for (list = technology->device_list; list; list = list->next) {
struct connman_device *device = list->data;
@@ -1433,6 +1445,471 @@ static DBusMessage *get_scan_state(DBusConnection *conn, DBusMessage *msg, void
}
#endif
+#if defined TIZEN_EXT_WIFI_MESH
+bool __connman_technology_get_connected(enum connman_service_type type)
+{
+ struct connman_technology *technology;
+
+ technology = technology_find(type);
+
+ if (!technology)
+ return false;
+
+ return technology->connected;
+}
+
+void __connman_technology_mesh_interface_create_finished(
+ enum connman_service_type type, bool success,
+ const char *error)
+{
+ DBusMessage *reply;
+ struct connman_technology *technology;
+ DBusMessage *msg;
+ technology = technology_find(type);
+
+ DBG("technology %p success %d", technology, success);
+
+ if (!technology)
+ return;
+
+ msg = technology->mesh_dbus_msg;
+ if (!msg) {
+ DBG("No pending dbus message");
+ return;
+ }
+
+ if (success) {
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ __connman_device_request_scan(technology->type);
+ } else
+ reply = g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+ ".MeshInterfaceAddFailed", "%s", error);
+ g_dbus_send_message(connection, reply);
+ dbus_message_unref(msg);
+ technology->mesh_dbus_msg = NULL;
+}
+
+void __connman_technology_mesh_interface_remove_finished(
+ enum connman_service_type type, bool success)
+{
+ DBusMessage *reply;
+ struct connman_technology *technology;
+ DBusMessage *msg;
+ technology = technology_find(type);
+
+ DBG("technology %p success %d", technology, success);
+
+ if (!technology || !technology->mesh_dbus_msg)
+ return;
+
+ msg = technology->mesh_dbus_msg;
+ if (!msg) {
+ DBG("No pending dbus message");
+ return;
+ }
+
+ if (success)
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ else
+ reply = __connman_error_failed(msg, EINVAL);
+ g_dbus_send_message(connection, reply);
+ dbus_message_unref(msg);
+ technology->mesh_dbus_msg = NULL;
+}
+
+void __connman_technology_notify_abort_scan(enum connman_service_type type,
+ int result)
+{
+ DBusMessage *reply;
+ struct connman_technology *technology;
+ DBusMessage *msg;
+ technology = technology_find(type);
+
+ DBG("technology %p result %d", technology, result);
+
+ if (!technology || !technology->mesh_dbus_msg)
+ return;
+
+ msg = technology->mesh_dbus_msg;
+ if (!msg) {
+ DBG("No pending dbus message");
+ return;
+ }
+
+ if (result < 0)
+ reply = __connman_error_scan_abort_failed(msg);
+ else
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ g_dbus_send_message(connection, reply);
+ dbus_message_unref(msg);
+ technology->mesh_dbus_msg = NULL;
+}
+
+static DBusMessage *mesh_commands(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct connman_technology *technology = data;
+ DBusMessageIter iter, value, dict;
+ const char *cmd = NULL, *ifname = NULL, *parent_ifname = NULL;
+ int err;
+
+ DBG("conn %p", conn);
+
+ if (technology->type != CONNMAN_SERVICE_TYPE_MESH)
+ return __connman_error_invalid_arguments(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ 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, &cmd);
+ 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);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
+ return __connman_error_invalid_arguments(msg);
+
+ DBG("Mesh Command %s", cmd);
+ if (g_str_equal(cmd, "MeshInterfaceAdd")) {
+ dbus_message_iter_recurse(&value, &dict);
+ const char *bridge_ifname = NULL;
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value2;
+ const char *key;
+ int type;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_recurse(&entry, &value2);
+
+ type = dbus_message_iter_get_arg_type(&value2);
+
+ if (g_str_equal(key, "Ifname")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &ifname);
+ } else if (g_str_equal(key, "ParentIfname")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &parent_ifname);
+ } else if (g_str_equal(key, "BridgeIfname")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &bridge_ifname);
+ }
+ dbus_message_iter_next(&dict);
+ }
+ DBG("Mesh Ifname %s parent %s bridge %s", ifname, parent_ifname,
+ bridge_ifname ? bridge_ifname : "NULL");
+ err = __connman_mesh_add_virtual_interface(ifname, parent_ifname,
+ bridge_ifname);
+
+ if (err != 0) {
+ DBG("Failed to add virtual mesh interface");
+ return __connman_error_failed(msg, -err);
+ }
+
+ DBG("Successfully added virtual mesh interface");
+
+ dbus_message_ref(msg);
+ technology->mesh_dbus_msg = msg;
+
+ } else if (g_str_equal(cmd, "MeshInterfaceRemove")) {
+ dbus_message_iter_recurse(&value, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value2;
+ const char *key;
+ int type;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_recurse(&entry, &value2);
+
+ type = dbus_message_iter_get_arg_type(&value2);
+
+ if (g_str_equal(key, "Ifname")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &ifname);
+ }
+ dbus_message_iter_next(&dict);
+ }
+ DBG("Mesh Ifname %s", ifname);
+ err = __connman_mesh_remove_virtual_interface(ifname);
+
+ if (err != 0) {
+ DBG("Failed to remove virtual mesh interface");
+ return __connman_error_failed(msg, -err);
+ }
+
+ DBG("Successfully removed virtual mesh interface");
+
+ dbus_message_ref(msg);
+ technology->mesh_dbus_msg = msg;
+
+ } else if (g_str_equal(cmd, "MeshCreateNetwork")) {
+ struct connman_mesh *connman_mesh;
+ const char *name = NULL;
+ const char *sec_type = NULL;
+ const char *mesh_ifname = NULL;
+ char *identifier, *group, *address;
+ unsigned int freq = 0;
+ unsigned int ieee80211w = 0;
+ GString *str;
+ int i;
+ dbus_message_iter_recurse(&value, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value2;
+ const char *key;
+ int type;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_recurse(&entry, &value2);
+
+ type = dbus_message_iter_get_arg_type(&value2);
+
+ if (g_str_equal(key, "Name")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &name);
+ } else if (g_str_equal(key, "Frequency")) {
+ if (type != DBUS_TYPE_UINT16)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &freq);
+ } else if (g_str_equal(key, "Security")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &sec_type);
+ } else if (g_str_equal(key, "Pmf")) {
+ if (type != DBUS_TYPE_UINT16)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &ieee80211w);
+ }
+ dbus_message_iter_next(&dict);
+ }
+
+ if (name == NULL || sec_type == NULL || freq == 0)
+ return __connman_error_invalid_arguments(msg);
+
+ DBG("Name %s Frequency %d Security type %s Pmf %u",
+ name, freq, sec_type, ieee80211w);
+
+ if (g_strcmp0(sec_type, "none") != 0 &&
+ g_strcmp0(sec_type, "sae") != 0) {
+ DBG("Unsupported security");
+ return __connman_error_invalid_arguments(msg);
+ }
+
+ mesh_ifname = connman_mesh_get_interface_name();
+
+ if (!connman_mesh_is_interface_created()) {
+ DBG("Mesh interface doesn't exists");
+ return __connman_error_invalid_command(msg);
+ }
+
+ str = g_string_sized_new((strlen(name) * 2) + 24);
+
+ for (i = 0; name[i]; i++)
+ g_string_append_printf(str, "%02x", name[i]);
+
+ g_string_append_printf(str, "_mesh");
+
+ if (g_strcmp0(sec_type, "none") == 0)
+ g_string_append_printf(str, "_none");
+ else if (g_strcmp0(sec_type, "sae") == 0)
+ g_string_append_printf(str, "_sae");
+
+ group = g_string_free(str, FALSE);
+
+ identifier = connman_inet_ifaddr(mesh_ifname);
+ address = connman_inet_ifname2addr(mesh_ifname);
+
+ connman_mesh = connman_mesh_create(identifier, group);
+ connman_mesh_set_name(connman_mesh, name);
+ connman_mesh_set_address(connman_mesh, address);
+ connman_mesh_set_security(connman_mesh, sec_type);
+ connman_mesh_set_frequency(connman_mesh, freq);
+ connman_mesh_set_index(connman_mesh, connman_inet_ifindex(mesh_ifname));
+ connman_mesh_set_peer_type(connman_mesh,
+ CONNMAN_MESH_PEER_TYPE_CREATED);
+ connman_mesh_set_ieee80211w(connman_mesh, ieee80211w);
+
+ connman_mesh_register(connman_mesh);
+ g_free(group);
+ g_free(identifier);
+ g_free(address);
+ DBG("Successfully Created Mesh Network");
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ } else if (g_str_equal(cmd, "AbortScan")) {
+ DBG("Abort Scan method");
+ err = __connman_device_abort_scan(technology->type);
+ if (err != 0) {
+ DBG("Failed to abort scan");
+ return __connman_error_failed(msg, -err);
+ }
+
+ DBG("Successfully requested to abort scan");
+ dbus_message_ref(msg);
+ technology->mesh_dbus_msg = msg;
+
+ } else if (g_str_equal(cmd, "MeshSpecificScan")) {
+ const char *name = NULL;
+ unsigned int freq = 0;
+ dbus_message_iter_recurse(&value, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value2;
+ const char *key;
+ int type;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_recurse(&entry, &value2);
+
+ type = dbus_message_iter_get_arg_type(&value2);
+
+ if (g_str_equal(key, "Name")) {
+ if (type != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &name);
+ } else if (g_str_equal(key, "Frequency")) {
+ if (type != DBUS_TYPE_UINT16)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &freq);
+ }
+ dbus_message_iter_next(&dict);
+ }
+
+ DBG("MeshID %s Frequency %d sender %s", name, freq,
+ dbus_message_get_sender(msg));
+
+ dbus_message_ref(msg);
+ technology->scan_pending =
+ g_slist_prepend(technology->scan_pending, msg);
+
+ err = __connman_device_request_mesh_specific_scan(technology->type,
+ name, freq);
+ if (err < 0)
+ reply_scan_pending(technology, err);
+ else
+ DBG("Successfully requested to scan specific Mesh Network");
+
+ } else if (g_str_equal(cmd, "SetMeshGate")) {
+ unsigned int hwmp_rootmode = 0;
+ bool gate_announce = false;
+ unsigned int stp = 0;
+ int err;
+ dbus_message_iter_recurse(&value, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value2;
+ const char *key;
+ int type;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_recurse(&entry, &value2);
+
+ type = dbus_message_iter_get_arg_type(&value2);
+
+ if (g_str_equal(key, "GateAnnounce")) {
+ if (type != DBUS_TYPE_BOOLEAN)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &gate_announce);
+ } else if (g_str_equal(key, "HWMPRootMode")) {
+ if (type != DBUS_TYPE_UINT16)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &hwmp_rootmode);
+ } else if (g_str_equal(key, "STP")) {
+ if (type != DBUS_TYPE_UINT16)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value2, &stp);
+ }
+ dbus_message_iter_next(&dict);
+ }
+
+ DBG("GateAnnounce %d HWMPRootMode %d STP %d sender %s",
+ gate_announce, hwmp_rootmode, stp, dbus_message_get_sender(msg));
+
+ err = __connman_mesh_set_stp_gate_announce(gate_announce,
+ hwmp_rootmode,
+ stp);
+
+ if (err < 0)
+ return __connman_error_failed(msg, -err);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ } else
+ return __connman_error_invalid_command(msg);
+ return NULL;
+}
+#endif
+
static const GDBusMethodTable technology_methods[] = {
{ GDBUS_DEPRECATED_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -1447,6 +1924,11 @@ static const GDBusMethodTable technology_methods[] = {
{ GDBUS_METHOD("GetScanState", NULL, GDBUS_ARGS({ "scan_state", "a{sv}" }),
get_scan_state) },
#endif
+#if defined TIZEN_EXT_WIFI_MESH
+ { GDBUS_ASYNC_METHOD("MeshCommands",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, mesh_commands) },
+#endif
{ },
};
@@ -1557,7 +2039,12 @@ static struct connman_technology *technology_get(enum connman_service_type type)
technology = technology_find(type);
if (technology) {
+#if defined TIZEN_EXT_WIFI_MESH
+ if (type != CONNMAN_SERVICE_TYPE_P2P &&
+ type != CONNMAN_SERVICE_TYPE_MESH)
+#else
if (type != CONNMAN_SERVICE_TYPE_P2P)
+#endif
__sync_fetch_and_add(&technology->refcount, 1);
return technology;
}
@@ -1588,6 +2075,16 @@ static struct connman_technology *technology_get(enum connman_service_type type)
technology->path = g_strdup_printf("%s/technology/%s",
CONNMAN_PATH, str);
+#if defined TIZEN_EXT_WIFI_MESH
+ if (type == CONNMAN_SERVICE_TYPE_MESH) {
+ struct connman_technology *wifi;
+
+ wifi = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+ if (wifi)
+ technology->enabled = wifi->enabled;
+ }
+#endif
+
technology_load(technology);
technology_list = g_slist_prepend(technology_list, technology);
technology->driver_list = tech_drivers;
@@ -1666,6 +2163,13 @@ exist:
return -ENOMEM;
}
+#if defined TIZEN_EXT_WIFI_MESH
+ if (driver->type == CONNMAN_SERVICE_TYPE_MESH) {
+ if (!technology_get(CONNMAN_SERVICE_TYPE_MESH))
+ return -ENOMEM;
+ }
+#endif
+
return 0;
}
@@ -1703,6 +2207,13 @@ void connman_technology_driver_unregister(struct connman_technology_driver *driv
if (technology)
technology_put(technology);
}
+#if defined TIZEN_EXT_WIFI_MESH
+ if (driver->type == CONNMAN_SERVICE_TYPE_MESH) {
+ technology = technology_find(CONNMAN_SERVICE_TYPE_MESH);
+ if (technology)
+ technology_put(technology);
+ }
+#endif
}
void __connman_technology_add_interface(enum connman_service_type type,
@@ -1725,6 +2236,9 @@ void __connman_technology_add_interface(enum connman_service_type type,
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_GADGET:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
@@ -1776,6 +2290,9 @@ void __connman_technology_remove_interface(enum connman_service_type type,
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_GADGET:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
break;
}
@@ -1971,6 +2488,15 @@ int __connman_technology_set_offlinemode(bool offlinemode)
return err;
}
+#if defined TIZEN_EXT_WIFI_MESH
+static gboolean __add_ethernet_to_bridge(gpointer data)
+{
+ DBG("");
+ __connman_mesh_add_ethernet_to_bridge();
+ return FALSE;
+}
+#endif
+
void __connman_technology_set_connected(enum connman_service_type type,
bool connected)
{
@@ -1985,6 +2511,11 @@ void __connman_technology_set_connected(enum connman_service_type type,
technology->connected = connected;
+#if defined TIZEN_EXT_WIFI_MESH
+ if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET && connected)
+ g_idle_add(__add_ethernet_to_bridge, NULL);
+#endif
+
val = connected;
connman_dbus_property_changed_basic(technology->path,
CONNMAN_TECHNOLOGY_INTERFACE, "Connected",
diff --git a/src/wispr.c b/src/wispr.c
index adf62303..a2df55a0 100755..100644
--- a/src/wispr.c
+++ b/src/wispr.c
@@ -849,6 +849,9 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
return -EOPNOTSUPP;
}