]> git.ipfire.org Git - people/ms/network.git/commitdiff
libnetwork: Add function to check if a PHY supports a specific channel
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 23 Sep 2018 00:44:37 +0000 (02:44 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 23 Sep 2018 00:44:37 +0000 (02:44 +0200)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/functions/functions.phy
src/libnetwork/libnetwork.sym
src/libnetwork/network/phy.h
src/libnetwork/phy.c
src/utils/.gitignore
src/utils/network-phy-list-channels.c [new file with mode: 0644]

index 05877438a8984c227a4d839b23535a15dfe45215..7903fd541345ea63a52efb21b79675fe8ae75943 100644 (file)
@@ -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
 
index a4067d4ebd8a9bfc252343b1c05275231fa98a31..76974fa860c46c2fc0766ad49f8436d5ea9842f9 100644 (file)
@@ -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}
+}
index 3ae81e90b99def7311a8d12927f25bfa205722c8..593c4a2b7fe9898cbe72b691d25b13268a2d1722 100644 (file)
@@ -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;
index 9f3326bcac55ae1d3c80cb6439b6232f60ea8f84..9059680447372e55fa0fe8cef51535989b7ddd9d 100644 (file)
@@ -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);
index b11ce9b9c0dd1a6ac316a0ec699bcc7264a2a04b..0bf9c81b844aab87c77ffadae0c1c834309e49bd 100644 (file)
 #include <netlink/attr.h>
 #include <netlink/genl/genl.h>
 #include <netlink/msg.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <network/libnetwork.h>
 #include <network/logging.h>
 #include <network/phy.h>
 #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);
+}
index 8ae0032857ea327bfebdb2b0ba35e7265d9d2d10..11cf3b699118f5d852951a1e7f91aa632bd9335d 100644 (file)
@@ -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 (file)
index 0000000..26165f6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <network/libnetwork.h>
+#include <network/logging.h>
+#include <network/phy.h>
+
+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;
+}