Fix issue with wpa_supplicant disconnecting
[connman] / plugins / wifi.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <linux/if_arp.h>
32 #include <linux/wireless.h>
33
34 #include <gdbus.h>
35
36 #include <connman/plugin.h>
37 #include <connman/driver.h>
38 #include <connman/rtnl.h>
39 #include <connman/log.h>
40
41 #include "inet.h"
42 #include "supplicant.h"
43
44 struct wifi_data {
45         GSList *list;
46         gchar *identifier;
47         gboolean connected;
48 };
49
50 static int network_probe(struct connman_element *element)
51 {
52         DBG("element %p name %s", element, element->name);
53
54         return 0;
55 }
56
57 static void network_remove(struct connman_element *element)
58 {
59         DBG("element %p name %s", element, element->name);
60 }
61
62 static int network_enable(struct connman_element *element)
63 {
64         char *name, *security = NULL, *passphrase = NULL;
65         unsigned char *ssid;
66         int ssid_len;
67
68         DBG("element %p name %s", element, element->name);
69
70         if (connman_element_get_static_property(element,
71                                                 "Name", &name) == FALSE)
72                 return -EIO;
73
74         if (connman_element_get_static_array_property(element,
75                                 "WiFi.SSID", &ssid, &ssid_len) == FALSE)
76                 return -EIO;
77
78         if (element->parent != NULL) {
79                 struct wifi_data *data = connman_element_get_data(element->parent);
80
81                 if (data->connected == TRUE)
82                         return -EBUSY;
83
84                 if (data != NULL) {
85                         g_free(data->identifier);
86                         data->identifier = g_strdup(name);
87                 }
88         }
89
90         connman_element_get_value(element,
91                         CONNMAN_PROPERTY_ID_WIFI_SECURITY, &security);
92
93         connman_element_get_value(element,
94                         CONNMAN_PROPERTY_ID_WIFI_PASSPHRASE, &passphrase);
95
96         DBG("name %s security %s passhprase %s",
97                                         name, security, passphrase);
98
99         if (__supplicant_connect(element, ssid, ssid_len,
100                                                 security, passphrase) < 0)
101                 connman_error("Failed to initiate connect");
102
103         return 0;
104 }
105
106 static int network_disable(struct connman_element *element)
107 {
108         DBG("element %p name %s", element, element->name);
109
110         connman_element_unregister_children(element);
111
112         __supplicant_disconnect(element);
113
114         return 0;
115 }
116
117 static struct connman_driver network_driver = {
118         .name           = "wifi-network",
119         .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
120         .subtype        = CONNMAN_ELEMENT_SUBTYPE_WIFI,
121         .probe          = network_probe,
122         .remove         = network_remove,
123         .enable         = network_enable,
124         .disable        = network_disable,
125 };
126
127 static struct connman_element *find_element(struct wifi_data *data,
128                                                 const char *identifier)
129 {
130         GSList *list;
131
132         for (list = data->list; list; list = list->next) {
133                 struct connman_element *element = list->data;
134
135                 if (connman_element_match_static_property(element,
136                                         "Name", &identifier) == TRUE)
137                         return element;
138         }
139
140         return NULL;
141 }
142
143 static void state_change(struct connman_element *parent,
144                                                 enum supplicant_state state)
145 {
146         struct wifi_data *data = connman_element_get_data(parent);
147         struct connman_element *element;
148
149         DBG("state %d", state);
150
151         if (data == NULL)
152                 return;
153
154         if (data->identifier == NULL)
155                 return;
156
157         element = find_element(data, data->identifier);
158         if (element == NULL)
159                 return;
160
161         if (state == STATE_COMPLETED) {
162                 struct connman_element *dhcp;
163
164                 data->connected = TRUE;
165
166                 dhcp = connman_element_create(NULL);
167
168                 dhcp->type = CONNMAN_ELEMENT_TYPE_DHCP;
169                 dhcp->index = element->index;
170
171                 connman_element_register(dhcp, element);
172         } else if (state == STATE_DISCONNECTED || state == STATE_INACTIVE)
173                 data->connected = FALSE;
174 }
175
176 static void scan_result(struct connman_element *parent,
177                                         struct supplicant_network *network)
178 {
179         struct wifi_data *data = connman_element_get_data(parent);
180         struct connman_element *element;
181         gchar *temp;
182         int i;
183
184         DBG("network %p identifier %s", network, network->identifier);
185
186         if (data == NULL)
187                 return;
188
189         if (network->identifier == NULL)
190                 return;
191
192         if (network->identifier[0] == '\0')
193                 return;
194
195         temp = g_strdup(network->identifier);
196
197         for (i = 0; i < strlen(temp); i++) {
198                 if (temp[i] == ' ' || temp[i] == '.')
199                         temp[i] = '_';
200                 else if (temp[i] == '-' || temp[i] == '+')
201                         temp[i] = '_';
202                 else if (temp[i] == '!' || temp[i] == '?')
203                         temp[i] = '_';
204                 else if (temp[i] == '(' || temp[i] == ')')
205                         temp[i] = '_';
206                 else if (g_ascii_isprint(temp[i]) == FALSE)
207                         temp[i] = '_';
208                 temp[i] = g_ascii_tolower(temp[i]);
209         }
210
211         element = find_element(data, network->identifier);
212         if (element == NULL) {
213                 guint8 strength;
214
215                 element = connman_element_create(temp);
216
217                 element->type = CONNMAN_ELEMENT_TYPE_NETWORK;
218                 element->index = parent->index;
219
220                 data->list = g_slist_append(data->list, element);
221
222                 connman_element_add_static_property(element, "Name",
223                                 DBUS_TYPE_STRING, &network->identifier);
224
225                 connman_element_add_static_array_property(element, "WiFi.SSID",
226                         DBUS_TYPE_BYTE, &network->ssid, network->ssid_len);
227
228                 if (element->wifi.security == NULL) {
229                         const char *security;
230
231                         if (network->has_rsn == TRUE)
232                                 security = "wpa2";
233                         else if (network->has_wpa == TRUE)
234                                 security = "wpa";
235                         else if (network->has_wep == TRUE)
236                                 security = "wep";
237                         else
238                                 security = "none";
239
240                         element->wifi.security = g_strdup(security);
241                 }
242
243                 strength = network->quality;
244
245                 connman_element_add_static_property(element, "WiFi.Strength",
246                                                 DBUS_TYPE_BYTE, &strength);
247
248                 //connman_element_add_static_property(element, "WiFi.Noise",
249                 //                      DBUS_TYPE_INT32, &network->noise);
250
251                 DBG("%s (%s) strength %d", network->identifier,
252                                         element->wifi.security, strength);
253
254                 connman_element_register(element, parent);
255         }
256
257         element->available = TRUE;
258
259         g_free(temp);
260 }
261
262 static struct supplicant_callback wifi_callback = {
263         .state_change   = state_change,
264         .scan_result    = scan_result,
265 };
266
267 static int wifi_probe(struct connman_element *element)
268 {
269         struct wifi_data *data;
270
271         DBG("element %p name %s", element, element->name);
272
273         data = g_try_new0(struct wifi_data, 1);
274         if (data == NULL)
275                 return -ENOMEM;
276
277         data->connected = FALSE;
278
279         connman_element_set_data(element, data);
280
281         return 0;
282 }
283
284 static void wifi_remove(struct connman_element *element)
285 {
286         struct wifi_data *data = connman_element_get_data(element);
287
288         DBG("element %p name %s", element, element->name);
289
290         connman_element_set_data(element, NULL);
291
292         g_free(data->identifier);
293         g_free(data);
294 }
295
296 static int wifi_update(struct connman_element *element)
297 {
298         DBG("element %p name %s", element, element->name);
299
300         __supplicant_scan(element);
301
302         return 0;
303 }
304
305 static int wifi_enable(struct connman_element *element)
306 {
307         int err;
308
309         DBG("element %p name %s", element, element->name);
310
311         err = __supplicant_start(element, &wifi_callback);
312         if (err < 0)
313                 return err;
314
315         __supplicant_scan(element);
316
317         return 0;
318 }
319
320 static int wifi_disable(struct connman_element *element)
321 {
322         struct wifi_data *data = connman_element_get_data(element);
323         GSList *list;
324
325         DBG("element %p name %s", element, element->name);
326
327         __supplicant_disconnect(element);
328
329         for (list = data->list; list; list = list->next) {
330                 struct connman_element *network = list->data;
331
332                 connman_element_unref(network);
333         }
334
335         g_slist_free(data->list);
336         data->list = NULL;
337
338         connman_element_unregister_children(element);
339
340         __supplicant_stop(element);
341
342         return 0;
343 }
344
345 static struct connman_driver wifi_driver = {
346         .name           = "wifi-device",
347         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
348         .subtype        = CONNMAN_ELEMENT_SUBTYPE_WIFI,
349         .probe          = wifi_probe,
350         .remove         = wifi_remove,
351         .update         = wifi_update,
352         .enable         = wifi_enable,
353         .disable        = wifi_disable,
354 };
355
356 static GSList *device_list = NULL;
357
358 static void wifi_newlink(unsigned short type, int index,
359                                         unsigned flags, unsigned change)
360 {
361         struct connman_element *device;
362         GSList *list;
363         gboolean exists = FALSE;
364         gchar *name, *devname;
365         struct iwreq iwr;
366         int sk;
367
368         DBG("index %d", index);
369
370         if (type != ARPHRD_ETHER)
371                 return;
372
373         name = inet_index2ident(index, "dev_");
374         devname = inet_index2name(index);
375
376         memset(&iwr, 0, sizeof(iwr));
377         strncpy(iwr.ifr_ifrn.ifrn_name, devname, IFNAMSIZ);
378
379         sk = socket(PF_INET, SOCK_DGRAM, 0);
380
381         if (ioctl(sk, SIOCGIWNAME, &iwr) < 0) {
382                 g_free(name);
383                 close(sk);
384                 return;
385         }
386
387         close(sk);
388
389         for (list = device_list; list; list = list->next) {
390                 struct connman_element *device = list->data;
391
392                 if (device->index == index) {
393                         exists = TRUE;
394                         break;
395                 }
396         }
397
398         if (exists == TRUE) {
399                 g_free(name);
400                 return;
401         }
402
403         device = connman_element_create(NULL);
404         device->type = CONNMAN_ELEMENT_TYPE_DEVICE;
405         device->subtype = CONNMAN_ELEMENT_SUBTYPE_WIFI;
406
407         device->index = index;
408         device->name = name;
409         device->devname = devname;
410
411         connman_element_register(device, NULL);
412         device_list = g_slist_append(device_list, device);
413 }
414
415 static void wifi_dellink(unsigned short type, int index,
416                                         unsigned flags, unsigned change)
417 {
418         GSList *list;
419
420         DBG("index %d", index);
421
422         for (list = device_list; list; list = list->next) {
423                 struct connman_element *device = list->data;
424
425                 if (device->index == index) {
426                         device_list = g_slist_remove(device_list, device);
427                         connman_element_unregister(device);
428                         connman_element_unref(device);
429                         break;
430                 }
431         }
432 }
433
434 static struct connman_rtnl wifi_rtnl = {
435         .name           = "wifi",
436         .newlink        = wifi_newlink,
437         .dellink        = wifi_dellink,
438 };
439
440 static void supplicant_connect(DBusConnection *connection, void *user_data)
441 {
442         DBG("connection %p", connection);
443
444         __supplicant_init(connection);
445
446         if (connman_rtnl_register(&wifi_rtnl) < 0)
447                 return;
448
449         connman_rtnl_send_getlink();
450 }
451
452 static void supplicant_disconnect(DBusConnection *connection, void *user_data)
453 {
454         GSList *list;
455
456         DBG("connection %p", connection);
457
458         connman_rtnl_unregister(&wifi_rtnl);
459
460         for (list = device_list; list; list = list->next) {
461                 struct connman_element *device = list->data;
462
463                 connman_element_unregister(device);
464                 connman_element_unref(device);
465         }
466
467         g_slist_free(device_list);
468         device_list = NULL;
469
470         __supplicant_exit();
471 }
472
473 static DBusConnection *connection;
474 static guint watch;
475
476 static int wifi_init(void)
477 {
478         int err;
479
480         connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
481         if (connection == NULL)
482                 return -EIO;
483
484         err = connman_driver_register(&network_driver);
485         if (err < 0) {
486                 dbus_connection_unref(connection);
487                 return err;
488         }
489
490         err = connman_driver_register(&wifi_driver);
491         if (err < 0) {
492                 connman_driver_unregister(&network_driver);
493                 dbus_connection_unref(connection);
494                 return err;
495         }
496
497         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
498                         supplicant_connect, supplicant_disconnect, NULL, NULL);
499
500         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
501                 supplicant_connect(connection, NULL);
502
503         return 0;
504 }
505
506 static void wifi_exit(void)
507 {
508         connman_driver_unregister(&network_driver);
509         connman_driver_unregister(&wifi_driver);
510
511         if (watch > 0)
512                 g_dbus_remove_watch(connection, watch);
513
514         supplicant_disconnect(connection, NULL);
515
516         dbus_connection_unref(connection);
517 }
518
519 CONNMAN_PLUGIN_DEFINE(wifi, "WiFi interface plugin", VERSION,
520                                                         wifi_init, wifi_exit)