From 451d80c342f0ed95363b7056221544207323bbaa Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 12 Mar 2008 14:51:59 +0100 Subject: [PATCH] Change 802.11 plugin to use D-Bus interface of supplicant --- plugins/80211.c | 432 +----------------------- plugins/supplicant.c | 903 ++++++++++++++++++++++++++++++++++++++++++++------- plugins/supplicant.h | 6 + 3 files changed, 792 insertions(+), 549 deletions(-) diff --git a/plugins/80211.c b/plugins/80211.c index 1bada4f..d21eec6 100644 --- a/plugins/80211.c +++ b/plugins/80211.c @@ -43,156 +43,10 @@ #include "supplicant.h" -#ifndef IW_EV_LCP_PK_LEN -#define IW_EV_LCP_PK_LEN (4) -#endif - -struct station_data { - char *address; - char *name; - int mode; - int qual; - int noise; - int level; - - unsigned char wpa_ie[40]; - int wpa_ie_len; - unsigned char rsn_ie[40]; - int rsn_ie_len; - - int has_wep; - int has_wpa; - int has_rsn; -}; - struct iface_data { char ifname[IFNAMSIZ]; - GSList *stations; - - gchar *network; - gchar *passphrase; }; -static void report_station(struct connman_iface *iface, - struct station_data *station) -{ - int security = 0; - - if (station == NULL) - return; - - if (station->name == NULL) - return; - - if (station->has_wep) - security |= 0x01; - if (station->has_wpa) - security |= 0x02; - if (station->has_rsn) - security |= 0x04; - - connman_iface_indicate_station(iface, station->name, - station->qual, security); -} - -static struct station_data *create_station(struct iface_data *iface, - const char *address) -{ - struct station_data *station; - GSList *list; - - for (list = iface->stations; list; list = list->next) { - station = list->data; - - if (g_ascii_strcasecmp(station->address, address) == 0) - return station; - } - - station = g_try_new0(struct station_data, 1); - if (station == NULL) - return NULL; - - station->address = g_strdup(address); - if (station->address == NULL) { - g_free(station); - return NULL; - } - - iface->stations = g_slist_append(iface->stations, station); - - return station; -} - -static void load_stations(struct iface_data *iface) -{ - GKeyFile *keyfile; - gchar **groups, **group; - gsize length; - - keyfile = g_key_file_new(); - - if (g_key_file_load_from_file(keyfile, "/tmp/stations.list", - G_KEY_FILE_KEEP_COMMENTS, NULL) == FALSE) - goto done; - - groups = g_key_file_get_groups(keyfile, &length); - - for (group = groups; *group; group++) { - struct station_data *station; - - station = create_station(iface, *group); - if (station == NULL) - continue; - - station->name = g_key_file_get_string(keyfile, - *group, "Name", NULL); - - station->mode = g_key_file_get_integer(keyfile, - *group, "Mode", NULL); - } - - g_strfreev(groups); - -done: - g_key_file_free(keyfile); - - printf("[802.11] loaded %d stations\n", - g_slist_length(iface->stations)); -} - -static void print_stations(struct iface_data *iface) -{ - GKeyFile *keyfile; - gchar *data; - gsize length; - GSList *list; - - keyfile = g_key_file_new(); - - for (list = iface->stations; list; list = list->next) { - struct station_data *station = list->data; - - //printf("Address:%s Mode:%d ESSID:\"%s\" Quality:%d/100\n", - // station->address, station->mode, - // station->name, station->qual); - - if (station->name == NULL) - continue; - - g_key_file_set_string(keyfile, station->address, - "Name", station->name); - - g_key_file_set_integer(keyfile, station->address, - "Mode", station->mode); - } - - data = g_key_file_to_data(keyfile, &length, NULL); - - g_file_set_contents("/tmp/stations.list", data, length, NULL); - - g_key_file_free(keyfile); -} - static int wifi_probe(struct connman_iface *iface) { struct iface_data *data; @@ -231,8 +85,6 @@ static int wifi_probe(struct connman_iface *iface) connman_iface_set_data(iface, data); - load_stations(data); - return 0; } @@ -246,9 +98,6 @@ static void wifi_remove(struct connman_iface *iface) connman_iface_set_data(iface, NULL); - g_free(data->network); - g_free(data->passphrase); - free(data); } @@ -277,36 +126,12 @@ static int wifi_stop(struct connman_iface *iface) static int wifi_scan(struct connman_iface *iface) { struct iface_data *data = connman_iface_get_data(iface); - struct iwreq iwr; - struct iw_scan_req iws; - int sk, err; DBG("iface %p %s", iface, data->ifname); - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -EIO; - - memset(&iws, 0, sizeof(iws)); - iws.scan_type = IW_SCAN_TYPE_PASSIVE; - //iws.scan_type = IW_SCAN_TYPE_ACTIVE; - - memset(&iwr, 0, sizeof(iwr)); - strncpy(iwr.ifr_name, data->ifname, IFNAMSIZ); + __supplicant_scan(iface); - iwr.u.data.pointer = (caddr_t ) &iws; - iwr.u.data.length = sizeof(iws); - iwr.u.data.flags = IW_SCAN_DEFAULT; - - err = ioctl(sk, SIOCSIWSCAN, &iwr); - - close(sk); - - if (err < 0) - connman_error("%s: scan initiate error %d", - data->ifname, errno); - - return err; + return 0; } static int wifi_connect(struct connman_iface *iface, @@ -316,8 +141,7 @@ static int wifi_connect(struct connman_iface *iface, DBG("iface %p %s", iface, data->ifname); - if (data->network != NULL) - __supplicant_connect(iface, data->network, data->passphrase); + __supplicant_connect(iface, network->identifier, network->passphrase); return 0; } @@ -328,256 +152,11 @@ static int wifi_disconnect(struct connman_iface *iface) DBG("iface %p %s", iface, data->ifname); - if (data->network != NULL) - __supplicant_disconnect(iface); + __supplicant_disconnect(iface); return 0; } -static void wifi_set_network(struct connman_iface *iface, - const char *network) -{ - struct iface_data *data = connman_iface_get_data(iface); - - DBG("iface %p %s", iface, data->ifname); - - g_free(data->network); - - data->network = g_strdup(network); -} - -static void wifi_set_passphrase(struct connman_iface *iface, - const char *passphrase) -{ - struct iface_data *data = connman_iface_get_data(iface); - - DBG("iface %p %s", iface, data->ifname); - - g_free(data->passphrase); - - data->passphrase = g_strdup(passphrase); -} - -static void parse_genie(struct station_data *station, - unsigned char *data, int len) -{ - int offset = 0; - - while (offset <= len - 2) { - //int i; - - switch (data[offset]) { - case 0xdd: /* WPA1 (and other) */ - station->has_wpa = 1; - break; - case 0x30: /* WPA2 (RSN) */ - station->has_rsn = 1; - break; - default: - break; - } - - //for (i = 0; i < len; i++) - // printf(" %02x", data[i]); - //printf("\n"); - - offset += data[offset + 1] + 2; - } -} - -static void parse_scan_results(struct connman_iface *iface, - unsigned char *data, int len) -{ - unsigned char *ptr = data; - struct station_data *station = NULL; - struct ether_addr *eth; - char addr[18]; - int num = 0; - - while (len > IW_EV_LCP_PK_LEN) { - struct iw_event *event = (void *) ptr; - - switch (event->cmd) { - case SIOCGIWAP: - report_station(iface, station); - eth = (void *) &event->u.ap_addr.sa_data; - sprintf(addr, "%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]); - station = create_station(connman_iface_get_data(iface), - addr); - num++; - break; - case SIOCGIWESSID: - if (station != NULL) { - station->name = malloc(event->len - IW_EV_POINT_LEN + 1); - if (station->name != NULL) { - memset(station->name, 0, - event->len - IW_EV_POINT_LEN + 1); - memcpy(station->name, ptr + IW_EV_POINT_LEN, - event->len - IW_EV_POINT_LEN); - } - } - break; - case SIOCGIWNAME: - break; - case SIOCGIWMODE: - if (station != NULL) - station->mode = event->u.mode; - break; - case SIOCGIWFREQ: - break; - case SIOCGIWENCODE: - if (station != NULL) { - if (!event->u.data.pointer) - event->u.data.flags |= IW_ENCODE_NOKEY; - - if (!(event->u.data.flags & IW_ENCODE_DISABLED)) - station->has_wep = 1; - } - break; - case SIOCGIWRATE: - break; - case IWEVQUAL: - if (station != NULL) { - station->qual = event->u.qual.qual; - station->noise = event->u.qual.noise; - station->level = event->u.qual.level; - } - break; - case IWEVGENIE: - if (station != NULL) - parse_genie(station, ptr + 8, event->len - 8); - break; - case IWEVCUSTOM: - break; - default: - printf("[802.11] scan element 0x%04x (len %d)\n", - event->cmd, event->len); - if (event->len == 0) - len = 0; - break; - } - - ptr += event->len; - len -= event->len; - } - - report_station(iface, station); - - printf("[802.11] found %d networks\n", num); -} - -static void scan_results(struct connman_iface *iface) -{ - struct iface_data *data = connman_iface_get_data(iface); - struct iwreq iwr; - void *buf; - size_t size; - int sk, err, done = 0; - - if (data == NULL) - return; - - memset(&iwr, 0, sizeof(iwr)); - memcpy(iwr.ifr_name, data->ifname, IFNAMSIZ); - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return; - - buf = NULL; - size = 1024; - - while (!done) { - void *newbuf; - - newbuf = g_realloc(buf, size); - if (newbuf == NULL) { - close(sk); - return; - } - - buf = newbuf; - iwr.u.data.pointer = buf; - iwr.u.data.length = size; - iwr.u.data.flags = 0; - - err = ioctl(sk, SIOCGIWSCAN, &iwr); - if (err < 0) { - if (errno == E2BIG) - size *= 2; - else - done = 1; - } else { - parse_scan_results(iface, iwr.u.data.pointer, - iwr.u.data.length); - done = 1; - } - } - - g_free(buf); - - close(sk); - - print_stations(data); -} - -static void wifi_wireless(struct connman_iface *iface, - void *data, unsigned short len) -{ - struct iw_event *event = data; - struct iw_point point; - struct ether_addr *eth; - char addr[18]; - - switch (event->cmd) { - case SIOCSIWFREQ: - printf("[802.11] Set Frequency (flags %d)\n", - event->u.freq.flags); - break; - case SIOCSIWMODE: - printf("[802.11] Set Mode (mode %d)\n", event->u.mode); - break; - case SIOCSIWESSID: - memcpy(&point, data + IW_EV_LCP_LEN - - IW_EV_POINT_OFF, sizeof(point)); - point.pointer = data + IW_EV_LCP_LEN + - sizeof(point) - IW_EV_POINT_OFF; - printf("[802.11] Set ESSID (length %d flags %d) \"%s\"\n", - point.length, point.flags, - (char *) point.pointer); - break; - case SIOCSIWENCODE: - printf("[802.11] Set Encryption key (flags %d)\n", - event->u.data.flags); - break; - - case SIOCGIWAP: - eth = (void *) &event->u.ap_addr.sa_data; - sprintf(addr, "%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]); - printf("[802.11] New Access Point %s\n", addr); - break; - case SIOCGIWSCAN: - scan_results(iface); - break; - default: - printf("[802.11] Wireless event (cmd 0x%04x len %d)\n", - event->cmd, event->len); - break; - } -} - static struct connman_iface_driver wifi_driver = { .name = "80211", .capability = "net.80211", @@ -588,9 +167,6 @@ static struct connman_iface_driver wifi_driver = { .scan = wifi_scan, .connect = wifi_connect, .disconnect = wifi_disconnect, - .set_network = wifi_set_network, - .set_passphrase = wifi_set_passphrase, - .rtnl_wireless = wifi_wireless, }; static int wifi_init(void) diff --git a/plugins/supplicant.c b/plugins/supplicant.c index b30b16c..c8853e1 100644 --- a/plugins/supplicant.c +++ b/plugins/supplicant.c @@ -33,21 +33,62 @@ #include #include -#include +#include +#include + +#include #include "supplicant.h" +enum supplicant_state { + STATE_INACTIVE, + STATE_SCANNING, + STATE_ASSOCIATING, + STATE_ASSOCIATED, + STATE_4WAY_HANDSHAKE, + STATE_GROUP_HANDSHAKE, + STATE_COMPLETED, + STATE_DISCONNECTED, +}; + +// COMPLETED ==> ASSOCIATING +// ASSOCIATED ==> DISCONNECTED +// DISCONNECTED ==> INACTIVE + +// DISCONNECTED ==> SCANNING +// SCANNING ==> ASSOCIATED + +// ASSOCIATING ==> ASSOCIATED +// ASSOCIATED ==> 4WAY_HANDSHAKE +// 4WAY_HANDSHAKE ==> GROUP_HANDSHAKE +// GROUP_HANDSHAKE ==> COMPLETED + struct supplicant_task { - GPid pid; + DBusConnection *conn; int ifindex; - char *ifname; + gchar *ifname; struct connman_iface *iface; - int socket; - GIOChannel *channel; + gchar *path; + gboolean created; + gchar *network; + enum supplicant_state state; }; static GSList *tasks = NULL; +struct supplicant_ap { + gchar *identifier; + GByteArray *ssid; + guint capabilities; + gboolean has_wep; + gboolean has_wpa; + gboolean has_rsn; +}; + +#define IEEE80211_CAP_ESS 0x0001 +#define IEEE80211_CAP_IBSS 0x0002 +#define IEEE80211_CAP_PRIVACY 0x0010 + static struct supplicant_task *find_task(int ifindex) { GSList *list; @@ -62,95 +103,715 @@ static struct supplicant_task *find_task(int ifindex) return NULL; } -static int exec_cmd(struct supplicant_task *task, char *cmd) +static int get_interface(struct supplicant_task *task) { - return write(task->socket, cmd, strlen(cmd)); + DBusMessage *message, *reply; + DBusError error; + const char *path; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH, + SUPPLICANT_INTF, "getInterface"); + if (message == NULL) + return -ENOMEM; + + dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname, + DBUS_TYPE_INVALID); + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to get interface"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_error_init(&error); + + if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Wrong arguments for interface"); + dbus_message_unref(reply); + return -EIO; + } + + DBG("path %s", path); + + task->path = g_strdup(path); + task->created = FALSE; + + dbus_message_unref(reply); + + return 0; } -static gboolean control_event(GIOChannel *chan, - GIOCondition cond, gpointer data) +static int add_interface(struct supplicant_task *task) { - struct supplicant_task *task = data; - char buf[256]; - gsize len; - GIOError err; + DBusMessage *message, *reply; + DBusError error; + const char *path; + + DBG("task %p", task); - if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; + message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH, + SUPPLICANT_INTF, "addInterface"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); - memset(buf, 0, sizeof(buf)); + dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname, + DBUS_TYPE_INVALID); - err = g_io_channel_read(chan, buf, sizeof(buf), &len); - if (err) { - if (err == G_IO_ERROR_AGAIN) - return TRUE; - return FALSE; + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to add interface"); + dbus_message_unref(message); + return -EIO; } - if (buf[0] != '<') - return TRUE; + dbus_message_unref(message); - printf("[SUPPLICANT] %s\n", buf + 3); + dbus_error_init(&error); - if (g_str_has_prefix(buf + 3, "CTRL-EVENT-CONNECTED") == TRUE) { - printf("[SUPPLICANT] connected\n"); - connman_iface_indicate_connected(task->iface); + if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Wrong arguments for interface"); + dbus_message_unref(reply); + return -EIO; } - if (g_str_has_prefix(buf + 3, "CTRL-EVENT-DISCONNECTED") == TRUE) { - printf("[SUPPLICANT] disconnected\n"); + DBG("path %s", path); + + task->path = g_strdup(path); + task->created = TRUE; + + dbus_message_unref(reply); + + return 0; +} + +static int add_network(struct supplicant_task *task) +{ + DBusMessage *message, *reply; + DBusError error; + const char *path; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, + SUPPLICANT_INTF ".Interface", "addNetwork"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to add network"); + dbus_message_unref(message); + return -EIO; } - if (g_str_has_prefix(buf + 3, "CTRL-EVENT-TERMINATING") == TRUE) { - printf("[SUPPLICANT] terminating\n"); + dbus_message_unref(message); + + dbus_error_init(&error); + + if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Wrong arguments for network"); + dbus_message_unref(reply); + return -EIO; } - return TRUE; + DBG("path %s", path); + + task->network = g_strdup(path); + + dbus_message_unref(reply); + + return 0; } -static int open_control(struct supplicant_task *task) +static int remove_network(struct supplicant_task *task) { - struct sockaddr_un addr; - int sk; + DBusMessage *message, *reply; + DBusError error; - printf("[SUPPLICANT] open control for %s\n", task->ifname); + DBG("task %p", task); - sk = socket(PF_UNIX, SOCK_DGRAM, 0); - if (sk < 0) - return -1; + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, + SUPPLICANT_INTF ".Interface", "removeNetwork"); + if (message == NULL) + return -ENOMEM; + + dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network, + DBUS_TYPE_INVALID); + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to remove network"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_message_unref(reply); + + return 0; +} + +static int select_network(struct supplicant_task *task) +{ + DBusMessage *message, *reply; + DBusError error; - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/%s.cli", STATEDIR, task->ifname); - //unlink(addr.sun_path); + DBG("task %p", task); - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -1; + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, + SUPPLICANT_INTF ".Interface", "selectNetwork"); + if (message == NULL) + return -ENOMEM; + + dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network, + DBUS_TYPE_INVALID); + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to select network"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_message_unref(reply); + + return 0; +} + +static int enable_network(struct supplicant_task *task) +{ + DBusMessage *message, *reply; + DBusError error; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network, + SUPPLICANT_INTF ".Network", "enable"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to enable network"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_message_unref(reply); + + return 0; +} + +static int disable_network(struct supplicant_task *task) +{ + DBusMessage *message, *reply; + DBusError error; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network, + SUPPLICANT_INTF ".Network", "disable"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to disable network"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_message_unref(reply); + + return 0; +} + +static void append_entry(DBusMessageIter *dict, + const char *key, int type, void *val) +{ + DBusMessageIter entry, value; + const char *signature; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + switch (type) { + case DBUS_TYPE_STRING: + signature = DBUS_TYPE_STRING_AS_STRING; + break; + case DBUS_TYPE_UINT16: + signature = DBUS_TYPE_UINT16_AS_STRING; + break; + default: + signature = DBUS_TYPE_VARIANT_AS_STRING; + break; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/%s", STATEDIR, task->ifname); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + signature, &value); + dbus_message_iter_append_basic(&value, type, val); + dbus_message_iter_close_container(&entry, &value); + + dbus_message_iter_close_container(dict, &entry); +} - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -1; +static int set_network(struct supplicant_task *task, const char *network, + const char *passphrase) +{ + DBusMessage *message, *reply; + DBusMessageIter array, dict; + DBusError error; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network, + SUPPLICANT_INTF ".Network", "set"); + if (message == NULL) + return -ENOMEM; + + dbus_message_iter_init_append(message, &array); + + dbus_message_iter_open_container(&array, 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); + + append_entry(&dict, "ssid", DBUS_TYPE_STRING, &network); + + if (passphrase && strlen(passphrase) > 0) { + //exec_cmd(task, "SET_NETWORK 0 proto RSN WPA"); + //exec_cmd(task, "SET_NETWORK 0 key_mgmt WPA-PSK"); + + append_entry(&dict, "psk", DBUS_TYPE_STRING, &passphrase); + } else { + //exec_cmd(task, "SET_NETWORK 0 proto RSN WPA"); + //exec_cmd(task, "SET_NETWORK 0 key_mgmt NONE"); } - task->socket = sk; + dbus_message_iter_close_container(&array, &dict); - task->channel = g_io_channel_unix_new(sk); - g_io_channel_set_close_on_unref(task->channel, TRUE); + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to set network options"); + dbus_message_unref(message); + return -EIO; + } - g_io_add_watch(task->channel, - G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, - control_event, task); + dbus_message_unref(message); - exec_cmd(task, "ATTACH"); - exec_cmd(task, "ADD_NETWORK"); + dbus_message_unref(reply); + + return 0; +} + +static int initiate_scan(struct supplicant_task *task) +{ + DBusMessage *message, *reply; + DBusError error; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, + SUPPLICANT_INTF ".Interface", "scan"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to initiate scan"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_message_unref(reply); + + return 0; +} + +static void extract_ssid(struct supplicant_ap *ap, DBusMessageIter *value) +{ + DBusMessageIter array; + unsigned char *ssid; + int ssid_len; + + dbus_message_iter_recurse(value, &array); + dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len); + + ap->identifier = g_strdup((char *) ssid); +} + +static void extract_wpaie(struct supplicant_ap *ap, DBusMessageIter *value) +{ + DBusMessageIter array; + unsigned char *ie; + int ie_len; + + dbus_message_iter_recurse(value, &array); + dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); + + if (ie_len > 0) + ap->has_wpa = TRUE; +} + +static void extract_rsnie(struct supplicant_ap *ap, DBusMessageIter *value) +{ + DBusMessageIter array; + unsigned char *ie; + int ie_len; + + dbus_message_iter_recurse(value, &array); + dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); + + if (ie_len > 0) + ap->has_rsn = TRUE; +} + +static void extract_capabilites(struct supplicant_ap *ap, + DBusMessageIter *value) +{ + guint capabilities; + + dbus_message_iter_get_basic(value, &capabilities); + + ap->capabilities = capabilities; + + if (capabilities & IEEE80211_CAP_PRIVACY) + ap->has_wep = TRUE; +} + +static int parse_network_properties(struct supplicant_task *task, + DBusMessage *message) +{ + DBusMessageIter array, dict; + struct supplicant_ap *ap; + int security = 0; + + DBG("task %p", task); + + ap = g_try_new0(struct supplicant_ap, 1); + if (ap == NULL) + return -ENOMEM; + + dbus_message_iter_init(message, &array); + + dbus_message_iter_recurse(&array, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + + dbus_message_iter_recurse(&entry, &value); + + //type = dbus_message_iter_get_arg_type(&value); + //dbus_message_iter_get_basic(&value, &val); + + if (g_str_equal(key, "ssid") == TRUE) + extract_ssid(ap, &value); + else if (g_str_equal(key, "wpaie") == TRUE) + extract_wpaie(ap, &value); + else if (g_str_equal(key, "rsnie") == TRUE) + extract_rsnie(ap, &value); + else if (g_str_equal(key, "capabilities") == TRUE) + extract_capabilites(ap, &value); + + dbus_message_iter_next(&dict); + } + + DBG("SSID %s", ap->identifier); + + if (ap->has_wep) + security |= 0x01; + if (ap->has_wpa) + security |= 0x02; + if (ap->has_rsn) + security |= 0x04; + + connman_iface_indicate_station(task->iface, + ap->identifier, 25, security); + + g_free(ap); + + return 0; +} + +static int get_network_properties(struct supplicant_task *task, + const char *path) +{ + DBusMessage *message, *reply; + DBusError error; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, path, + SUPPLICANT_INTF ".BSSID", + "properties"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to get network properties"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + parse_network_properties(task, reply); + + dbus_message_unref(reply); + + return 0; +} + +static int scan_results_available(struct supplicant_task *task) +{ + DBusMessage *message, *reply; + DBusError error; + char **results; + int i, num_results; + + DBG("task %p", task); + + message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path, + SUPPLICANT_INTF ".Interface", + "scanResults"); + if (message == NULL) + return -ENOMEM; + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(task->conn, + message, -1, &error); + if (reply == NULL) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Failed to request scan result"); + dbus_message_unref(message); + return -EIO; + } + + dbus_message_unref(message); + + dbus_error_init(&error); + + if (dbus_message_get_args(reply, &error, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, + &results, &num_results, + DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Wrong arguments for scan result"); + dbus_message_unref(reply); + return -EIO; + } + + for (i = 0; i < num_results; i++) + get_network_properties(task, results[i]); + + g_strfreev(results); + + dbus_message_unref(reply); + + return 0; +} + +static void state_change(struct supplicant_task *task, DBusMessage *msg) +{ + DBusError error; + const char *state, *previous; + + dbus_error_init(&error); + + if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &state, + DBUS_TYPE_STRING, &previous, + DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&error) == TRUE) { + connman_error("%s", error.message); + dbus_error_free(&error); + } else + connman_error("Wrong arguments for state change"); + return; + } + + DBG("state %s ==> %s", previous, state); + + if (g_str_equal(state, "INACTIVE") == TRUE) + task->state = STATE_INACTIVE; + else if (g_str_equal(state, "SCANNING") == TRUE) + task->state = STATE_SCANNING; + else if (g_str_equal(state, "ASSOCIATING") == TRUE) + task->state = STATE_ASSOCIATING; + else if (g_str_equal(state, "ASSOCIATED") == TRUE) + task->state = STATE_ASSOCIATED; + else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE) + task->state = STATE_4WAY_HANDSHAKE; + else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE) + task->state = STATE_4WAY_HANDSHAKE; + else if (g_str_equal(state, "COMPLETED") == TRUE) + task->state = STATE_COMPLETED; + else if (g_str_equal(state, "DISCONNECTED") == TRUE) + task->state = STATE_DISCONNECTED; +} + +static DBusHandlerResult supplicant_filter(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct supplicant_task *task = data; + const char *member; + + if (dbus_message_has_interface(msg, + SUPPLICANT_INTF ".Interface") == FALSE) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + member = dbus_message_get_member(msg); + if (member == NULL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + DBG("task %p member %s", task, member); + + if (g_str_equal(member, "ScanResultsAvailable") == TRUE) + scan_results_available(task); + else if (g_str_equal(member, "StateChange") == TRUE) + state_change(task, msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static int add_filter(struct supplicant_task *task) +{ + DBusError error; + gchar *filter; + + if (dbus_connection_add_filter(task->conn, + supplicant_filter, task, NULL) == FALSE) + return -EIO; + + filter = g_strdup_printf("type=signal,interface=%s.Interface,path=%s", + SUPPLICANT_INTF, task->path); + + DBG("filter %s", filter); + + dbus_error_init(&error); + + dbus_bus_add_match(task->conn, filter, &error); + + g_free(filter); + + if (dbus_error_is_set(&error) == TRUE) { + connman_error("Can't add match: %s", error.message); + dbus_error_free(&error); + } return 0; } @@ -159,7 +820,6 @@ int __supplicant_start(struct connman_iface *iface) { struct ifreq ifr; struct supplicant_task *task; - char *argv[9]; int sk, err; sk = socket(PF_INET, SOCK_DGRAM, 0); @@ -176,14 +836,14 @@ int __supplicant_start(struct connman_iface *iface) if (err < 0) return -EIO; - printf("[SUPPLICANT] start %s\n", ifr.ifr_name); + DBG("interface %s", ifr.ifr_name); task = g_try_new0(struct supplicant_task, 1); if (task == NULL) return -ENOMEM; task->ifindex = iface->index; - task->ifname = strdup(ifr.ifr_name); + task->ifname = g_strdup(ifr.ifr_name); task->iface = iface; if (task->ifname == NULL) { @@ -191,32 +851,33 @@ int __supplicant_start(struct connman_iface *iface) return -ENOMEM; } - argv[0] = "/sbin/wpa_supplicant"; - argv[1] = "-qq"; - argv[2] = "-C"; - argv[3] = STATEDIR; - argv[4] = "-D"; - argv[5] = "wext"; - argv[6] = "-i"; - argv[7] = task->ifname; - argv[8] = NULL; - - if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, &task->pid, NULL) == FALSE) { - printf("Failed to spawn wpa_supplicant\n"); - return -1; + task->conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + if (task->conn == NULL) { + g_free(task); + return -EIO; + } + + task->created = FALSE; + + err = get_interface(task); + if (err < 0) { + err = add_interface(task); + if (err < 0) { + g_free(task); + return err; + } } - tasks = g_slist_append(tasks, task); + task->state = STATE_INACTIVE; - printf("[SUPPLICANT] executed with pid %d\n", task->pid); + tasks = g_slist_append(tasks, task); - sleep(1); + add_filter(task); - task->socket = -1; + add_network(task); - if (open_control(task) < 0) - printf("[SUPPLICANT] control failed\n"); + select_network(task); + disable_network(task); return 0; } @@ -224,33 +885,51 @@ int __supplicant_start(struct connman_iface *iface) int __supplicant_stop(struct connman_iface *iface) { struct supplicant_task *task; - char pathname[PATH_MAX]; task = find_task(iface->index); if (task == NULL) return -ENODEV; - printf("[SUPPLICANT] stop %s\n", task->ifname); + DBG("interface %s", task->ifname); tasks = g_slist_remove(tasks, task); - exec_cmd(task, "DISABLE_NETWORK 0"); - exec_cmd(task, "DETACH"); + remove_network(task); - sleep(1); + dbus_connection_unref(task->conn); + + g_free(task->ifname); + g_free(task->network); + g_free(task->path); + g_free(task); - kill(task->pid, SIGTERM); + return 0; +} - g_io_channel_shutdown(task->channel, TRUE, NULL); - g_io_channel_unref(task->channel); +int __supplicant_scan(struct connman_iface *iface) +{ + struct supplicant_task *task; + int err; - snprintf(pathname, sizeof(pathname), - "%s/%s.cli", STATEDIR, task->ifname); - unlink(pathname); + task = find_task(iface->index); + if (task == NULL) + return -ENODEV; - free(task->ifname); + DBG("interface %s", task->ifname); + + switch (task->state) { + case STATE_SCANNING: + return -EALREADY; + case STATE_ASSOCIATING: + case STATE_ASSOCIATED: + case STATE_4WAY_HANDSHAKE: + case STATE_GROUP_HANDSHAKE: + return -EBUSY; + default: + break; + } - g_free(task); + err = initiate_scan(task); return 0; } @@ -259,34 +938,16 @@ int __supplicant_connect(struct connman_iface *iface, const char *network, const char *passphrase) { struct supplicant_task *task; - char cmd[128]; task = find_task(iface->index); if (task == NULL) return -ENODEV; - printf("[SUPPLICANT] connect %s\n", task->ifname); - - exec_cmd(task, "DISABLE_NETWORK 0"); - - if (network == NULL) - return 0; - - sprintf(cmd, "SET_NETWORK 0 ssid \"%s\"", network); - exec_cmd(task, cmd); + DBG("interface %s", task->ifname); - if (passphrase && strlen(passphrase) > 0) { - exec_cmd(task, "SET_NETWORK 0 proto RSN WPA"); - exec_cmd(task, "SET_NETWORK 0 key_mgmt WPA-PSK"); - - sprintf(cmd, "SET_NETWORK 0 psk \"%s\"", passphrase); - exec_cmd(task, cmd); - } else { - exec_cmd(task, "SET_NETWORK 0 proto RSN WPA"); - exec_cmd(task, "SET_NETWORK 0 key_mgmt NONE"); - } + set_network(task, network, passphrase); - exec_cmd(task, "ENABLE_NETWORK 0"); + enable_network(task); return 0; } @@ -299,9 +960,9 @@ int __supplicant_disconnect(struct connman_iface *iface) if (task == NULL) return -ENODEV; - printf("[SUPPLICANT] disconnect %s\n", task->ifname); + DBG("interface %s", task->ifname); - exec_cmd(task, "DISABLE_NETWORK 0"); + disable_network(task); return 0; } diff --git a/plugins/supplicant.h b/plugins/supplicant.h index dc015ed..59e4dfe 100644 --- a/plugins/supplicant.h +++ b/plugins/supplicant.h @@ -21,9 +21,15 @@ #include +#define SUPPLICANT_NAME "fi.epitest.hostap.WPASupplicant" +#define SUPPLICANT_INTF "fi.epitest.hostap.WPASupplicant" +#define SUPPLICANT_PATH "/fi/epitest/hostap/WPASupplicant" + int __supplicant_start(struct connman_iface *iface); int __supplicant_stop(struct connman_iface *iface); +int __supplicant_scan(struct connman_iface *iface); + int __supplicant_connect(struct connman_iface *iface, const char *network, const char *passphrase); int __supplicant_disconnect(struct connman_iface *iface); -- 2.7.4