5 * Copyright (C) 2007 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
36 #include <dbus/dbus.h>
39 #include <connman/log.h>
41 #include "supplicant.h"
43 enum supplicant_state {
49 STATE_GROUP_HANDSHAKE,
54 struct supplicant_task {
58 struct connman_iface *iface;
62 enum supplicant_state state;
65 static GSList *tasks = NULL;
67 struct supplicant_ap {
76 #define IEEE80211_CAP_ESS 0x0001
77 #define IEEE80211_CAP_IBSS 0x0002
78 #define IEEE80211_CAP_PRIVACY 0x0010
80 static struct supplicant_task *find_task(int ifindex)
84 for (list = tasks; list; list = list->next) {
85 struct supplicant_task *task = list->data;
87 if (task->ifindex == ifindex)
94 static int get_interface(struct supplicant_task *task)
96 DBusMessage *message, *reply;
100 DBG("task %p", task);
102 message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
103 SUPPLICANT_INTF, "getInterface");
107 dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
110 dbus_error_init(&error);
112 reply = dbus_connection_send_with_reply_and_block(task->conn,
113 message, -1, &error);
115 if (dbus_error_is_set(&error) == TRUE) {
116 connman_error("%s", error.message);
117 dbus_error_free(&error);
119 connman_error("Failed to get interface");
120 dbus_message_unref(message);
124 dbus_message_unref(message);
126 dbus_error_init(&error);
128 if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
129 DBUS_TYPE_INVALID) == FALSE) {
130 if (dbus_error_is_set(&error) == TRUE) {
131 connman_error("%s", error.message);
132 dbus_error_free(&error);
134 connman_error("Wrong arguments for interface");
135 dbus_message_unref(reply);
139 DBG("path %s", path);
141 task->path = g_strdup(path);
142 task->created = FALSE;
144 dbus_message_unref(reply);
149 static int add_interface(struct supplicant_task *task)
151 DBusMessage *message, *reply;
155 DBG("task %p", task);
157 message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
158 SUPPLICANT_INTF, "addInterface");
162 dbus_error_init(&error);
164 dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
167 reply = dbus_connection_send_with_reply_and_block(task->conn,
168 message, -1, &error);
170 if (dbus_error_is_set(&error) == TRUE) {
171 connman_error("%s", error.message);
172 dbus_error_free(&error);
174 connman_error("Failed to add interface");
175 dbus_message_unref(message);
179 dbus_message_unref(message);
181 dbus_error_init(&error);
183 if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
184 DBUS_TYPE_INVALID) == FALSE) {
185 if (dbus_error_is_set(&error) == TRUE) {
186 connman_error("%s", error.message);
187 dbus_error_free(&error);
189 connman_error("Wrong arguments for interface");
190 dbus_message_unref(reply);
194 DBG("path %s", path);
196 task->path = g_strdup(path);
197 task->created = TRUE;
199 dbus_message_unref(reply);
204 static int add_network(struct supplicant_task *task)
206 DBusMessage *message, *reply;
210 DBG("task %p", task);
212 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
213 SUPPLICANT_INTF ".Interface", "addNetwork");
217 dbus_error_init(&error);
219 reply = dbus_connection_send_with_reply_and_block(task->conn,
220 message, -1, &error);
222 if (dbus_error_is_set(&error) == TRUE) {
223 connman_error("%s", error.message);
224 dbus_error_free(&error);
226 connman_error("Failed to add network");
227 dbus_message_unref(message);
231 dbus_message_unref(message);
233 dbus_error_init(&error);
235 if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
236 DBUS_TYPE_INVALID) == FALSE) {
237 if (dbus_error_is_set(&error) == TRUE) {
238 connman_error("%s", error.message);
239 dbus_error_free(&error);
241 connman_error("Wrong arguments for network");
242 dbus_message_unref(reply);
246 DBG("path %s", path);
248 task->network = g_strdup(path);
250 dbus_message_unref(reply);
255 static int remove_network(struct supplicant_task *task)
257 DBusMessage *message, *reply;
260 DBG("task %p", task);
262 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
263 SUPPLICANT_INTF ".Interface", "removeNetwork");
267 dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network,
270 dbus_error_init(&error);
272 reply = dbus_connection_send_with_reply_and_block(task->conn,
273 message, -1, &error);
275 if (dbus_error_is_set(&error) == TRUE) {
276 connman_error("%s", error.message);
277 dbus_error_free(&error);
279 connman_error("Failed to remove network");
280 dbus_message_unref(message);
284 dbus_message_unref(message);
286 dbus_message_unref(reply);
291 static int select_network(struct supplicant_task *task)
293 DBusMessage *message, *reply;
296 DBG("task %p", task);
298 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
299 SUPPLICANT_INTF ".Interface", "selectNetwork");
303 dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network,
306 dbus_error_init(&error);
308 reply = dbus_connection_send_with_reply_and_block(task->conn,
309 message, -1, &error);
311 if (dbus_error_is_set(&error) == TRUE) {
312 connman_error("%s", error.message);
313 dbus_error_free(&error);
315 connman_error("Failed to select network");
316 dbus_message_unref(message);
320 dbus_message_unref(message);
322 dbus_message_unref(reply);
327 static int enable_network(struct supplicant_task *task)
329 DBusMessage *message, *reply;
332 DBG("task %p", task);
334 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
335 SUPPLICANT_INTF ".Network", "enable");
339 dbus_error_init(&error);
341 reply = dbus_connection_send_with_reply_and_block(task->conn,
342 message, -1, &error);
344 if (dbus_error_is_set(&error) == TRUE) {
345 connman_error("%s", error.message);
346 dbus_error_free(&error);
348 connman_error("Failed to enable network");
349 dbus_message_unref(message);
353 dbus_message_unref(message);
355 dbus_message_unref(reply);
360 static int disable_network(struct supplicant_task *task)
362 DBusMessage *message, *reply;
365 DBG("task %p", task);
367 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
368 SUPPLICANT_INTF ".Network", "disable");
372 dbus_error_init(&error);
374 reply = dbus_connection_send_with_reply_and_block(task->conn,
375 message, -1, &error);
377 if (dbus_error_is_set(&error) == TRUE) {
378 connman_error("%s", error.message);
379 dbus_error_free(&error);
381 connman_error("Failed to disable network");
382 dbus_message_unref(message);
386 dbus_message_unref(message);
388 dbus_message_unref(reply);
393 static void append_entry(DBusMessageIter *dict,
394 const char *key, int type, void *val)
396 DBusMessageIter entry, value;
397 const char *signature;
399 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
402 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
405 case DBUS_TYPE_STRING:
406 signature = DBUS_TYPE_STRING_AS_STRING;
408 case DBUS_TYPE_UINT16:
409 signature = DBUS_TYPE_UINT16_AS_STRING;
412 signature = DBUS_TYPE_VARIANT_AS_STRING;
416 dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
418 dbus_message_iter_append_basic(&value, type, val);
419 dbus_message_iter_close_container(&entry, &value);
421 dbus_message_iter_close_container(dict, &entry);
424 static int set_network(struct supplicant_task *task, const char *network,
425 const char *passphrase)
427 DBusMessage *message, *reply;
428 DBusMessageIter array, dict;
431 DBG("task %p", task);
433 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
434 SUPPLICANT_INTF ".Network", "set");
438 dbus_message_iter_init_append(message, &array);
440 dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
441 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
442 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
443 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
445 append_entry(&dict, "ssid", DBUS_TYPE_STRING, &network);
447 if (passphrase && strlen(passphrase) > 0) {
448 const char *key_mgmt = "WPA-PSK";
449 append_entry(&dict, "key_mgmt", DBUS_TYPE_STRING, &key_mgmt);
450 append_entry(&dict, "psk", DBUS_TYPE_STRING, &passphrase);
452 const char *key_mgmt = "NONE";
453 append_entry(&dict, "key_mgmt", DBUS_TYPE_STRING, &key_mgmt);
456 dbus_message_iter_close_container(&array, &dict);
458 dbus_error_init(&error);
460 reply = dbus_connection_send_with_reply_and_block(task->conn,
461 message, -1, &error);
463 if (dbus_error_is_set(&error) == TRUE) {
464 connman_error("%s", error.message);
465 dbus_error_free(&error);
467 connman_error("Failed to set network options");
468 dbus_message_unref(message);
472 dbus_message_unref(message);
474 dbus_message_unref(reply);
479 static int initiate_scan(struct supplicant_task *task)
481 DBusMessage *message, *reply;
484 DBG("task %p", task);
486 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
487 SUPPLICANT_INTF ".Interface", "scan");
491 dbus_error_init(&error);
493 reply = dbus_connection_send_with_reply_and_block(task->conn,
494 message, -1, &error);
496 if (dbus_error_is_set(&error) == TRUE) {
497 connman_error("%s", error.message);
498 dbus_error_free(&error);
500 connman_error("Failed to initiate scan");
501 dbus_message_unref(message);
505 dbus_message_unref(message);
507 dbus_message_unref(reply);
512 static void extract_ssid(struct supplicant_ap *ap, DBusMessageIter *value)
514 DBusMessageIter array;
518 dbus_message_iter_recurse(value, &array);
519 dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
521 ap->identifier = g_strdup((char *) ssid);
524 static void extract_wpaie(struct supplicant_ap *ap, DBusMessageIter *value)
526 DBusMessageIter array;
530 dbus_message_iter_recurse(value, &array);
531 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
537 static void extract_rsnie(struct supplicant_ap *ap, DBusMessageIter *value)
539 DBusMessageIter array;
543 dbus_message_iter_recurse(value, &array);
544 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
550 static void extract_capabilites(struct supplicant_ap *ap,
551 DBusMessageIter *value)
555 dbus_message_iter_get_basic(value, &capabilities);
557 ap->capabilities = capabilities;
559 if (capabilities & IEEE80211_CAP_PRIVACY)
563 static int parse_network_properties(struct supplicant_task *task,
564 DBusMessage *message)
566 DBusMessageIter array, dict;
567 struct supplicant_ap *ap;
570 DBG("task %p", task);
572 ap = g_try_new0(struct supplicant_ap, 1);
576 dbus_message_iter_init(message, &array);
578 dbus_message_iter_recurse(&array, &dict);
580 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
581 DBusMessageIter entry, value;
584 dbus_message_iter_recurse(&dict, &entry);
585 dbus_message_iter_get_basic(&entry, &key);
587 dbus_message_iter_next(&entry);
589 dbus_message_iter_recurse(&entry, &value);
591 //type = dbus_message_iter_get_arg_type(&value);
592 //dbus_message_iter_get_basic(&value, &val);
594 if (g_str_equal(key, "ssid") == TRUE)
595 extract_ssid(ap, &value);
596 else if (g_str_equal(key, "wpaie") == TRUE)
597 extract_wpaie(ap, &value);
598 else if (g_str_equal(key, "rsnie") == TRUE)
599 extract_rsnie(ap, &value);
600 else if (g_str_equal(key, "capabilities") == TRUE)
601 extract_capabilites(ap, &value);
603 dbus_message_iter_next(&dict);
606 DBG("SSID %s", ap->identifier);
615 connman_iface_indicate_station(task->iface,
616 ap->identifier, 25, security);
623 static int get_network_properties(struct supplicant_task *task,
626 DBusMessage *message, *reply;
629 DBG("task %p", task);
631 message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
632 SUPPLICANT_INTF ".BSSID",
637 dbus_error_init(&error);
639 reply = dbus_connection_send_with_reply_and_block(task->conn,
640 message, -1, &error);
642 if (dbus_error_is_set(&error) == TRUE) {
643 connman_error("%s", error.message);
644 dbus_error_free(&error);
646 connman_error("Failed to get network properties");
647 dbus_message_unref(message);
651 dbus_message_unref(message);
653 parse_network_properties(task, reply);
655 dbus_message_unref(reply);
660 static int scan_results_available(struct supplicant_task *task)
662 DBusMessage *message, *reply;
667 DBG("task %p", task);
669 message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
670 SUPPLICANT_INTF ".Interface",
675 dbus_error_init(&error);
677 reply = dbus_connection_send_with_reply_and_block(task->conn,
678 message, -1, &error);
680 if (dbus_error_is_set(&error) == TRUE) {
681 connman_error("%s", error.message);
682 dbus_error_free(&error);
684 connman_error("Failed to request scan result");
685 dbus_message_unref(message);
689 dbus_message_unref(message);
691 dbus_error_init(&error);
693 if (dbus_message_get_args(reply, &error,
694 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
695 &results, &num_results,
696 DBUS_TYPE_INVALID) == FALSE) {
697 if (dbus_error_is_set(&error) == TRUE) {
698 connman_error("%s", error.message);
699 dbus_error_free(&error);
701 connman_error("Wrong arguments for scan result");
702 dbus_message_unref(reply);
706 for (i = 0; i < num_results; i++)
707 get_network_properties(task, results[i]);
711 dbus_message_unref(reply);
716 static void state_change(struct supplicant_task *task, DBusMessage *msg)
719 const char *state, *previous;
721 dbus_error_init(&error);
723 if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &state,
724 DBUS_TYPE_STRING, &previous,
725 DBUS_TYPE_INVALID) == FALSE) {
726 if (dbus_error_is_set(&error) == TRUE) {
727 connman_error("%s", error.message);
728 dbus_error_free(&error);
730 connman_error("Wrong arguments for state change");
734 DBG("state %s ==> %s", previous, state);
736 if (g_str_equal(state, "INACTIVE") == TRUE)
737 task->state = STATE_INACTIVE;
738 else if (g_str_equal(state, "SCANNING") == TRUE)
739 task->state = STATE_SCANNING;
740 else if (g_str_equal(state, "ASSOCIATING") == TRUE)
741 task->state = STATE_ASSOCIATING;
742 else if (g_str_equal(state, "ASSOCIATED") == TRUE)
743 task->state = STATE_ASSOCIATED;
744 else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
745 task->state = STATE_4WAY_HANDSHAKE;
746 else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
747 task->state = STATE_4WAY_HANDSHAKE;
748 else if (g_str_equal(state, "COMPLETED") == TRUE)
749 task->state = STATE_COMPLETED;
750 else if (g_str_equal(state, "DISCONNECTED") == TRUE)
751 task->state = STATE_DISCONNECTED;
754 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
755 DBusMessage *msg, void *data)
757 struct supplicant_task *task = data;
760 if (dbus_message_has_interface(msg,
761 SUPPLICANT_INTF ".Interface") == FALSE)
762 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
764 member = dbus_message_get_member(msg);
766 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
768 DBG("task %p member %s", task, member);
770 if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
771 scan_results_available(task);
772 else if (g_str_equal(member, "StateChange") == TRUE)
773 state_change(task, msg);
775 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
778 static int add_filter(struct supplicant_task *task)
783 if (dbus_connection_add_filter(task->conn,
784 supplicant_filter, task, NULL) == FALSE)
787 filter = g_strdup_printf("type=signal,interface=%s.Interface,path=%s",
788 SUPPLICANT_INTF, task->path);
790 DBG("filter %s", filter);
792 dbus_error_init(&error);
794 dbus_bus_add_match(task->conn, filter, &error);
798 if (dbus_error_is_set(&error) == TRUE) {
799 connman_error("Can't add match: %s", error.message);
800 dbus_error_free(&error);
806 static int remove_filter(struct supplicant_task *task)
811 filter = g_strdup_printf("type=signal,interface=%s.Interface,path=%s",
812 SUPPLICANT_INTF, task->path);
814 DBG("filter %s", filter);
816 dbus_error_init(&error);
818 dbus_bus_add_match(task->conn, filter, &error);
822 if (dbus_error_is_set(&error) == TRUE) {
823 connman_error("Can't add match: %s", error.message);
824 dbus_error_free(&error);
827 dbus_connection_remove_filter(task->conn, supplicant_filter, task);
832 int __supplicant_start(struct connman_iface *iface)
835 struct supplicant_task *task;
838 sk = socket(PF_INET, SOCK_DGRAM, 0);
842 memset(&ifr, 0, sizeof(ifr));
843 ifr.ifr_ifindex = iface->index;
845 err = ioctl(sk, SIOCGIFNAME, &ifr);
852 DBG("interface %s", ifr.ifr_name);
854 task = g_try_new0(struct supplicant_task, 1);
858 task->ifindex = iface->index;
859 task->ifname = g_strdup(ifr.ifr_name);
862 if (task->ifname == NULL) {
867 task->conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
868 if (task->conn == NULL) {
873 task->created = FALSE;
875 err = get_interface(task);
877 err = add_interface(task);
884 task->state = STATE_INACTIVE;
886 tasks = g_slist_append(tasks, task);
892 select_network(task);
893 disable_network(task);
898 int __supplicant_stop(struct connman_iface *iface)
900 struct supplicant_task *task;
902 task = find_task(iface->index);
906 DBG("interface %s", task->ifname);
908 tasks = g_slist_remove(tasks, task);
912 remove_network(task);
914 dbus_connection_unref(task->conn);
916 g_free(task->ifname);
917 g_free(task->network);
924 int __supplicant_scan(struct connman_iface *iface)
926 struct supplicant_task *task;
929 task = find_task(iface->index);
933 DBG("interface %s", task->ifname);
935 switch (task->state) {
938 case STATE_ASSOCIATING:
939 case STATE_ASSOCIATED:
940 case STATE_4WAY_HANDSHAKE:
941 case STATE_GROUP_HANDSHAKE:
947 err = initiate_scan(task);
952 int __supplicant_connect(struct connman_iface *iface,
953 const char *network, const char *passphrase)
955 struct supplicant_task *task;
957 task = find_task(iface->index);
961 DBG("interface %s", task->ifname);
963 set_network(task, network, passphrase);
965 enable_network(task);
970 int __supplicant_disconnect(struct connman_iface *iface)
972 struct supplicant_task *task;
974 task = find_task(iface->index);
978 DBG("interface %s", task->ifname);
980 disable_network(task);