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