Move the common parts of iface_lists.[ch] from ui/gtk/ to ui/. Leave the
[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                 iface->ifname = g_strdup_printf("%s.mon",
211                 nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]));
212         }
213
214         nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
215                 bandidx++;
216
217                 nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
218                           nla_len(nl_band), NULL);
219
220 #ifdef NL80211_BAND_ATTR_HT_CAPA
221                 if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
222                         gboolean ht40;
223                         iface->channel_types |= 1 << WS80211_CHAN_HT20;
224                         ht40 = !!(nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]) & 0x02);
225                         if (ht40) {
226                                 iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS;
227                                 iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS;
228                         }
229                 }
230 #endif /* NL80211_BAND_ATTR_HT_CAPA */
231
232                 nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
233                         uint32_t freq;
234                         nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
235                                   nla_len(nl_freq), freq_policy);
236                         if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
237                                 continue;
238                         if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
239                                 continue;
240
241                         freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
242                         g_array_append_val(iface->frequencies, freq);
243                 }
244         }
245
246         /* Can frequency be set? */
247         if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) {
248                 int cmd;
249                 nla_for_each_nested(nl_cmd, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS], cmd) {
250                         if(nla_get_u32(nl_cmd) == NL80211_CMD_SET_CHANNEL)
251                                 iface->can_set_freq = TRUE;
252                 }
253         }
254         g_array_append_val(cookie->interfaces, iface);
255
256         return NL_SKIP;
257 }
258
259
260 static int ws80211_get_phys(GArray *interfaces)
261 {
262         struct nliface_cookie cookie;
263         struct nl_msg *msg;
264         struct nl_cb *cb;
265         msg = nlmsg_alloc();
266         if (!msg) {
267                 fprintf(stderr, "failed to allocate netlink message\n");
268                 return 2;
269         }
270
271         cb = nl_cb_alloc(NL_CB_DEFAULT);
272
273         cookie.interfaces = interfaces;
274
275         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
276                     NLM_F_DUMP, NL80211_CMD_GET_WIPHY, 0);
277
278         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_phys_handler, &cookie);
279
280         return nl80211_do_cmd(msg, cb);
281
282 }
283
284 static int get_freq_wext(const char *ifname)
285 {
286         int fd;
287         int ret = -1;
288         /* Ugly hack to avoid incuding wireless.h */
289         struct {
290                 char name1[IFNAMSIZ];
291                 __s32 m;
292                 __s16 e;
293                 __u8 i;
294                 __u8 flags;
295         } wrq;
296
297         fd = socket(AF_INET, SOCK_DGRAM, 0);
298         if (fd == -1)
299                 return -1;
300
301         g_strlcpy(wrq.name1, ifname, IFNAMSIZ);
302         /* SIOCGIWFREQ */
303         if (ioctl(fd, 0x8B05, &wrq) == 0) {
304                 if (wrq.e == 6)
305                         ret = wrq.m;
306         }
307         close(fd);
308         return ret;
309 }
310
311 struct __iface_info
312 {
313         struct ws80211_iface_info *pub;
314         int type;
315         int phyidx;
316 };
317
318 static int get_iface_info_handler(struct nl_msg *msg, void *arg)
319 {
320         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
321         struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
322         struct __iface_info *iface_info = arg;
323
324         nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
325                   genlmsg_attrlen(gnlh, 0), NULL);
326
327         if (tb_msg[NL80211_ATTR_IFTYPE]) {
328                 iface_info->type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]);
329         }
330         if (tb_msg[NL80211_ATTR_WIPHY]) {
331                 iface_info->phyidx = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
332         }
333
334         if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
335                 iface_info->pub->current_freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
336                 iface_info->pub->current_chan_type = NL80211_CHAN_NO_HT;
337
338                 if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
339                         iface_info->pub->current_chan_type =  nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
340
341         }
342         return NL_SKIP;
343 }
344
345
346 static int __ws80211_get_iface_info(const char *name, struct __iface_info *iface_info)
347 {
348         int devidx;
349         struct nl_msg *msg;
350         struct nl_cb *cb;
351         msg = nlmsg_alloc();
352         if (!msg) {
353                 fprintf(stderr, "failed to allocate netlink message\n");
354                 return 2;
355         }
356
357         cb = nl_cb_alloc(NL_CB_DEFAULT);
358
359         devidx = if_nametoindex(name);
360
361         genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0,
362                     0, NL80211_CMD_GET_INTERFACE, 0);
363         NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
364
365         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_info_handler, iface_info);
366
367         if (nl80211_do_cmd(msg, cb))
368                 return -1;
369
370         /* Old kernels cant get the current freq via netlink. Try WEXT too :( */
371         if (iface_info->pub->current_freq == -1)
372                 iface_info->pub->current_freq = get_freq_wext(name);
373         return 0;
374
375 nla_put_failure:
376         fprintf(stderr, "building message failed\n");
377         return -1;
378 }
379
380 int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info)
381 {
382         struct __iface_info __iface_info;
383
384         memset(iface_info, 0, sizeof(*iface_info));
385         __iface_info.pub = iface_info;
386         __iface_info.type = -1;
387         __iface_info.phyidx= -1;
388         __iface_info.pub->current_freq = -1;
389         __iface_info.pub->current_chan_type = -1;
390
391         return __ws80211_get_iface_info(name, &__iface_info);
392 }
393
394 static int ws80211_populate_devices(GArray *interfaces)
395 {
396         FILE *fh;
397         char line[200];
398         char *t;
399         gchar *t2;
400         char *ret;
401         int i;
402         unsigned int j;
403
404         struct ws80211_iface_info pub;
405         struct __iface_info iface_info;
406         struct ws80211_interface *iface;
407
408         /* Get a list of phy's that can handle monitor mode */
409         ws80211_get_phys(interfaces);
410
411         fh = g_fopen("/proc/net/dev", "r");
412         if(!fh) {
413                 fprintf(stderr, "Cannot open /proc/net/dev");
414                 return -ENOENT;
415         }
416
417         /* Skip the first two lines */
418         for (i = 0; i < 2; i++) {
419                 ret = fgets(line, sizeof(line), fh);
420                 if (ret == NULL) {
421                         fprintf(stderr, "Error parsing /proc/net/dev");
422                         fclose(fh);
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 */