1c4368799ac45546ecf86e616a729bf0b9ca7976
[project/luci.git] / contrib / package / iwinfo / src / iwinfo_madwifi.c
1 /*
2  * iwinfo - Wireless Information Library - Madwifi Backend
3  *
4  *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  * The iwinfo library is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation.
9  *
10  * The iwinfo library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with the iwinfo library. If not, see http://www.gnu.org/licenses/.
17  *
18  * The signal handling code is derived from the official madwifi tools,
19  * wlanconfig.c in particular. The encryption property handling was
20  * inspired by the hostapd madwifi driver.
21  */
22
23 #include "iwinfo_madwifi.h"
24 #include "iwinfo_wext.h"
25
26 static int ioctl_socket = -1;
27
28 static int madwifi_ioctl(struct iwreq *wrq, const char *ifname, int cmd, void *data, size_t len)
29 {
30         /* prepare socket */
31         if( ioctl_socket == -1 )
32                 ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
33
34         strncpy(wrq->ifr_name, ifname, IFNAMSIZ);
35
36         if( data != NULL )
37         {
38                 if( len < IFNAMSIZ )
39                 {
40                         memcpy(wrq->u.name, data, len);
41                 }
42                 else
43                 {
44                         wrq->u.data.pointer = data;
45                         wrq->u.data.length = len;
46                 }
47         }
48
49         return ioctl(ioctl_socket, cmd, wrq);
50 }
51
52 static int get80211priv(const char *ifname, int op, void *data, size_t len)
53 {
54         struct iwreq iwr;
55
56         if( madwifi_ioctl(&iwr, ifname, op, data, len) < 0 )
57                 return -1;
58
59         return iwr.u.data.length;
60 }
61
62 static int madwifi_isvap(const char *ifname, const char *wifiname)
63 {
64         int fd, ret;
65         char path[32];
66         char name[IFNAMSIZ];
67
68         ret = 0;
69
70         if( strlen(ifname) <= 9 )
71         {
72                 sprintf(path, "/proc/sys/net/%s/%%parent", ifname);
73
74                 if( (fd = open(path, O_RDONLY)) > -1 )
75                 {
76                         if( wifiname != NULL )
77                         {
78                                 if( read(fd, name, strlen(wifiname)) == strlen(wifiname) )
79                                         ret = strncmp(name, wifiname, strlen(wifiname)) ? 0 : 1;
80                         }
81                         else if( read(fd, name, 4) == 4 )
82                         {
83                                 ret = strncmp(name, "wifi", 4) ? 0 : 1;
84                         }
85
86                         (void) close(fd);
87                 }
88         }
89
90         return ret;
91 }
92
93 static int madwifi_iswifi(const char *ifname)
94 {
95         int ret;
96         char path[32];
97         struct stat s;
98
99         ret = 0;
100
101         if( strlen(ifname) <= 7 )
102         {
103                 sprintf(path, "/proc/sys/dev/%s/diversity", ifname);
104
105                 if( ! stat(path, &s) )
106                         ret = (s.st_mode & S_IFREG);
107         }
108
109         return ret;
110 }
111
112
113 int madwifi_probe(const char *ifname)
114 {
115         return ( madwifi_isvap(ifname, NULL) || madwifi_iswifi(ifname) );
116 }
117
118 int madwifi_get_mode(const char *ifname, char *buf)
119 {
120         return wext_get_mode(ifname, buf);
121 }
122
123 int madwifi_get_ssid(const char *ifname, char *buf)
124 {
125         return wext_get_ssid(ifname, buf);
126 }
127
128 int madwifi_get_bssid(const char *ifname, char *buf)
129 {
130         return wext_get_bssid(ifname, buf);
131 }
132
133 int madwifi_get_channel(const char *ifname, int *buf)
134 {
135         int i;
136         uint16_t freq;
137         struct iwreq wrq;
138         struct ieee80211req_chaninfo chans;
139
140         if( madwifi_ioctl(&wrq, ifname, SIOCGIWFREQ, NULL, 0) >= 0 )
141         {
142                 /* Madwifi returns a Hz frequency, get it's freq list to find channel index */
143                 freq = (uint16_t)(wrq.u.freq.m / 100000);
144
145                 if( get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans)) >= 0 )
146                 {
147                         *buf = 0;
148
149                         for( i = 0; i < chans.ic_nchans; i++ )
150                         {
151                                 if( freq == chans.ic_chans[i].ic_freq )
152                                 {
153                                         *buf = chans.ic_chans[i].ic_ieee;
154                                         break;
155                                 }
156                         }
157
158                         return 0;
159                 }
160         }
161
162         return -1;
163 }
164
165 int madwifi_get_frequency(const char *ifname, int *buf)
166 {
167         struct iwreq wrq;
168
169         if( madwifi_ioctl(&wrq, ifname, SIOCGIWFREQ, NULL, 0) >= 0 )
170         {
171                 *buf = (uint16_t)(wrq.u.freq.m / 100000);
172                 return 0;
173         }
174
175         return -1;
176 }
177
178 int madwifi_get_txpower(const char *ifname, int *buf)
179 {
180         return wext_get_txpower(ifname, buf);
181 }
182
183 int madwifi_get_bitrate(const char *ifname, int *buf)
184 {
185         unsigned int mode, len, rate, rate_count;
186         uint8_t tmp[24*1024];
187         uint8_t *cp;
188         struct iwreq wrq;
189         struct ieee80211req_sta_info *si;
190
191         if( madwifi_ioctl(&wrq, ifname, SIOCGIWMODE, NULL, 0) >= 0 )
192         {
193                 mode = wrq.u.mode;
194
195                 /* Calculate bitrate average from associated stations in ad-hoc mode */
196                 if( mode == 1 )
197                 {
198                         rate = rate_count = 0;
199
200                         if( (len = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
201                         {
202                                 cp = tmp;
203
204                                 do {
205                                         si = (struct ieee80211req_sta_info *) cp;
206
207                                         if( si->isi_rssi > 0 )
208                                         {
209                                                 rate_count++;
210                                                 rate += ((si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2);
211                                         }
212
213                                         cp   += si->isi_len;
214                                         len  -= si->isi_len;
215                                 } while (len >= sizeof(struct ieee80211req_sta_info));
216                         }
217
218                         *buf = (rate == 0 || rate_count == 0) ? 0 : (rate / rate_count) * 1000;
219                         return 0;
220                 }
221
222                 /* Return whatever wext tells us ... */
223                 return wext_get_bitrate(ifname, buf);
224         }
225
226         return -1;
227 }
228
229 int madwifi_get_signal(const char *ifname, int *buf)
230 {
231         unsigned int mode, len, rssi, rssi_count;
232         uint8_t tmp[24*1024];
233         uint8_t *cp;
234         struct iwreq wrq;
235         struct ieee80211req_sta_info *si;
236
237         if( madwifi_ioctl(&wrq, ifname, SIOCGIWMODE, NULL, 0) >= 0 )
238         {
239                 mode = wrq.u.mode;
240
241                 /* Calculate signal average from associated stations in ap or ad-hoc mode */
242                 if( mode == 1 )
243                 {
244                         rssi = rssi_count = 0;
245
246                         if( (len = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
247                         {
248                                 cp = tmp;
249
250                                 do {
251                                         si = (struct ieee80211req_sta_info *) cp;
252
253                                         if( si->isi_rssi > 0 )
254                                         {
255                                                 rssi_count++;
256                                                 rssi -= (si->isi_rssi - 95);
257                                         }
258
259                                         cp   += si->isi_len;
260                                         len  -= si->isi_len;
261                                 } while (len >= sizeof(struct ieee80211req_sta_info));
262                         }
263
264                         *buf = (rssi == 0 || rssi_count == 0) ? 1 : -(rssi / rssi_count);
265                         return 0;
266                 }
267
268                 /* Return whatever wext tells us ... */
269                 return wext_get_signal(ifname, buf);
270         }
271
272         return -1;
273 }
274
275 int madwifi_get_noise(const char *ifname, int *buf)
276 {
277         return wext_get_noise(ifname, buf);
278 }
279
280 int madwifi_get_quality(const char *ifname, int *buf)
281 {
282         unsigned int mode, len, quality, quality_count;
283         uint8_t tmp[24*1024];
284         uint8_t *cp;
285         struct iwreq wrq;
286         struct ieee80211req_sta_info *si;
287
288         if( madwifi_ioctl(&wrq, ifname, SIOCGIWMODE, NULL, 0) >= 0 )
289         {
290                 mode = wrq.u.mode;
291
292                 /* Calculate signal average from associated stations in ad-hoc mode */
293                 if( mode == 1 )
294                 {
295                         quality = quality_count = 0;
296
297                         if( (len = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
298                         {
299                                 cp = tmp;
300
301                                 do {
302                                         si = (struct ieee80211req_sta_info *) cp;
303
304                                         if( si->isi_rssi > 0 )
305                                         {
306                                                 quality_count++;
307                                                 quality += si->isi_rssi;
308                                         }
309
310                                         cp   += si->isi_len;
311                                         len  -= si->isi_len;
312                                 } while (len >= sizeof(struct ieee80211req_sta_info));
313                         }
314
315                         *buf = (quality == 0 || quality_count == 0) ? 0 : (quality / quality_count);
316                         return 0;
317                 }
318
319                 /* Return whatever wext tells us ... */
320                 return wext_get_quality(ifname, buf);
321         }
322
323         return -1;
324 }
325
326 int madwifi_get_quality_max(const char *ifname, int *buf)
327 {
328         return wext_get_quality_max(ifname, buf);
329 }
330
331 int madwifi_get_encryption(const char *ifname, char *buf)
332 {
333         int ciphers = 0, key_type = 0, key_len = 0;
334         struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
335         struct iwreq wrq;
336         struct ieee80211req_key wk;
337
338         memset(&wrq, 0, sizeof(wrq));
339         memset(&wk, 0, sizeof(wk));
340         memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
341
342         /* Get key information */
343         if( get80211priv(ifname, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk)) >= 0 )
344         {
345                 key_type = wk.ik_type;
346
347                 /* Type 0 == WEP */
348                 if( key_type == 0 )
349                         c->auth_algs = (IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED);
350         }
351
352         /* Get wpa protocol version */
353         wrq.u.mode = IEEE80211_PARAM_WPA;
354         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
355                 c->wpa_version = wrq.u.mode;
356
357         /* Get authentication suites */
358         wrq.u.mode = IEEE80211_PARAM_AUTHMODE;
359         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
360         {
361                 switch(wrq.u.mode) {
362                         case IEEE80211_AUTH_8021X:
363                                 c->auth_suites |= IWINFO_KMGMT_8021x;
364                                 break;
365
366                         case IEEE80211_AUTH_WPA:
367                                 c->auth_suites |= IWINFO_KMGMT_PSK;
368                                 break;
369
370                         default:
371                                 c->auth_suites |= IWINFO_KMGMT_NONE;
372                                 break;
373                 }
374         }
375
376         /* Get group key length */
377         wrq.u.mode = IEEE80211_PARAM_MCASTKEYLEN;
378         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
379                 key_len = wrq.u.mode;
380
381         /* Get used pairwise ciphers */
382         wrq.u.mode = IEEE80211_PARAM_UCASTCIPHERS;
383         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
384         {
385                 ciphers = wrq.u.mode;
386
387                 if( ciphers & (1 << IEEE80211_CIPHER_TKIP) )
388                         c->pair_ciphers |= IWINFO_CIPHER_TKIP;
389
390                 if( ciphers & (1 << IEEE80211_CIPHER_AES_CCM) )
391                         c->pair_ciphers |= IWINFO_CIPHER_CCMP;
392
393                 if( ciphers & (1 << IEEE80211_CIPHER_AES_OCB) )
394                         c->pair_ciphers |= IWINFO_CIPHER_AESOCB;
395
396                 if( ciphers & (1 << IEEE80211_CIPHER_CKIP) )
397                         c->pair_ciphers |= IWINFO_CIPHER_CKIP;
398
399                 if( ciphers & (1 << IEEE80211_CIPHER_WEP) )
400                 {
401                         switch(key_len) {
402                                 case 13:
403                                         c->pair_ciphers |= IWINFO_CIPHER_WEP104;
404                                         break;
405
406                                 case 5:
407                                         c->pair_ciphers |= IWINFO_CIPHER_WEP40;
408                                         break;
409
410                                 default:
411                                         break;
412                         }
413                 }
414
415                 if( ciphers & (1 << IEEE80211_CIPHER_NONE) )
416                         c->pair_ciphers |= IWINFO_CIPHER_NONE;
417         }
418
419         /* Get used group cipher */
420         wrq.u.mode = IEEE80211_PARAM_MCASTCIPHER;
421         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
422         {
423                 ciphers = wrq.u.mode;
424
425                 switch(wrq.u.mode) {
426                         case IEEE80211_CIPHER_TKIP:
427                                 c->group_ciphers |= IWINFO_CIPHER_TKIP;
428                                 break;
429
430                         case IEEE80211_CIPHER_AES_CCM:
431                                 c->group_ciphers |= IWINFO_CIPHER_CCMP;
432                                 break;
433
434                         case IEEE80211_CIPHER_AES_OCB:
435                                 c->group_ciphers |= IWINFO_CIPHER_AESOCB;
436                                 break;
437
438                         case IEEE80211_CIPHER_CKIP:
439                                 c->group_ciphers |= IWINFO_CIPHER_CKIP;
440                                 break;
441
442                         case IEEE80211_CIPHER_WEP:
443                                 switch(key_len) {
444                                         case 13:
445                                                 c->group_ciphers |= IWINFO_CIPHER_WEP104;
446                                                 break;
447
448                                         case 5:
449                                                 c->group_ciphers |= IWINFO_CIPHER_WEP40;
450                                                 break;
451
452                                         default:
453                                                 break;
454                                 }
455                                 break;
456
457                         case IEEE80211_CIPHER_NONE:
458                                 c->group_ciphers |= IWINFO_CIPHER_NONE;
459                                 break;
460
461                         default:
462                                 break;
463                 }
464         }
465
466         c->enabled = (c->wpa_version || c->auth_algs) ? 1 : 0;
467
468         return 0;
469 }
470
471 int madwifi_get_assoclist(const char *ifname, char *buf, int *len)
472 {
473         int bl, tl, noise;
474         uint8_t *cp;
475         uint8_t tmp[24*1024];
476         struct ieee80211req_sta_info *si;
477         struct iwinfo_assoclist_entry entry;
478
479         if( (tl = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
480         {
481                 cp = tmp;
482                 bl = 0;
483
484                 if( madwifi_get_noise(ifname, &noise) )
485                         noise = 0;
486
487                 do {
488                         si = (struct ieee80211req_sta_info *) cp;
489
490                         entry.signal = (si->isi_rssi - 95);
491                         entry.noise  = noise;
492                         memcpy(entry.mac, &si->isi_macaddr, 6);
493                         memcpy(&buf[bl], &entry, sizeof(struct iwinfo_assoclist_entry));
494
495                         bl += sizeof(struct iwinfo_assoclist_entry);
496                         cp += si->isi_len;
497                         tl -= si->isi_len;
498                 } while (tl >= sizeof(struct ieee80211req_sta_info));
499
500                 *len = bl;
501                 return 0;
502         }
503
504         return -1;
505 }
506
507 int madwifi_get_txpwrlist(const char *ifname, char *buf, int *len)
508 {
509         int rc = -1;
510         char cmd[256];
511
512         /* A wifiX device? */
513         if( madwifi_iswifi(ifname) )
514         {
515                 sprintf(cmd, "wlanconfig ath-txpwr create nounit "
516                         "wlandev %s wlanmode ap >/dev/null", ifname);
517
518                 if( ! WEXITSTATUS(system(cmd)) )
519                 {
520                         rc = wext_get_txpwrlist("ath-txpwr", buf, len);
521                         (void) WEXITSTATUS(system("wlanconfig ath-txpwr destroy"));
522                 }
523         }
524
525         /* Its an athX ... */
526         else if( madwifi_isvap(ifname, NULL) )
527         {
528                 rc = wext_get_txpwrlist(ifname, buf, len);
529         }
530
531         return rc;
532 }
533
534 int madwifi_get_scanlist(const char *ifname, char *buf, int *len)
535 {
536         int ret;
537         char cmd[256];
538         DIR *proc;
539         struct dirent *e;
540
541         ret = -1;
542
543         /* We got a wifiX device passed, try to lookup a vap on it */
544         if( madwifi_iswifi(ifname) )
545         {
546                 if( (proc = opendir("/proc/sys/net/")) != NULL )
547                 {
548                         while( (e = readdir(proc)) != NULL )
549                         {
550                                 if( madwifi_isvap(e->d_name, ifname) )
551                                 {
552                                         sprintf(cmd, "ifconfig %s up", e->d_name);
553
554                                         if( ! WEXITSTATUS(system(cmd)) )
555                                         {
556                                                 ret = wext_get_scanlist(e->d_name, buf, len);
557                                                 break;
558                                         }
559                                 }
560                         }
561
562                         closedir(proc);
563                 }
564
565                 /* Still nothing found, try to create a vap */
566                 if( ret == -1 )
567                 {
568                         sprintf(cmd, "wlanconfig ath-scan create nounit "
569                                 "wlandev %s wlanmode sta >/dev/null", ifname);
570
571                         if( ! WEXITSTATUS(system(cmd)) && ! WEXITSTATUS(system("ifconfig ath-scan up")) )
572                         {
573                                 ret = wext_get_scanlist("ath-scan", buf, len);
574
575                                 (void) WEXITSTATUS(system("ifconfig ath-scan down"));
576                                 (void) WEXITSTATUS(system("wlanconfig ath-scan destroy"));
577                         }
578                 }
579         }
580
581         /* Got athX device? */
582         else if( madwifi_isvap(ifname, NULL) )
583         {
584                 ret = wext_get_scanlist(ifname, buf, len);
585         }
586
587         return ret;
588 }
589
590 int madwifi_get_freqlist(const char *ifname, char *buf, int *len)
591 {
592         int i, bl;
593         int rc = -1;
594         char cmd[256];
595         struct ieee80211req_chaninfo chans;
596         struct iwinfo_freqlist_entry entry;
597
598         /* A wifiX device? */
599         if( madwifi_iswifi(ifname) )
600         {
601                 sprintf(cmd, "wlanconfig ath-channels create nounit "
602                         "wlandev %s wlanmode ap >/dev/null", ifname);
603
604                 if( ! WEXITSTATUS(system(cmd)) )
605                 {
606                         rc = get80211priv("ath-channels", IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans));
607                         (void) WEXITSTATUS(system("wlanconfig ath-channels destroy"));
608                 }
609         }
610
611         /* Its an athX ... */
612         else if( madwifi_isvap(ifname, NULL) )
613         {
614                 rc = get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans));
615         }
616
617
618         /* Got chaninfo? */
619         if( rc >= 0 )
620         {
621                 bl = 0;
622
623                 for( i = 0; i < chans.ic_nchans; i++ )
624                 {
625                         entry.mhz     = chans.ic_chans[i].ic_freq;
626                         entry.channel = chans.ic_chans[i].ic_ieee;
627
628                         memcpy(&buf[bl], &entry, sizeof(struct iwinfo_freqlist_entry));
629                         bl += sizeof(struct iwinfo_freqlist_entry);
630                 }
631
632                 *len = bl;
633                 return 0;
634         }
635
636         return -1;
637 }
638
639 int madwifi_get_mbssid_support(const char *ifname, int *buf)
640 {
641         /* We assume that multi bssid is always possible */
642         *buf = 1;
643         return 0;
644 }
645