]> git.ipfire.org Git - people/ms/network.git/blame - src/libnetwork/phy.c
ibnetwork: Add command to show available VHT capabilities of phys
[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>
26#include <stdint.h>
9cb2f0c0
MT
27#include <stdio.h>
28#include <stdlib.h>
29
30#include <network/libnetwork.h>
31#include <network/logging.h>
e145b2f3 32#include <network/phy.h>
9cb2f0c0
MT
33#include "libnetwork-private.h"
34
35struct network_phy {
36 struct network_ctx* ctx;
37 int refcount;
38
39 int index;
40 char* name;
e145b2f3 41
b2323e58
MT
42 ssize_t max_mpdu_length;
43 unsigned int vht_caps;
e145b2f3 44 unsigned int ht_caps;
9cb2f0c0
MT
45};
46
47static int phy_get_index(const char* name) {
48 char path[1024];
49 char index[8];
50
51 snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/index", name);
52
53 FILE* f = fopen(path, "r");
54 if (!f)
55 return -1;
56
57 int p = fread(index, 1, sizeof(index), f);
58 if (p < 0) {
59 fclose(f);
60 return -1;
61 }
62
63 // Terminate buffer
64 index[p] = '\0';
65 fclose(f);
66
67 return atoi(index);
68}
69
b2323e58
MT
70static void phy_parse_vht_capabilities(struct network_phy* phy, __u32 caps) {
71 // Max MPDU length
72 switch (caps & 0x3) {
73 case 0:
74 phy->max_mpdu_length = 3895;
75 break;
76
77 case 1:
78 phy->max_mpdu_length = 7991;
79 break;
80
81 case 2:
82 phy->max_mpdu_length = 11454;
83 break;
84
85 case 3:
86 phy->max_mpdu_length = -1;
87 }
88
89 // Supported channel widths
90 switch ((caps >> 2) & 0x3) {
91 case 0:
92 break;
93
94 // Supports 160 MHz
95 case 1:
96 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT160;
97 break;
98
99 // Supports 160 MHz and 80+80 MHz
100 case 2:
101 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT160;
102 phy->vht_caps |= NETWORK_PHY_VHT_CAP_VHT80PLUS80;
103 break;
104 }
105
106 // RX LDPC
107 if (caps & BIT(4))
108 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_LDPC;
109
110 // RX Short GI 80 MHz
111 if (caps & BIT(5))
112 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80;
113
114 // RX Short GI 160 MHz and 80+80 MHz
115 if (caps & BIT(6))
116 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160;
117
118 // TX STBC
119 if (caps & BIT(7))
120 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TX_STBC;
121
122 // Single User Beamformer
123 if (caps & BIT(11))
124 phy->vht_caps |= NETWORK_PHY_VHT_CAP_SU_BEAMFORMER;
125
126 // Single User Beamformee
127 if (caps & BIT(12))
128 phy->vht_caps |= NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE;
129
130 // Multi User Beamformer
131 if (caps & BIT(19))
132 phy->vht_caps |= NETWORK_PHY_VHT_CAP_MU_BEAMFORMER;
133
134 // Multi User Beamformee
135 if (caps & BIT(20))
136 phy->vht_caps |= NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE;
137
138 // TX-OP-PS
139 if (caps & BIT(21))
140 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TXOP_PS;
141
142 // HTC-VHT
143 if (caps & BIT(22))
144 phy->vht_caps |= NETWORK_PHY_VHT_CAP_HTC_VHT;
145
146 // RX Antenna Pattern Consistency
147 if (caps & BIT(28))
148 phy->vht_caps |= NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN;
149
150 // TX Antenna Pattern Consistency
151 if (caps & BIT(29))
152 phy->vht_caps |= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN;
153}
154
e145b2f3
MT
155static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
156 // RX LDCP
157 if (caps & BIT(0))
158 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDCP;
159
160 // HT40
161 if (caps & BIT(1))
162 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40;
163
164 // Static/Dynamic SM Power Save
165 switch ((caps >> 2) & 0x3) {
166 case 0:
167 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_STATIC;
168 break;
169
170 case 1:
171 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC;
172 break;
173
174 default:
175 break;
176 }
177
178 // RX Greenfield
179 if (caps & BIT(4))
180 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_GF;
181
182 // RX HT20 Short GI
183 if (caps & BIT(5))
184 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT20_SGI;
185
186 // RX HT40 Short GI
187 if (caps & BIT(6))
188 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT40_SGI;
189
190 // TX STBC
191 if (caps & BIT(7))
192 phy->ht_caps |= NETWORK_PHY_HT_CAP_TX_STBC;
193
194 // RX STBC
195 switch ((caps >> 8) & 0x3) {
196 case 1:
197 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC1;
198 break;
199
200 case 2:
201 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC2;
202 break;
203
204 case 3:
205 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC3;
206 break;
207
208 default:
209 break;
210 }
211
212 // HT Delayed Block ACK
213 if (caps & BIT(10))
214 phy->ht_caps |= NETWORK_PHY_HT_CAP_DELAYED_BA;
215
216 // Max AMSDU length 7935
217 if (caps & BIT(11))
218 phy->ht_caps |= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935;
219
220 // DSSS/CCK HT40
221 if (caps & BIT(12))
222 phy->ht_caps |= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40;
223
224 // Bit 13 is reserved
225
226 // HT40 Intolerant
227 if (caps & BIT(14))
228 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40_INTOLERANT;
229
230 // L-SIG TXOP protection
231 if (caps & BIT(15))
232 phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT;
233}
234
235static int phy_parse_info(struct nl_msg* msg, void* data) {
236 struct network_phy* phy = data;
237
238 struct nlattr* attrs[NL80211_ATTR_MAX + 1];
239 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
240
241 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
242 genlmsg_attrlen(gnlh, 0), NULL);
243
244 if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
245 struct nlattr* nl_band;
246 int i;
247
248 nla_for_each_nested(nl_band, attrs[NL80211_ATTR_WIPHY_BANDS], i) {
249 struct nlattr* band_attrs[NL80211_BAND_ATTR_MAX + 1];
250 nla_parse(band_attrs, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
251 nla_len(nl_band), NULL);
252
253 // HT Capabilities
254 if (band_attrs[NL80211_BAND_ATTR_HT_CAPA]) {
b2323e58
MT
255 __u16 ht_caps = nla_get_u16(band_attrs[NL80211_BAND_ATTR_HT_CAPA]);
256 phy_parse_ht_capabilities(phy, ht_caps);
257 }
258
259 // VHT Capabilities
260 if (band_attrs[NL80211_BAND_ATTR_VHT_CAPA]) {
261 __u32 vht_caps = nla_get_u32(band_attrs[NL80211_BAND_ATTR_VHT_CAPA]);
262
263 phy_parse_vht_capabilities(phy, vht_caps);
e145b2f3
MT
264 }
265 }
266 }
267
268 return NL_OK;
269}
270
271static int phy_get_info(struct network_phy* phy) {
272 DEBUG(phy->ctx, "Getting information for %s\n", phy->name);
273
274 struct nl_msg* msg = network_phy_make_netlink_message(phy, NL80211_CMD_GET_WIPHY, 0);
275 if (!msg)
276 return -1;
277
984a1852
MT
278 int r = network_send_netlink_message(phy->ctx, msg, phy_parse_info, phy);
279
280 // This is fine since some devices are not supported by NL80211
281 if (r == -ENODEV) {
282 DEBUG(phy->ctx, "Could not fetch information from kernel\n");
283 return 0;
284 }
285
286 return r;
e145b2f3
MT
287}
288
289static void network_phy_free(struct network_phy* phy) {
290 DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
291
292 if (phy->name)
293 free(phy->name);
294
295 network_unref(phy->ctx);
296 free(phy);
297}
298
9cb2f0c0
MT
299NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy** phy,
300 const char* name) {
301 if (!name)
302 return -EINVAL;
303
304 int index = phy_get_index(name);
305 if (index < 0)
306 return -ENODEV;
307
308 struct network_phy* p = calloc(1, sizeof(*p));
309 if (!p)
310 return -ENOMEM;
311
312 // Initalise object
313 p->ctx = network_ref(ctx);
314 p->refcount = 1;
315
e145b2f3
MT
316 p->name = strdup(name);
317
318 // Load information from kernel
319 int r = phy_get_info(p);
320 if (r) {
321 ERROR(p->ctx, "Error getting PHY information from kernel\n");
322 network_phy_free(p);
323
324 return r;
325 }
326
9cb2f0c0
MT
327 DEBUG(p->ctx, "Allocated phy at %p\n", p);
328 *phy = p;
329 return 0;
330}
331
332NETWORK_EXPORT struct network_phy* network_phy_ref(struct network_phy* phy) {
333 if (!phy)
334 return NULL;
335
336 phy->refcount++;
337 return phy;
338}
339
9cb2f0c0
MT
340NETWORK_EXPORT struct network_phy* network_phy_unref(struct network_phy* phy) {
341 if (!phy)
342 return NULL;
343
344 if (--phy->refcount > 0)
345 return phy;
346
347 network_phy_free(phy);
348 return NULL;
349}
e145b2f3
MT
350
351struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
352 enum nl80211_commands cmd, int flags) {
353 struct nl_msg* msg = network_make_netlink_message(phy->ctx, cmd, flags);
354 if (!msg)
355 return NULL;
356
357 // Set PHY index
358 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy->index);
359
360 return msg;
361
362nla_put_failure:
363 nlmsg_free(msg);
364
365 return NULL;
366}
367
b2323e58
MT
368NETWORK_EXPORT int network_phy_has_vht_capability(struct network_phy* phy, const enum network_phy_vht_caps cap) {
369 return phy->vht_caps & cap;
370}
371
e145b2f3
MT
372NETWORK_EXPORT int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap) {
373 return phy->ht_caps & cap;
374}
375
b2323e58
MT
376static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap) {
377 switch (cap) {
378 case NETWORK_PHY_VHT_CAP_VHT160:
379 return "[VHT-160]";
380
381 case NETWORK_PHY_VHT_CAP_VHT80PLUS80:
382 return "[VHT-160-80PLUS80]";
383
384 case NETWORK_PHY_VHT_CAP_RX_LDPC:
385 return "[RXLDPC]";
386
387 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80:
388 return "[SHORT-GI-80]";
389
390 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160:
391 return "[SHORT-GI-160]";
392
393 case NETWORK_PHY_VHT_CAP_TX_STBC:
394 return "[TX-STBC-2BY1]";
395
396 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER:
397 return "[SU-BEAMFORMER]";
398
399 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE:
400 return "[SU-BEAMFORMEE]";
401
402 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER:
403 return "[MU-BEAMFORMER]";
404
405 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE:
406 return "[MU-BEAMFORMEE]";
407
408 case NETWORK_PHY_VHT_CAP_TXOP_PS:
409 return "[VHT-TXOP-PS]";
410
411 case NETWORK_PHY_VHT_CAP_HTC_VHT:
412 return "[HTC-VHT]";
413
414 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN:
415 return "[RX-ANTENNA-PATTERN]";
416
417 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN:
418 return "[TX-ANTENNA-PATTERN]";
419 }
420
421 return NULL;
422}
423
e145b2f3
MT
424static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap) {
425 switch (cap) {
426 case NETWORK_PHY_HT_CAP_RX_LDCP:
427 return "[LDPC]";
428
429 case NETWORK_PHY_HT_CAP_HT40:
430 return "[HT40+][HT40-]";
431
432 case NETWORK_PHY_HT_CAP_SMPS_STATIC:
433 return "[SMPS-STATIC]";
434
435 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC:
436 return "[SMPS-DYNAMIC]";
437
438 case NETWORK_PHY_HT_CAP_RX_GF:
439 return "[GF]";
440
441 case NETWORK_PHY_HT_CAP_RX_HT20_SGI:
442 return "[SHORT-GI-20]";
443
444 case NETWORK_PHY_HT_CAP_RX_HT40_SGI:
445 return "[SHORT-GI-40]";
446
447 case NETWORK_PHY_HT_CAP_TX_STBC:
448 return "[TX-STBC]";
449
450 case NETWORK_PHY_HT_CAP_RX_STBC1:
451 return "[RX-STBC1]";
452
453 case NETWORK_PHY_HT_CAP_RX_STBC2:
454 return "[RX-STBC12]";
455
456 case NETWORK_PHY_HT_CAP_RX_STBC3:
457 return "[RX-STBC123]";
458
459 case NETWORK_PHY_HT_CAP_DELAYED_BA:
460 return "[DELAYED-BA]";
461
462 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935:
463 return "[MAX-AMSDU-7935]";
464
465 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40:
466 return "[DSSS_CCK-40]";
467
468 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT:
469 return "[40-INTOLERANT]";
470
471 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT:
472 return "[LSIG-TXOP-PROT]";
473 }
474
475 return NULL;
476}
477
b2323e58
MT
478NETWORK_EXPORT char* network_phy_list_vht_capabilities(struct network_phy* phy) {
479 char* buffer = malloc(1024);
480 *buffer = '\0';
481
482 char* p = buffer;
483
484 switch (phy->max_mpdu_length) {
485 case 7991:
486 case 11454:
487 snprintf(p, 1024 - 1, "[MAX-MPDU-%zu]", phy->max_mpdu_length);
488 break;
489
490 }
491
492 foreach_vht_cap(cap) {
493 printf("%d\n", cap);
494 if (network_phy_has_vht_capability(phy, cap)) {
495 const char* cap_str = network_phy_get_vht_capability_string(cap);
496
497 if (cap_str)
498 p = strncat(p, cap_str, 1024 - 1);
499 }
500 }
501
502 return buffer;
503}
504
e145b2f3
MT
505NETWORK_EXPORT char* network_phy_list_ht_capabilities(struct network_phy* phy) {
506 char* buffer = malloc(1024);
507 *buffer = '\0';
508
509 char* p = buffer;
510 foreach_ht_cap(cap) {
511 if (network_phy_has_ht_capability(phy, cap)) {
512 const char* cap_str = network_phy_get_ht_capability_string(cap);
513
514 if (cap_str)
515 p = strncat(p, cap_str, 1024 - 1);
516 }
517 }
518
519 return buffer;
520}