]> git.ipfire.org Git - people/ms/network.git/blob - src/libnetwork/phy.c
wireless-ap: Automatically enable all supported ciphers
[people/ms/network.git] / src / libnetwork / phy.c
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>
22 #include <linux/nl80211.h>
23 #include <netlink/attr.h>
24 #include <netlink/genl/genl.h>
25 #include <netlink/msg.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/queue.h>
31
32 #include <network/libnetwork.h>
33 #include <network/logging.h>
34 #include <network/phy.h>
35 #include "libnetwork-private.h"
36
37 struct 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
46 struct network_phy {
47 struct network_ctx* ctx;
48 int refcount;
49
50 int index;
51 char* name;
52
53 TAILQ_HEAD(head, network_phy_channel) channels;
54
55 enum network_phy_ciphers ciphers;
56 ssize_t max_mpdu_length;
57 unsigned int vht_caps;
58 unsigned int ht_caps;
59 };
60
61 static 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
84 static 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
159 static 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
244 static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
245 // RX LDPC
246 if (caps & BIT(0))
247 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDPC;
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
324 static 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
333 static 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
352 static 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
395 static 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
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
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]) {
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);
431 }
432
433 // Frequencies
434 if (band_attrs[NL80211_BAND_ATTR_FREQS]) {
435 phy_parse_channels(phy, band_attrs[NL80211_BAND_ATTR_FREQS]);
436 }
437 }
438 }
439
440 return NL_OK;
441 }
442
443 static 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
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;
459 }
460
461 static void network_phy_free(struct network_phy* phy) {
462 DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
463
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
471 if (phy->name)
472 free(phy->name);
473
474 network_unref(phy->ctx);
475 free(phy);
476 }
477
478 NETWORK_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
495 p->name = strdup(name);
496 p->index = index;
497
498 TAILQ_INIT(&p->channels);
499
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
509 DEBUG(p->ctx, "Allocated phy at %p (index = %d)\n", p, p->index);
510 *phy = p;
511 return 0;
512 }
513
514 NETWORK_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
522 NETWORK_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 }
532
533 struct 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
544 nla_put_failure:
545 nlmsg_free(msg);
546
547 return NULL;
548 }
549
550 NETWORK_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
592 NETWORK_EXPORT int network_phy_supports_cipher(struct network_phy* phy, const enum network_phy_ciphers cipher) {
593 return phy->ciphers & cipher;
594 }
595
596 NETWORK_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
616 NETWORK_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
620 NETWORK_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
624 static 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
672 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap) {
673 switch (cap) {
674 case NETWORK_PHY_HT_CAP_RX_LDPC:
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
726 NETWORK_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) {
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
752 NETWORK_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 }
768
769 NETWORK_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 }