From Jouni Malinen via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8711 Wi...
[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 <strings.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         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, &err);
142         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
143         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &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                 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
192                 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
193                 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
194                 [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
195                 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
196                 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
197         };
198
199         struct nlattr *nl_band;
200         struct nlattr *nl_freq;
201         struct nlattr *nl_mode;
202         int bandidx = 1;
203         int rem_band, rem_freq, rem_mode;
204         struct ws80211_interface *iface;
205         int cap_monitor = 0;
206
207         nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
208                   genlmsg_attrlen(gnlh, 0), NULL);
209
210         if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
211                 return NL_SKIP;
212
213         if (tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]) {
214                 nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode) {
215                         if (nla_type(nl_mode) == NL80211_IFTYPE_MONITOR)
216                                 cap_monitor = 1;
217                 }
218         }
219         if (!cap_monitor)
220                 return NL_SKIP;
221
222         iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface));
223         if (!iface)
224                 return NL_SKIP;
225
226         iface->frequencies = g_array_new(FALSE, FALSE, sizeof(int));
227         iface->channel_types = 1 << WS80211_CHAN_NO_HT;
228
229         if (tb_msg[NL80211_ATTR_WIPHY_NAME]) {
230                 iface->ifname = g_strdup_printf("%s.mon",
231                 nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]));
232         }
233
234         nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
235                 bandidx++;
236
237                 nla_parse(tb_band, NL80211_BAND_ATTR_MAX,
238                           (struct nlattr *)nla_data(nl_band),
239                           nla_len(nl_band), NULL);
240
241 #ifdef NL80211_BAND_ATTR_HT_CAPA
242                 if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
243                         gboolean ht40;
244                         iface->channel_types |= 1 << WS80211_CHAN_HT20;
245                         ht40 = !!(nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]) & 0x02);
246                         if (ht40) {
247                                 iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS;
248                                 iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS;
249                         }
250                 }
251 #endif /* NL80211_BAND_ATTR_HT_CAPA */
252
253                 nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
254                         uint32_t freq;
255                         nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
256                                   (struct nlattr *)nla_data(nl_freq),
257                                   nla_len(nl_freq), freq_policy);
258                         if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
259                                 continue;
260                         if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
261                                 continue;
262
263                         freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
264                         g_array_append_val(iface->frequencies, freq);
265                 }
266         }
267
268         /* Can frequency be set? Only newer versions of cfg80211 supports this */
269 #ifdef HAVE_NL80211_CMD_SET_CHANNEL
270         if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) {
271                 int cmd;
272                 struct nlattr *nl_cmd;
273                 nla_for_each_nested(nl_cmd, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS], cmd) {
274                         if(nla_get_u32(nl_cmd) == NL80211_CMD_SET_CHANNEL)
275                                 iface->can_set_freq = TRUE;
276                 }
277         }
278 #else
279         iface->can_set_freq = TRUE;
280 #endif
281         g_array_append_val(cookie->interfaces, iface);
282
283         return NL_SKIP;
284 }
285
286
287 static int ws80211_get_phys(GArray *interfaces)
288 {
289         struct nliface_cookie cookie;
290         struct nl_msg *msg;
291         struct nl_cb *cb;
292         msg = nlmsg_alloc();
293         if (!msg) {
294                 fprintf(stderr, "failed to allocate netlink message\n");
295                 return 2;
296         }
297
298         cb = nl_cb_alloc(NL_CB_DEFAULT);
299
300         cookie.interfaces = interfaces;
301
302         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
303                     NLM_F_DUMP, NL80211_CMD_GET_WIPHY, 0);
304
305         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_phys_handler, &cookie);
306
307         return nl80211_do_cmd(msg, cb);
308
309 }
310
311 static int get_freq_wext(const char *ifname)
312 {
313         int fd;
314         int ret = -1;
315         /* Ugly hack to avoid incuding wireless.h */
316         struct {
317                 char name1[IFNAMSIZ];
318                 __s32 m;
319                 __s16 e;
320                 __u8 i;
321                 __u8 flags;
322         } wrq;
323
324         fd = socket(AF_INET, SOCK_DGRAM, 0);
325         if (fd == -1)
326                 return -1;
327
328         g_strlcpy(wrq.name1, ifname, IFNAMSIZ);
329         /* SIOCGIWFREQ */
330         if (ioctl(fd, 0x8B05, &wrq) == 0) {
331                 if (wrq.e == 6)
332                         ret = wrq.m;
333         }
334         close(fd);
335         return ret;
336 }
337
338 struct __iface_info
339 {
340         struct ws80211_iface_info *pub;
341         int type;
342         int phyidx;
343 };
344
345 static int get_iface_info_handler(struct nl_msg *msg, void *arg)
346 {
347         struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
348         struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
349         struct __iface_info *iface_info = (struct __iface_info *)arg;
350
351         nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
352                   genlmsg_attrlen(gnlh, 0), NULL);
353
354         if (tb_msg[NL80211_ATTR_IFTYPE]) {
355                 iface_info->type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]);
356         }
357         if (tb_msg[NL80211_ATTR_WIPHY]) {
358                 iface_info->phyidx = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
359         }
360
361         if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
362                 iface_info->pub->current_freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
363                 iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT;
364
365                 if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
366                         switch (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
367
368                         case NL80211_CHAN_NO_HT:
369                                 iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT;
370                                 break;
371
372                         case NL80211_CHAN_HT20:
373                                 iface_info->pub->current_chan_type = WS80211_CHAN_HT20;
374                                 break;
375
376                         case NL80211_CHAN_HT40MINUS:
377                                 iface_info->pub->current_chan_type = WS80211_CHAN_HT40MINUS;
378                                 break;
379
380                         case NL80211_CHAN_HT40PLUS:
381                                 iface_info->pub->current_chan_type = WS80211_CHAN_HT40PLUS;
382                                 break;
383                         }
384                 }
385
386         }
387         return NL_SKIP;
388 }
389
390
391 static int __ws80211_get_iface_info(const char *name, struct __iface_info *iface_info)
392 {
393         int devidx;
394         struct nl_msg *msg;
395         struct nl_cb *cb;
396         msg = nlmsg_alloc();
397         if (!msg) {
398                 fprintf(stderr, "failed to allocate netlink message\n");
399                 return 2;
400         }
401
402         cb = nl_cb_alloc(NL_CB_DEFAULT);
403
404         devidx = if_nametoindex(name);
405
406         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
407                     0, NL80211_CMD_GET_INTERFACE, 0);
408         NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
409
410         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_info_handler, iface_info);
411
412         if (nl80211_do_cmd(msg, cb))
413                 return -1;
414
415         /* Old kernels cant get the current freq via netlink. Try WEXT too :( */
416         if (iface_info->pub->current_freq == -1)
417                 iface_info->pub->current_freq = get_freq_wext(name);
418         return 0;
419
420 nla_put_failure:
421         fprintf(stderr, "building message failed\n");
422         return -1;
423 }
424
425 int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info)
426 {
427         struct __iface_info __iface_info;
428
429         memset(iface_info, 0, sizeof(*iface_info));
430         __iface_info.pub = iface_info;
431         __iface_info.type = -1;
432         __iface_info.phyidx= -1;
433         __iface_info.pub->current_freq = -1;
434         __iface_info.pub->current_chan_type = WS80211_CHAN_NO_HT;
435
436         return __ws80211_get_iface_info(name, &__iface_info);
437 }
438
439 static int ws80211_populate_devices(GArray *interfaces)
440 {
441         FILE *fh;
442         char line[200];
443         char *t;
444         gchar *t2;
445         char *ret;
446         int i;
447         unsigned int j;
448
449         struct ws80211_iface_info pub;
450         struct __iface_info iface_info;
451         struct ws80211_interface *iface;
452
453         /* Get a list of phy's that can handle monitor mode */
454         ws80211_get_phys(interfaces);
455
456         fh = g_fopen("/proc/net/dev", "r");
457         if(!fh) {
458                 fprintf(stderr, "Cannot open /proc/net/dev");
459                 return -ENOENT;
460         }
461
462         /* Skip the first two lines */
463         for (i = 0; i < 2; i++) {
464                 ret = fgets(line, sizeof(line), fh);
465                 if (ret == NULL) {
466                         fprintf(stderr, "Error parsing /proc/net/dev");
467                         fclose(fh);
468                         return -1;
469                 }
470         }
471
472         /* Update names of user created monitor interfaces */
473         while(fgets(line, sizeof(line), fh)) {
474                 t = index(line, ':');
475                 if (!t)
476                         continue;
477                 *t = 0;
478                 t = line;
479                 while (*t && *t == ' ')
480                         t++;
481                 memset(&iface_info, 0, sizeof(iface_info));
482                 iface_info.pub = &pub;
483                 __ws80211_get_iface_info(t, &iface_info);
484
485                 if (iface_info.type == NL80211_IFTYPE_MONITOR) {
486                         for (j = 0; j < interfaces->len; j++) {
487                                 iface = g_array_index(interfaces, struct ws80211_interface *, j);
488                                 t2 = g_strdup_printf("phy%d.mon", iface_info.phyidx);
489                                 if (t2) {
490                                         if (!strcmp(t2, iface->ifname)) {
491                                                 g_free(iface->ifname);
492                                                 iface->ifname = g_strdup(t);
493                                         }
494                                         g_free(t2);
495                                 }
496                         }
497                 }
498         }
499         fclose(fh);
500         return 0;
501 }
502
503 static int ws80211_iface_up(const char *ifname)
504 {
505         int sock;
506         struct ifreq ifreq;
507
508         sock = socket(AF_PACKET, SOCK_RAW, 0);
509         if (sock == -1)
510                 return -1;
511
512         g_strlcpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name));
513
514         if (ioctl(sock, SIOCGIFFLAGS, &ifreq))
515                 goto out_err;
516
517         ifreq.ifr_flags |= IFF_UP;
518
519         if (ioctl(sock, SIOCSIFFLAGS, &ifreq))
520                 goto out_err;
521
522         close(sock);
523         return 0;
524
525 out_err:
526         close(sock);
527         return -1;
528 }
529
530 static int ws80211_create_on_demand_interface(const char *name)
531 {
532         int devidx, phyidx, err;
533         struct nl_msg *msg;
534         struct nl_cb *cb;
535
536         devidx = if_nametoindex(name);
537         if (devidx)
538                 return ws80211_iface_up(name);
539
540         if (sscanf(name, "phy%d.mon", &phyidx) != 1)
541                 return -EINVAL;
542
543         cb = nl_cb_alloc(NL_CB_DEFAULT);
544         msg = nlmsg_alloc();
545         if (!msg) {
546                 fprintf(stderr, "failed to allocate netlink message\n");
547                 return 2;
548         }
549
550         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
551                     0, NL80211_CMD_NEW_INTERFACE, 0);
552         NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phyidx);
553
554         NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name);
555         NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
556
557         err = nl80211_do_cmd(msg, cb);
558         if (err)
559                 return err;
560         return ws80211_iface_up(name);
561
562 nla_put_failure:
563         fprintf(stderr, "building message failed\n");
564         return 2;
565 }
566
567 int ws80211_set_freq(const char *name, int freq, int chan_type)
568 {
569         int devidx, err;
570         struct nl_msg *msg;
571         struct nl_cb *cb;
572
573         err = ws80211_create_on_demand_interface(name);
574         if (err)
575                 return err;
576
577         msg = nlmsg_alloc();
578         if (!msg) {
579                 fprintf(stderr, "failed to allocate netlink message\n");
580                 return 2;
581         }
582
583         cb = nl_cb_alloc(NL_CB_DEFAULT);
584
585         devidx = if_nametoindex(name);
586
587 #ifdef HAVE_NL80211_CMD_SET_CHANNEL
588         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
589                     0, NL80211_CMD_SET_CHANNEL, 0);
590 #else
591         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
592                     0, NL80211_CMD_SET_WIPHY, 0);
593 #endif
594
595         NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
596         NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
597
598         switch (chan_type) {
599
600 #ifdef NL80211_BAND_ATTR_HT_CAPA
601         case WS80211_CHAN_NO_HT:
602                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_NO_HT);
603                 break;
604
605         case WS80211_CHAN_HT20:
606                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
607                 break;
608
609         case WS80211_CHAN_HT40MINUS:
610                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS);
611                 break;
612
613         case WS80211_CHAN_HT40PLUS:
614                 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS);
615                 break;
616 #endif
617
618         default:
619                 break;
620         }
621         err = nl80211_do_cmd(msg, cb);
622         return err;
623
624 nla_put_failure:
625         fprintf(stderr, "building message failed\n");
626         return 2;
627
628 }
629
630 void ws80211_free_interfaces(GArray *interfaces)
631 {
632         struct ws80211_interface *iface;
633
634         if (!interfaces)
635                 return;
636
637         while (interfaces->len) {
638                 iface = g_array_index(interfaces, struct ws80211_interface *, 0);
639                 g_array_remove_index(interfaces, 0);
640                 g_array_free(iface->frequencies, TRUE);
641                 g_free(iface->ifname);
642                 g_free(iface);
643         }
644         g_array_free(interfaces, TRUE);
645 }
646
647 GArray* ws80211_find_interfaces(void)
648 {
649         GArray *interfaces;
650
651         if (!nl_state.nl_sock)
652                 return NULL;
653
654         interfaces = g_array_new(FALSE, FALSE, sizeof(struct ws80211_interface *));
655         if (!interfaces)
656                 return NULL;
657
658         if (ws80211_populate_devices(interfaces)) {
659                 ws80211_free_interfaces(interfaces);
660                 return NULL;
661         }
662         return interfaces;
663 }
664
665 int ws80211_frequency_to_channel(int freq)
666 {
667         if (freq == 2484)
668                 return 14;
669
670         if (freq < 2484)
671                 return (freq - 2407) / 5;
672
673         return freq / 5 - 1000;
674 }
675
676 int
677 ws80211_str_to_chan_type(const gchar *s)
678 {
679         int ret = -1;
680         if (!s)
681                 return -1;
682
683         if (!strcmp(s, CHAN_NO_HT))
684                 ret = WS80211_CHAN_NO_HT;
685         if (!strcmp(s, CHAN_HT20))
686                 ret = WS80211_CHAN_HT20;
687         if (!strcmp(s, CHAN_HT40MINUS))
688                 ret = WS80211_CHAN_HT40MINUS;
689         if (!strcmp(s, CHAN_HT40PLUS))
690                 ret = WS80211_CHAN_HT40PLUS;
691         return ret;
692 }
693
694 const gchar
695 *ws80211_chan_type_to_str(int type)
696 {
697         switch (type) {
698         case WS80211_CHAN_NO_HT:
699                 return CHAN_NO_HT;
700         case WS80211_CHAN_HT20:
701                 return CHAN_HT20;
702         case WS80211_CHAN_HT40MINUS:
703                 return CHAN_HT40MINUS;
704         case WS80211_CHAN_HT40PLUS:
705                 return CHAN_HT40PLUS;
706         }
707         return NULL;
708 }
709
710 #else /* HAVE_LIBNL */
711 int ws80211_init(void)
712 {
713         return -1;
714 }
715
716 GArray* ws80211_find_interfaces(void)
717 {
718         return NULL;
719 }
720
721 int ws80211_get_iface_info(const char *name _U_, struct ws80211_iface_info *iface_info _U_)
722 {
723         return -1;
724 }
725
726 void ws80211_free_interfaces(GArray *interfaces _U_)
727 {
728 }
729
730 int ws80211_frequency_to_channel(int freq _U_)
731 {
732         return -1;
733 }
734
735 int ws80211_set_freq(const char *name _U_, int freq _U_, int chan_type _U_)
736 {
737         return -1;
738 }
739
740 int ws80211_str_to_chan_type(const gchar *s _U_)
741 {
742         return -1;
743 }
744
745 const gchar *ws80211_chan_type_to_str(int type _U_)
746 {
747         return NULL;
748 }
749 #endif /* HAVE_LIBNL && HAVE_NL80211 */