libs/iwinfo: add *_get_freqlist()
[project/luci.git] / libs / 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_bitrate(const char *ifname, int *buf)
179 {
180         unsigned int mode, len, rate, rate_count;
181         uint8_t tmp[24*1024];
182         uint8_t *cp;
183         struct iwreq wrq;
184         struct ieee80211req_sta_info *si;
185
186         if( madwifi_ioctl(&wrq, ifname, SIOCGIWMODE, NULL, 0) >= 0 )
187         {
188                 mode = wrq.u.mode;
189
190                 /* Calculate bitrate average from associated stations in ad-hoc mode */
191                 if( mode == 1 )
192                 {
193                         rate = rate_count = 0;
194
195                         if( (len = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
196                         {
197                                 cp = tmp;
198
199                                 do {
200                                         si = (struct ieee80211req_sta_info *) cp;
201
202                                         if( si->isi_rssi > 0 )
203                                         {
204                                                 rate_count++;
205                                                 rate += ((si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2);
206                                         }
207
208                                         cp   += si->isi_len;
209                                         len  -= si->isi_len;
210                                 } while (len >= sizeof(struct ieee80211req_sta_info));
211                         }
212
213                         *buf = (rate == 0 || rate_count == 0) ? 0 : (rate / rate_count) * 1000;
214                         return 0;
215                 }
216
217                 /* Return whatever wext tells us ... */
218                 return wext_get_bitrate(ifname, buf);
219         }
220
221         return -1;
222 }
223
224 int madwifi_get_signal(const char *ifname, int *buf)
225 {
226         unsigned int mode, len, rssi, rssi_count;
227         uint8_t tmp[24*1024];
228         uint8_t *cp;
229         struct iwreq wrq;
230         struct ieee80211req_sta_info *si;
231
232         if( madwifi_ioctl(&wrq, ifname, SIOCGIWMODE, NULL, 0) >= 0 )
233         {
234                 mode = wrq.u.mode;
235
236                 /* Calculate signal average from associated stations in ap or ad-hoc mode */
237                 if( mode == 1 )
238                 {
239                         rssi = rssi_count = 0;
240
241                         if( (len = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
242                         {
243                                 cp = tmp;
244
245                                 do {
246                                         si = (struct ieee80211req_sta_info *) cp;
247
248                                         if( si->isi_rssi > 0 )
249                                         {
250                                                 rssi_count++;
251                                                 rssi -= (si->isi_rssi - 95);
252                                         }
253
254                                         cp   += si->isi_len;
255                                         len  -= si->isi_len;
256                                 } while (len >= sizeof(struct ieee80211req_sta_info));
257                         }
258
259                         *buf = (rssi == 0 || rssi_count == 0) ? 1 : -(rssi / rssi_count);
260                         return 0;
261                 }
262
263                 /* Return whatever wext tells us ... */
264                 return wext_get_signal(ifname, buf);
265         }
266
267         return -1;
268 }
269
270 int madwifi_get_noise(const char *ifname, int *buf)
271 {
272         return wext_get_noise(ifname, buf);
273 }
274
275 int madwifi_get_quality(const char *ifname, int *buf)
276 {
277         unsigned int mode, len, quality, quality_count;
278         uint8_t tmp[24*1024];
279         uint8_t *cp;
280         struct iwreq wrq;
281         struct ieee80211req_sta_info *si;
282
283         if( madwifi_ioctl(&wrq, ifname, SIOCGIWMODE, NULL, 0) >= 0 )
284         {
285                 mode = wrq.u.mode;
286
287                 /* Calculate signal average from associated stations in ad-hoc mode */
288                 if( mode == 1 )
289                 {
290                         quality = quality_count = 0;
291
292                         if( (len = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
293                         {
294                                 cp = tmp;
295
296                                 do {
297                                         si = (struct ieee80211req_sta_info *) cp;
298
299                                         if( si->isi_rssi > 0 )
300                                         {
301                                                 quality_count++;
302                                                 quality += si->isi_rssi;
303                                         }
304
305                                         cp   += si->isi_len;
306                                         len  -= si->isi_len;
307                                 } while (len >= sizeof(struct ieee80211req_sta_info));
308                         }
309
310                         *buf = (quality == 0 || quality_count == 0) ? 0 : (quality / quality_count);
311                         return 0;
312                 }
313
314                 /* Return whatever wext tells us ... */
315                 return wext_get_quality(ifname, buf);
316         }
317
318         return -1;
319 }
320
321 int madwifi_get_quality_max(const char *ifname, int *buf)
322 {
323         return wext_get_quality_max(ifname, buf);
324 }
325
326 int madwifi_get_enctype(const char *ifname, char *buf)
327 {
328         struct iwreq wrq;
329         struct ieee80211req_key wk;
330         int wpa_version = 0, ciphers = 0, key_type = 0;
331         char cipher_string[32];
332
333         sprintf(buf, "Unknown");
334
335         memset(&wrq, 0, sizeof(wrq));
336         memset(&wk, 0, sizeof(wk));
337         memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
338
339         /* Get key information */
340         if( get80211priv(ifname, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk)) >= 0 )
341                 key_type = wk.ik_type;
342
343         /* Get wpa protocol version */
344         wrq.u.mode = IEEE80211_PARAM_WPA;
345         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
346                 wpa_version = wrq.u.mode;
347
348         /* Get used pairwise ciphers */
349         wrq.u.mode = IEEE80211_PARAM_UCASTCIPHERS;
350         if( madwifi_ioctl(&wrq, ifname, IEEE80211_IOCTL_GETPARAM, NULL, 0) >= 0 )
351         {
352                 ciphers = wrq.u.mode;
353
354                 if( wpa_version > 0 )
355                 {
356                         memset(cipher_string, 0, sizeof(cipher_string));
357
358                         /* Looks like mixed wpa/wpa2 ? */
359                         if( (ciphers & (1<<IEEE80211_CIPHER_TKIP)) && (ciphers & (1<<IEEE80211_CIPHER_AES_CCM)) )
360                                 wpa_version = 3;
361
362
363                         if( (ciphers & (1<<IEEE80211_CIPHER_TKIP)) )
364                                 strcat(cipher_string, "TKIP, ");
365
366                         if( (ciphers & (1<<IEEE80211_CIPHER_AES_CCM)) )
367                                 strcat(cipher_string, "CCMP, ");
368
369                         if( (ciphers & (1<<IEEE80211_CIPHER_AES_OCB)) )
370                                 strcat(cipher_string, "AES-OCB, ");
371
372                         if( (ciphers & (1<<IEEE80211_CIPHER_CKIP)) )
373                                 strcat(cipher_string, "CKIP, ");
374
375                         cipher_string[strlen(cipher_string)-2] = 0;
376                 }
377
378                 switch(wpa_version)
379                 {
380                         case 3:
381                                 sprintf(buf, "mixed WPA/WPA2 (%s)", cipher_string);
382                                 break;
383
384                         case 2:
385                                 sprintf(buf, "WPA2 (%s)", cipher_string);
386                                 break;
387
388                         case 1:
389                                 sprintf(buf, "WPA (%s)", cipher_string);
390                                 break;
391                         
392                         default:
393                                 sprintf(buf, "%s", (key_type == 0) ? "WEP" : "None");
394                 }
395         }
396
397         return 0;
398 }
399
400 int madwifi_get_assoclist(const char *ifname, char *buf, int *len)
401 {
402         int bl, tl, noise;
403         uint8_t *cp;
404         uint8_t tmp[24*1024];
405         struct ieee80211req_sta_info *si;
406         struct iwinfo_assoclist_entry entry;
407
408         if( (tl = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
409         {
410                 cp = tmp;
411                 bl = 0;
412
413                 if( madwifi_get_noise(ifname, &noise) )
414                         noise = 0;
415
416                 do {
417                         si = (struct ieee80211req_sta_info *) cp;
418
419                         entry.signal = (si->isi_rssi - 95);
420                         entry.noise  = noise;
421                         memcpy(entry.mac, &si->isi_macaddr, 6);
422                         memcpy(&buf[bl], &entry, sizeof(struct iwinfo_assoclist_entry));
423
424                         bl += sizeof(struct iwinfo_assoclist_entry);
425                         cp += si->isi_len;
426                         tl -= si->isi_len;
427                 } while (tl >= sizeof(struct ieee80211req_sta_info));
428
429                 *len = bl;
430                 return 0;
431         }
432
433         return -1;
434 }
435
436 int madwifi_get_txpwrlist(const char *ifname, char *buf, int *len)
437 {
438         return wext_get_txpwrlist(ifname, buf, len);
439 }
440
441 int madwifi_get_scanlist(const char *ifname, char *buf, int *len)
442 {
443         int ret;
444         char cmd[256];
445         DIR *proc;
446         struct dirent *e;
447
448         ret = -1;
449
450         /* We got a wifiX device passed, try to lookup a vap on it */
451         if( madwifi_iswifi(ifname) )
452         {
453                 if( (proc = opendir("/proc/sys/net/")) != NULL )
454                 {
455                         while( (e = readdir(proc)) != NULL )
456                         {
457                                 if( madwifi_isvap(e->d_name, ifname) )
458                                 {
459                                         sprintf(cmd, "ifconfig %s up", e->d_name);
460
461                                         if( ! WEXITSTATUS(system(cmd)) )
462                                         {
463                                                 ret = wext_get_scanlist(e->d_name, buf, len);
464                                                 break;
465                                         }
466                                 }
467                         }
468
469                         closedir(proc);
470                 }
471
472                 /* Still nothing found, try to create a vap */
473                 if( ret == -1 )
474                 {
475                         sprintf(cmd, "wlanconfig ath-scan create nounit "
476                                 "wlandev %s wlanmode sta >/dev/null", ifname);
477
478                         if( ! WEXITSTATUS(system(cmd)) && ! WEXITSTATUS(system("ifconfig ath-scan up")) )
479                         {
480                                 ret = wext_get_scanlist("ath-scan", buf, len);
481
482                                 (void) WEXITSTATUS(system("ifconfig ath-scan down"));
483                                 (void) WEXITSTATUS(system("wlanconfig ath-scan destroy"));
484                         }
485                 }
486         }
487
488         /* Got athX device? */
489         else if( madwifi_isvap(ifname, NULL) )
490         {
491                 ret = wext_get_scanlist(ifname, buf, len);
492         }
493
494         return ret;
495 }
496
497 int madwifi_get_freqlist(const char *ifname, char *buf, int *len)
498 {
499         int i, bl;
500         struct ieee80211req_chaninfo chans;
501         struct iwinfo_freqlist_entry entry;
502
503         if( get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans)) >= 0 )
504         {
505                 bl = 0;
506
507                 for( i = 0; i < chans.ic_nchans; i++ )
508                 {
509                         entry.mhz     = (int)(chans.ic_chans[i].ic_freq / 1000);
510                         entry.channel = chans.ic_chans[i].ic_ieee;
511
512                         memcpy(&buf[bl], &entry, sizeof(struct iwinfo_freqlist_entry));
513                         bl += sizeof(struct iwinfo_freqlist_entry);
514                 }
515
516                 *len = bl;
517                 return 0;
518         }
519
520         return -1;
521 }
522
523 int madwifi_get_mbssid_support(const char *ifname, int *buf)
524 {
525         /* We assume that multi bssid is always possible */
526         *buf = 1;
527         return 0;
528 }
529