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