From 394cff5309adcc9134f2cd85de91da71858dd082 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sun, 23 Sep 2018 02:44:37 +0200 Subject: [PATCH] libnetwork: Add function to check if a PHY supports a specific channel Signed-off-by: Michael Tremer --- Makefile.am | 7 ++ src/functions/functions.phy | 17 ++++ src/libnetwork/libnetwork.sym | 1 + src/libnetwork/network/phy.h | 1 + src/libnetwork/phy.c | 115 ++++++++++++++++++++++++++ src/utils/.gitignore | 1 + src/utils/network-phy-list-channels.c | 61 ++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 src/utils/network-phy-list-channels.c diff --git a/Makefile.am b/Makefile.am index 05877438..7903fd54 100644 --- a/Makefile.am +++ b/Makefile.am @@ -298,9 +298,16 @@ EXTRA_DIST += \ # ------------------------------------------------------------------------------ util_PROGRAMS = \ + src/utils/network-phy-list-channels \ src/utils/network-phy-list-ht-caps \ src/utils/network-phy-list-vht-caps +src_utils_network_phy_list_channels_SOURCES = \ + src/utils/network-phy-list-channels.c + +src_utils_network_phy_list_channels_LDADD = \ + src/libnetwork.la + src_utils_network_phy_list_ht_caps_SOURCES = \ src/utils/network-phy-list-ht-caps.c diff --git a/src/functions/functions.phy b/src/functions/functions.phy index a4067d4e..76974fa8 100644 --- a/src/functions/functions.phy +++ b/src/functions/functions.phy @@ -152,3 +152,20 @@ phy_leds_autoconf() { return ${EXIT_OK} } + +phy_supports_channel() { + local phy="${1}" + assert isset phy + + local channel="${2}" + assert isinteger channel + + local _channel _frequency _dfs _max_tx_power + while read -r _channel _frequency _dfs _max_tx_power; do + if [ "${channel}" = "${_channel}" ]; then + return ${EXIT_TRUE} + fi + done <<< "$(network-phy-list-channels "${phy}")" + + return ${EXIT_FALSE} +} diff --git a/src/libnetwork/libnetwork.sym b/src/libnetwork/libnetwork.sym index 3ae81e90..593c4a2b 100644 --- a/src/libnetwork/libnetwork.sym +++ b/src/libnetwork/libnetwork.sym @@ -7,6 +7,7 @@ global: network_new; network_phy_has_ht_capability; network_phy_has_vht_capability; + network_phy_list_channels; network_phy_list_ht_capabilities; network_phy_list_vht_capabilities; network_phy_new; diff --git a/src/libnetwork/network/phy.h b/src/libnetwork/network/phy.h index 9f3326bc..90596804 100644 --- a/src/libnetwork/network/phy.h +++ b/src/libnetwork/network/phy.h @@ -67,6 +67,7 @@ enum network_phy_vht_caps { }; +char* network_phy_list_channels(struct network_phy* phy); int network_phy_has_vht_capability(struct network_phy* phy, const enum network_phy_vht_caps cap); char* network_phy_list_vht_capabilities(struct network_phy* phy); int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap); diff --git a/src/libnetwork/phy.c b/src/libnetwork/phy.c index b11ce9b9..0bf9c81b 100644 --- a/src/libnetwork/phy.c +++ b/src/libnetwork/phy.c @@ -23,15 +23,26 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include "libnetwork-private.h" +struct network_phy_channel { + unsigned int number; + unsigned int frequency; + bool dfs; + double max_tx_power; // dBm + + TAILQ_ENTRY(network_phy_channel) channels; +}; + struct network_phy { struct network_ctx* ctx; int refcount; @@ -39,6 +50,8 @@ struct network_phy { int index; char* name; + TAILQ_HEAD(head, network_phy_channel) channels; + ssize_t max_mpdu_length; unsigned int vht_caps; unsigned int ht_caps; @@ -232,6 +245,77 @@ static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) { phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT; } +static struct nla_policy phy_frequency_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, + [__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, +}; + +static unsigned int phy_frequency_to_channel(unsigned int freq) { + if (freq == 2484) + return 14; + + else if (freq < 2484) + return (freq - 2407) / 5; + + else if (freq >= 4910 && freq <= 4980) + return (freq - 4000) / 5; + + else if (freq <= 45000) + return (freq - 5000) / 5; + + else if (freq >= 58320 && freq <= 64800) + return (freq - 56160) / 2160; + + return 0; +}; + +static int phy_parse_channels(struct network_phy* phy, struct nlattr* nl_freqs) { + struct nlattr* nl_freq; + int rem_freq; + + struct nlattr* tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + nla_for_each_nested(nl_freq, nl_freqs, rem_freq) { + // Get data + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), phy_frequency_policy); + + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + // Skip any disabled channels + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + continue; + + struct network_phy_channel* channel = calloc(1, sizeof(*channel)); + if (!channel) + return -1; + + // Append object to list of channels + TAILQ_INSERT_TAIL(&phy->channels, channel, channels); + + // Get frequency + channel->frequency = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + + // Convert frequency to channel + channel->number = phy_frequency_to_channel(channel->frequency); + + // Radar detection + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + channel->dfs = true; + + // Maximum TX power + if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) + channel->max_tx_power = \ + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) * 0.01; + } + + return 0; +} + static int phy_parse_info(struct nl_msg* msg, void* data) { struct network_phy* phy = data; @@ -262,6 +346,11 @@ static int phy_parse_info(struct nl_msg* msg, void* data) { phy_parse_vht_capabilities(phy, vht_caps); } + + // Frequencies + if (band_attrs[NL80211_BAND_ATTR_FREQS]) { + phy_parse_channels(phy, band_attrs[NL80211_BAND_ATTR_FREQS]); + } } } @@ -289,6 +378,13 @@ static int phy_get_info(struct network_phy* phy) { static void network_phy_free(struct network_phy* phy) { DEBUG(phy->ctx, "Releasing phy at %p\n", phy); + // Destroy all channels + while (!TAILQ_EMPTY(&phy->channels)) { + struct network_phy_channel* channel = TAILQ_FIRST(&phy->channels); + TAILQ_REMOVE(&phy->channels, channel, channels); + free(channel); + } + if (phy->name) free(phy->name); @@ -316,6 +412,8 @@ NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy** p->name = strdup(name); p->index = index; + TAILQ_INIT(&p->channels); + // Load information from kernel int r = phy_get_info(p); if (r) { @@ -518,3 +616,20 @@ NETWORK_EXPORT char* network_phy_list_ht_capabilities(struct network_phy* phy) { return buffer; } + +NETWORK_EXPORT char* network_phy_list_channels(struct network_phy* phy) { + char string[10240] = "CHAN FREQ DFS TXPWR\n"; + char* p = string + strlen(string); + + struct network_phy_channel* channel; + TAILQ_FOREACH(channel, &phy->channels, channels) { + p += sprintf(p, "%-4u %-5u %-3s %-4.1f\n", + channel->number, + channel->frequency, + (channel->dfs) ? "Y" : "N", + channel->max_tx_power + ); + } + + return strdup(string); +} diff --git a/src/utils/.gitignore b/src/utils/.gitignore index 8ae00328..11cf3b69 100644 --- a/src/utils/.gitignore +++ b/src/utils/.gitignore @@ -1,2 +1,3 @@ +/network-phy-list-channels /network-phy-list-ht-caps /network-phy-list-vht-caps diff --git a/src/utils/network-phy-list-channels.c b/src/utils/network-phy-list-channels.c new file mode 100644 index 00000000..26165f60 --- /dev/null +++ b/src/utils/network-phy-list-channels.c @@ -0,0 +1,61 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2018 IPFire Network Development Team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include +#include + +#include +#include +#include + +int main(int argc, char** argv) { + struct network_ctx* ctx = NULL; + struct network_phy* phy = NULL; + int r; + + if (argc < 2) { + fprintf(stderr, "No enough arguments\n"); + r = 2; + goto END; + } + + // Initialise context + r = network_new(&ctx); + if (r) + return r; + + r = network_phy_new(ctx, &phy, argv[1]); + if (r) { + fprintf(stderr, "Could not find %s\n", argv[1]); + goto END; + } + + // Print all supported HT capabilities + char* channels = network_phy_list_channels(phy); + if (channels && *channels) { + printf("%s", channels); + free(channels); + } + +END: + network_phy_unref(phy); + network_unref(ctx); + return r; +} -- 2.47.2