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