558dd97114c3e6604b84ccf73c5f5616363fd6e1
[framework/connectivity/connman.git] / plugins / 80211.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  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 <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <net/if.h>
35 #include <net/ethernet.h>
36 #include <linux/wireless.h>
37
38 #include <glib.h>
39
40 #include <connman/plugin.h>
41 #include <connman/iface.h>
42
43 #include "supplicant.h"
44
45 struct station_data {
46         char *address;
47         char *name;
48         int mode;
49         int qual;
50         int noise;
51         int level;
52
53         unsigned char wpa_ie[40];
54         int wpa_ie_len;
55         unsigned char rsn_ie[40];
56         int rsn_ie_len;
57 };
58
59 struct iface_data {
60         char ifname[IFNAMSIZ];
61         GSList *stations;
62
63         gchar *network;
64         gchar *passphrase;
65 };
66
67 static struct station_data *create_station(struct iface_data *iface,
68                                                         const char *address)
69 {
70         struct station_data *station;
71         GSList *list;
72
73         for (list = iface->stations; list; list = list->next) {
74                 station = list->data;
75
76                 if (g_ascii_strcasecmp(station->address, address) == 0)
77                         return station;
78         }
79
80         station = g_try_new0(struct station_data, 1);
81         if (station == NULL)
82                 return NULL;
83
84         station->address = g_strdup(address);
85         if (station->address == NULL) {
86                 g_free(station);
87                 return NULL;
88         }
89
90         iface->stations = g_slist_append(iface->stations, station);
91
92         return station;
93 }
94
95 static void load_stations(struct iface_data *iface)
96 {
97         GKeyFile *keyfile;
98         gchar **groups, **group;
99         gsize length;
100
101         keyfile = g_key_file_new();
102
103         if (g_key_file_load_from_file(keyfile, "/tmp/stations.list",
104                                 G_KEY_FILE_KEEP_COMMENTS, NULL) == FALSE)
105                 goto done;
106
107         groups = g_key_file_get_groups(keyfile, &length);
108
109         for (group = groups; *group; group++) {
110                 struct station_data *station;
111
112                 station = create_station(iface, *group);
113                 if (station == NULL)
114                         continue;
115
116                 station->name = g_key_file_get_string(keyfile,
117                                                 *group, "Name", NULL);
118         
119                 station->mode = g_key_file_get_integer(keyfile,
120                                                 *group, "Mode", NULL);
121         }
122
123         g_strfreev(groups);
124
125 done:
126         g_key_file_free(keyfile);
127
128         printf("[802.11] loaded %d stations\n",
129                                 g_slist_length(iface->stations));
130 }
131
132 static void print_stations(struct iface_data *iface)
133 {
134         GKeyFile *keyfile;
135         gchar *data;
136         gsize length;
137         GSList *list;
138
139         keyfile = g_key_file_new();
140
141         for (list = iface->stations; list; list = list->next) {
142                 struct station_data *station = list->data;
143
144                 //printf("Address:%s Mode:%d ESSID:\"%s\" Quality:%d/100\n",
145                 //                      station->address, station->mode,
146                 //                              station->name, station->qual);
147
148                 g_key_file_set_string(keyfile, station->address,
149                                                 "Name", station->name);
150
151                 g_key_file_set_integer(keyfile, station->address,
152                                                 "Mode", station->mode);
153         }
154
155         data = g_key_file_to_data(keyfile, &length, NULL);
156
157         g_file_set_contents("/tmp/stations.list", data, length, NULL);
158
159         g_key_file_free(keyfile);
160 }
161
162 static int iface_probe(struct connman_iface *iface)
163 {
164         struct iface_data *data;
165         struct ifreq ifr;
166         int sk, err;
167
168         sk = socket(PF_INET, SOCK_DGRAM, 0);
169         if (sk < 0)
170                 return -EIO;
171
172         memset(&ifr, 0, sizeof(ifr));
173         ifr.ifr_ifindex = iface->index;
174
175         err = ioctl(sk, SIOCGIFNAME, &ifr);
176
177         close(sk);
178
179         if (err < 0)
180                 return -EIO;
181
182         printf("[802.11] probe %s\n", ifr.ifr_name);
183
184         data = malloc(sizeof(*data));
185         if (data == NULL)
186                 return -ENOMEM;
187
188         memset(data, 0, sizeof(*data));
189
190         memcpy(data->ifname, ifr.ifr_name, IFNAMSIZ);
191
192         iface->type = CONNMAN_IFACE_TYPE_80211;
193
194         iface->flags = CONNMAN_IFACE_FLAG_RTNL |
195                                 CONNMAN_IFACE_FLAG_IPV4;
196
197         connman_iface_set_data(iface, data);
198
199         load_stations(data);
200
201         return 0;
202 }
203
204 static void iface_remove(struct connman_iface *iface)
205 {
206         struct iface_data *data = connman_iface_get_data(iface);
207
208         printf("[802.11] remove %s\n", data->ifname);
209
210         __supplicant_stop(iface);
211
212         connman_iface_set_data(iface, NULL);
213
214         g_free(data->network);
215         g_free(data->passphrase);
216
217         free(data);
218 }
219
220 static int iface_activate(struct connman_iface *iface)
221 {
222         struct iface_data *data = connman_iface_get_data(iface);
223
224         printf("[802.11] activate %s\n", data->ifname);
225
226         connman_iface_update(iface, CONNMAN_IFACE_STATE_ENABLED);
227
228         return 0;
229 }
230
231 static int iface_shutdown(struct connman_iface *iface)
232 {
233         struct iface_data *data = connman_iface_get_data(iface);
234
235         printf("[802.11] shutdown %s\n", data->ifname);
236
237         connman_iface_update(iface, CONNMAN_IFACE_STATE_SHUTDOWN);
238
239         return 0;
240 }
241
242 static int iface_scan(struct connman_iface *iface)
243 {
244         struct iface_data *data = connman_iface_get_data(iface);
245         struct iwreq iwr;
246         struct iw_scan_req iws;
247         int sk, err;
248
249         printf("[802.11] scanning %s\n", data->ifname);
250
251         sk = socket(PF_INET, SOCK_DGRAM, 0);
252         if (sk < 0)
253                 return -EIO;
254
255         memset(&iws, 0, sizeof(iws));
256         iws.scan_type = IW_SCAN_TYPE_PASSIVE;
257         //iws.scan_type = IW_SCAN_TYPE_ACTIVE;
258
259         memset(&iwr, 0, sizeof(iwr));
260         strncpy(iwr.ifr_name, data->ifname, IFNAMSIZ);
261
262         iwr.u.data.pointer = (caddr_t ) &iws;
263         iwr.u.data.length = sizeof(iws);
264         iwr.u.data.flags = IW_SCAN_DEFAULT;
265
266         err = ioctl(sk, SIOCSIWSCAN, &iwr);
267
268         close(sk);
269
270         if (err < 0)
271                 printf("[802.11] scan initiate error %d\n", errno);
272
273         return err;
274 }
275
276 static int iface_connect(struct connman_iface *iface,
277                                         struct connman_network *network)
278 {
279         struct iface_data *data = connman_iface_get_data(iface);
280
281         printf("[802.11] connect %s\n", data->ifname);
282
283         __supplicant_start(iface);
284
285         __supplicant_connect(iface, data->network, data->passphrase);
286
287         return 0;
288 }
289
290 static void iface_set_network(struct connman_iface *iface,
291                                                 const char *network)
292 {
293         struct iface_data *data = connman_iface_get_data(iface);
294
295         printf("[802.11] set network %s\n", data->ifname);
296
297         g_free(data->network);
298
299         data->network = g_strdup(network);
300 }
301
302 static void iface_set_passphrase(struct connman_iface *iface,
303                                                 const char *passphrase)
304 {
305         struct iface_data *data = connman_iface_get_data(iface);
306
307         printf("[802.11] set passphrase %s\n", data->ifname);
308
309         g_free(data->passphrase);
310
311         data->passphrase = g_strdup(passphrase);
312 }
313
314 static void iface_carrier(struct connman_iface *iface, int carrier)
315 {
316         printf("[802.11] carrier %s\n", carrier ? "on" : "off");
317
318         connman_iface_indicate_carrier(iface, carrier);
319 }
320
321 static void parse_genie(struct station_data *station,
322                                         unsigned char *data, int len)
323 {
324         int offset = 0;
325
326         while (offset <= len - 2) {
327                 //int i;
328
329                 switch (data[offset]) {
330                 case 0xdd:      /* WPA1 (and other) */
331                         break;
332                 case 0x30:      /* WPA2 (RSN) */
333                         break;
334                 default:
335                         break;
336                 }
337
338                 //for (i = 0; i < len; i++)
339                 //      printf(" %02x", data[i]);
340                 //printf("\n");
341
342                 offset += data[offset + 1] + 2;
343         }
344 }
345
346 static void parse_scan_results(struct connman_iface *iface,
347                                         unsigned char *data, int len)
348 {
349         unsigned char *ptr = data;
350         struct station_data *station = NULL;
351         struct ether_addr *eth;
352         char addr[18];
353         int num = 0;
354
355         while (len > IW_EV_LCP_PK_LEN) {
356                 struct iw_event *event = (void *) ptr;
357
358                 switch (event->cmd) {
359                 case SIOCGIWAP:
360                         eth = (void *) &event->u.ap_addr.sa_data;
361                         sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X",
362                                                 eth->ether_addr_octet[0],
363                                                 eth->ether_addr_octet[1],
364                                                 eth->ether_addr_octet[2],
365                                                 eth->ether_addr_octet[3],
366                                                 eth->ether_addr_octet[4],
367                                                 eth->ether_addr_octet[5]);
368                         station = create_station(connman_iface_get_data(iface),
369                                                                         addr);
370                         num++;
371                         break;
372                 case SIOCGIWESSID:
373                         if (station != NULL) {
374                                 station->name = malloc(event->len - 7);
375                                 if (station->name != NULL) {
376                                         memset(station->name, 0, event->len - 7);
377                                         memcpy(station->name, ptr + 8,
378                                                                 event->len - 8);
379                                 }
380                         }
381                         break;
382                 case SIOCGIWNAME:
383                         break;
384                 case SIOCGIWMODE:
385                         if (station != NULL)
386                                 station->mode = event->u.mode;
387                         break;
388                 case SIOCGIWFREQ:
389                         break;
390                 case SIOCGIWENCODE:
391                         if (station != NULL) {
392                                 if (!(event->u.data.flags & IW_ENCODE_DISABLED)) {
393                                         /* privacy */
394                                 }
395                         }
396                         break;
397                 case SIOCGIWRATE:
398                         break;
399                 case IWEVQUAL:
400                         if (station != NULL) {
401                                 station->qual = event->u.qual.qual;
402                                 station->noise = event->u.qual.noise;
403                                 station->level = event->u.qual.level;
404                         }
405                         break;
406                 case IWEVGENIE:
407                         if (station != NULL)
408                                 parse_genie(station, ptr + 8, event->len - 8);
409                         break;
410                 case IWEVCUSTOM:
411                         break;
412                 default:
413                         printf("[802.11] scan element 0x%04x (len %d)\n",
414                                                 event->cmd, event->len);
415                         break;
416                 }
417
418                 ptr += event->len;
419                 len -= event->len;
420         }
421
422         printf("[802.11] found %d networks\n", num);
423 }
424
425 static void iface_scan_results(struct connman_iface *iface)
426 {
427         struct iface_data *data = connman_iface_get_data(iface);
428         struct iwreq iwr;
429         unsigned char *buf;
430         int sk, err, size = 1024;
431
432         sk = socket(PF_INET, SOCK_DGRAM, 0);
433         if (sk < 0)
434                 return;
435
436 retrieve:
437         buf = malloc(size);
438         if (buf == NULL) {
439                 close(sk);
440                 return;
441         }
442
443         memset(&iwr, 0, sizeof(iwr));
444         strncpy(iwr.ifr_name, data->ifname, IFNAMSIZ);
445         iwr.u.data.pointer = buf;
446         iwr.u.data.length = size;
447         iwr.u.data.flags = 0;
448
449         err = ioctl(sk, SIOCGIWSCAN, &iwr);
450         if (err < 0) {
451                 if (errno == E2BIG) {
452                         free(buf);
453                         size *= 2;
454                         goto retrieve;
455                 }
456         } else
457                 parse_scan_results(iface, iwr.u.data.pointer,
458                                                 iwr.u.data.length);
459
460         close(sk);
461
462         free(buf);
463
464         print_stations(data);
465 }
466
467 static void iface_wireless(struct connman_iface *iface,
468                                         void *data, unsigned short len)
469 {
470         struct iw_event *event = data;
471         struct iw_point point;
472         struct ether_addr *eth;
473         char addr[18];
474
475         switch (event->cmd) {
476         case SIOCSIWFREQ:
477                 printf("[802.11] Set Frequency (flags %d)\n",
478                                                         event->u.freq.flags);
479                 break;
480         case SIOCSIWMODE:
481                 printf("[802.11] Set Mode (mode %d)\n", event->u.mode);
482                 break;
483         case SIOCSIWESSID:
484                 memcpy(&point, data + IW_EV_LCP_LEN -
485                                         IW_EV_POINT_OFF, sizeof(point));
486                 point.pointer = data + IW_EV_LCP_LEN +
487                                         sizeof(point) - IW_EV_POINT_OFF;
488                 printf("[802.11] Set ESSID (length %d flags %d) \"%s\"\n",
489                                         point.length, point.flags,
490                                                 (char *) point.pointer);
491                 break;
492         case SIOCSIWENCODE:
493                 printf("[802.11] Set Encryption key (flags %d)\n",
494                                                         event->u.data.flags);
495                 break;
496
497         case SIOCGIWAP:
498                 eth = (void *) &event->u.ap_addr.sa_data;
499                 sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X",
500                                                 eth->ether_addr_octet[0],
501                                                 eth->ether_addr_octet[1],
502                                                 eth->ether_addr_octet[2],
503                                                 eth->ether_addr_octet[3],
504                                                 eth->ether_addr_octet[4],
505                                                 eth->ether_addr_octet[5]);
506                 printf("[802.11] New Access Point %s\n", addr);
507                 break;
508         case SIOCGIWSCAN:
509                 iface_scan_results(iface);
510                 break;
511         default:
512                 printf("[802.11] Wireless event (cmd 0x%04x len %d)\n",
513                                                 event->cmd, event->len);
514                 break;
515         }
516 }
517
518 static struct connman_iface_driver iface_driver = {
519         .name           = "80211",
520         .capability     = "net.80211",
521         .probe          = iface_probe,
522         .remove         = iface_remove,
523         .activate       = iface_activate,
524         .shutdown       = iface_shutdown,
525         .scan           = iface_scan,
526         .connect        = iface_connect,
527         .set_network    = iface_set_network,
528         .set_passphrase = iface_set_passphrase,
529         .rtnl_carrier   = iface_carrier,
530         .rtnl_wireless  = iface_wireless,
531 };
532
533 static int plugin_init(void)
534 {
535         connman_iface_register(&iface_driver);
536
537         return 0;
538 }
539
540 static void plugin_exit(void)
541 {
542         connman_iface_unregister(&iface_driver);
543 }
544
545 CONNMAN_PLUGIN_DEFINE("80211", "IEEE 802.11 interface plugin", VERSION,
546                                                 plugin_init, plugin_exit)