triggersdir = $(networkdir)/triggers
logdir = $(localestatedir)/log/network
+utildir = $(networkdir)
CLEANFILES =
DISTCLEANFILES =
# ------------------------------------------------------------------------------
+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
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]),
#define NETWORK_EXPORT __attribute__ ((visibility("default")))
+#define BIT(x) (1ULL << (x))
+
#endif
#include <ctype.h>
#include <errno.h>
+#include <linux/nl80211.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/netlink.h>
// 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) {
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;
+}
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;
#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
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 */
#############################################################################*/
#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 {
int index;
char* name;
+
+ unsigned int ht_caps;
};
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)
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;
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;
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;
+}
--- /dev/null
+/network-phy-list-ht-caps
--- /dev/null
+/*#############################################################################
+# #
+# 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;
+}