3fbed238bf8777614de2cabe1dbe2c132afdabd4
[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 #define CLEANUP_TIMEOUT   8     /* in seconds */
45 #define INACTIVE_TIMEOUT  12    /* in seconds */
46
47 struct wifi_data {
48         GSList *current;
49         GSList *pending;
50         guint cleanup_timer;
51         guint inactive_timer;
52         gchar *identifier;
53         gboolean connected;
54 };
55
56 static int network_probe(struct connman_element *element)
57 {
58         DBG("element %p name %s", element, element->name);
59
60         return 0;
61 }
62
63 static void network_remove(struct connman_element *element)
64 {
65         DBG("element %p name %s", element, element->name);
66 }
67
68 static int network_enable(struct connman_element *element)
69 {
70         char *name, *security = NULL, *passphrase = NULL;
71         unsigned char *ssid;
72         int ssid_len;
73
74         DBG("element %p name %s", element, element->name);
75
76         if (connman_element_get_static_property(element,
77                                                 "Name", &name) == FALSE)
78                 return -EIO;
79
80         if (connman_element_get_static_array_property(element,
81                                 "WiFi.SSID", &ssid, &ssid_len) == FALSE)
82                 return -EIO;
83
84         if (element->parent != NULL) {
85                 struct wifi_data *data = connman_element_get_data(element->parent);
86
87                 if (data->connected == TRUE)
88                         return -EBUSY;
89
90                 if (data != NULL) {
91                         g_free(data->identifier);
92                         data->identifier = g_strdup(name);
93                 }
94         }
95
96         connman_element_get_value(element,
97                         CONNMAN_PROPERTY_ID_WIFI_SECURITY, &security);
98
99         connman_element_get_value(element,
100                         CONNMAN_PROPERTY_ID_WIFI_PASSPHRASE, &passphrase);
101
102         DBG("name %s security %s passhprase %s",
103                                         name, security, passphrase);
104
105         if (__supplicant_connect(element, ssid, ssid_len,
106                                                 security, passphrase) < 0)
107                 connman_error("Failed to initiate connect");
108
109         return 0;
110 }
111
112 static int network_disable(struct connman_element *element)
113 {
114         DBG("element %p name %s", element, element->name);
115
116         connman_element_unregister_children(element);
117
118         __supplicant_disconnect(element);
119
120         return 0;
121 }
122
123 static struct connman_driver network_driver = {
124         .name           = "wifi-network",
125         .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
126         .subtype        = CONNMAN_ELEMENT_SUBTYPE_WIFI,
127         .probe          = network_probe,
128         .remove         = network_remove,
129         .enable         = network_enable,
130         .disable        = network_disable,
131 };
132
133 static struct connman_element *find_current_element(struct wifi_data *data,
134                                                         const char *identifier)
135 {
136         GSList *list;
137
138         for (list = data->current; list; list = list->next) {
139                 struct connman_element *element = list->data;
140
141                 if (connman_element_match_static_property(element,
142                                         "Name", &identifier) == TRUE)
143                         return element;
144         }
145
146         return NULL;
147 }
148
149 static struct connman_element *find_pending_element(struct wifi_data *data,
150                                                         const char *identifier)
151 {
152         GSList *list;
153
154         for (list = data->pending; list; list = list->next) {
155                 struct connman_element *element = list->data;
156
157                 if (connman_element_match_static_property(element,
158                                         "Name", &identifier) == TRUE)
159                         return element;
160         }
161
162         return NULL;
163 }
164
165 static gboolean inactive_scan(gpointer user_data)
166 {
167         struct connman_element *device = user_data;
168         struct wifi_data *data = connman_element_get_data(device);
169
170         DBG("");
171
172         if (data->cleanup_timer > 0) {
173                 g_source_remove(data->cleanup_timer);
174                 data->cleanup_timer = 0;
175         }
176
177         __supplicant_scan(device);
178
179         data->inactive_timer = 0;
180
181         return FALSE;
182 }
183
184 static void state_change(struct connman_element *parent,
185                                                 enum supplicant_state state)
186 {
187         struct wifi_data *data = connman_element_get_data(parent);
188         struct connman_element *element;
189
190         DBG("state %d", state);
191
192         if (state == STATE_INACTIVE && data->inactive_timer == 0)
193                 data->inactive_timer = g_timeout_add_seconds(INACTIVE_TIMEOUT,
194                                                         inactive_scan, parent);
195
196         if (data == NULL)
197                 return;
198
199         if (data->identifier == NULL)
200                 return;
201
202         element = find_current_element(data, data->identifier);
203         if (element == NULL)
204                 return;
205
206         if (state == STATE_COMPLETED) {
207                 struct connman_element *dhcp;
208
209                 data->connected = TRUE;
210
211                 dhcp = connman_element_create(NULL);
212
213                 dhcp->type = CONNMAN_ELEMENT_TYPE_DHCP;
214                 dhcp->index = element->index;
215
216                 connman_element_register(dhcp, element);
217         } else if (state == STATE_DISCONNECTED || state == STATE_INACTIVE)
218                 data->connected = FALSE;
219 }
220
221 static gboolean cleanup_pending(gpointer user_data)
222 {
223         struct wifi_data *data = user_data;
224         GSList *list;
225
226         DBG("");
227
228         for (list = data->pending; list; list = list->next) {
229                 struct connman_element *element = list->data;
230
231                 DBG("element %p name %s", element, element->name);
232
233                 connman_element_unregister(element);
234                 connman_element_unref(element);
235         }
236
237         g_slist_free(data->pending);
238         data->pending = NULL;
239
240         data->cleanup_timer = 0;
241
242         return FALSE;
243 }
244
245 static void clear_results(struct connman_element *parent)
246 {
247         struct wifi_data *data = connman_element_get_data(parent);
248
249         DBG("pending %d", g_slist_length(data->pending));
250         DBG("current %d", g_slist_length(data->current));
251
252         data->pending = data->current;
253         data->current = NULL;
254
255         if (data->cleanup_timer > 0)
256                 return;
257
258         data->cleanup_timer = g_timeout_add_seconds(CLEANUP_TIMEOUT,
259                                                         cleanup_pending, data);
260 }
261
262 static void scan_result(struct connman_element *parent,
263                                         struct supplicant_network *network)
264 {
265         struct wifi_data *data = connman_element_get_data(parent);
266         struct connman_element *element;
267         gchar *temp;
268         int i;
269
270         DBG("network %p identifier %s", network, network->identifier);
271
272         if (data == NULL)
273                 return;
274
275         if (network->identifier == NULL)
276                 return;
277
278         if (network->identifier[0] == '\0')
279                 return;
280
281         temp = g_strdup(network->identifier);
282
283         for (i = 0; i < strlen(temp); i++) {
284                 gchar tmp = g_ascii_tolower(temp[i]);
285
286                 if (tmp < 'a' || tmp > 'z')
287                         temp[i] = '_';
288         }
289
290         element = find_pending_element(data, network->identifier);
291         if (element == NULL) {
292                 guint8 strength;
293
294                 element = connman_element_create(temp);
295
296                 element->type = CONNMAN_ELEMENT_TYPE_NETWORK;
297                 element->index = parent->index;
298
299                 connman_element_add_static_property(element, "Name",
300                                 DBUS_TYPE_STRING, &network->identifier);
301
302                 connman_element_add_static_array_property(element, "WiFi.SSID",
303                         DBUS_TYPE_BYTE, &network->ssid, network->ssid_len);
304
305                 if (element->wifi.security == NULL) {
306                         const char *security;
307
308                         if (network->has_rsn == TRUE)
309                                 security = "wpa2";
310                         else if (network->has_wpa == TRUE)
311                                 security = "wpa";
312                         else if (network->has_wep == TRUE)
313                                 security = "wep";
314                         else
315                                 security = "none";
316
317                         element->wifi.security = g_strdup(security);
318                 }
319
320                 strength = network->quality;
321
322                 connman_element_add_static_property(element, "WiFi.Strength",
323                                                 DBUS_TYPE_BYTE, &strength);
324
325                 //connman_element_add_static_property(element, "WiFi.Noise",
326                 //                      DBUS_TYPE_INT32, &network->noise);
327
328                 DBG("%s (%s) strength %d", network->identifier,
329                                         element->wifi.security, strength);
330
331                 connman_element_register(element, parent);
332         } else
333                 data->pending = g_slist_remove(data->pending, element);
334
335         data->current = g_slist_append(data->current, element);
336
337         element->available = TRUE;
338
339         g_free(temp);
340 }
341
342 static struct supplicant_callback wifi_callback = {
343         .state_change   = state_change,
344         .clear_results  = clear_results,
345         .scan_result    = scan_result,
346 };
347
348 static int wifi_probe(struct connman_element *element)
349 {
350         struct wifi_data *data;
351
352         DBG("element %p name %s", element, element->name);
353
354         data = g_try_new0(struct wifi_data, 1);
355         if (data == NULL)
356                 return -ENOMEM;
357
358         data->connected = FALSE;
359
360         connman_element_set_data(element, data);
361
362         return 0;
363 }
364
365 static void wifi_remove(struct connman_element *element)
366 {
367         struct wifi_data *data = connman_element_get_data(element);
368
369         DBG("element %p name %s", element, element->name);
370
371         connman_element_set_data(element, NULL);
372
373         g_free(data->identifier);
374         g_free(data);
375 }
376
377 static int wifi_update(struct connman_element *element)
378 {
379         DBG("element %p name %s", element, element->name);
380
381         __supplicant_scan(element);
382
383         return 0;
384 }
385
386 static int wifi_enable(struct connman_element *element)
387 {
388         int err;
389
390         DBG("element %p name %s", element, element->name);
391
392         err = __supplicant_start(element, &wifi_callback);
393         if (err < 0)
394                 return err;
395
396         __supplicant_scan(element);
397
398         return 0;
399 }
400
401 static int wifi_disable(struct connman_element *element)
402 {
403         struct wifi_data *data = connman_element_get_data(element);
404         GSList *list;
405
406         DBG("element %p name %s", element, element->name);
407
408         if (data->cleanup_timer > 0) {
409                 g_source_remove(data->cleanup_timer);
410                 data->cleanup_timer = 0;
411         }
412
413         if (data->inactive_timer > 0) {
414                 g_source_remove(data->inactive_timer);
415                 data->inactive_timer = 0;
416         }
417
418         __supplicant_disconnect(element);
419
420         for (list = data->pending; list; list = list->next) {
421                 struct connman_element *network = list->data;
422
423                 connman_element_unref(network);
424         }
425
426         g_slist_free(data->pending);
427         data->pending = NULL;
428
429         for (list = data->current; list; list = list->next) {
430                 struct connman_element *network = list->data;
431
432                 connman_element_unref(network);
433         }
434
435         g_slist_free(data->current);
436         data->current = NULL;
437
438         connman_element_unregister_children(element);
439
440         __supplicant_stop(element);
441
442         return 0;
443 }
444
445 static struct connman_driver wifi_driver = {
446         .name           = "wifi-device",
447         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
448         .subtype        = CONNMAN_ELEMENT_SUBTYPE_WIFI,
449         .probe          = wifi_probe,
450         .remove         = wifi_remove,
451         .update         = wifi_update,
452         .enable         = wifi_enable,
453         .disable        = wifi_disable,
454 };
455
456 static GSList *device_list = NULL;
457
458 static void wifi_newlink(unsigned short type, int index,
459                                         unsigned flags, unsigned change)
460 {
461         struct connman_element *device;
462         GSList *list;
463         gboolean exists = FALSE;
464         gchar *name, *devname;
465         struct iwreq iwr;
466         int sk;
467
468         DBG("index %d", index);
469
470         if (type != ARPHRD_ETHER)
471                 return;
472
473         name = inet_index2ident(index, "dev_");
474         devname = inet_index2name(index);
475
476         memset(&iwr, 0, sizeof(iwr));
477         strncpy(iwr.ifr_ifrn.ifrn_name, devname, IFNAMSIZ);
478
479         sk = socket(PF_INET, SOCK_DGRAM, 0);
480
481         if (ioctl(sk, SIOCGIWNAME, &iwr) < 0) {
482                 g_free(name);
483                 close(sk);
484                 return;
485         }
486
487         close(sk);
488
489         for (list = device_list; list; list = list->next) {
490                 struct connman_element *device = list->data;
491
492                 if (device->index == index) {
493                         exists = TRUE;
494                         break;
495                 }
496         }
497
498         if (exists == TRUE) {
499                 g_free(name);
500                 return;
501         }
502
503         device = connman_element_create(NULL);
504         device->type = CONNMAN_ELEMENT_TYPE_DEVICE;
505         device->subtype = CONNMAN_ELEMENT_SUBTYPE_WIFI;
506
507         device->index = index;
508         device->name = name;
509         device->devname = devname;
510
511         connman_element_register(device, NULL);
512         device_list = g_slist_append(device_list, device);
513 }
514
515 static void wifi_dellink(unsigned short type, int index,
516                                         unsigned flags, unsigned change)
517 {
518         GSList *list;
519
520         DBG("index %d", index);
521
522         for (list = device_list; list; list = list->next) {
523                 struct connman_element *device = list->data;
524
525                 if (device->index == index) {
526                         device_list = g_slist_remove(device_list, device);
527                         connman_element_unregister(device);
528                         connman_element_unref(device);
529                         break;
530                 }
531         }
532 }
533
534 static struct connman_rtnl wifi_rtnl = {
535         .name           = "wifi",
536         .newlink        = wifi_newlink,
537         .dellink        = wifi_dellink,
538 };
539
540 static void supplicant_connect(DBusConnection *connection, void *user_data)
541 {
542         DBG("connection %p", connection);
543
544         __supplicant_init(connection);
545
546         if (connman_rtnl_register(&wifi_rtnl) < 0)
547                 return;
548
549         connman_rtnl_send_getlink();
550 }
551
552 static void supplicant_disconnect(DBusConnection *connection, void *user_data)
553 {
554         GSList *list;
555
556         DBG("connection %p", connection);
557
558         connman_rtnl_unregister(&wifi_rtnl);
559
560         for (list = device_list; list; list = list->next) {
561                 struct connman_element *device = list->data;
562
563                 connman_element_unregister(device);
564                 connman_element_unref(device);
565         }
566
567         g_slist_free(device_list);
568         device_list = NULL;
569
570         __supplicant_exit();
571 }
572
573 static DBusConnection *connection;
574 static guint watch;
575
576 static int wifi_init(void)
577 {
578         int err;
579
580         connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
581         if (connection == NULL)
582                 return -EIO;
583
584         err = connman_driver_register(&network_driver);
585         if (err < 0) {
586                 dbus_connection_unref(connection);
587                 return err;
588         }
589
590         err = connman_driver_register(&wifi_driver);
591         if (err < 0) {
592                 connman_driver_unregister(&network_driver);
593                 dbus_connection_unref(connection);
594                 return err;
595         }
596
597         watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
598                         supplicant_connect, supplicant_disconnect, NULL, NULL);
599
600         if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
601                 supplicant_connect(connection, NULL);
602
603         return 0;
604 }
605
606 static void wifi_exit(void)
607 {
608         connman_driver_unregister(&network_driver);
609         connman_driver_unregister(&wifi_driver);
610
611         if (watch > 0)
612                 g_dbus_remove_watch(connection, watch);
613
614         supplicant_disconnect(connection, NULL);
615
616         dbus_connection_unref(connection);
617 }
618
619 CONNMAN_PLUGIN_DEFINE(wifi, "WiFi interface plugin", VERSION,
620                                                         wifi_init, wifi_exit)