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
);
318 // Load information from kernel
319 int r
= phy_get_info(p
);
321 ERROR(p
->ctx
, "Error getting PHY information from kernel\n");
327 DEBUG(p
->ctx
, "Allocated phy at %p\n", p
);
332 NETWORK_EXPORT
struct network_phy
* network_phy_ref(struct network_phy
* phy
) {
340 NETWORK_EXPORT
struct network_phy
* network_phy_unref(struct network_phy
* phy
) {
344 if (--phy
->refcount
> 0)
347 network_phy_free(phy
);
351 struct 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
);
358 NLA_PUT_U32(msg
, NL80211_ATTR_WIPHY
, phy
->index
);
368 NETWORK_EXPORT
int network_phy_has_vht_capability(struct network_phy
* phy
, const enum network_phy_vht_caps cap
) {
369 return phy
->vht_caps
& cap
;
372 NETWORK_EXPORT
int network_phy_has_ht_capability(struct network_phy
* phy
, const enum network_phy_ht_caps cap
) {
373 return phy
->ht_caps
& cap
;
376 static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap
) {
378 case NETWORK_PHY_VHT_CAP_VHT160
:
381 case NETWORK_PHY_VHT_CAP_VHT80PLUS80
:
382 return "[VHT-160-80PLUS80]";
384 case NETWORK_PHY_VHT_CAP_RX_LDPC
:
387 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
:
388 return "[SHORT-GI-80]";
390 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
:
391 return "[SHORT-GI-160]";
393 case NETWORK_PHY_VHT_CAP_TX_STBC
:
394 return "[TX-STBC-2BY1]";
396 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
:
397 return "[SU-BEAMFORMER]";
399 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
:
400 return "[SU-BEAMFORMEE]";
402 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
:
403 return "[MU-BEAMFORMER]";
405 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
:
406 return "[MU-BEAMFORMEE]";
408 case NETWORK_PHY_VHT_CAP_TXOP_PS
:
409 return "[VHT-TXOP-PS]";
411 case NETWORK_PHY_VHT_CAP_HTC_VHT
:
414 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
:
415 return "[RX-ANTENNA-PATTERN]";
417 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
:
418 return "[TX-ANTENNA-PATTERN]";
424 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap
) {
426 case NETWORK_PHY_HT_CAP_RX_LDPC
:
429 case NETWORK_PHY_HT_CAP_HT40
:
430 return "[HT40+][HT40-]";
432 case NETWORK_PHY_HT_CAP_SMPS_STATIC
:
433 return "[SMPS-STATIC]";
435 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
:
436 return "[SMPS-DYNAMIC]";
438 case NETWORK_PHY_HT_CAP_RX_GF
:
441 case NETWORK_PHY_HT_CAP_RX_HT20_SGI
:
442 return "[SHORT-GI-20]";
444 case NETWORK_PHY_HT_CAP_RX_HT40_SGI
:
445 return "[SHORT-GI-40]";
447 case NETWORK_PHY_HT_CAP_TX_STBC
:
450 case NETWORK_PHY_HT_CAP_RX_STBC1
:
453 case NETWORK_PHY_HT_CAP_RX_STBC2
:
454 return "[RX-STBC12]";
456 case NETWORK_PHY_HT_CAP_RX_STBC3
:
457 return "[RX-STBC123]";
459 case NETWORK_PHY_HT_CAP_DELAYED_BA
:
460 return "[DELAYED-BA]";
462 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
:
463 return "[MAX-AMSDU-7935]";
465 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
:
466 return "[DSSS_CCK-40]";
468 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT
:
469 return "[40-INTOLERANT]";
471 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
:
472 return "[LSIG-TXOP-PROT]";
478 NETWORK_EXPORT
char* network_phy_list_vht_capabilities(struct network_phy
* phy
) {
479 char* buffer
= malloc(1024);
484 switch (phy
->max_mpdu_length
) {
487 snprintf(p
, 1024 - 1, "[MAX-MPDU-%zu]", phy
->max_mpdu_length
);
492 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);