diff --git a/src/linux/external/hostap/CMakeLists.txt b/src/linux/external/hostap/CMakeLists.txt index fe4866d2..792df86d 100644 --- a/src/linux/external/hostap/CMakeLists.txt +++ b/src/linux/external/hostap/CMakeLists.txt @@ -83,6 +83,12 @@ set(HOSTAPD_BIN "hostapd daemon binary" ) +set(HOSTAPD_CLI_BIN + ${HOSTAP_INSTALL_BIN_DIR}/hostapd_cli + CACHE FILEPATH + "hostapd_cli binary" +) + install( FILES ${LIBWPA_CLIENT} TYPE LIB @@ -91,6 +97,7 @@ install( install( PROGRAMS ${HOSTAPD_BIN} + ${HOSTAPD_CLI_BIN} DESTINATION ${CMAKE_INSTALL_SBINDIR} ) diff --git a/src/linux/libnl-helpers/CMakeLists.txt b/src/linux/libnl-helpers/CMakeLists.txt index 887a7ea8..e4534415 100644 --- a/src/linux/libnl-helpers/CMakeLists.txt +++ b/src/linux/libnl-helpers/CMakeLists.txt @@ -10,8 +10,11 @@ target_sources(libnl-helpers Netlink80211.cxx Netlink80211Interface.cxx Netlink80211ProtocolState.cxx + Netlink80211Wiphy.cxx NetlinkMessage.cxx NetlinkSocket.cxx + Netlink80211WiphyBand.cxx + Netlink80211WiphyBandFrequency.cxx PUBLIC FILE_SET HEADERS BASE_DIRS ${LIBNL_HELPERS_PUBLIC_INCLUDE} @@ -21,6 +24,7 @@ target_sources(libnl-helpers ${LIBNL_HELPERS_PUBLIC_INCLUDE_PREFIX}/nl80211/Netlink80211.hxx ${LIBNL_HELPERS_PUBLIC_INCLUDE_PREFIX}/nl80211/Netlink80211Interface.hxx ${LIBNL_HELPERS_PUBLIC_INCLUDE_PREFIX}/nl80211/Netlink80211ProtocolState.hxx + ${LIBNL_HELPERS_PUBLIC_INCLUDE_PREFIX}/nl80211/Netlink80211Wiphy.hxx ) target_link_libraries(libnl-helpers diff --git a/src/linux/libnl-helpers/Netlink80211.cxx b/src/linux/libnl-helpers/Netlink80211.cxx index f09df8f6..8bbe3a94 100644 --- a/src/linux/libnl-helpers/Netlink80211.cxx +++ b/src/linux/libnl-helpers/Netlink80211.cxx @@ -451,6 +451,52 @@ Nl80211InterfaceTypeToString(nl80211_iftype interfaceType) noexcept } } +std::string_view +Nl80211CipherSuiteToString(uint32_t cipherSuite) noexcept +{ + static constexpr uint32_t CipherTypeWep40 = 0x000fac01; + static constexpr uint32_t CipherTypeWep104 = 0x000fac05; + static constexpr uint32_t CipherTypeTkip = 0x000fac02; + static constexpr uint32_t CipherTypeCcmp128 = 0x000fac04; + static constexpr uint32_t CipherTypeCmac = 0x000fac06; + static constexpr uint32_t CipherTypeGcmp128 = 0x000fac08; + static constexpr uint32_t CipherTypeGcmp256 = 0x000fac09; + static constexpr uint32_t CipherTypeCcmp256 = 0x000fac0a; + static constexpr uint32_t CipherTypeGmac128 = 0x000fac0b; + static constexpr uint32_t CipherTypeGmac256 = 0x000fac0c; + static constexpr uint32_t CipherTypeCmac256 = 0x000fac0d; + static constexpr uint32_t CipherTypeWpiSms4 = 0x00147201; + + switch (cipherSuite) { + case CipherTypeWep40: + return "WEP40"; + case CipherTypeWep104: + return "WEP104"; + case CipherTypeTkip: + return "TKIP"; + case CipherTypeCcmp128: + return "CCMP-128"; + case CipherTypeCmac: + return "CMAC"; + case CipherTypeGcmp128: + return "GCMP-128"; + case CipherTypeGcmp256: + return "GCMP-256"; + case CipherTypeCcmp256: + return "CCMP-256"; + case CipherTypeGmac128: + return "GMAC-128"; + case CipherTypeGmac256: + return "GMAC-256"; + case CipherTypeCmac256: + return "CMAC-256"; + case CipherTypeWpiSms4: + return "WPI-SMS4"; + default: + return "Uknown"; + } +} + using Microsoft::Net::Netlink::NetlinkSocket; std::optional diff --git a/src/linux/libnl-helpers/Netlink80211Interface.cxx b/src/linux/libnl-helpers/Netlink80211Interface.cxx index f80ffb68..3f661567 100644 --- a/src/linux/libnl-helpers/Netlink80211Interface.cxx +++ b/src/linux/libnl-helpers/Netlink80211Interface.cxx @@ -17,22 +17,23 @@ using namespace Microsoft::Net::Netlink::Nl80211; using Microsoft::Net::Netlink::NetlinkMessage; using Microsoft::Net::Netlink::NetlinkSocket; -Nl80211Interface::Nl80211Interface(std::string_view name, nl80211_iftype type, uint32_t index) noexcept : +Nl80211Interface::Nl80211Interface(std::string_view name, nl80211_iftype type, uint32_t index, uint32_t wiphyIndex) noexcept : Name(name), Type(type), - Index(index) + Index(index), + WiphyIndex(wiphyIndex) { } std::string Nl80211Interface::ToString() const { - return std::format("[{}] {} {}", Index, Name, magic_enum::enum_name(Type)); + return std::format("[{}/{}] {} {}", Index, WiphyIndex, Name, magic_enum::enum_name(Type)); } /* static */ std::optional -Nl80211Interface::Parse(struct nl_msg* nl80211Message) noexcept +Nl80211Interface::Parse(struct nl_msg *nl80211Message) noexcept { // Ensure the message is valid. if (nl80211Message == nullptr) { @@ -62,18 +63,19 @@ Nl80211Interface::Parse(struct nl_msg* nl80211Message) noexcept auto *interfaceName = static_cast(nla_data(newInterfaceMessageAttributes[NL80211_ATTR_IFNAME])); auto interfaceType = static_cast(nla_get_u32(newInterfaceMessageAttributes[NL80211_ATTR_IFTYPE])); auto interfaceIndex = static_cast(nla_get_u32(newInterfaceMessageAttributes[NL80211_ATTR_IFINDEX])); + auto wiphyIndex = static_cast(nla_get_u32(newInterfaceMessageAttributes[NL80211_ATTR_WIPHY])); - return Nl80211Interface(interfaceName, interfaceType, interfaceIndex); + return Nl80211Interface(interfaceName, interfaceType, interfaceIndex, wiphyIndex); } namespace detail { /** * @brief Handler function for NL80211_CMD_GET_INTERFACE responses. - * + * * @param nl80211Message The response message to a NL80211_CMD_GET_INTERFACE dump request. * @param context The context pointer provided to nl_socket_modify_cb. This must be a std::vector*. - * @return int + * @return int */ int HandleNl80211InterfaceDumpResponse(struct nl_msg *nl80211Message, void *context) @@ -152,3 +154,9 @@ Nl80211Interface::Enumerate() return nl80211Interfaces; } + +std::optional +Nl80211Interface::GetWiphy() const +{ + return Nl80211Wiphy::FromIndex(WiphyIndex); +} diff --git a/src/linux/libnl-helpers/Netlink80211Wiphy.cxx b/src/linux/libnl-helpers/Netlink80211Wiphy.cxx new file mode 100644 index 00000000..1ce452c5 --- /dev/null +++ b/src/linux/libnl-helpers/Netlink80211Wiphy.cxx @@ -0,0 +1,222 @@ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Microsoft::Net::Netlink::Nl80211; + +Nl80211Wiphy::Nl80211Wiphy(uint32_t index, std::string_view name, std::vector cipherSuites, std::unordered_map bands, std::vector supportedInterfaceTypes, bool supportsRoaming) noexcept : + Index(index), + Name(name), + CipherSuites(std::move(cipherSuites)), + Bands(std::move(bands)), + SupportedInterfaceTypes(std::move(supportedInterfaceTypes)), + SupportsRoaming(supportsRoaming) +{ +} + +namespace detail +{ +int +HandleNl80211GetWiphyResponse(struct nl_msg *nl80211Message, void *context) noexcept +{ + if (context == nullptr) { + LOGE << "Received nl80211 get wiphy response with null context"; + return NL_SKIP; + } + + // Extract the std::optional from the context. + auto &nl80211WiphyResult = *static_cast *>(context); + + // Attempt to parse the message. + nl80211WiphyResult = Nl80211Wiphy::Parse(nl80211Message); + if (!nl80211WiphyResult.has_value()) { + LOGE << "Failed to parse nl80211 wiphy message"; + return NL_SKIP; + } + + LOGD << std::format("Successfully parsed an nl80211 wiphy:\n{}", nl80211WiphyResult->ToString()); + + return NL_OK; +} +} // namespace detail + +/* static */ +std::optional +Nl80211Wiphy::FromIndex(uint32_t wiphyIndex) +{ + // Allocate a new netlink socket. + auto nl80211SocketOpt{ CreateNl80211Socket() }; + if (!nl80211SocketOpt.has_value()) { + LOGE << "Failed to create nl80211 socket"; + return std::nullopt; + } + + // Allocate a new nl80211 message for sending the dump request for all interfaces. + auto nl80211Socket{ std::move(nl80211SocketOpt.value()) }; + auto nl80211MessageGetWiphy{ NetlinkMessage::Allocate() }; + if (nl80211MessageGetWiphy == nullptr) { + LOGE << "Failed to allocate nl80211 message for wiphy request"; + return std::nullopt; + } + + // Populate the genl message for the wiphy request. + const int nl80211DriverId = Nl80211ProtocolState::Instance().DriverId; + const auto *genlMessageGetInterfaces = genlmsg_put(nl80211MessageGetWiphy, NL_AUTO_PID, NL_AUTO_SEQ, nl80211DriverId, 0, 0, NL80211_CMD_GET_WIPHY, 0); + if (genlMessageGetInterfaces == nullptr) { + LOGE << "Failed to populate genl message for interface dump request"; + return std::nullopt; + } + + // Add the wiphy index attribute so nl80211 knows what to lookup. + nla_put_u32(nl80211MessageGetWiphy, NL80211_ATTR_WIPHY, wiphyIndex); + std::optional nl80211Wiphy{}; + int ret = nl_socket_modify_cb(nl80211Socket, NL_CB_VALID, NL_CB_CUSTOM, detail::HandleNl80211GetWiphyResponse, &nl80211Wiphy); + + if (ret < 0) { + LOGE << std::format("Failed to modify socket callback with error {} ({})", ret, nl_geterror(ret)); + return std::nullopt; + } + + // Send the request. + ret = nl_send_auto(nl80211Socket, nl80211MessageGetWiphy); + if (ret < 0) { + LOGE << std::format("Failed to send get wiphy request with error {} ({})", ret, nl_geterror(ret)); + return std::nullopt; + } + + // Receive the response, which will invoke the configured callback. + ret = nl_recvmsgs_default(nl80211Socket); + if (ret < 0) { + LOGE << std::format("Failed to receive get wiphy response with error {} ({})", ret, nl_geterror(ret)); + return std::nullopt; + } + + return nl80211Wiphy; +} + +/* static */ +std::optional +Nl80211Wiphy::Parse(struct nl_msg *nl80211Message) noexcept +{ + // Ensure the message is valid. + if (nl80211Message == nullptr) { + LOGE << "Received null nl80211 message"; + return std::nullopt; + } + + // Ensure the message has a valid genl header. + auto *nl80211MessageHeader{ static_cast(nlmsg_hdr(nl80211Message)) }; + if (genlmsg_valid_hdr(nl80211MessageHeader, 1) < 0) { + LOGE << "Received invalid nl80211 message header"; + return std::nullopt; + } + + // Extract the nl80211 (genl) message header. + const auto *genl80211MessageHeader{ static_cast(nlmsg_data(nl80211MessageHeader)) }; + + // Parse the message. + std::array wiphyAttributes{}; + int ret = nla_parse(std::data(wiphyAttributes), std::size(wiphyAttributes), genlmsg_attrdata(genl80211MessageHeader, 0), genlmsg_attrlen(genl80211MessageHeader, 0), nullptr); + if (ret < 0) { + LOG_ERROR << std::format("Failed to parse netlink message attributes with error {} ({})", ret, strerror(-ret)); + return std::nullopt; + } + + // Process top-level identifiers. + if (wiphyAttributes[NL80211_ATTR_WIPHY] == nullptr || wiphyAttributes[NL80211_ATTR_WIPHY_NAME] == nullptr) { + LOGE << "Received nl80211 message with missing wiphy index or name"; + return std::nullopt; + } + + auto wiphyIndex = static_cast(nla_get_u32(wiphyAttributes[NL80211_ATTR_WIPHY])); + auto wiphyName = static_cast(nla_data(wiphyAttributes[NL80211_ATTR_WIPHY_NAME])); + + // Process bands. + auto wiphyBands = wiphyAttributes[NL80211_ATTR_WIPHY_BANDS]; + std::unordered_map wiphyBandMap{}; + if (wiphyBands != nullptr) { + int remainingBands; + struct nlattr *wiphyBand; + + nla_for_each_nested(wiphyBand, wiphyBands, remainingBands) + { + auto nl80211BandType = static_cast(wiphyBand->nla_type); + if (nl80211BandType == nl80211_band::NUM_NL80211_BANDS) { + continue; + } + + auto nl80211Band = Nl80211WiphyBand::Parse(wiphyBand); + if (nl80211Band.has_value()) { + wiphyBandMap.emplace(nl80211BandType, std::move(nl80211Band.value())); + } + } + } + + // Process cipher suites. + uint32_t *wiphyCipherSuites; + auto wiphyNumCipherSuites = static_cast(nla_len(wiphyAttributes[NL80211_ATTR_CIPHER_SUITES])) / sizeof(*wiphyCipherSuites); + wiphyCipherSuites = static_cast(nla_data(wiphyAttributes[NL80211_ATTR_CIPHER_SUITES])); + std::vector cipherSuites(wiphyCipherSuites, wiphyCipherSuites + wiphyNumCipherSuites); + + // Process supported interface types. + std::vector supportedInterfaceTypes{}; + if (wiphyAttributes[NL80211_ATTR_SUPPORTED_IFTYPES] != nullptr) { + int remainingSupportedInterfaceTypes; + struct nlattr *supportedInterfaceType; + nla_for_each_nested(supportedInterfaceType, wiphyAttributes[NL80211_ATTR_SUPPORTED_IFTYPES], remainingSupportedInterfaceTypes) + { + auto interfaceType = static_cast(supportedInterfaceType->nla_type); + supportedInterfaceTypes.emplace_back(interfaceType); + } + } + + // Process roaming support. + auto wiphySupportsRoaming = wiphyAttributes[NL80211_ATTR_ROAM_SUPPORT] != nullptr; + + Nl80211Wiphy nl80211Wiphy{ wiphyIndex, wiphyName, std::move(cipherSuites), std::move(wiphyBandMap), std::move(supportedInterfaceTypes), wiphySupportsRoaming }; + return nl80211Wiphy; +} + +std::string +Nl80211Wiphy::ToString() const +{ + std::ostringstream ss; + + ss << std::format("Wiphy {} [{}]\n", Name, Index); + ss << std::format(" Supports roaming: {}\n", SupportsRoaming); + + ss << " Cipher Suites:\n "; + for (const auto &cipherSuite : CipherSuites) { + ss << std::format("{} ", Nl80211CipherSuiteToString(cipherSuite)); + } + + constexpr auto IfTypePrefixLength = std::size(std::string_view("NL80211_IFTYPE_")); + ss << "\n Supported Interface Types:\n"; + for (const auto &interfaceType : SupportedInterfaceTypes) { + std::string_view interfaceTypeName{ magic_enum::enum_name(interfaceType) }; + interfaceTypeName.remove_prefix(IfTypePrefixLength); + ss << std::format(" {}\n", interfaceTypeName); + } + + constexpr auto BandPrefixLength = std::size(std::string_view("NL80211_BAND_")); + ss << "\n Bands:\n"; + for (const auto &[band, wiphyBand] : Bands) { + std::string_view bandName{ magic_enum::enum_name(band) }; + bandName.remove_prefix(BandPrefixLength); + ss << std::format(" [Band {}]\n{}\n", bandName, wiphyBand.ToString()); + } + + return ss.str(); +} diff --git a/src/linux/libnl-helpers/Netlink80211WiphyBand.cxx b/src/linux/libnl-helpers/Netlink80211WiphyBand.cxx new file mode 100644 index 00000000..bfd78166 --- /dev/null +++ b/src/linux/libnl-helpers/Netlink80211WiphyBand.cxx @@ -0,0 +1,110 @@ + +#include +#include + +#include +#include +#include +#include +#include + +using namespace Microsoft::Net::Netlink::Nl80211; + +Nl80211WiphyBand::Nl80211WiphyBand(std::vector frequencies, std::vector bitRates, uint16_t htCapabilities, uint32_t VhtCapabilities, std::optional> vhtMcsSetOpt) noexcept : + Frequencies(std::move(frequencies)), + Bitrates(std::move(bitRates)), + HtCapabilities(htCapabilities), + VhtCapabilities(VhtCapabilities), + VhtMcsSet(std::move(vhtMcsSetOpt)) +{ +} + +/* static */ +std::optional +Nl80211WiphyBand::Parse(struct nlattr *wiphyBand) noexcept +{ + // Parse the attribute message. + std::array wiphyBandAttributes{}; + int ret = nla_parse(std::data(wiphyBandAttributes), std::size(wiphyBandAttributes), static_cast(nla_data(wiphyBand)), nla_len(wiphyBand), nullptr); + if (ret < 0) { + LOGE << std::format("Failed to parse wiphy band attributes with error {} ({})", ret, nl_geterror(ret)); + return std::nullopt; + } + + uint16_t htCapabilities{ 0 }; + if (wiphyBandAttributes[NL80211_BAND_ATTR_HT_CAPA] != nullptr) { + htCapabilities = nla_get_u16(wiphyBandAttributes[NL80211_BAND_ATTR_HT_CAPA]); + } + + uint32_t vhtCapabilities{ 0 }; + std::optional> vhtMcsSetOpt{}; + if (wiphyBandAttributes[NL80211_BAND_ATTR_VHT_CAPA] != nullptr) { + vhtCapabilities = nla_get_u32(wiphyBandAttributes[NL80211_BAND_ATTR_VHT_CAPA]); + if (wiphyBandAttributes[NL80211_BAND_ATTR_VHT_MCS_SET] != nullptr) { + vhtMcsSetOpt.emplace(); + auto& vhtMcsSet = vhtMcsSetOpt.value(); + std::copy_n(static_cast(nla_data(wiphyBandAttributes[NL80211_BAND_ATTR_VHT_MCS_SET])), std::size(vhtMcsSet), std::begin(vhtMcsSet)); + } + } + + std::vector frequencies{}; + if (wiphyBandAttributes[NL80211_BAND_ATTR_FREQS] != nullptr) { + struct nlattr *wiphyBandFrequency; + int remainingBandFrequencies; + nla_for_each_nested(wiphyBandFrequency, wiphyBandAttributes[NL80211_BAND_ATTR_FREQS], remainingBandFrequencies) + { + auto frequency = WiphyBandFrequency::Parse(wiphyBandFrequency); + if (frequency.has_value()) { + frequencies.emplace_back(std::move(frequency.value())); + } + } + } + + std::vector bitRates{}; + if (wiphyBandAttributes[NL80211_BAND_ATTR_RATES] != nullptr) { + int remainingBitRates; + struct nlattr *bitRate; + nla_for_each_nested(bitRate, wiphyBandAttributes[NL80211_BAND_ATTR_RATES], remainingBitRates) + { + std::array bitRateAttributes{}; + ret = nla_parse(std::data(bitRateAttributes), std::size(bitRateAttributes), static_cast(nla_data(bitRate)), nla_len(bitRate), nullptr); + if (ret < 0) { + LOGW << std::format("Failed to parse wiphy band bit rate attributes with error {} ({})", ret, nl_geterror(ret)); + return std::nullopt; + } else if (bitRateAttributes[NL80211_BITRATE_ATTR_RATE] == nullptr) { + continue; + } + + auto bitRateValue = static_cast(nla_get_u32(bitRateAttributes[NL80211_BITRATE_ATTR_RATE])); + bitRates.emplace_back(bitRateValue); + } + } + + return Nl80211WiphyBand(std::move(frequencies), std::move(bitRates), htCapabilities, vhtCapabilities, std::move(vhtMcsSetOpt)); +} + +std::string +Nl80211WiphyBand::ToString() const +{ + std::ostringstream ss; + + ss << " HT Capabilities: " << std::format("0x{:04x}\n", HtCapabilities); + ss << " VHT Capabilities: " << std::format("0x{:08x}\n", VhtCapabilities); + if (VhtMcsSet.has_value()) { + ss << " VHT MCS Set: 0x"; + for (const auto &mcs : VhtMcsSet.value()) { + ss << std::format("{:02x}", mcs); + } + ss << '\n'; + } + ss << " Frequencies:\n"; + for (const auto &frequency : Frequencies) { + ss << std::format(" {}\n", frequency.ToString()); + } + ss << " Bitrates:\n"; + for (const auto bitRate : Bitrates) { + ss << std::format(" {:2.1f} Mbps\n", BitRateUnitMbpsMultiplier * bitRate); + } + + return ss.str(); +} diff --git a/src/linux/libnl-helpers/Netlink80211WiphyBandFrequency.cxx b/src/linux/libnl-helpers/Netlink80211WiphyBandFrequency.cxx new file mode 100644 index 00000000..17508634 --- /dev/null +++ b/src/linux/libnl-helpers/Netlink80211WiphyBandFrequency.cxx @@ -0,0 +1,58 @@ + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Microsoft::Net::Netlink::Nl80211; + +WiphyBandFrequency::WiphyBandFrequency(uint32_t frequency, std::optional frequencyOffset, bool isDisabled) noexcept : + Frequency(frequency), + FrequencyOffset(frequencyOffset), + IsDisabled(isDisabled) +{ +} + +/* static */ +std::optional +WiphyBandFrequency::Parse(struct nlattr *wiphyBandFrequencyNlAttribute) noexcept +{ + // Parse the attribute message. + std::array wiphyBandFrequenciesAttributes{}; + int ret = nla_parse(std::data(wiphyBandFrequenciesAttributes), std::size(wiphyBandFrequenciesAttributes), static_cast(nla_data(wiphyBandFrequencyNlAttribute)), nla_len(wiphyBandFrequencyNlAttribute), nullptr); + if (ret < 0) { + LOGE << std::format("Failed to parse wiphy band frequency attributes with error {} ({})", ret, nl_geterror(ret)); + return std::nullopt; + } + + if (wiphyBandFrequenciesAttributes[NL80211_FREQUENCY_ATTR_FREQ] == nullptr) { + return std::nullopt; + } + + const auto isDisabled = wiphyBandFrequenciesAttributes[NL80211_FREQUENCY_ATTR_DISABLED] != nullptr; + const auto frequency = static_cast(nla_get_u32(wiphyBandFrequenciesAttributes[NL80211_FREQUENCY_ATTR_FREQ])); + std::optional frequencyOffset{}; + if (wiphyBandFrequenciesAttributes[NL80211_FREQUENCY_ATTR_OFFSET] != nullptr) { + frequencyOffset = static_cast(nla_get_u32(wiphyBandFrequenciesAttributes[NL80211_FREQUENCY_ATTR_OFFSET])); + } + + WiphyBandFrequency wiphyBandFrequency{ frequency, frequencyOffset, isDisabled }; + return wiphyBandFrequency; +} + +std::string +WiphyBandFrequency::ToString() const +{ + std::ostringstream ss; + ss << std::format("{}.{} MHz", Frequency, FrequencyOffset.value_or(0)); + if (IsDisabled) { + ss << " (disabled)"; + } + + return ss.str(); +} diff --git a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211.hxx b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211.hxx index 3ebabbb5..26eb74ac 100644 --- a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211.hxx +++ b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211.hxx @@ -48,6 +48,14 @@ Nl80211CommandToString(nl80211_commands command) noexcept; std::string_view Nl80211InterfaceTypeToString(nl80211_iftype type) noexcept; +/** + * @brief Convert a cipher suite value to a string. + * + * @return std::string_view + */ +std::string_view +Nl80211CipherSuiteToString(uint32_t cipherType) noexcept; + /** * @brief Create a netlink socket for use with Nl80211. * diff --git a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Interface.hxx b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Interface.hxx index f46bbddb..5c797047 100644 --- a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Interface.hxx +++ b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Interface.hxx @@ -12,6 +12,8 @@ #include #include +#include + namespace Microsoft::Net::Netlink::Nl80211 { /** @@ -20,13 +22,14 @@ namespace Microsoft::Net::Netlink::Nl80211 struct Nl80211Interface { std::string Name; - nl80211_iftype Type{ nl80211_iftype::NL80211_IFTYPE_UNSPECIFIED}; + nl80211_iftype Type{ nl80211_iftype::NL80211_IFTYPE_UNSPECIFIED }; uint32_t Index; + uint32_t WiphyIndex; /** * @brief Parse a netlink message into an Nl80211Interface. The netlink message must contain a response to the * NL80211_CMD_GET_INTERFACE command, which is encoded as a NL80211_CMD_NEW_INTERFACE. - * + * * @param nl80211Message The message to parse. * @return std::optional Will contain a valid Nl80211Interface if the message was parsed * successfully, otherwise has no value, indicating the message did not contain a valid NL80211_CMD_NEW_INTERFACE @@ -37,16 +40,24 @@ struct Nl80211Interface /** * @brief Enumerate all netlink 802.11 interfaces on the system. - * + * * @return std::vector */ static std::vector Enumerate(); /** - * @brief Convert the interface to a string representation. + * @brief Get the Wiphy (PHY) object associated with this interface. * - * @return std::string + * @return std::optional + */ + std::optional + GetWiphy() const; + + /** + * @brief Convert the interface to a string representation. + * + * @return std::string */ std::string ToString() const; @@ -54,12 +65,13 @@ struct Nl80211Interface private: /** * @brief Construct a new Nl80211Interface object with the specified attributes. - * + * * @param name The name of the interface. * @param type The nl80211_iftype of the interface. * @param index The interface index in the kernel. + * @param wiphyIndex The phy interface index in the kernel. */ - Nl80211Interface(std::string_view name, nl80211_iftype type, uint32_t index) noexcept; + Nl80211Interface(std::string_view name, nl80211_iftype type, uint32_t index, uint32_t wiphyIndex) noexcept; }; } // namespace Microsoft::Net::Netlink::Nl80211 diff --git a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Wiphy.hxx b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Wiphy.hxx new file mode 100644 index 00000000..8b6263e1 --- /dev/null +++ b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211Wiphy.hxx @@ -0,0 +1,73 @@ + +#ifndef NETLINK_80211_WIPHY_HXX +#define NETLINK_80211_WIPHY_HXX + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Microsoft::Net::Netlink::Nl80211 +{ +/** + * @brief Represents a netlink 802.11 wiphy. This is the interface to the physical radio and describes raw capabilities + * of the device, irrespective of how the device is being used (eg. in userspace via hostapd, etc.). + */ +struct Nl80211Wiphy +{ + uint32_t Index; + std::string Name; + std::vector CipherSuites; + std::unordered_map Bands; + std::vector SupportedInterfaceTypes; + bool SupportsRoaming; + + /** + * @brief Creates a new Nl80211Wiphy object from the specified wiphy index. + * + * @param wiphyIndex The wiphy index to create the object from. + * @return std::optional + */ + static std::optional + FromIndex(uint32_t wiphyIndex); + + /** + * @brief Parse a netlink message into an Nl80211Wiphy. The netlink message must contain a response to the + * NL80211_CMD_GET_WIPHY command. + * + * @param nl80211Message The message to parse. + * @return std::optional A valid Nl80211Wiphy if the message was parsed successfully, otherwise has no + * value. + */ + static std::optional + Parse(struct nl_msg* nl80211Message) noexcept; + + /** + * @brief Convert the wiphy to a string representation. + * + * @return std::string + */ + std::string + ToString() const; + +private: + /** + * @brief Construct a new Nl80211Wiphy object. + * + * @param index The wiphy index. + * @param name The wiphy name. + */ + Nl80211Wiphy(uint32_t index, std::string_view name, std::vector cipherSuites, std::unordered_map bands, std::vector supportedInterfaceTypes, bool supportsRoaming) noexcept; +}; + +} // namespace Microsoft::Net::Netlink::Nl80211 + +#endif // NETLINK_80211_WIPHY_HXX diff --git a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211WiphyBand.hxx b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211WiphyBand.hxx new file mode 100644 index 00000000..d564e101 --- /dev/null +++ b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211WiphyBand.hxx @@ -0,0 +1,68 @@ + +#ifndef NETLINK_80211_WIPHY_BAND_HXX +#define NETLINK_80211_WIPHY_BAND_HXX + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Microsoft::Net::Netlink::Nl80211 +{ +/** + * @brief Represents a netlink 802.11 wiphy radio band and its properties. + */ +struct Nl80211WiphyBand +{ + std::vector Frequencies; + std::vector Bitrates; + uint16_t HtCapabilities; + uint32_t VhtCapabilities; + + // From documentation in nl80211.h under nl80211_band_attr defintion. + static constexpr auto VhtMcsSetNumBytes{ 32 }; + std::optional> VhtMcsSet; + + /** + * @brief Parses a netlink attribute into a Nl80211WiphyBand. + * + * @param wiphyBand The netlink attribute to parse. + * @return std::optional + */ + static std::optional + Parse(struct nlattr* wiphyBand) noexcept; + + /** + * @brief Convert the wiphy band to a string representation. + * + * @return std::string + */ + std::string + ToString() const; + +private: + /** + * @brief The value to multiply the bitrate by to get the actual bitrate in Mbps. + */ + static constexpr auto BitRateUnitMbpsMultiplier = 0.1; + +private: + /** + * @brief Construct a new Nl80211WiphyBand object. + * + * @param frequencies The frequencies supported by this band. + * @param bitRates The bitrates supported by this band. + * @param htCapabilities The high-throughput ('HT' aka 802.11n) capabilities of this band. + * @param vhtCapabilities The very-high-throughput ('VHT' aka 802.11ac) capabilities of this band. + */ + Nl80211WiphyBand(std::vector frequencies, std::vector bitRates, uint16_t htCapabilities, uint32_t vhtCapabilities, std::optional> vhtMcsSet) noexcept; +}; +} // namespace Microsoft::Net::Netlink::Nl80211 + +#endif // NETLINK_80211_WIPHY_BAND_HXX diff --git a/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211WiphyBandFrequency.hxx b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211WiphyBandFrequency.hxx new file mode 100644 index 00000000..72922e84 --- /dev/null +++ b/src/linux/libnl-helpers/include/microsoft/net/netlink/nl80211/Netlink80211WiphyBandFrequency.hxx @@ -0,0 +1,53 @@ + +#ifndef NETLINK_80211_WIPHY_BAND_FREQUENCY_HXX +#define NETLINK_80211_WIPHY_BAND_FREQUENCY_HXX + +#include +#include +#include + +#include +#include +#include + +namespace Microsoft::Net::Netlink::Nl80211 +{ +/** + * @brief Represents information about a frequency in a netlink 802.11 wiphy radio band. + */ +struct WiphyBandFrequency +{ + uint32_t Frequency; + std::optional FrequencyOffset; + bool IsDisabled; + + /** + * @brief Parses a netlink attribute into a WiphyBandFrequency. + * + * @param wiphyBandFrequency The netlink attribute to parse. + * @return std::optional + */ + static std::optional + Parse(struct nlattr* wiphyBandFrequency) noexcept; + + /** + * @brief Convert the frequency to a string representation. + * + * @return std::string + */ + std::string + ToString() const; + +private: + /** + * @brief Construct a new Wiphy Band Frequency object. + * + * @param frequency The frequency in MHz. + * @param frequencyOffset The fractional portion of the frequency, if any. + * @param isDisabled Whether the frequency is disabled. + */ + WiphyBandFrequency(uint32_t frequency, std::optional frequencyOffset, bool isDisabled) noexcept; +}; +} // namespace Microsoft::Net::Netlink::Nl80211 + +#endif // NETLINK_80211_WIPHY_BAND_FREQUENCY_HXX