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 <sys/queue.h>
32 #include <network/libnetwork.h>
33 #include <network/logging.h>
34 #include <network/phy.h>
35 #include "libnetwork-private.h"
37 struct network_phy_channel
{
39 unsigned int frequency
;
41 double max_tx_power
; // dBm
43 TAILQ_ENTRY(network_phy_channel
) channels
;
47 struct network_ctx
* ctx
;
53 TAILQ_HEAD(head
, network_phy_channel
) channels
;
55 ssize_t max_mpdu_length
;
56 unsigned int vht_caps
;
60 static int phy_get_index(const char* name
) {
64 snprintf(path
, sizeof(path
), "/sys/class/ieee80211/%s/index", name
);
66 FILE* f
= fopen(path
, "r");
70 int p
= fread(index
, 1, sizeof(index
), f
);
83 static void phy_parse_vht_capabilities(struct network_phy
* phy
, __u32 caps
) {
87 phy
->max_mpdu_length
= 3895;
91 phy
->max_mpdu_length
= 7991;
95 phy
->max_mpdu_length
= 11454;
99 phy
->max_mpdu_length
= -1;
102 // Supported channel widths
103 switch ((caps
>> 2) & 0x3) {
109 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT160
;
112 // Supports 160 MHz and 80+80 MHz
114 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT160
;
115 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT80PLUS80
;
121 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_LDPC
;
123 // RX Short GI 80 MHz
125 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
;
127 // RX Short GI 160 MHz and 80+80 MHz
129 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
;
133 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TX_STBC
;
135 // Single User Beamformer
137 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
;
139 // Single User Beamformee
141 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
;
143 // Multi User Beamformer
145 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
;
147 // Multi User Beamformee
149 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
;
153 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TXOP_PS
;
157 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_HTC_VHT
;
159 // RX Antenna Pattern Consistency
161 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
;
163 // TX Antenna Pattern Consistency
165 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
;
168 static void phy_parse_ht_capabilities(struct network_phy
* phy
, __u16 caps
) {
171 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_LDPC
;
175 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_HT40
;
177 // Static/Dynamic SM Power Save
178 switch ((caps
>> 2) & 0x3) {
180 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_SMPS_STATIC
;
184 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
;
193 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_GF
;
197 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_HT20_SGI
;
201 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_HT40_SGI
;
205 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_TX_STBC
;
208 switch ((caps
>> 8) & 0x3) {
210 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC1
;
214 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC2
;
218 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC3
;
225 // HT Delayed Block ACK
227 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_DELAYED_BA
;
229 // Max AMSDU length 7935
231 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
;
235 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
;
237 // Bit 13 is reserved
241 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_HT40_INTOLERANT
;
243 // L-SIG TXOP protection
245 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
;
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
},
257 static unsigned int phy_frequency_to_channel(unsigned int freq
) {
261 else if (freq
< 2484)
262 return (freq
- 2407) / 5;
264 else if (freq
>= 4910 && freq
<= 4980)
265 return (freq
- 4000) / 5;
267 else if (freq
<= 45000)
268 return (freq
- 5000) / 5;
270 else if (freq
>= 58320 && freq
<= 64800)
271 return (freq
- 56160) / 2160;
276 static int phy_parse_channels(struct network_phy
* phy
, struct nlattr
* nl_freqs
) {
277 struct nlattr
* nl_freq
;
280 struct nlattr
* tb_freq
[NL80211_FREQUENCY_ATTR_MAX
+ 1];
281 nla_for_each_nested(nl_freq
, nl_freqs
, rem_freq
) {
283 nla_parse(tb_freq
, NL80211_FREQUENCY_ATTR_MAX
, nla_data(nl_freq
),
284 nla_len(nl_freq
), phy_frequency_policy
);
286 if (!tb_freq
[NL80211_FREQUENCY_ATTR_FREQ
])
289 // Skip any disabled channels
290 if (tb_freq
[NL80211_FREQUENCY_ATTR_DISABLED
])
293 struct network_phy_channel
* channel
= calloc(1, sizeof(*channel
));
297 // Append object to list of channels
298 TAILQ_INSERT_TAIL(&phy
->channels
, channel
, channels
);
301 channel
->frequency
= nla_get_u32(tb_freq
[NL80211_FREQUENCY_ATTR_FREQ
]);
303 // Convert frequency to channel
304 channel
->number
= phy_frequency_to_channel(channel
->frequency
);
307 if (tb_freq
[NL80211_FREQUENCY_ATTR_RADAR
])
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;
319 static int phy_parse_info(struct nl_msg
* msg
, void* data
) {
320 struct network_phy
* phy
= data
;
322 struct nlattr
* attrs
[NL80211_ATTR_MAX
+ 1];
323 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
325 nla_parse(attrs
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
326 genlmsg_attrlen(gnlh
, 0), NULL
);
328 if (attrs
[NL80211_ATTR_WIPHY_BANDS
]) {
329 struct nlattr
* nl_band
;
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
);
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
);
344 if (band_attrs
[NL80211_BAND_ATTR_VHT_CAPA
]) {
345 __u32 vht_caps
= nla_get_u32(band_attrs
[NL80211_BAND_ATTR_VHT_CAPA
]);
347 phy_parse_vht_capabilities(phy
, vht_caps
);
351 if (band_attrs
[NL80211_BAND_ATTR_FREQS
]) {
352 phy_parse_channels(phy
, band_attrs
[NL80211_BAND_ATTR_FREQS
]);
360 static int phy_get_info(struct network_phy
* phy
) {
361 DEBUG(phy
->ctx
, "Getting information for %s\n", phy
->name
);
363 struct nl_msg
* msg
= network_phy_make_netlink_message(phy
, NL80211_CMD_GET_WIPHY
, 0);
367 int r
= network_send_netlink_message(phy
->ctx
, msg
, phy_parse_info
, phy
);
369 // This is fine since some devices are not supported by NL80211
371 DEBUG(phy
->ctx
, "Could not fetch information from kernel\n");
378 static void network_phy_free(struct network_phy
* phy
) {
379 DEBUG(phy
->ctx
, "Releasing phy at %p\n", phy
);
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
);
391 network_unref(phy
->ctx
);
395 NETWORK_EXPORT
int network_phy_new(struct network_ctx
* ctx
, struct network_phy
** phy
,
400 int index
= phy_get_index(name
);
404 struct network_phy
* p
= calloc(1, sizeof(*p
));
409 p
->ctx
= network_ref(ctx
);
412 p
->name
= strdup(name
);
415 TAILQ_INIT(&p
->channels
);
417 // Load information from kernel
418 int r
= phy_get_info(p
);
420 ERROR(p
->ctx
, "Error getting PHY information from kernel\n");
426 DEBUG(p
->ctx
, "Allocated phy at %p (index = %d)\n", p
, p
->index
);
431 NETWORK_EXPORT
struct network_phy
* network_phy_ref(struct network_phy
* phy
) {
439 NETWORK_EXPORT
struct network_phy
* network_phy_unref(struct network_phy
* phy
) {
443 if (--phy
->refcount
> 0)
446 network_phy_free(phy
);
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
);
457 NLA_PUT_U32(msg
, NL80211_ATTR_WIPHY
, phy
->index
);
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
;
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
;
475 static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap
) {
477 case NETWORK_PHY_VHT_CAP_VHT160
:
480 case NETWORK_PHY_VHT_CAP_VHT80PLUS80
:
481 return "[VHT-160-80PLUS80]";
483 case NETWORK_PHY_VHT_CAP_RX_LDPC
:
486 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
:
487 return "[SHORT-GI-80]";
489 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
:
490 return "[SHORT-GI-160]";
492 case NETWORK_PHY_VHT_CAP_TX_STBC
:
493 return "[TX-STBC-2BY1]";
495 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
:
496 return "[SU-BEAMFORMER]";
498 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
:
499 return "[SU-BEAMFORMEE]";
501 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
:
502 return "[MU-BEAMFORMER]";
504 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
:
505 return "[MU-BEAMFORMEE]";
507 case NETWORK_PHY_VHT_CAP_TXOP_PS
:
508 return "[VHT-TXOP-PS]";
510 case NETWORK_PHY_VHT_CAP_HTC_VHT
:
513 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
:
514 return "[RX-ANTENNA-PATTERN]";
516 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
:
517 return "[TX-ANTENNA-PATTERN]";
523 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap
) {
525 case NETWORK_PHY_HT_CAP_RX_LDPC
:
528 case NETWORK_PHY_HT_CAP_HT40
:
529 return "[HT40+][HT40-]";
531 case NETWORK_PHY_HT_CAP_SMPS_STATIC
:
532 return "[SMPS-STATIC]";
534 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
:
535 return "[SMPS-DYNAMIC]";
537 case NETWORK_PHY_HT_CAP_RX_GF
:
540 case NETWORK_PHY_HT_CAP_RX_HT20_SGI
:
541 return "[SHORT-GI-20]";
543 case NETWORK_PHY_HT_CAP_RX_HT40_SGI
:
544 return "[SHORT-GI-40]";
546 case NETWORK_PHY_HT_CAP_TX_STBC
:
549 case NETWORK_PHY_HT_CAP_RX_STBC1
:
552 case NETWORK_PHY_HT_CAP_RX_STBC2
:
553 return "[RX-STBC12]";
555 case NETWORK_PHY_HT_CAP_RX_STBC3
:
556 return "[RX-STBC123]";
558 case NETWORK_PHY_HT_CAP_DELAYED_BA
:
559 return "[DELAYED-BA]";
561 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
:
562 return "[MAX-AMSDU-7935]";
564 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
:
565 return "[DSSS_CCK-40]";
567 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT
:
568 return "[40-INTOLERANT]";
570 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
:
571 return "[LSIG-TXOP-PROT]";
577 NETWORK_EXPORT
char* network_phy_list_vht_capabilities(struct network_phy
* phy
) {
578 char* buffer
= malloc(1024);
583 switch (phy
->max_mpdu_length
) {
586 snprintf(p
, 1024 - 1, "[MAX-MPDU-%zu]", phy
->max_mpdu_length
);
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
);
596 p
= strncat(p
, cap_str
, 1024 - 1);
603 NETWORK_EXPORT
char* network_phy_list_ht_capabilities(struct network_phy
* phy
) {
604 char* buffer
= malloc(1024);
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
);
613 p
= strncat(p
, cap_str
, 1024 - 1);
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
);
624 struct network_phy_channel
* channel
;
625 TAILQ_FOREACH(channel
, &phy
->channels
, channels
) {
626 p
+= sprintf(p
, "%-4u %-5u %-3s %-4.1f\n",
629 (channel
->dfs
) ? "Y" : "N",
630 channel
->max_tx_power
634 return strdup(string
);