1 /*#############################################################################
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2018 IPFire Network Development Team #
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. #
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. #
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/>. #
19 #############################################################################*/
22 #include <linux/nl80211.h>
23 #include <netlink/attr.h>
24 #include <netlink/genl/genl.h>
25 #include <netlink/msg.h>
30 #include <network/libnetwork.h>
31 #include <network/logging.h>
32 #include <network/phy.h>
33 #include "libnetwork-private.h"
36 struct network_ctx
* ctx
;
42 ssize_t max_mpdu_length
;
43 unsigned int vht_caps
;
47 static int phy_get_index(const char* name
) {
51 snprintf(path
, sizeof(path
), "/sys/class/ieee80211/%s/index", name
);
53 FILE* f
= fopen(path
, "r");
57 int p
= fread(index
, 1, sizeof(index
), f
);
70 static void phy_parse_vht_capabilities(struct network_phy
* phy
, __u32 caps
) {
74 phy
->max_mpdu_length
= 3895;
78 phy
->max_mpdu_length
= 7991;
82 phy
->max_mpdu_length
= 11454;
86 phy
->max_mpdu_length
= -1;
89 // Supported channel widths
90 switch ((caps
>> 2) & 0x3) {
96 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT160
;
99 // Supports 160 MHz and 80+80 MHz
101 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT160
;
102 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT80PLUS80
;
108 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_LDPC
;
110 // RX Short GI 80 MHz
112 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
;
114 // RX Short GI 160 MHz and 80+80 MHz
116 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
;
120 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TX_STBC
;
122 // Single User Beamformer
124 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
;
126 // Single User Beamformee
128 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
;
130 // Multi User Beamformer
132 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
;
134 // Multi User Beamformee
136 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
;
140 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TXOP_PS
;
144 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_HTC_VHT
;
146 // RX Antenna Pattern Consistency
148 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
;
150 // TX Antenna Pattern Consistency
152 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
;
155 static void phy_parse_ht_capabilities(struct network_phy
* phy
, __u16 caps
) {
158 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_LDPC
;
162 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_HT40
;
164 // Static/Dynamic SM Power Save
165 switch ((caps
>> 2) & 0x3) {
167 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_SMPS_STATIC
;
171 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
;
180 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_GF
;
184 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_HT20_SGI
;
188 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_HT40_SGI
;
192 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_TX_STBC
;
195 switch ((caps
>> 8) & 0x3) {
197 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC1
;
201 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC2
;
205 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC3
;
212 // HT Delayed Block ACK
214 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_DELAYED_BA
;
216 // Max AMSDU length 7935
218 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
;
222 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
;
224 // Bit 13 is reserved
228 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_HT40_INTOLERANT
;
230 // L-SIG TXOP protection
232 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
;
235 static int phy_parse_info(struct nl_msg
* msg
, void* data
) {
236 struct network_phy
* phy
= data
;
238 struct nlattr
* attrs
[NL80211_ATTR_MAX
+ 1];
239 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
241 nla_parse(attrs
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
242 genlmsg_attrlen(gnlh
, 0), NULL
);
244 if (attrs
[NL80211_ATTR_WIPHY_BANDS
]) {
245 struct nlattr
* nl_band
;
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
);
254 if (band_attrs
[NL80211_BAND_ATTR_HT_CAPA
]) {
255 __u16 ht_caps
= nla_get_u16(band_attrs
[NL80211_BAND_ATTR_HT_CAPA
]);
256 phy_parse_ht_capabilities(phy
, ht_caps
);
260 if (band_attrs
[NL80211_BAND_ATTR_VHT_CAPA
]) {
261 __u32 vht_caps
= nla_get_u32(band_attrs
[NL80211_BAND_ATTR_VHT_CAPA
]);
263 phy_parse_vht_capabilities(phy
, vht_caps
);
271 static int phy_get_info(struct network_phy
* phy
) {
272 DEBUG(phy
->ctx
, "Getting information for %s\n", phy
->name
);
274 struct nl_msg
* msg
= network_phy_make_netlink_message(phy
, NL80211_CMD_GET_WIPHY
, 0);
278 int r
= network_send_netlink_message(phy
->ctx
, msg
, phy_parse_info
, phy
);
280 // This is fine since some devices are not supported by NL80211
282 DEBUG(phy
->ctx
, "Could not fetch information from kernel\n");
289 static void network_phy_free(struct network_phy
* phy
) {
290 DEBUG(phy
->ctx
, "Releasing phy at %p\n", phy
);
295 network_unref(phy
->ctx
);
299 NETWORK_EXPORT
int network_phy_new(struct network_ctx
* ctx
, struct network_phy
** phy
,
304 int index
= phy_get_index(name
);
308 struct network_phy
* p
= calloc(1, sizeof(*p
));
313 p
->ctx
= network_ref(ctx
);
316 p
->name
= strdup(name
);
319 // Load information from kernel
320 int r
= phy_get_info(p
);
322 ERROR(p
->ctx
, "Error getting PHY information from kernel\n");
328 DEBUG(p
->ctx
, "Allocated phy at %p (index = %d)\n", p
, p
->index
);
333 NETWORK_EXPORT
struct network_phy
* network_phy_ref(struct network_phy
* phy
) {
341 NETWORK_EXPORT
struct network_phy
* network_phy_unref(struct network_phy
* phy
) {
345 if (--phy
->refcount
> 0)
348 network_phy_free(phy
);
352 struct nl_msg
* network_phy_make_netlink_message(struct network_phy
* phy
,
353 enum nl80211_commands cmd
, int flags
) {
354 struct nl_msg
* msg
= network_make_netlink_message(phy
->ctx
, cmd
, flags
);
359 NLA_PUT_U32(msg
, NL80211_ATTR_WIPHY
, phy
->index
);
369 NETWORK_EXPORT
int network_phy_has_vht_capability(struct network_phy
* phy
, const enum network_phy_vht_caps cap
) {
370 return phy
->vht_caps
& cap
;
373 NETWORK_EXPORT
int network_phy_has_ht_capability(struct network_phy
* phy
, const enum network_phy_ht_caps cap
) {
374 return phy
->ht_caps
& cap
;
377 static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap
) {
379 case NETWORK_PHY_VHT_CAP_VHT160
:
382 case NETWORK_PHY_VHT_CAP_VHT80PLUS80
:
383 return "[VHT-160-80PLUS80]";
385 case NETWORK_PHY_VHT_CAP_RX_LDPC
:
388 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
:
389 return "[SHORT-GI-80]";
391 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
:
392 return "[SHORT-GI-160]";
394 case NETWORK_PHY_VHT_CAP_TX_STBC
:
395 return "[TX-STBC-2BY1]";
397 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
:
398 return "[SU-BEAMFORMER]";
400 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
:
401 return "[SU-BEAMFORMEE]";
403 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
:
404 return "[MU-BEAMFORMER]";
406 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
:
407 return "[MU-BEAMFORMEE]";
409 case NETWORK_PHY_VHT_CAP_TXOP_PS
:
410 return "[VHT-TXOP-PS]";
412 case NETWORK_PHY_VHT_CAP_HTC_VHT
:
415 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
:
416 return "[RX-ANTENNA-PATTERN]";
418 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
:
419 return "[TX-ANTENNA-PATTERN]";
425 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap
) {
427 case NETWORK_PHY_HT_CAP_RX_LDPC
:
430 case NETWORK_PHY_HT_CAP_HT40
:
431 return "[HT40+][HT40-]";
433 case NETWORK_PHY_HT_CAP_SMPS_STATIC
:
434 return "[SMPS-STATIC]";
436 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
:
437 return "[SMPS-DYNAMIC]";
439 case NETWORK_PHY_HT_CAP_RX_GF
:
442 case NETWORK_PHY_HT_CAP_RX_HT20_SGI
:
443 return "[SHORT-GI-20]";
445 case NETWORK_PHY_HT_CAP_RX_HT40_SGI
:
446 return "[SHORT-GI-40]";
448 case NETWORK_PHY_HT_CAP_TX_STBC
:
451 case NETWORK_PHY_HT_CAP_RX_STBC1
:
454 case NETWORK_PHY_HT_CAP_RX_STBC2
:
455 return "[RX-STBC12]";
457 case NETWORK_PHY_HT_CAP_RX_STBC3
:
458 return "[RX-STBC123]";
460 case NETWORK_PHY_HT_CAP_DELAYED_BA
:
461 return "[DELAYED-BA]";
463 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
:
464 return "[MAX-AMSDU-7935]";
466 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
:
467 return "[DSSS_CCK-40]";
469 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT
:
470 return "[40-INTOLERANT]";
472 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
:
473 return "[LSIG-TXOP-PROT]";
479 NETWORK_EXPORT
char* network_phy_list_vht_capabilities(struct network_phy
* phy
) {
480 char* buffer
= malloc(1024);
485 switch (phy
->max_mpdu_length
) {
488 snprintf(p
, 1024 - 1, "[MAX-MPDU-%zu]", phy
->max_mpdu_length
);
493 foreach_vht_cap(cap
) {
494 if (network_phy_has_vht_capability(phy
, cap
)) {
495 const char* cap_str
= network_phy_get_vht_capability_string(cap
);
498 p
= strncat(p
, cap_str
, 1024 - 1);
505 NETWORK_EXPORT
char* network_phy_list_ht_capabilities(struct network_phy
* phy
) {
506 char* buffer
= malloc(1024);
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
);
515 p
= strncat(p
, cap_str
, 1024 - 1);