Clarify which filter types can share the same name
[metze/wireshark/wip.git] / ws80211_utils.c
1 /*
2  * ws80211 utilities
3  * Copyright 2012, Pontus Fuchs <pontus.fuchs@gmail.com>
4
5 $Id$
6
7 Parts of this file was copied from iw:
8
9 Copyright (c) 2007, 2008        Johannes Berg
10 Copyright (c) 2007              Andy Lutomirski
11 Copyright (c) 2007              Mike Kershaw
12 Copyright (c) 2008-2009         Luis R. Rodriguez
13
14 Permission to use, copy, modify, and/or distribute this software for any
15 purpose with or without fee is hereby granted, provided that the above
16 copyright notice and this permission notice appear in all copies.
17
18 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 */
26
27 #include "config.h"
28
29 #include <stdio.h>
30
31 #include <glib.h>
32 #include <glib/gstdio.h>
33
34 #include "ws80211_utils.h"
35
36 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
37 #include <string.h>
38 #include <errno.h>
39 #include <unistd.h>
40
41 #include <net/if.h>
42 #include <sys/ioctl.h>
43
44 #include <netlink/genl/genl.h>
45 #include <netlink/genl/family.h>
46 #include <netlink/genl/ctrl.h>
47 #include <netlink/msg.h>
48 #include <netlink/attr.h>
49
50 #include <linux/nl80211.h>
51
52 /* libnl 1.x compatibility code */
53 #ifdef HAVE_LIBNL1
54 #define nl_sock nl_handle
55 static inline struct nl_handle *nl_socket_alloc(void)
56 {
57         return nl_handle_alloc();
58 }
59
60 static inline void nl_socket_free(struct nl_sock *h)
61 {
62         nl_handle_destroy(h);
63 }
64 #endif /* HAVE_LIBNL1 */
65
66 struct nl80211_state {
67         struct nl_sock *nl_sock;
68         int nl80211_id;
69 };
70
71 static struct nl80211_state nl_state;
72
73 int ws80211_init(void)
74 {
75         int err;
76
77         struct nl80211_state *state = &nl_state;
78
79         state->nl_sock = nl_socket_alloc();
80         if (!state->nl_sock) {
81                 fprintf(stderr, "Failed to allocate netlink socket.\n");
82                 return -ENOMEM;
83         }
84
85         if (genl_connect(state->nl_sock)) {
86                 fprintf(stderr, "Failed to connect to generic netlink.\n");
87                 err = -ENOLINK;
88                 goto out_handle_destroy;
89         }
90
91         state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211");
92         if (state->nl80211_id < 0) {
93                 fprintf(stderr, "nl80211 not found.\n");
94                 err = -ENOENT;
95                 goto out_handle_destroy;
96         }
97
98         return 0;
99
100  out_handle_destroy:
101         nl_socket_free(state->nl_sock);
102         state->nl_sock = 0;
103         return err;
104 }
105
106 static int error_handler(struct sockaddr_nl *nla _U_, struct nlmsgerr *err,
107                          void *arg)
108 {
109         int *ret = (int *)arg;
110         *ret = err->error;
111         return NL_STOP;
112 }
113
114 static int finish_handler(struct nl_msg *msg _U_, void *arg)
115 {
116         int *ret = (int *)arg;
117         *ret = 0;
118         return NL_SKIP;
119 }
120
121 static int ack_handler(struct nl_msg *msg _U_, void *arg)
122 {
123         int *ret = (int *)arg;
124         *ret = 0;
125         return NL_STOP;
126 }
127
128 static int nl80211_do_cmd(struct nl_msg *msg, struct nl_cb *cb)
129 {
130         volatile int err;
131
132         if (!nl_state.nl_sock)
133                 return -ENOLINK;
134
135         err = nl_send_auto_complete(nl_state.nl_sock, msg);
136         if (err < 0)
137                 goto out;
138
139         err = 1;
140
141         nl_cb_err(cb, NL_CB_CUSTOM, error_handler, (void *)&err);
142         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, (void *)&err);
143         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, (void *)&err);
144
145         while (err > 0)
146                 nl_recvmsgs(nl_state.nl_sock, cb);
147  out:
148         nl_cb_put(cb);
149
150         return err;
151 }
152
153 struct nliface_cookie
154 {
155         char *ifname;
156         GArray *interfaces;
157 };
158
159 /*
160  * And now for a steaming heap of suck.
161  *
162  * The nla_for_each_nested() macro defined by at least some versions of the
163  * Linux kernel's headers doesn't do the casting required when compiling
164  * with a C++ compiler or with -Wc++-compat, so we get warnings, and those
165  * warnings are fatal when we compile this file.
166  *
167  * So we replace it with our own version, which does the requisite cast.
168  */
169
170 /**
171  * nla_for_each_nested - iterate over nested attributes
172  * @pos: loop counter, set to current attribute
173  * @nla: attribute containing the nested attributes
174  * @rem: initialized to len, holds bytes currently remaining in stream
175  */
176 #undef nla_for_each_nested
177 #define nla_for_each_nested(pos, nla, rem) \
178         nla_for_each_attr(pos, (struct nlattr *)nla_data(nla), nla_len(nla), rem)
179
180 static int get_phys_handler(struct nl_msg *msg, void *arg)
181 {
182         struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
183         struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
184
185         struct nliface_cookie *cookie = (struct nliface_cookie *)arg;
186
187         struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
188
189         struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
190         static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
191                 {NLA_UNSPEC, 0, 0},             /* __NL80211_FREQUENCY_ATTR_INVALID */
192                 {NLA_U32, 0, 0},                /* NL80211_FREQUENCY_ATTR_FREQ */
193                 {NLA_FLAG, 0, 0},               /* NL80211_FREQUENCY_ATTR_DISABLED */
194                 {NLA_FLAG, 0, 0},               /* NL80211_FREQUENCY_ATTR_PASSIVE_SCAN */
195                 {NLA_FLAG, 0, 0},               /* NL80211_FREQUENCY_ATTR_NO_IBSS */
196                 {NLA_FLAG, 0, 0},               /* NL80211_FREQUENCY_ATTR_RADAR */
197                 {NLA_U32, 0, 0}                 /* NL80211_FREQUENCY_ATTR_MAX_TX_POWER */
198         };
199
200         struct nlattr *nl_band;
201         struct nlattr *nl_freq;
202         struct nlattr *nl_mode;
203         int bandidx = 1;
204         int rem_band, rem_freq, rem_mode;
205         struct ws80211_interface *iface;
206         int cap_monitor = 0;
207
208         nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
209                   genlmsg_attrlen(gnlh, 0), NULL);
210
211         if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
212                 return NL_SKIP;
213
214         if (tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]) {
215                 nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode) {
216                         if (nla_type(nl_mode) == NL80211_IFTYPE_MONITOR)
217                                 cap_monitor = 1;
218                 }
219         }
220         if (!cap_monitor)
221                 return NL_SKIP;
222
223         iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface));
224         if (!iface)
225                 return NL_SKIP;
226
227         iface->frequencies = g_array_new(FALSE, FALSE, sizeof(int));
228         iface->channel_types = 1 << WS80211_CHAN_NO_HT;
229
230         if (tb_msg[NL80211_ATTR_WIPHY_NAME]) {
231                 iface->ifname = g_strdup_printf("%s.mon",
232                 nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]));
233         }
234
235         nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
236                 bandidx++;
237
238                 nla_parse(tb_band, NL80211_BAND_ATTR_MAX,
239                           (struct nlattr *)nla_data(nl_band),
240                           nla_len(nl_band), NULL);
241
242 #ifdef NL80211_BAND_ATTR_HT_CAPA
243                 if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
244                         gboolean ht40;
245                         iface->channel_types |= 1 << WS80211_CHAN_HT20;
246                         ht40 = !!(nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]) & 0x02);
247                         if (ht40) {
248                                 iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS;
249                                 iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS;
250                         }
251                 }
252 #endif /* NL80211_BAND_ATTR_HT_CAPA */
253
254                 nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
255                         uint32_t freq;
256                         nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
257                                   (struct nlattr *)nla_data(nl_freq),
258                                   nla_len(nl_freq), freq_policy);
259                         if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
260                                 continue;
261                         if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
262                                 continue;
263
264                         freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
265                         g_array_append_val(iface->frequencies, freq);
266                 }
267         }
268
269         /* Can frequency be set? Only newer versions of cfg80211 supports this */
270 #ifdef HAVE_NL80211_CMD_SET_CHANNEL
271         if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) {
272                 int cmd;
273                 struct nlattr *nl_cmd;
274                 nla_for_each_nested(nl_cmd, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS], cmd) {
275                         if(nla_get_u32(nl_cmd) == NL80211_CMD_SET_CHANNEL)
276                                 iface->can_set_freq = TRUE;
277                 }
278         }
279 #else
280         iface->can_set_freq = TRUE;
281 #endif
282         g_array_append_val(cookie->interfaces, iface);
283
284         return NL_SKIP;
285 }
286
287
288 static int ws80211_get_phys(GArray *interfaces)
289 {
290         struct nliface_cookie cookie;
291         struct nl_msg *msg;
292         struct nl_cb *cb;
293         msg = nlmsg_alloc();
294         if (!msg) {
295                 fprintf(stderr, "failed to allocate netlink message\n");
296                 return 2;
297         }
298
299         cb = nl_cb_alloc(NL_CB_DEFAULT);
300
301         cookie.interfaces = interfaces;
302
303         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
304                     NLM_F_DUMP, NL80211_CMD_GET_WIPHY, 0);
305
306         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_phys_handler, &cookie);
307
308         return nl80211_do_cmd(msg, cb);
309
310 }
311
312 static int get_freq_wext(const char *ifname)
313 {
314         int fd;
315         int ret = -1;
316         /* Ugly hack to avoid incuding wireless.h */
317         struct {
318                 char name1[IFNAMSIZ];
319                 __s32 m;
320                 __s16 e;
321                 __u8 i;
322                 __u8 flags;
323         } wrq;
324
325         fd = socket(AF_INET, SOCK_DGRAM, 0);
326         if (fd == -1)
327                 return -1;
328
329         g_strlcpy(wrq.name1, ifname, IFNAMSIZ);
330         /* SIOCGIWFREQ */
331         if (ioctl(fd, 0x8B05, &wrq) == 0) {
332                 if (wrq.e == 6)
333                         ret = wrq.m;
334         }
335         close(fd);
336         return ret;
337 }
338
339 struct __iface_info
340 {
341         struct ws80211_iface_info *pub;
342         int type;
343         int phyidx;
344 };
345
346 static int get_iface_info_handler(struct nl_msg *msg, void *arg)
347 {
348         struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
349         struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
350         struct __iface_info *iface_info = (struct __iface_info *)arg;
351
352         nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
353                   genlmsg_attrlen(gnlh, 0), NULL);
354
355         if (tb_msg[NL80211_ATTR_IFTYPE]) {
356                 iface_info->type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]);
357         }
358         if (tb_msg[NL80211_ATTR_WIPHY]) {
359                 iface_info->phyidx = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
360         }
361
362         if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
363                 iface_info->pub->current_freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
364                 iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT;
365
366                 if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
367                         switch (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
368
369                         case NL80211_CHAN_NO_HT:
370                                 iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT;
371                                 break;
372
373                         case NL80211_CHAN_HT20:
374                                 iface_info->pub->current_chan_type = WS80211_CHAN_HT20;
375                                 break;
376
377                         case NL80211_CHAN_HT40MINUS:
378                                 iface_info->pub->current_chan_type = WS80211_CHAN_HT40MINUS;
379                                 break;
380
381                         case NL80211_CHAN_HT40PLUS:
382                                 iface_info->pub->current_chan_type = WS80211_CHAN_HT40PLUS;
383                                 break;
384                         }
385                 }
386
387         }
388         return NL_SKIP;
389 }
390
391
392 static int __ws80211_get_iface_info(const char *name, struct __iface_info *iface_info)
393 {
394         int devidx;
395         struct nl_msg *msg;
396         struct nl_cb *cb;
397         msg = nlmsg_alloc();
398         if (!msg) {
399                 fprintf(stderr, "failed to allocate netlink message\n");
400                 return 2;
401         }
402
403         cb = nl_cb_alloc(NL_CB_DEFAULT);
404
405         devidx = if_nametoindex(name);
406
407         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
408                     0, NL80211_CMD_GET_INTERFACE, 0);
409         NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
410
411         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_info_handler, iface_info);
412
413         if (nl80211_do_cmd(msg, cb))
414                 return -1;
415
416         /* Old kernels cant get the current freq via netlink. Try WEXT too :( */
417         if (iface_info->pub->current_freq == -1)
418                 iface_info->pub->current_freq = get_freq_wext(name);
419         return 0;
420
421 nla_put_failure:
422         fprintf(stderr, "building message failed\n");
423         return -1;
424 }
425
426 int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info)
427 {
428         struct __iface_info __iface_info;
429
430         memset(iface_info, 0, sizeof(*iface_info));
431         __iface_info.pub = iface_info;
432         __iface_info.type = -1;
433         __iface_info.phyidx= -1;
434         __iface_info.pub->current_freq = -1;
435         __iface_info.pub->current_chan_type = WS80211_CHAN_NO_HT;
436
437         return __ws80211_get_iface_info(name, &__iface_info);
438 }
439
440 static int ws80211_populate_devices(GArray *interfaces)
441 {
442         FILE *fh;
443         char line[200];
444         char *t;
445         gchar *t2;
446         char *ret;
447         int i;
448         unsigned int j;
449
450         struct ws80211_iface_info pub;
451         struct __iface_info iface_info;
452         struct ws80211_interface *iface;
453
454         /* Get a list of phy's that can handle monitor mode */
455         ws80211_get_phys(interfaces);
456
457         fh = g_fopen("/proc/net/dev", "r");
458         if(!fh) {
459                 fprintf(stderr, "Cannot open /proc/net/dev");
460                 return -ENOENT;
461         }
462
463         /* Skip the first two lines */
464         for (i = 0; i < 2; i++) {
465                 ret = fgets(line, sizeof(line), fh);
466                 if (ret == NULL) {
467                         fprintf(stderr, "Error parsing /proc/net/dev");
468                         fclose(fh);
469                         return -1;
470                 }
471         }
472
473         /* Update names of user created monitor interfaces */
474         while(fgets(line, sizeof(line), fh)) {
475                 t = index(line, ':');
476                 if (!t)
477                         continue;
478                 *t = 0;
479                 t = line;
480                 while (*t && *t == ' ')
481                         t++;
482                 memset(&iface_info, 0, sizeof(iface_info));
483                 iface_info.pub = &pub;
484                 __ws80211_get_iface_info(t, &iface_info);
485
486                 if (iface_info.type == NL80211_IFTYPE_MONITOR) {
487                         for (j = 0; j < interfaces->len; j++) {
488                                 iface = g_array_index(interfaces, struct ws80211_interface *, j);
489                                 t2 = g_strdup_printf("phy%d.mon", iface_info.phyidx);
490                                 if (t2) {
491                                         if (!strcmp(t2, iface->ifname)) {
492                                                 g_free(iface->ifname);
493                                                 iface->ifname = g_strdup(t);
494                                         }
495                                         g_free(t2);
496                                 }
497                         }
498                 }
499         }
500         fclose(fh);
501         return 0;
502 }
503
504 static int ws80211_iface_up(const char *ifname)
505 {
506         int sock;
507         struct ifreq ifreq;
508
509         sock = socket(AF_PACKET, SOCK_RAW, 0);
510         if (sock == -1)
511                 return -1;
512
513         g_strlcpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name));
514
515         if (ioctl(sock, SIOCGIFFLAGS, &ifreq))
516                 goto out_err;
517
518         ifreq.ifr_flags |= IFF_UP;
519
520         if (ioctl(sock, SIOCSIFFLAGS, &ifreq))
521                 goto out_err;
522
523         close(sock);
524         return 0;
525
526 out_err:
527         close(sock);
528         return -1;
529 }
530
531 static int ws80211_create_on_demand_interface(const char *name)
532 {
533         int devidx, phyidx, err;
534         struct nl_msg *msg;
535         struct nl_cb *cb;
536
537         devidx = if_nametoindex(name);
538         if (devidx)
539                 return ws80211_iface_up(name);
540
541         if (sscanf(name, "phy%d.mon", &phyidx) != 1)
542                 return -EINVAL;
543
544         cb = nl_cb_alloc(NL_CB_DEFAULT);
545         msg = nlmsg_alloc();
546         if (!msg) {
547                 fprintf(stderr, "failed to allocate netlink message\n");
548                 return 2;
549         }
550
551         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
552                     0, NL80211_CMD_NEW_INTERFACE, 0);
553         NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phyidx);
554
555         NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name);
556         NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
557
558         err = nl80211_do_cmd(msg, cb);
559         if (err)
560                 return err;
561         return ws80211_iface_up(name);
562
563 nla_put_failure:
564         fprintf(stderr, "building message failed\n");
565         return 2;
566 }
567
568 int ws80211_set_freq(const char *name, int freq, int chan_type)
569 {
570         int devidx, err;
571         struct nl_msg *msg;
572         struct nl_cb *cb;
573
574         err = ws80211_create_on_demand_interface(name);
575         if (err)
576                 return err;
577
578         msg = nlmsg_alloc();
579         if (!msg) {
580                 fprintf(stderr, "failed to allocate netlink message\n");
581                 return 2;
582         }
583
584         cb = nl_cb_alloc(NL_CB_DEFAULT);
585
586         devidx = if_nametoindex(name);
587
588 #ifdef HAVE_NL80211_CMD_SET_CHANNEL
589         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
590                     0, NL80211_CMD_SET_CHANNEL, 0);
591 #else
592         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
593                     0, NL80211_CMD_SET_WIPHY, 0);
594 #endif
595
596         NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
597         NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
598
599         switch (chan_type) {
600
601 #ifdef NL80211_BAND_ATTR_HT_CAPA
602         case WS80211_CHAN_NO_HT:
603                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_NO_HT);
604                 break;
605
606         case WS80211_CHAN_HT20:
607                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
608                 break;
609
610         case WS80211_CHAN_HT40MINUS:
611                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS);
612                 break;
613
614         case WS80211_CHAN_HT40PLUS:
615                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS);
616                 break;
617 #endif
618
619         default:
620                 break;
621         }
622         err = nl80211_do_cmd(msg, cb);
623         return err;
624
625 nla_put_failure:
626         fprintf(stderr, "building message failed\n");
627         return 2;
628
629 }
630
631 void ws80211_free_interfaces(GArray *interfaces)
632 {
633         struct ws80211_interface *iface;
634
635         if (!interfaces)
636                 return;
637
638         while (interfaces->len) {
639                 iface = g_array_index(interfaces, struct ws80211_interface *, 0);
640                 g_array_remove_index(interfaces, 0);
641                 g_array_free(iface->frequencies, TRUE);
642                 g_free(iface->ifname);
643                 g_free(iface);
644         }
645         g_array_free(interfaces, TRUE);
646 }
647
648 GArray* ws80211_find_interfaces(void)
649 {
650         GArray *interfaces;
651
652         if (!nl_state.nl_sock)
653                 return NULL;
654
655         interfaces = g_array_new(FALSE, FALSE, sizeof(struct ws80211_interface *));
656         if (!interfaces)
657                 return NULL;
658
659         if (ws80211_populate_devices(interfaces)) {
660                 ws80211_free_interfaces(interfaces);
661                 return NULL;
662         }
663         return interfaces;
664 }
665
666 int ws80211_frequency_to_channel(int freq)
667 {
668         if (freq == 2484)
669                 return 14;
670
671         if (freq < 2484)
672                 return (freq - 2407) / 5;
673
674         return freq / 5 - 1000;
675 }
676
677 int
678 ws80211_str_to_chan_type(const gchar *s)
679 {
680         int ret = -1;
681         if (!s)
682                 return -1;
683
684         if (!strcmp(s, CHAN_NO_HT))
685                 ret = WS80211_CHAN_NO_HT;
686         if (!strcmp(s, CHAN_HT20))
687                 ret = WS80211_CHAN_HT20;
688         if (!strcmp(s, CHAN_HT40MINUS))
689                 ret = WS80211_CHAN_HT40MINUS;
690         if (!strcmp(s, CHAN_HT40PLUS))
691                 ret = WS80211_CHAN_HT40PLUS;
692         return ret;
693 }
694
695 const gchar
696 *ws80211_chan_type_to_str(int type)
697 {
698         switch (type) {
699         case WS80211_CHAN_NO_HT:
700                 return CHAN_NO_HT;
701         case WS80211_CHAN_HT20:
702                 return CHAN_HT20;
703         case WS80211_CHAN_HT40MINUS:
704                 return CHAN_HT40MINUS;
705         case WS80211_CHAN_HT40PLUS:
706                 return CHAN_HT40PLUS;
707         }
708         return NULL;
709 }
710
711 #else /* HAVE_LIBNL */
712 int ws80211_init(void)
713 {
714         return -1;
715 }
716
717 GArray* ws80211_find_interfaces(void)
718 {
719         return NULL;
720 }
721
722 int ws80211_get_iface_info(const char *name _U_, struct ws80211_iface_info *iface_info _U_)
723 {
724         return -1;
725 }
726
727 void ws80211_free_interfaces(GArray *interfaces _U_)
728 {
729 }
730
731 int ws80211_frequency_to_channel(int freq _U_)
732 {
733         return -1;
734 }
735
736 int ws80211_set_freq(const char *name _U_, int freq _U_, int chan_type _U_)
737 {
738         return -1;
739 }
740
741 int ws80211_str_to_chan_type(const gchar *s _U_)
742 {
743         return -1;
744 }
745
746 const gchar *ws80211_chan_type_to_str(int type _U_)
747 {
748         return NULL;
749 }
750 #endif /* HAVE_LIBNL && HAVE_NL80211 */