]> git.ipfire.org Git - people/ms/network.git/blob - src/libnetwork/phy.c
dns: Always enable EDNS0
[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 ssize_t max_mpdu_length;
56 unsigned int vht_caps;
57 unsigned int ht_caps;
58 };
59
60 static int phy_get_index(const char* name) {
61 char path[1024];
62 char index[8];
63
64 snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/index", name);
65
66 FILE* f = fopen(path, "r");
67 if (!f)
68 return -1;
69
70 int p = fread(index, 1, sizeof(index), f);
71 if (p < 0) {
72 fclose(f);
73 return -1;
74 }
75
76 // Terminate buffer
77 index[p] = '\0';
78 fclose(f);
79
80 return atoi(index);
81 }
82
83 static void phy_parse_vht_capabilities(struct network_phy* phy, __u32 caps) {
84 // Max MPDU length
85 switch (caps & 0x3) {
86 case 0:
87 phy->max_mpdu_length = 3895;
88 break;
89
90 case 1:
91 phy->max_mpdu_length = 7991;
92 break;
93
94 case 2:
95 phy->max_mpdu_length = 11454;
96 break;
97
98 case 3:
99 phy->max_mpdu_length = -1;
100 }
101
102 // Supported channel widths
103 switch ((caps >> 2) & 0x3) {
104 case 0:
105 break;
106
107 // Supports 160 MHz
108 case 1:
109 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT160;
110 break;
111
112 // Supports 160 MHz and 80+80 MHz
113 case 2:
114 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT160;
115 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT80PLUS80;
116 break;
117 }
118
119 // RX LDPC
120 if (caps & BIT(4))
121 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_LDPC;
122
123 // RX Short GI 80 MHz
124 if (caps & BIT(5))
125 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80;
126
127 // RX Short GI 160 MHz and 80+80 MHz
128 if (caps & BIT(6))
129 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160;
130
131 // TX STBC
132 if (caps & BIT(7))
133 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TX_STBC;
134
135 // Single User Beamformer
136 if (caps & BIT(11))
137 phy->vht_caps |= NETWORK_PHY_VHT_CAP_SU_BEAMFORMER;
138
139 // Single User Beamformee
140 if (caps & BIT(12))
141 phy->vht_caps |= NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE;
142
143 // Multi User Beamformer
144 if (caps & BIT(19))
145 phy->vht_caps |= NETWORK_PHY_VHT_CAP_MU_BEAMFORMER;
146
147 // Multi User Beamformee
148 if (caps & BIT(20))
149 phy->vht_caps |= NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE;
150
151 // TX-OP-PS
152 if (caps & BIT(21))
153 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TXOP_PS;
154
155 // HTC-VHT
156 if (caps & BIT(22))
157 phy->vht_caps |= NETWORK_PHY_VHT_CAP_HTC_VHT;
158
159 // RX Antenna Pattern Consistency
160 if (caps & BIT(28))
161 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN;
162
163 // TX Antenna Pattern Consistency
164 if (caps & BIT(29))
165 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN;
166 }
167
168 static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
169 // RX LDPC
170 if (caps & BIT(0))
171 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDPC;
172
173 // HT40
174 if (caps & BIT(1))
175 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40;
176
177 // Static/Dynamic SM Power Save
178 switch ((caps >> 2) & 0x3) {
179 case 0:
180 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_STATIC;
181 break;
182
183 case 1:
184 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC;
185 break;
186
187 default:
188 break;
189 }
190
191 // RX Greenfield
192 if (caps & BIT(4))
193 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_GF;
194
195 // RX HT20 Short GI
196 if (caps & BIT(5))
197 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT20_SGI;
198
199 // RX HT40 Short GI
200 if (caps & BIT(6))
201 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT40_SGI;
202
203 // TX STBC
204 if (caps & BIT(7))
205 phy->ht_caps |= NETWORK_PHY_HT_CAP_TX_STBC;
206
207 // RX STBC
208 switch ((caps >> 8) & 0x3) {
209 case 1:
210 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC1;
211 break;
212
213 case 2:
214 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC2;
215 break;
216
217 case 3:
218 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC3;
219 break;
220
221 default:
222 break;
223 }
224
225 // HT Delayed Block ACK
226 if (caps & BIT(10))
227 phy->ht_caps |= NETWORK_PHY_HT_CAP_DELAYED_BA;
228
229 // Max AMSDU length 7935
230 if (caps & BIT(11))
231 phy->ht_caps |= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935;
232
233 // DSSS/CCK HT40
234 if (caps & BIT(12))
235 phy->ht_caps |= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40;
236
237 // Bit 13 is reserved
238
239 // HT40 Intolerant
240 if (caps & BIT(14))
241 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40_INTOLERANT;
242
243 // L-SIG TXOP protection
244 if (caps & BIT(15))
245 phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT;
246 }
247
248 static struct nla_policy phy_frequency_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
249 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
250 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
251 [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
252 [__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
253 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
254 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
255 };
256
257 static unsigned int phy_frequency_to_channel(unsigned int freq) {
258 if (freq == 2484)
259 return 14;
260
261 else if (freq < 2484)
262 return (freq - 2407) / 5;
263
264 else if (freq >= 4910 && freq <= 4980)
265 return (freq - 4000) / 5;
266
267 else if (freq <= 45000)
268 return (freq - 5000) / 5;
269
270 else if (freq >= 58320 && freq <= 64800)
271 return (freq - 56160) / 2160;
272
273 return 0;
274 };
275
276 static int phy_parse_channels(struct network_phy* phy, struct nlattr* nl_freqs) {
277 struct nlattr* nl_freq;
278 int rem_freq;
279
280 struct nlattr* tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
281 nla_for_each_nested(nl_freq, nl_freqs, rem_freq) {
282 // Get data
283 nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
284 nla_len(nl_freq), phy_frequency_policy);
285
286 if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
287 continue;
288
289 // Skip any disabled channels
290 if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
291 continue;
292
293 struct network_phy_channel* channel = calloc(1, sizeof(*channel));
294 if (!channel)
295 return -1;
296
297 // Append object to list of channels
298 TAILQ_INSERT_TAIL(&phy->channels, channel, channels);
299
300 // Get frequency
301 channel->frequency = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
302
303 // Convert frequency to channel
304 channel->number = phy_frequency_to_channel(channel->frequency);
305
306 // Radar detection
307 if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
308 channel->dfs = true;
309
310 // Maximum TX power
311 if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
312 channel->max_tx_power = \
313 nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) * 0.01;
314 }
315
316 return 0;
317 }
318
319 static int phy_parse_info(struct nl_msg* msg, void* data) {
320 struct network_phy* phy = data;
321
322 struct nlattr* attrs[NL80211_ATTR_MAX + 1];
323 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
324
325 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
326 genlmsg_attrlen(gnlh, 0), NULL);
327
328 if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
329 struct nlattr* nl_band;
330 int i;
331
332 nla_for_each_nested(nl_band, attrs[NL80211_ATTR_WIPHY_BANDS], i) {
333 struct nlattr* band_attrs[NL80211_BAND_ATTR_MAX + 1];
334 nla_parse(band_attrs, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
335 nla_len(nl_band), NULL);
336
337 // HT Capabilities
338 if (band_attrs[NL80211_BAND_ATTR_HT_CAPA]) {
339 __u16 ht_caps = nla_get_u16(band_attrs[NL80211_BAND_ATTR_HT_CAPA]);
340 phy_parse_ht_capabilities(phy, ht_caps);
341 }
342
343 // VHT Capabilities
344 if (band_attrs[NL80211_BAND_ATTR_VHT_CAPA]) {
345 __u32 vht_caps = nla_get_u32(band_attrs[NL80211_BAND_ATTR_VHT_CAPA]);
346
347 phy_parse_vht_capabilities(phy, vht_caps);
348 }
349
350 // Frequencies
351 if (band_attrs[NL80211_BAND_ATTR_FREQS]) {
352 phy_parse_channels(phy, band_attrs[NL80211_BAND_ATTR_FREQS]);
353 }
354 }
355 }
356
357 return NL_OK;
358 }
359
360 static int phy_get_info(struct network_phy* phy) {
361 DEBUG(phy->ctx, "Getting information for %s\n", phy->name);
362
363 struct nl_msg* msg = network_phy_make_netlink_message(phy, NL80211_CMD_GET_WIPHY, 0);
364 if (!msg)
365 return -1;
366
367 int r = network_send_netlink_message(phy->ctx, msg, phy_parse_info, phy);
368
369 // This is fine since some devices are not supported by NL80211
370 if (r == -ENODEV) {
371 DEBUG(phy->ctx, "Could not fetch information from kernel\n");
372 return 0;
373 }
374
375 return r;
376 }
377
378 static void network_phy_free(struct network_phy* phy) {
379 DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
380
381 // Destroy all channels
382 while (!TAILQ_EMPTY(&phy->channels)) {
383 struct network_phy_channel* channel = TAILQ_FIRST(&phy->channels);
384 TAILQ_REMOVE(&phy->channels, channel, channels);
385 free(channel);
386 }
387
388 if (phy->name)
389 free(phy->name);
390
391 network_unref(phy->ctx);
392 free(phy);
393 }
394
395 NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy** phy,
396 const char* name) {
397 if (!name)
398 return -EINVAL;
399
400 int index = phy_get_index(name);
401 if (index < 0)
402 return -ENODEV;
403
404 struct network_phy* p = calloc(1, sizeof(*p));
405 if (!p)
406 return -ENOMEM;
407
408 // Initalise object
409 p->ctx = network_ref(ctx);
410 p->refcount = 1;
411
412 p->name = strdup(name);
413 p->index = index;
414
415 TAILQ_INIT(&p->channels);
416
417 // Load information from kernel
418 int r = phy_get_info(p);
419 if (r) {
420 ERROR(p->ctx, "Error getting PHY information from kernel\n");
421 network_phy_free(p);
422
423 return r;
424 }
425
426 DEBUG(p->ctx, "Allocated phy at %p (index = %d)\n", p, p->index);
427 *phy = p;
428 return 0;
429 }
430
431 NETWORK_EXPORT struct network_phy* network_phy_ref(struct network_phy* phy) {
432 if (!phy)
433 return NULL;
434
435 phy->refcount++;
436 return phy;
437 }
438
439 NETWORK_EXPORT struct network_phy* network_phy_unref(struct network_phy* phy) {
440 if (!phy)
441 return NULL;
442
443 if (--phy->refcount > 0)
444 return phy;
445
446 network_phy_free(phy);
447 return NULL;
448 }
449
450 struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
451 enum nl80211_commands cmd, int flags) {
452 struct nl_msg* msg = network_make_netlink_message(phy->ctx, cmd, flags);
453 if (!msg)
454 return NULL;
455
456 // Set PHY index
457 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy->index);
458
459 return msg;
460
461 nla_put_failure:
462 nlmsg_free(msg);
463
464 return NULL;
465 }
466
467 NETWORK_EXPORT int network_phy_has_vht_capability(struct network_phy* phy, const enum network_phy_vht_caps cap) {
468 return phy->vht_caps & cap;
469 }
470
471 NETWORK_EXPORT int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap) {
472 return phy->ht_caps & cap;
473 }
474
475 static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap) {
476 switch (cap) {
477 case NETWORK_PHY_VHT_CAP_VHT160:
478 return "[VHT-160]";
479
480 case NETWORK_PHY_VHT_CAP_VHT80PLUS80:
481 return "[VHT-160-80PLUS80]";
482
483 case NETWORK_PHY_VHT_CAP_RX_LDPC:
484 return "[RXLDPC]";
485
486 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80:
487 return "[SHORT-GI-80]";
488
489 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160:
490 return "[SHORT-GI-160]";
491
492 case NETWORK_PHY_VHT_CAP_TX_STBC:
493 return "[TX-STBC-2BY1]";
494
495 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER:
496 return "[SU-BEAMFORMER]";
497
498 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE:
499 return "[SU-BEAMFORMEE]";
500
501 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER:
502 return "[MU-BEAMFORMER]";
503
504 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE:
505 return "[MU-BEAMFORMEE]";
506
507 case NETWORK_PHY_VHT_CAP_TXOP_PS:
508 return "[VHT-TXOP-PS]";
509
510 case NETWORK_PHY_VHT_CAP_HTC_VHT:
511 return "[HTC-VHT]";
512
513 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN:
514 return "[RX-ANTENNA-PATTERN]";
515
516 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN:
517 return "[TX-ANTENNA-PATTERN]";
518 }
519
520 return NULL;
521 }
522
523 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap) {
524 switch (cap) {
525 case NETWORK_PHY_HT_CAP_RX_LDPC:
526 return "[LDPC]";
527
528 case NETWORK_PHY_HT_CAP_HT40:
529 return "[HT40+][HT40-]";
530
531 case NETWORK_PHY_HT_CAP_SMPS_STATIC:
532 return "[SMPS-STATIC]";
533
534 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC:
535 return "[SMPS-DYNAMIC]";
536
537 case NETWORK_PHY_HT_CAP_RX_GF:
538 return "[GF]";
539
540 case NETWORK_PHY_HT_CAP_RX_HT20_SGI:
541 return "[SHORT-GI-20]";
542
543 case NETWORK_PHY_HT_CAP_RX_HT40_SGI:
544 return "[SHORT-GI-40]";
545
546 case NETWORK_PHY_HT_CAP_TX_STBC:
547 return "[TX-STBC]";
548
549 case NETWORK_PHY_HT_CAP_RX_STBC1:
550 return "[RX-STBC1]";
551
552 case NETWORK_PHY_HT_CAP_RX_STBC2:
553 return "[RX-STBC12]";
554
555 case NETWORK_PHY_HT_CAP_RX_STBC3:
556 return "[RX-STBC123]";
557
558 case NETWORK_PHY_HT_CAP_DELAYED_BA:
559 return "[DELAYED-BA]";
560
561 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935:
562 return "[MAX-AMSDU-7935]";
563
564 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40:
565 return "[DSSS_CCK-40]";
566
567 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT:
568 return "[40-INTOLERANT]";
569
570 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT:
571 return "[LSIG-TXOP-PROT]";
572 }
573
574 return NULL;
575 }
576
577 NETWORK_EXPORT char* network_phy_list_vht_capabilities(struct network_phy* phy) {
578 char* buffer = malloc(1024);
579 *buffer = '\0';
580
581 char* p = buffer;
582
583 switch (phy->max_mpdu_length) {
584 case 7991:
585 case 11454:
586 snprintf(p, 1024 - 1, "[MAX-MPDU-%zu]", phy->max_mpdu_length);
587 break;
588
589 }
590
591 foreach_vht_cap(cap) {
592 if (network_phy_has_vht_capability(phy, cap)) {
593 const char* cap_str = network_phy_get_vht_capability_string(cap);
594
595 if (cap_str)
596 p = strncat(p, cap_str, 1024 - 1);
597 }
598 }
599
600 return buffer;
601 }
602
603 NETWORK_EXPORT char* network_phy_list_ht_capabilities(struct network_phy* phy) {
604 char* buffer = malloc(1024);
605 *buffer = '\0';
606
607 char* p = buffer;
608 foreach_ht_cap(cap) {
609 if (network_phy_has_ht_capability(phy, cap)) {
610 const char* cap_str = network_phy_get_ht_capability_string(cap);
611
612 if (cap_str)
613 p = strncat(p, cap_str, 1024 - 1);
614 }
615 }
616
617 return buffer;
618 }
619
620 NETWORK_EXPORT char* network_phy_list_channels(struct network_phy* phy) {
621 char string[10240] = "CHAN FREQ DFS TXPWR\n";
622 char* p = string + strlen(string);
623
624 struct network_phy_channel* channel;
625 TAILQ_FOREACH(channel, &phy->channels, channels) {
626 p += sprintf(p, "%-4u %-5u %-3s %-4.1f\n",
627 channel->number,
628 channel->frequency,
629 (channel->dfs) ? "Y" : "N",
630 channel->max_tx_power
631 );
632 }
633
634 return strdup(string);
635 }