From: Michael Tremer Date: Tue, 6 Feb 2018 10:47:03 +0000 (+0000) Subject: libnetwork: Add command that returns supported HT caps for wireless PHYs X-Git-Tag: 010~168 X-Git-Url: http://git.ipfire.org/?p=people%2Fms%2Fnetwork.git;a=commitdiff_plain;h=e145b2f3fa76fbfec7974ebedac3dbfabad01019 libnetwork: Add command that returns supported HT caps for wireless PHYs Fixes #11611 Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index f3157a77..993a0a36 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index 156e32a5..cc5b21e1 100644 --- a/configure.ac +++ b/configure.ac @@ -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]), diff --git a/src/libnetwork/libnetwork-private.h b/src/libnetwork/libnetwork-private.h index d94f3956..9a471e19 100644 --- a/src/libnetwork/libnetwork-private.h +++ b/src/libnetwork/libnetwork-private.h @@ -23,4 +23,6 @@ #define NETWORK_EXPORT __attribute__ ((visibility("default"))) +#define BIT(x) (1ULL << (x)) + #endif diff --git a/src/libnetwork/libnetwork.c b/src/libnetwork/libnetwork.c index f8757250..65faa932 100644 --- a/src/libnetwork/libnetwork.c +++ b/src/libnetwork/libnetwork.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -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; +} diff --git a/src/libnetwork/libnetwork.sym b/src/libnetwork/libnetwork.sym index 89ed3034..0b6c90b8 100644 --- a/src/libnetwork/libnetwork.sym +++ b/src/libnetwork/libnetwork.sym @@ -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; diff --git a/src/libnetwork/network/libnetwork.h b/src/libnetwork/network/libnetwork.h index c6181f0f..2919fc9e 100644 --- a/src/libnetwork/network/libnetwork.h +++ b/src/libnetwork/network/libnetwork.h @@ -40,10 +40,18 @@ const char* network_version(); #ifdef NETWORK_PRIVATE +#include +#include + 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 diff --git a/src/libnetwork/network/phy.h b/src/libnetwork/network/phy.h index 7adb2590..afa42d57 100644 --- a/src/libnetwork/network/phy.h +++ b/src/libnetwork/network/phy.h @@ -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 +#include + +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 */ diff --git a/src/libnetwork/phy.c b/src/libnetwork/phy.c index 9e364dec..de20aa90 100644 --- a/src/libnetwork/phy.c +++ b/src/libnetwork/phy.c @@ -19,11 +19,17 @@ #############################################################################*/ #include +#include +#include +#include +#include +#include #include #include #include #include +#include #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 index 00000000..9310e4a2 --- /dev/null +++ b/src/utils/.gitignore @@ -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 index 00000000..0b878724 --- /dev/null +++ b/src/utils/network-phy-list-ht-caps.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; + 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; +}