diff options
author | Nishant Chaprana <n.chaprana@samsung.com> | 2018-08-10 16:41:37 +0530 |
---|---|---|
committer | Nishant Chaprana <n.chaprana@samsung.com> | 2018-08-24 10:20:11 +0530 |
commit | d04bfa0350781ebfb8cbb2e64fabdfb2f36cd302 (patch) | |
tree | 750ce9bb53579e1ee54d91aa3a6c976e55f1a8db /src | |
parent | 037a4a5e75f3fc0845a924bb94addc2549550e63 (diff) | |
download | connman-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.c | 3 | ||||
-rw-r--r--[-rwxr-xr-x] | src/connman.h | 28 | ||||
-rw-r--r--[-rwxr-xr-x] | src/device.c | 110 | ||||
-rw-r--r--[-rwxr-xr-x] | src/dhcp.c | 35 | ||||
-rw-r--r--[-rwxr-xr-x] | src/error.c | 14 | ||||
-rw-r--r--[-rwxr-xr-x] | src/inet.c | 93 | ||||
-rw-r--r--[-rwxr-xr-x] | src/main.c | 6 | ||||
-rw-r--r--[-rwxr-xr-x] | src/manager.c | 123 | ||||
-rw-r--r-- | src/mesh-netlink.c | 187 | ||||
-rw-r--r-- | src/mesh.c | 1660 | ||||
-rw-r--r--[-rwxr-xr-x] | src/notifier.c | 6 | ||||
-rw-r--r--[-rwxr-xr-x] | src/rtnl.c | 14 | ||||
-rw-r--r--[-rwxr-xr-x] | src/service.c | 37 | ||||
-rw-r--r--[-rwxr-xr-x] | src/session.c | 3 | ||||
-rw-r--r--[-rwxr-xr-x] | src/technology.c | 531 | ||||
-rw-r--r--[-rwxr-xr-x] | src/wispr.c | 3 |
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(ð, &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(ð, &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; } |