]> git.ipfire.org Git - people/ms/network.git/commitdiff
libnetwork: Add command that returns supported HT caps for wireless PHYs
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 6 Feb 2018 10:47:03 +0000 (10:47 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 6 Feb 2018 11:41:12 +0000 (11:41 +0000)
Fixes #11611

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
configure.ac
src/libnetwork/libnetwork-private.h
src/libnetwork/libnetwork.c
src/libnetwork/libnetwork.sym
src/libnetwork/network/libnetwork.h
src/libnetwork/network/phy.h
src/libnetwork/phy.c
src/utils/.gitignore [new file with mode: 0644]
src/utils/network-phy-list-ht-caps.c [new file with mode: 0644]

index f3157a772ad73aeb06694eba9ec30337e729c0d9..993a0a3604c9a21ce8b4cb1dd370557cd24ada04 100644 (file)
@@ -51,6 +51,7 @@ hooks_zonesdir   = $(hooksdir)/zones
 triggersdir      = $(networkdir)/triggers
 
 logdir           = $(localestatedir)/log/network
+utildir          = $(networkdir)
 
 CLEANFILES =
 DISTCLEANFILES =
@@ -285,6 +286,17 @@ EXTRA_DIST += \
 
 # ------------------------------------------------------------------------------
 
+util_PROGRAMS = \
+       src/utils/network-phy-list-ht-caps
+
+src_utils_network_phy_list_ht_caps_SOURCES = \
+       src/utils/network-phy-list-ht-caps.c
+
+src_utils_network_phy_list_ht_caps_LDADD = \
+       src/libnetwork.la
+
+# ------------------------------------------------------------------------------
+
 ppp_SCRIPTS = \
        src/ppp/ip-updown
 
index 156e32a5c52dc5c8488d52e5838265c088e5df4b..cc5b21e106f85d36e35c5225c1e1da6dbcfbba9a 100644 (file)
@@ -111,6 +111,15 @@ AC_CHECK_FUNCS([ \
        secure_getenv \
 ])
 
+# ------------------------------------------------------------------------------
+
+AC_ARG_ENABLE([debug],
+       AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]),
+       [], [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"], [
+       AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.])
+])
+
 # ------------------------------------------------------------------------------
 AC_ARG_WITH([systemdsystemunitdir],
        AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
index d94f395664dfc884000a18fcce9690b2280bc826..9a471e1931b5f3bcf27bacfd93d782bafb1eef6a 100644 (file)
@@ -23,4 +23,6 @@
 
 #define NETWORK_EXPORT __attribute__ ((visibility("default")))
 
+#define BIT(x) (1ULL << (x))
+
 #endif
index f875725058d4d14c69af68773baa73fb7955d59e..65faa932a264ab25c2acd535f85aed0be336011e 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <linux/nl80211.h>
 #include <netlink/genl/ctrl.h>
 #include <netlink/genl/genl.h>
 #include <netlink/netlink.h>
@@ -101,6 +102,15 @@ static int init_netlink(struct network_ctx* ctx) {
        // Set buffer size
        nl_socket_set_buffer_size(ctx->nl_socket, 8192, 8192);
 
+       // Register socket callback
+       struct nl_cb* callback = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!callback) {
+               ERROR(ctx, "Could not allocate socket callback\n");
+               return -ENOMEM;
+       }
+
+       nl_socket_set_cb(ctx->nl_socket, callback);
+
        // Get nl80211 id
        ctx->nl80211_id = genl_ctrl_resolve(ctx->nl_socket, "nl80211");
        if (ctx->nl80211_id < 0) {
@@ -188,3 +198,67 @@ NETWORK_EXPORT void network_set_log_priority(struct network_ctx* ctx, int priori
 NETWORK_EXPORT const char* network_version() {
        return "network " VERSION;
 }
+
+// Creates a netlink message that can be sent with network_send_netlink_message
+struct nl_msg* network_make_netlink_message(struct network_ctx* ctx,
+               enum nl80211_commands cmd, int flags) {
+       // Allocate message
+       struct nl_msg* msg = nlmsg_alloc();
+       if (!msg)
+               return NULL;
+
+       genlmsg_put(msg, 0, 0, ctx->nl80211_id, 0, flags, cmd, 0);
+
+       DEBUG(ctx, "Created new netlink message %p\n", msg);
+
+       return msg;
+}
+
+static int __nl_ack_handler(struct nl_msg* msg, void* data) {
+       int* r = data;
+       *r = 0;
+
+       return NL_STOP;
+}
+
+static int __nl_finish_handler(struct nl_msg* msg, void* data) {
+       int* r = data;
+       *r = 0;
+
+       return NL_SKIP;
+}
+
+// Sends a netlink message and calls the handler to handle the result
+int network_send_netlink_message(struct network_ctx* ctx, struct nl_msg* msg,
+               int(*handler)(struct nl_msg* msg, void* data), void* data) {
+       DEBUG(ctx, "Sending netlink message %p\n", msg);
+
+       // Sending the message
+       int r = nl_send_auto(ctx->nl_socket, msg);
+       if (r < 0) {
+               ERROR(ctx, "Error sending netlink message: %d\n", r);
+               return r;
+       }
+
+       // Register callback
+       struct nl_cb* callback = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!callback) {
+               ERROR(ctx, "Could not allocate callback\n");
+               nlmsg_free(msg);
+
+               return -1;
+       }
+
+       r = 1;
+
+       nl_cb_set(callback, NL_CB_VALID, NL_CB_CUSTOM, handler, data);
+       nl_cb_set(callback, NL_CB_ACK, NL_CB_CUSTOM, __nl_ack_handler, &r);
+       nl_cb_set(callback, NL_CB_FINISH, NL_CB_CUSTOM, __nl_finish_handler, &r);
+
+       while (r > 0)
+               nl_recvmsgs(ctx->nl_socket, callback);
+
+       DEBUG(ctx, "Netlink message returned with status %d\n", r);
+
+       return r;
+}
index 89ed3034ec5295f1d94f106c5f5a06a8a2855730..0b6c90b81f96630a5166f70b6b9a1e6becfaebf4 100644 (file)
@@ -5,6 +5,8 @@ global:
         network_interface_ref;
         network_interface_unref;
         network_new;
+        network_phy_has_ht_capability;
+        network_phy_list_ht_capabilities;
         network_phy_new;
         network_phy_ref;
         network_phy_unref;
index c6181f0f68de75189b8c86413a236559abd07b84..2919fc9eb37331f1eaed5a333be682df7b91aafb 100644 (file)
@@ -40,10 +40,18 @@ const char* network_version();
 
 #ifdef NETWORK_PRIVATE
 
+#include <linux/nl80211.h>
+#include <netlink/msg.h>
+
 void network_log(struct network_ctx* ctx,
        int priority, const char* file, int line, const char* fn,
        const char* format, ...);
 
+struct nl_msg* network_make_netlink_message(struct network_ctx* ctx,
+               enum nl80211_commands cmd, int flags);
+int network_send_netlink_message(struct network_ctx* ctx, struct nl_msg* msg,
+               int(*handler)(struct nl_msg* msg, void* data), void* data);
+
 #endif
 
 #endif
index 7adb2590c5b6a3cae0a88bda94cffc1581c0d908..afa42d572263bc01a948f85c4e094c9ab65a805a 100644 (file)
@@ -30,4 +30,39 @@ int network_phy_new(struct network_ctx*, struct network_phy** phy, const char* n
 struct network_phy* network_phy_ref(struct network_phy* phy);
 struct network_phy* network_phy_unref(struct network_phy* phy);
 
+enum network_phy_ht_caps {
+       NETWORK_PHY_HT_CAP_RX_LDCP         = (1 <<  0),
+       NETWORK_PHY_HT_CAP_HT40            = (1 <<  1),
+       NETWORK_PHY_HT_CAP_SMPS_STATIC     = (1 <<  2),
+       NETWORK_PHY_HT_CAP_SMPS_DYNAMIC    = (1 <<  3),
+       NETWORK_PHY_HT_CAP_RX_GF           = (1 <<  4),
+       NETWORK_PHY_HT_CAP_RX_HT20_SGI     = (1 <<  5),
+       NETWORK_PHY_HT_CAP_RX_HT40_SGI     = (1 <<  6),
+       NETWORK_PHY_HT_CAP_TX_STBC         = (1 <<  7),
+       NETWORK_PHY_HT_CAP_RX_STBC1        = (1 <<  8),
+       NETWORK_PHY_HT_CAP_RX_STBC2        = (1 <<  9),
+       NETWORK_PHY_HT_CAP_RX_STBC3        = (1 << 10),
+       NETWORK_PHY_HT_CAP_DELAYED_BA      = (1 << 11),
+       NETWORK_PHY_HT_CAP_MAX_AMSDU_7935  = (1 << 12),
+       NETWORK_PHY_HT_CAP_DSSS_CCK_HT40   = (1 << 13),
+       NETWORK_PHY_HT_CAP_HT40_INTOLERANT = (1 << 14),
+       NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT  = (1 << 15),
+};
+
+int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap);
+char* network_phy_list_ht_capabilities(struct network_phy* phy);
+
+#ifdef NETWORK_PRIVATE
+
+#include <linux/nl80211.h>
+#include <netlink/msg.h>
+
+struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
+       enum nl80211_commands cmd, int flags);
+
+#define foreach_ht_cap(cap) \
+       for(int cap = NETWORK_PHY_HT_CAP_RX_LDCP; cap != NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT; cap <<= 1)
+
+#endif
+
 #endif /* NETWORK_PHY_H */
index 9e364dec4a5d05488781ffe85e06cde182c15673..de20aa907c7df126e8e115f6e87d529cdb01a03c 100644 (file)
 #############################################################################*/
 
 #include <errno.h>
+#include <linux/nl80211.h>
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/msg.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <network/libnetwork.h>
 #include <network/logging.h>
+#include <network/phy.h>
 #include "libnetwork-private.h"
 
 struct network_phy {
@@ -32,6 +38,8 @@ struct network_phy {
 
        int index;
        char* name;
+
+       unsigned int ht_caps;
 };
 
 static int phy_get_index(const char* name) {
@@ -57,6 +65,135 @@ static int phy_get_index(const char* name) {
        return atoi(index);
 }
 
+static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
+       // RX LDCP
+       if (caps & BIT(0))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDCP;
+
+       // HT40
+       if (caps & BIT(1))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40;
+
+       // Static/Dynamic SM Power Save
+       switch ((caps >> 2) & 0x3) {
+               case 0:
+                       phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_STATIC;
+                       break;
+
+               case 1:
+                       phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC;
+                       break;
+
+               default:
+                       break;
+       }
+
+       // RX Greenfield
+       if (caps & BIT(4))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_GF;
+
+       // RX HT20 Short GI
+       if (caps & BIT(5))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT20_SGI;
+
+       // RX HT40 Short GI
+       if (caps & BIT(6))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT40_SGI;
+
+       // TX STBC
+       if (caps & BIT(7))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_TX_STBC;
+
+       // RX STBC
+       switch ((caps >> 8) & 0x3) {
+               case 1:
+                       phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC1;
+                       break;
+
+               case 2:
+                       phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC2;
+                       break;
+
+               case 3:
+                       phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC3;
+                       break;
+
+               default:
+                       break;
+       }
+
+       // HT Delayed Block ACK
+       if (caps & BIT(10))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_DELAYED_BA;
+
+       // Max AMSDU length 7935
+       if (caps & BIT(11))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935;
+
+       // DSSS/CCK HT40
+       if (caps & BIT(12))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40;
+
+       // Bit 13 is reserved
+
+       // HT40 Intolerant
+       if (caps & BIT(14))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40_INTOLERANT;
+
+       // L-SIG TXOP protection
+       if (caps & BIT(15))
+               phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT;
+}
+
+static int phy_parse_info(struct nl_msg* msg, void* data) {
+       struct network_phy* phy = data;
+
+       struct nlattr* attrs[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+       nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+               genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
+               struct nlattr* nl_band;
+               int i;
+
+               nla_for_each_nested(nl_band, attrs[NL80211_ATTR_WIPHY_BANDS], i) {
+                       struct nlattr* band_attrs[NL80211_BAND_ATTR_MAX + 1];
+                       nla_parse(band_attrs, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+                               nla_len(nl_band), NULL);
+
+                       // HT Capabilities
+                       if (band_attrs[NL80211_BAND_ATTR_HT_CAPA]) {
+                               __u16 caps = nla_get_u16(band_attrs[NL80211_BAND_ATTR_HT_CAPA]);
+                               phy_parse_ht_capabilities(phy, caps);
+                       }
+               }
+       }
+
+       return NL_OK;
+}
+
+static int phy_get_info(struct network_phy* phy) {
+       DEBUG(phy->ctx, "Getting information for %s\n", phy->name);
+
+       struct nl_msg* msg = network_phy_make_netlink_message(phy, NL80211_CMD_GET_WIPHY, 0);
+       if (!msg)
+               return -1;
+
+       return network_send_netlink_message(phy->ctx, msg, phy_parse_info, phy);
+}
+
+static void network_phy_free(struct network_phy* phy) {
+       DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
+
+       if (phy->name)
+               free(phy->name);
+
+       network_unref(phy->ctx);
+       free(phy);
+}
+
 NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy** phy,
                const char* name) {
        if (!name)
@@ -74,6 +211,17 @@ NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy**
        p->ctx = network_ref(ctx);
        p->refcount = 1;
 
+       p->name = strdup(name);
+
+       // Load information from kernel
+       int r = phy_get_info(p);
+       if (r) {
+               ERROR(p->ctx, "Error getting PHY information from kernel\n");
+               network_phy_free(p);
+
+               return r;
+       }
+
        DEBUG(p->ctx, "Allocated phy at %p\n", p);
        *phy = p;
        return 0;
@@ -87,16 +235,6 @@ NETWORK_EXPORT struct network_phy* network_phy_ref(struct network_phy* phy) {
        return phy;
 }
 
-static void network_phy_free(struct network_phy* phy) {
-       DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
-
-       if (phy->name)
-               free(phy->name);
-
-       network_unref(phy->ctx);
-       free(phy);
-}
-
 NETWORK_EXPORT struct network_phy* network_phy_unref(struct network_phy* phy) {
        if (!phy)
                return NULL;
@@ -107,3 +245,95 @@ NETWORK_EXPORT struct network_phy* network_phy_unref(struct network_phy* phy) {
        network_phy_free(phy);
        return NULL;
 }
+
+struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
+               enum nl80211_commands cmd, int flags) {
+       struct nl_msg* msg = network_make_netlink_message(phy->ctx, cmd, flags);
+       if (!msg)
+               return NULL;
+
+       // Set PHY index
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy->index);
+
+       return msg;
+
+nla_put_failure:
+       nlmsg_free(msg);
+
+       return NULL;
+}
+
+NETWORK_EXPORT int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap) {
+       return phy->ht_caps & cap;
+}
+
+static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap) {
+       switch (cap) {
+               case NETWORK_PHY_HT_CAP_RX_LDCP:
+                       return "[LDPC]";
+
+               case NETWORK_PHY_HT_CAP_HT40:
+                       return "[HT40+][HT40-]";
+
+               case NETWORK_PHY_HT_CAP_SMPS_STATIC:
+                       return "[SMPS-STATIC]";
+
+               case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC:
+                       return "[SMPS-DYNAMIC]";
+
+               case NETWORK_PHY_HT_CAP_RX_GF:
+                       return "[GF]";
+
+               case NETWORK_PHY_HT_CAP_RX_HT20_SGI:
+                       return "[SHORT-GI-20]";
+
+               case NETWORK_PHY_HT_CAP_RX_HT40_SGI:
+                       return "[SHORT-GI-40]";
+
+               case NETWORK_PHY_HT_CAP_TX_STBC:
+                       return "[TX-STBC]";
+
+               case NETWORK_PHY_HT_CAP_RX_STBC1:
+                       return "[RX-STBC1]";
+
+               case NETWORK_PHY_HT_CAP_RX_STBC2:
+                       return "[RX-STBC12]";
+
+               case NETWORK_PHY_HT_CAP_RX_STBC3:
+                       return "[RX-STBC123]";
+
+               case NETWORK_PHY_HT_CAP_DELAYED_BA:
+                       return "[DELAYED-BA]";
+
+               case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935:
+                       return "[MAX-AMSDU-7935]";
+
+               case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40:
+                       return "[DSSS_CCK-40]";
+
+               case NETWORK_PHY_HT_CAP_HT40_INTOLERANT:
+                       return "[40-INTOLERANT]";
+
+               case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT:
+                       return "[LSIG-TXOP-PROT]";
+       }
+
+       return NULL;
+}
+
+NETWORK_EXPORT char* network_phy_list_ht_capabilities(struct network_phy* phy) {
+       char* buffer = malloc(1024);
+       *buffer = '\0';
+
+       char* p = buffer;
+       foreach_ht_cap(cap) {
+               if (network_phy_has_ht_capability(phy, cap)) {
+                       const char* cap_str = network_phy_get_ht_capability_string(cap);
+
+                       if (cap_str)
+                               p = strncat(p, cap_str, 1024 - 1);
+               }
+       }
+
+       return buffer;
+}
diff --git a/src/utils/.gitignore b/src/utils/.gitignore
new file mode 100644 (file)
index 0000000..9310e4a
--- /dev/null
@@ -0,0 +1 @@
+/network-phy-list-ht-caps
diff --git a/src/utils/network-phy-list-ht-caps.c b/src/utils/network-phy-list-ht-caps.c
new file mode 100644 (file)
index 0000000..0b87872
--- /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;
+    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;
+
+    struct network_phy* phy;
+    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* ht_caps = network_phy_list_ht_capabilities(phy);
+    if (ht_caps) {
+        printf("%s\n", ht_caps);
+        free(ht_caps);
+    }
+
+END:
+    network_phy_unref(phy);
+    network_unref(ctx);
+    return r;
+}