From d04bfa0350781ebfb8cbb2e64fabdfb2f36cd302 Mon Sep 17 00:00:00 2001 From: Nishant Chaprana Date: Fri, 10 Aug 2018 16:41:37 +0530 Subject: [PATCH] [connman] Added Tizen Wi-Fi Mesh Change-Id: Iec2ec99fb7d4fc6d9c0539e5a9e7c903e1969580 Signed-off-by: Nishant Chaprana --- Makefile.am | 24 + client/commands.c | 618 +++++++++++++++++ client/dbus_helpers.c | 49 ++ client/dbus_helpers.h | 9 + client/mesh.c | 166 +++++ client/mesh.h | 40 ++ configure.ac | 5 + gsupplicant/gsupplicant.h | 53 ++ gsupplicant/supplicant.c | 526 +++++++++++++- include/dbus.h | 3 + include/device.h | 7 + include/inet.h | 5 + include/mesh-netlink.h | 58 ++ include/mesh.h | 155 +++++ include/service.h | 7 + packaging/connman.spec | 21 +- plugins/ethernet.c | 65 ++ plugins/wifi.c | 805 +++++++++++++++++++++- src/config.c | 3 + src/connman.h | 28 + src/device.c | 110 +++ src/dhcp.c | 35 + src/error.c | 14 + src/inet.c | 93 ++- src/main.c | 6 + src/manager.c | 123 ++++ src/mesh-netlink.c | 187 +++++ src/mesh.c | 1660 +++++++++++++++++++++++++++++++++++++++++++++ src/notifier.c | 6 + src/rtnl.c | 14 + src/service.c | 37 + src/session.c | 3 + src/technology.c | 531 +++++++++++++++ src/wispr.c | 3 + 34 files changed, 5437 insertions(+), 32 deletions(-) mode change 100755 => 100644 Makefile.am mode change 100755 => 100644 client/commands.c mode change 100755 => 100644 client/dbus_helpers.c mode change 100755 => 100644 client/dbus_helpers.h create mode 100644 client/mesh.c create mode 100644 client/mesh.h mode change 100755 => 100644 gsupplicant/gsupplicant.h mode change 100755 => 100644 include/dbus.h mode change 100755 => 100644 include/device.h mode change 100755 => 100644 include/inet.h create mode 100644 include/mesh-netlink.h create mode 100644 include/mesh.h mode change 100755 => 100644 include/service.h mode change 100755 => 100644 packaging/connman.spec mode change 100755 => 100644 plugins/ethernet.c mode change 100755 => 100644 plugins/wifi.c mode change 100755 => 100644 src/config.c mode change 100755 => 100644 src/connman.h mode change 100755 => 100644 src/device.c mode change 100755 => 100644 src/dhcp.c mode change 100755 => 100644 src/error.c mode change 100755 => 100644 src/inet.c mode change 100755 => 100644 src/main.c mode change 100755 => 100644 src/manager.c create mode 100644 src/mesh-netlink.c create mode 100644 src/mesh.c mode change 100755 => 100644 src/notifier.c mode change 100755 => 100644 src/rtnl.c mode change 100755 => 100644 src/service.c mode change 100755 => 100644 src/session.c mode change 100755 => 100644 src/technology.c mode change 100755 => 100644 src/wispr.c diff --git a/Makefile.am b/Makefile.am old mode 100755 new mode 100644 index 76c5419..94c6e9d --- a/Makefile.am +++ b/Makefile.am @@ -13,6 +13,10 @@ include_HEADERS = include/log.h include/plugin.h \ include/session.h include/ipaddress.h include/agent.h \ include/inotify.h include/peer.h include/machine.h +if TIZEN_EXT_WIFI_MESH +include_HEADERS += include/mesh.h include/mesh-netlink.h +endif + nodist_include_HEADERS = include/version.h noinst_HEADERS = include/rtnl.h include/task.h \ @@ -129,10 +133,18 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) \ src/inotify.c src/ipv6pd.c src/peer.c \ src/peer_service.c src/machine.c src/util.c +if TIZEN_EXT_WIFI_MESH +src_connmand_SOURCES += src/mesh.c src/mesh-netlink.c +endif + src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ @LIBSYSTEMD_LIBS@ \ -lresolv -ldl -lrt +if TIZEN_EXT_WIFI_MESH +src_connmand_LDADD += -lnl -lnl-genl +endif + src_connmand_LDFLAGS = -Wl,--export-dynamic -pie \ -Wl,--version-script=$(srcdir)/src/connman.ver @@ -190,11 +202,19 @@ vpn_connman_vpnd_SOURCES = $(gdhcp_sources) $(builtin_vpn_sources) \ vpn/vpn-agent.c vpn/vpn-agent.h \ vpn/vpn-config.c +if TIZEN_EXT_WIFI_MESH +vpn_connman_vpnd_SOURCES += src/mesh.c src/mesh-netlink.c +endif + vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \ @LIBSYSTEMD_LIBS@ \ -lresolv -ldl +if TIZEN_EXT_WIFI_MESH +vpn_connman_vpnd_LDADD += -lnl -lnl-genl +endif + vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \ -Wl,--version-script=$(srcdir)/vpn/vpn.ver endif @@ -314,6 +334,10 @@ client_connmanctl_SOURCES = client/dbus_helpers.h client/dbus_helpers.c \ client/vpnconnections.h client/vpnconnections.c \ client/main.c +if TIZEN_EXT_WIFI_MESH +client_connmanctl_SOURCES += client/mesh.c client/mesh.h +endif + client_connmanctl_LDADD = gdbus/libgdbus-internal.la @DBUS_LIBS@ @GLIB_LIBS@ \ -lreadline -ldl -lncurses endif diff --git a/client/commands.c b/client/commands.c old mode 100755 new mode 100644 index 746e158..ce82791 --- a/client/commands.c +++ b/client/commands.c @@ -43,6 +43,9 @@ #include "commands.h" #include "agent.h" #include "vpnconnections.h" +#if defined TIZEN_EXT_WIFI_MESH +#include "mesh.h" +#endif static DBusConnection *connection; static GHashTable *service_hash; @@ -599,6 +602,578 @@ static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering) return -EINPROGRESS; } +#if defined TIZEN_EXT_WIFI_MESH +struct mesh_if_prop { + char *ifname; + char *parent_ifname; + char *bridge_ifname; +}; + +struct mesh_create_network { + char *name; + unsigned int freq; + char *sec_type; +}; + +struct mesh_specific_scan_params { + char *name; + unsigned int freq; +}; + +struct mesh_gate_params { + bool gate_announce; + int hwmp_rootmode; + int stp; +}; + +static int mesh_return(DBusMessageIter *iter, const char *error, + void *user_data) +{ + char *method = user_data; + + if (error) + fprintf(stderr, "Error %s: %s\n", method, error); + else + fprintf(stderr, "Success %s\n", method); + + g_free(method); + + return 0; +} + +static void mesh_interface_add_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_if_prop *append = user_data; + + /* Append Virtual Interface Name */ + __connmanctl_dbus_append_dict_entry(iter, "Ifname", + DBUS_TYPE_STRING, &append->ifname); + + /* Append Parent WiFi Interface Name */ + __connmanctl_dbus_append_dict_entry(iter, "ParentIfname", + DBUS_TYPE_STRING, &append->parent_ifname); + + /* Append Bridge Interface Name */ + if (append->bridge_ifname) + __connmanctl_dbus_append_dict_entry(iter, "BridgeIfname", + DBUS_TYPE_STRING, &append->bridge_ifname); +} + +static void mesh_interface_remove_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_if_prop *append = user_data; + + /* Append Virtual Interface Name */ + __connmanctl_dbus_append_dict_entry(iter, "Ifname", + DBUS_TYPE_STRING, &append->ifname); +} + +static void mesh_create_network_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_create_network *append = user_data; + + /* Append Mesh Network Name */ + __connmanctl_dbus_append_dict_entry(iter, "Name", + DBUS_TYPE_STRING, &append->name); + + /* Append Mesh Network Frequency */ + __connmanctl_dbus_append_dict_entry(iter, "Frequency", + DBUS_TYPE_UINT16, &append->freq); + + /* Append Mesh Network Security Type */ + __connmanctl_dbus_append_dict_entry(iter, "Security", + DBUS_TYPE_STRING, &append->sec_type); +} + +static void mesh_specific_scan_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_specific_scan_params *append = user_data; + + /* Append Mesh Network Name */ + __connmanctl_dbus_append_dict_entry(iter, "Name", + DBUS_TYPE_STRING, &append->name); + + /* Append Mesh Network Frequency */ + __connmanctl_dbus_append_dict_entry(iter, "Frequency", + DBUS_TYPE_UINT16, &append->freq); +} + +static void mesh_set_gate_append(DBusMessageIter *iter, void *user_data) +{ + struct mesh_gate_params *append = user_data; + + /* Append Gate Announce Protocol */ + __connmanctl_dbus_append_dict_entry(iter, "GateAnnounce", + DBUS_TYPE_BOOLEAN, &append->gate_announce); + + /* Append HWMP Root Mode */ + __connmanctl_dbus_append_dict_entry(iter, "HWMPRootMode", + DBUS_TYPE_UINT16, &append->hwmp_rootmode); + + /* Append STP */ + __connmanctl_dbus_append_dict_entry(iter, "STP", DBUS_TYPE_UINT16, + &append->stp); +} + +static void mesh_peer_append(DBusMessageIter *iter, void *user_data) +{ + char *peer_addr = user_data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &peer_addr); + + g_free(peer_addr); +} + +static int mesh_peers_list(DBusMessageIter *iter, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_mesh_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int connected_mesh_peers_list(DBusMessageIter *iter, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_mesh_connected_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int disconnected_mesh_peers_list(DBusMessageIter *iter, + const char *error, void *user_data) +{ + if (!error) { + __connmanctl_mesh_disconnected_peers_list(iter); + fprintf(stdout, "\n"); + } else + fprintf(stderr, "Error: %s\n", error); + + return 0; +} + +static int mesh_connect_return(DBusMessageIter *iter, const char *error, + void *user_data) +{ + char *path = user_data; + + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Connected %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); + + g_free(user_data); + + return 0; +} + +static int mesh_disconnect_return(DBusMessageIter *iter, const char *error, + void *user_data) +{ + char *path = user_data; + + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Disconnected %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); + + g_free(user_data); + + return 0; +} + +static int mesh_remove_return(DBusMessageIter *iter, const char *error, + void *user_data) +{ + char *path = user_data; + + if (!error) { + char *str = strrchr(path, '/'); + str++; + fprintf(stdout, "Removed %s\n", str); + } else + fprintf(stderr, "Error %s: %s\n", path, error); + + g_free(user_data); + + return 0; +} + +static int mesh_config_return(DBusMessageIter *iter, const char *error, + void *user_data) +{ + char *path = user_data; + char *str = strrchr(path, '/'); + str++; + + if (error) + fprintf(stderr, "Error %s: %s\n", path, error); + else + fprintf(stdout, "Success SetProperty %s\n", str); + + g_free(user_data); + + return 0; +} + +static int cmd_mesh(char *args[], int num, struct connman_option *options) +{ + int result = 0; + int c; + char *path = NULL; + char *method = NULL; + char *mesh_peer_name = NULL; + char *mesh_peer_path = NULL; + char *property = NULL; + char *value = NULL; + struct mesh_if_prop *append; + struct mesh_create_network *network; + struct mesh_specific_scan_params *scan_params; + struct mesh_gate_params *gate_params; + char *mesh_peer_addr = NULL; + + c = parse_args(args[1], options); + + switch (c) { + case 'a': + if (num < 4 || num > 5) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + append = dbus_malloc0(sizeof(struct mesh_if_prop)); + append->ifname = g_strdup(args[2]); + append->parent_ifname = g_strdup(args[3]); + if (num == 5) + append->bridge_ifname = g_strdup(args[4]); + method = g_strdup("MeshInterfaceAdd"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshInterfaceAdd", DBUS_TYPE_STRING, + mesh_interface_add_append, append); + g_free(append->ifname); + g_free(append->parent_ifname); + g_free(append->bridge_ifname); + g_free(append); + break; + + case 'r': + if (num != 3) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + append = dbus_malloc0(sizeof(struct mesh_if_prop)); + append->ifname = g_strdup(args[2]); + method = g_strdup("MeshInterfaceRemove"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshInterfaceRemove", DBUS_TYPE_STRING, + mesh_interface_remove_append, append); + g_free(append->ifname); + g_free(append); + break; + + case 'p': + if (num > 3) { + result = -E2BIG; + break; + } + + if (num == 3) + mesh_peer_name = args[2]; + + if (!mesh_peer_name) { + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetMeshPeers", + mesh_peers_list, NULL, NULL, NULL); + break; + } + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } + + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "GetProperties", + object_properties, mesh_peer_path, NULL, NULL); + break; + + case 'c': + if (num < 3) { + result = -EINVAL; + break; + } + + if (num > 3) { + result = -E2BIG; + break; + } + + mesh_peer_name = args[2]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } + + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "Connect", + mesh_connect_return, mesh_peer_path, NULL, NULL); + break; + + case 'd': + if (num < 3) { + result = -EINVAL; + break; + } + + if (num > 3) { + result = -E2BIG; + break; + } + + mesh_peer_name = args[2]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } + + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "Disconnect", + mesh_disconnect_return, mesh_peer_path, NULL, NULL); + break; + + case 'f': + if (num < 3) { + result = -EINVAL; + break; + } + + if (num > 3) { + result = -E2BIG; + break; + } + + mesh_peer_name = args[2]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } + + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, + mesh_peer_path, "net.connman.Mesh", "Remove", + mesh_remove_return, mesh_peer_path, NULL, NULL); + break; + + case 'C': + if (num > 2) { + result = -E2BIG; + break; + } + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "GetConnectedMeshPeers", + connected_mesh_peers_list, NULL, NULL, NULL); + break; + + case 'D': + if (num > 2) { + result = -E2BIG; + break; + } + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", + "GetDisconnectedMeshPeers", + disconnected_mesh_peers_list, NULL, NULL, NULL); + break; + + case 'n': + if (num != 5) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + network = dbus_malloc0(sizeof(struct mesh_create_network)); + network->name = g_strdup(args[2]); + network->freq = atoi(args[3]); + network->sec_type = g_strdup(args[4]); + method = g_strdup("MeshCreateNetwork"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshCreateNetwork", DBUS_TYPE_STRING, + mesh_create_network_append, network); + g_free(network->name); + g_free(network->sec_type); + g_free(network); + break; + + case 'A': + if (num != 2) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + method = g_strdup("AbortScan"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "AbortScan", DBUS_TYPE_STRING, + NULL, NULL); + break; + + case 'S': + if (num != 4) { + result = -EINVAL; + break; + } + path = g_strdup_printf("/net/connman/technology/mesh"); + + scan_params = dbus_malloc0(sizeof(struct mesh_specific_scan_params)); + scan_params->name = g_strdup(args[2]); + scan_params->freq = atoi(args[3]); + method = g_strdup("MeshSpecificScan"); + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "MeshSpecificScan", DBUS_TYPE_STRING, + mesh_specific_scan_append, scan_params); + g_free(scan_params->name); + g_free(scan_params); + break; + + case 'P': + if (num != 5) { + result = -EINVAL; + break; + } + + mesh_peer_name = args[2]; + property = args[3]; + value = args[4]; + + if (check_dbus_name(mesh_peer_name) == false) { + result = -EINVAL; + break; + } + + mesh_peer_path = g_strdup_printf("/net/connman/mesh/%s", + mesh_peer_name); + + if (g_strcmp0(property, "Passphrase") == 0) { + result = __connmanctl_dbus_set_property(connection, + mesh_peer_path, "net.connman.Mesh", + mesh_config_return, mesh_peer_path, property, + DBUS_TYPE_STRING, &value); + } else { + printf("Invalid property %s\n", property); + result = -EINVAL; + } + + break; + + case 'G': + if (num != 5) { + result = -EINVAL; + break; + } + + path = g_strdup_printf("/net/connman/technology/mesh"); + + gate_params = dbus_malloc0(sizeof(struct mesh_gate_params)); + gate_params->gate_announce = atoi(args[2]); + gate_params->hwmp_rootmode = atoi(args[3]); + gate_params->stp = atoi(args[4]); + + method = g_strdup("SetMeshGate"); + + result = __connmanctl_dbus_mesh_dict(connection, path, + "net.connman.Technology", mesh_return, method, + "SetMeshGate", DBUS_TYPE_STRING, + mesh_set_gate_append, gate_params); + + break; + + case 'z': + if (num != 3) { + result = -EINVAL; + break; + } + + mesh_peer_addr = g_strdup(args[2]); + method = g_strdup("MeshAddPeer"); + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "MeshAddPeer", + mesh_return, method, mesh_peer_append, + mesh_peer_addr); + + break; + + case 'y': + if (num != 3) { + result = -EINVAL; + break; + } + + mesh_peer_addr = g_strdup(args[2]); + method = g_strdup("MeshRemovePeer"); + + result = __connmanctl_dbus_method_call(connection, + CONNMAN_SERVICE, CONNMAN_PATH, + "net.connman.Manager", "MeshRemovePeer", + mesh_return, method, mesh_peer_append, + mesh_peer_addr); + + break; + + default: + result = -EINVAL; + break; + } + + g_free(path); + + if (result < 0) { + if (result != -EINPROGRESS) + printf("Error '%s': %s\n", args[1], strerror(-result)); + } + + + return result; +} +#endif + static int cmd_tether(char *args[], int num, struct connman_option *options) { char *ssid, *passphrase; @@ -2251,6 +2826,38 @@ static struct connman_option session_options[] = { { NULL, } }; +#if defined TIZEN_EXT_WIFI_MESH +static struct connman_option mesh_options[] = { + {"ifadd", 'a', " \n" + " [bridge_ifname] Add Virtual Mesh " + "interface"}, + {"ifrmv", 'r', " Remove Virtual Mesh " + "interface"}, + {"peers", 'p', "[peer] Display Mesh peer " + "informations"}, + {"connect", 'c', " Connect Mesh Peer"}, + {"disconnect", 'd', " Disconnect Mesh Peer"}, + {"remove", 'f', " Forget Mesh Peer"}, + {"connected_peers", 'C', "[] Displays connected" + " Peer informations"}, + {"disconnected_peers", 'D', "[] Displays " + "Disconnected Peer informations"}, + {"create_network", 'n', " Create New Mesh " + "Network"}, + {"abort_scan", 'A', " Abort ongoing mesh " + "scan"}, + {"specific_scan", 'S', " Create New Mesh " + "Network"}, + {"config", 'P', " Set Mesh Network " + "Configurations\n Passphrase "}, + {"set_gate", 'G', " Set Mesh Gate " + "Option"}, + {"add_peer", 'z', " Add Mesh Peer"}, + {"remove_peer", 'y', " Remove Mesh Peer"}, + { NULL, } +}; +#endif + static char *lookup_options(struct connman_option *options, const char *text, int state) { @@ -2303,6 +2910,13 @@ static char *lookup_session(const char *text, int state) return lookup_options(session_options, text, state); } +#if defined TIZEN_EXT_WIFI_MESH +static char *lookup_mesh(const char *text, int state) +{ + return lookup_options(mesh_options, text, state); +} +#endif + static int peer_service_cb(DBusMessageIter *iter, const char *error, void *user_data) { @@ -2614,6 +3228,10 @@ static const struct { { "disable", "|offline", NULL, cmd_disable, "Disables given technology or offline mode", lookup_technology_offline }, +#if defined TIZEN_EXT_WIFI_MESH + { "mesh", "", mesh_options, cmd_mesh, "Mesh specific commands", + lookup_mesh }, +#endif { "tether", " on|off\n" " wifi [on|off] ", NULL, cmd_tether, diff --git a/client/dbus_helpers.c b/client/dbus_helpers.c old mode 100755 new mode 100644 index 6ca407d..8c6bdbb --- a/client/dbus_helpers.c +++ b/client/dbus_helpers.c @@ -224,6 +224,11 @@ static int append_variant(DBusMessageIter *iter, const char *property, case DBUS_TYPE_INT32: type_str = DBUS_TYPE_INT32_AS_STRING; break; +#if defined TIZEN_EXT_WIFI_MESH + case DBUS_TYPE_UINT16: + type_str = DBUS_TYPE_UINT16_AS_STRING; + break; +#endif default: return -EOPNOTSUPP; } @@ -354,6 +359,50 @@ int __connmanctl_dbus_set_property_dict(DBusConnection *connection, return send_method_call(connection, message, cb, user_data); } +#if defined TIZEN_EXT_WIFI_MESH +int __connmanctl_dbus_mesh_dict(DBusConnection *connection, + const char *path, const char *interface, + connmanctl_dbus_method_return_func_t cb, void *user_data, + const char *property, int type, + connmanctl_dbus_append_func_t append_fn, + void *append_user_data) +{ + DBusMessage *message; + DBusMessageIter iter, variant, dict; + + message = dbus_message_new_method_call(CONNMAN_SERVICE, path, + interface, "MeshCommands"); + + if (!message) + return -ENOMEM; + + dbus_message_iter_init_append(message, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property); + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + 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, + &variant); + + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &dict); + + if (append_fn) + append_fn(&dict, append_user_data); + + dbus_message_iter_close_container(&variant, &dict); + dbus_message_iter_close_container(&iter, &variant); + + return send_method_call(connection, message, cb, user_data); +} +#endif + static void append_variant_array(DBusMessageIter *iter, const char *property, connmanctl_dbus_append_func_t append_fn, void *append_user_data) diff --git a/client/dbus_helpers.h b/client/dbus_helpers.h old mode 100755 new mode 100644 index 395808a..6945839 --- a/client/dbus_helpers.h +++ b/client/dbus_helpers.h @@ -67,6 +67,15 @@ int __connmanctl_dbus_set_property_dict(DBusConnection *connection, connmanctl_dbus_append_func_t append_fn, void *append_user_data); +#if defined TIZEN_EXT_WIFI_MESH +int __connmanctl_dbus_mesh_dict(DBusConnection *connection, + const char *path, const char *interface, + connmanctl_dbus_method_return_func_t cb, void *user_data, + const char *property, int type, + connmanctl_dbus_append_func_t append_fn, + void *append_user_data); +#endif + void __connmanctl_dbus_append_dict_string_array(DBusMessageIter *iter, const char *property, connmanctl_dbus_append_func_t append_fn, void *append_user_data); diff --git a/client/mesh.c b/client/mesh.c new file mode 100644 index 0000000..6e57793 --- /dev/null +++ b/client/mesh.c @@ -0,0 +1,166 @@ +/* + * + * 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 +#endif + +#include +#include + +#include "mesh.h" +#include "dbus_helpers.h" + +static void print_mesh_peer(char *path, DBusMessageIter *iter) +{ + char *name = ""; + char state = ' '; + char *str, *property; + DBusMessageIter entry, val; + int count = 0, favorite = 0; + + while (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(iter, &entry); + dbus_message_iter_get_basic(&entry, &property); + + if (strcmp(property, "Name") == 0) { + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &val); + dbus_message_iter_get_basic(&val, &name); + } else if (strcmp(property, "State") == 0) { + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &val); + dbus_message_iter_get_basic(&val, &str); + + if (str) { + if (strcmp(str, "online") == 0) + state = 'O'; + else if (strcmp(str, "ready") == 0) + state = 'R'; + else if (!strcmp(str, "association")) + state = 'a'; + else if (!strcmp(str, "configuration")) + state = 'c'; + else if (!strcmp(str, "disconnect")) + state = 'd'; + } + } else if (strcmp(property, "Favorite") == 0) { + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &val); + dbus_message_iter_get_basic(&val, &favorite); + } + + dbus_message_iter_next(iter); + count++; + } + + str = strrchr(path, '/'); + if (str) + str++; + else + str = path; + + if (count > 0) + fprintf(stdout, "%c%c %-20s %s", favorite != 0 ? 'A' : ' ', + state, name, str); + else + fprintf(stdout, "%s %s", "unchanged", str); +} + +static void list_mesh_peer_array(DBusMessageIter *iter) +{ + DBusMessageIter array, dict; + char *path = NULL; + + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) { + dbus_message_iter_recurse(iter, &array); + if (dbus_message_iter_get_arg_type(&array) + != DBUS_TYPE_OBJECT_PATH) + return; + + dbus_message_iter_get_basic(&array, &path); + + dbus_message_iter_next(&array); + if (dbus_message_iter_get_arg_type(&array) + == DBUS_TYPE_ARRAY) { + dbus_message_iter_recurse(&array, &dict); + print_mesh_peer(path, &dict); + } + + if (dbus_message_iter_has_next(iter)) + fprintf(stdout, "\n"); + + dbus_message_iter_next(iter); + } +} + +void __connmanctl_mesh_peers_list(DBusMessageIter *iter) +{ + DBusMessageIter array; + char *path; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &array); + list_mesh_peer_array(&array); + + dbus_message_iter_next(iter); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + fprintf(stdout, "\n}, {"); + + dbus_message_iter_recurse(iter, &array); + while (dbus_message_iter_get_arg_type(&array) + == DBUS_TYPE_OBJECT_PATH) { + dbus_message_iter_get_basic(&array, &path); + fprintf(stdout, "\n%s %s", "removed", path); + + dbus_message_iter_next(&array); + } + +} + +void __connmanctl_mesh_connected_peers_list(DBusMessageIter *iter) +{ + DBusMessageIter array; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &array); + __connmanctl_dbus_print(&array, " ", " = ", "\n"); + fprintf(stdout, "\n"); +} + +void __connmanctl_mesh_disconnected_peers_list(DBusMessageIter *iter) +{ + DBusMessageIter array; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &array); + __connmanctl_dbus_print(&array, " ", " = ", "\n"); + fprintf(stdout, "\n"); +} diff --git a/client/mesh.h b/client/mesh.h new file mode 100644 index 0000000..8dd413d --- /dev/null +++ b/client/mesh.h @@ -0,0 +1,40 @@ +/* + * + * 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 + */ + +#ifndef __CONNMANCTL_MESH_H +#define __CONNMANCTL_MESH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void __connmanctl_mesh_peers_list(DBusMessageIter *iter); +void __connmanctl_mesh_connected_peers_list(DBusMessageIter *iter); +void __connmanctl_mesh_disconnected_peers_list(DBusMessageIter *iter); + +#ifdef __cplusplus +} +#endif + +#endif /* __CONNMANCTL_MESH_H */ diff --git a/configure.ac b/configure.ac index 01ecd95..3a569d4 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,11 @@ AC_ARG_ENABLE(tizen-ext, fi]) AM_CONDITIONAL(TIZEN_EXT, test "${enable-tizen-ext}" != "no") +AC_ARG_ENABLE(tizen-ext-wifi-mesh, + AC_HELP_STRING([--enable-tizen-ext-wifi-mesh], [enable TIZEN extensions for Wi-Fi Mesh]), + [CFLAGS="$CFLAGS -DTIZEN_EXT_WIFI_MESH"], [enable_tizen_ext_wifi_mesh="no"]) +AM_CONDITIONAL(TIZEN_EXT_WIFI_MESH, test "${enable_tizen_ext_wifi_mesh}" != "no") + AC_ARG_WITH(openconnect, AC_HELP_STRING([--with-openconnect=PROGRAM], [specify location of openconnect binary]), [path_openconnect=${withval}]) diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h old mode 100755 new mode 100644 index 648ee57..7e94711 --- a/gsupplicant/gsupplicant.h +++ b/gsupplicant/gsupplicant.h @@ -50,6 +50,9 @@ extern "C" { #define G_SUPPLICANT_CAPABILITY_MODE_IBSS (1 << 1) #define G_SUPPLICANT_CAPABILITY_MODE_AP (1 << 2) #define G_SUPPLICANT_CAPABILITY_MODE_P2P (1 << 3) +#if defined TIZEN_EXT_WIFI_MESH +#define G_SUPPLICANT_CAPABILITY_MODE_MESH (1 << 4) +#endif #define G_SUPPLICANT_KEYMGMT_NONE (1 << 0) #define G_SUPPLICANT_KEYMGMT_IEEE8021X (1 << 1) @@ -66,6 +69,9 @@ extern "C" { #define G_SUPPLICANT_KEYMGMT_WPA_EAP (1 << 7) #define G_SUPPLICANT_KEYMGMT_WPA_EAP_256 (1 << 8) #define G_SUPPLICANT_KEYMGMT_WPS (1 << 9) +#if defined TIZEN_EXT_WIFI_MESH +#define G_SUPPLICANT_KEYMGMT_SAE (1 << 10) +#endif #define G_SUPPLICANT_PROTO_WPA (1 << 0) #define G_SUPPLICANT_PROTO_RSN (1 << 1) @@ -94,8 +100,19 @@ typedef enum { G_SUPPLICANT_MODE_INFRA, G_SUPPLICANT_MODE_IBSS, G_SUPPLICANT_MODE_MASTER, +#if defined TIZEN_EXT_WIFI_MESH + G_SUPPLICANT_MODE_MESH, +#endif } GSupplicantMode; +#if defined TIZEN_EXT_WIFI_MESH +typedef enum { + G_SUPPLICANT_IEEE80211W_UNKNOWN, + G_SUPPLICANT_IEEE80211W_OPTIONAL, + G_SUPPLICANT_IEEE80211W_REQUIRED, +} GSupplicantPmf; +#endif + typedef enum { G_SUPPLICANT_SECURITY_UNKNOWN, G_SUPPLICANT_SECURITY_NONE, @@ -106,6 +123,9 @@ typedef enum { G_SUPPLICANT_SECURITY_FT_PSK, G_SUPPLICANT_SECURITY_FT_IEEE8021X, #endif +#if defined TIZEN_EXT_WIFI_MESH + G_SUPPLICANT_SECURITY_SAE, +#endif } GSupplicantSecurity; #if defined TIZEN_EXT @@ -192,6 +212,9 @@ struct _GSupplicantSSID { const char *phase1; const char *pac_file; #endif +#if defined TIZEN_EXT_WIFI_MESH + uint16_t ieee80211w; +#endif }; typedef struct _GSupplicantSSID GSupplicantSSID; @@ -257,6 +280,9 @@ struct _GSupplicantPeer; typedef struct _GSupplicantInterface GSupplicantInterface; typedef struct _GSupplicantPeer GSupplicantPeer; +#if defined TIZEN_EXT_WIFI_MESH +typedef struct _GSupplicantMeshPeer GSupplicantMeshPeer; +#endif typedef void (*GSupplicantInterfaceCallback) (int result, GSupplicantInterface *interface, @@ -346,6 +372,26 @@ GSupplicantPeer *g_supplicant_interface_peer_lookup(GSupplicantInterface *interf const char *identifier); bool g_supplicant_interface_is_p2p_finding(GSupplicantInterface *interface); +#if defined TIZEN_EXT_WIFI_MESH +bool g_supplicant_interface_has_mesh(GSupplicantInterface *interface); +int g_supplicant_mesh_interface_create(const char *ifname, const char *driver, + const char *bridge, const char *parent_ifname, + GSupplicantInterfaceCallback callback, void *user_data); +const void *g_supplicant_interface_get_mesh_group_ssid( + GSupplicantInterface *interface, + unsigned int *ssid_len); +int g_supplicant_mesh_get_disconnect_reason(GSupplicantInterface *interface); +const char *g_supplicant_mesh_peer_get_address(GSupplicantMeshPeer *mesh_peer); +int g_supplicant_mesh_peer_get_disconnect_reason( + GSupplicantMeshPeer *mesh_peer); +int g_supplicant_interface_abort_scan(GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, void *user_data); +int g_supplicant_interface_mesh_peer_change_status( + GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, const char *peer_address, + const char *method, void *user_data); +#endif + /* Network and Peer API */ struct _GSupplicantNetwork; struct _GSupplicantGroup; @@ -437,6 +483,13 @@ struct _GSupplicantCallbacks { int reasoncode); void (*assoc_status_code)(GSupplicantInterface *interface, int reasoncode); +#if defined TIZEN_EXT_WIFI_MESH + void (*mesh_support) (GSupplicantInterface *interface); + void (*mesh_group_started) (GSupplicantInterface *interface); + void (*mesh_group_removed) (GSupplicantInterface *interface); + void (*mesh_peer_connected) (GSupplicantMeshPeer *mesh_peer); + void (*mesh_peer_disconnected) (GSupplicantMeshPeer *mesh_peer); +#endif }; typedef struct _GSupplicantCallbacks GSupplicantCallbacks; diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c index bbaad04..4f6957b 100644 --- a/gsupplicant/supplicant.c +++ b/gsupplicant/supplicant.c @@ -96,6 +96,9 @@ static struct strvalmap keymgmt_map[] = { { "wpa-eap", G_SUPPLICANT_KEYMGMT_WPA_EAP }, { "wpa-eap-sha256", G_SUPPLICANT_KEYMGMT_WPA_EAP_256 }, { "wps", G_SUPPLICANT_KEYMGMT_WPS }, +#if defined TIZEN_EXT_WIFI_MESH + { "sae", G_SUPPLICANT_KEYMGMT_SAE }, +#endif { } }; @@ -139,6 +142,9 @@ static struct strvalmap mode_capa_map[] = { { "ad-hoc", G_SUPPLICANT_CAPABILITY_MODE_IBSS }, { "ap", G_SUPPLICANT_CAPABILITY_MODE_AP }, { "p2p", G_SUPPLICANT_CAPABILITY_MODE_P2P }, +#if defined TIZEN_EXT_WIFI_MESH + { "mesh", G_SUPPLICANT_CAPABILITY_MODE_MESH }, +#endif { } }; @@ -162,6 +168,14 @@ struct added_network_information { char * private_passphrase; }; +#if defined TIZEN_EXT_WIFI_MESH +struct _GSupplicantMeshGroupInfo { + unsigned char ssid[32]; + unsigned int ssid_len; + int disconnect_reason; +}; +#endif + struct _GSupplicantInterface { char *path; char *network_path; @@ -198,6 +212,10 @@ struct _GSupplicantInterface { #if defined TIZEN_EXT int disconnect_reason; #endif +#if defined TIZEN_EXT_WIFI_MESH + bool mesh_support; + struct _GSupplicantMeshGroupInfo group_info; +#endif }; struct g_supplicant_bss { @@ -230,6 +248,9 @@ struct g_supplicant_bss { unsigned char country_code[COUNTRY_CODE_LENGTH]; #endif unsigned int wps_capabilities; +#if defined TIZEN_EXT_WIFI_MESH + dbus_bool_t sae; +#endif }; struct _GSupplicantNetwork { @@ -297,6 +318,10 @@ struct interface_create_data { char *ifname; char *driver; char *bridge; +#if defined TIZEN_EXT_WIFI_MESH + char *parent_ifname; + bool is_mesh_interface; +#endif GSupplicantInterface *interface; GSupplicantInterfaceCallback callback; void *user_data; @@ -331,6 +356,14 @@ struct g_connman_bssids { static int network_remove(struct interface_data *data); +#if defined TIZEN_EXT_WIFI_MESH +struct _GSupplicantMeshPeer { + GSupplicantInterface *interface; + char *peer_address; + int disconnect_reason; +}; +#endif + static inline void debug(const char *format, ...) { char str[256]; @@ -359,6 +392,10 @@ static GSupplicantMode string2mode(const char *mode) return G_SUPPLICANT_MODE_INFRA; else if (g_str_equal(mode, "ad-hoc")) return G_SUPPLICANT_MODE_IBSS; +#if defined TIZEN_EXT_WIFI_MESH + else if (g_str_equal(mode, "mesh")) + return G_SUPPLICANT_MODE_MESH; +#endif return G_SUPPLICANT_MODE_UNKNOWN; } @@ -374,6 +411,10 @@ static const char *mode2string(GSupplicantMode mode) return "adhoc"; case G_SUPPLICANT_MODE_MASTER: return "ap"; +#if defined TIZEN_EXT_WIFI_MESH + case G_SUPPLICANT_MODE_MESH: + return "mesh"; +#endif } return NULL; @@ -398,6 +439,10 @@ static const char *security2string(GSupplicantSecurity security) case G_SUPPLICANT_SECURITY_FT_IEEE8021X: return "ft_ieee8021x"; #endif +#if defined TIZEN_EXT_WIFI_MESH + case G_SUPPLICANT_SECURITY_SAE: + return "sae"; +#endif } return NULL; @@ -580,6 +625,27 @@ static void callback_p2p_support(GSupplicantInterface *interface) } #endif +#if defined TIZEN_EXT_WIFI_MESH +static void callback_mesh_support(GSupplicantInterface *interface) +{ + SUPPLICANT_DBG(""); + + if (!interface->mesh_support) + return; + + if (callbacks_pointer && callbacks_pointer->mesh_support) + callbacks_pointer->mesh_support(interface); +} + +bool g_supplicant_interface_has_mesh(GSupplicantInterface *interface) +{ + if (!interface) + return false; + + return interface->mesh_support; +} +#endif + static void callback_scan_started(GSupplicantInterface *interface) { if (!callbacks_pointer) @@ -1698,6 +1764,10 @@ static void merge_network(GSupplicantNetwork *network) g_string_append_printf(str, "_managed"); else if (g_strcmp0(mode, "1") == 0) g_string_append_printf(str, "_adhoc"); +#if defined TIZEN_EXT_WIFI_MESH + else if (g_strcmp0(mode, "5") == 0) + g_string_append_printf(str, "_mesh"); +#endif if (g_strcmp0(key_mgmt, "WPA-PSK") == 0) g_string_append_printf(str, "_psk"); @@ -2266,6 +2336,11 @@ static void bss_compute_security(struct g_supplicant_bss *bss) bss->psk = TRUE; #endif +#if defined TIZEN_EXT_WIFI_MESH + if (bss->keymgmt & G_SUPPLICANT_KEYMGMT_SAE) + bss->sae = TRUE; +#endif + if (bss->ieee8021x) bss->security = G_SUPPLICANT_SECURITY_IEEE8021X; else if (bss->psk) @@ -2276,6 +2351,10 @@ static void bss_compute_security(struct g_supplicant_bss *bss) else if (bss->ft_ieee8021x == TRUE) bss->security = G_SUPPLICANT_SECURITY_IEEE8021X; #endif +#if defined TIZEN_EXT_WIFI_MESH + else if (bss->sae) + bss->security = G_SUPPLICANT_SECURITY_SAE; +#endif else if (bss->privacy) bss->security = G_SUPPLICANT_SECURITY_WEP; else @@ -2672,6 +2751,10 @@ static void interface_property(const char *key, DBusMessageIter *iter, if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_P2P) interface->p2p_support = true; #endif +#if defined TIZEN_EXT_WIFI_MESH + if (interface->mode_capa & G_SUPPLICANT_CAPABILITY_MODE_MESH) + interface->mesh_support = true; +#endif } else if (g_strcmp0(key, "State") == 0) { const char *str = NULL; @@ -3969,6 +4052,214 @@ static void signal_group_peer_disconnected(const char *path, DBusMessageIter *it peer->connection_requested = false; } +#if defined TIZEN_EXT_WIFI_MESH +const void *g_supplicant_interface_get_mesh_group_ssid( + GSupplicantInterface *interface, + unsigned int *ssid_len) +{ + if (!ssid_len) + return NULL; + + if (!interface || interface->group_info.ssid_len == 0) { + *ssid_len = 0; + return NULL; + } + + *ssid_len = interface->group_info.ssid_len; + return interface->group_info.ssid; +} + +int g_supplicant_mesh_get_disconnect_reason(GSupplicantInterface *interface) +{ + if (!interface) + return -EINVAL; + + return interface->group_info.disconnect_reason; +} + +const char *g_supplicant_mesh_peer_get_address(GSupplicantMeshPeer *mesh_peer) +{ + if (!mesh_peer || !mesh_peer->peer_address) + return NULL; + + return mesh_peer->peer_address; +} + +int g_supplicant_mesh_peer_get_disconnect_reason(GSupplicantMeshPeer *mesh_peer) +{ + if (!mesh_peer) + return -EINVAL; + + return mesh_peer->disconnect_reason; +} + +static void callback_mesh_group_started(GSupplicantInterface *interface) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->mesh_group_started) + return; + + callbacks_pointer->mesh_group_started(interface); +} + +static void callback_mesh_group_removed(GSupplicantInterface *interface) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->mesh_group_removed) + return; + + callbacks_pointer->mesh_group_removed(interface); +} + +static void mesh_group_info(const char *key, DBusMessageIter *iter, + void *user_data) +{ + GSupplicantInterface *interface = user_data; + if (!key) + return; + + if (g_strcmp0(key, "SSID") == 0) { + DBusMessageIter array; + unsigned char *ssid; + int ssid_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len); + + if (ssid_len > 0 && ssid_len < 33) { + memcpy(interface->group_info.ssid, ssid, ssid_len); + interface->group_info.ssid_len = ssid_len; + } else { + memset(interface->group_info.ssid, 0, 32); + interface->group_info.ssid_len = 0; + } + } else if (g_strcmp0(key, "DisconnectReason") == 0) { + int disconnect_reason = 0; + dbus_message_iter_get_basic(iter, &disconnect_reason); + interface->group_info.disconnect_reason = disconnect_reason; + } +} + +static void signal_mesh_group_started(const char *path, DBusMessageIter *iter) +{ + GSupplicantInterface *interface; + + interface = g_hash_table_lookup(interface_table, path); + if (!interface) + return; + + supplicant_dbus_property_foreach(iter, mesh_group_info, interface); + + callback_mesh_group_started(interface); +} + +static void signal_mesh_group_removed(const char *path, DBusMessageIter *iter) +{ + GSupplicantInterface *interface; + + interface = g_hash_table_lookup(interface_table, path); + if (!interface) + return; + + supplicant_dbus_property_foreach(iter, mesh_group_info, interface); + + callback_mesh_group_removed(interface); +} + +static void callback_mesh_peer_connected(GSupplicantMeshPeer *mesh_peer) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->mesh_peer_connected) + return; + + callbacks_pointer->mesh_peer_connected(mesh_peer); +} + +static void callback_mesh_peer_disconnected(GSupplicantMeshPeer *mesh_peer) +{ + if (!callbacks_pointer) + return; + + if (!callbacks_pointer->mesh_peer_disconnected) + return; + + callbacks_pointer->mesh_peer_disconnected(mesh_peer); +} + +static void mesh_peer_info(const char *key, DBusMessageIter *iter, + void *user_data) +{ + GSupplicantMeshPeer *mesh_peer = user_data; + if (!key) + return; + + if (g_strcmp0(key, "PeerAddress") == 0) { + DBusMessageIter array; + unsigned char *addr; + int addr_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &addr, &addr_len); + + if (addr_len == 6) { + mesh_peer->peer_address = g_malloc0(19); + snprintf(mesh_peer->peer_address, 19, + "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], + addr[2], addr[3], addr[4], addr[5]); + } + } else if (g_strcmp0(key, "DisconnectReason") == 0) { + int disconnect_reason = 0; + dbus_message_iter_get_basic(iter, &disconnect_reason); + mesh_peer->disconnect_reason = disconnect_reason; + } +} + +static void signal_mesh_peer_connected(const char *path, DBusMessageIter *iter) +{ + GSupplicantInterface *interface; + GSupplicantMeshPeer *mesh_peer; + + interface = g_hash_table_lookup(interface_table, path); + if (!interface) + return; + + mesh_peer = dbus_malloc0(sizeof(GSupplicantMeshPeer)); + mesh_peer->interface = interface; + + supplicant_dbus_property_foreach(iter, mesh_peer_info, mesh_peer); + + callback_mesh_peer_connected(mesh_peer); + g_free(mesh_peer->peer_address); + g_free(mesh_peer); +} + +static void signal_mesh_peer_disconnected(const char *path, + DBusMessageIter *iter) +{ + GSupplicantInterface *interface; + GSupplicantMeshPeer *mesh_peer; + + interface = g_hash_table_lookup(interface_table, path); + if (!interface) + return; + + mesh_peer = dbus_malloc0(sizeof(GSupplicantMeshPeer)); + mesh_peer->interface = interface; + + supplicant_dbus_property_foreach(iter, mesh_peer_info, mesh_peer); + + callback_mesh_peer_disconnected(mesh_peer); + g_free(mesh_peer->peer_address); + g_free(mesh_peer); +} +#endif + static struct { const char *interface; const char *member; @@ -4012,6 +4303,16 @@ static struct { { SUPPLICANT_INTERFACE ".Group", "PeerJoined", signal_group_peer_joined }, { SUPPLICANT_INTERFACE ".Group", "PeerDisconnected", signal_group_peer_disconnected }, +#if defined TIZEN_EXT_WIFI_MESH + { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshGroupStarted", + signal_mesh_group_started }, + { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshGroupRemoved", + signal_mesh_group_removed }, + { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshPeerConnected", + signal_mesh_peer_connected }, + { SUPPLICANT_INTERFACE ".Interface.Mesh", "MeshPeerDisconnected", + signal_mesh_peer_disconnected }, +#endif { } }; @@ -4290,6 +4591,9 @@ static void interface_create_data_free(struct interface_create_data *data) g_free(data->ifname); g_free(data->driver); g_free(data->bridge); +#if defined TIZEN_EXT_WIFI_MESH + g_free(data->parent_ifname); +#endif dbus_free(data); } @@ -4317,6 +4621,9 @@ static void interface_create_property(const char *key, DBusMessageIter *iter, #if !defined TIZEN_EXT callback_p2p_support(interface); #endif +#if defined TIZEN_EXT_WIFI_MESH + callback_mesh_support(interface); +#endif } interface_create_data_free(data); @@ -4403,6 +4710,17 @@ static void interface_create_params(DBusMessageIter *iter, void *user_data) DBUS_TYPE_STRING, &config_file); } +#if defined TIZEN_EXT_WIFI_MESH + if (data->is_mesh_interface) { + if (data->parent_ifname) + supplicant_dbus_dict_append_basic(&dict, "ParentIfname", + DBUS_TYPE_STRING, &data->parent_ifname); + + supplicant_dbus_dict_append_basic(&dict, "IsMeshInterface", + DBUS_TYPE_BOOLEAN, &data->is_mesh_interface); + } +#endif + supplicant_dbus_dict_close(iter, &dict); } @@ -4438,6 +4756,9 @@ static void interface_get_result(const char *error, #if !defined TIZEN_EXT callback_p2p_support(interface); #endif +#if defined TIZEN_EXT_WIFI_MESH + callback_mesh_support(interface); +#endif } interface_create_data_free(data); @@ -4477,6 +4798,117 @@ static void interface_get_params(DBusMessageIter *iter, void *user_data) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->ifname); } +#if defined TIZEN_EXT_WIFI_MESH +int g_supplicant_mesh_interface_create(const char *ifname, const char *driver, + const char *bridge, const char *parent_ifname, + GSupplicantInterfaceCallback callback, void *user_data) +{ + struct interface_create_data *data; + int ret; + + SUPPLICANT_DBG("ifname %s", ifname); + + if (!ifname || !parent_ifname) + return -EINVAL; + + if (!system_available) + return -EFAULT; + + data = dbus_malloc0(sizeof(*data)); + if (!data) + return -ENOMEM; + + data->ifname = g_strdup(ifname); + data->driver = g_strdup(driver); + data->bridge = g_strdup(bridge); + data->is_mesh_interface = true; + data->parent_ifname = g_strdup(parent_ifname); + data->callback = callback; + data->user_data = user_data; + + ret = supplicant_dbus_method_call(SUPPLICANT_PATH, + SUPPLICANT_INTERFACE, + "CreateInterface", + interface_create_params, + interface_create_result, data, + NULL); + return ret; +} + +struct interface_mesh_peer_data { + char *peer_address; + char *method; + GSupplicantInterface *interface; + GSupplicantInterfaceCallback callback; + void *user_data; +}; + +static void interface_mesh_change_peer_params(DBusMessageIter *iter, + void *user_data) +{ + struct interface_mesh_peer_data *data = user_data; + + SUPPLICANT_DBG(""); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->peer_address); +} + +static void interface_mesh_change_peer_result(const char *error, + DBusMessageIter *iter, void *user_data) +{ + struct interface_mesh_peer_data *data = user_data; + int err = 0; + + SUPPLICANT_DBG("%s", data->method); + + if (error) { + err = -EIO; + SUPPLICANT_DBG("error %s", error); + } + + if (data->callback) + data->callback(err, data->interface, data->user_data); + + g_free(data->peer_address); + g_free(data->method); + dbus_free(data); +} + +int g_supplicant_interface_mesh_peer_change_status( + GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, const char *peer_address, + const char *method, void *user_data) +{ + struct interface_mesh_peer_data *data; + int ret; + + if (!peer_address) + return -EINVAL; + + data = dbus_malloc0(sizeof(*data)); + if (!data) + return -ENOMEM; + + data->peer_address = g_strdup(peer_address); + data->method = g_strdup(method); + data->interface = interface; + data->callback = callback; + data->user_data = user_data; + + ret = supplicant_dbus_method_call(interface->path, + SUPPLICANT_INTERFACE ".Interface.Mesh", + method, interface_mesh_change_peer_params, + interface_mesh_change_peer_result, data, NULL); + if (ret < 0) { + g_free(data->peer_address); + g_free(data->method); + dbus_free(data); + } + + return ret; +} +#endif + int g_supplicant_interface_create(const char *ifname, const char *driver, const char *bridge, GSupplicantInterfaceCallback callback, @@ -4787,6 +5219,57 @@ static int interface_ready_to_scan(GSupplicantInterface *interface) return 0; } +#if defined TIZEN_EXT_WIFI_MESH +static void interface_abort_scan_result(const char *error, + DBusMessageIter *iter, void *user_data) +{ + struct interface_scan_data *data = user_data; + int err = 0; + + if (error) { + SUPPLICANT_DBG("error %s", error); + err = -EIO; + } + + g_free(data->path); + + if (data->callback) + data->callback(err, data->interface, data->user_data); + + dbus_free(data); +} + +int g_supplicant_interface_abort_scan(GSupplicantInterface *interface, + GSupplicantInterfaceCallback callback, void *user_data) +{ + struct interface_scan_data *data; + int ret; + + if (!interface->scanning) + return -EEXIST; + + data = dbus_malloc0(sizeof(*data)); + if (!data) + return -ENOMEM; + + data->interface = interface; + data->path = g_strdup(interface->path); + data->callback = callback; + data->user_data = user_data; + + ret = supplicant_dbus_method_call(interface->path, + SUPPLICANT_INTERFACE ".Interface", "AbortScan", NULL, + interface_abort_scan_result, data, interface); + + if (ret < 0) { + g_free(data->path); + dbus_free(data); + } + + return ret; +} +#endif + int g_supplicant_interface_scan(GSupplicantInterface *interface, GSupplicantScanParams *scan_data, GSupplicantInterfaceCallback callback, @@ -5429,6 +5912,17 @@ static void add_network_security_proto(DBusMessageIter *dict, g_free(proto); } +#if defined TIZEN_EXT_WIFI_MESH +static void add_network_ieee80211w(DBusMessageIter *dict, GSupplicantSSID *ssid) +{ + if (ssid->security != G_SUPPLICANT_SECURITY_SAE) + return; + + supplicant_dbus_dict_append_basic(dict, "ieee80211w", DBUS_TYPE_UINT32, + &ssid->ieee80211w); +} +#endif + static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid) { char *key_mgmt; @@ -5471,6 +5965,12 @@ static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid) add_network_security_proto(dict, ssid); break; #endif +#if defined TIZEN_EXT_WIFI_MESH + case G_SUPPLICANT_SECURITY_SAE: + key_mgmt = "SAE"; + add_network_security_psk(dict, ssid); + break; +#endif } supplicant_dbus_dict_append_basic(dict, "key_mgmt", @@ -5492,6 +5992,11 @@ static void add_network_mode(DBusMessageIter *dict, GSupplicantSSID *ssid) case G_SUPPLICANT_MODE_MASTER: mode = 2; break; +#if defined TIZEN_EXT_WIFI_MESH + case G_SUPPLICANT_MODE_MESH: + mode = 5; + break; +#endif } supplicant_dbus_dict_append_basic(dict, "mode", @@ -5522,6 +6027,10 @@ static void interface_add_network_params(DBusMessageIter *iter, void *user_data) add_network_security(&dict, ssid); +#if defined TIZEN_EXT_WIFI_MESH + add_network_ieee80211w(&dict, ssid); +#endif + supplicant_dbus_dict_append_fixed_array(&dict, "ssid", DBUS_TYPE_BYTE, &ssid->ssid, ssid->ssid_len); @@ -5865,7 +6374,12 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface, network_remove(intf_data); } else #if defined TIZEN_EXT - if (ssid->passphrase && g_strcmp0(ssid->passphrase, "") != 0 && !ssid->eap) { + if (ssid->passphrase && + g_strcmp0(ssid->passphrase, "") != 0 && +#if defined TIZEN_EXT_WIFI_MESH + ssid->mode != G_SUPPLICANT_MODE_MESH && +#endif + !ssid->eap) { ret = send_decryption_request(ssid->passphrase, data); if (ret < 0) SUPPLICANT_DBG("Decryption request failed %d", ret); @@ -6549,6 +7063,10 @@ static const char *g_supplicant_rule7 = "type=signal," static const char *g_supplicant_rule8 = "type=signal," "interface=" SUPPLICANT_INTERFACE ".Group"; #endif +#if defined TIZEN_EXT_WIFI_MESH +static const char *g_supplicant_rule9 = "type=signal," + "interface=" SUPPLICANT_INTERFACE ".Interface.Mesh"; +#endif static void invoke_introspect_method(void) { @@ -6615,6 +7133,9 @@ int g_supplicant_register(const GSupplicantCallbacks *callbacks) dbus_bus_add_match(connection, g_supplicant_rule7, NULL); dbus_bus_add_match(connection, g_supplicant_rule8, NULL); #endif +#if defined TIZEN_EXT_WIFI_MESH + dbus_bus_add_match(connection, g_supplicant_rule9, NULL); +#endif dbus_connection_flush(connection); if (dbus_bus_name_has_owner(connection, @@ -6656,6 +7177,9 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks) SUPPLICANT_DBG(""); if (connection) { +#if defined TIZEN_EXT_WIFI_MESH + dbus_bus_remove_match(connection, g_supplicant_rule9, NULL); +#endif #if !defined TIZEN_EXT dbus_bus_remove_match(connection, g_supplicant_rule8, NULL); dbus_bus_remove_match(connection, g_supplicant_rule7, NULL); diff --git a/include/dbus.h b/include/dbus.h old mode 100755 new mode 100644 index 3087fe4..19e4938 --- a/include/dbus.h +++ b/include/dbus.h @@ -47,6 +47,9 @@ extern "C" { #define CONNMAN_SESSION_INTERFACE CONNMAN_SERVICE ".Session" #define CONNMAN_NOTIFICATION_INTERFACE CONNMAN_SERVICE ".Notification" #define CONNMAN_PEER_INTERFACE CONNMAN_SERVICE ".Peer" +#if defined TIZEN_EXT_WIFI_MESH +#define CONNMAN_MESH_INTERFACE CONNMAN_SERVICE ".Mesh" +#endif #define CONNMAN_PRIVILEGE_MODIFY 1 #define CONNMAN_PRIVILEGE_SECRET 2 diff --git a/include/device.h b/include/device.h old mode 100755 new mode 100644 index 36b2f55..852207d --- a/include/device.h +++ b/include/device.h @@ -131,6 +131,13 @@ struct connman_device_driver { struct connman_device *device, int scan_type, GSList *specific_scan_list, void *user_data); #endif +#if defined TIZEN_EXT_WIFI_MESH + int (*abort_scan) (enum connman_service_type type, + struct connman_device *device); + int (*mesh_specific_scan) (enum connman_service_type type, + struct connman_device *device, const char *ssid, unsigned int freq, + void *user_data); +#endif }; int connman_device_driver_register(struct connman_device_driver *driver); diff --git a/include/inet.h b/include/inet.h old mode 100755 new mode 100644 index 0742730..ef61af9 --- a/include/inet.h +++ b/include/inet.h @@ -81,6 +81,11 @@ int connman_inet_check_ipaddress(const char *host); bool connman_inet_check_hostname(const char *ptr, size_t len); bool connman_inet_is_ipv6_supported(); +#if defined TIZEN_EXT_WIFI_MESH +char *connman_inet_ifaddr(const char *name); +char *connman_inet_ifname2addr(const char *name); +#endif + #ifdef __cplusplus } #endif diff --git a/include/mesh-netlink.h b/include/mesh-netlink.h new file mode 100644 index 0000000..78b3e36 --- /dev/null +++ b/include/mesh-netlink.h @@ -0,0 +1,58 @@ +/* + * + * 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 + */ + +#ifndef __CONNMAN_MESH_NETLINK_H +#define __CONNMAN_MESH_NETLINK_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int id; + struct nl_sock *nl_socket; + struct nl_cb *cb; +} mesh_nl80211_global; + +#define MESH_HWMP_ROOTMODE_NO_ROOT 0 +#define MESH_HWMP_ROOTMODE_PROACTIVE_PREQ_NO_PREP 2 +#define MESH_HWMP_ROOTMODE_PROACTIVE_PREQ_WITH_PREP 3 +#define MESH_HWMP_ROOTMODE_RANN 4 + +#define NL80211_ATTR_IFINDEX 3 +#define NL80211_CMD_SET_MESH_CONFIG 29 +#define NL80211_ATTR_MESH_CONFIG 35 + +#define NL80211_MESHCONF_HWMP_ROOTMODE 14 +#define NL80211_MESHCONF_GATE_ANNOUNCEMENTS 17 + +int __connman_mesh_netlink_set_gate_announce(mesh_nl80211_global *global, + int mesh_if_index, bool gate_announce, int hwmp_rootmode); + +mesh_nl80211_global *__connman_mesh_nl80211_global_init(void); +void __connman_mesh_nl80211_global_deinit(mesh_nl80211_global *global); + +#ifdef __cplusplus +} +#endif + +#endif /* __CONNMAN_MESH_NETLINK_H */ diff --git a/include/mesh.h b/include/mesh.h new file mode 100644 index 0000000..547c237 --- /dev/null +++ b/include/mesh.h @@ -0,0 +1,155 @@ +/* + * + * 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 + */ + +#ifndef __CONNMAN_MESH_H +#define __CONNMAN_MESH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct connman_mesh; + +enum connman_mesh_security { + CONNMAN_MESH_SECURITY_UNKNOWN = 0, + CONNMAN_MESH_SECURITY_NONE = 1, + CONNMAN_MESH_SECURITY_SAE = 2, +}; + +enum connman_mesh_state { + CONNMAN_MESH_STATE_UNKNOWN = 0, + CONNMAN_MESH_STATE_IDLE = 1, + CONNMAN_MESH_STATE_ASSOCIATION = 2, + CONNMAN_MESH_STATE_CONFIGURATION = 3, + CONNMAN_MESH_STATE_READY = 4, + CONNMAN_MESH_STATE_DISCONNECT = 5, + CONNMAN_MESH_STATE_FAILURE = 6, +}; + +enum connman_mesh_peer_type { + CONNMAN_MESH_PEER_TYPE_CREATED = 0, + CONNMAN_MESH_PEER_TYPE_DISCOVERED = 1, +}; + +enum connman_mesh_peer_disconnect_reason { + CONNMAN_MESH_REASON_UNKNOWN = 0, + CONNMAN_MESH_DEAUTH_LEAVING = 1, + CONNMAN_MESH_PEERING_CANCELLED = 2, + CONNMAN_MESH_MAX_PEERS = 3, + CONNMAN_MESH_CONFIG_POLICY_VIOLATION = 4, + CONNMAN_MESH_CLOSE_RCVD = 5, + CONNMAN_MESH_MAX_RETRIES = 6, + CONNMAN_MESH_CONFIRM_TIMEOUT = 7, + CONNMAN_MESH_INVALID_GTK = 8, + CONNMAN_MESH_INCONSISTENT_PARAMS = 9, + CONNMAN_MESH_INVALID_SECURITY_CAP = 10, +}; + +enum connman_mesh_peer_status { + CONNMAN_MESH_PEER_ADD = 0, + CONNMAN_MESH_PEER_REMOVE = 1, +}; + +struct connman_mesh *connman_mesh_create(const char *interface_addr, + const char *identifier); + +void connman_mesh_set_name(struct connman_mesh *mesh, const char *name); +const char *connman_mesh_get_name(struct connman_mesh *mesh); +void connman_mesh_set_passphrase(struct connman_mesh *mesh, + const char *passphrase); +const char *connman_mesh_get_passphrase(struct connman_mesh *mesh); +void connman_mesh_set_address(struct connman_mesh *mesh, const char *address); +void connman_mesh_set_security(struct connman_mesh *mesh, const char *security); +const char *connman_mesh_get_security(struct connman_mesh *mesh); +void connman_mesh_set_frequency(struct connman_mesh *mesh, uint16_t frequency); +uint16_t connman_mesh_get_frequency(struct connman_mesh *mesh); +void connman_mesh_set_ieee80211w(struct connman_mesh *mesh, uint16_t ieee80211w); +uint16_t connman_mesh_get_ieee80211w(struct connman_mesh *mesh); +int connman_mesh_peer_set_state(struct connman_mesh *mesh, + enum connman_mesh_state new_state); +void connman_mesh_set_peer_type(struct connman_mesh *mesh, + enum connman_mesh_peer_type type); +bool connman_mesh_peer_is_connected_state(struct connman_mesh *mesh); +struct connman_mesh *connman_get_connected_mesh_from_name(char *name); +struct connman_mesh *connman_get_connecting_mesh_from_name(char *name); +void connman_mesh_set_index(struct connman_mesh *mesh, int index); +void connman_mesh_set_strength(struct connman_mesh *mesh, uint8_t strength); +void connman_mesh_peer_set_disconnect_reason(struct connman_mesh *mesh, + int disconnect_reason); +void __connman_mesh_add_ethernet_to_bridge(void); +void __connman_mesh_remove_ethernet_from_bridge(void); +int __connman_mesh_change_peer_status(DBusMessage *msg, + const char *peer_address, + enum connman_mesh_peer_status status); + +int connman_mesh_register(struct connman_mesh *mesh); +void connman_mesh_unregister(struct connman_mesh *mesh); + +int __connman_mesh_add_virtual_interface(const char *ifname, + const char *parent_ifname, const char *bridge_ifname); + +int __connman_mesh_remove_virtual_interface(const char *ifname); +int __connman_mesh_set_stp_gate_announce(bool gate_announce, int hwmp_rootmode, + int stp); + +const char *connman_mesh_get_interface_name(void); +bool connman_mesh_is_interface_created(void); + +struct connman_mesh *connman_mesh_get(const char *interface_addr, + const char *identifier); + +int connman_mesh_notify_interface_create(bool success); +int connman_mesh_notify_interface_remove(bool success); + +int connman_mesh_add_connected_peer(const char *peer_address); +int connman_mesh_remove_connected_peer(const char *peer_address, int reason); + +typedef void (*mesh_change_peer_status_cb_t) (int result, void *user_data); + +struct connman_mesh_driver { + int (*add_interface) (const char *ifname, const char *parent_ifname); + int (*remove_interface) (const char *ifname); + int (*connect) (struct connman_mesh *mesh); + int (*disconnect) (struct connman_mesh *mesh); + int (*change_peer_status) (const char *peer_address, + enum connman_mesh_peer_status status, + mesh_change_peer_status_cb_t callback, void *user_data); +}; + +int connman_mesh_driver_register(struct connman_mesh_driver *driver); +void connman_mesh_driver_unregister(struct connman_mesh_driver *driver); + +struct connman_mesh_eth_driver { + int (*add_to_bridge) (const char *bridge); + int (*remove_from_bridge) (const char *bridge); +}; + +int connman_mesh_eth_driver_register(struct connman_mesh_eth_driver *driver); +void connman_mesh_eth_driver_unregister(struct connman_mesh_eth_driver *driver); + +#ifdef __cplusplus +} +#endif + +#endif /* __CONNMAN_MESH_H */ diff --git a/include/service.h b/include/service.h old mode 100755 new mode 100644 index 3055023..961b594 --- a/include/service.h +++ b/include/service.h @@ -49,8 +49,15 @@ enum connman_service_type { CONNMAN_SERVICE_TYPE_VPN = 7, CONNMAN_SERVICE_TYPE_GADGET = 8, CONNMAN_SERVICE_TYPE_P2P = 9, +#if defined TIZEN_EXT_WIFI_MESH + CONNMAN_SERVICE_TYPE_MESH = 10, +#endif }; +#if defined TIZEN_EXT_WIFI_MESH +#define MAX_CONNMAN_SERVICE_TYPES 11 +#else #define MAX_CONNMAN_SERVICE_TYPES 10 +#endif enum connman_service_security { diff --git a/packaging/connman.spec b/packaging/connman.spec old mode 100755 new mode 100644 index 411a651..fd69829 --- a/packaging/connman.spec +++ b/packaging/connman.spec @@ -5,7 +5,7 @@ Name: connman Version: 1.35 -Release: 27 +Release: 28 License: GPL-2.0+ Summary: Connection Manager Url: http://connman.net @@ -18,6 +18,7 @@ BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(libiptc) BuildRequires: pkgconfig(xtables) BuildRequires: pkgconfig(libsmack) +BuildRequires: pkgconfig(libnl-2.0) BuildRequires: pkgconfig(libsystemd-daemon) %if %{with connman_openconnect} BuildRequires: openconnect @@ -141,8 +142,6 @@ This overwrites conf file of %{name}. %build -#CFLAGS+=" -DTIZEN_EXT -lsmack -Werror" - %if %{with connman_vpnd} VPN_CFLAGS+=" -DTIZEN_EXT -lsmack -Werror" %endif @@ -151,9 +150,9 @@ chmod +x bootstrap ./bootstrap %configure \ --sysconfdir=/etc \ - --enable-client \ - --enable-tizen-ext \ - --enable-pacrunner \ + --enable-client \ + --enable-tizen-ext \ + --enable-pacrunner \ --enable-wifi=builtin \ %if %{with connman_openconnect} --enable-openconnect \ @@ -170,11 +169,11 @@ chmod +x bootstrap --disable-ofono \ --enable-telephony=builtin \ --enable-test \ - --enable-loopback \ - --enable-ethernet \ - --with-systemdunitdir=%{_libdir}/systemd/system \ - --enable-pie \ - --disable-wispr + --enable-loopback \ + --enable-ethernet \ + --with-systemdunitdir=%{_libdir}/systemd/system \ + --enable-pie \ + --disable-wispr make %{?_smp_mflags} diff --git a/plugins/ethernet.c b/plugins/ethernet.c old mode 100755 new mode 100644 index 9a4d741..aadfe89 --- a/plugins/ethernet.c +++ b/plugins/ethernet.c @@ -48,6 +48,9 @@ #include #include #include +#if defined TIZEN_EXT_WIFI_MESH +#include +#endif static bool eth_tethering = false; @@ -252,6 +255,10 @@ static void ethernet_newlink(unsigned flags, unsigned change, void *user_data) } else { DBG("carrier off"); remove_network(device, ethernet); +#if defined TIZEN_EXT_WIFI_MESH + /* Remove ethernet from mesh bridge */ + __connman_mesh_remove_ethernet_from_bridge(); +#endif } } @@ -430,6 +437,54 @@ static struct connman_technology_driver eth_tech_driver = { .set_tethering = eth_tech_set_tethering, }; +#if defined TIZEN_EXT_WIFI_MESH +static int eth_mesh_add_to_bridge(const char *bridge) +{ + GList *list; + struct ethernet_data *ethernet; + + DBG("Add ethernet to bridge %s", bridge); + + for (list = eth_interface_list; list; list = list->next) { + int index = GPOINTER_TO_INT(list->data); + struct connman_device *device = + connman_device_find_by_index(index); + + if (device) { + ethernet = connman_device_get_data(device); + if (ethernet) + remove_network(device, ethernet); + } + + connman_inet_ifup(index); + + connman_inet_add_to_bridge(index, bridge); + } + + return 0; +} + +static int eth_mesh_remove_from_bridge(const char *bridge) +{ + GList *list; + + DBG("Remove ethernet from bridge %s", bridge); + + for (list = eth_interface_list; list; list = list->next) { + int index = GPOINTER_TO_INT(list->data); + + connman_inet_remove_from_bridge(index, bridge); + } + + return 0; +} + +static struct connman_mesh_eth_driver eth_mesh_driver = { + .add_to_bridge = eth_mesh_add_to_bridge, + .remove_from_bridge = eth_mesh_remove_from_bridge, +}; +#endif + static int ethernet_init(void) { int err; @@ -438,6 +493,12 @@ static int ethernet_init(void) if (err < 0) return err; +#if defined TIZEN_EXT_WIFI_MESH + err = connman_mesh_eth_driver_register(ð_mesh_driver); + if (err < 0) + return err; +#endif + err = connman_network_driver_register(ð_network_driver); if (err < 0) return err; @@ -455,6 +516,10 @@ static void ethernet_exit(void) { connman_technology_driver_unregister(ð_tech_driver); +#if defined TIZEN_EXT_WIFI_MESH + connman_mesh_eth_driver_unregister(ð_mesh_driver); +#endif + connman_network_driver_unregister(ð_network_driver); connman_device_driver_unregister(ð_dev_driver); diff --git a/plugins/wifi.c b/plugins/wifi.c old mode 100755 new mode 100644 index 6103dc8..8e8017d --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -163,6 +163,10 @@ struct wifi_data { #endif int disconnect_code; int assoc_code; +#if defined TIZEN_EXT_WIFI_MESH + bool mesh_interface; + struct wifi_mesh_info *mesh_info; +#endif }; #if defined TIZEN_EXT @@ -347,6 +351,506 @@ static void add_pending_wifi_device(struct wifi_data *wifi) pending_wifi_device = g_list_append(pending_wifi_device, wifi); } +#if defined TIZEN_EXT_WIFI_MESH +struct wifi_mesh_info { + struct wifi_data *wifi; + GSupplicantInterface *interface; + struct connman_mesh *mesh; + char *parent_ifname; + char *ifname; + char *identifier; + int index; +}; + +struct mesh_change_peer_status_info { + char *peer_address; + enum connman_mesh_peer_status peer_status; + mesh_change_peer_status_cb_t callback; + void *user_data; +}; + +static struct connman_technology_driver mesh_tech_driver = { + .name = "mesh", + .type = CONNMAN_SERVICE_TYPE_MESH, +}; + +static void mesh_interface_create_callback(int result, + GSupplicantInterface *interface, + void *user_data) +{ + struct wifi_mesh_info *mesh_info = user_data; + struct wifi_data *wifi; + bool success = false; + + DBG("result %d ifname %s, mesh_info %p", result, + g_supplicant_interface_get_ifname(interface), + mesh_info); + + if (result < 0 || !mesh_info) + goto done; + + wifi = mesh_info->wifi; + + mesh_info->interface = interface; + mesh_info->identifier = connman_inet_ifaddr(mesh_info->ifname); + mesh_info->index = connman_inet_ifindex(mesh_info->ifname); + DBG("Mesh Interface identifier %s", mesh_info->identifier); + wifi->mesh_interface = true; + wifi->mesh_info = mesh_info; + g_supplicant_interface_set_data(interface, wifi); + success = true; + +done: + connman_mesh_notify_interface_create(success); +} + +static int add_mesh_interface(const char *ifname, const char *parent_ifname) +{ + GList *list; + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + const char *wifi_ifname; + bool parent_found = false; + const char *driver = "nl80211"; + + for (list = iface_list; list; list = list->next) { + wifi = list->data; + + if (!g_supplicant_interface_has_mesh(wifi->interface)) + continue; + + wifi_ifname = g_supplicant_interface_get_ifname(wifi->interface); + if (!wifi_ifname) + continue; + + if (!g_strcmp0(wifi_ifname, parent_ifname)) { + parent_found = true; + break; + } + } + + if (!parent_found) { + DBG("Parent interface %s doesn't exist", parent_ifname); + return -ENODEV; + } + + mesh_info = g_try_malloc0(sizeof(struct wifi_mesh_info)); + if (!mesh_info) + return -ENOMEM; + + mesh_info->wifi = wifi; + mesh_info->ifname = g_strdup(ifname); + mesh_info->parent_ifname = g_strdup(parent_ifname); + + g_supplicant_mesh_interface_create(ifname, driver, NULL, parent_ifname, + mesh_interface_create_callback, mesh_info); + return -EINPROGRESS; +} + +static void mesh_interface_remove_callback(int result, + GSupplicantInterface *interface, + void *user_data) +{ + struct wifi_data *wifi = user_data; + struct wifi_mesh_info *mesh_info = wifi->mesh_info; + bool success = false; + + DBG("result %d mesh_info %p", result, mesh_info); + + if (result < 0 || !mesh_info) + goto done; + + mesh_info->interface = NULL; + g_free(mesh_info->parent_ifname); + g_free(mesh_info->ifname); + g_free(mesh_info->identifier); + g_free(mesh_info); + wifi->mesh_interface = false; + wifi->mesh_info = NULL; + success = true; + +done: + connman_mesh_notify_interface_remove(success); +} + +static int remove_mesh_interface(const char *ifname) +{ + GList *list; + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + bool mesh_if_found = false; + int ret; + + for (list = iface_list; list; list = list->next) { + wifi = list->data; + + if (wifi->mesh_interface) { + mesh_if_found = true; + break; + } + } + + if (!mesh_if_found) { + DBG("Mesh interface %s doesn't exist", ifname); + return -ENODEV; + } + + mesh_info = wifi->mesh_info; + ret = g_supplicant_interface_remove(mesh_info->interface, + mesh_interface_remove_callback, wifi); + if (ret < 0) + return ret; + + return -EINPROGRESS; +} + +static void mesh_disconnect_callback(int result, + GSupplicantInterface *interface, void *user_data) +{ + struct connman_mesh *mesh = user_data; + + DBG("result %d interface %p mesh %p", result, interface, mesh); +} + +static int mesh_peer_disconnect(struct connman_mesh *mesh) +{ + GList *list; + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + bool mesh_if_found = false; + GSupplicantInterface *interface; + + for (list = iface_list; list; list = list->next) { + wifi = list->data; + + if (wifi->mesh_interface) { + mesh_if_found = true; + break; + } + } + + if (!mesh_if_found) { + DBG("Mesh interface is not created"); + return -ENODEV; + } + + mesh_info = wifi->mesh_info; + + interface = mesh_info->interface; + return g_supplicant_interface_disconnect(interface, + mesh_disconnect_callback, mesh); +} + +static void mesh_connect_callback(int result, GSupplicantInterface *interface, + void *user_data) +{ + struct connman_mesh *mesh = user_data; + DBG("mesh %p result %d", mesh, result); + + if (result < 0) + connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_FAILURE); + else + connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_ASSOCIATION); +} + +static GSupplicantSecurity mesh_network_security(const char *security) +{ + if (g_str_equal(security, "none")) + return G_SUPPLICANT_SECURITY_NONE; + else if (g_str_equal(security, "sae")) + return G_SUPPLICANT_SECURITY_SAE; + + return G_SUPPLICANT_SECURITY_UNKNOWN; +} + +static void mesh_ssid_init(GSupplicantSSID *ssid, struct connman_mesh *mesh) +{ + const char *name; + const char *security; + + if (ssid->ssid) + g_free(ssid->ssid); + + memset(ssid, 0, sizeof(*ssid)); + ssid->mode = G_SUPPLICANT_MODE_MESH; + + security = connman_mesh_get_security(mesh); + ssid->security = mesh_network_security(security); + + if (ssid->security == G_SUPPLICANT_SECURITY_SAE) + ssid->passphrase = connman_mesh_get_passphrase(mesh); + + ssid->freq = connman_mesh_get_frequency(mesh); + name = connman_mesh_get_name(mesh); + if (name) { + ssid->ssid_len = strlen(name); + ssid->ssid = g_malloc0(ssid->ssid_len + 1); + memcpy(ssid->ssid, name, ssid->ssid_len); + ssid->scan_ssid = 1; + } +} + +static int mesh_peer_connect(struct connman_mesh *mesh) +{ + GList *list; + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + bool mesh_if_found = false; + GSupplicantInterface *interface; + GSupplicantSSID *ssid; + + for (list = iface_list; list; list = list->next) { + wifi = list->data; + + if (wifi->mesh_interface) { + mesh_if_found = true; + break; + } + } + + if (!mesh_if_found) { + DBG("Mesh interface is not created"); + return -ENODEV; + } + + mesh_info = wifi->mesh_info; + + interface = mesh_info->interface; + + ssid = g_try_malloc0(sizeof(GSupplicantSSID)); + if (!ssid) + return -ENOMEM; + + mesh_info->mesh = mesh; + + mesh_ssid_init(ssid, mesh); + return g_supplicant_interface_connect(interface, ssid, + mesh_connect_callback, mesh); +} + +static void mesh_peer_change_status_callback(int result, + GSupplicantInterface *interface, + void *user_data) +{ + struct mesh_change_peer_status_info *data = user_data; + + DBG("result %d Peer Status %d", result, data->peer_status); + + if (result == 0 && data->peer_status == CONNMAN_MESH_PEER_REMOVE) { + /* WLAN_REASON_MESH_PEERING_CANCELLED = 52 */ + connman_mesh_remove_connected_peer(data->peer_address, 52); + } + + if (data->callback) + data->callback(result, data->user_data); + + g_free(data->peer_address); + g_free(data); + return; +} + +static int mesh_change_peer_status(const char *peer_address, + enum connman_mesh_peer_status status, + mesh_change_peer_status_cb_t callback, void *user_data) +{ + GList *list; + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + bool mesh_if_found = false; + GSupplicantInterface *interface; + struct mesh_change_peer_status_info *data; + const char *method; + + for (list = iface_list; list; list = list->next) { + wifi = list->data; + + if (wifi->mesh_interface) { + mesh_if_found = true; + break; + } + } + + if (!mesh_if_found) { + DBG("Mesh interface is not created"); + return -ENODEV; + } + + mesh_info = wifi->mesh_info; + + interface = mesh_info->interface; + + switch (status) { + case CONNMAN_MESH_PEER_ADD: + method = "MeshPeerAdd"; + break; + case CONNMAN_MESH_PEER_REMOVE: + method = "MeshPeerRemove"; + break; + default: + DBG("Invalid method"); + return -EINVAL; + } + + data = g_try_malloc0(sizeof(struct mesh_change_peer_status_info)); + if (data == NULL) { + DBG("Memory allocation failed"); + return -ENOMEM; + } + + data->peer_address = g_strdup(peer_address); + data->peer_status = status; + data->callback = callback; + data->user_data = user_data; + + return g_supplicant_interface_mesh_peer_change_status(interface, + mesh_peer_change_status_callback, peer_address, method, + data); +} + +static struct connman_mesh_driver mesh_driver = { + .add_interface = add_mesh_interface, + .remove_interface = remove_mesh_interface, + .connect = mesh_peer_connect, + .disconnect = mesh_peer_disconnect, + .change_peer_status = mesh_change_peer_status, +}; + +static void mesh_support(GSupplicantInterface *interface) +{ + DBG(""); + + if (!g_supplicant_interface_has_mesh(interface)) + return; + + if (connman_technology_driver_register(&mesh_tech_driver) < 0) { + DBG("Could not register Mesh technology driver"); + return; + } + + connman_mesh_driver_register(&mesh_driver); +} + +static void check_mesh_technology(void) +{ + bool mesh_exists = false; + GList *list; + + for (list = iface_list; list; list = list->next) { + struct wifi_data *w = list->data; + + if (w->interface && + g_supplicant_interface_has_mesh(w->interface)) + mesh_exists = true; + } + + if (!mesh_exists) { + connman_technology_driver_unregister(&mesh_tech_driver); + connman_mesh_driver_unregister(&mesh_driver); + } +} + +static void mesh_group_started(GSupplicantInterface *interface) +{ + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + struct connman_mesh *mesh; + const unsigned char *ssid; + unsigned int ssid_len; + char name[33]; + + ssid = g_supplicant_interface_get_mesh_group_ssid(interface, &ssid_len); + memcpy(name, ssid, ssid_len); + name[ssid_len] = '\0'; + DBG("name %s", name); + wifi = g_supplicant_interface_get_data(interface); + DBG("wifi %p", wifi); + + if (!wifi) + return; + + mesh_info = wifi->mesh_info; + if (!mesh_info) + return; + + mesh = mesh_info->mesh; + if (!mesh) + return; + + connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_CONFIGURATION); +} + +static void mesh_group_removed(GSupplicantInterface *interface) +{ + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + struct connman_mesh *mesh; + const unsigned char *ssid; + unsigned int ssid_len; + int disconnect_reason; + char name[33]; + + ssid = g_supplicant_interface_get_mesh_group_ssid(interface, &ssid_len); + memcpy(name, ssid, ssid_len); + name[ssid_len] = '\0'; + DBG("name %s", name); + + disconnect_reason = g_supplicant_mesh_get_disconnect_reason(interface); + DBG("Disconnect Reason %d", disconnect_reason); + + wifi = g_supplicant_interface_get_data(interface); + DBG("wifi %p", wifi); + + if (!wifi) + return; + + mesh_info = wifi->mesh_info; + if (!mesh_info) + return; + + mesh = connman_get_connected_mesh_from_name(name); + if (!mesh) { + DBG("%s is not connected", name); + mesh = connman_get_connecting_mesh_from_name(name); + if (!mesh) { + DBG("%s is not connecting", name); + return; + } + } + + connman_mesh_peer_set_disconnect_reason(mesh, disconnect_reason); + connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_DISCONNECT); +} + +static void mesh_peer_connected(GSupplicantMeshPeer *mesh_peer) +{ + const char *peer_address; + + peer_address = g_supplicant_mesh_peer_get_address(mesh_peer); + + if (!peer_address) + return; + + DBG("Peer %s connected", peer_address); + connman_mesh_add_connected_peer(peer_address); +} + +static void mesh_peer_disconnected(GSupplicantMeshPeer *mesh_peer) +{ + const char *peer_address; + int reason; + + peer_address = g_supplicant_mesh_peer_get_address(mesh_peer); + + if (!peer_address) + return; + + reason = g_supplicant_mesh_peer_get_disconnect_reason(mesh_peer); + + DBG("Peer %s disconnected with reason %d", peer_address, reason); + connman_mesh_remove_connected_peer(peer_address, reason); +} +#endif + static struct wifi_data *get_pending_wifi_data(const char *ifname) { GList *list; @@ -1034,6 +1538,9 @@ static void wifi_remove(struct connman_device *device) iface_list = g_list_remove(iface_list, wifi); check_p2p_technology(); +#if defined TIZEN_EXT_WIFI_MESH + check_mesh_technology(); +#endif remove_pending_wifi_device(wifi); @@ -2094,7 +2601,7 @@ static int wifi_specific_scan(enum connman_service_type type, } memcpy(scan_ssid->ssid, ssid, (ssid_len + 1)); - DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len); + /* DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len); */ scan_ssid->ssid_len = ssid_len; scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid); count++; @@ -2160,7 +2667,7 @@ static int wifi_specific_scan(enum connman_service_type type, } memcpy(scan_ssid->ssid, ssid, (ssid_len + 1)); - DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len); + /* DBG("scan ssid %s len: %d", scan_ssid->ssid, ssid_len); */ scan_ssid->ssid_len = ssid_len; scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid); ap_count++; @@ -2197,6 +2704,159 @@ static int wifi_specific_scan(enum connman_service_type type, } #endif +#if defined TIZEN_EXT_WIFI_MESH +static void mesh_scan_callback(int result, GSupplicantInterface *interface, + void *user_data) +{ + struct connman_device *device = user_data; + struct wifi_data *wifi = connman_device_get_data(device); + bool scanning; + + DBG("result %d wifi %p", result, wifi); + + scanning = connman_device_get_scanning(device); + if (scanning) + connman_device_set_scanning(device, + CONNMAN_SERVICE_TYPE_MESH, false); + + if (scanning) + connman_device_unref(device); +} + +static int mesh_scan(struct connman_device *device) +{ + struct wifi_data *wifi; + struct wifi_mesh_info *mesh_info; + int ret; + + DBG(""); + + wifi = connman_device_get_data(device); + + if (!wifi->mesh_interface) + return -ENOTSUP; + + mesh_info = wifi->mesh_info; + reset_autoscan(device); + connman_device_ref(device); + + ret = g_supplicant_interface_scan(mesh_info->interface, NULL, + mesh_scan_callback, device); + if (ret) + connman_device_unref(device); + else + connman_device_set_scanning(device, + CONNMAN_SERVICE_TYPE_MESH, true); + + return ret; +} + +static void abort_scan_callback(int result, GSupplicantInterface *interface, + void *user_data) +{ + struct connman_device *device = user_data; + struct wifi_data *wifi = connman_device_get_data(device); + + DBG("result %d wifi %p", result, wifi); + + __connman_technology_notify_abort_scan(CONNMAN_SERVICE_TYPE_MESH, result); +} + +static int mesh_abort_scan(enum connman_service_type type, + struct connman_device *device) +{ + struct wifi_data *wifi = connman_device_get_data(device); + struct wifi_mesh_info *mesh_info; + bool scanning; + int ret; + + if (!wifi || !wifi->mesh_interface) + return -ENODEV; + + if (type != CONNMAN_SERVICE_TYPE_MESH) + return -EINVAL; + + mesh_info = wifi->mesh_info; + + scanning = connman_device_get_scanning(device); + if (!scanning) + return -EEXIST; + + ret = g_supplicant_interface_abort_scan(mesh_info->interface, + abort_scan_callback, device); + + return ret; +} + +static int mesh_specific_scan(enum connman_service_type type, + struct connman_device *device, const char *ssid, + unsigned int freq, void *user_data) +{ + struct wifi_data *wifi = connman_device_get_data(device); + GSupplicantScanParams *scan_params = NULL; + struct wifi_mesh_info *mesh_info; + struct scan_ssid *scan_ssid; + bool scanning; + int ret; + + if (!wifi || !wifi->mesh_interface) + return -ENODEV; + + if (type != CONNMAN_SERVICE_TYPE_MESH) + return -EINVAL; + + if (wifi->p2p_device) + return 0; + + mesh_info = wifi->mesh_info; + + scanning = connman_device_get_scanning(device); + if (scanning) + return -EALREADY; + + scan_params = g_try_malloc0(sizeof(GSupplicantScanParams)); + if (!scan_params) + return -ENOMEM; + + scan_ssid = g_try_new(struct scan_ssid, 1); + if (!scan_ssid) { + g_free(scan_params); + return -ENOMEM; + } + + scan_ssid->ssid_len = strlen(ssid); + memcpy(scan_ssid->ssid, ssid, scan_ssid->ssid_len); + scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid); + scan_params->num_ssids = 1; + + scan_params->freqs = g_try_new(uint16_t, 1); + if (!scan_params->freqs) { + g_slist_free_full(scan_params->ssids, g_free); + g_free(scan_params); + return -ENOMEM; + } + + scan_params->freqs[0] = freq; + scan_params->num_freqs = 1; + + reset_autoscan(device); + connman_device_ref(device); + + ret = g_supplicant_interface_scan(mesh_info->interface, scan_params, + mesh_scan_callback, device); + + if (ret == 0) { + connman_device_set_scanning(device, + CONNMAN_SERVICE_TYPE_MESH, true); + } else { + g_supplicant_free_scan_params(scan_params); + connman_device_unref(device); + } + + return ret; +} +#endif + /* * Note that the hidden scan is only used when connecting to this specific * hidden AP first time. It is not used when system autoconnects to hidden AP. @@ -2228,6 +2888,11 @@ static int wifi_scan(enum connman_service_type type, if (type == CONNMAN_SERVICE_TYPE_P2P) return p2p_find(device); +#if defined TIZEN_EXT_WIFI_MESH + if (type == CONNMAN_SERVICE_TYPE_MESH) + return mesh_scan(device); +#endif + DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid); scanning = connman_device_get_scanning(device); @@ -2379,6 +3044,10 @@ static struct connman_device_driver wifi_ng_driver = { #if defined TIZEN_EXT .specific_scan = wifi_specific_scan, #endif +#if defined TIZEN_EXT_WIFI_MESH + .abort_scan = mesh_abort_scan, + .mesh_specific_scan = mesh_specific_scan, +#endif }; static void system_ready(void) @@ -3238,6 +3907,21 @@ static void interface_removed(GSupplicantInterface *interface) wifi = g_supplicant_interface_get_data(interface); +#if defined TIZEN_EXT_WIFI_MESH + if (wifi && wifi->mesh_interface) { + DBG("Notify mesh interface remove"); + connman_mesh_notify_interface_remove(true); + struct wifi_mesh_info *mesh_info = wifi->mesh_info; + g_free(mesh_info->parent_ifname); + g_free(mesh_info->ifname); + g_free(mesh_info->identifier); + g_free(mesh_info); + wifi->mesh_interface = false; + wifi->mesh_info = NULL; + return; + } +#endif + if (wifi) wifi->interface = NULL; @@ -3252,6 +3936,9 @@ static void interface_removed(GSupplicantInterface *interface) connman_device_set_powered(wifi->device, false); check_p2p_technology(); +#if defined TIZEN_EXT_WIFI_MESH + check_mesh_technology(); +#endif } static void set_device_type(const char *type, char dev_type[17]) @@ -3391,6 +4078,97 @@ static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network) return strength; } +#if defined TIZEN_EXT_WIFI_MESH +static void mesh_peer_added(GSupplicantNetwork *supplicant_network) +{ + GSupplicantInterface *interface; + struct wifi_data *wifi; + const char *name, *security; + struct connman_mesh *connman_mesh; + struct wifi_mesh_info *mesh_info; + const unsigned char *bssid; + const char *identifier; + char *address; + uint16_t frequency; + int ret; + + interface = g_supplicant_network_get_interface(supplicant_network); + wifi = g_supplicant_interface_get_data(interface); + if (!wifi || !wifi->mesh_interface) { + DBG("Virtual Mesh interface not created"); + return; + } + + bssid = g_supplicant_network_get_bssid(supplicant_network); + address = g_malloc0(19); + snprintf(address, 19, "%02x:%02x:%02x:%02x:%02x:%02x", bssid[0], bssid[1], + bssid[2], bssid[3], bssid[4], bssid[5]); + + identifier = g_supplicant_network_get_identifier(supplicant_network); + name = g_supplicant_network_get_name(supplicant_network); + security = g_supplicant_network_get_security(supplicant_network); + frequency = g_supplicant_network_get_frequency(supplicant_network); + + mesh_info = wifi->mesh_info; + connman_mesh = connman_mesh_get(mesh_info->identifier, identifier); + if (connman_mesh) + goto done; + + DBG("Mesh Peer name %s identifier %s security %s added", name, identifier, + security); + connman_mesh = connman_mesh_create(mesh_info->identifier, identifier); + connman_mesh_set_name(connman_mesh, name); + connman_mesh_set_security(connman_mesh, security); + connman_mesh_set_frequency(connman_mesh, frequency); + connman_mesh_set_address(connman_mesh, address); + connman_mesh_set_index(connman_mesh, mesh_info->index); + connman_mesh_set_strength(connman_mesh, + calculate_strength(supplicant_network)); + connman_mesh_set_peer_type(connman_mesh, CONNMAN_MESH_PEER_TYPE_DISCOVERED); + + ret = connman_mesh_register(connman_mesh); + if (ret == -EALREADY) + DBG("Mesh Peer is already registered"); + +done: + g_free(address); +} + +static void mesh_peer_removed(GSupplicantNetwork *supplicant_network) +{ + GSupplicantInterface *interface; + struct wifi_data *wifi; + struct connman_mesh *connman_mesh; + struct wifi_mesh_info *mesh_info; + const char *identifier; + + interface = g_supplicant_network_get_interface(supplicant_network); + wifi = g_supplicant_interface_get_data(interface); + if (!wifi || !wifi->mesh_interface) { + DBG("Virtual Mesh interface not created"); + return; + } + + identifier = g_supplicant_network_get_identifier(supplicant_network); + if (!identifier) { + DBG("Failed to get Mesh Peer identifier"); + return; + } + + mesh_info = wifi->mesh_info; + connman_mesh = connman_mesh_get(mesh_info->identifier, identifier); + if (connman_mesh) { + /* Do not unregister connected mesh peer */ + if (connman_mesh_peer_is_connected_state(connman_mesh)) { + DBG("Mesh Peer %s is connected", identifier); + return; + } + DBG("Mesh Peer identifier %s removed", identifier); + connman_mesh_unregister(connman_mesh); + } +} +#endif + static void network_added(GSupplicantNetwork *supplicant_network) { struct connman_network *network; @@ -3417,6 +4195,13 @@ static void network_added(GSupplicantNetwork *supplicant_network) if (!g_strcmp0(mode, "adhoc")) return; +#if defined TIZEN_EXT_WIFI_MESH + if (!g_strcmp0(mode, "mesh")) { + mesh_peer_added(supplicant_network); + return; + } +#endif + interface = g_supplicant_network_get_interface(supplicant_network); wifi = g_supplicant_interface_get_data(interface); name = g_supplicant_network_get_name(supplicant_network); @@ -3543,6 +4328,15 @@ static void network_removed(GSupplicantNetwork *network) const char *name, *identifier; struct connman_network *connman_network; +#if defined TIZEN_EXT_WIFI_MESH + const char *mode; + mode = g_supplicant_network_get_mode(network); + if (!g_strcmp0(mode, "mesh")) { + mesh_peer_removed(network); + return; + } +#endif + interface = g_supplicant_network_get_interface(network); wifi = g_supplicant_interface_get_data(interface); identifier = g_supplicant_network_get_identifier(network); @@ -4026,6 +4820,13 @@ static const GSupplicantCallbacks callbacks = { .debug = debug, .disconnect_reasoncode = disconnect_reasoncode, .assoc_status_code = assoc_status_code, +#if defined TIZEN_EXT_WIFI_MESH + .mesh_support = mesh_support, + .mesh_group_started = mesh_group_started, + .mesh_group_removed = mesh_group_removed, + .mesh_peer_connected = mesh_peer_connected, + .mesh_peer_disconnected = mesh_peer_disconnected, +#endif }; diff --git a/src/config.c b/src/config.c old mode 100755 new mode 100644 index 75cd717..0126711 --- 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 old mode 100755 new mode 100644 index d1069b9..905467e --- 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 + +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 typedef void (* service_iterate_cb) (struct connman_service *service, diff --git a/src/device.c b/src/device.c old mode 100755 new mode 100644 index 3ec8f71..8b77021 --- 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 old mode 100755 new mode 100644 index c428c1d..26a350b --- 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 old mode 100755 new mode 100644 index 4f24ae2..1a05920 --- 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 old mode 100755 new mode 100644 index 93be1e7..25e5372 --- 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 old mode 100755 new mode 100644 index 4bc2266..7a6d802 --- 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 old mode 100755 new mode 100644 index 2e1367f..bd44fea --- 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 0000000..22d6925 --- /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 +#endif + +#include "connman.h" +#include +#include +#include +#include +#include +#include +#include + +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 0000000..b902934 --- /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 +#endif + +#include +#include + +#include +#include "connman.h" +#include +#include +#include +#include +#include +#include +#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 old mode 100755 new mode 100644 index 5ba5324..7c3d031 --- 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 old mode 100755 new mode 100644 index ac29f31..d733288 --- 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 old mode 100755 new mode 100644 index d7a7039..d13aafb --- 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 old mode 100755 new mode 100644 index 9e3c559..26cbf87 --- 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 old mode 100755 new mode 100644 index 57ab8e1..6604599 --- 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 old mode 100755 new mode 100644 index adf6230..a2df55a --- 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; } -- 2.7.4