]> git.ipfire.org Git - people/ms/network.git/blame - src/libnetwork/phy.c
wireless-ap: Automatically enable all supported ciphers
[people/ms/network.git] / src / libnetwork / phy.c
CommitLineData
9cb2f0c0
MT
1/*#############################################################################
2# #
3# IPFire.org - A linux based firewall #
4# Copyright (C) 2018 IPFire Network Development Team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
e145b2f3
MT
22#include <linux/nl80211.h>
23#include <netlink/attr.h>
24#include <netlink/genl/genl.h>
25#include <netlink/msg.h>
394cff53 26#include <stdbool.h>
e145b2f3 27#include <stdint.h>
9cb2f0c0
MT
28#include <stdio.h>
29#include <stdlib.h>
394cff53 30#include <sys/queue.h>
9cb2f0c0
MT
31
32#include <network/libnetwork.h>
33#include <network/logging.h>
e145b2f3 34#include <network/phy.h>
9cb2f0c0
MT
35#include "libnetwork-private.h"
36
394cff53
MT
37struct network_phy_channel {
38 unsigned int number;
39 unsigned int frequency;
40 bool dfs;
41 double max_tx_power; // dBm
42
43 TAILQ_ENTRY(network_phy_channel) channels;
44};
45
9cb2f0c0
MT
46struct network_phy {
47 struct network_ctx* ctx;
48 int refcount;
49
50 int index;
51 char* name;
e145b2f3 52
394cff53
MT
53 TAILQ_HEAD(head, network_phy_channel) channels;
54
2e4e3c88 55 enum network_phy_ciphers ciphers;
b2323e58
MT
56 ssize_t max_mpdu_length;
57 unsigned int vht_caps;
e145b2f3 58 unsigned int ht_caps;
9cb2f0c0
MT
59};
60
61static int phy_get_index(const char* name) {
62 char path[1024];
63 char index[8];
64
65 snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/index", name);
66
67 FILE* f = fopen(path, "r");
68 if (!f)
69 return -1;
70
71 int p = fread(index, 1, sizeof(index), f);
72 if (p < 0) {
73 fclose(f);
74 return -1;
75 }
76
77 // Terminate buffer
78 index[p] = '\0';
79 fclose(f);
80
81 return atoi(index);
82}
83
2e4e3c88
MT
84static void phy_parse_ciphers(struct network_phy* phy, __u32* ciphers, int num) {
85 enum network_phy_ciphers cipher;
86
87 // Reset value
88 phy->ciphers = 0;
89
90 for (int i = 0; i < num; i++) {
91 switch (ciphers[i]) {
92 case 0x000fac01:
93 cipher = NETWORK_PHY_CIPHER_WEP40;
94 break;
95
96 case 0x000fac02:
97 cipher = NETWORK_PHY_CIPHER_TKIP;
98 break;
99
100 case 0x000fac04:
101 cipher = NETWORK_PHY_CIPHER_CCMP128;
102 break;
103
104 case 0x000fac05:
105 cipher = NETWORK_PHY_CIPHER_WEP104;
106 break;
107
108 case 0x000fac06:
109 cipher = NETWORK_PHY_CIPHER_CMAC128;
110 break;
111
112 case 0x000fac08:
113 cipher = NETWORK_PHY_CIPHER_GCMP128;
114 break;
115
116 case 0x000fac09:
117 cipher = NETWORK_PHY_CIPHER_GCMP256;
118 break;
119
120 /*
121 I have no idea what these are. My card reports them but
122 I could not find out anything about them.
123 */
124 case 0x000fac0a:
125 case 0x000fac0b:
126 case 0x000fac0c:
127 case 0x000fac0d:
128 continue;
129
130 case 0x000fac10:
131 cipher = NETWORK_PHY_CIPHER_CCMP256;
132 break;
133
134 case 0x000fac11:
135 cipher = NETWORK_PHY_CIPHER_GMAC128;
136 break;
137
138 case 0x000fac12:
139 cipher = NETWORK_PHY_CIPHER_GMAC256;
140 break;
141
142 case 0x000fac13:
143 cipher = NETWORK_PHY_CIPHER_CMAC256;
144 break;
145
146 case 0x00147201:
147 cipher = NETWORK_PHY_CIPHER_WPISMS4;
148 break;
149
150 default:
151 ERROR(phy->ctx, "Unknown cipher found: %x\n", ciphers[i]);
152 continue;
153 }
154
155 phy->ciphers |= cipher;
156 }
157}
158
b2323e58
MT
159static void phy_parse_vht_capabilities(struct network_phy* phy, __u32 caps) {
160 // Max MPDU length
161 switch (caps & 0x3) {
162 case 0:
163 phy->max_mpdu_length = 3895;
164 break;
165
166 case 1:
167 phy->max_mpdu_length = 7991;
168 break;
169
170 case 2:
171 phy->max_mpdu_length = 11454;
172 break;
173
174 case 3:
175 phy->max_mpdu_length = -1;
176 }
177
178 // Supported channel widths
179 switch ((caps >> 2) & 0x3) {
180 case 0:
181 break;
182
183 // Supports 160 MHz
184 case 1:
185 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT160;
186 break;
187
188 // Supports 160 MHz and 80+80 MHz
189 case 2:
190 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT160;
191 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT80PLUS80;
192 break;
193 }
194
195 // RX LDPC
196 if (caps & BIT(4))
197 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_LDPC;
198
199 // RX Short GI 80 MHz
200 if (caps & BIT(5))
201 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80;
202
203 // RX Short GI 160 MHz and 80+80 MHz
204 if (caps & BIT(6))
205 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160;
206
207 // TX STBC
208 if (caps & BIT(7))
209 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TX_STBC;
210
211 // Single User Beamformer
212 if (caps & BIT(11))
213 phy->vht_caps |= NETWORK_PHY_VHT_CAP_SU_BEAMFORMER;
214
215 // Single User Beamformee
216 if (caps & BIT(12))
217 phy->vht_caps |= NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE;
218
219 // Multi User Beamformer
220 if (caps & BIT(19))
221 phy->vht_caps |= NETWORK_PHY_VHT_CAP_MU_BEAMFORMER;
222
223 // Multi User Beamformee
224 if (caps & BIT(20))
225 phy->vht_caps |= NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE;
226
227 // TX-OP-PS
228 if (caps & BIT(21))
229 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TXOP_PS;
230
231 // HTC-VHT
232 if (caps & BIT(22))
233 phy->vht_caps |= NETWORK_PHY_VHT_CAP_HTC_VHT;
234
235 // RX Antenna Pattern Consistency
236 if (caps & BIT(28))
237 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN;
238
239 // TX Antenna Pattern Consistency
240 if (caps & BIT(29))
241 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN;
242}
243
e145b2f3 244static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
a4a2f65d 245 // RX LDPC
e145b2f3 246 if (caps & BIT(0))
a4a2f65d 247 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDPC;
e145b2f3
MT
248
249 // HT40
250 if (caps & BIT(1))
251 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40;
252
253 // Static/Dynamic SM Power Save
254 switch ((caps >> 2) & 0x3) {
255 case 0:
256 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_STATIC;
257 break;
258
259 case 1:
260 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC;
261 break;
262
263 default:
264 break;
265 }
266
267 // RX Greenfield
268 if (caps & BIT(4))
269 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_GF;
270
271 // RX HT20 Short GI
272 if (caps & BIT(5))
273 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT20_SGI;
274
275 // RX HT40 Short GI
276 if (caps & BIT(6))
277 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT40_SGI;
278
279 // TX STBC
280 if (caps & BIT(7))
281 phy->ht_caps |= NETWORK_PHY_HT_CAP_TX_STBC;
282
283 // RX STBC
284 switch ((caps >> 8) & 0x3) {
285 case 1:
286 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC1;
287 break;
288
289 case 2:
290 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC2;
291 break;
292
293 case 3:
294 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC3;
295 break;
296
297 default:
298 break;
299 }
300
301 // HT Delayed Block ACK
302 if (caps & BIT(10))
303 phy->ht_caps |= NETWORK_PHY_HT_CAP_DELAYED_BA;
304
305 // Max AMSDU length 7935
306 if (caps & BIT(11))
307 phy->ht_caps |= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935;
308
309 // DSSS/CCK HT40
310 if (caps & BIT(12))
311 phy->ht_caps |= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40;
312
313 // Bit 13 is reserved
314
315 // HT40 Intolerant
316 if (caps & BIT(14))
317 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40_INTOLERANT;
318
319 // L-SIG TXOP protection
320 if (caps & BIT(15))
321 phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT;
322}
323
394cff53
MT
324static struct nla_policy phy_frequency_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
325 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
326 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
327 [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
328 [__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
329 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
330 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
331};
332
333static unsigned int phy_frequency_to_channel(unsigned int freq) {
334 if (freq == 2484)
335 return 14;
336
337 else if (freq < 2484)
338 return (freq - 2407) / 5;
339
340 else if (freq >= 4910 && freq <= 4980)
341 return (freq - 4000) / 5;
342
343 else if (freq <= 45000)
344 return (freq - 5000) / 5;
345
346 else if (freq >= 58320 && freq <= 64800)
347 return (freq - 56160) / 2160;
348
349 return 0;
350};
351
352static int phy_parse_channels(struct network_phy* phy, struct nlattr* nl_freqs) {
353 struct nlattr* nl_freq;
354 int rem_freq;
355
356 struct nlattr* tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
357 nla_for_each_nested(nl_freq, nl_freqs, rem_freq) {
358 // Get data
359 nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
360 nla_len(nl_freq), phy_frequency_policy);
361
362 if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
363 continue;
364
365 // Skip any disabled channels
366 if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
367 continue;
368
369 struct network_phy_channel* channel = calloc(1, sizeof(*channel));
370 if (!channel)
371 return -1;
372
373 // Append object to list of channels
374 TAILQ_INSERT_TAIL(&phy->channels, channel, channels);
375
376 // Get frequency
377 channel->frequency = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
378
379 // Convert frequency to channel
380 channel->number = phy_frequency_to_channel(channel->frequency);
381
382 // Radar detection
383 if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
384 channel->dfs = true;
385
386 // Maximum TX power
387 if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
388 channel->max_tx_power = \
389 nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) * 0.01;
390 }
391
392 return 0;
393}
394
e145b2f3
MT
395static int phy_parse_info(struct nl_msg* msg, void* data) {
396 struct network_phy* phy = data;
397
398 struct nlattr* attrs[NL80211_ATTR_MAX + 1];
399 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
400
401 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
402 genlmsg_attrlen(gnlh, 0), NULL);
403
2e4e3c88
MT
404 // Ciphers
405 if (attrs[NL80211_ATTR_CIPHER_SUITES]) {
406 int num = nla_len(attrs[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32);
407 __u32* ciphers = nla_data(attrs[NL80211_ATTR_CIPHER_SUITES]);
408 phy_parse_ciphers(phy, ciphers, num);
409 }
410
e145b2f3
MT
411 if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
412 struct nlattr* nl_band;
413 int i;
414
415 nla_for_each_nested(nl_band, attrs[NL80211_ATTR_WIPHY_BANDS], i) {
416 struct nlattr* band_attrs[NL80211_BAND_ATTR_MAX + 1];
417 nla_parse(band_attrs, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
418 nla_len(nl_band), NULL);
419
420 // HT Capabilities
421 if (band_attrs[NL80211_BAND_ATTR_HT_CAPA]) {
b2323e58
MT
422 __u16 ht_caps = nla_get_u16(band_attrs[NL80211_BAND_ATTR_HT_CAPA]);
423 phy_parse_ht_capabilities(phy, ht_caps);
424 }
425
426 // VHT Capabilities
427 if (band_attrs[NL80211_BAND_ATTR_VHT_CAPA]) {
428 __u32 vht_caps = nla_get_u32(band_attrs[NL80211_BAND_ATTR_VHT_CAPA]);
429
430 phy_parse_vht_capabilities(phy, vht_caps);
e145b2f3 431 }
394cff53
MT
432
433 // Frequencies
434 if (band_attrs[NL80211_BAND_ATTR_FREQS]) {
435 phy_parse_channels(phy, band_attrs[NL80211_BAND_ATTR_FREQS]);
436 }
e145b2f3
MT
437 }
438 }
439
440 return NL_OK;
441}
442
443static int phy_get_info(struct network_phy* phy) {
444 DEBUG(phy->ctx, "Getting information for %s\n", phy->name);
445
446 struct nl_msg* msg = network_phy_make_netlink_message(phy, NL80211_CMD_GET_WIPHY, 0);
447 if (!msg)
448 return -1;
449
984a1852
MT
450 int r = network_send_netlink_message(phy->ctx, msg, phy_parse_info, phy);
451
452 // This is fine since some devices are not supported by NL80211
453 if (r == -ENODEV) {
454 DEBUG(phy->ctx, "Could not fetch information from kernel\n");
455 return 0;
456 }
457
458 return r;
e145b2f3
MT
459}
460
461static void network_phy_free(struct network_phy* phy) {
462 DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
463
394cff53
MT
464 // Destroy all channels
465 while (!TAILQ_EMPTY(&phy->channels)) {
466 struct network_phy_channel* channel = TAILQ_FIRST(&phy->channels);
467 TAILQ_REMOVE(&phy->channels, channel, channels);
468 free(channel);
469 }
470
e145b2f3
MT
471 if (phy->name)
472 free(phy->name);
473
474 network_unref(phy->ctx);
475 free(phy);
476}
477
9cb2f0c0
MT
478NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy** phy,
479 const char* name) {
480 if (!name)
481 return -EINVAL;
482
483 int index = phy_get_index(name);
484 if (index < 0)
485 return -ENODEV;
486
487 struct network_phy* p = calloc(1, sizeof(*p));
488 if (!p)
489 return -ENOMEM;
490
491 // Initalise object
492 p->ctx = network_ref(ctx);
493 p->refcount = 1;
494
e145b2f3 495 p->name = strdup(name);
1ffc4679 496 p->index = index;
e145b2f3 497
394cff53
MT
498 TAILQ_INIT(&p->channels);
499
e145b2f3
MT
500 // Load information from kernel
501 int r = phy_get_info(p);
502 if (r) {
503 ERROR(p->ctx, "Error getting PHY information from kernel\n");
504 network_phy_free(p);
505
506 return r;
507 }
508
1ffc4679 509 DEBUG(p->ctx, "Allocated phy at %p (index = %d)\n", p, p->index);
9cb2f0c0
MT
510 *phy = p;
511 return 0;
512}
513
514NETWORK_EXPORT struct network_phy* network_phy_ref(struct network_phy* phy) {
515 if (!phy)
516 return NULL;
517
518 phy->refcount++;
519 return phy;
520}
521
9cb2f0c0
MT
522NETWORK_EXPORT struct network_phy* network_phy_unref(struct network_phy* phy) {
523 if (!phy)
524 return NULL;
525
526 if (--phy->refcount > 0)
527 return phy;
528
529 network_phy_free(phy);
530 return NULL;
531}
e145b2f3
MT
532
533struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
534 enum nl80211_commands cmd, int flags) {
535 struct nl_msg* msg = network_make_netlink_message(phy->ctx, cmd, flags);
536 if (!msg)
537 return NULL;
538
539 // Set PHY index
540 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy->index);
541
542 return msg;
543
544nla_put_failure:
545 nlmsg_free(msg);
546
547 return NULL;
548}
549
2e4e3c88
MT
550NETWORK_EXPORT const char* network_phy_get_cipher_string(const enum network_phy_ciphers cipher) {
551 switch (cipher) {
552 case NETWORK_PHY_CIPHER_WEP40:
553 return "WEP40";
554
555 case NETWORK_PHY_CIPHER_TKIP:
556 return "TKIP";
557
558 case NETWORK_PHY_CIPHER_CCMP128:
559 return "CCMP-128";
560
561 case NETWORK_PHY_CIPHER_WEP104:
562 return "WEP-104";
563
564 case NETWORK_PHY_CIPHER_CMAC128:
565 return "CMAC-128";
566
567 case NETWORK_PHY_CIPHER_GCMP128:
568 return "GCMP-128";
569
570 case NETWORK_PHY_CIPHER_GCMP256:
571 return "GCMP-256";
572
573 case NETWORK_PHY_CIPHER_CCMP256:
574 return "CCMP-256";
575
576 case NETWORK_PHY_CIPHER_GMAC128:
577 return "GMAC-128";
578
579 case NETWORK_PHY_CIPHER_GMAC256:
580 return "GMAC-256";
581
582 case NETWORK_PHY_CIPHER_CMAC256:
583 return "CMAC-256";
584
585 case NETWORK_PHY_CIPHER_WPISMS4:
586 return "WPI-SMS4";
587 }
588
589 return NULL;
590}
591
592NETWORK_EXPORT int network_phy_supports_cipher(struct network_phy* phy, const enum network_phy_ciphers cipher) {
593 return phy->ciphers & cipher;
594}
595
596NETWORK_EXPORT char* network_phy_list_ciphers(struct network_phy* phy) {
597 char* buffer = NULL;
598
599 foreach_cipher(cipher) {
600 if (network_phy_supports_cipher(phy, cipher)) {
601 const char* s = network_phy_get_cipher_string(cipher);
602
603 if (!s)
604 continue;
605
606 if (buffer)
607 asprintf(&buffer, "%s %s", buffer, s);
608 else
609 asprintf(&buffer, "%s", s);
610 }
611 }
612
613 return buffer;
614}
615
b2323e58
MT
616NETWORK_EXPORT int network_phy_has_vht_capability(struct network_phy* phy, const enum network_phy_vht_caps cap) {
617 return phy->vht_caps & cap;
618}
619
e145b2f3
MT
620NETWORK_EXPORT int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap) {
621 return phy->ht_caps & cap;
622}
623
b2323e58
MT
624static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap) {
625 switch (cap) {
626 case NETWORK_PHY_VHT_CAP_VHT160:
627 return "[VHT-160]";
628
629 case NETWORK_PHY_VHT_CAP_VHT80PLUS80:
630 return "[VHT-160-80PLUS80]";
631
632 case NETWORK_PHY_VHT_CAP_RX_LDPC:
633 return "[RXLDPC]";
634
635 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80:
636 return "[SHORT-GI-80]";
637
638 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160:
639 return "[SHORT-GI-160]";
640
641 case NETWORK_PHY_VHT_CAP_TX_STBC:
642 return "[TX-STBC-2BY1]";
643
644 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER:
645 return "[SU-BEAMFORMER]";
646
647 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE:
648 return "[SU-BEAMFORMEE]";
649
650 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER:
651 return "[MU-BEAMFORMER]";
652
653 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE:
654 return "[MU-BEAMFORMEE]";
655
656 case NETWORK_PHY_VHT_CAP_TXOP_PS:
657 return "[VHT-TXOP-PS]";
658
659 case NETWORK_PHY_VHT_CAP_HTC_VHT:
660 return "[HTC-VHT]";
661
662 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN:
663 return "[RX-ANTENNA-PATTERN]";
664
665 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN:
666 return "[TX-ANTENNA-PATTERN]";
667 }
668
669 return NULL;
670}
671
e145b2f3
MT
672static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap) {
673 switch (cap) {
a4a2f65d 674 case NETWORK_PHY_HT_CAP_RX_LDPC:
e145b2f3
MT
675 return "[LDPC]";
676
677 case NETWORK_PHY_HT_CAP_HT40:
678 return "[HT40+][HT40-]";
679
680 case NETWORK_PHY_HT_CAP_SMPS_STATIC:
681 return "[SMPS-STATIC]";
682
683 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC:
684 return "[SMPS-DYNAMIC]";
685
686 case NETWORK_PHY_HT_CAP_RX_GF:
687 return "[GF]";
688
689 case NETWORK_PHY_HT_CAP_RX_HT20_SGI:
690 return "[SHORT-GI-20]";
691
692 case NETWORK_PHY_HT_CAP_RX_HT40_SGI:
693 return "[SHORT-GI-40]";
694
695 case NETWORK_PHY_HT_CAP_TX_STBC:
696 return "[TX-STBC]";
697
698 case NETWORK_PHY_HT_CAP_RX_STBC1:
699 return "[RX-STBC1]";
700
701 case NETWORK_PHY_HT_CAP_RX_STBC2:
702 return "[RX-STBC12]";
703
704 case NETWORK_PHY_HT_CAP_RX_STBC3:
705 return "[RX-STBC123]";
706
707 case NETWORK_PHY_HT_CAP_DELAYED_BA:
708 return "[DELAYED-BA]";
709
710 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935:
711 return "[MAX-AMSDU-7935]";
712
713 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40:
714 return "[DSSS_CCK-40]";
715
716 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT:
717 return "[40-INTOLERANT]";
718
719 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT:
720 return "[LSIG-TXOP-PROT]";
721 }
722
723 return NULL;
724}
725
b2323e58
MT
726NETWORK_EXPORT char* network_phy_list_vht_capabilities(struct network_phy* phy) {
727 char* buffer = malloc(1024);
728 *buffer = '\0';
729
730 char* p = buffer;
731
732 switch (phy->max_mpdu_length) {
733 case 7991:
734 case 11454:
735 snprintf(p, 1024 - 1, "[MAX-MPDU-%zu]", phy->max_mpdu_length);
736 break;
737
738 }
739
740 foreach_vht_cap(cap) {
b2323e58
MT
741 if (network_phy_has_vht_capability(phy, cap)) {
742 const char* cap_str = network_phy_get_vht_capability_string(cap);
743
744 if (cap_str)
745 p = strncat(p, cap_str, 1024 - 1);
746 }
747 }
748
749 return buffer;
750}
751
e145b2f3
MT
752NETWORK_EXPORT char* network_phy_list_ht_capabilities(struct network_phy* phy) {
753 char* buffer = malloc(1024);
754 *buffer = '\0';
755
756 char* p = buffer;
757 foreach_ht_cap(cap) {
758 if (network_phy_has_ht_capability(phy, cap)) {
759 const char* cap_str = network_phy_get_ht_capability_string(cap);
760
761 if (cap_str)
762 p = strncat(p, cap_str, 1024 - 1);
763 }
764 }
765
766 return buffer;
767}
394cff53
MT
768
769NETWORK_EXPORT char* network_phy_list_channels(struct network_phy* phy) {
770 char string[10240] = "CHAN FREQ DFS TXPWR\n";
771 char* p = string + strlen(string);
772
773 struct network_phy_channel* channel;
774 TAILQ_FOREACH(channel, &phy->channels, channels) {
775 p += sprintf(p, "%-4u %-5u %-3s %-4.1f\n",
776 channel->number,
777 channel->frequency,
778 (channel->dfs) ? "Y" : "N",
779 channel->max_tx_power
780 );
781 }
782
783 return strdup(string);
784}