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