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 enum network_phy_ciphers ciphers
;
56 ssize_t max_mpdu_length
;
57 unsigned int vht_caps
;
61 static int phy_get_index(const char* name
) {
65 snprintf(path
, sizeof(path
), "/sys/class/ieee80211/%s/index", name
);
67 FILE* f
= fopen(path
, "r");
71 int p
= fread(index
, 1, sizeof(index
), f
);
84 static void phy_parse_ciphers(struct network_phy
* phy
, __u32
* ciphers
, int num
) {
85 enum network_phy_ciphers cipher
;
90 for (int i
= 0; i
< num
; i
++) {
93 cipher
= NETWORK_PHY_CIPHER_WEP40
;
97 cipher
= NETWORK_PHY_CIPHER_TKIP
;
101 cipher
= NETWORK_PHY_CIPHER_CCMP128
;
105 cipher
= NETWORK_PHY_CIPHER_WEP104
;
109 cipher
= NETWORK_PHY_CIPHER_CMAC128
;
113 cipher
= NETWORK_PHY_CIPHER_GCMP128
;
117 cipher
= NETWORK_PHY_CIPHER_GCMP256
;
121 I have no idea what these are. My card reports them but
122 I could not find out anything about them.
131 cipher
= NETWORK_PHY_CIPHER_CCMP256
;
135 cipher
= NETWORK_PHY_CIPHER_GMAC128
;
139 cipher
= NETWORK_PHY_CIPHER_GMAC256
;
143 cipher
= NETWORK_PHY_CIPHER_CMAC256
;
147 cipher
= NETWORK_PHY_CIPHER_WPISMS4
;
151 ERROR(phy
->ctx
, "Unknown cipher found: %x\n", ciphers
[i
]);
155 phy
->ciphers
|= cipher
;
159 static void phy_parse_vht_capabilities(struct network_phy
* phy
, __u32 caps
) {
161 switch (caps
& 0x3) {
163 phy
->max_mpdu_length
= 3895;
167 phy
->max_mpdu_length
= 7991;
171 phy
->max_mpdu_length
= 11454;
175 phy
->max_mpdu_length
= -1;
178 // Supported channel widths
179 switch ((caps
>> 2) & 0x3) {
185 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT160
;
188 // Supports 160 MHz and 80+80 MHz
190 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT160
;
191 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_VHT80PLUS80
;
197 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_LDPC
;
199 // RX Short GI 80 MHz
201 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
;
203 // RX Short GI 160 MHz and 80+80 MHz
205 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
;
209 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TX_STBC
;
211 // Single User Beamformer
213 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
;
215 // Single User Beamformee
217 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
;
219 // Multi User Beamformer
221 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
;
223 // Multi User Beamformee
225 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
;
229 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TXOP_PS
;
233 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_HTC_VHT
;
235 // RX Antenna Pattern Consistency
237 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
;
239 // TX Antenna Pattern Consistency
241 phy
->vht_caps
|= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
;
244 static void phy_parse_ht_capabilities(struct network_phy
* phy
, __u16 caps
) {
247 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_LDPC
;
251 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_HT40
;
253 // Static/Dynamic SM Power Save
254 switch ((caps
>> 2) & 0x3) {
256 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_SMPS_STATIC
;
260 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
;
269 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_GF
;
273 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_HT20_SGI
;
277 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_HT40_SGI
;
281 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_TX_STBC
;
284 switch ((caps
>> 8) & 0x3) {
286 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC1
;
290 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC2
;
294 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_RX_STBC3
;
301 // HT Delayed Block ACK
303 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_DELAYED_BA
;
305 // Max AMSDU length 7935
307 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
;
311 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
;
313 // Bit 13 is reserved
317 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_HT40_INTOLERANT
;
319 // L-SIG TXOP protection
321 phy
->ht_caps
|= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
;
324 static struct nla_policy phy_frequency_policy
[NL80211_FREQUENCY_ATTR_MAX
+ 1] = {
325 [NL80211_FREQUENCY_ATTR_FREQ
] = { .type
= NLA_U32
},
326 [NL80211_FREQUENCY_ATTR_DISABLED
] = { .type
= NLA_FLAG
},
327 [NL80211_FREQUENCY_ATTR_NO_IR
] = { .type
= NLA_FLAG
},
328 [__NL80211_FREQUENCY_ATTR_NO_IBSS
] = { .type
= NLA_FLAG
},
329 [NL80211_FREQUENCY_ATTR_RADAR
] = { .type
= NLA_FLAG
},
330 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER
] = { .type
= NLA_U32
},
333 static unsigned int phy_frequency_to_channel(unsigned int freq
) {
337 else if (freq
< 2484)
338 return (freq
- 2407) / 5;
340 else if (freq
>= 4910 && freq
<= 4980)
341 return (freq
- 4000) / 5;
343 else if (freq
<= 45000)
344 return (freq
- 5000) / 5;
346 else if (freq
>= 58320 && freq
<= 64800)
347 return (freq
- 56160) / 2160;
352 static int phy_parse_channels(struct network_phy
* phy
, struct nlattr
* nl_freqs
) {
353 struct nlattr
* nl_freq
;
356 struct nlattr
* tb_freq
[NL80211_FREQUENCY_ATTR_MAX
+ 1];
357 nla_for_each_nested(nl_freq
, nl_freqs
, rem_freq
) {
359 nla_parse(tb_freq
, NL80211_FREQUENCY_ATTR_MAX
, nla_data(nl_freq
),
360 nla_len(nl_freq
), phy_frequency_policy
);
362 if (!tb_freq
[NL80211_FREQUENCY_ATTR_FREQ
])
365 // Skip any disabled channels
366 if (tb_freq
[NL80211_FREQUENCY_ATTR_DISABLED
])
369 struct network_phy_channel
* channel
= calloc(1, sizeof(*channel
));
373 // Append object to list of channels
374 TAILQ_INSERT_TAIL(&phy
->channels
, channel
, channels
);
377 channel
->frequency
= nla_get_u32(tb_freq
[NL80211_FREQUENCY_ATTR_FREQ
]);
379 // Convert frequency to channel
380 channel
->number
= phy_frequency_to_channel(channel
->frequency
);
383 if (tb_freq
[NL80211_FREQUENCY_ATTR_RADAR
])
387 if (tb_freq
[NL80211_FREQUENCY_ATTR_MAX_TX_POWER
])
388 channel
->max_tx_power
= \
389 nla_get_u32(tb_freq
[NL80211_FREQUENCY_ATTR_MAX_TX_POWER
]) * 0.01;
395 static int phy_parse_info(struct nl_msg
* msg
, void* data
) {
396 struct network_phy
* phy
= data
;
398 struct nlattr
* attrs
[NL80211_ATTR_MAX
+ 1];
399 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
401 nla_parse(attrs
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
402 genlmsg_attrlen(gnlh
, 0), NULL
);
405 if (attrs
[NL80211_ATTR_CIPHER_SUITES
]) {
406 int num
= nla_len(attrs
[NL80211_ATTR_CIPHER_SUITES
]) / sizeof(__u32
);
407 __u32
* ciphers
= nla_data(attrs
[NL80211_ATTR_CIPHER_SUITES
]);
408 phy_parse_ciphers(phy
, ciphers
, num
);
411 if (attrs
[NL80211_ATTR_WIPHY_BANDS
]) {
412 struct nlattr
* nl_band
;
415 nla_for_each_nested(nl_band
, attrs
[NL80211_ATTR_WIPHY_BANDS
], i
) {
416 struct nlattr
* band_attrs
[NL80211_BAND_ATTR_MAX
+ 1];
417 nla_parse(band_attrs
, NL80211_BAND_ATTR_MAX
, nla_data(nl_band
),
418 nla_len(nl_band
), NULL
);
421 if (band_attrs
[NL80211_BAND_ATTR_HT_CAPA
]) {
422 __u16 ht_caps
= nla_get_u16(band_attrs
[NL80211_BAND_ATTR_HT_CAPA
]);
423 phy_parse_ht_capabilities(phy
, ht_caps
);
427 if (band_attrs
[NL80211_BAND_ATTR_VHT_CAPA
]) {
428 __u32 vht_caps
= nla_get_u32(band_attrs
[NL80211_BAND_ATTR_VHT_CAPA
]);
430 phy_parse_vht_capabilities(phy
, vht_caps
);
434 if (band_attrs
[NL80211_BAND_ATTR_FREQS
]) {
435 phy_parse_channels(phy
, band_attrs
[NL80211_BAND_ATTR_FREQS
]);
443 static int phy_get_info(struct network_phy
* phy
) {
444 DEBUG(phy
->ctx
, "Getting information for %s\n", phy
->name
);
446 struct nl_msg
* msg
= network_phy_make_netlink_message(phy
, NL80211_CMD_GET_WIPHY
, 0);
450 int r
= network_send_netlink_message(phy
->ctx
, msg
, phy_parse_info
, phy
);
452 // This is fine since some devices are not supported by NL80211
454 DEBUG(phy
->ctx
, "Could not fetch information from kernel\n");
461 static void network_phy_free(struct network_phy
* phy
) {
462 DEBUG(phy
->ctx
, "Releasing phy at %p\n", phy
);
464 // Destroy all channels
465 while (!TAILQ_EMPTY(&phy
->channels
)) {
466 struct network_phy_channel
* channel
= TAILQ_FIRST(&phy
->channels
);
467 TAILQ_REMOVE(&phy
->channels
, channel
, channels
);
474 network_unref(phy
->ctx
);
478 NETWORK_EXPORT
int network_phy_new(struct network_ctx
* ctx
, struct network_phy
** phy
,
483 int index
= phy_get_index(name
);
487 struct network_phy
* p
= calloc(1, sizeof(*p
));
492 p
->ctx
= network_ref(ctx
);
495 p
->name
= strdup(name
);
498 TAILQ_INIT(&p
->channels
);
500 // Load information from kernel
501 int r
= phy_get_info(p
);
503 ERROR(p
->ctx
, "Error getting PHY information from kernel\n");
509 DEBUG(p
->ctx
, "Allocated phy at %p (index = %d)\n", p
, p
->index
);
514 NETWORK_EXPORT
struct network_phy
* network_phy_ref(struct network_phy
* phy
) {
522 NETWORK_EXPORT
struct network_phy
* network_phy_unref(struct network_phy
* phy
) {
526 if (--phy
->refcount
> 0)
529 network_phy_free(phy
);
533 struct nl_msg
* network_phy_make_netlink_message(struct network_phy
* phy
,
534 enum nl80211_commands cmd
, int flags
) {
535 struct nl_msg
* msg
= network_make_netlink_message(phy
->ctx
, cmd
, flags
);
540 NLA_PUT_U32(msg
, NL80211_ATTR_WIPHY
, phy
->index
);
550 NETWORK_EXPORT
const char* network_phy_get_cipher_string(const enum network_phy_ciphers cipher
) {
552 case NETWORK_PHY_CIPHER_WEP40
:
555 case NETWORK_PHY_CIPHER_TKIP
:
558 case NETWORK_PHY_CIPHER_CCMP128
:
561 case NETWORK_PHY_CIPHER_WEP104
:
564 case NETWORK_PHY_CIPHER_CMAC128
:
567 case NETWORK_PHY_CIPHER_GCMP128
:
570 case NETWORK_PHY_CIPHER_GCMP256
:
573 case NETWORK_PHY_CIPHER_CCMP256
:
576 case NETWORK_PHY_CIPHER_GMAC128
:
579 case NETWORK_PHY_CIPHER_GMAC256
:
582 case NETWORK_PHY_CIPHER_CMAC256
:
585 case NETWORK_PHY_CIPHER_WPISMS4
:
592 NETWORK_EXPORT
int network_phy_supports_cipher(struct network_phy
* phy
, const enum network_phy_ciphers cipher
) {
593 return phy
->ciphers
& cipher
;
596 NETWORK_EXPORT
char* network_phy_list_ciphers(struct network_phy
* phy
) {
599 foreach_cipher(cipher
) {
600 if (network_phy_supports_cipher(phy
, cipher
)) {
601 const char* s
= network_phy_get_cipher_string(cipher
);
607 asprintf(&buffer
, "%s %s", buffer
, s
);
609 asprintf(&buffer
, "%s", s
);
616 NETWORK_EXPORT
int network_phy_has_vht_capability(struct network_phy
* phy
, const enum network_phy_vht_caps cap
) {
617 return phy
->vht_caps
& cap
;
620 NETWORK_EXPORT
int network_phy_has_ht_capability(struct network_phy
* phy
, const enum network_phy_ht_caps cap
) {
621 return phy
->ht_caps
& cap
;
624 static const char* network_phy_get_vht_capability_string(const enum network_phy_vht_caps cap
) {
626 case NETWORK_PHY_VHT_CAP_VHT160
:
629 case NETWORK_PHY_VHT_CAP_VHT80PLUS80
:
630 return "[VHT-160-80PLUS80]";
632 case NETWORK_PHY_VHT_CAP_RX_LDPC
:
635 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_80
:
636 return "[SHORT-GI-80]";
638 case NETWORK_PHY_VHT_CAP_RX_SHORT_GI_160
:
639 return "[SHORT-GI-160]";
641 case NETWORK_PHY_VHT_CAP_TX_STBC
:
642 return "[TX-STBC-2BY1]";
644 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMER
:
645 return "[SU-BEAMFORMER]";
647 case NETWORK_PHY_VHT_CAP_SU_BEAMFORMEE
:
648 return "[SU-BEAMFORMEE]";
650 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMER
:
651 return "[MU-BEAMFORMER]";
653 case NETWORK_PHY_VHT_CAP_MU_BEAMFORMEE
:
654 return "[MU-BEAMFORMEE]";
656 case NETWORK_PHY_VHT_CAP_TXOP_PS
:
657 return "[VHT-TXOP-PS]";
659 case NETWORK_PHY_VHT_CAP_HTC_VHT
:
662 case NETWORK_PHY_VHT_CAP_RX_ANTENNA_PATTERN
:
663 return "[RX-ANTENNA-PATTERN]";
665 case NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN
:
666 return "[TX-ANTENNA-PATTERN]";
672 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap
) {
674 case NETWORK_PHY_HT_CAP_RX_LDPC
:
677 case NETWORK_PHY_HT_CAP_HT40
:
678 return "[HT40+][HT40-]";
680 case NETWORK_PHY_HT_CAP_SMPS_STATIC
:
681 return "[SMPS-STATIC]";
683 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC
:
684 return "[SMPS-DYNAMIC]";
686 case NETWORK_PHY_HT_CAP_RX_GF
:
689 case NETWORK_PHY_HT_CAP_RX_HT20_SGI
:
690 return "[SHORT-GI-20]";
692 case NETWORK_PHY_HT_CAP_RX_HT40_SGI
:
693 return "[SHORT-GI-40]";
695 case NETWORK_PHY_HT_CAP_TX_STBC
:
698 case NETWORK_PHY_HT_CAP_RX_STBC1
:
701 case NETWORK_PHY_HT_CAP_RX_STBC2
:
702 return "[RX-STBC12]";
704 case NETWORK_PHY_HT_CAP_RX_STBC3
:
705 return "[RX-STBC123]";
707 case NETWORK_PHY_HT_CAP_DELAYED_BA
:
708 return "[DELAYED-BA]";
710 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935
:
711 return "[MAX-AMSDU-7935]";
713 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40
:
714 return "[DSSS_CCK-40]";
716 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT
:
717 return "[40-INTOLERANT]";
719 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT
:
720 return "[LSIG-TXOP-PROT]";
726 NETWORK_EXPORT
char* network_phy_list_vht_capabilities(struct network_phy
* phy
) {
727 char* buffer
= malloc(1024);
732 switch (phy
->max_mpdu_length
) {
735 snprintf(p
, 1024 - 1, "[MAX-MPDU-%zu]", phy
->max_mpdu_length
);
740 foreach_vht_cap(cap
) {
741 if (network_phy_has_vht_capability(phy
, cap
)) {
742 const char* cap_str
= network_phy_get_vht_capability_string(cap
);
745 p
= strncat(p
, cap_str
, 1024 - 1);
752 NETWORK_EXPORT
char* network_phy_list_ht_capabilities(struct network_phy
* phy
) {
753 char* buffer
= malloc(1024);
757 foreach_ht_cap(cap
) {
758 if (network_phy_has_ht_capability(phy
, cap
)) {
759 const char* cap_str
= network_phy_get_ht_capability_string(cap
);
762 p
= strncat(p
, cap_str
, 1024 - 1);
769 NETWORK_EXPORT
char* network_phy_list_channels(struct network_phy
* phy
) {
770 char string
[10240] = "CHAN FREQ DFS TXPWR\n";
771 char* p
= string
+ strlen(string
);
773 struct network_phy_channel
* channel
;
774 TAILQ_FOREACH(channel
, &phy
->channels
, channels
) {
775 p
+= sprintf(p
, "%-4u %-5u %-3s %-4.1f\n",
778 (channel
->dfs
) ? "Y" : "N",
779 channel
->max_tx_power
783 return strdup(string
);